Пример #1
0
        // Leaves the link in the same state as when it started. The out param leafExprType will be Function if
        // the array is empty in the first bottom-level subarray: {{}, {1,2,3}}.
        internal static int determineIncomingArrayDepth(IMathLink ml /*, out ExpressionType leafExprType*/)
        {
            int       actualDepth = 0;
            ILinkMark mark        = ml.CreateMark();

//        // Initial value just to satisfy compiler; if depth of 0 is returned, then the leafExprType will be ignored by callers.
//        leafExprType = ExpressionType.Function;
            try {
                int    len;
                string head = ml.GetFunction(out len);
                actualDepth = 1;
                while (len > 0)
                {
                    ExpressionType leafExprType = ml.GetNextExpressionType();
                    if (leafExprType == ExpressionType.Function)
                    {
                        head = ml.GetFunction(out len);
                        actualDepth++;
                    }
                    else
                    {
                        break;
                    }
                }
//            // If the last sublist is empty (len == 0), we will walk off the end of the above loop with
//            // leafExprType == ExpressionType.Function, which is what we want.
            } catch (MathLinkException) {
                // Do nothing but clear it. Returning 0 is enough.
                ml.ClearError();
            } finally {
                ml.SeekMark(mark);
                ml.DestroyMark(mark);
            }
            return(actualDepth);
        }
Пример #2
0
        public static object readArbitraryResult(IKernelLink ml, GH_Component component)
        {
            Object    exprOrObjectResult = null;
            ILinkMark mark = ml.CreateMark();

            try {
                exprOrObjectResult = ml.GetObject();
            } catch (MathLinkException) {
                ml.ClearError();
                ml.SeekMark(mark);
                // Technically, GetExpr() should never throw MathLinkException. But there was at least one bug I found and fixed where it did.
                // So to be safe we leave this try/catch here, otherwise the link will get in a bad state.
                try {
                    Expr ex = ml.GetExpr();
                    exprOrObjectResult = new ExprType(ex);
                } catch (MathLinkException) {
                    ml.ClearError();
                    component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Error reading result as an Expr. This is a bug in RhinoLink, not the user program.");
                }
            } finally {
                ml.DestroyMark(mark);
                ml.NewPacket();
            }

            return(exprOrObjectResult);
        }
Пример #3
0
        public Expr PeekExpr()
        {
            ILinkMark mark = CreateMark();

            try {
                return(Expr.CreateFromLink(this));
            } finally {
                SeekMark(mark);
                DestroyMark(mark);
            }
        }
Пример #4
0
        // Tests whether symbol waiting on link is a valid object reference or the symbol Null.
        // Called by GetNextExpressionType() and GetExpressionType(), after it has already been verified that the type is MLTKSYM.
        protected bool isObject()
        {
            ILinkMark mark = CreateMark();

            try {
                getObj();
                return(true);
            } catch (MathLinkException) {
                ClearError();
                return(false);
            } finally {
                SeekMark(mark);
                DestroyMark(mark);
            }
        }
Пример #5
0
        /******************************  GetObject  ******************************/

        public virtual object GetObject()
        {
            // Note not GetNextExpressionType(), as that will likely already have been called.
            ExpressionType leafExprType = GetExpressionType();

            switch (leafExprType)
            {
            case ExpressionType.Integer:
                return(GetInteger());

            case ExpressionType.Real:
                return(GetDouble());

            case ExpressionType.String:
                return(GetString());

            case ExpressionType.Object:
                return(getObj());

            case ExpressionType.Boolean:
                return(GetBoolean());

            case ExpressionType.Complex:
                return(GetComplex());

            case ExpressionType.Symbol: {
                ILinkMark mark = CreateMark();
                try {
                    string s = GetSymbol();
                    if (s == "Null")
                    {
                        return(null);
                    }
                    SeekMark(mark);
                    throw new MathLinkException(MathLinkException.MLE_SYMBOL);
                } finally {
                    DestroyMark(mark);
                }
            }

            case ExpressionType.Function:
                return(Utils.readArbitraryArray(this, typeof(Array)));

            default:
                Debug.Fail("Unhandled ExpressionType value in MathLinkImpl.GetObject()");
                return(null);
            }
        }
Пример #6
0
        public void CheckFunctionWithArgCount(string f, int argCount)
        {
            ILinkMark mark = CreateMark();

            try {
                int    argc;
                string s = GetFunction(out argc);
                if (s != f || argc != argCount)
                {
                    SeekMark(mark);
                    throw new MathLinkException(MathLinkException.MLE_CHECKFUNCTION);
                }
            } finally {
                DestroyMark(mark);
            }
        }
Пример #7
0
        public int CheckFunction(string f)
        {
            ILinkMark mark = CreateMark();

            try {
                int    argCount;
                string s = GetFunction(out argCount);
                if (s != f)
                {
                    SeekMark(mark);
                    throw new MathLinkException(MathLinkException.MLE_CHECKFUNCTION);
                }
                return(argCount);
            } finally {
                DestroyMark(mark);
            }
        }
Пример #8
0
        // Special method for reading results typed as "Any". We want native objects for simple types like int, double,
        // arbitrary object refs as objects (let Grasshopper internals worry about wrapping in IGH_Goo), and Expr for anything else.
        // Note that we don't have to worry here about list/tree access type, because this is only called either for
        // leaf elements, or to get whole nested lists as a single item for DataAccess.item slots.
        private static object ReadAsAny(IKernelLink link)
        {
            ILinkMark mark = link.CreateMark();

            try {
                ExpressionType leafExprType = link.GetNextExpressionType();
                link.SeekMark(mark);
                switch (leafExprType)
                {
                case ExpressionType.Integer:
                    return(link.GetInteger());

                case ExpressionType.Real:
                    return(link.GetDouble());

                case ExpressionType.String:
                case ExpressionType.Symbol:
                    return(link.GetString());

                case ExpressionType.Object:
                    return(link.GetObject());

                case ExpressionType.Boolean:
                    return(link.GetBoolean());

                case ExpressionType.Complex:
                    // TODO: Don't have any idea how this is handled elsewhere
                    return(link.GetComplex());

                case ExpressionType.Function:
                    return(new ExprType(link.GetExpr()));

                default:
                    // Just to satisfy the compiler.
                    return(null);
                }
            } finally {
                link.DestroyMark(mark);
            }
        }
Пример #9
0
        public virtual bool OnPacketArrived(PacketType pkt)
        {
            if (PacketArrived == null)
            {
                return(true);
            }

            bool continueProcessing = true;

            Delegate[] pktHandlers = PacketArrived.GetInvocationList();
            if (pktHandlers.Length > 0)
            {
                object[]  args = new object[] { this, pkt };
                ILinkMark mark = CreateMark();
                try {
                    foreach (Delegate d in pktHandlers)
                    {
                        try {
                            continueProcessing = (bool)d.DynamicInvoke(args);
                        } catch (Exception) {
                            // Don't want an exception thrown by a PacketHandler method to
                            // prevent all handlers from being notified.
                        }
                        ClearError();
                        SeekMark(mark);
                        if (!continueProcessing)
                        {
                            break;
                        }
                    }
                } finally {
                    DestroyMark(mark);
                }
            }
            return(continueProcessing);
        }
Пример #10
0
 public override void SeekMark(ILinkMark mark)
 {
     impl.SeekMark(mark);
 }
Пример #11
0
 public abstract void DestroyMark(ILinkMark mark);
Пример #12
0
 public abstract void SeekMark(ILinkMark mark);
Пример #13
0
        protected override void SolveInstance(IGH_DataAccess DA)
        {
            ` GetData `

            // Final, optional link input param is always present.
            LinkType linkType = null;

            DA.GetData(` InputArgCount `, ref linkType);

            IKernelLink link = linkType != null ? linkType.Value : Utils.GetLink();

            // Send initialization code and saved defs.
            try {
                if (` UseInit `)
                {
                    link.Evaluate("ReleaseHold[\"" + ` Initialization ` + "\"]");
                    link.WaitAndDiscardAnswer();
                }
                if (` UseDefs `)
                {
                    link.Evaluate(` Definitions `);
                    link.WaitAndDiscardAnswer();
                }
            } catch (MathLinkException exc) {
                link.ClearError();
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "WSTPException communicating with Wolfram Engine: " + exc);
                return;
            }

            // Now begin sending the actual computation.
            try {
                link.PutFunction("EvaluatePacket", 1);
                link.PutNext(ExpressionType.Function);
                link.PutArgCount(` InputArgCount `);

                if (` HeadIsSymbol `)
                {
                    link.PutSymbol("`Func`");
                }
                else
                {
                    // pure function
                    link.PutFunction("ToExpression", 1);
                    link.Put("`Func`");
                }

                ` SendInputParams `

                link.EndPacket();
                link.WaitForAnswer();
            } catch (MathLinkException exc) {
                link.ClearError();
                link.NewPacket();
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "WSTPException communicating with Wolfram Engine: " + exc);
                return;
            }

            // This is one part of a (likely) temporary feature that puts the Expr result on the second output.
            Expr debuggingExpr = link.PeekExpr();

            // Read the result(s) and store them in the DA object.
            ILinkMark mark = link.CreateMark();
            // TODO: ghResultCount is not used, but if we are going to allow multiple outputs, then we have to protect against
            // users specifying more than one, but only one arrives. This will hang reading the second one.
            int  ghResultCount = 0;
            bool isGHResult    = false;

            try {
                ghResultCount = link.CheckFunction("GHResult") - 1;
                isGHResult    = true;
            } catch (MathLinkException) {
                link.SeekMark(mark);
            } finally {
                link.DestroyMark(mark);
            }
            try {
                if (isGHResult)
                {
                    ` ReadAndStoreResults `
                }
                else
                {
                    ` ReadAndStoreResults `
                }

                // This is the second part of the temporary feature that puts the entire result as an Expr on a second output port.
                DA.SetData(++ghResultCount, new ExprType(debuggingExpr));

                // Last output item is always the link.
                DA.SetData(ghResultCount + 1, new LinkType(link));
            } catch (MathLinkException exc) {
                link.ClearError();
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "WSTPException communicating with Wolfram Engine while reading results: " + exc);
            } finally {
                link.NewPacket();
            }
        }
Пример #14
0
        // Can read any kind of array, except for jagged arrays that are multidimensional at the beginning, like [][,]
        // (jagged at start like [,][] or [,...][][] is OK, but only if we know the type, not for an Array slot). Recall
        // that mixed array types are read backwards, so [][,] is a 2-deep array of element type [] (i.e., it is multidimensional
        // at the start, not the end). One more limitation: For an Array argument slot, if the incoming array is 3-deep or deeper,
        // it must be rectangular, not jagged.
        // We know the incoming expression has head List.
        internal static Array readArbitraryArray(IMathLink ml, Type t)
        {
            int len;

            if (t == typeof(Array))
            {
                ExpressionType leafExprType;
                Type           leafType;
                // For the Array type, we have no clue about how the leaf elements will be read. Thus we need to peek into
                // the incoming data to decide what type it is.
                int           depth = determineIncomingArrayDepth(ml);
                ILinkMark     mark  = ml.CreateMark();
                ILoopbackLink loop  = null;
                try {
                    if (depth == 1)
                    {
                        len = ml.CheckFunction("List");
                        if (len == 0)
                        {
                            throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY);
                        }
                        leafExprType = ml.GetNextExpressionType();
                        ml.SeekMark(mark);
                        leafType = netTypeFromExpressionType(leafExprType, ml);
                        // Fail if data could only be read as Expr. This means that we cannot pass a list of arbitrary expressions
                        // to an arg slot typed as Array. It would have to be typed as Expr[]. We make this choice to provide
                        // more meaningful error reporting for the cases where the array has bogus data in it. We assume that
                        // this convenience outweighs the very rare cases where a programmer would want to pass an array of exprs
                        // for an Array slot (they could always create the array separately and pass it as an object reference).
                        if (leafType == typeof(Expr))
                        {
                            if (leafExprType == ExpressionType.Complex)
                            {
                                throw new MathLinkException(MathLinkException.MLE_NO_COMPLEX);
                            }
                            else
                            {
                                throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY);
                            }
                        }
                        return((Array)ml.GetArray(leafType, 1));
                    }
                    else if (depth == 2)
                    {
                        // The loopback link is just a utility for quickly reading expressions off the link (via TransferExpression).
                        // Nothing is ever read back off the loopback link.
                        loop = MathLinkFactory.CreateLoopbackLink();
                        // Determine if the array is rectangular or jagged.
                        len = ml.CheckFunction("List");
                        bool isJagged      = false;
                        bool foundLeafType = false;
                        // This next assignment is strictly for the compiler. The value will never be used
                        // unless it is set to an actual value below. We have an existing function that will get
                        // the leafExprType of the incoming array (getLeafExprType()), but we also need to check if the array
                        // is jagged here, so we will do both tasks at once and save a little time.
                        leafExprType = ExpressionType.Integer;
                        int lenAtLevel2 = ml.CheckFunction("List");
                        if (lenAtLevel2 > 0)
                        {
                            leafExprType  = ml.GetNextExpressionType();
                            foundLeafType = true;
                        }
                        // peel off all the elements in the first sublist.
                        for (int j = 0; j < lenAtLevel2; j++)
                        {
                            loop.TransferExpression(ml);
                        }
                        // For each remaining sublist, check its length and peel off its members. At this point, though, we
                        // cannot be guaranteed that all elements are lists (or, at least, guaranteed that it is an error
                        // if they are not). They could be Null if the array is jagged: {{1, 2}, Null}.
                        for (int i = 1; i < len; i++)
                        {
                            ExpressionType nextExprType = ml.GetNextExpressionType();
                            if (nextExprType == ExpressionType.Object)
                            {
                                isJagged = true;
                                // OK to have null as an element in a jagged array.
                                if (ml.GetObject() != null)
                                {
                                    throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE);
                                }
                            }
                            else if (nextExprType == ExpressionType.Function)
                            {
                                int thisLength = ml.CheckFunction("List");
                                if (!foundLeafType && thisLength > 0)
                                {
                                    leafExprType  = ml.GetNextExpressionType();
                                    foundLeafType = true;
                                }
                                if (thisLength != lenAtLevel2)
                                {
                                    isJagged = true;
                                    break;
                                }
                                else
                                {
                                    for (int j = 0; j < thisLength; j++)
                                    {
                                        loop.TransferExpression(ml);
                                    }
                                }
                            }
                            else
                            {
                                throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_DEPTH);
                            }
                        }
                        // If the array is empty we cannot create a .NET array for it, as there is no type info for the array we are creating.
                        if (!foundLeafType)
                        {
                            throw new MathLinkException(MathLinkException.MLE_EMPTY_ARRAY);
                        }
                        leafType = netTypeFromExpressionType(leafExprType, ml);
                        ml.SeekMark(mark);
                        if (isJagged)
                        {
                            ml.CheckFunction("List");
                            Array result = Array.CreateInstance(Array.CreateInstance(leafType, 0).GetType(), len);
                            for (int i = 0; i < len; i++)
                            {
                                // Have to check if elements are lists or Null.
                                ExpressionType nextExprType = ml.GetNextExpressionType();
                                if (nextExprType == ExpressionType.Function)
                                {
                                    result.SetValue(ml.GetArray(leafType, 1), i);
                                }
                                else
                                {
                                    string sym = ml.GetSymbol();
                                    if (sym != "Null")
                                    {
                                        throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE);
                                    }
                                    result.SetValue(null, i);
                                }
                            }
                            return(result);
                        }
                        else
                        {
                            return(ml.GetArray(leafType, 2));
                        }
                    }
                    else
                    {
                        // For an Array argument slot, we only support passing >2-deep rectangular arrays, not jagged.
                        for (int i = 0; i < depth; i++)
                        {
                            ml.CheckFunction("List");
                        }
                        leafExprType = ml.GetNextExpressionType();
                        leafType     = netTypeFromExpressionType(leafExprType, ml);
                        ml.SeekMark(mark);
                        return(ml.GetArray(leafType, depth));
                    }
                } finally {
                    if (loop != null)
                    {
                        loop.Close();
                    }
                    ml.DestroyMark(mark);
                }
            }
            else
            {
                // We have the actual array shape encoded in the type. Either [], [,..], or array-of-arrays: [][]....
                int  arrayRank   = t.GetArrayRank();
                Type elementType = t.GetElementType();
                if (elementType.IsArray)
                {
                    if (arrayRank > 1)
                    {
                        // Don't support multidimensional array at start of jagged array: [][,]. Recall that mixed array types
                        // are read backwards, so [][,] is a 2-deep array of element type [].
                        throw new MathLinkException(MathLinkException.MLE_MULTIDIM_ARRAY_OF_ARRAY);
                    }
                    len = ml.CheckFunction("List");
                    Array result = Array.CreateInstance(elementType, len);
                    for (int i = 0; i < len; i++)
                    {
                        ExpressionType nextExprType = ml.GetNextExpressionType();
                        if (nextExprType == ExpressionType.Function)
                        {
                            result.SetValue(readArbitraryArray(ml, elementType), i);
                        }
                        else
                        {
                            string sym = ml.GetSymbol();
                            if (sym != "Null")
                            {
                                throw new MathLinkException(MathLinkException.MLE_BAD_ARRAY_SHAPE);
                            }
                            result.SetValue(null, i);
                        }
                    }
                    return(result);
                }
                else if (elementType == typeof(Array))
                {
                    // Don't support Array[].
                    throw new MathLinkException(MathLinkException.MLE_ARRAY_OF_ARRAYCLASS);
                }
                else
                {
                    return(ml.GetArray(elementType, arrayRank));
                }
            }
        }
Пример #15
0
        // M code must ensure that the only types that can arrive in this function are ones that are currently handled here.
        public static bool ReadAndStoreResult(string type, int index, IKernelLink link, IGH_DataAccess DA, GH_ParamAccess accessType, GH_Component component)
        {
            try
            {
                switch (accessType)
                {
                case GH_ParamAccess.item:
                {
                    object res = ReadSingleItemAsType(type, link);
                    DA.SetData(index, res);
                    break;
                }

                case GH_ParamAccess.list:
                {
                    ILinkMark mark   = link.CreateMark();
                    int       length = 1;
                    bool      isList = false;
                    try
                    {
                        length = link.CheckFunction("List");
                        isList = true;
                    }
                    catch (MathLinkException)
                    {
                        link.ClearError();
                    }
                    finally
                    {
                        link.SeekMark(mark);
                        link.DestroyMark(mark);
                    }
                    if (isList)
                    {
                        IList list = ReadListOfItemsAsType(type, link);
                        DA.SetDataList(index, list);
                    }
                    else
                    {
                        object item = ReadSingleItemAsType(type, link);
                        DA.SetData(index, item);
                    }
                    break;
                }

                case GH_ParamAccess.tree:
                {
                    // Quick and dirty method. Only support numbers. Read as Expr, then use Dimensions, etc.
                    // TODO: Handle objects. See how it is done in ReadSingleItemAsType().
                    // This read-as-expr method shows problems for tree type. It would be better, for tree, to not use
                    // the expr, but rather re-read the data from the link (as is done for item, list). In the curremt
                    // method, I don't see any good way to handle "Any".
                    // WAIT, IT'S WORSE. Expr.AsArray() only does Integer, Real, and only up to depth 2.
                    ILinkMark mark = link.CreateMark();
                    try
                    {
                        Expr  e    = link.GetExpr();
                        int[] dims = e.Dimensions;
                        if (dims.Length == 0)
                        {
                            link.SeekMark(mark);
                            object res = ReadSingleItemAsType(type, link);
                            DA.SetData(index, res);
                        }
                        else if (dims.Length == 1)
                        {
                            link.SeekMark(mark);
                            IList list = ReadListOfItemsAsType(type, link);
                            DA.SetDataList(index, list);
                        }
                        else if (dims.Length == 2)
                        {
                            // This code could be cleaner with GH_Structure, but I dont quite understand that class...
                            switch (type)
                            {
                            case "Number":
                            {
                                DataTree <double> tree      = new DataTree <double>();
                                Array             dataArray = e.AsArray(ExpressionType.Real, 2);
                                for (int i = 0; i < dims[0]; i++)
                                {
                                    GH_Path pth = new GH_Path(i);
                                    for (int j = 0; j < dims[1]; j++)
                                    {
                                        tree.Add((double)dataArray.GetValue(i, j), pth);
                                    }
                                }
                                DA.SetDataTree(index, tree);
                                break;
                            }

                            case "Integer":
                            {
                                DataTree <int> tree      = new DataTree <int>();
                                Array          dataArray = e.AsArray(ExpressionType.Integer, 2);
                                for (int i = 0; i < dims[0]; i++)
                                {
                                    GH_Path pth = new GH_Path(i);
                                    for (int j = 0; j < dims[1]; j++)
                                    {
                                        tree.Add((int)dataArray.GetValue(i, j), pth);
                                    }
                                }
                                DA.SetDataTree(index, tree);
                                break;
                            }

                            default:
                                // Can't handle this.
                                return(false);
                            }
                        }
                        else
                        {
                            // TODO. At least set a RuntimeMessage before returning false.
                            return(false);
                        }
                    }
                    finally
                    {
                        link.DestroyMark(mark);
                    }
                    break;

                    // Problem with the read-and-fill-element-by-element approach is that this is a GH_Structure<IGH_Goo>,
                    // so I need to create an IGH_Goo pbjectout of every piece of data. But there are zillions of IGH_Goo
                    // objects out there. I don't want to map every object to its IGH_Goo type (e.g., Circle --> GH_Circle).
                    // For lists, I could simply create a List<anything> and apparently it gets converted to the appopriate
                    // IGH_Goo object later on. But with trees, I don't see the corresponding technique. Compare the signature
                    // of DA.SetDataList() and DA.SetDataTree().

                    /*
                     *  ILinkMark mark3 = link.CreateMark();
                     *  int length = 1;
                     *  try {
                     *      length = link.CheckFunction("List");
                     *      // Don't catch; allow to fail. Must be a list arriving for tree results.
                     *  } finally {
                     *      link.DestroyMark(mark3);
                     *  }
                     *  GH_Structure<IGH_Goo> structure = new GH_Structure<IGH_Goo>();
                     *  GH_Path path = new GH_Path(0);
                     *  int pathLen = 1;
                     *  int pathIndex = 0;
                     *  for (int i = 0; i < length; i++) {
                     *      if (isListWaitingOnLink(link)) {
                     *           path.
                     *      } else {
                     *          structure.Append(
                     *      }
                     *  }
                     */
                }
                }
            }
            catch (Exception)
            {
                component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Unexpected type of result from Wolfram Engine");
                link.ClearError();
                link.NewPacket();
                return(false);
            }
            return(true);
        }
Пример #16
0
 public override void DestroyMark(ILinkMark mark)
 {
     impl.DestroyMark(mark);
 }
Пример #17
0
        public override PacketType NextPacket()
        {
            // Code here is not just a simple call to impl.nextPacket(). For a KernelLink, nextPacket() returns a
            // wider set of packet constants than the MathLink C API itself. We want nextPacket() to work on the
            // non-packet types of heads the kernel and FE send back and forth.
            // Because for some branches below we seek back to the start before returning, it is not guaranteed
            // that when nextPacket() returns, the current packet has been "opened". Thus it is not safe to write
            // a J/Link program that loops calling nextPacket()/newPacket(). You need to call handlePacket() after
            // nextPacket() to ensure that newPacket() will throw away the curent "packet".

            // createMark will fail on an unconnected link, and the user might call this
            // before any reading that would connect the link.
            if (!linkConnected)
            {
                Connect();
                linkConnected = true;
            }

            PacketType pkt;
            ILinkMark  mark = CreateMark();

            try {
                pkt = impl.NextPacket();
            } catch (MathLinkException e) {
                if (e.ErrCode == 23 /* MLEUNKNOWNPACKET */)
                {
                    ClearError();
                    SeekMark(mark);
                    int    argCount;
                    string f = GetFunction(out argCount);
                    if (f == "ExpressionPacket")
                    {
                        pkt = PacketType.Expression;
                    }
                    else if (f == "BoxData")
                    {
                        // Probably more than just BoxData will need to join this group. I thought that kernel
                        // always sent cell contents (e.g. Print output) wrapped in ExpressionPacket, but not so.
                        // "Un-wrapped" BoxData is also possible, perhaps others. We need to treat this case
                        // specially, since there is no packet wrapper.
                        SeekMark(mark);
                        pkt = PacketType.Expression;
                    }
                    else
                    {
                        // Note that all other non-recognized functions get labelled as PacketType.FrontEnd. I could perhaps be
                        // more discriminating, but then I risk not recognizing a legitimate communication
                        // intended for the front end. This means that PacketType.FrontEnd is not a specific indication that
                        // a front-end-related packet is on the link. Because there is no diagnostic packet head,
                        // we need to seek back to before the function was read. That way, when programs call
                        // methods to read the "contents" of the packet, they will in fact get the whole thing.
                        SeekMark(mark);
                        pkt = PacketType.FrontEnd;
                    }
                }
                else
                {
                    // The other MathLink error from MLNextPacket is MLENEXTPACKET, which is a common one saying
                    // that you have called MLNextPacket when more data is in the current packet.
                    throw e;
                }
            } finally {
                DestroyMark(mark);
            }
            return(pkt);
        }
Пример #18
0
        public virtual void HandlePacket(PacketType pkt)
        {
            switch (pkt)
            {
            // If you ever change the default behavior on the 4 "answer" packets to read off the link,
            // you'll need to add a seekMark in NativeKernelLink.waitForAnswer...
            case PacketType.Return:
            case PacketType.InputName:
            case PacketType.ReturnText:
            case PacketType.ReturnExpression:
            case PacketType.Menu:
            case PacketType.Message:
                break;

            // From here on, the cases do actual work.
            case PacketType.Call: {
                ExpressionType type = GetExpressionType();
                if (type == ExpressionType.Integer)
                {
                    // A normal CallPacket representing a call to .NET via nCall.
                    callPktHandler.handleCallPacket(this);
                }
                else
                {
                    // A CallPacket destined for the FE via MathLink`CallFrontEnd[] and routed through
                    // .NET due to ShareFrontEnd[]. This would only be in a 5.1 FE, as earlier
                    // versions do not use CallPacket and later versions would use the FE's Service Link.
                    IMathLink feServerLink = callPktHandler.FEServerLink;
                    if (feServerLink != null)
                    {
                        feServerLink.PutFunction("CallPacket", 1);
                        feServerLink.TransferExpression(this);
                        // FE will always reply to a CallPacket. Note that it is technically possible for
                        // the FE to send back an EvaluatePacket, which means that we really need to run a
                        // little loop here, not just write the result back to the kernel. But this branch
                        // is only for a 5.1 FE, and I don't think that the 5.1 FE ever does that.
                        TransferExpression(feServerLink);
                    }
                }
                break;
            }

            case PacketType.Display:
            case PacketType.DisplayEnd: {
                IMathLink feServerLink = callPktHandler.FEServerLink;
                if (feServerLink != null)
                {
                    if (accumulatingPS == null)
                    {
                        accumulatingPS = new System.Text.StringBuilder(34000);  // 34K is large enough to hold an entire packet
                    }
                    accumulatingPS.Append(GetString());
                    if (pkt == PacketType.DisplayEnd)
                    {
                        // XXXPacket[stuff] ---> Cell[GraphicsData["PostScript", stuff], "Graphics"]
                        feServerLink.PutFunction("FrontEnd`FrontEndExecute", 1);
                        feServerLink.PutFunction("FrontEnd`NotebookWrite", 2);
                        feServerLink.PutFunction("FrontEnd`SelectedNotebook", 0);
                        feServerLink.PutFunction("Cell", 2);
                        feServerLink.PutFunction("GraphicsData", 2);
                        feServerLink.Put("PostScript");
                        feServerLink.Put(accumulatingPS.ToString());
                        feServerLink.Put("Graphics");
                        feServerLink.Flush();
                        accumulatingPS = null;
                    }
                }
                else
                {
                    Debug.WriteLine("Got PacketType.Display in handlePacket, but no FE link");
                }
                break;
            }

            case PacketType.Input:
            case PacketType.InputString: {
                IMathLink feServerLink = callPktHandler.FEServerLink;
                if (feServerLink != null)
                {
                    feServerLink.PutFunction(pkt == PacketType.InputString ? "InputStringPacket" : "InputPacket", 1);
                    feServerLink.Put(GetString());
                    feServerLink.Flush();
                    NewPacket();
                    Put(feServerLink.GetString());
                    Flush();
                }
                break;
            }

            case PacketType.Text:
            case PacketType.Expression: {
                // Print output, or message text.
                IMathLink feServerLink = callPktHandler.FEServerLink;
                if (feServerLink != null)
                {
                    // XXXPacket[stuff] ---> Cell[stuff, "Print"]
                    feServerLink.PutFunction("FrontEnd`FrontEndExecute", 1);
                    feServerLink.PutFunction("FrontEnd`NotebookWrite", 2);
                    feServerLink.PutFunction("FrontEnd`SelectedNotebook", 0);
                    feServerLink.PutFunction("Cell", 2);
                    feServerLink.TransferExpression(this);
                    feServerLink.Put((lastPktWasMsg) ? "Message" : "Print");
                    feServerLink.Flush();
                }
                else
                {
                    // For one type of PacketType.Expression, no part of it has been read yet. Thus we must "open" the
                    // packet so that later calls to newPacket() throw it away.
                    if (pkt == PacketType.Expression)
                    {
                        int ignore;
                        GetFunction(out ignore);
                    }
                }
                break;
            }

            case PacketType.FrontEnd: {
                // This case is different from the others. At the point of entry, the link is at the point
                // _before_ the "packet" has been read. As a result, we must at least open the packet.
                // Note that PacketType.FrontEnd is really just a fall-through for unrecognized packets. We don't have any
                // checks that it is truly intended for the FE.
                IMathLink feServerLink = callPktHandler.FEServerLink;
                if (feServerLink != null)
                {
                    ILinkMark mark = CreateMark();
                    try {
                        // Wrap FrontEndExecute around it if not already there.
                        int    ignore;
                        string wrapper = GetFunction(out ignore);
                        if (wrapper != "FrontEnd`FrontEndExecute")
                        {
                            feServerLink.PutFunction("FrontEnd`FrontEndExecute", 1);
                        }
                    } finally {
                        SeekMark(mark);
                        DestroyMark(mark);
                    }
                    feServerLink.TransferExpression(this);
                    feServerLink.Flush();
                    // Wait until either the fe is ready (because what we just sent causes a return value)
                    // or kernel is ready (the computation is continuing because the kernel is not waiting
                    // for a return value).
                    do
                    {
                        System.Threading.Thread.Sleep(50);
                    } while (!feServerLink.Ready && !Ready);
                    if (feServerLink.Ready)
                    {
                        // fe link has something to return to kernel from last PacketType.FrontEnd we sent it.
                        TransferExpression(feServerLink);
                        Flush();
                    }
                }
                else
                {
                    // It's OK to get here. For example, this happens if you don't share the fe, but have a
                    // button that calls NotebookCreate[]. This isn't a very good example, because that
                    // function expects the fe to return something, so Java will hang. you will get into
                    // trouble if you make calls on the fe that expect a return. Everything is OK for calls
                    // that don't expect a return, though.
                    int ignore;
                    GetFunction(out ignore); // Must at least open the packet, so newPacket (back in caller) will get rid of it.
                }
                break;
            }

            default:
                break;
            }
            lastPktWasMsg = pkt == PacketType.Message;
        }