public virtual void dump()
        {
            StringBuilder buf = new StringBuilder(7);

            for (int i = 0; i < 7; i++)
            {
                buf.Append((char)dis.read());
            }
            String magic = buf.ToString();

            Console.WriteLine("Header: \"" + magic + "\" Version " + dis.read());

            if (!"MiniJoe".Equals(magic))
            {
                throw new IOException("Magic does not match \"MiniJoe\"!");
            }

            dumpTables();

            Console.WriteLine("EOF: " + (dis.read() == -1));
        }
        /**
         * Parses the given stream and runs the main function
         * @throws IOException
         */
        public static Object exec(DataInputStream dis, JsObject context)
        {
            //TODO check magic
            for (int i = 0; i < 8; i++)
            {
                dis.read();
            }

            JsFunction main = new JsFunction(new JsFunction(dis, null), context);

            JsArray stack = new JsArray();

            stack.setObject(0, context);
            stack.setObject(1, context);
            stack.setObject(2, main);

            main.eval(stack, 1, 0);

            return(stack.getObject(3));
        }
        /**
         * Constructs a function literal from the serialized binary form including the
         * string table. Please note that function literals cannot be invoked
         * directly, a function must be created from this function literal using the
         * corresponding constructor. If the global string table is null, the file
         * header is expected.
         *
         * @throws IOException thrown for underlying stream IO errors
         */
        public JsFunction(DataInputStream dis, string[] globalStringTable) :

            base(FUNCTION_PROTOTYPE)
        {
            // __proto__ above, prototype below...
            this.prototype = new JsObject(OBJECT_PROTOTYPE);

            sbyte[] buf   = null;
            int     flags = 0;

            //loop:
            while (true)
            {
                int blockType = dis.read();
                int count;
                switch (blockType)
                {
                case BLOCK_COMMENT:
                    count = dis.readUnsignedShort();
                    if (buf == null || buf.Length < count)
                    {
                        buf = new sbyte[count];
                    }
                    dis.readFully(buf, 0, count);
                    break;

                case BLOCK_GLOBAL_STRING_TABLE:
                    count             = dis.readUnsignedShort();
                    globalStringTable = new String[count];
                    for (int i = 0; i < count; i++)
                    {
                        globalStringTable[i] = dis.readUTF();
                    }
                    break;

                case BLOCK_STRING_LITERALS:
                    count          = dis.readUnsignedShort();
                    stringLiterals = new String[count];
                    for (int i = 0; i < count; i++)
                    {
                        stringLiterals[i] = globalStringTable[dis.readShort()];
                    }
                    break;

                case BLOCK_NUMBER_LITERALS:
                    count          = dis.readUnsignedShort();
                    numberLiterals = new double[count];
                    for (int i = 0; i < count; i++)
                    {
                        numberLiterals[i] = dis.readDouble();
                    }
                    break;

                case BLOCK_FUNCTION_LITERALS:
                    count            = dis.readUnsignedShort();
                    functionLiterals = new JsFunction[count];
                    for (int i = 0; i < count; i++)
                    {
                        functionLiterals[i] = new JsFunction(dis, globalStringTable);
                    }
                    break;

                case BLOCK_LOCAL_VARIABLE_NAMES:
                    count      = dis.readUnsignedShort();
                    localNames = new String[count];
                    for (int i = 0; i < count; i++)
                    {
                        localNames[i] = globalStringTable[dis.readShort()];
                    }
                    break;

                case BLOCK_BYTE_CODE:
                    varCount = dis.readUnsignedShort();
                    expectedParameterCount = dis.readUnsignedShort();
                    varCount -= expectedParameterCount;
                    flags     = dis.read();
                    byteCode  = new sbyte[dis.readShort()];
                    dis.readFully(byteCode);
                    break;

                case BLOCK_LINE_NUMBERS:
                    count       = dis.readUnsignedShort();
                    lineNumbers = new int[count * 2];
                    for (int i = 0; i < count; i++)
                    {
                        lineNumbers[i << 1]       = dis.readUnsignedShort();
                        lineNumbers[(i << 1) + 1] = dis.readUnsignedShort();
                    }
                    break;

                case END_MARKER:
                    goto loopBreak;

                default:
                    throw new IOException("Illegal Block type "
                                          + MyInteger.toString(blockType, 16));
                }
            }
loopBreak:

            if ((flags & 1) == 0)
            {
                if (localNames == null)
                {
                    localNames = new String[0];
                }
            }
            else
            {
                localNames = null;
            }
        }