/// <summary> /// Creates an instantiation of the dispatch routine (or proto, which may /// serve as one) supplied and augments it with the provided candidates. /// It relies on being passed the instantiation of the dispatcher from the /// last outer scope that had an instantiation, and we thus take its /// candidates. This may or may not hold up in the long run; it works out /// in the Perl 6-y "you can make a new instance from any object" sense /// though, and seems more likely to get the closure semantics right than /// any of the other approaches I've considered so far. /// </summary> /// <param name="TC"></param> /// <param name="ToInstantiate"></param> /// <param name="ExtraDispatchees"></param> /// <returns></returns> public static RakudoObject create_dispatch_and_add_candidates(ThreadContext TC, RakudoObject ToInstantiate, RakudoObject ExtraDispatchees) { // Make sure we got the right things. var Source = ToInstantiate as RakudoCodeRef.Instance; var AdditionalDispatchList = ExtraDispatchees as P6list.Instance; if (Source == null || AdditionalDispatchList == null) throw new Exception("create_dispatch_and_add_candidates expects a RakudoCodeRef and a P6list"); // Clone all but SC (since it's a new object and doesn't live in any // SC yet) and dispatchees (which we want to munge). var NewDispatch = new RakudoCodeRef.Instance(Source.STable); NewDispatch.Body = Source.Body; NewDispatch.CurrentContext = Source.CurrentContext; NewDispatch.Handlers = Source.Handlers; NewDispatch.OuterBlock = Source.OuterBlock; NewDispatch.OuterForNextInvocation = Source.OuterForNextInvocation; NewDispatch.Sig = Source.Sig; NewDispatch.StaticLexPad = Source.StaticLexPad; // Take existing candidates and add new ones. NewDispatch.Dispatchees = new RakudoObject[Source.Dispatchees.Length + AdditionalDispatchList.Storage.Count]; var i = 0; for (int j = 0; j < Source.Dispatchees.Length; j++) NewDispatch.Dispatchees[i++] = Source.Dispatchees[j]; for (int j = 0; j < AdditionalDispatchList.Storage.Count; j++) NewDispatch.Dispatchees[i++] = AdditionalDispatchList.Storage[j]; return NewDispatch; }
/// <summary> /// Initializes the context. /// </summary> /// <param name="StaticCodeObject"></param> /// <param name="Caller"></param> public Context(RakudoObject StaticCodeObject_Uncast, Context Caller, RakudoObject Capture) { // Set up static code object and caller pointers. var StaticCodeObject = (RakudoCodeRef.Instance)StaticCodeObject_Uncast; this.StaticCodeObject = StaticCodeObject; this.Caller = Caller; this.Capture = Capture; // Static sub object should have this as the current // context. StaticCodeObject.CurrentContext = this; // Lex pad should be an "instantiation" of the static one. // Instantiating a lexpad creates a new dynamic instance of it // from a static one, copying over the slot storage entries // from the static one but sharing the slot mapping. this.LexPad.SlotMapping = StaticCodeObject.StaticLexPad.SlotMapping; this.LexPad.Storage = (RakudoObject[])StaticCodeObject.StaticLexPad.Storage.Clone(); // Set outer context. if (StaticCodeObject.OuterForNextInvocation != null) { this.Outer = StaticCodeObject.OuterForNextInvocation; } else if (StaticCodeObject.OuterBlock.CurrentContext != null) { this.Outer = StaticCodeObject.OuterBlock.CurrentContext; } else { // Auto-close. In this we go setting up fake contexts // that use the static lexpad until we find a real one. var CurContext = this; var OuterBlock = StaticCodeObject.OuterBlock; while (OuterBlock != null) { // If we found a block with a context, we're done. if (OuterBlock.CurrentContext != null) { CurContext.Outer = OuterBlock.CurrentContext; break; } // Build the fake context. var OuterContext = new Context(); OuterContext.StaticCodeObject = OuterBlock; OuterContext.LexPad = OuterBlock.StaticLexPad; // Link it. CurContext.Outer = OuterContext; // Step back one level. CurContext = OuterContext; OuterBlock = OuterBlock.OuterBlock; } } }
/// <summary> /// Creates a clone of the given code object, and makes its outer context /// be set to the current context. /// </summary> /// <param name="TC"></param> /// <param name="Block"></param> /// <returns></returns> public static RakudoObject new_closure(ThreadContext TC, RakudoCodeRef.Instance Block) { // Clone all but OuterForNextInvocation and SC (since it's a new // object and doesn't live in any SC yet). var NewBlock = new RakudoCodeRef.Instance(Block.STable); NewBlock.Body = Block.Body; NewBlock.CurrentContext = Block.CurrentContext; NewBlock.Dispatchees = Block.Dispatchees; NewBlock.Handlers = Block.Handlers; NewBlock.OuterBlock = Block.OuterBlock; NewBlock.Sig = Block.Sig; NewBlock.StaticLexPad = Block.StaticLexPad; // Set the outer for next invocation and return the cloned block. NewBlock.OuterForNextInvocation = TC.CurrentContext; return NewBlock; }
/// <summary> /// Creates a LeaveStackUnwinderException to target the given block /// and exit it with the specified payload. /// </summary> /// <param name="TargetBlock"></param> /// <param name="PayLoad"></param> public LeaveStackUnwinderException(RakudoCodeRef.Instance TargetBlock, RakudoObject PayLoad) { this.TargetBlock = TargetBlock; this.PayLoad = PayLoad; }
/// <summary> /// Sorts the candidates. /// </summary> /// <param name="Unsorted"></param> /// <returns></returns> private static RakudoCodeRef.Instance[] Sort(ThreadContext TC, RakudoObject[] Unsorted) { /* Allocate results array (just allocate it for worst case, which * is no ties ever, so a null between all of them, and then space * for the terminating null. */ var NumCandidates = Unsorted.Length; var Result = new RakudoCodeRef.Instance[2 * NumCandidates + 1]; /* Create a node for each candidate in the graph. */ var Graph = new CandidateGraphNode[NumCandidates]; for (int i = 0; i < NumCandidates; i++) { Graph[i] = new CandidateGraphNode() { Candidate = (RakudoCodeRef.Instance)Unsorted[i], Edges = new CandidateGraphNode[NumCandidates] }; } /* Now analyze type narrowness of the candidates relative to each other * and create the edges. */ for (int i = 0; i < NumCandidates; i++) { for (int j = 0; j < NumCandidates; j++) { if (i == j) continue; if (IsNarrower(TC, Graph[i].Candidate, Graph[j].Candidate) != 0) { Graph[i].Edges[Graph[i].EdgesOut] = Graph[j]; Graph[i].EdgesOut++; Graph[j].EdgesIn++; } } } /* Perform the topological sort. */ int CandidatesToSort = NumCandidates; int ResultPos = 0; while (CandidatesToSort > 0) { int StartPoint = ResultPos; /* Find any nodes that have no incoming edges and add them to * results. */ for (int i = 0; i < NumCandidates; i++) { if (Graph[i].EdgesIn == 0) { /* Add to results. */ Result[ResultPos] = Graph[i].Candidate; Graph[i].Candidate = null; ResultPos++; CandidatesToSort--; Graph[i].EdgesIn = EDGE_REMOVAL_TODO; } } if (StartPoint == ResultPos) { throw new Exception("Circularity detected in multi sub types."); } /* Now we need to decrement edges in counts for things that had * edges from candidates we added here. */ for (int i = 0; i < NumCandidates; i++) { if (Graph[i].EdgesIn == EDGE_REMOVAL_TODO) { for (int j = 0; j < Graph[i].EdgesOut; j++) Graph[i].Edges[j].EdgesIn--; Graph[i].EdgesIn = EDGE_REMOVED; } } /* This is end of a tied group, so leave a gap. */ ResultPos++; } return Result; }