Example #1
0
  private static void put(ESObject obj, String name, int n, int len) {
    ESId id = ESId.intern(name);

    obj.put(id, new NativeArray(name, n, len), DONT_ENUM);
  }
 public void setProperty(String name, ESBase value) throws Throwable {
   setProperty(ESId.intern(name), value);
 }
Example #3
0
/** JavaScript object */
class NativeArray extends Native {
  static ESId LENGTH = ESId.intern("length");
  static final int NEW = 1;
  static final int JOIN = NEW + 1;
  static final int TO_STRING = JOIN + 1;
  static final int REVERSE = TO_STRING + 1;
  static final int SORT = REVERSE + 1;

  // js1.2
  static final int CONCAT = SORT + 1;
  static final int POP = CONCAT + 1;
  static final int PUSH = POP + 1;
  static final int SHIFT = PUSH + 1;
  static final int UNSHIFT = SHIFT + 1;
  static final int SLICE = UNSHIFT + 1;
  static final int SPLICE = SLICE + 1;

  /** Create a new object based on a prototype */
  private NativeArray(String name, int n, int len) {
    super(name, len);

    this.n = n;
  }

  /** Creates the native Array object */
  static ESObject create(Global resin) {
    Native nativeArray = new NativeArray("Array", NEW, 1);
    ESArray proto = new ESArray();
    proto.prototype = resin.objProto;
    NativeWrapper array = new NativeWrapper(resin, nativeArray, proto, ESThunk.ARRAY_THUNK);
    resin.arrayProto = proto;

    put(proto, "join", JOIN, 1);
    put(proto, "toString", TO_STRING, 0);
    put(proto, "reverse", REVERSE, 0);
    put(proto, "sort", SORT, 0);

    // js1.2
    put(proto, "concat", CONCAT, 0);
    put(proto, "pop", POP, 0);
    put(proto, "push", PUSH, 0);
    put(proto, "shift", SHIFT, 0);
    put(proto, "unshift", UNSHIFT, 0);
    put(proto, "slice", SLICE, 2);
    put(proto, "splice", SPLICE, 0);

    proto.setClean();
    array.setClean();

    return array;
  }

  private static void put(ESObject obj, String name, int n, int len) {
    ESId id = ESId.intern(name);

    obj.put(id, new NativeArray(name, n, len), DONT_ENUM);
  }

  public ESBase call(Call eval, int length) throws Throwable {
    switch (n) {
      case NEW:
        return create(eval, length);

      case JOIN:
        if (length == 0) return toString(eval, length);
        else return join(eval, length);

      case TO_STRING:
        return toString(eval, length);

      case REVERSE:
        return reverse(eval, length);

      case SORT:
        return sort(eval, length);

      case CONCAT:
        return concat(eval, length);

      case POP:
        return pop(eval, length);

      case PUSH:
        return push(eval, length);

      case SHIFT:
        return shift(eval, length);

      case UNSHIFT:
        return unshift(eval, length);

      case SLICE:
        return slice(eval, length);

      case SPLICE:
        return splice(eval, length);

      default:
        throw new ESException("Unknown object function");
    }
  }

  ESBase create(Call eval, int length) throws Throwable {
    ESObject obj = Global.getGlobalProto().createArray();

    if (length == 0) return obj;
    if (length == 1) {
      ESBase arg = eval.getArg(0);

      if (arg instanceof ESNumber) obj.setProperty(LENGTH, ESNumber.create(arg.toInt32()));
      else obj.setProperty(0, arg);

      return obj;
    }

    for (int i = 0; i < length; i++) obj.setProperty(i, eval.getArg(i));

    return obj;
  }

  static ESBase join(ESObject array, String separator) throws Throwable {
    if (array.mark != 0) {
      return ESString.create("...");
    }
    array.mark = -1;

    try {
      int len = array.getProperty(LENGTH).toInt32();
      StringBuffer sbuf = new StringBuffer();

      for (int i = 0; i < len; i++) {
        if (i != 0) sbuf.append(separator);

        ESBase value = array.hasProperty(i);

        if (value != null && value != esNull && value != esUndefined) sbuf.append(value.toString());
      }

      return ESString.create(sbuf.toString());
    } finally {
      array.mark = 0;
    }
  }

  ESBase join(Call eval, int length) throws Throwable {
    String separator = length == 0 ? "," : eval.getArg(0).toString();

    ESObject array = eval.getArg(-1).toObject();

    return join(array, separator);
  }

  static ESBase toString(ESObject array) throws Throwable {
    return join(array, ",");
  }

  // XXX: different for non-js1.2
  ESBase toString(Call eval, int length) throws Throwable {
    ESObject array = eval.getArg(-1).toObject();

    return toString(array);
  }

  ESBase reverse(Call eval, int length) throws Throwable {
    ESObject array = eval.getArg(-1).toObject();

    int len = (int) array.getProperty(LENGTH).toInt32();
    for (int k = 0; k < len / 2; k++) {
      int firstIndex = k;
      int secondIndex = len - k - 1;

      ESBase first = array.hasProperty(firstIndex);
      ESBase second = array.hasProperty(secondIndex);

      if (first == null) array.delete(secondIndex);
      else array.setProperty(secondIndex, first);

      if (second == null) array.delete(firstIndex);
      else array.setProperty(firstIndex, second);
    }

    return array;
  }

  ESBase sort(Call eval, int length) throws Throwable {
    ESObject array = eval.getArg(-1).toObject();

    ESBase cmp = length == 0 ? null : eval.getArg(0);

    int len = (int) array.getProperty(LENGTH).toInt32();
    ESBase[] values = new ESBase[len];

    for (int i = 0; i < len; i++) values[i] = array.getProperty("" + i);

    qsort(values, 0, len, cmp);

    for (int i = 0; i < len; i++) {
      if (values[i] == esUndefined) array.delete("" + i);
      else array.setProperty("" + i, values[i]);
    }

    return array;
  }

  private void qsort(ESBase[] array, int offset, int length, ESBase cmp) throws Throwable {
    if (length == 2) {
      if (compare(cmp, array[offset], array[offset + 1]) > 0) {
        ESBase temp = array[offset];
        array[offset] = array[offset + 1];
        array[offset + 1] = temp;
      }
    } else if (length > 2) {
      int keyIndex = offset + length / 2;
      ESBase key = array[keyIndex];
      int keys = 0;
      int tail = 0;
      int val;

      if ((val = compare(cmp, array[offset], key)) > 0) {
        key = array[offset];
        array[offset] = array[keyIndex];
        array[keyIndex] = key;
      } else if (val == 0) keys++;

      if ((val = compare(cmp, key, array[offset + length - 1])) > 0) {
        key = array[offset + length - 1];
        array[offset + length - 1] = array[keyIndex];
        array[keyIndex] = key;
        keys = 0;
        tail = 1;

        if ((val = compare(cmp, array[offset], key)) > 0) {
          key = array[offset];
          array[offset] = array[keyIndex];
          array[keyIndex] = key;
        } else if (val == 0) keys++;
      } else if (val < 0) tail = 1;

      int i;
      if (keyIndex == offset + 1) {
        i = 2 + tail;
        keys++;
      } else i = 1 + tail;

      for (; i < length; i++) {
        int index = offset + i - tail;

        if (array[index] == key) {
          keys++;
          continue;
        }

        int cmpResult = compare(cmp, key, array[index]);
        if (cmpResult > 0 && keys != 0) {
          ESBase temp = array[index];
          array[index] = array[index - keys];
          array[index - keys] = temp;
        } else if (cmpResult < 0) {
          ESBase temp = array[offset + length - tail - 1];
          array[offset + length - tail - 1] = array[index];
          array[index] = temp;
          tail += 1;
        } else if (cmpResult == 0) keys++;
      }

      if (length - tail - keys > 1) qsort(array, offset, length - tail - keys, cmp);
      if (tail > 1) qsort(array, offset + length - tail, tail, cmp);
    }
  }

  private int compare(ESBase cmp, ESBase a, ESBase b) throws Throwable {
    if (a == b) return 0;
    else if (a == esUndefined) return 1;
    else if (b == esUndefined) return -1;
    else if (a == esNull) return 1;
    else if (b == esNull) return -1;
    else if (cmp != null) {
      // Call eval = new Call(ESGlobal.getGlobalProto(), false);
      Global resin = Global.getGlobalProto();
      Call eval = resin.getCall();

      eval.stack[0] = esNull;
      eval.stack[1] = a;
      eval.stack[2] = b;
      eval.top = 1;

      int result = cmp.call(eval, 2).toInt32();

      resin.freeCall(eval);

      return result;
    } else {
      String sa = a.toString();
      String sb = b.toString();

      return sa.compareTo(sb);
    }
  }

  ESBase concat(Call eval, int length) throws Throwable {
    ESArray array = Global.getGlobalProto().createArray();

    int k = 0;
    for (int i = -1; i < length; i++) {
      ESBase arg = eval.getArg(i);

      if (arg == esNull || arg == esUndefined || arg == esEmpty) continue;

      ESBase arglen = arg.hasProperty(LENGTH);

      if (arglen == null) {
        array.setProperty(k++, arg);
        continue;
      }

      int len = (int) arglen.toInt32();

      if (len < 0) {
        array.setProperty(k++, arg);
        continue;
      }

      for (int j = 0; j < len; j++) {
        ESBase obj = arg.hasProperty(j);

        if (obj != null) array.setProperty(k, obj);
        k++;
      }
    }
    array.setProperty(LENGTH, ESNumber.create(k));

    return array;
  }

  ESBase pop(Call eval, int length) throws Throwable {
    ESObject obj = eval.getArg(-1).toObject();

    ESBase lenObj = obj.hasProperty(LENGTH);
    int len;
    if (lenObj == null || (len = lenObj.toInt32()) <= 0) return esUndefined;

    ESBase value = obj.getProperty(len - 1);

    obj.setProperty(LENGTH, ESNumber.create(len - 1));

    return value;
  }

  ESBase push(Call eval, int length) throws Throwable {
    ESObject obj = eval.getArg(-1).toObject();

    ESBase lenObj = obj.getProperty(LENGTH);
    int len = lenObj.toInt32();
    if (len < 0) len = 0;

    for (int i = 0; i < length; i++) obj.setProperty(len + i, eval.getArg(i));

    ESNumber newLen = ESNumber.create(len + length);
    obj.setProperty(LENGTH, newLen);

    return newLen;
  }

  ESBase shift(Call eval, int length) throws Throwable {
    ESObject obj = eval.getArg(-1).toObject();

    ESBase lenObj = obj.hasProperty(LENGTH);
    int len;
    if (lenObj == null || (len = (int) lenObj.toInt32()) <= 0) return esUndefined;

    ESBase value = obj.getProperty(0);

    for (int i = 1; i < len; i++) {
      ESBase temp = obj.hasProperty(i);
      if (temp == null) obj.delete(ESString.create(i - 1));
      else obj.setProperty(i - 1, temp);
    }

    obj.setProperty(LENGTH, ESNumber.create(len - 1));

    return value;
  }

  ESBase unshift(Call eval, int length) throws Throwable {
    ESObject obj = eval.getArg(-1).toObject();

    ESBase lenObj = obj.getProperty(LENGTH);
    int len = lenObj.toInt32();
    if (len < 0) len = 0;

    if (length == 0) return ESNumber.create(0);

    for (int i = len - 1; i >= 0; i--) {
      ESBase value = obj.getProperty(i);

      if (value == null) obj.delete(ESString.create(length + i));
      else obj.setProperty(length + i, value);
    }

    for (int i = 0; i < length; i++) {
      ESBase value = eval.getArg(i);

      if (value == null) obj.delete(ESString.create(i));
      else obj.setProperty(i, value);
    }

    ESNumber numLen = ESNumber.create(len + length);
    obj.setProperty(LENGTH, numLen);

    return numLen;
  }

  ESBase slice(Call eval, int length) throws Throwable {
    ESObject obj = eval.getArg(-1).toObject();

    ESBase lenObj = obj.getProperty(LENGTH);
    int len = lenObj.toInt32();

    ESArray array = Global.getGlobalProto().createArray();
    if (len <= 0) return array;

    int start = 0;
    if (length > 0) start = eval.getArg(0).toInt32();
    if (start < 0) start += len;
    if (start < 0) start = 0;
    if (start > len) return array;

    int end = len;
    if (length > 1) end = eval.getArg(1).toInt32();
    if (end < 0) end += len;
    if (end < 0) return array;
    if (end > len) end = len;

    if (start >= end) return array;

    for (int i = 0; i < end - start; i++) {
      ESBase value = obj.hasProperty(start + i);

      if (value != null) array.setProperty(i, value);
    }

    array.setProperty(LENGTH, ESNumber.create(end - start));

    return array;
  }

  ESBase splice(Call eval, int length) throws Throwable {
    if (length < 2) return esUndefined;

    ESObject obj = eval.getArg(-1).toObject();

    int index = eval.getArg(0).toInt32();
    int count = eval.getArg(1).toInt32();
    boolean single = count == 1;

    ESBase lenObj = obj.getProperty(LENGTH);
    int len = lenObj.toInt32();

    if (index < 0) index += len;
    if (index < 0) index = 0;

    if (count < 0) count = 0;
    if (index + count > len) count = len - index;

    ESBase value;

    if (count < 1) value = esUndefined;
    else {
      value = Global.getGlobalProto().createArray();

      for (int i = 0; i < count; i++) value.setProperty(i, obj.getProperty(index + i));
    }

    int delta = length - 2 - count;
    if (delta < 0) {
      for (int i = 0; i < len - count; i++) {
        ESBase temp = obj.getProperty(i + index + count);
        if (temp == null) obj.delete(ESString.create(i + index + count + delta));
        else obj.setProperty(i + index + count + delta, temp);
      }
    } else if (delta > 0) {
      for (int i = len - count - 1; i >= 0; i--) {
        ESBase temp = obj.getProperty(i + index + count);
        if (temp == null) obj.delete(ESString.create(i + index + count + delta));
        else obj.setProperty(i + index + count + delta, temp);
      }
    }

    for (int i = 0; i < length - 2; i++) obj.setProperty(i + index, eval.getArg(i + 2));

    obj.setProperty(LENGTH, ESNumber.create(len - count + length - 2));

    return value;
  }
}