/// <summary> /// Returns an expression which creates the function object. /// </summary> internal MSAst.Expression MakeFunctionExpression() { var defaults = new List <MSAst.Expression>(); var kwdefaults = new List <MSAst.Expression>(); var annotations = new List <MSAst.Expression>(); if (ReturnAnnotation != null) { annotations.Add(Ast.Constant("return", typeof(string))); annotations.Add(ReturnAnnotation); } foreach (var param in _parameters) { if (param.Kind == ParameterKind.Normal && param.DefaultValue != null) { defaults.Add(AstUtils.Convert(param.DefaultValue, typeof(object))); } if (param.Kind == ParameterKind.KeywordOnly && param.DefaultValue != null) { kwdefaults.Add(Ast.Constant(param.Name, typeof(string))); kwdefaults.Add(param.DefaultValue); } if (param.Annotation != null) { annotations.Add(Ast.Constant(param.Name, typeof(string))); annotations.Add(param.Annotation); } } MSAst.Expression funcCode = GlobalParent.Constant(GetOrMakeFunctionCode()); FuncCodeExpr = funcCode; MSAst.Expression ret; if (EmitDebugFunction()) { LightLambdaExpression code = CreateFunctionLambda(); // we need to compile all of the debuggable code together at once otherwise mdbg gets confused. If we're // in tracing mode we'll still compile things one off though just to keep things simple. The code will still // be debuggable but naive debuggers like mdbg will have more issues. ret = Ast.Call( AstMethods.MakeFunctionDebug, // method Parent.LocalContext, // 1. Emit CodeContext FuncCodeExpr, // 2. FunctionCode ((IPythonGlobalExpression)GetVariableExpression(_nameVariable)).RawValue(), // 3. module name defaults.Count == 0 ? // 4. default values AstUtils.Constant(null, typeof(object[])) : (MSAst.Expression)Ast.NewArrayInit(typeof(object), defaults), kwdefaults.Count == 0 ? AstUtils.Constant(null, typeof(PythonDictionary)) : (MSAst.Expression)Ast.Call( // 5. kwdefaults AstMethods.MakeDictFromItems, Ast.NewArrayInit( typeof(object), kwdefaults ) ), annotations.Count == 0 ? AstUtils.Constant(null, typeof(PythonDictionary)) : (MSAst.Expression)Ast.Call( // 6. annotations AstMethods.MakeDictFromItems, Ast.NewArrayInit( typeof(object), annotations ) ), IsGenerator ? (MSAst.Expression) new PythonGeneratorExpression(code, GlobalParent.PyContext.Options.CompilationThreshold) : (MSAst.Expression)code ); } else { ret = Ast.Call( AstMethods.MakeFunction, // method Parent.LocalContext, // 1. Emit CodeContext FuncCodeExpr, // 2. FunctionCode ((IPythonGlobalExpression)GetVariableExpression(_nameVariable)).RawValue(), // 3. module name defaults.Count == 0 ? // 4. default values AstUtils.Constant(null, typeof(object[])) : (MSAst.Expression)Ast.NewArrayInit(typeof(object), defaults), kwdefaults.Count == 0 ? AstUtils.Constant(null, typeof(PythonDictionary)) : (MSAst.Expression)Ast.Call( // 5. kwdefaults AstMethods.MakeDictFromItems, Ast.NewArrayInit( typeof(object), kwdefaults ) ), annotations.Count == 0 ? AstUtils.Constant(null, typeof(PythonDictionary)) : (MSAst.Expression)Ast.Call( // 6. annotations AstMethods.MakeDictFromItems, Ast.NewArrayInit( typeof(object), annotations ) ) ); } return(AddDecorators(ret, _decorators)); }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { return(AstFactory.Block(Transform(gen), Ast.Constant(null))); }
internal MSAst.Expression ReduceWorker() { var retStmt = _body as ReturnStatement; if (retStmt != null && (_languageFeatures == ModuleOptions.None || _languageFeatures == (ModuleOptions.ExecOrEvalCode | ModuleOptions.Interpret) || _languageFeatures == (ModuleOptions.ExecOrEvalCode | ModuleOptions.Interpret | ModuleOptions.LightThrow))) { // for simple eval's we can construct a simple tree which just // leaves the value on the stack. Return's can't exist in modules // so this is always safe. Debug.Assert(!_isModule); var ret = (ReturnStatement)_body; Ast simpleBody; if ((_languageFeatures & ModuleOptions.LightThrow) != 0) { simpleBody = LightExceptions.Rewrite(retStmt.Expression.Reduce()); } else { simpleBody = retStmt.Expression.Reduce(); } var start = IndexToLocation(ret.Expression.StartIndex); var end = IndexToLocation(ret.Expression.EndIndex); return(Ast.Block( Ast.DebugInfo( _document, start.Line, start.Column, end.Line, end.Column ), AstUtils.Convert(simpleBody, typeof(object)) )); } ReadOnlyCollectionBuilder <MSAst.Expression> block = new ReadOnlyCollectionBuilder <MSAst.Expression>(); AddInitialiation(block); if (_isModule) { block.Add(AssignValue(GetVariableExpression(_docVariable), Ast.Constant(GetDocumentation(_body)))); } if (!(_body is SuiteStatement) && _body.CanThrow) { // we only initialize line numbers in suite statements but if we don't generate a SuiteStatement // at the top level we can miss some line number updates. block.Add(UpdateLineNumber(_body.Start.Line)); } block.Add(_body); MSAst.Expression body = Ast.Block(block.ToReadOnlyCollection()); body = WrapScopeStatements(body, Body.CanThrow); // new ComboActionRewriter().VisitNode(Transform(ag)) body = AddModulePublishing(body); body = AddProfiling(body); if ((((PythonCompilerOptions)_compilerContext.Options).Module & ModuleOptions.LightThrow) != 0) { body = LightExceptions.Rewrite(body); } body = Ast.Label(FunctionDefinition._returnLabel, AstUtils.Convert(body, typeof(object))); if (body.Type == typeof(void)) { body = Ast.Block(body, Ast.Constant(null)); } return(body); }
/// <summary> /// Transform multiple python except handlers for a try block into a single catch body. /// </summary> /// <param name="exception">The variable for the exception in the catch block.</param> /// <returns>Null if there are no except handlers. Else the statement to go inside the catch handler</returns> private MSAst.Expression TransformHandlers(MSAst.ParameterExpression exception) { Assert.NotEmpty(_handlers); MSAst.ParameterExpression extracted = Ast.Variable(typeof(object), "$extracted"); var tests = new List <Microsoft.Scripting.Ast.IfStatementTest>(_handlers.Length); MSAst.ParameterExpression converted = null; MSAst.Expression catchAll = null; for (int index = 0; index < _handlers.Length; index++) { TryStatementHandler tsh = _handlers[index]; if (tsh.Test != null) { Microsoft.Scripting.Ast.IfStatementTest ist; // translating: // except Test ... // // generate following AST for the Test (common part): // CheckException(exception, Test) MSAst.Expression test = Ast.Call( AstMethods.CheckException, Parent.LocalContext, extracted, AstUtils.Convert(tsh.Test, typeof(object)) ); if (tsh.Target != null) { // translating: // except Test, Target: // <body> // into: // if ((converted = CheckException(exception, Test)) != null) { // Target = converted; // traceback-header // <body> // } if (converted == null) { converted = Ast.Variable(typeof(object), "$converted"); } ist = AstUtils.IfCondition( Ast.NotEqual( Ast.Assign(converted, test), AstUtils.Constant(null) ), Ast.Block( tsh.Target.TransformSet(SourceSpan.None, converted, PythonOperationKind.None), GlobalParent.AddDebugInfo( GetTracebackHeader( this, exception, tsh.Body ), new SourceSpan(GlobalParent.IndexToLocation(tsh.StartIndex), GlobalParent.IndexToLocation(tsh.HeaderIndex)) ), AstUtils.Empty() ) ); } else { // translating: // except Test: // <body> // into: // if (CheckException(exception, Test) != null) { // traceback-header // <body> // } ist = AstUtils.IfCondition( Ast.NotEqual( test, AstUtils.Constant(null) ), GlobalParent.AddDebugInfo( GetTracebackHeader( this, exception, tsh.Body ), new SourceSpan(GlobalParent.IndexToLocation(tsh.StartIndex), GlobalParent.IndexToLocation(tsh.HeaderIndex)) ) ); } // Add the test to the if statement test cascade tests.Add(ist); } else { Debug.Assert(index == _handlers.Length - 1); Debug.Assert(catchAll == null); // translating: // except: // <body> // into: // { // traceback-header // <body> // } catchAll = GlobalParent.AddDebugInfo( GetTracebackHeader(this, exception, tsh.Body), new SourceSpan(GlobalParent.IndexToLocation(tsh.StartIndex), GlobalParent.IndexToLocation(tsh.HeaderIndex)) ); } } MSAst.Expression body = null; if (tests.Count > 0) { // rethrow the exception if we have no catch-all block if (catchAll == null) { catchAll = Ast.Block( Parent.GetSaveLineNumberExpression(exception, true), Ast.Throw( Ast.Call( typeof(ExceptionHelpers).GetMethod("UpdateForRethrow"), exception ) ) ); } body = AstUtils.If( tests.ToArray(), catchAll ); } else { Debug.Assert(catchAll != null); body = catchAll; } IList <MSAst.ParameterExpression> args; if (converted != null) { args = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression> { converted, extracted }; } else { args = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression> { extracted }; } // Codegen becomes: // extracted = PythonOps.SetCurrentException(exception) // < dynamic exception analysis > return(Ast.Block( args, Ast.Assign( extracted, Ast.Call( AstMethods.SetCurrentException, Parent.LocalContext, exception ) ), body, Ast.Assign(extracted, Ast.Constant(null)), AstUtils.Empty() )); }
internal override void MakeGetExpression(PythonBinder /*!*/ binder, Expression /*!*/ codeContext, DynamicMetaObject instance, DynamicMetaObject /*!*/ owner, ConditionalBuilder /*!*/ builder) { builder.FinishCondition(Ast.Constant(this)); }
// if (site.Version == <context>.ConstantAccessVersion) { // object value = site.Value; // if (value.GetType() == typeof(WeakReference)) { // if (value == ConstantSiteCache.Missing) { // <result> = ConstantMissing(...); // } else { // <result> = ((WeakReference)value).Target; // } // } else { // <result> = value; // } // } else { // <result> = GetConstant(...); // } private static MSA.Expression /*!*/ MakeCachedRead(AstGenerator /*!*/ gen, int opKind, bool isGlobal, bool isQualified, MSA.Expression /*!*/ name) { object siteCache; MSA.ParameterExpression siteVar, valueVar; FieldInfo versionField, valueField; MSA.Expression readValue; MSA.Expression fallback; if (opKind == OpIsDefined) { siteCache = new IsDefinedConstantSiteCache(); gen.CurrentScope.GetIsDefinedConstantSiteCacheVariables(out siteVar); versionField = Fields.IsDefinedConstantSiteCache_Version; valueField = Fields.IsDefinedConstantSiteCache_Value; readValue = Ast.Field(siteVar, valueField); fallback = (isQualified) ? Methods.IsDefinedQualifiedConstant.OpCall(gen.CurrentScopeVariable, siteVar, name, AstUtils.Constant(isGlobal)) : (isGlobal ? Methods.IsDefinedGlobalConstant : Methods.IsDefinedUnqualifiedConstant). OpCall(gen.CurrentScopeVariable, siteVar, name); } else { siteCache = (ConstantSiteCache) new ConstantSiteCache(); gen.CurrentScope.GetConstantSiteCacheVariables(out siteVar, out valueVar); versionField = Fields.ConstantSiteCache_Version; valueField = Fields.ConstantSiteCache_Value; MSA.Expression weakValue = Ast.Call(Ast.Convert(valueVar, typeof(WeakReference)), Methods.WeakReference_get_Target); if (!isQualified) { weakValue = Ast.Condition( // const missing: Ast.Equal(valueVar, AstUtils.Constant(ConstantSiteCache.WeakMissingConstant)), (isGlobal ? Methods.GetGlobalMissingConstant : Methods.GetMissingConstant). OpCall(gen.CurrentScopeVariable, siteVar, name), // weak value: weakValue ); } readValue = Ast.Condition( Ast.TypeEqual(Ast.Assign(valueVar, Ast.Field(siteVar, valueField)), typeof(WeakReference)), weakValue, valueVar ); fallback = (isQualified ? Methods.GetQualifiedConstant : Methods.GetUnqualifiedConstant). OpCall(gen.CurrentScopeVariable, siteVar, name, AstUtils.Constant(isGlobal)); } return(Ast.Block( Ast.Condition( Ast.Equal( Ast.Field(Ast.Assign(siteVar, Ast.Constant(siteCache)), versionField), Ast.Field(Ast.Constant(gen.Context), Fields.RubyContext_ConstantAccessVersion) ), readValue, fallback ) )); }
private DynamicMetaObject /*!*/ MakeMemberAccess(DynamicMetaObjectBinder /*!*/ member, string name, MemberAccess access, params DynamicMetaObject /*!*/[] /*!*/ args) { DynamicMetaObject self = Restrict(typeof(OldInstance)); CustomInstanceDictionaryStorage dict; int key = GetCustomStorageSlot(name, out dict); if (key == -1) { PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldInstance " + access + " NoOptimized"); return(MakeDynamicMemberAccess(member, name, access, args)); } ParameterExpression tmp = Ast.Variable(typeof(object), "dict"); Expression target; ValidationInfo test = new ValidationInfo( Ast.NotEqual( Ast.Assign( tmp, Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceGetOptimizedDictionary)), self.Expression, AstUtils.Constant(dict.KeyVersion) ) ), AstUtils.Constant(null) ) ); PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldInstance " + access + " Optimized"); switch (access) { case MemberAccess.Invoke: ParameterExpression value = Ast.Variable(typeof(object), "value"); target = Ast.Block( new[] { value }, Ast.Condition( Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.TryOldInstanceDictionaryGetValueHelper)), tmp, Ast.Constant(key), AstUtils.Convert(Expression, typeof(object)), value ), AstUtils.Convert( ((InvokeMemberBinder)member).FallbackInvoke(new DynamicMetaObject(value, BindingRestrictions.Empty), args, null).Expression, typeof(object) ), AstUtils.Convert( ((InvokeMemberBinder)member).FallbackInvokeMember(self, args).Expression, typeof(object) ) ) ); break; case MemberAccess.Get: // BUG: There's a missing Fallback path here that's always been present even // in the version that used rules. target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceDictionaryGetValueHelper)), tmp, AstUtils.Constant(key), AstUtils.Convert(Expression, typeof(object)) ); break; case MemberAccess.Set: target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceDictionarySetExtraValue)), tmp, AstUtils.Constant(key), AstUtils.Convert(args[1].Expression, typeof(object)) ); break; case MemberAccess.Delete: target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceDeleteCustomMember)), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), AstUtils.Convert(Expression, typeof(OldInstance)), AstUtils.Constant(name) ); break; default: throw new InvalidOperationException(); } return(BindingHelpers.AddDynamicTestAndDefer( member, new DynamicMetaObject( target, BindingRestrictions.Combine(args).Merge(self.Restrictions) ), args, test, tmp )); }
// TODO: // In Ruby 1.9 encoding of ASCII symbols is always BINARY (unlike strings). public MSA.Expression /*!*/ CreateExpression(AstGenerator /*!*/ gen, string /*!*/ literal) { return(Ast.Constant(gen.Context.CreateSymbol(literal, gen.Encoding))); }
/// <summary> /// Adds a try/finally which enforces recursion limits around the target method. /// </summary> internal static Expression AddRecursionCheck(PythonContext pyContext, Expression expr) { ParameterExpression tmp = Ast.Variable(expr.Type, "callres"); expr = Ast.Block( new [] { tmp }, AstUtils.Try( Ast.Call(typeof(PythonOps).GetMethod(nameof(PythonOps.FunctionPushFrame)), Ast.Constant(pyContext)), Ast.Assign(tmp, expr) ).Finally( Ast.Call(typeof(PythonOps).GetMethod(nameof(PythonOps.FunctionPopFrame))) ), tmp ); return(expr); }
internal MSA.LambdaExpression /*!*/ TransformBody(AstGenerator /*!*/ gen, RubyScope /*!*/ declaringScope, RubyModule /*!*/ declaringModule) { string encodedName = RubyStackTraceBuilder.EncodeMethodName(_name, gen.SourcePath, Location, gen.DebugMode); AstParameters parameters; ScopeBuilder scope = DefineLocals(out parameters); var scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyMethodScope)); var selfParameter = parameters[0]; var blockParameter = parameters[1]; // exclude block parameter even if it is explicitly specified: int visiblePrameterCountAndSignatureFlags = (parameters.Count - 2) << 2; if (_parameters.Block != null) { visiblePrameterCountAndSignatureFlags |= RubyMethodScope.HasBlockFlag; } if (_parameters.Array != null) { visiblePrameterCountAndSignatureFlags |= RubyMethodScope.HasUnsplatFlag; } gen.EnterMethodDefinition( scope, selfParameter, scopeVariable, blockParameter, _name, _parameters ); // profiling: MSA.Expression profileStart, profileEnd; if (gen.Profiler != null) { int profileTickIndex = gen.Profiler.GetTickIndex(encodedName); var stampVariable = scope.DefineHiddenVariable("#stamp", typeof(long)); profileStart = Ast.Assign(stampVariable, Methods.Stopwatch_GetTimestamp.OpCall()); profileEnd = Methods.UpdateProfileTicks.OpCall(AstUtils.Constant(profileTickIndex), stampVariable); } else { profileStart = profileEnd = AstUtils.Empty(); } // tracing: MSA.Expression traceCall, traceReturn; if (gen.TraceEnabled) { traceCall = Methods.TraceMethodCall.OpCall( scopeVariable, gen.SourcePathConstant, AstUtils.Constant(Location.Start.Line) ); traceReturn = Methods.TraceMethodReturn.OpCall( gen.CurrentScopeVariable, gen.SourcePathConstant, AstUtils.Constant(Location.End.Line) ); } else { traceCall = traceReturn = AstUtils.Empty(); } MSA.ParameterExpression unwinder; MSA.Expression body = AstUtils.Try( profileStart, _parameters.TransformOptionalsInitialization(gen), traceCall, Body.TransformResult(gen, ResultOperation.Return) ).Filter(unwinder = Ast.Parameter(typeof(Exception), "#u"), Methods.IsMethodUnwinderTargetFrame.OpCall(scopeVariable, unwinder), Ast.Return(gen.ReturnLabel, Methods.GetMethodUnwinderReturnValue.OpCall(unwinder)) ).Finally( // leave frame: Methods.LeaveMethodFrame.OpCall(scopeVariable), Ast.Empty(), profileEnd, traceReturn ); body = gen.AddReturnTarget( scope.CreateScope( scopeVariable, Methods.CreateMethodScope.OpCall(new AstExpressions { scope.MakeLocalsStorage(), scope.GetVariableNamesExpression(), Ast.Constant(visiblePrameterCountAndSignatureFlags), Ast.Constant(declaringScope, typeof(RubyScope)), Ast.Constant(declaringModule, typeof(RubyModule)), Ast.Constant(_name), selfParameter, blockParameter, EnterInterpretedFrameExpression.Instance }), body ) ); gen.LeaveMethodDefinition(); return(CreateLambda(encodedName, parameters, body)); }
public MSA.Expression /*!*/ CreateExpression(AstGenerator /*!*/ gen, string /*!*/ literal) { return(Methods.CreateMutableStringL.OpCall(Ast.Constant(literal), gen.EncodingConstant)); }
public static Expression FxOdd(params Expression[] args) { if (Expect <int>(args, 1)) { return(Ast.Equal(Ast.Add(UnwrapAndCast <int>(args[0]), Ast.Constant(1)), Ast.Constant(1))); } return(null); }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { return(Methods.CreateSymbolB.OpCall(Ast.Constant(_value))); }
/// <summary> /// Creates the LambdaExpression which implements the body of the function. /// /// The functions signature is either "object Function(PythonFunction, ...)" /// where there is one object parameter for each user defined parameter or /// object Function(PythonFunction, object[]) for functions which take more /// than PythonCallTargets.MaxArgs arguments. /// </summary> private LightLambdaExpression CreateFunctionLambda() { bool needsWrapperMethod = _parameters.Length > PythonCallTargets.MaxArgs; Delegate originalDelegate; Type delegateType = GetDelegateType(_parameters, needsWrapperMethod, out originalDelegate); MSAst.ParameterExpression localContext = null; ReadOnlyCollectionBuilder <MSAst.ParameterExpression> locals = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression>(); if (NeedsLocalContext) { localContext = LocalCodeContextVariable; locals.Add(localContext); } MSAst.ParameterExpression[] parameters = CreateParameters(needsWrapperMethod, locals); List <MSAst.Expression> init = new List <MSAst.Expression>(); foreach (var param in _parameters) { IPythonVariableExpression pyVar = GetVariableExpression(param.PythonVariable) as IPythonVariableExpression; if (pyVar != null) { var varInit = pyVar.Create(); if (varInit != null) { init.Add(varInit); } } } // Transform the parameters. init.Add(Ast.ClearDebugInfo(GlobalParent.Document)); locals.Add(PythonAst._globalContext); init.Add(Ast.Assign(PythonAst._globalContext, new GetGlobalContextExpression(_parentContext))); GlobalParent.PrepareScope(locals, init); // Create variables and references. Since references refer to // parameters, do this after parameters have been created. CreateFunctionVariables(locals, init); // If the __class__ variable is used the a class method then we need to initialize it. // This must be done before parameter initialization (in case one of the parameters is called __class__). ClassDefinition parent = FindParentOfType <ClassDefinition>(); PythonVariable pVar; if (parent != null && TryGetVariable("__class__", out pVar)) { init.Add( AssignValue( GetVariableExpression(pVar), Ast.Call(AstMethods.LookupName, parent.Parent.LocalContext, Ast.Constant(parent.Name)) ) ); } // Initialize parameters - unpack tuples. // Since tuples unpack into locals, this must be done after locals have been created. InitializeParameters(init, needsWrapperMethod, parameters); List <MSAst.Expression> statements = new List <MSAst.Expression>(); // add beginning sequence point var start = GlobalParent.IndexToLocation(StartIndex); statements.Add(GlobalParent.AddDebugInfo( AstUtils.Empty(), new SourceSpan(new SourceLocation(0, start.Line, start.Column), new SourceLocation(0, start.Line, Int32.MaxValue)))); // For generators, we need to do a check before the first statement for Generator.Throw() / Generator.Close(). // The exception traceback needs to come from the generator's method body, and so we must do the check and throw // from inside the generator. if (IsGenerator) { MSAst.Expression s1 = YieldExpression.CreateCheckThrowExpression(SourceSpan.None); statements.Add(s1); } MSAst.ParameterExpression extracted = null; if (!IsGenerator && _canSetSysExcInfo) { // need to allocate the exception here so we don't share w/ exceptions made & freed // during the body. extracted = Ast.Parameter(typeof(Exception), "$ex"); locals.Add(extracted); } if (_body.CanThrow && !(_body is SuiteStatement) && _body.StartIndex != -1) { statements.Add(UpdateLineNumber(GlobalParent.IndexToLocation(_body.StartIndex).Line)); } statements.Add(Body); MSAst.Expression body = Ast.Block(statements); // If this function can modify sys.exc_info() (_canSetSysExcInfo), then it must restore the result on finish. // // Wrap in // $temp = PythonOps.SaveCurrentException() // <body> // PythonOps.RestoreCurrentException($temp) // Skip this if we're a generator. For generators, the try finally is handled by the PythonGenerator class // before it's invoked. This is because the restoration must occur at every place the function returns from // a yield point. That's different than the finally semantics in a generator. if (extracted != null) { MSAst.Expression s = AstUtils.Try( Ast.Assign( extracted, Ast.Call(AstMethods.SaveCurrentException) ), body ).Finally( Ast.Call( AstMethods.RestoreCurrentException, extracted ) ); body = s; } if (_body.CanThrow && GlobalParent.PyContext.PythonOptions.Frames) { body = AddFrame(LocalContext, Ast.Property(_functionParam, typeof(PythonFunction).GetProperty("__code__")), body); locals.Add(FunctionStackVariable); } body = AddProfiling(body); body = WrapScopeStatements(body, _body.CanThrow); body = Ast.Block(body, AstUtils.Empty()); body = AddReturnTarget(body); MSAst.Expression bodyStmt = body; if (localContext != null) { var createLocal = CreateLocalContext(_parentContext); init.Add( Ast.Assign( localContext, createLocal ) ); } init.Add(bodyStmt); bodyStmt = Ast.Block(init); // wrap a scope if needed bodyStmt = Ast.Block(locals.ToReadOnlyCollection(), bodyStmt); return(AstUtils.LightLambda( typeof(object), delegateType, AddDefaultReturn(bodyStmt, typeof(object)), Name + "$" + Interlocked.Increment(ref _lambdaId), parameters )); }
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)); }
internal protected override Expression ToExpression(OverloadResolver resolver, RestrictedArguments args, bool[] hasBeenUsed) { var actualArgs = resolver.GetActualArguments(); int splatIndex = actualArgs.SplatIndex; int collapsedCount = actualArgs.CollapsedCount; int firstSplatted = actualArgs.FirstSplattedArg; var result = new Expression[2 + _expandedCount + (collapsedCount > 0 ? 2 : 0)]; var arrayVariable = resolver.GetTemporary(_elementType.MakeArrayType(), "a"); int e = 0; result[e++] = Ast.Assign(arrayVariable, Ast.NewArrayBounds(_elementType, Ast.Constant(_expandedCount + collapsedCount))); int itemIndex = 0; int i = _start; while (true) { // inject loop copying collapsed items: if (i == splatIndex) { var indexVariable = resolver.GetTemporary(typeof(int), "t"); // for (int t = 0; t <= {collapsedCount}; t++) { // a[{itemIndex} + t] = CONVERT<ElementType>(list.get_Item({splatIndex - firstSplatted} + t)) // } result[e++] = Ast.Assign(indexVariable, AstUtils.Constant(0)); result[e++] = AstUtils.Loop( Ast.LessThan(indexVariable, Ast.Constant(collapsedCount)), // TODO: not implemented in the old interpreter // Ast.PostIncrementAssign(indexVariable), Ast.Assign(indexVariable, Ast.Add(indexVariable, AstUtils.Constant(1))), Ast.Assign( Ast.ArrayAccess(arrayVariable, Ast.Add(AstUtils.Constant(itemIndex), indexVariable)), resolver.Convert( new DynamicMetaObject( resolver.GetSplattedItemExpression(Ast.Add(AstUtils.Constant(splatIndex - firstSplatted), indexVariable)), BindingRestrictions.Empty ), null, ParameterInfo, _elementType ) ), null ); itemIndex += collapsedCount; } if (i >= _start + _expandedCount) { break; } Debug.Assert(!hasBeenUsed[i]); hasBeenUsed[i] = true; result[e++] = Ast.Assign( Ast.ArrayAccess(arrayVariable, AstUtils.Constant(itemIndex++)), resolver.Convert(args.GetObject(i), args.GetType(i), ParameterInfo, _elementType) ); i++; } result[e++] = arrayVariable; Debug.Assert(e == result.Length); return(Ast.Block(result)); }
internal static void BuildConversion(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, Type /*!*/ resultType, params ProtocolConversionAction /*!*/[] /*!*/ conversions) { Assert.NotNull(metaBuilder, args, conversions); Debug.Assert(args.SimpleArgumentCount == 0 && !args.Signature.HasBlock && !args.Signature.HasSplattedArgument && !args.Signature.HasRhsArgument); Debug.Assert(!args.Signature.HasScope); // implicit conversions should only depend on the static type: foreach (var conversion in conversions) { if (conversion.TryImplicitConversion(metaBuilder, args)) { metaBuilder.AddObjectTypeRestriction(args.Target, args.TargetExpression); if (!metaBuilder.Error) { metaBuilder.Result = ConvertResult(metaBuilder.Result, resultType); } return; } } RubyClass targetClass = args.RubyContext.GetImmediateClassOf(args.Target); Expression targetClassNameConstant = AstUtils.Constant(targetClass.GetNonSingletonClass().Name, typeof(string)); MethodResolutionResult respondToMethod, methodMissing = MethodResolutionResult.NotFound; ProtocolConversionAction selectedConversion = null; RubyMemberInfo conversionMethod = null; using (targetClass.Context.ClassHierarchyLocker()) { // check for type version: metaBuilder.AddTargetTypeTest(args.Target, targetClass, args.TargetExpression, args.MetaContext, ArrayUtils.Insert(Symbols.RespondTo, Symbols.MethodMissing, ArrayUtils.ConvertAll(conversions, (c) => c.ToMethodName)) ); // we can optimize if Kernel#respond_to? method is not overridden: respondToMethod = targetClass.ResolveMethodForSiteNoLock(Symbols.RespondTo, VisibilityContext.AllVisible); if (respondToMethod.Found && respondToMethod.Info.DeclaringModule == targetClass.Context.KernelModule && respondToMethod.Info is RubyLibraryMethodInfo) // TODO: better override detection { respondToMethod = MethodResolutionResult.NotFound; // get the first applicable conversion: foreach (var conversion in conversions) { selectedConversion = conversion; conversionMethod = targetClass.ResolveMethodForSiteNoLock(conversion.ToMethodName, VisibilityContext.AllVisible).Info; if (conversionMethod != null) { break; } else { // find method_missing - we need to add "to_xxx" methods to the missing methods table: if (!methodMissing.Found) { methodMissing = targetClass.ResolveMethodNoLock(Symbols.MethodMissing, VisibilityContext.AllVisible); } methodMissing.InvalidateSitesOnMissingMethodAddition(conversion.ToMethodName, targetClass.Context); } } } } if (!respondToMethod.Found) { if (conversionMethod == null) { // error: selectedConversion.SetError(metaBuilder, args, targetClassNameConstant, resultType); return; } else { // invoke target.to_xxx() and validate it; returns an instance of TTargetType: conversionMethod.BuildCall(metaBuilder, args, selectedConversion.ToMethodName); if (!metaBuilder.Error) { metaBuilder.Result = ConvertResult( selectedConversion.MakeValidatorCall(args, targetClassNameConstant, metaBuilder.Result), resultType ); } return; } } // slow path: invoke respond_to?, to_xxx and result validation: for (int i = conversions.Length - 1; i >= 0; i--) { string toMethodName = conversions[i].ToMethodName; var conversionCallSite = AstUtils.LightDynamic( RubyCallAction.Make(args.RubyContext, toMethodName, RubyCallSignature.WithImplicitSelf(0)), args.TargetExpression ); metaBuilder.Result = Ast.Condition( // If // respond_to?() Methods.IsTrue.OpCall( AstUtils.LightDynamic( RubyCallAction.Make(args.RubyContext, Symbols.RespondTo, RubyCallSignature.WithImplicitSelf(1)), args.TargetExpression, Ast.Constant(args.RubyContext.CreateSymbol(toMethodName, RubyEncoding.Binary)) ) ), // Then // to_xxx(): ConvertResult( conversions[i].MakeValidatorCall(args, targetClassNameConstant, conversionCallSite), resultType ), // Else (i < conversions.Length - 1) ? metaBuilder.Result : conversions[i].MakeErrorExpression(args, targetClassNameConstant, resultType) ); } }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { return(Ast.Constant(gen.Context.CreateSymbol((string)Value, gen.Encoding))); }
protected static Expression MakeClosure(CodeBlock cb, bool varargs, bool typed) { if (varargs) { return(Ast.SimpleCallHelper(Closure_MakeVarArgsX, Ast.CodeBlockExpression(cb, false, typed), Ast.Constant(cb.ParameterCount))); } else { return(Ast.SimpleCallHelper(Closure_Make, Ast.CodeBlockExpression(cb, false, typed), Ast.Constant(cb.ParameterCount))); } }
// Conversion to a delegate. // Explicit: convertible to any delegate type. // Implicit: convertible to a delegate type of the same arity as the block. public Convertibility IsConvertibleTo(Type /*!*/ type, bool @explicit) { if (!typeof(Delegate).IsAssignableFrom(type)) { return(Convertibility.NotConvertible); } if (@explicit) { return(Convertibility.AlwaysConvertible); } if (!HasValue) { return(Convertibility.NotConvertible); } MethodInfo invoke = type.GetMethod("Invoke"); if (invoke == null) { return(Convertibility.NotConvertible); } int delegateArity = invoke.GetParameters().Length; return(new Convertibility( delegateArity == Value.Dispatcher.ParameterCount || delegateArity > Value.Dispatcher.ParameterCount && Value.Dispatcher.HasUnsplatParameter, Ast.Equal(Methods.GetProcArity.OpCall(AstUtils.Convert(Expression, typeof(Proc))), Ast.Constant(Value.Dispatcher.Arity)) )); }
internal override void BuildSuperCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name, RubyModule /*!*/ declaringModule) { Assert.NotNull(declaringModule, metaBuilder, args); var visibleOverloads = GetVisibleOverloads(args, MethodBases, true); if (visibleOverloads.Count == 0) { metaBuilder.SetError(Methods.MakeClrVirtualMethodCalledError.OpCall( args.MetaContext.Expression, args.MetaTarget.Expression, Ast.Constant(name) )); } else { BuildCallNoFlow(metaBuilder, args, name, visibleOverloads, CallConvention, ImplicitProtocolConversions); } }
internal static MSA.Expression /*!*/ MakeUserMethodBody(AstGenerator gen, int lastLine, MSA.Expression /*!*/ blockParameter, MSA.Expression /*!*/ rfcVariable, MSA.ParameterExpression /*!*/ methodUnwinder, MSA.Expression /*!*/ bodyStatement, ResultOperation resultOperation, int profileTickIndex, MSA.ParameterExpression stampVariable, MSA.LabelTarget returnLabel) { Assert.NotNull(blockParameter, rfcVariable, bodyStatement, methodUnwinder); Debug.Assert(!resultOperation.IsIgnore, "return value should not be ignored"); Debug.Assert(returnLabel != null || resultOperation.Variable != null, "return label needed"); MSA.Expression resultExpression = Ast.Field(methodUnwinder, MethodUnwinder.ReturnValueField); if (resultOperation.Variable != null) { resultExpression = Ast.Assign(resultOperation.Variable, resultExpression); } else { resultExpression = Ast.Return(returnLabel, resultExpression); } // TODO: move this to the caller: MSA.Expression profileStart, profileEnd; if (stampVariable != null) { profileStart = Ast.Assign(stampVariable, Methods.Stopwatch_GetTimestamp.OpCall()); profileEnd = Methods.UpdateProfileTicks.OpCall(Ast.Constant(profileTickIndex), stampVariable); } else { profileStart = profileEnd = Ast.Empty(); } return(AstUtils.Try( // initialize frame (RFC): profileStart, Ast.Assign(rfcVariable, Methods.CreateRfcForMethod.OpCall(AstUtils.Convert(blockParameter, typeof(Proc)))), bodyStatement ).Filter(methodUnwinder, Ast.Equal(Ast.Field(methodUnwinder, MethodUnwinder.TargetFrameField), rfcVariable), // return unwinder.ReturnValue; resultExpression ).Finally( Ast.Assign(Ast.Field(rfcVariable, RuntimeFlowControl.IsActiveMethodField), Ast.Constant(false)), profileEnd, gen != null && gen.TraceEnabled ? Methods.TraceMethodReturn.OpCall( gen.CurrentScopeVariable, Ast.Convert(Ast.Constant(gen.SourceUnit.Path), typeof(string)), Ast.Constant(lastLine) ) : Ast.Empty() )); }
public override MSAst.Expression Reduce() { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression lineUpdated = null; MSAst.ParameterExpression runElse = null; if (_else != null || (_handlers != null && _handlers.Length > 0)) { lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_try"); if (_else != null) { runElse = Ast.Variable(typeof(bool), "run_else"); } } // don't allocate locals below here... MSAst.Expression body = _body; MSAst.Expression @else = _else; MSAst.Expression @catch, result; MSAst.ParameterExpression exception; if (_handlers != null && _handlers.Length > 0) { exception = Ast.Variable(typeof(Exception), "$exception"); @catch = TransformHandlers(exception); } else { exception = null; @catch = null; } // We have else clause, must generate guard around it if (@else != null) { Debug.Assert(@catch != null); // run_else = true; // try { // try_body // } catch ( ... ) { // run_else = false; // catch_body // } // if (run_else) { // else_body // } result = Ast.Block( Ast.Assign(runElse, AstUtils.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. PushLineUpdated(false, lineUpdated), LightExceptions.RewriteExternal( AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(_headerIndex))), body, AstUtils.Constant(null) ).Catch(exception, Ast.Assign(runElse, AstUtils.Constant(false)), @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ), AstUtils.IfThen(runElse, @else ), AstUtils.Empty() ); } else if (@catch != null) // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // { result = LightExceptions.RewriteExternal( AstUtils.Try( GlobalParent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(_headerIndex))), // save existing line updated PushLineUpdated(false, lineUpdated), body, AstUtils.Constant(null) ).Catch(exception, @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Call(AstMethods.ExceptionHandled, Parent.LocalContext), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ); } else { result = body; } return(Ast.Block( GetVariables(lineUpdated, runElse), AddFinally(result), AstUtils.Default(typeof(void)) )); }
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) )); } }
internal static DynamicMetaObject Call(DynamicMetaObjectBinder /*!*/ call, DynamicMetaObject target, DynamicMetaObject /*!*/[] /*!*/ args) { Assert.NotNull(call, args); Assert.NotNullItems(args); if (target.NeedsDeferral()) { return(call.Defer(ArrayUtils.Insert(target, args))); } foreach (DynamicMetaObject mo in args) { if (mo.NeedsDeferral()) { RestrictTypes(args); return(call.Defer( ArrayUtils.Insert(target, args) )); } } DynamicMetaObject self = target.Restrict(target.GetLimitType()); ValidationInfo valInfo = BindingHelpers.GetValidationInfo(target); PythonType pt = DynamicHelpers.GetPythonType(target.Value); PythonContext pyContext = PythonContext.GetPythonContext(call); // look for __call__, if it's present dispatch to it. Otherwise fall back to the // default binder PythonTypeSlot callSlot; if (!typeof(Delegate).IsAssignableFrom(target.GetLimitType()) && pt.TryResolveSlot(pyContext.SharedContext, "__call__", out callSlot)) { ConditionalBuilder cb = new ConditionalBuilder(call); Expression body; callSlot.MakeGetExpression( pyContext.Binder, PythonContext.GetCodeContext(call), self, GetPythonType(self), cb ); if (!cb.IsFinal) { cb.FinishCondition(GetCallError(call, self)); } Expression[] callArgs = ArrayUtils.Insert( PythonContext.GetCodeContext(call), cb.GetMetaObject().Expression, DynamicUtils.GetExpressions(args) ); body = DynamicExpression.Dynamic( PythonContext.GetPythonContext(call).Invoke( BindingHelpers.GetCallSignature(call) ), typeof(object), callArgs ); body = Ast.TryFinally( Ast.Block( Ast.Call(typeof(PythonOps).GetMethod(nameof(PythonOps.FunctionPushFrame)), Ast.Constant(pyContext)), body ), Ast.Call(typeof(PythonOps).GetMethod(nameof(PythonOps.FunctionPopFrame))) ); return(BindingHelpers.AddDynamicTestAndDefer( call, new DynamicMetaObject(body, self.Restrictions.Merge(BindingRestrictions.Combine(args))), args, valInfo )); } return(null); }
protected internal static Expression GetAst(object args, CodeBlock cb, bool istailposition) { var spanHint = SpanHint; if (args is Annotation) { args = ((Annotation)args).stripped; } Cons c = args as Cons; if (c != null) { // in debug mode, this will be (annotated-call <location> . symbol) // TODO: see if the discarded annotation is useful if (ScriptDomainManager.Options.DebugMode && c.car is Cons) { Cons ac = c.car as Cons; if (ac.car == SymbolTable.StringToObject("annotated-call")) { object se = ((Cons)ac.cdr).cdr; if (se is SymbolId) { c.car = se; } } } if (c.car is SymbolId) { SymbolId f = (SymbolId)c.car; Variable var = cb.Lookup(f); if (var != null && !assigns.ContainsKey(f)) { var = null; } object m; #if OPTIMIZATIONS #if !BLAH CodeBlockExpression cbe; //// needs to do the same for overloads... if (SimpleGenerator.libraryglobals.TryGetValue(f, out cbe)) { Expression[] ppp = GetAstListNoCast(c.cdr as Cons, cb); if (cbe.Block.ParameterCount < 9 && cbe.Block.ParameterCount == ppp.Length) { //inline here? we could for simple bodies, but we need to copy the entire structure if (!(cbe.Block.HasEnvironment || cbe.Block.IsClosure)) { if (cbe.Block.Body is ReturnStatement) { ReturnStatement rs = (ReturnStatement)cbe.Block.Body; if (!cb.IsGlobal && IsSimpleExpression(rs.Expression)) { return(InlineCall(cb, Ast.CodeBlockExpression(RewriteBody(cbe.Block), false, cbe.IsStronglyTyped), ppp)); } } } if (cbe.Block != cb.Parent && cbe.Block != cb) // do TCE later { return(CallNormal(cbe, ppp)); } } } // varargs if (SimpleGenerator.libraryglobalsX.TryGetValue(f, out cbe)) { Expression[] ppp = GetAstListNoCast(c.cdr as Cons, cb); if (cbe.Block.ParameterCount < 9 && cbe.Block.ParameterCount - 1 <= ppp.Length) { //inline here? return(CallVarArgs(cbe, ppp)); } } // overloads CodeBlockDescriptor[] cbd; if (SimpleGenerator.libraryglobalsN.TryGetValue(f, out cbd)) { Expression[] ppp = GetAstListNoCast(c.cdr as Cons, cb); foreach (CodeBlockDescriptor d in cbd) { if (d.codeblock.Block.ParameterCount < 9) { if (ppp.Length == d.arity || (d.varargs && ppp.Length > d.arity)) { if (d.varargs) { //inline here? return(CallVarArgs(d.codeblock, ppp)); } else { //inline here? //if (d.codeblock.Block != cb.Parent && d.codeblock.Block != cb) // do TCE later, not yet { return(CallNormal(d.codeblock, ppp)); } } } } } } #endif //if (!ScriptDomainManager.Options.DebugMode) { if (f == SymbolTable.StringToId("call-with-values")) { Expression[] ppp = GetAstListNoCast(c.cdr as Cons, cb); if (ppp.Length == 2 && ppp[1] is MethodCallExpression) { MethodCallExpression consumer = ppp[1] as MethodCallExpression; if (ppp[0] is MethodCallExpression) { MethodCallExpression producer = ppp[0] as MethodCallExpression; if (consumer.Method == Closure_Make && producer.Method == Closure_Make) { CodeBlockExpression ccbe = consumer.Arguments[0] as CodeBlockExpression; CodeBlockExpression pcbe = producer.Arguments[0] as CodeBlockExpression; pcbe.Block.Bind(); ccbe.Block.Bind(); if (ccbe.Block.ParameterCount == 0) { return(InlineCall(cb, ccbe)); } else if (ccbe.Block.ParameterCount == 1) { return(InlineCall(cb, ccbe, Ast.SimpleCallHelper(typeof(Helpers).GetMethod("UnwrapValue"), InlineCall(cb, pcbe)))); } else { Variable values = cb.CreateTemporaryVariable((SymbolId)Builtins.GenSym("values"), typeof(object[])); Expression valuesarr = Ast.Read(values); Expression[] pppp = new Expression[ccbe.Block.ParameterCount]; for (int i = 0; i < pppp.Length; i++) { pppp[i] = Ast.ArrayIndex(valuesarr, Ast.Constant(i)); } return(Ast.Comma( Ast.Void( Ast.Write( values, Ast.ComplexCallHelper( Ast.SimpleCallHelper(typeof(Helpers).GetMethod("WrapValue"), InlineCall(cb, pcbe)), typeof(MultipleValues).GetMethod("ToArray", new Type[] { typeof(int) }), Ast.Constant(pppp.Length)))), InlineCall(cb, ccbe, pppp))); } } } if (consumer.Method == Closure_Make) { CodeBlockExpression ccbe = consumer.Arguments[0] as CodeBlockExpression; ccbe.Block.Bind(); Expression producer = ppp[0]; Expression exx = Ast.ConvertHelper(producer, typeof(Callable)); MethodInfo callx = GetCallable(0); if (ccbe.Block.ParameterCount == 0) { return(InlineCall(cb, ccbe)); } else if (ccbe.Block.ParameterCount == 1) { return(InlineCall(cb, ccbe, Ast.SimpleCallHelper(typeof(Helpers).GetMethod("UnwrapValue"), Ast.Call(exx, callx)))); } else { Variable values = cb.CreateTemporaryVariable((SymbolId)Builtins.GenSym("values"), typeof(object[])); Expression valuesarr = Ast.Read(values); Expression[] pppp = new Expression[ccbe.Block.ParameterCount]; for (int i = 0; i < pppp.Length; i++) { pppp[i] = Ast.ArrayIndex(valuesarr, Ast.Constant(i)); } return(Ast.Comma( Ast.Void( Ast.Write( values, Ast.ComplexCallHelper( Ast.SimpleCallHelper(typeof(Helpers).GetMethod("WrapValue"), Ast.Call(exx, callx)), typeof(MultipleValues).GetMethod("ToArray", new Type[] { typeof(int) }), Ast.Constant(pppp.Length)))), InlineCall(cb, ccbe, pppp))); } } } else { ; } } } #endif // this can be enabled once builtins are auto CPS'd. // ok I tried, but there are issues still, not sure what #if OPTIMIZATIONS // check for inline emitter InlineEmitter ie; if (TryGetInlineEmitter(f, out ie)) { Expression result = ie(GetAstList(c.cdr as Cons, cb)); // if null is returned, the method cannot be inlined if (result != null) { if (spanHint.IsValid) { result.SetLoc(spanHint); } return(result); } } #endif if (Context.Scope.TryLookupName(f, out m)) { if (var == null) { IGenerator gh = m as IGenerator; if (gh != null) { return(gh.Generate(c.cdr, cb)); } BuiltinMethod bf = m as BuiltinMethod; if (bf != null) { MethodBinder mb = bf.Binder; Expression[] pars = Array.ConvertAll(GetAstList(c.cdr as Cons, cb), e => Unwrap(e)); if (bf.AllowConstantFold && !ScriptDomainManager.Options.DebugMode) { bool constant = Array.TrueForAll(pars, e => e is ConstantExpression && e.Type != typeof(BigInteger)); if (constant) { object[] cargs = Array.ConvertAll(pars, e => GetRuntimeConstant((ConstantExpression)e)); CallTarget0 disp = delegate { return(bf.Call(cargs)); }; CallTarget1 handler = delegate(object e) { throw new CompileTimeEvaluationException(); }; try { object result = Runtime.R6RS.Exceptions.WithExceptionHandler( Closure.Create(handler), Closure.Create(disp)); var rrrr = GetCons(result, cb); if (spanHint.IsValid) { rrrr.SetLoc(spanHint); } return(rrrr); } catch (CompileTimeEvaluationException) { } } } Type[] types = GetExpressionTypes(pars); MethodCandidate mc = mb.MakeBindingTarget(CallType.None, types); if (mc != null) { if (mc.Target.NeedsContext) { pars = ArrayUtils.Insert <Expression>(Ast.CodeContext(), pars); } MethodBase meth = mc.Target.Method; var rrrr = Ast.ComplexCallHelper(meth as MethodInfo, pars); if (spanHint.IsValid) { rrrr.SetLoc(spanHint); } return(rrrr); } } Closure clos = m as Closure; if (clos != null && !SetGenerator.IsAssigned(f)) { // no provision for varargs MethodInfo[] mis = clos.Targets; if (mis.Length > 0) { MethodBinder mb = MethodBinder.MakeBinder(binder, SymbolTable.IdToString(f), mis, BinderType.Normal); Expression[] pars = Array.ConvertAll(GetAstList(c.cdr as Cons, cb), e => Unwrap(e)); if (clos.AllowConstantFold && !ScriptDomainManager.Options.DebugMode) { bool constant = Array.TrueForAll(pars, e => e is ConstantExpression && e.Type != typeof(BigInteger)); if (constant) { object[] cargs = Array.ConvertAll(pars, e => GetRuntimeConstant((ConstantExpression)e)); CallTarget0 disp = delegate { var rrrr = clos.Call(cargs); return(rrrr); }; CallTarget1 handler = delegate(object e) { throw new CompileTimeEvaluationException(); }; try { object result = Runtime.R6RS.Exceptions.WithExceptionHandler( Closure.Create(handler), Closure.Create(disp)); var rrrr = GetCons(result, cb); if (spanHint.IsValid) { rrrr.SetLoc(spanHint); } return(rrrr); } catch (CompileTimeEvaluationException) { } } } // exclude transient members if needed if (!AllowTransientBinding) { mis = Array.FindAll(mis, x => !IsTransient(x.Module)); } if (mis.Length > 0) { Type[] types = GetExpressionTypes(pars); MethodCandidate mc = mb.MakeBindingTarget(CallType.None, types); if (mc != null) { if (mc.Target.NeedsContext) { pars = ArrayUtils.Insert <Expression>(Ast.CodeContext(), pars); } MethodBase meth = mc.Target.Method; var rrrr = Ast.ComplexCallHelper(meth as MethodInfo, pars); if (spanHint.IsValid) { rrrr.SetLoc(spanHint); } return(rrrr); } } } // check for overload thing } } } } Expression ex = Unwrap(GetAst(c.car, cb)); // a 'let' if (ex is MethodCallExpression) { var ppp = GetAstList(c.cdr as Cons, cb); MethodCallExpression mcexpr = (MethodCallExpression)ex; if (mcexpr.Method == Closure_Make) { CodeBlockExpression cbe = mcexpr.Arguments[0] as CodeBlockExpression; if (ppp.Length < 9 && cbe.Block.ParameterCount == ppp.Length) { return(InlineCall(cb, cbe, istailposition, ppp)); } } // cater for varargs more efficiently, this does not seem to hit, probably needed somewhere else, hits in ironscheme-test and cut test if (mcexpr.Method == Closure_MakeVarArgsX) { CodeBlockExpression cbe = mcexpr.Arguments[0] as CodeBlockExpression; if (ppp.Length < 9 && cbe.Block.ParameterCount <= ppp.Length && !NeedsContext(cbe)) { // TODO: See why having a context fails here return(CallVarArgs(cbe, ppp)); } } } if (ex is NewExpression && typeof(ITypedCallable).IsAssignableFrom(ex.Type)) { NewExpression mcexpr = ex as NewExpression; CodeBlockExpression cbe = mcexpr.Arguments[0] as CodeBlockExpression; if (cbe == null && mcexpr.Arguments[0].Type == typeof(CodeContext) && mcexpr.Arguments[0] is ConstantExpression) // implies null { cbe = mcexpr.Arguments[1] as CodeBlockExpression; } if (cbe != null) { var ppp = GetAstListNoCast(c.cdr as Cons, cb); if (ppp.Length < 9 && cbe.Block.ParameterCount == ppp.Length) { return(InlineCall(cb, cbe, istailposition, ppp)); } } } if (ex is ConstantExpression) { Builtins.SyntaxError(SymbolTable.StringToObject("generator"), "expecting a procedure", c.car, c); } Expression r = null; if (ex.Type.Name.StartsWith("TypedClosure")) { Expression[] pp = GetAstListNoCast(c.cdr as Cons, cb); var m = ex.Type.GetMethod("Invoke"); //TODO: add more checks, should we attempt some casting for types? if (m.GetParameters().Length != pp.Length) { Builtins.SyntaxError(SymbolTable.StringToObject("apply-typed-lambda"), string.Format("incorrect number of parameters, expected {0} got {1}", m.GetParameters().Length, pp.Length), c.car, c); } r = Ast.SimpleCallHelper(ex, m, pp); } else { Expression[] pp = GetAstList(c.cdr as Cons, cb); if (ex.Type != typeof(Callable)) { ex = Ast.ConvertHelper(ex, typeof(Callable)); } MethodInfo call = GetCallable(pp.Length); r = pp.Length > 8 ? Ast.Call(ex, call, Ast.NewArray(typeof(object[]), pp)) : Ast.Call(ex, call, pp); } if (spanHint.IsValid) { r.SetLoc(spanHint); } return(r); } object[] v = args as object[]; if (v != null) { return(GetConsVector(v, cb)); } else if (args is byte[]) { Expression[] ba = Array.ConvertAll(args as byte[], b => Ast.Constant(b)); return(Ast.NewArray(typeof(byte[]), ba)); } else { if (args is SymbolId) { SymbolId sym = (SymbolId)args; if (sym == SymbolTable.StringToId("uninitialized")) { return(Ast.ReadField(null, typeof(Uninitialized), "Instance")); } else { return(Read(sym, cb, typeof(object))); } } if (args == Builtins.Unspecified) { return(Ast.ReadField(null, Unspecified)); } if (args is Fraction) { Fraction f = (Fraction)args; return(Ast.Constant(new FractionConstant(f))); } if (args is ComplexFraction) { ComplexFraction f = (ComplexFraction)args; return(Ast.Constant(new ComplexFractionConstant(f))); } if (args != null && args.GetType().Name == "stx") { args = new SerializedConstant(args); } return(Ast.Constant(args)); } }
private DynamicMetaObject /*!*/ MakeSetMember(SetMemberBinder /*!*/ member, DynamicMetaObject /*!*/ value) { PythonContext state = PythonContext.GetPythonContext(member); DynamicMetaObject self = Restrict(Value.GetType()); if (Value.GetType() != typeof(PythonType) && DynamicHelpers.GetPythonType(Value).IsSystemType) { // built-in subclass of .NET type. Usually __setattr__ is handled by MetaUserObject // but we can have a built-in subtype that's not a user type. PythonTypeSlot pts; if (Value.TryGetCustomSetAttr(state.SharedContext, out pts)) { Debug.Assert(pts.GetAlwaysSucceeds); ParameterExpression tmp = Ast.Variable(typeof(object), "boundVal"); return(BindingHelpers.AddDynamicTestAndDefer( member, new DynamicMetaObject( Ast.Block( new[] { tmp }, DynamicExpression.Dynamic( state.Invoke(new CallSignature(2)), typeof(object), AstUtils.Constant(state.SharedContext), Ast.Block( Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.SlotTryGetValue)), AstUtils.Constant(state.SharedContext), AstUtils.Convert(AstUtils.WeakConstant(pts), typeof(PythonTypeSlot)), AstUtils.Convert(Expression, typeof(object)), AstUtils.Convert(AstUtils.WeakConstant(DynamicHelpers.GetPythonType(Value)), typeof(PythonType)), tmp ), tmp ), Ast.Constant(member.Name), value.Expression ) ), self.Restrictions ), new DynamicMetaObject[] { this, value }, TestUserType() )); } } return(BindingHelpers.AddDynamicTestAndDefer( member, new DynamicMetaObject( Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.PythonTypeSetCustomMember)), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), self.Expression, AstUtils.Constant(member.Name), AstUtils.Convert( value.Expression, typeof(object) ) ), self.Restrictions.Merge(value.Restrictions) ), new DynamicMetaObject[] { this, value }, TestUserType() )); }
protected static Expression GetCons(object args, CodeBlock cb) { if (args is Annotation) { args = ((Annotation)args).stripped; } Cons c = args as Cons; if (c != null) { if (inconstant) { return(GetConsList(c, cb)); } else { return(Ast.Constant(new IronSchemeConstant(c, cb))); } } object[] v = args as object[]; if (v != null) { return(GetConsVector(v, cb)); } else if (args is byte[]) { return(Ast.Constant(new ArrayConstant <byte>((byte[])args))); } else if (args is int[]) { return(Ast.Constant(new ArrayConstant <int>((int[])args))); } else if (args is double[]) { return(Ast.Constant(new ArrayConstant <double>((double[])args))); } else if (args is Fraction) { Fraction f = (Fraction)args; return(Ast.Constant(new FractionConstant(f))); } else if (args is ComplexFraction) { ComplexFraction cf = (ComplexFraction)args; return(Ast.Constant(new ComplexFractionConstant(cf))); } else if (args is SchemeChar) { SchemeChar f = (SchemeChar)args; return(Ast.Constant(new SchemeCharConstant(f))); } else if (args is StringBuilder) { StringBuilder f = (StringBuilder)args; return(Ast.Constant(f.ToString())); } else { if (args is long) { args = (BigInteger)(long)args; } if (args != null && args.GetType().Namespace.StartsWith("record.")) { args = new SerializedConstant(args); } return(Ast.Constant(args)); } }
private MSAst.Expression AddModulePublishing(MSAst.Expression body) { if (_isModule) { PythonCompilerOptions pco = _compilerContext.Options as PythonCompilerOptions; string moduleName = ModuleName; if ((pco.Module & ModuleOptions.Initialize) != 0) { var tmp = Ast.Variable(typeof(object), "$originalModule"); // TODO: Should be try/fault body = Ast.Block( new[] { tmp }, AstUtils.Try( Ast.Assign(tmp, Ast.Call(AstMethods.PublishModule, LocalContext, Ast.Constant(moduleName))), body ).Catch( typeof(Exception), Ast.Call(AstMethods.RemoveModule, LocalContext, Ast.Constant(moduleName), tmp), Ast.Rethrow(body.Type) ) ); } } return(body); }
/// <summary> if a member-injector is defined-on or registered-for this type call it </summary> protected void MakeOperatorGetMemberBody(Type type, string name) { MethodInfo getMem = GetMethod(type, name); if (getMem != null && getMem.IsSpecialName) { ParameterExpression tmp = Rule.GetTemporary(typeof(object), "getVal"); AddToBody( AstUtils.If( Ast.NotEqual( Ast.Assign( tmp, Binder.MakeCallExpression(Rule.Context, getMem, Instance, Ast.Constant(StringName)) ), Ast.Field(null, typeof(OperationFailed).GetField("Value")) ), Rule.MakeReturn(Binder, tmp) ) ); } }