The commonalities of every object.
Esempio n. 1
0
 /// <summary>
 /// Boxes a native string into its matching value type.
 /// </summary>
 /// <param name="Value"></param>
 /// <returns></returns>
 public static RakudoObject box_str(ThreadContext TC, string Value, RakudoObject To)
 {
     var REPR = To.STable.REPR;
     var Result = REPR.instance_of(TC, To);
     REPR.set_str(TC, Result, Value);
     return Result;
 }
Esempio n. 2
0
 /// <summary>
 /// Binds a value at a given positional index from a low level list
 /// (something that uses the P6list representation).
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="LLList"></param>
 /// <param name="Index"></param>
 /// <returns></returns>
 public static RakudoObject lllist_bind_at_pos(ThreadContext TC, RakudoObject LLList, RakudoObject IndexObj, RakudoObject Value)
 {
     if (LLList is P6list.Instance)
     {
         var Storage = ((P6list.Instance)LLList).Storage;
         var Index = Ops.unbox_int(TC, IndexObj);
         if (Index < Storage.Count)
         {
             Storage[Index] = Value;
         }
         else
         {
             // XXX Need some more efficient resizable array approach...
             // Also this is no way thread safe.
             while (Index > Storage.Count)
                 Storage.Add(null);
             Storage.Add(Value);
         }
         return Value;
     }
     else
     {
         throw new Exception("Cannot use lllist_bind_at_pos if representation is not P6list");
     }
 }
Esempio n. 3
0
 /// <summary>
 /// Forms a capture from the provided positional and named arguments.
 /// </summary>
 /// <param name="PosArgs"></param>
 /// <param name="NamedArgs"></param>
 /// <returns></returns>
 public static RakudoObject FormWith(RakudoObject[] PosArgs, Dictionary<string, RakudoObject> NamedArgs)
 {
     var C = (P6capture.Instance)CaptureTypeObject.STable.REPR.instance_of(null, CaptureTypeObject);
     C.Positionals = PosArgs;
     C.Nameds = NamedArgs;
     return C;
 }
Esempio n. 4
0
        /// <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;
        }
Esempio n. 5
0
        /// <summary>
        /// Adds the given result to the dispatch cache for the provided
        /// positional arguments.
        /// </summary>
        /// <param name="Positionals"></param>
        /// <param name="Result"></param>
        public void Add(RakudoObject[] Positionals, RakudoObject Result)
        {
            // Don't cache things with excessive arity.
            if (Positionals.Length <= MAX_ARITY)
            {
                // Compute the type cache ID tuple.
                var ToAdd = PositionalsToTypeCacheIDs(Positionals);

                // Snapshot the previous arity cache.
                var Previous = ArityCaches[Positionals.Length];

                // Build a new one.
                var New = new ArityCache();
                if (Previous == null)
                {
                    // First time. We go in slot 0.
                    New.NumEntries = 1;
                    New.Results = new RakudoObject[MAX_ENTRIES + 1];
                    New.TypeIDs = new long[MAX_ENTRIES * Positionals.Length];
                    for (int i = 0; i < ToAdd.Length; i++)
                        New.TypeIDs[i] = ToAdd[i];
                    New.Results[0] = Result;
                }
                else
                {
                    // Copy existing entries.
                    New.NumEntries = Previous.NumEntries;
                    New.TypeIDs = (long[])Previous.TypeIDs.Clone();
                    New.Results = (RakudoObject[])Previous.Results.Clone();

                    // Space for the new one?
                    if (New.NumEntries <= MAX_ENTRIES)
                    {
                        // We can go on the end.
                        int i, j;
                        for (i = 0, j = New.NumEntries * ToAdd.Length; i < ToAdd.Length; i++, j++)
                            New.TypeIDs[j] = ToAdd[i];
                        New.Results[New.NumEntries] = Result;
                        New.NumEntries++;
                    }
                    else
                    {
                        // Pick a victim.
                        var Evictee = new Random().Next(MAX_ENTRIES + 1);
                        int i, j;
                        for (i = 0, j = Evictee * ToAdd.Length; i < ToAdd.Length; i++, j++)
                            New.TypeIDs[j] = ToAdd[i];
                        New.Results[Evictee] = Result;
                    }
                }

                // Pop it in place, if nothing beat us to it. Otherwise,
                // we let whatever slipped in first win. (We may find it is
                // also beneficial to loop here and try to add this entry
                // again, but may be too much churn, and if it a given combination
                // is called a lot, it'll make it in at some point.)
                Interlocked.CompareExchange<ArityCache>(ref ArityCaches[ToAdd.Length], New, Previous);
            }
        }
Esempio n. 6
0
 /// <summary>
 /// Checks if a routine is considered a dispatcher (that is, if it has a
 /// candidate list).
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="Check"></param>
 /// <returns></returns>
 public static RakudoObject is_dispatcher(ThreadContext TC, RakudoObject Check)
 {
     var Checkee = Check as RakudoCodeRef.Instance;
     if (Checkee != null && Checkee.Dispatchees != null)
         return Ops.box_int(TC, 1, TC.DefaultBoolBoxType);
     else
         return Ops.box_int(TC, 0, TC.DefaultBoolBoxType);
 }
Esempio n. 7
0
        /// <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;
                }
            }
        }
Esempio n. 8
0
 /// <summary>
 /// Gets a value at a given positional index from a low level list
 /// (something that uses the P6list representation).
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="LLList"></param>
 /// <param name="Index"></param>
 /// <returns></returns>
 public static RakudoObject lllist_get_at_pos(ThreadContext TC, RakudoObject LLList, RakudoObject Index)
 {
     if (LLList is P6list.Instance)
     {
         return ((P6list.Instance)LLList).Storage[Ops.unbox_int(TC, Index)];
     }
     else
     {
         throw new Exception("Cannot use lllist_get_at_pos if representation is not P6list");
     }
 }
Esempio n. 9
0
 /// <summary>
 /// Gets the number of elements in a low level list (something that
 /// uses the P6list representation).
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="LLList"></param>
 /// <returns></returns>
 public static RakudoObject lllist_elems(ThreadContext TC, RakudoObject LLList)
 {
     if (LLList is P6list.Instance)
     {
         return Ops.box_int(TC, ((P6list.Instance)LLList).Storage.Count, TC.DefaultIntBoxType);
     }
     else
     {
         throw new Exception("Cannot use lllist_elems if representation is not P6list");
     }
 }
Esempio n. 10
0
        /// <summary>
        /// Loads a module (that is, some pre-compiled compilation unit that
        /// was compiled using NQP). Expects the path minus an extension
        /// (that is, the .dll will be added). Returns what the body of the
        /// compilation unit evaluated to.
        /// </summary>
        /// <param name="TC"></param>
        /// <param name="Path"></param>
        /// <returns></returns>
        public static RakudoObject load_module(ThreadContext TC, RakudoObject Path)
        {
            // Load the assembly and grab the first type in it.
            var Assembly = AppDomain.CurrentDomain.Load(Ops.unbox_str(TC, Path));
            var Class = Assembly.GetTypes()[0];

            // Call the Load method, passing along the current thread context
            // and the setting to use with it. What's returned is what the main
            // body of the compilation unit evaluates to.
            var Method = Class.GetMethod("Load", BindingFlags.NonPublic | BindingFlags.Static);
            return (RakudoObject)Method.Invoke(null, new object[] { TC, TC.Domain.Setting });
        }
Esempio n. 11
0
File: Init.cs Progetto: Util/6model
        /// <summary>
        /// Loads the setting with the given name.
        /// </summary>
        /// <param name="Name"></param>
        /// <param name="KnowHOW"></param>
        /// <returns></returns>
        public static Context LoadSetting(string Name, RakudoObject KnowHOW, RakudoObject KnowHOWAttribute)
        {
            // Load the assembly.
            var SettingAssembly = AppDomain.CurrentDomain.Load(Name);

            // Find the setting type and its LoadSetting method.
            var Class = SettingAssembly.GetType("NQPSetting");
            var Method = Class.GetMethod("LoadSetting", BindingFlags.NonPublic | BindingFlags.Static);

            // Run it to get the context we want.
            var SettingContext = (Context)Method.Invoke(null, new object[] { });

            // Fudge a few more things in.
            // XXX Should be able to toss all of these but KnowHOW.
            SettingContext.LexPad.Extend(new string[]
                { "KnowHOW", "KnowHOWAttribute", "print", "say", "capture" });
            SettingContext.LexPad.SetByName("KnowHOW", KnowHOW);
            SettingContext.LexPad.SetByName("KnowHOWAttribute", KnowHOWAttribute);
            SettingContext.LexPad.SetByName("print",
                CodeObjectUtility.WrapNativeMethod((TC, self, C) =>
                    {
                        for (int i = 0; i < CaptureHelper.NumPositionals(C); i++)
                        {
                            var Value = CaptureHelper.GetPositional(C, i);
                            var StrMeth = self.STable.FindMethod(TC, Value, "Str", 0);
                            var StrVal = StrMeth.STable.Invoke(TC, StrMeth,
                                CaptureHelper.FormWith( new RakudoObject[] { Value }));
                            Console.Write(Ops.unbox_str(null, StrVal));
                        }
                        return CaptureHelper.Nil();
                    }));
            SettingContext.LexPad.SetByName("say",
                CodeObjectUtility.WrapNativeMethod((TC, self, C) =>
                    {
                        for (int i = 0; i < CaptureHelper.NumPositionals(C); i++)
                        {
                            var Value = CaptureHelper.GetPositional(C, i);
                            var StrMeth = self.STable.FindMethod(TC, Value, "Str", 0);
                            var StrVal = StrMeth.STable.Invoke(TC, StrMeth,
                                CaptureHelper.FormWith( new RakudoObject[] { Value }));
                            Console.Write(Ops.unbox_str(null, StrVal));
                        }
                        Console.WriteLine();
                        return CaptureHelper.Nil();
                    }));
            SettingContext.LexPad.SetByName("capture", REPRRegistry.get_REPR_by_name("P6capture").type_object_for(null, null));

            return SettingContext;
        }
Esempio n. 12
0
 /// <summary>
 /// Binds the given value to a lexical variable of the given name.
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="name"></param>
 /// <returns></returns>
 public static RakudoObject bind_lex(ThreadContext TC, string Name, RakudoObject Value)
 {
     var CurContext = TC.CurrentContext;
     while (CurContext != null)
     {
         int Index;
         if (CurContext.LexPad.SlotMapping.TryGetValue(Name, out Index))
         {
             CurContext.LexPad.Storage[Index] = Value;
             return Value;
         }
         CurContext = CurContext.Outer;
     }
     throw new InvalidOperationException("No variable " + Name + " found in the lexical scope");
 }
Esempio n. 13
0
 /// <summary>
 /// Gets a value at a given key from a low level mapping (something that
 /// uses the P6mapping representation).
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="LLMapping"></param>
 /// <param name="Key"></param>
 /// <returns></returns>
 public static RakudoObject llmapping_get_at_key(ThreadContext TC, RakudoObject LLMapping, RakudoObject Key)
 {
     if (LLMapping is P6mapping.Instance)
     {
         var Storage = ((P6mapping.Instance)LLMapping).Storage;
         var StrKey = Ops.unbox_str(TC, Key);
         if (Storage.ContainsKey(StrKey))
             return Storage[StrKey];
         else
             return null;
     }
     else
     {
         throw new Exception("Cannot use llmapping_get_at_key if representation is not P6mapping");
     }
 }
Esempio n. 14
0
        /// <summary>
        /// Dies from an unhandled exception.
        /// </summary>
        /// <param name="Exception"></param>
        public static void DieFromUnhandledException(ThreadContext TC, RakudoObject Exception)
        {
            // Try to stringify the exception object.
            try
            {
                var StrMeth = Exception.STable.FindMethod(TC, Exception, "Str", Hints.NO_HINT);
                var Stringified = StrMeth.STable.Invoke(TC, StrMeth, CaptureHelper.FormWith(new RakudoObject[] { Exception }));
                Console.WriteLine(Ops.unbox_str(TC, Stringified));
            }
            catch
            {
                Console.Error.WriteLine("Died from an exception, and died trying to stringify it too.");
            }

            // Exit with an error code.
            Environment.Exit(1);
        }
Esempio n. 15
0
 /// <summary>
 /// Binds a value at a given key from a low level mapping (something that
 /// uses the P6mapping representation).
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="LLMapping"></param>
 /// <param name="Key"></param>
 /// <param name="Value"></param>
 /// <returns></returns>
 public static RakudoObject llmapping_bind_at_key(ThreadContext TC, RakudoObject LLMapping, RakudoObject Key, RakudoObject Value)
 {
     if (LLMapping is P6mapping.Instance)
     {
         var Storage = ((P6mapping.Instance)LLMapping).Storage;
         var StrKey = Ops.unbox_str(TC, Key);
         if (Storage.ContainsKey(StrKey))
             Storage[StrKey] = Value;
         else
             Storage.Add(StrKey, Value);
         return Value;
     }
     else
     {
         throw new Exception("Cannot use llmapping_bind_at_key if representation is not P6mapping");
     }
 }
Esempio n. 16
0
 /// <summary>
 /// Throws the specified exception, looking for an exception handler in the
 /// dynamic scope.
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="ExceptionObject"></param>
 /// <param name="ExceptionType"></param>
 /// <returns></returns>
 public static RakudoObject throw_dynamic(ThreadContext TC, RakudoObject ExceptionObject, RakudoObject ExceptionType)
 {
     int WantType = Ops.unbox_int(TC, ExceptionType);
     var CurContext = TC.CurrentContext;
     while (CurContext != null)
     {
         if (CurContext.StaticCodeObject != null)
         {
             var Handlers = CurContext.StaticCodeObject.Handlers;
             if (Handlers != null)
                 for (int i = 0; i < Handlers.Length; i++)
                     if (Handlers[i].Type == WantType)
                         return Exceptions.ExceptionDispatcher.CallHandler(TC,
                             Handlers[i].HandleBlock, ExceptionObject);
         }
         CurContext = CurContext.Caller;
     }
     Exceptions.ExceptionDispatcher.DieFromUnhandledException(TC, ExceptionObject);
     return null; // Unreachable; above call exits always.
 }
Esempio n. 17
0
        /// <summary>
        /// Invokes the specified exception handler with the given exception
        /// object.
        /// </summary>
        /// <param name="Handler"></param>
        /// <param name="ExceptionObject"></param>
        /// <returns></returns>
        public static RakudoObject CallHandler(ThreadContext TC, RakudoObject Handler, RakudoObject ExceptionObject)
        {
            // Invoke the handler. Note that in some cases we never return from it;
            // for example, the return exception handler does .leave.
            var Returned = Handler.STable.Invoke(TC, Handler, CaptureHelper.FormWith(new RakudoObject[] { ExceptionObject }));

            // So, we returned. Let's see if it's resumable.
            var ResumableMeth = Returned.STable.FindMethod(TC, Returned, "resumable", Hints.NO_HINT);
            var Resumable = ResumableMeth.STable.Invoke(TC, ResumableMeth, CaptureHelper.FormWith(new RakudoObject[] { Returned }));
            if (Ops.unbox_int(TC, Resumable) != 0)
            {
                // Resumable, so don't need to stack unwind. Simply return
                // from here.
                return Returned;
            }
            else
            {
                // Not resumable, so stack unwind out of the block containing
                // the handler.
                throw new LeaveStackUnwinderException(
                    (Handler as RakudoCodeRef.Instance).OuterBlock,
                    Returned);
            }
        }
Esempio n. 18
0
 /// <summary>
 /// Compares two floating point numbers for greater-than inequality.
 /// </summary>
 /// <param name="x"></param>
 /// <param name="y"></param>
 /// <param name="ResultType"></param>
 /// <returns></returns>
 public static RakudoObject greater_than_nums(ThreadContext TC, RakudoObject x, RakudoObject y)
 {
     return Ops.box_int(TC,
         (Ops.unbox_num(TC, x) > Ops.unbox_num(TC, y) ? 1 : 0),
         TC.DefaultBoolBoxType);
 }
Esempio n. 19
0
 /// <summary>
 /// Compares reference equality.
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="x"></param>
 /// <param name="y"></param>
 /// <returns></returns>
 public static RakudoObject equal_refs(ThreadContext TC, RakudoObject x, RakudoObject y)
 {
     return Ops.box_int(TC, x == y ? 1 : 0, TC.DefaultBoolBoxType);
 }
Esempio n. 20
0
 /// <summary>
 /// Compares two strings for less-than inequality.
 /// </summary>
 /// <param name="x"></param>
 /// <param name="y"></param>
 /// <param name="ResultType"></param>
 /// <returns></returns>
 public static RakudoObject less_than_strs(ThreadContext TC, RakudoObject x, RakudoObject y)
 {
     return Ops.box_int(TC,
         String.Compare(Ops.unbox_str(TC, x), Ops.unbox_str(TC, y)) < 0 ? 1 : 0,
         TC.DefaultBoolBoxType);
 }
Esempio n. 21
0
 /// <summary>
 /// Compares two floating point numbers for less-than-or-equal inequality.
 /// </summary>
 /// <param name="x"></param>
 /// <param name="y"></param>
 /// <param name="ResultType"></param>
 /// <returns></returns>
 public static RakudoObject less_than_or_equal_nums(ThreadContext TC, RakudoObject x, RakudoObject y)
 {
     return Ops.box_int(TC,
         (Ops.unbox_num(TC, x) <= Ops.unbox_num(TC, y) ? 1 : 0),
         TC.DefaultBoolBoxType);
 }
Esempio n. 22
0
 /// <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. 23
0
File: Init.cs Progetto: Util/6model
 /// <summary>
 /// Sets up the bootstrapping setting that we use to compile the
 /// real setting.
 /// </summary>
 /// <param name="KnowHOW"></param>
 /// <returns></returns>
 private static Context BootstrapSetting(RakudoObject KnowHOW, RakudoObject KnowHOWAttribute)
 {
     var SettingContext = new Context();
     SettingContext.LexPad = new Lexpad(new string[]
         { "KnowHOW", "KnowHOWAttribute", "capture", "NQPInt", "NQPNum", "NQPStr", "NQPList", "NQPCode", "list", "NQPArray", "NQPHash" });
     SettingContext.LexPad.Storage = new RakudoObject[]
         {
             KnowHOW,
             KnowHOWAttribute,
             REPRRegistry.get_REPR_by_name("P6capture").type_object_for(null, null),
             REPRRegistry.get_REPR_by_name("P6int").type_object_for(null, null),
             REPRRegistry.get_REPR_by_name("P6num").type_object_for(null, null),
             REPRRegistry.get_REPR_by_name("P6str").type_object_for(null, null),
             REPRRegistry.get_REPR_by_name("P6list").type_object_for(null, null),
             REPRRegistry.get_REPR_by_name("RakudoCodeRef").type_object_for(null, KnowHOW.STable.REPR.instance_of(null, KnowHOW)),
             CodeObjectUtility.WrapNativeMethod((TC, self, C) =>
                 {
                     var NQPList = Ops.get_lex(TC, "NQPList");
                     var List = NQPList.STable.REPR.instance_of(TC, NQPList) as P6list.Instance;
                     var NativeCapture = C as P6capture.Instance;
                     foreach (var Obj in NativeCapture.Positionals)
                         List.Storage.Add(Obj);
                     return List;
                 }),
             null,
             null
         };
     return SettingContext;
 }
Esempio n. 24
0
        /// <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;
        }
Esempio n. 25
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. 26
0
        /// <summary>
        /// Compares two types to see if the first is narrower than the second.
        /// </summary>
        /// <returns></returns>
        public static bool IsNarrowerType(ThreadContext TC, RakudoObject A, RakudoObject B)
        {
            // If one of the types is null, then we know that's automatically
            // wider than anything.
            if (B == null && A != null)
                return true;
            else if (A == null || B == null)
                return false;

            // Otherwise, check with the type system.
            return Ops.unbox_int(TC, Ops.type_check(TC, A, B)) != 0;
        }
Esempio n. 27
0
 /// <summary>
 /// If the first passed object reference is not null, returns it. Otherwise,
 /// returns the second passed object reference. (Note, we should one day drop
 /// this and implement it as a compiler transformation, to avoid having to
 /// look up the thing to vivify).
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="Check"></param>
 /// <param name="VivifyWith"></param>
 /// <returns></returns>
 public static RakudoObject vivify(ThreadContext TC, RakudoObject Check, RakudoObject VivifyWith)
 {
     return Check ?? VivifyWith;
 }
Esempio n. 28
0
 /// <summary>
 /// Takes a set of positional parameters and, based on their STable
 /// IDs and definedness,
 /// </summary>
 /// <param name="Positionals"></param>
 /// <returns></returns>
 private long[] PositionalsToTypeCacheIDs(RakudoObject[] Positionals)
 {
     var Result = new long[Positionals.Length];
     for (int i = 0; i < Positionals.Length; i++)
     {
         var STable = Positionals[i].STable;
         Result[i] = STable.TypeCacheID | (STable.REPR.defined(null, Positionals[i]) ? 1L : 0L);
     }
     return Result;
 }
Esempio n. 29
0
        /// <summary>
        /// Does a cache lookup based on the passed positional arguments.
        /// If a candidate is found, returns it. Otherwise, returns null.
        /// </summary>
        /// <param name="Positionals"></param>
        /// <returns></returns>
        public RakudoObject Lookup(RakudoObject[] Positionals)
        {
            // If it's within the arity we cache...
            if (Positionals.Length <= MAX_ARITY)
            {
                // ...and we did cache something...
                var Cache = ArityCaches[Positionals.Length];
                if (Cache != null && Cache.NumEntries != 0)
                {
                    // Get what we're looking for.
                    var Seeking = PositionalsToTypeCacheIDs(Positionals);

                    // Look through the cache for it. ci = type cache array index,
                    // ri = result list index.
                    int ci = 0;
                    for (int ri = 0; ri < Cache.NumEntries; ri++)
                    {
                        var Matched = true;
                        for (int j = 0; j < Positionals.Length; j++)
                        {
                            if (Seeking[j] != Cache.TypeIDs[ci])
                            {
                                Matched = false;
                                break;
                            }
                            ci++;
                        }
                        if (Matched)
                            return Cache.Results[ri];
                    }
                }
            }
            return null;
        }
Esempio n. 30
0
 /// <summary>
 /// Leaves the specified block, returning the specified value from it. This
 /// unwinds the stack.
 /// </summary>
 /// <param name="TC"></param>
 /// <param name="Block"></param>
 /// <param name="ReturnValue"></param>
 /// <returns></returns>
 public static RakudoObject leave_block(ThreadContext TC, RakudoObject Block, RakudoObject ReturnValue)
 {
     throw new Exceptions.LeaveStackUnwinderException(Block as RakudoCodeRef.Instance, ReturnValue);
 }