예제 #1
0
 private static void ExplCheck(IScriptCodeGen scg, Token errorAt, bool explicitAllowed, string oldString, string newString)
 {
     if (!explicitAllowed)
     {
         scg.ErrorMsg(errorAt, "must explicitly cast from " + oldString + " to " + newString);
     }
 }
예제 #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);
        }