Example #1
0
        /**
         * Java implementations of JS array members and methods.
         */
        public override void evalNative(int index, JsArray stack,
                                        int sp, int parCount)
        {
            switch (index)
            {
            case ID_CONCAT:
                JsArray array = new JsArray();
                copy(0, array, 0, size_);
                stack.copy(sp + 2, array, size_, parCount);
                stack.setObject(sp, array);
                break;

            case ID_JOIN:
                StringBuilder buf = new StringBuilder();
                String        sep = stack.isNull(sp + 2) ? "," : stack.getString(sp + 2);
                for (int i = 0; i < size_; i++)
                {
                    if (i > 0)
                    {
                        buf.Append(sep);
                    }
                    if (!isNull(i))
                    {
                        buf.Append(getString(i));
                    }
                }
                stack.setObject(sp, buf.ToString());
                break;

            case ID_LENGTH_SET:
                setSize(stack.getInt(sp));
                break;

            case ID_LENGTH:
                stack.setInt(sp, size_);
                break;

            case ID_POP:
                if (size_ == 0)
                {
                    stack.setObject(sp, null);
                }
                else
                {
                    copy(size_ - 1, stack, sp);
                    setSize(size_ - 1);
                }
                break;

            case ID_PUSH:
                stack.copy(sp + 2, this, size_, parCount);
                stack.setNumber(sp, size_);
                break;

            case ID_REVERSE:
                for (int i = 0; i < size_ / 2; i++)
                {
                    swap(i, size_ - 1 - i);
                }
                break;

            case ID_SHIFT:
                if (size_ == 0)
                {
                    stack.setObject(sp, null);
                }
                else
                {
                    copy(0, stack, sp);
                    copy(1, this, 0, size_ - 1);
                    setSize(size_ - 1);
                }
                break;

            case ID_SLICE:
                int start = stack.getInt(sp + 2);
                int end   = stack.isNull(sp + 3) ? size_ : stack.getInt(sp + 3);
                if (start < 0)
                {
                    start = Math.Max(size_ + start, 0);
                }
                if (end < 0)
                {
                    end = Math.Max(size_ + start, 0);
                }
                if (start > size_)
                {
                    start = size_;
                }
                if (end > size_)
                {
                    end = size_;
                }
                if (end < start)
                {
                    end = start;
                }
                array = new JsArray();
                copy(start, array, 0, end - start);
                stack.setObject(sp, array);
                break;

            case ID_SORT:
                Object compare = stack.getObject(sp + 2);
                if (compare is JsFunction)
                {
                    sort(0, size_, (JsFunction)compare, stack.getJsObject(sp), stack, sp);
                }
                else
                {
                    sort(0, size_);
                }
                stack.setObject(sp, this);
                break;

            case ID_SPLICE:
                array = new JsArray();
                start = stack.getInt(sp + 2);
                int delCount = stack.getInt(sp + 3);
                if (start < 0)
                {
                    start = size_ - start;
                }
                int itemCount = Math.Max(0, parCount - 2);
                copy(0, array, 0, start);
                stack.copy(sp + 4, array, start, itemCount);
                copy(start + delCount, array, start + itemCount, size_ - (start + delCount));
                stack.setObject(sp, array);
                break;

            case ID_UNSHIFT:
                copy(0, this, parCount, size_);
                stack.copy(sp + 2, this, 0, parCount);
                stack.setInt(sp, parCount);
                break;

            default:
                base.evalNative(index, stack, sp, parCount);
                break;
            }
        }
Example #2
0
        /**
         * Execute java member implementation. Parameters for functions start at
         * stack[sp+2]. Function and getter results are returned at stack[sp+0].
         * The assignement value for a setter is stored at stack[sp+0].
         */
        public virtual void evalNative(int index, JsArray stack, int sp, int parCount)
        {
            Object obj;

            switch (index)
            {
            // object methods

            case ID_NOOP:
                break;

            case ID_INIT_OBJECT:
                obj = stack.getObject(sp + 2);
                if (isConstruction(stack, sp))
                {
                    if (obj is Boolean || obj is Double ||
                        obj is String)
                    {
                        value = obj;
                    }
                    else if (obj is JsObject)
                    {
                        stack.setObject(sp - 1, obj);
                    }
                    // otherwise, don't do anything -- regular constructor call
                }
                else
                {
                    if (obj == null || obj == JsSystem.JS_NULL)
                    {
                        stack.setObject(sp, new JsObject(OBJECT_PROTOTYPE));
                    }
                    else
                    {
                        stack.setObject(sp, JsSystem.toJsObject(obj));
                    }
                }
                break;

            case ID_TO_STRING:
            case ID_TO_LOCALE_STRING:
                stack.setObject(sp, JsSystem.toString(stack.getObject(sp)));
                break;

            case ID_HAS_OWN_PROPERTY:
                stack.setBoolean(sp, data != null &&
                                 data[stack.getString(sp + 2)] != null);
                break;

            case ID_IS_PROTOTYPE_OF:
                obj = stack.getObject(sp + 2);
                stack.setBoolean(sp, false);
                while (obj is JsObject)
                {
                    if (obj == this)
                    {
                        stack.setBoolean(sp, true);
                        break;
                    }
                }
                break;

            case ID_PROPERTY_IS_ENUMERABLE:
                obj = getRawInPrototypeChain(stack.getString(sp + 2));
                stack.setBoolean(sp, obj != null && !(obj is JsFunction));
                break;

            case ID_VALUE_OF:
                stack.setObject(sp, value == null ? this : value);
                break;

            // Number methods

            case ID_INIT_NUMBER:
                if (isConstruction(stack, sp))
                {
                    value = (Double)(stack.getNumber(sp + 2));
                }
                else
                {
                    stack.setNumber(sp, stack.getNumber(sp + 2));
                }
                break;

            // Boolean methods

            case ID_INIT_BOOLEAN:
                if (isConstruction(stack, sp))
                {
                    value = stack.getBoolean(sp + 2) ? true : false;
                }
                else
                {
                    stack.setObject(sp, stack.getBoolean(sp + 2)
                          ? true : false);
                }
                break;

            case ID_INIT_STRING:
                if (isConstruction(stack, sp))
                {
                    value = stack.getString(sp + 2);
                }
                else
                {
                    stack.setObject(sp, parCount == 0 ? "" : stack.getString(sp + 2));
                }
                break;

            // initializers that can be used as functions need to be covered in Object

            case ID_INIT_ARRAY:
                JsArray array = (isConstruction(stack, sp)
          ? (JsArray)this : new JsArray());
                if (parCount == 1 && stack.isNumber(sp + 2))
                {
                    array.setSize(stack.getInt(sp + 2));
                }
                else
                {
                    for (int i2 = 0; i2 < parCount; i2++)
                    {
                        stack.copy(sp + i2 + 2, array, i2);
                    }
                }
                stack.setObject(sp, array);
                break;

            case ID_INIT_ERROR:
                if (isConstruction(stack, sp))
                {
                    setObject("message", stack.getString(sp + 2));
                }
                else
                {
                    stack.setObject(sp, new JsError(stack.getJsObject(sp),
                                                    stack.getString(sp + 2)));
                }
                break;


            case ID_INIT_FUNCTION:
                // Note: this will exchange the "this" object at sp-1 if it is used as constructor
                bool construction = isConstruction(stack, sp);

                StringBuilder buf = new StringBuilder("(function(");
                for (int i3 = 0; i3 < parCount - 1; i3++)
                {
                    if (i3 != 0)
                    {
                        buf.Append(',');
                    }
                    buf.Append(stack.getString(sp + 2 + i3));
                }
                buf.Append("){");
                if (parCount != 0)
                {
                    buf.Append(stack.getString(sp + 2 + parCount - 1));
                }
                buf.Append("});");

                Console.WriteLine("eval: " + buf);

                JsObject   global = (JsObject)stack.getObject(0);
                JsFunction eval   = (JsFunction)global.getObject("eval");
                stack.setObject(sp, global); // global
                stack.setObject(sp + 1, eval);
                stack.setObject(sp + 2, buf.ToString());
                eval.eval(stack, sp, 1);

                if (construction)
                {
                    stack.copy(sp, stack, sp - 1);
                }
                break;


            case ID_INIT_DATE:
                // reset to defaults
                if (isConstruction(stack, sp))
                {
                    JsDate d_2 = (JsDate)this;
                    if (parCount == 1)
                    {
                        d_2.time.setTime(new DateTime((long)stack.getNumber(sp + 2)));
                    }
                    else if (parCount > 1)
                    {
                        d_2.time.setTime(new DateTime());
                        int year = stack.getInt(sp + 2);
                        if (year >= 0 && year <= 99)
                        {
                            year += 1900;
                        }
                        d_2.setDate(false, year,
                                    stack.getNumber(sp + 3),
                                    stack.getNumber(sp + 4));

                        d_2.setTime(false, stack.getNumber(sp + 5),
                                    stack.getNumber(sp + 6),
                                    stack.getNumber(sp + 7),
                                    stack.getNumber(sp + 8));
                    }
                }
                else
                {
                    stack.setObject(sp,
                                    new JsDate(JsDate.DATE_PROTOTYPE).toString(true, true, true));
                }
                break;


            // global properties

            case ID_PRINT:
                Console.WriteLine(stack.getString(sp + 2));
                break;

            case ID_PARSE_INT:
                String s = stack.getString(sp + 2).Trim().ToLower();
                try {
                    if (stack.isNull(sp + 3))
                    {
                        stack.setInt(sp, s.StartsWith("0x")
                                ? Convert.ToInt32(s.Substring(2), 16)
                                    : Convert.ToInt32(s));
                    }
                    else
                    {
                        stack.setInt(sp, Convert.ToInt32(s, stack.getInt(sp + 3)));
                    }
                } catch (FormatException) {
                    stack.setInt(sp, 0);
                }
                break;

            case ID_PARSE_FLOAT:
                try {
                    stack.setNumber(sp, Convert.ToDouble(stack.getString(sp + 2)));
                } catch (FormatException) {
                    stack.setNumber(sp, double.NaN);
                }
                break;

            case ID_IS_NAN:
                stack.setBoolean(sp, double.IsNaN(stack.getNumber(sp + 2)));
                break;

            case ID_IS_FINITE:
                double d_ = stack.getNumber(sp + 2);
                stack.setBoolean(sp, !Double.IsInfinity(d_) && !Double.IsNaN(d_));
                break;

            case ID_DECODE_URI:
                obj = stack.getObject(sp + 2);
                if (obj is sbyte[])
                {
                    stack.setObject(sp, JsSystem.decodeURI((sbyte[])obj));
                }
                else
                {
                    stack.setObject(sp, JsSystem.decodeURI(obj.ToString()));
                }
                break;

            case ID_ENCODE_URI:
                obj = stack.getObject(sp + 2);
                if (obj is sbyte[])
                {
                    stack.setObject(sp, JsSystem.encodeURI((sbyte[])obj));
                }
                else
                {
                    stack.setObject(sp, JsSystem.encodeURI(obj.ToString()));
                }
                break;

            //TODO Implement
            case ID_DECODE_URI_COMPONENT:
            case ID_ENCODE_URI_COMPONENT:
                throw new Exception("NYI");

            // math properties

            case ID_ABS:
                stack.setNumber(sp, Math.Abs(stack.getNumber(sp + 2)));
                break;

            case ID_ACOS:
            case ID_ASIN:
            case ID_ATAN:
            case ID_ATAN2:
                throw new Exception("NYI");

            case ID_CEIL:
                stack.setNumber(sp, Math.Ceiling(stack.getNumber(sp + 2)));
                break;

            case ID_COS:
                stack.setNumber(sp, Math.Cos(stack.getNumber(sp + 2)));
                break;

            case ID_EXP:
                stack.setNumber(sp, JsSystem.exp(stack.getNumber(sp + 2)));
                break;

            case ID_FLOOR:
                stack.setNumber(sp, Math.Floor(stack.getNumber(sp + 2)));
                break;

            case ID_LOG:
                stack.setNumber(sp, JsSystem.ln(stack.getNumber(sp + 2)));
                break;

            case ID_MAX:
                double d2 = Double.NegativeInfinity;
                for (int i3 = 0; i3 < parCount; i3++)
                {
                    d2 = Math.Max(d2, stack.getNumber(sp + 2 + i3));
                }
                stack.setNumber(sp, d2);
                break;

            case ID_MIN:
                double d3 = Double.PositiveInfinity;
                for (int i4 = 0; i4 < parCount; i4++)
                {
                    d3 = Math.Min(d3, stack.getNumber(sp + 2 + i4));
                }
                stack.setNumber(sp, d3);
                break;

            case ID_POW:
                stack.setNumber(sp, JsSystem.pow(stack.getNumber(sp + 2),
                                                 stack.getNumber(sp + 3)));
                break;

            case ID_RANDOM:
                stack.setNumber(sp, JsSystem.random.NextDouble());
                break;

            case ID_ROUND:
                stack.setNumber(sp, Math.Floor(stack.getNumber(sp + 2) + 0.5));
                break;

            case ID_SIN:
                stack.setNumber(sp, Math.Sin(stack.getNumber(sp + 2)));
                break;

            case ID_SQRT:
                stack.setNumber(sp, Math.Sqrt(stack.getNumber(sp + 2)));
                break;

            case ID_TAN:
                stack.setNumber(sp, Math.Tan(stack.getNumber(sp + 2)));
                break;

            // string methods

            case ID_FROM_CHAR_CODE:
                char[] chars = new char[parCount];
                for (int i5 = 0; i5 < parCount; i5++)
                {
                    chars[i5] = (char)stack.getInt(sp + 2 + i5);
                }
                stack.setObject(sp, new String(chars));
                break;

            // string.prototype methods

            case ID_CHAR_AT:
                s = stack.getString(sp);
                int i6 = stack.getInt(sp + 2);
                stack.setObject(sp, i6 < 0 || i6 >= s.Length
                    ? "" : s.Substring(i6, 1));
                break;

            case ID_CHAR_CODE_AT:
                s = stack.getString(sp);
                int i7 = stack.getInt(sp + 2);
                stack.setNumber(sp, i7 < 0 || i7 >= s.Length
                        ? Double.NaN : s[i7]);
                break;

            case ID_CONCAT:
                buf = new StringBuilder(stack.getString(sp));
                for (int i8 = 0; i8 < parCount; i8++)
                {
                    buf.Append(stack.getString(sp + i8 + 2));
                }
                stack.setObject(sp, buf.ToString());
                break;

            case ID_INDEX_OF:
                stack.setNumber(sp, stack.getString(sp).IndexOf(stack.getString(sp + 2),
                                                                stack.getInt(sp + 3)));
                break;

            case ID_LAST_INDEX_OF:
                s = stack.getString(sp);
                String find = stack.getString(sp + 2);
                double d4   = stack.getNumber(sp + 3);
                int    max  = (Double.IsNaN(d4)) ? s.Length : (int)d4;

                int best = -1;
                while (true)
                {
                    int found = s.IndexOf(find, best + 1);
                    if (found == -1 || found > max)
                    {
                        break;
                    }
                    best = found;
                }

                stack.setNumber(sp, best);
                break;

            case ID_LOCALE_COMPARE:
                stack.setNumber(sp,
                                stack.getString(sp).CompareTo(stack.getString(sp + 2)));
                break;

            case ID_REPLACE:
                s    = stack.getString(sp);
                find = stack.getString(sp + 2);
                String replace = stack.getString(sp + 3);
                if (!find.Equals(""))
                {
                    StringBuilder sb     = new StringBuilder(s);
                    int           length = find.Length;

                    // Parse nodes into vector
                    while ((index = sb.ToString().IndexOf(find)) >= 0)
                    {
                        sb.Remove(index, index + length);
                        sb.Insert(index, replace);
                    }
                    stack.setObject(sp, sb.ToString());
                    sb = null;
                }
                break;

            case ID_MATCH:
            case ID_SEARCH:
                throw new Exception("Regexp NYI");

            case ID_SLICE:
                s = stack.getString(sp);
                int len   = s.Length;
                int start = stack.getInt(sp + 2);
                int end   = stack.isNull(sp + 3) ? len : stack.getInt(sp + 3);
                if (start < 0)
                {
                    start = Math.Max(len + start, 0);
                }
                if (end < 0)
                {
                    end = Math.Max(len + start, 0);
                }
                if (start > len)
                {
                    start = len;
                }
                if (end > len)
                {
                    end = len;
                }
                if (end < start)
                {
                    end = start;
                }
                stack.setObject(sp, s.Substring(start, end - start));
                break;

            case ID_SPLIT:
                s = stack.getString(sp);
                String sep   = stack.getString(sp + 2);
                double limit = stack.getNumber(sp + 3);
                if (Double.IsNaN(limit) || limit < 1)
                {
                    limit = Double.MaxValue;
                }

                JsArray a = new JsArray();
                if (sep.Length == 0)
                {
                    if (s.Length < limit)
                    {
                        limit = s.Length;
                    }
                    for (int i9 = 0; i9 < limit; i9++)
                    {
                        a.setObject(i9, s.Substring(i9, 1));
                    }
                }
                else
                {
                    int cut0 = 0;
                    while (cut0 < s.Length && a.size() < limit)
                    {
                        int cut = s.IndexOf(sep, cut0);
                        if (cut == -1)
                        {
                            cut = s.Length;
                        }
                        a.setObject(a.size(), s.Substring(cut0, cut - cut0));
                        cut0 = cut + sep.Length;
                    }
                }
                stack.setObject(sp, a);
                break;

            case ID_SUBSTRING:
                s     = stack.getString(sp);
                len   = s.Length;
                start = stack.getInt(sp + 2);
                end   = stack.isNull(sp + 3) ? len : stack.getInt(sp + 3);
                if (start > end)
                {
                    int tmp = end;
                    end   = start;
                    start = tmp;
                }
                start = Math.Min(Math.Max(0, start), len);
                end   = Math.Min(Math.Max(0, end), len);
                stack.setObject(sp, s.Substring(start, end - start));
                break;

            case ID_TO_LOWER_CASE: //TODO: which locale to use as defautlt? us?
            case ID_TO_LOCALE_LOWER_CASE:
                stack.setObject(sp, stack.getString(sp + 2).ToLower());
                break;

            case ID_TO_UPPER_CASE: //TODO: which locale to use as defautlt? us?
            case ID_TO_LOCALE_UPPER_CASE:
                stack.setObject(sp, stack.getString(sp + 2).ToUpper());
                break;

            case ID_LENGTH:
                stack.setInt(sp, ToString().Length);
                break;

            case ID_LENGTH_SET:
                // cannot be changed!
                break;

            case ID_TO_EXPONENTIAL:
            case ID_TO_FIXED:
            case ID_TO_PRECISION:
                stack.setObject(sp, JsSystem.formatNumber(index,
                                                          stack.getNumber(sp + 2), stack.getNumber(sp + 3)));
                break;

            case ID_UTC:
                JsDate date = new JsDate(JsDate.DATE_PROTOTYPE);
                date.time.setTime(new DateTime());
                int year2 = stack.getInt(sp + 2);
                if (year2 >= 0 && year2 <= 99)
                {
                    year2 += 1900;
                }
                date.setDate(true, year2,
                             stack.getNumber(sp + 3),
                             stack.getNumber(sp + 4));

                date.setTime(true, stack.getNumber(sp + 5),
                             stack.getNumber(sp + 6),
                             stack.getNumber(sp + 7),
                             stack.getNumber(sp + 8));

                stack.setNumber(sp, date.time.getTime().Ticks);
                break;

            case ID_PARSE:
                double[] vals = { Double.NaN, Double.NaN, Double.NaN,
                                  Double.NaN, Double.NaN, Double.NaN, Double.NaN };

                s = stack.getString(sp + 2);
                int curr = -1;
                int pos  = 0;
                for (int i10 = 0; i10 < s.Length; i10++)
                {
                    char c = s[i10];
                    if (c >= '0' && c <= '9')
                    {
                        if (curr == -1)
                        {
                            curr = c - 48;
                        }
                        else
                        {
                            curr = curr * 10 + (c - 48);
                        }
                    }
                    else if (curr != -1)
                    {
                        if (pos < vals.Length)
                        {
                            vals[pos++] = curr;
                        }
                        curr = -1;
                    }
                }
                if (curr != -1 && pos < vals.Length)
                {
                    vals[pos++] = curr;
                }

                bool utc = s.EndsWith("GMT") || s.EndsWith("UTC");
                date = new JsDate(JsDate.DATE_PROTOTYPE);
                date.time.setTime(new DateTime());
                date.setDate(utc, vals[0], vals[1], vals[2]);
                date.setTime(utc, vals[3], vals[4], vals[5], vals[6]);
                stack.setNumber(sp, date.time.getTime().Ticks);
                break;

            // Math constants

            case ID_E:
                stack.setNumber(sp, Math.E);
                break;

            case ID_LN10:
                stack.setNumber(sp, 2.302585092994046);
                break;

            case ID_LN2:
                stack.setNumber(sp, JsSystem.LN2);
                break;

            case ID_LOG2E:
                stack.setNumber(sp, 1.4426950408889634);
                break;

            case ID_LOG10E:
                stack.setNumber(sp, 0.4342944819032518);
                break;

            case ID_PI:
                stack.setNumber(sp, Math.PI);
                break;

            case ID_SQRT1_2:
                stack.setNumber(sp, Math.Sqrt(0.5));
                break;

            case ID_SQRT2:
                stack.setNumber(sp, Math.Sqrt(2.0));
                break;

            case ID_E_SET:
            case ID_LN10_SET:
            case ID_LN2_SET:
            case ID_LOG2E_SET:
            case ID_LOG10E_SET:
            case ID_PI_SET:
            case ID_SQRT1_2_SET:
            case ID_SQRT2_SET:
                // dont do anything: cannot overwrite those values!
                break;

            default:
                throw new ArgumentException("Unknown native id: " + index
                                            + " this: " + this);
            }
        }
Example #3
0
        /**
         * Evaluate this function. The this-pointer, function object and parameters
         * must be on stack (sp + 0 = context, sp + 1=function, sp + 2 = first param
         * etc.). The result is expected at sp + 0.
         */
        public void eval(JsArray stack, int sp, int actualParameterCount)
        {
            for (int i = actualParameterCount; i < expectedParameterCount; i++)
            {
                stack.setObject(sp + i + 2, null);
            }

            JsObject thisPtr = stack.getJsObject(sp);

            if (byteCode == null)
            {
                thisPtr.evalNative(index, stack, sp, actualParameterCount);
                return;
            }

            // sp initially points to context
            // bp points to parameter 0. context is at bp-2, lambda at bp-1

            sp += 2;
            int bp = sp;

            JsObject context;

            // note: arguments available here only!
            if (localNames != null)
            {
                context            = new JsObject(JsObject.OBJECT_PROTOTYPE);
                context.scopeChain = this.context;
                JsArguments args = new JsArguments(this, context);
                for (int i = 0; i < expectedParameterCount; i++)
                {
                    context.addVar(localNames[i], stack.getObject(sp + i));
                    args.addVar("" + i, (Int32)(i));
                }
                for (int i = expectedParameterCount; i < this.localNames.Length; i++)
                {
                    context.addVar(localNames[i], null);
                }
                for (int i = expectedParameterCount; i < actualParameterCount; i++)
                {
                    args.setObject("" + i, stack.getObject(bp + i));
                }
                args.setNumber("length", actualParameterCount);
                args.setObject("callee", this);
                context.addVar("arguments", args);
            }
            else
            {
                context = this.context;
                sp     += expectedParameterCount + varCount;
            }

            int initialSp = sp;
            int opcode;

            sbyte[] bytecode = this.byteCode;
            int     pc       = 0;
            int     end      = bytecode.Length;

            try {
                while (pc < end)
                {
                    opcode = bytecode[pc++];

                    if (opcode < 0)
                    {
                        int imm;

                        if ((opcode & 1) == 0)
                        {
                            imm = bytecode[pc++];
                        }
                        else
                        {
                            imm = (bytecode[pc] << 8) | (bytecode[pc + 1] & 255);
                            pc += 2;
                        }

                        switch ((opcode & 0x0ff) >> 1)
                        {
                        case XOP_ADD:
                            stack.setNumber(sp - 1, stack.getNumber(sp - 1) + imm);
                            break;

                        case XOP_TRY_CALL:
                            try {
                                sp = sp - imm - 2; // on stack: context, lambda, params
                                JsFunction m1 = (JsFunction)stack.getObject(sp + 1);
                                m1.eval(stack, sp, imm);
                                stack.setBoolean(sp + 1, true);
                                sp += 2;
                            } catch (JsException e) {
                                stack.setObject(sp++, e.getError());
                                stack.setBoolean(sp++, false); // not successfull
                            } catch (Exception e) {
                                stack.setObject(sp++, new JsError(e));
                                stack.setBoolean(sp++, false); // not successfull
                            }
                            break;

                        case XOP_CALL:
                            sp = sp - imm - 2; // on stack: context, lambda, params
                            JsFunction m2 = (JsFunction)stack.getObject(sp + 1);
                            m2.eval(stack, sp++, imm);
                            // System.out.println("Ret val received: "
                            //  + stack.getObject(sp-1)+" sp: "+sp);
                            break;

                        case XOP_PUSH_FN:
                            stack.setObject(sp++,
                                            new JsFunction(functionLiterals[imm], context));
                            break;

                        case XOP_GO:
                            pc += imm;
                            break;

                        case XOP_IF:
                            if (!stack.getBoolean(--sp))
                            {
                                pc += imm;
                            }
                            break;

                        case XOP_PUSH_INT:
                            stack.setNumber(sp++, imm);
                            break;

                        case XOP_LCL_GET:
                            stack.copy(bp + imm, stack, sp++);
                            break;

//          case XOP_LOCAL_INC:
//          stack.setFP(sp - 1, stack.getFP(sp - 1)
//          + stack.getFP(bp + imm));
//          // fall-through!
                        case XOP_LCL_SET:
                            stack.copy(sp - 1, stack, bp + imm);
                            break;

                        case XOP_NEXT:
                            IEnumerator en = (IEnumerator)stack.getObject(sp - 1);
                            if (en.MoveNext())
                            {
                                stack.setObject(sp++, en.Current);
                            }
                            else
                            {
                                pc += imm;
                            }
                            break;

                        case XOP_PUSH_NUM:
                            stack.setNumber(sp++, numberLiterals[imm]);
                            break;

                        case XOP_PUSH_STR:
                            // System.out.println("String:" + stringList[(int)param]);
                            stack.setObject(sp++, stringLiterals[imm]);
                            break;

                        default:
                            throw new Exception("Illegal opcode: "
                                                + MyInteger.toString(opcode & 0xff, 16) + " par: " + imm);
                        } // switch
                    }
                    else
                    {
                        switch (opcode)
                        {
                        case OP_ADD:
                            if (stack.isNumber(sp - 2) && stack.isNumber(sp - 1))
                            {
                                stack.setNumber(sp - 2,
                                                stack.getNumber(sp - 2) + stack.getNumber(sp - 1));
                            }
                            else
                            {
                                stack.setObject(sp - 2,
                                                stack.getString(sp - 2) + stack.getString(sp - 1));
                            }
                            sp--;
                            break;

                        case OP_AND:
                            stack.setNumber(sp - 2, stack.getInt(sp - 2) & stack.getInt(sp - 1));
                            sp--;
                            break;


                        case OP_APPEND:
                            JsArray arr = (JsArray)stack.getObject(sp - 2);
                            stack.copy(sp - 1, arr, arr.size());
                            // ((Array)
                            // stack.getObject(sp-2)).addElement(stack.getObject(sp-1));
                            sp--;
                            break;

                        case OP_ASR:
                            stack.setNumber(sp - 2,
                                            (stack.getInt(sp - 2) & 0xffffffffL) >> (stack.getInt(sp - 1) & 0x1f));
                            sp--;
                            break;

                        case OP_CTX_GET:
                            context.vmGetOperation(stack, sp - 1, sp - 1);
                            break;

                        case OP_CTX_SET:
                            context.vmSetOperation(stack, sp - 1, sp - 2);
                            sp--; // take away name, not value
                            break;

                        case OP_CTX:
                            stack.setObject(sp++, context);
                            break;

                        case OP_DEC:
                            stack.setNumber(sp - 1, stack.getNumber(sp - 1) - 1);
                            break;

                        case OP_DEL:
                            stack.setBoolean(sp - 2, stack.getJsObject(sp - 2).delete(stack.getString(sp - 1)));
                            sp--;
                            break;

                        case OP_DIV:
                            stack.setNumber(sp - 2, stack.getNumber(sp - 2) / stack.getNumber(sp - 1));
                            sp--;
                            break;

                        case OP_DROP:
                            sp--;
                            break;

                        case OP_DUP:
                            stack.copy(sp - 1, stack, sp);
                            sp++;
                            break;

                        case OP_DDUP:
                            stack.copy(sp - 2, stack, sp, 2);
                            sp += 2;
                            break;

                        case OP_ENUM:
                            stack.setObject(sp - 1, ((JsObject)stack.getObject(sp - 1)).keys());
                            break;

                        case OP_EQEQEQ:
                            if (stack.getType(sp - 2) != stack.getType(sp - 1))
                            {
                                sp--;
                                stack.setObject(sp - 1, false);
                                break;
                            }
                            // fall-trough
                            goto case OP_EQEQ;

                        case OP_EQEQ:
                            // System.out.println(""+stack.getObject(sp-2)+ " = "+
                            // stack.getObject(sp-1));

                            int tX = stack.getType(sp - 2);
                            int tY = stack.getType(sp - 1);

                            if (tX == tY)
                            {
                                switch (tX)
                                {
                                case TYPE_UNDEFINED:
                                case TYPE_NULL:
                                    stack.setObject(sp - 2, true);
                                    break;

                                case TYPE_NUMBER:
                                    stack.setBoolean(sp - 2,
                                                     stack.getNumber(sp - 2) == stack.getNumber(sp - 1));
                                    break;

                                default:
                                    stack.setBoolean(sp - 2,
                                                     stack.getObject(sp - 2).Equals(
                                                         stack.getObject(sp - 1)));
                                    break;
                                }
                            }
                            else
                            {
                                bool result;
                                if ((tX == TYPE_UNDEFINED && tY == TYPE_NULL) ||
                                    (tX == TYPE_NULL && tY == TYPE_UNDEFINED))
                                {
                                    result = true;
                                }
                                else if (tX == TYPE_NUMBER || tY == TYPE_NUMBER)
                                {
                                    result = stack.getNumber(sp - 2) == stack.getNumber(sp - 1);
                                }
                                else if ((tX == TYPE_STRING && tY == TYPE_OBJECT) ||
                                         tX == TYPE_OBJECT && tY == TYPE_STRING)
                                {
                                    result = stack.getString(sp - 2)
                                             .Equals(stack.getString(sp - 1));
                                }
                                else
                                {
                                    result = false;
                                }
                                stack.setBoolean(sp - 2, result);
                            }
                            sp--;
                            break;

                        case OP_GET:
                            JsObject ctx = stack.getJsObject(sp - 2);
//          System.out.println("GetMember ctx: "+ctx);
//          System.out.println("GetMember name: " + stack.getObject(sp - 1));
                            ctx.vmGetOperation(stack, sp - 1, sp - 2);
                            sp--;
                            break;

                        case OP_GT:
                            if (stack.isNumber(sp - 2) && stack.isNumber(sp - 1))
                            {
                                stack.setObject(sp - 2,
                                                stack.getNumber(sp - 2) > stack.getNumber(sp - 1)
                                          ? true
                                          : false);
                            }
                            else
                            {
                                stack.setObject(sp - 2,
                                                stack.getString(sp - 2).CompareTo(stack.getString(sp - 1))
                                                > 0 ? true : false);
                            }
                            sp--;
                            break;

                        case OP_IN:
                            Object o = stack.getObject(sp - 1);
                            if (o is JsArray && stack.isNumber(sp - 2))
                            {
                                int i = stack.getInt(sp - 2);
                                stack.setObject(sp - 2,
                                                i >= 0 && i <= ((JsArray)o).size()
                                            ? true
                                                : false);
                                sp--;
                                break;
                            }
                            if (o is JsObject)
                            {
                                stack.setObject(sp - 2,
                                                ((JsObject)o).getRawInPrototypeChain(stack.getString(sp - 2)) == null
                                            ? true
                                            : false);
                                sp--;
                                break;
                            }
                            stack.setObject(sp - 2, false);
                            sp--;
                            break;

                        case OP_INC:
                            stack.setNumber(sp - 1, stack.getNumber(sp - 1) + 1);
                            break;

                        case OP_INV:
                            stack.setInt(sp - 1, ~stack.getInt(sp - 1));
                            break;

                        case OP_LT:
                            if (stack.isNumber(sp - 2) && stack.isNumber(sp - 1))
                            {
                                stack.setObject(sp - 2, stack.getNumber(sp - 2) <
                                                stack.getNumber(sp - 1) ? true : false);
                            }
                            else
                            {
                                stack.setObject(sp - 2, stack.getString(sp - 2).
                                                CompareTo(stack.getString(sp - 1)) < 0
                                        ? true : false);
                            }
                            sp--;
                            break;

                        case OP_MOD:
                            stack.setNumber(sp - 2,
                                            (stack.getNumber(sp - 2) % (stack.getNumber(sp - 1))));
                            sp--;
                            break;

                        case OP_MUL:
                            stack.setNumber(sp - 2, stack.getNumber(sp - 2) * stack
                                            .getNumber(sp - 1));
                            sp--;
                            break;

                        case OP_NEW_ARR:
                            stack.setObject(sp++, new JsArray());
                            break;

                        case OP_NEW:
                            JsFunction constructor = ((JsFunction)stack.getObject(sp - 1));
                            ctx = constructor.factory.newInstance(constructor.factoryTypeId);
                            stack.setObject(sp - 1, ctx);
                            stack.setObject(sp++, ctx);
                            stack.setObject(sp++, constructor);
                            break;

                        case OP_NEW_OBJ:
                            stack.setObject(sp++, new JsObject(OBJECT_PROTOTYPE));
                            break;

                        case OP_NEG:
                            stack.setNumber(sp - 1, -stack.getNumber(sp - 1));
                            break;

                        case OP_NOT:
                            stack.setObject(sp - 1, stack.getBoolean(sp - 1) ? false
                  : true);
                            break;

                        case OP_OR:
                            stack.setNumber(sp - 2, stack.getInt(sp - 2) | stack.getInt(sp - 1));
                            sp--;
                            break;

                        case OP_PUSH_FALSE:
                            stack.setObject(sp++, false);
                            break;

                        case OP_PUSH_GLOBAL:
                            stack.setObject(sp++, stack.getObject(0));
                            break;

                        case OP_PUSH_NULL:
                            stack.setObject(sp++, JsSystem.JS_NULL);
                            break;

                        case OP_PUSH_THIS:
                            stack.setObject(sp++, thisPtr);
                            break;

                        case OP_PUSH_TRUE:
                            stack.setObject(sp++, true);
                            break;

                        case OP_PUSH_UNDEF:
                            stack.setObject(sp++, null);
                            break;

                        case OP_RET:
                            // System.out.println("sp: "+sp+" returning:
                            // "+stack.getObject(sp-1));
                            stack.copy(sp - 1, stack, bp - 2);
                            return;

                        case OP_ROT:
                            stack.copy(sp - 3, stack, sp - 2, 3);
                            stack.copy(sp, stack, sp - 3);
                            break;

                        case OP_SET_KC:
                            // ctx: sp-3
                            // property name: sp-2
                            // value to set: sp-1;

                            ctx = stack.getJsObject(sp - 3);
                            ctx.vmSetOperation(stack, sp - 2, sp - 1);

                            // key = (String) stack.getObject(sp-2);
                            // Object curr = ctx.getRaw(key);
                            // System.out.println("SetMember KC ctx: "+ctx);
                            // System.out.println("SetMember name: "+stack.getObject(sp-2));
                            // System.out.println("SetMember value: "+stack.getObject(sp-1));

                            sp -= 2; // leave value on the stack(!)
                            break;

                        case OP_SET:
                            ctx = stack.getJsObject(sp - 2);
                            ctx.vmSetOperation(stack, sp - 1, sp - 3);

                            // key = (String) stack.getObject(sp-1);
                            // curr = ctx.getRaw(key);

                            // System.out.println("SetMember KV ctx: "+ctx);
                            // System.out.println("SetMember name: "+stack.getObject(sp-1));
                            // System.out.println("SetMember value: "+stack.getObject(sp-3));

                            sp -= 2; // leave value on the stack(!)
                            break;

                        case OP_SHR:
                            stack.setNumber(sp - 2, stack.getInt(sp - 2) >> (stack.getInt(sp - 1) & 0x1f));
                            sp--;
                            break;

                        case OP_SHL:
                            stack.setNumber(sp - 2, stack.getInt(sp - 2) << (stack.getInt(sp - 1) & 0x1f));
                            sp--;
                            break;

                        case OP_SUB:
                            stack.setNumber(sp - 2, stack.getNumber(sp - 2) - stack.getNumber(sp - 1));
                            sp--;
                            break;

                        case OP_SWAP:
                            stack.swap(sp - 1, sp - 2);
                            break;

                        case OP_THROW:
                            // line number is added in try..catch below
                            throw new JsException(stack.getJsObject(sp));

                        case OP_WITH_START:
                            JsObject nc = new JsObject((JsObject)stack.getObject(sp - 1));
                            nc.scopeChain = context;
                            context       = nc;
                            sp--;
                            break;

                        case OP_WITH_END:
                            context = context.scopeChain;
                            break;

                        case OP_TYPEOF:
                            stack.setObject(sp - 1, TYPE_NAMES[stack.getType(sp - 1)]);
                            break;

                        case OP_INSTANCEOF:
                            o = stack.getObject(sp - 2);
                            JsObject p = stack.getJsObject(sp - 1);
                            if (p is JsFunction && o is JsObject)
                            {
                                JsObject j = ((JsObject)o);
                                p = ((JsFunction)p).prototype;
                                while (j.__proto__ != null && j.__proto__ != p)
                                {
                                    j = j.__proto__;
                                }
                                stack.setBoolean(sp - 2, j != null);
                            }
                            else
                            {
                                stack.setObject(sp - 2, false);
                            }
                            sp--;
                            break;

                        case OP_XOR:
                            stack.setNumber(sp - 2, stack.getInt(sp - 2) ^ stack.getInt(sp - 1));
                            sp--;
                            break;

                        default:
                            throw new Exception("Illegal opcode: '" + ((char)opcode)
                                                + "'/" + opcode);
                        }
                    }
                }
            } catch (Exception e) {
                JsException jse;
                if (e is JsException)
                {
                    jse = (JsException)e;
                }
                else
                {
                    Console.WriteLine(e.StackTrace);
                    jse = new JsException(e);
                }
                if (jse.pc == -1)
                {
                    jse.pc         = pc - 1;
                    jse.lineNumber = getLineNumber(pc - 1);
                }
                throw jse;
            }

            if (sp == initialSp + 1)
            {
//    System.out.println("sp: "+sp+" returning: "+stack.getObject(sp-1));
                stack.copy(sp - 1, stack, bp - 2);
            }
            else if (sp == initialSp)
            {
//    System.out.println("sp: "+sp+" returning NULL");
                stack.setObject(bp - 2, null);
            }
            else
            {
                throw new Exception("too much or too little on the stack; sp: "
                                    + sp + " bp: " + bp + " varCount: " + varCount + " parCount: "
                                    + actualParameterCount);
            }
            return;
        }