org.mozilla.javascript.NativeIterator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino Show documentation
Show all versions of rhino Show documentation
Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically
embedded into Java applications to provide scripting to end users.
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;
import java.util.Iterator;
/**
* This class implements iterator objects. See
* http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Iterators
*
* @author Norris Boyd
*/
public final class NativeIterator extends IdScriptableObject {
private static final long serialVersionUID = -4136968203581667681L;
private static final Object ITERATOR_TAG = "Iterator";
static void init(Context cx, ScriptableObject scope, boolean sealed) {
// Iterator
NativeIterator iterator = new NativeIterator();
iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
// Generator
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
ES6Generator.init(scope, sealed);
} else {
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);
}
/** Only for constructing the prototype object. */
private NativeIterator() {}
private NativeIterator(Object objectIterator) {
this.objectIterator = objectIterator;
}
/**
* Get the value of the "StopIteration" object. Note that this value is stored in the top-level
* scope using "associateValue" so the value can still be found even if a script overwrites or
* deletes the global "StopIteration" property.
*
* @param scope a scope whose parent chain reaches a top-level scope
* @return the StopIteration object
*/
public static Object getStopIterationObject(Scriptable scope) {
Scriptable top = ScriptableObject.getTopLevelScope(scope);
return ScriptableObject.getTopScopeValue(top, ITERATOR_TAG);
}
private static final String STOP_ITERATION = "StopIteration";
public static final String ITERATOR_PROPERTY_NAME = "__iterator__";
public static class StopIteration extends NativeObject {
private static final long serialVersionUID = 2485151085722377663L;
private Object value = Undefined.instance;
public StopIteration() {}
public StopIteration(Object val) {
this.value = val;
}
public Object getValue() {
return value;
}
@Override
public String getClassName() {
return STOP_ITERATION;
}
/* StopIteration has custom instanceof behavior since it
* doesn't have a constructor.
*/
@Override
public boolean hasInstance(Scriptable instance) {
return instance instanceof StopIteration;
}
}
@Override
public String getClassName() {
return "Iterator";
}
@Override
protected void initPrototypeId(int id) {
String s;
int arity;
switch (id) {
case Id_constructor:
arity = 2;
s = "constructor";
break;
case Id_next:
arity = 0;
s = "next";
break;
case Id___iterator__:
arity = 1;
s = ITERATOR_PROPERTY_NAME;
break;
default:
throw new IllegalArgumentException(String.valueOf(id));
}
initPrototypeMethod(ITERATOR_TAG, id, s, arity);
}
@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);
}
NativeIterator iterator = ensureType(thisObj, NativeIterator.class, f);
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));
}
}
/* 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.typeErrorById(
"msg.no.properties", ScriptRuntime.toString(argument));
}
Scriptable obj = ScriptRuntime.toObject(cx, 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 = getJavaIterator(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,
scope,
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;
}
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);
}
/**
* If "obj" is a java.util.Iterator or a java.lang.Iterable, return a wrapping as a JavaScript
* Iterator. Otherwise, return null. This method is in VMBridge since Iterable is a JDK 1.5
* addition.
*/
private static Iterator> getJavaIterator(Object obj) {
if (obj instanceof Wrapper) {
Object unwrapped = ((Wrapper) obj).unwrap();
Iterator> iterator = null;
if (unwrapped instanceof Iterator) iterator = (Iterator>) unwrapped;
if (unwrapped instanceof Iterable) iterator = ((Iterable>) unwrapped).iterator();
return iterator;
}
return null;
}
public static class WrappedJavaIterator {
WrappedJavaIterator(Iterator> iterator, Scriptable scope) {
this.iterator = iterator;
this.scope = scope;
}
public Object next() {
if (!iterator.hasNext()) {
// Out of values. Throw StopIteration.
throw new JavaScriptException(
NativeIterator.getStopIterationObject(scope), null, 0);
}
return iterator.next();
}
public Object __iterator__(boolean b) {
return this;
}
private Iterator> iterator;
private Scriptable scope;
}
@Override
protected int findPrototypeId(String s) {
int id;
switch (s) {
case "constructor":
id = Id_constructor;
break;
case "next":
id = Id_next;
break;
case "__iterator__":
id = Id___iterator__;
break;
default:
id = 0;
break;
}
return id;
}
private static final int Id_constructor = 1,
Id_next = 2,
Id___iterator__ = 3,
MAX_PROTOTYPE_ID = 3;
private Object objectIterator;
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy