Package com.google.minijoe.sys

Source Code of com.google.minijoe.sys.JsFunction

// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.minijoe.sys;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.Enumeration;

/**
* Javascript function and bytecode interpreter implementation.
*
* @author Stefan Haustein
*/
public class JsFunction extends JsObject {

  public static final int OP_NOP = 0x00;
  public static final int OP_ADD = 0x01;
  public static final int OP_AND = 0x02;
  public static final int OP_APPEND = 0X03;
  public static final int OP_ASR = 0x04;
  public static final int OP_ENUM = 0x05;
  public static final int OP_IN = 0x06;
  public static final int OP_DIV = 0x07;
  public static final int OP_DUP = 0x08;
  public static final int OP_EQEQ = 0x09;
  public static final int OP_CTX_GET = 0x0a;
  public static final int OP_GET = 0x0b;
  public static final int OP_CTX = 0x0c;
  public static final int OP_DEL = 0x0d;
  public static final int OP_GT  = 0x0e;
  public static final int OP_THROW = 0x0F;

  public static final int OP_INC = 0x10;
  public static final int OP_DEC = 0x11;
  public static final int OP_LT  = 0x12;
  public static final int OP_MOD = 0x13;
  public static final int OP_MUL = 0x14;
  public static final int OP_NEG  = 0x15;
  public static final int OP_NEW_ARR = 0x16;
  public static final int OP_NEW_OBJ = 0x17;
  public static final int OP_NEW  = 0x18;
  public static final int OP_NOT = 0x19;
  public static final int OP_OR  = 0x1a;
  public static final int OP_DROP = 0x1b;
  public static final int OP_PUSH_TRUE = 0x1c;
  public static final int OP_PUSH_FALSE = 0x1d;
  public static final int OP_RET  = 0x1e;
  public static final int OP_CTX_SET = 0x1f;

  public static final int OP_SET_KC  = 0x020;
  public static final int OP_SET  = 0x021;
  public static final int OP_SHL = 0x022;
  public static final int OP_SHR = 0x023;
  public static final int OP_SUB = 0x024;
  public static final int OP_SWAP =  0x025;
  public static final int OP_PUSH_THIS = 0x026;
  public static final int OP_PUSH_NULL = 0x027;
  public static final int OP_PUSH_UNDEF = 0x028;
  public static final int OP_DDUP = 0x29;
  public static final int OP_ROT = 0x2A;
  public static final int OP_EQEQEQ = 0x2B;    //TODO
  public static final int OP_XOR = 0x2C;
  public static final int OP_INV = 0x2D;
  public static final int OP_WITH_START = 0x2E;
  public static final int OP_WITH_END = 0x2F;

  public static final int OP_ABOVE = 0x30;
  public static final int OP_INSTANCEOF = 0x31;
  public static final int OP_TYPEOF = 0x32;
  public static final int OP_PUSH_GLOBAL = 0x33;

  public static final int XOP_TRY_CALL = 0xE6 >>> 1;
  public static final int XOP_ADD = 0xE8 >>> 1// add immediate to stacktop
  public static final int XOP_PUSH_FN = 0xEA >>> 1;
  public static final int XOP_PUSH_NUM = 0xEC >>> 1;
  public static final int XOP_GO = 0xEE >>> 1;
  public static final int XOP_IF = 0xF0 >>> 1;
  public static final int XOP_CALL = 0xF2 >>> 1;
//                           unused: 0x0F4
  public static final int XOP_LCL_GET = 0xF6 >>> 1;
  public static final int XOP_LCL_SET = 0xF8 >>> 1;
  public static final int XOP_NEXT = 0xFA >>> 1;
  public static final int XOP_PUSH_INT = 0xFC >>> 1;
  public static final int XOP_PUSH_STR = 0xFE >>> 1;

  public static final int BLOCK_COMMENT = 0x00;
  public static final int BLOCK_GLOBAL_STRING_TABLE = 0x10;
  public static final int BLOCK_NUMBER_LITERALS = 0x20;
  public static final int BLOCK_STRING_LITERALS = 0x30;
  public static final int BLOCK_FUNCTION_LITERALS = 0x50;
  public static final int BLOCK_LOCAL_VARIABLE_NAMES = 0x60;
  public static final int BLOCK_BYTE_CODE = 0x80;
  public static final int BLOCK_LINE_NUMBERS = 0xe0;
  public static final int BLOCK_DEBUG_DATA = 0xf0;

  static final int END_MARKER = 0x0ff;

  static final int ID_PROTOTYPE = 100;
  static final int ID_PROTOTYPE_SET = 101;
  static final int ID_APPLY = 102;

  public static final JsObject FUNCTION_PROTOTYPE =
    new JsObject(OBJECT_PROTOTYPE)
        .addVar("prototype", new JsFunction(ID_PROTOTYPE, -1))
        .addVar("length", new JsFunction(ID_LENGTH, -1))
        .addVar("apply", new JsFunction(ID_APPLY, 1))
        ;

  /** Number of declared parameters; -1 for native getter/setter */
  int expectedParameterCount;

  /** Number of local variables  */
  private int varCount;

  /** Byte code containing the implementation of this function */
  private byte[] byteCode;

  /** native method index if this function is implemented in Java */
  int index;

  String[] localNames;

  /** String literal table, used when putting strings on the stack. */
  private String[] stringLiterals;

  /** function literal table, used when putting strings on the stack. */
  private JsFunction[] functionLiterals;

  /** number literal table, used when putting strings on the stack. */
  private double[] numberLiterals;

  /**
   * Prototype object if this function is a constructor. Currently not used;
   * required to implement the JS prototype property.
   */
  private JsObject prototype;

  /** Factory to use if this is a native constructor. */
  private JsObjectFactory factory;

  /** Object factory id if this is a native constructor. */
  int factoryTypeId;

  /** Evaluation context for this function. */
  private JsObject context;

  private int[] lineNumbers;
 
  /**
   * Parses the given stream and runs the main function
   * @throws IOException
   */
  public static Object exec(DataInputStream dis, JsObject context)
      throws IOException{

    //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);
  }

  /**
   * Constructor for functions implemented in Java.
   */
  public JsFunction(int index, int parCount) {
    super(FUNCTION_PROTOTYPE);
    this.index = index;
    this.expectedParameterCount = parCount;
  }

  /**
   * Creates a new function from the given function literal and context.
   */
  public JsFunction(JsFunction literal, JsObject context){
    super(literal.__proto__);
    this.byteCode = literal.byteCode;
    this.context = context;
    this.functionLiterals = literal.functionLiterals;
    this.localNames = literal.localNames;
    this.numberLiterals = literal.numberLiterals;
    this.expectedParameterCount = literal.expectedParameterCount;
    this.prototype = literal.prototype;
    this.stringLiterals = literal.stringLiterals;
    this.varCount = literal.varCount;
    this.factory = JsSystem.getInstance();
    this.factoryTypeId = JsSystem.FACTORY_ID_OBJECT;
    this.lineNumbers = literal.lineNumbers;
  }


  /**
   * Constructor for constructors implemented in Java
   *
   * @param factory factory instance that is able to create new objects
   * @param factoryTypeId instance type id, used by the factory
   * @param prototype the prototype object
   * @param index the ID of the function call, used in evalNative
   * @param parCount
   */
  public JsFunction(JsObjectFactory factory, int factoryTypeId,
      JsObject prototype, int nativeId, int parCount) {
    this(nativeId, parCount);
    this.prototype = prototype;
    this.factory = factory;
    this.factoryTypeId = factoryTypeId;
  }

  /**
   * 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)
      throws IOException {
    super(FUNCTION_PROTOTYPE);
    // __proto__ above, prototype below...
    this.prototype = new JsObject(OBJECT_PROTOTYPE);

    byte[] 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 byte[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 byte[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:
        break loop;
      default:
        throw new IOException("Illegal Block type "
            + Integer.toString(blockType, 16));
      }
    }

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

  /**
   * 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, new Integer(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;
    byte[] 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 m = (JsFunction) stack.getObject(sp + 1);
                m.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 m = (JsFunction) stack.getObject(sp + 1);
              m.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:
              Enumeration e = (Enumeration) stack.getObject(sp - 1);
              if (e.hasMoreElements()){
                stack.setObject(sp++, e.nextElement());
              } 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 RuntimeException("Illegal opcode: "
                  + Integer.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, Boolean.FALSE);
                break;
              }
              // fall-trough

            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, Boolean.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)));
                }
              } else {
                boolean 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)
                      ? Boolean.TRUE
                      : Boolean.FALSE);
              } else {
                stack.setObject(sp - 2,
                    stack.getString(sp - 2).compareTo(stack.getString(sp - 1))
                    > 0 ? Boolean.TRUE : Boolean.FALSE);
              }
              sp--;
              break;

            case OP_IN:
              Object o = stack.getObject(sp - 1);
              if (o instanceof JsArray && stack.isNumber(sp - 2)) {
                int i = stack.getInt(sp - 2);
                stack.setObject(sp -2,
                    i >= 0 && i <= ((JsArray) o).size()
                        ? Boolean.TRUE
                        : Boolean.FALSE);
                sp--;
                break;
              }
              if (o instanceof JsObject) {
                stack.setObject(sp - 2,
                    ((JsObject) o).getRawInPrototypeChain(stack.getString(sp-2)) == null
                        ? Boolean.TRUE
                        : Boolean.FALSE);
                sp--;
                break;
              }
              stack.setObject(sp - 2, Boolean.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) ? Boolean.TRUE : Boolean.FALSE);
              } else {
                stack.setObject(sp - 2, stack.getString(sp - 2).
                    compareTo(stack.getString(sp - 1)) < 0
                    ? Boolean.TRUE : Boolean.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) ? Boolean.FALSE
                  : Boolean.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++, Boolean.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++, Boolean.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 instanceof JsFunction && o instanceof 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, Boolean.FALSE);
              }
              sp--;
              break;

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

            default:
              throw new RuntimeException("Illegal opcode: '" + ((char) opcode)
                  + "'/" + opcode);
          }
        }
      }
    } catch (Exception e) {
      JsException jse;
      if(e instanceof JsException) {
        jse = (JsException) e;
      } else {
        e.printStackTrace();
        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 RuntimeException("too much or too little on the stack; sp: "
          + sp + " bp: " + bp + " varCount: " + varCount + " parCount: "
          + actualParameterCount);
    }
    return;
  }

  int getLineNumber(int pc) {
    if(lineNumbers != null && lineNumbers.length > 0) {
      int i = 0;
      while(i + 2 < lineNumbers.length && lineNumbers[i+2] <= pc) {
        i += 2;
      }
      return lineNumbers[i + 1];
    }
    return -1;
  }
 
 
  /**
   * Returns the number of expected (declared) parameters.
   */
  public int getParameterCount(){
    return expectedParameterCount;
  }

  public void evalNative(int id, JsArray stack, int sp, int pc) {
    switch(id){
      case ID_PROTOTYPE:
        stack.setObject(sp, prototype);
        break;

      case ID_PROTOTYPE_SET:
        break;

      case ID_LENGTH:
        stack.setNumber(sp, pc);
        break;

      case ID_APPLY:
        throw new RuntimeException("NYI");

      default:
        super.evalNative(id, stack, sp, pc);
    }
  }

  /**
   * Returns a string representation of this function.
   */
  public String toString() {
    StringBuffer buf = new StringBuffer("function(");
    for (int i = 0; i < expectedParameterCount; i++) {
      if (i > 0){
        buf.append(", ");
      }
      buf.append(localNames == null ? ("p" + i) : localNames[i]);
    }
    buf.append(") { [");
    buf.append(byteCode == null ? "native code" : "bytecode");
    buf.append("] }");
    return buf.toString();
  }
}
TOP

Related Classes of com.google.minijoe.sys.JsFunction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.