@Override
  public Object execIdCall(
      IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
    if (!f.hasTag(ITERATOR_TAG)) {
      return super.execIdCall(f, cx, scope, thisObj, args);
    }
    int id = f.methodId();

    if (id == Id_constructor) {
      return jsConstructor(cx, scope, thisObj, args);
    }

    if (!(thisObj instanceof NativeIterator)) throw incompatibleCallError(f);

    NativeIterator iterator = (NativeIterator) thisObj;

    switch (id) {
      case Id_next:
        return iterator.next(cx, scope);

      case Id___iterator__:
        // / XXX: what about argument? SpiderMonkey apparently ignores it
        return thisObj;

      default:
        throw new IllegalArgumentException(String.valueOf(id));
    }
  }
 public Object next() {
   if (!iterator.hasNext()) {
     // Out of values. Throw StopIteration.
     throw new JavaScriptException(NativeIterator.getStopIterationObject(scope), null, 0);
   }
   return iterator.next();
 }
 private Object next(Context cx, Scriptable scope) {
   Boolean b = ScriptRuntime.enumNext(this.objectIterator);
   if (!b.booleanValue()) {
     // Out of values. Throw StopIteration.
     throw new JavaScriptException(NativeIterator.getStopIterationObject(scope), null, 0);
   }
   return ScriptRuntime.enumId(this.objectIterator, cx);
 }
  /* The JavaScript constructor */
  private static Object jsConstructor(
      Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
    if (args.length == 0 || args[0] == null || args[0] == Undefined.instance) {
      Object argument = args.length == 0 ? Undefined.instance : args[0];
      throw ScriptRuntime.typeError1("msg.no.properties", ScriptRuntime.toString(argument));
    }
    Scriptable obj = ScriptRuntime.toObject(scope, args[0]);
    boolean keyOnly = args.length > 1 && ScriptRuntime.toBoolean(args[1]);
    if (thisObj != null) {
      // Called as a function. Convert to iterator if possible.

      // For objects that implement java.lang.Iterable or
      // java.util.Iterator, have JavaScript Iterator call the underlying
      // iteration methods
      Iterator<?> iterator = VMBridge.instance.getJavaIterator(cx, scope, obj);
      if (iterator != null) {
        scope = ScriptableObject.getTopLevelScope(scope);
        return cx.getWrapFactory()
            .wrap(cx, scope, new WrappedJavaIterator(iterator, scope), WrappedJavaIterator.class);
      }

      // Otherwise, just call the runtime routine
      Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj, keyOnly);
      if (jsIterator != null) {
        return jsIterator;
      }
    }

    // Otherwise, just set up to iterate over the properties of the object.
    // Do not call __iterator__ method.
    Object objectIterator =
        ScriptRuntime.enumInit(
            obj,
            cx,
            keyOnly
                ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR
                : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR);
    ScriptRuntime.setEnumNumbers(objectIterator, true);
    NativeIterator result = new NativeIterator(objectIterator);
    result.setPrototype(ScriptableObject.getClassPrototype(scope, result.getClassName()));
    result.setParentScope(scope);
    return result;
  }
  static void init(ScriptableObject scope, boolean sealed) {
    // Iterator
    NativeIterator iterator = new NativeIterator();
    iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);

    // Generator
    NativeGenerator.init(scope, sealed);

    // StopIteration
    NativeObject obj = new StopIteration();
    obj.setPrototype(getObjectPrototype(scope));
    obj.setParentScope(scope);
    if (sealed) {
      obj.sealObject();
    }
    ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, ScriptableObject.DONTENUM);
    // Use "associateValue" so that generators can continue to
    // throw StopIteration even if the property of the global
    // scope is replaced or deleted.
    scope.associateValue(ITERATOR_TAG, obj);
  }