A representation for a low-level code object (something that actually references a piece of code that we'll run). This is used for things that serve the role of an only sub (that has a body) and a dispatcher (which has a body as well as a list of candidates that it operates on).
Inheritance: Representation
Esempio n. 1
0
        /// <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;
        }
Esempio n. 2
0
        /// <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;
 }
Esempio n. 4
0
        /// <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");
        }
Esempio n. 5
0
        /// <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;
        }
Esempio n. 6
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;
 }