/// <summary> /// This is a helper to build a bunch of static block information. /// </summary> /// <param name="Code"></param> /// <returns></returns> public static RakudoCodeRef.Instance BuildStaticBlockInfo( Func<ThreadContext, RakudoObject, RakudoObject, RakudoObject> Code, RakudoCodeRef.Instance Outer, string[] LexNames) { // Create code wrapper object. var Result = (RakudoCodeRef.Instance)LLCodeTypeObject.STable.REPR.instance_of(null, LLCodeTypeObject); // Put body, outer and handlers in place. Result.Body = Code; Result.OuterBlock = Outer; // Setup static lexpad. Result.StaticLexPad = new Lexpad(LexNames); return Result; }
/// <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> /// Finds the best candidate, if one exists, and returns it. /// </summary> /// <param name="Candidates"></param> /// <param name="Capture"></param> /// <returns></returns> public static RakudoObject FindBestCandidate(ThreadContext TC, RakudoCodeRef.Instance DispatchRoutine, RakudoObject Capture) { // Extract the native capture. // XXX Handle non-native captures too. var NativeCapture = Capture as P6capture.Instance; // First, try the dispatch cache. if (DispatchRoutine.MultiDispatchCache != null && NativeCapture.Nameds == null) { var CacheResult = DispatchRoutine.MultiDispatchCache.Lookup(NativeCapture.Positionals); if (CacheResult != null) return CacheResult; } // Sort the candidates. // XXX Cache this in the future. var SortedCandidates = Sort(TC, DispatchRoutine.Dispatchees); // Now go through the sorted candidates and find the first one that // matches. var PossiblesList = new List<RakudoCodeRef.Instance>(); foreach (RakudoCodeRef.Instance Candidate in SortedCandidates) { // If we hit a null, we're at the end of a group. if (Candidate == null) { if (PossiblesList.Count == 1) { // We have an unambiguous first candidate. Cache if possible and // return it. if (NativeCapture.Nameds == null) { if (DispatchRoutine.MultiDispatchCache == null) DispatchRoutine.MultiDispatchCache = new DispatchCache(); DispatchRoutine.MultiDispatchCache.Add(NativeCapture.Positionals, PossiblesList[0]); } return PossiblesList[0]; } else if (PossiblesList.Count > 1) { // Here is where you'd handle constraints. throw new Exception("Ambiguous dispatch: more than one candidate matches"); } else { continue; } } /* Check if it's admissible by arity. */ var NumArgs = NativeCapture.Positionals.Length; if (NumArgs < Candidate.Sig.NumRequiredPositionals || NumArgs > Candidate.Sig.NumPositionals) continue; /* Check if it's admissible by types and definedness. */ var TypeCheckCount = Math.Min(NumArgs, Candidate.Sig.NumPositionals); var TypeMismatch = false; for (int i = 0; i < TypeCheckCount; i++) { var Arg = NativeCapture.Positionals[i]; var Type = Candidate.Sig.Parameters[i].Type; if (Type != null && Ops.unbox_int(TC, Type.STable.TypeCheck(TC, Arg.STable.WHAT, Type)) == 0) { TypeMismatch = true; break; } var Definedness = Candidate.Sig.Parameters[i].Definedness; if (Definedness != DefinednessConstraint.None) { var ArgDefined = Arg.STable.REPR.defined(null, Arg); if (Definedness == DefinednessConstraint.DefinedOnly && !ArgDefined || Definedness == DefinednessConstraint.UndefinedOnly && ArgDefined) { TypeMismatch = true; break; } } } if (TypeMismatch) continue; /* If we get here, it's an admissible candidate; add to list. */ PossiblesList.Add(Candidate); } // If we get here, no candidates matched. throw new Exception("No candidates found to dispatch to"); }
/// <summary> /// Checks if one signature is narrower than another. /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> private static int IsNarrower(ThreadContext TC, RakudoCodeRef.Instance a, RakudoCodeRef.Instance b) { var Narrower = 0; var Tied = 0; int i, TypesToCheck; /* Work out how many parameters to compare, factoring in slurpiness * and optionals. */ if (a.Sig.NumPositionals == b.Sig.NumPositionals) TypesToCheck = a.Sig.NumPositionals; else if (a.Sig.NumRequiredPositionals == b.Sig.NumRequiredPositionals) TypesToCheck = Math.Min(a.Sig.NumPositionals, b.Sig.NumPositionals); else return 0; /* Analyse each parameter in the two candidates. */ for (i = 0; i < TypesToCheck; i++) { var TypeObjA = a.Sig.Parameters[i].Type; var TypeObjB = b.Sig.Parameters[i].Type; if (TypeObjA == TypeObjB) { /* In a full Perl 6 multi dispatcher, you'd consider * constraints here. */ Tied++; } else { if (IsNarrowerType(TC, TypeObjA, TypeObjB)) Narrower++; else if (!IsNarrowerType(TC, TypeObjB, TypeObjA)) Tied++; } } /* If one is narrower than the other from current analysis, we're done. */ if (Narrower >= 1 && Narrower + Tied == TypesToCheck) return 1; /* If they aren't tied, we're also done. */ else if (Tied != TypesToCheck) return 0; /* Otherwise, we see if one has a slurpy and the other not. A lack of * slurpiness makes the candidate narrower. Otherwise, they're tied. */ return !a.Sig.HasSlurpyPositional() && b.Sig.HasSlurpyPositional() ? 1 : 0; }
/// <summary> /// Makes the outer context of the provided block be set to the current /// context. /// </summary> /// <param name="TC"></param> /// <param name="Block"></param> /// <returns></returns> public static RakudoObject capture_outer(ThreadContext TC, RakudoCodeRef.Instance Block) { Block.OuterForNextInvocation = TC.CurrentContext; return Block; }