Exemple #1
0
        /**
         * @brief Perform runtime casting of XMRSDTypeClObj's.
         * @param ob = XMRSDTypeClObj of unknown script-defined class to cast
         * @param classindex = script-defined class to cast it to
         * @returns ob is a valid instance of classindex; else exception thrown
         */
        public static XMRSDTypeClObj CastClass2Class(object ob, int classindex)
        {
            /*
             * Let mono check to see if we at least have an XMRSDTypeClObj.
             */
            XMRSDTypeClObj ci = (XMRSDTypeClObj)ob;

            if (ci != null)
            {
                /*
                 * This is the target class, ie, what we are hoping the object can cast to.
                 */
                TokenDeclSDTypeClass tc = (TokenDeclSDTypeClass)ci.xmrInst.m_ObjCode.sdObjTypesIndx[classindex];

                /*
                 * Step from the object's actual class rootward.
                 * If we find the target class along the way, the cast is valid.
                 * If we run off the end of the root, the cast is not valid.
                 */
                for (TokenDeclSDTypeClass ac = ci.sdtcClass; ac != tc; ac = ac.extends)
                {
                    if (ac == null)
                    {
                        throw new InvalidCastException("invalid cast from " + ci.sdtcClass.longName.val +
                                                       " to " + tc.longName.val);
                    }
                }

                /*
                 * The target class is at or rootward of the actual class,
                 * so the cast is valid.
                 */
            }
            return(ci);
        }
Exemple #2
0
        /**
         * @brief Emit code that converts the top stack item from 'oldType' to 'newType'
         * @param scg = what script we are compiling
         * @param errorAt = token used for source location for error messages
         * @param oldType = type of item currently on the stack
         * @param newType = type to convert it to
         * @param explicitAllowed = false: only consider implicit casts
         *                           true: consider both implicit and explicit casts
         * @returns with code emitted for conversion (or error message output if not allowed, and stack left unchanged)
         */
        public static void CastTopOfStack(IScriptCodeGen scg, Token errorAt, TokenType oldType, TokenType newType, bool explicitAllowed)
        {
            CastDelegate castDelegate;
            string       oldString = oldType.ToString();
            string       newString = newType.ToString();

            // 'key' -> 'bool' is the only time we care about key being different than string.
            if ((oldString == "key") && (newString == "bool"))
            {
                LSLUnwrap(scg, errorAt, oldType);
                scg.ilGen.Emit(errorAt, OpCodes.Call, keyToBoolMethodInfo);
                LSLWrap(scg, errorAt, newType);
                return;
            }

            // Treat key and string as same type for all other type casts.
            if (oldString == "key")
            {
                oldString = "string";
            }
            if (newString == "key")
            {
                newString = "string";
            }

            // If the types are the same, there is no conceptual casting needed.
            // However, there may be wraping/unwraping to/from the LSL wrappers.
            if (oldString == newString)
            {
                if (oldType.ToLSLWrapType() != newType.ToLSLWrapType())
                {
                    LSLUnwrap(scg, errorAt, oldType);
                    LSLWrap(scg, errorAt, newType);
                }
                return;
            }

            // Script-defined classes can be cast up and down the tree.
            if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeClass))
            {
                TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
                TokenDeclSDTypeClass newSDTC = ((TokenTypeSDTypeClass)newType).decl;

                // implicit cast allowed from leaf toward root
                for (TokenDeclSDTypeClass sdtc = oldSDTC; sdtc != null; sdtc = sdtc.extends)
                {
                    if (sdtc == newSDTC)
                    {
                        return;
                    }
                }

                // explicit cast allowed from root toward leaf
                for (TokenDeclSDTypeClass sdtc = newSDTC; sdtc != null; sdtc = sdtc.extends)
                {
                    if (sdtc == oldSDTC)
                    {
                        ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
                        scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, newSDTC.sdTypeIndex);
                        scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
                        return;
                    }
                }

                // not on same branch
                goto illcast;
            }

            // One script-defined interface type cannot be cast to another script-defined interface type,
            // unless the old interface declares that it implements the new interface.  That proves that
            // the underlying object, no matter what type, implements the new interface.
            if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeInterface))
            {
                TokenDeclSDTypeInterface oldDecl = ((TokenTypeSDTypeInterface)oldType).decl;
                TokenDeclSDTypeInterface newDecl = ((TokenTypeSDTypeInterface)newType).decl;
                if (!oldDecl.Implements(newDecl))
                {
                    goto illcast;
                }
                scg.ilGen.Emit(errorAt, OpCodes.Ldstr, newType.ToString());
                scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
                return;
            }

            // A script-defined class type can be implicitly cast to a script-defined interface type that it
            // implements.  The result is an array of delegates that give the class's implementation of the
            // various methods defined by the interface.
            if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeSDTypeInterface))
            {
                TokenDeclSDTypeClass oldSDTC = ((TokenTypeSDTypeClass)oldType).decl;
                int intfIndex;
                if (!oldSDTC.intfIndices.TryGetValue(newType.ToString(), out intfIndex))
                {
                    goto illcast;
                }
                scg.ilGen.Emit(errorAt, OpCodes.Ldfld, sdtcITableFieldInfo);
                scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, intfIndex);
                scg.ilGen.Emit(errorAt, OpCodes.Ldelem, typeof(Delegate[]));
                return;
            }

            // A script-defined interface type can be explicitly cast to a script-defined class type by
            // extracting the Target property from element 0 of the delegate array that is the interface
            // object and making sure it casts to the correct script-defined class type.
            //
            // But then only if the class type implements the interface type.
            if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeSDTypeClass))
            {
                TokenTypeSDTypeInterface oldSDTI = (TokenTypeSDTypeInterface)oldType;
                TokenTypeSDTypeClass     newSDTC = (TokenTypeSDTypeClass)newType;

                if (!newSDTC.decl.CanCastToIntf(oldSDTI.decl))
                {
                    goto illcast;
                }

                ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
                scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, newSDTC.decl.sdTypeIndex);
                scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastIFace2ClassMethodInfo);
                return;
            }

            // A script-defined interface type can be implicitly cast to object.
            if ((oldType is TokenTypeSDTypeInterface) && (newType is TokenTypeObject))
            {
                return;
            }

            // An object can be explicitly cast to a script-defined interface.
            if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeInterface))
            {
                ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
                scg.ilGen.Emit(errorAt, OpCodes.Ldstr, newString);
                scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastObj2IFaceMethodInfo);
                return;
            }

            // Cast to void is always allowed, such as discarding value from 'i++' or function return value.
            if (newType is TokenTypeVoid)
            {
                scg.ilGen.Emit(errorAt, OpCodes.Pop);
                return;
            }

            // Cast from undef to object or script-defined type is always allowed.
            if ((oldType is TokenTypeUndef) &&
                ((newType is TokenTypeObject) ||
                 (newType is TokenTypeSDTypeClass) ||
                 (newType is TokenTypeSDTypeInterface)))
            {
                return;
            }

            // Script-defined classes can be implicitly cast to objects.
            if ((oldType is TokenTypeSDTypeClass) && (newType is TokenTypeObject))
            {
                return;
            }

            // Script-defined classes can be explicitly cast from objects and other script-defined classes.
            // Note that we must manually check that it is the correct SDTypeClass however because as far as
            // mono is concerned, all SDTypeClass's are the same.
            if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeClass))
            {
                ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
                scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, ((TokenTypeSDTypeClass)newType).decl.sdTypeIndex);
                scg.ilGen.Emit(errorAt, OpCodes.Call, sdTypeClassCastClass2ClassMethodInfo);
                return;
            }

            // Delegates can be implicitly cast to/from objects.
            if ((oldType is TokenTypeSDTypeDelegate) && (newType is TokenTypeObject))
            {
                return;
            }
            if ((oldType is TokenTypeObject) && (newType is TokenTypeSDTypeDelegate))
            {
                scg.ilGen.Emit(errorAt, OpCodes.Castclass, newType.ToSysType());
                return;
            }

            // Some actual conversion is needed, see if it is in table of legal casts.
            string key = oldString + " " + newString;

            if (!legalTypeCasts.TryGetValue(key, out castDelegate))
            {
                key = oldString + "*" + newString;
                if (!legalTypeCasts.TryGetValue(key, out castDelegate))
                {
                    goto illcast;
                }
                ExplCheck(scg, errorAt, explicitAllowed, oldString, newString);
            }

            // Ok, output cast.  But make sure it is in native form without any LSL wrapping
            // before passing to our casting routine.  Then if caller is expecting an LSL-
            // wrapped value on the stack upon return, wrap it up after our casting.
            LSLUnwrap(scg, errorAt, oldType);
            castDelegate(scg, errorAt);
            LSLWrap(scg, errorAt, newType);
            return;

illcast:
            scg.ErrorMsg(errorAt, "illegal to cast from " + oldString + " to " + newString);
            if (!(oldType is TokenTypeVoid))
            {
                scg.ilGen.Emit(errorAt, OpCodes.Pop);
            }
            scg.PushDefaultValue(newType);
        }
        /**
         * @brief Fill in ScriptObjCode from an YEngine object file.
         *   'objFileReader' is a serialized form of the CIL code we generated
         *   'asmFileWriter' is where we write the disassembly to (or null if not wanted)
         *   'srcFileWriter' is where we write the decompilation to (or null if not wanted)
         * Throws an exception if there is any error (theoretically).
         */
        public ScriptObjCode(BinaryReader objFileReader, TextWriter asmFileWriter, TextWriter srcFileWriter)
        {
            // Check version number to make sure we know how to process file contents.
            char[] ocm = objFileReader.ReadChars(ScriptCodeGen.OBJECT_CODE_MAGIC.Length);
            if (new String(ocm) != ScriptCodeGen.OBJECT_CODE_MAGIC)
            {
                throw new CVVMismatchException("Not an Yengine object file (bad magic)");
            }

            int cvv = objFileReader.ReadInt32();

            if (cvv != ScriptCodeGen.COMPILED_VERSION_VALUE)
            {
                throw new CVVMismatchException(
                          "Object version is " + cvv.ToString() + " but accept only " + ScriptCodeGen.COMPILED_VERSION_VALUE.ToString());
            }
            // Fill in simple parts of scriptObjCode object.
            sourceHash = objFileReader.ReadString();
            glblSizes.ReadFromFile(objFileReader);
            int nStates = objFileReader.ReadInt32();

            stateNames = new string[nStates];
            for (int i = 0; i < nStates; i++)
            {
                stateNames[i] = objFileReader.ReadString();
                if (asmFileWriter != null)
                {
                    asmFileWriter.WriteLine("  state[{0}] = {1}", i, stateNames[i]);
                }
            }

            if (asmFileWriter != null)
            {
                glblSizes.WriteAsmFile(asmFileWriter, "numGbl");
            }

            string gblName;

            while ((gblName = objFileReader.ReadString()) != "")
            {
                string gblType  = objFileReader.ReadString();
                int    gblIndex = objFileReader.ReadInt32();
                Dictionary <int, string> names;
                if (!globalVarNames.TryGetValue(gblType, out names))
                {
                    names = new Dictionary <int, string>();
                    globalVarNames.Add(gblType, names);
                }
                names.Add(gblIndex, gblName);
                if (asmFileWriter != null)
                {
                    asmFileWriter.WriteLine("  {0} = {1}[{2}]", gblName, gblType, gblIndex);
                }
            }

            // Read in script-defined types.
            sdObjTypesName = new Dictionary <string, TokenDeclSDType>();
            sdDelTypes     = new Dictionary <Type, string>();
            int maxIndex = -1;

            while ((gblName = objFileReader.ReadString()) != "")
            {
                TokenDeclSDType sdt = TokenDeclSDType.ReadFromFile(sdObjTypesName,
                                                                   gblName, objFileReader, asmFileWriter);
                sdObjTypesName.Add(gblName, sdt);
                if (maxIndex < sdt.sdTypeIndex)
                {
                    maxIndex = sdt.sdTypeIndex;
                }
                if (sdt is TokenDeclSDTypeDelegate)
                {
                    sdDelTypes.Add(sdt.GetSysType(), gblName);
                }
            }
            sdObjTypesIndx = new TokenDeclSDType[maxIndex + 1];
            foreach (TokenDeclSDType sdt in sdObjTypesName.Values)
            {
                sdObjTypesIndx[sdt.sdTypeIndex] = sdt;
            }

            // Now fill in the methods (the hard part).
            scriptEventHandlerTable = new ScriptEventHandler[nStates, (int)ScriptEventCode.Size];
            dynamicMethods          = new Dictionary <string, DynamicMethod>();
            scriptSrcLocss          = new Dictionary <string, KeyValuePair <int, ScriptSrcLoc>[]>();

            ObjectTokens objectTokens = null;

            if (asmFileWriter != null)
            {
                objectTokens = new OTDisassemble(this, asmFileWriter);
            }
            else if (srcFileWriter != null)
            {
                objectTokens = new OTDecompile(this, srcFileWriter);
            }

            try
            {
                ScriptObjWriter.CreateObjCode(sdObjTypesName, objFileReader, this, objectTokens);
            }
            finally
            {
                if (objectTokens != null)
                {
                    objectTokens.Close();
                }
            }

            // We enter all script event handler methods in the ScriptEventHandler table.
            // They are named:  <statename> <eventname>
            foreach (KeyValuePair <string, DynamicMethod> kvp in dynamicMethods)
            {
                string methName = kvp.Key;
                int    i        = methName.IndexOf(' ');
                if (i < 0)
                {
                    continue;
                }
                string stateName = methName.Substring(0, i);
                string eventName = methName.Substring(++i);
                int    stateCode;
                for (stateCode = stateNames.Length; --stateCode >= 0;)
                {
                    if (stateNames[stateCode] == stateName)
                    {
                        break;
                    }
                }

                int eventCode = (int)Enum.Parse(typeof(ScriptEventCode), eventName);
                scriptEventHandlerTable[stateCode, eventCode] =
                    (ScriptEventHandler)kvp.Value.CreateDelegate(typeof(ScriptEventHandler));
            }

            // Fill in all script-defined class vtables.
            foreach (TokenDeclSDType sdt in sdObjTypesIndx)
            {
                if ((sdt != null) && (sdt is TokenDeclSDTypeClass))
                {
                    TokenDeclSDTypeClass sdtc = (TokenDeclSDTypeClass)sdt;
                    sdtc.FillVTables(this);
                }
            }
        }
Exemple #4
0
        /**
         * @brief Set up everything except the instVars arrays.
         * @param inst = script instance this object is part of
         * @param classindex = which script-defined type class this object is an onstance of
         * @returns with the vtables filled in
         */
        private void Construct(XMRInstAbstract inst, int classindex)
        {
            Delegate[]           thisMid = null;
            TokenDeclSDTypeClass clas    = (TokenDeclSDTypeClass)inst.m_ObjCode.sdObjTypesIndx[classindex];

            xmrInst   = inst;
            sdtcClass = clas;
            instVars  = new XMRInstArrays(inst);

            /*
             * VTable consists of delegates built from DynamicMethods and the 'this' pointer.
             * Yes, yes, lots of shitty little mallocs.
             */
            DynamicMethod[] vDynMeths = clas.vDynMeths;
            if (vDynMeths != null)
            {
                int    n          = vDynMeths.Length;
                Type[] vMethTypes = clas.vMethTypes;
                sdtcVTable = new Delegate[n];
                for (int i = 0; i < n; i++)
                {
                    sdtcVTable[i] = vDynMeths[i].CreateDelegate(vMethTypes[i], this);
                }
            }

            /*
             * Fill in interface vtables.
             * There is one array of delegates for each implemented interface.
             * The array of delegates IS the interface's object, ie, the 'this' value of the interface.
             * To cast from the class type to the interface type, just get the correct array from the table.
             * To cast from the interface type to the class type, just get Target of entry 0.
             *
             * So we end up with this:
             *    sdtcITable[interfacenumber][methodofintfnumber] = delegate of this.ourimplementationofinterfacesmethod
             */
            if (clas.iDynMeths != null)
            {
                int nIFaces = clas.iDynMeths.Length;
                sdtcITable = new Delegate[nIFaces][];
                for (int i = 0; i < nIFaces; i++)
                {
                    // get vector of entrypoints of our instance methods that implement that interface
                    DynamicMethod[] iDynMeths  = clas.iDynMeths[i];
                    Type[]          iMethTypes = clas.iMethTypes[i];

                    // allocate an array with a slot for each method the interface defines
                    int        nMeths = iDynMeths.Length;
                    Delegate[] ivec;
                    if (nMeths > 0)
                    {
                        // fill in the array with delegates that reference back to this class instance
                        ivec = new Delegate[nMeths];
                        for (int j = 0; j < nMeths; j++)
                        {
                            ivec[j] = iDynMeths[j].CreateDelegate(iMethTypes[j], this);
                        }
                    }
                    else
                    {
                        // just a marker interface with no methods,
                        // allocate a one-element array and fill
                        // with a dummy entry.  this will allow casting
                        // back to the original class instance (this)
                        // by reading Target of entry 0.
                        if (thisMid == null)
                        {
                            thisMid    = new Delegate[1];
                            thisMid[0] = markerInterfaceDummy.CreateDelegate(typeof(MarkerInterfaceDummy), this);
                        }
                        ivec = thisMid;
                    }

                    // save whatever we ended up allocating
                    sdtcITable[i] = ivec;
                }
            }
        }