1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 package org.mozilla.javascript; 8 9 import java.util.Iterator; 10 11 /** 12 * This class implements iterator objects. See 13 * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Iterators 14 * 15 * @author Norris Boyd 16 */ 17 public final class NativeIterator extends IdScriptableObject { 18 private static final long serialVersionUID = -4136968203581667681L; 19 private static final Object ITERATOR_TAG = "Iterator"; 20 init(ScriptableObject scope, boolean sealed)21 static void init(ScriptableObject scope, boolean sealed) { 22 // Iterator 23 NativeIterator iterator = new NativeIterator(); 24 iterator.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed); 25 26 // Generator 27 NativeGenerator.init(scope, sealed); 28 29 // StopIteration 30 NativeObject obj = new StopIteration(); 31 obj.setPrototype(getObjectPrototype(scope)); 32 obj.setParentScope(scope); 33 if (sealed) { obj.sealObject(); } 34 ScriptableObject.defineProperty(scope, STOP_ITERATION, obj, 35 ScriptableObject.DONTENUM); 36 // Use "associateValue" so that generators can continue to 37 // throw StopIteration even if the property of the global 38 // scope is replaced or deleted. 39 scope.associateValue(ITERATOR_TAG, obj); 40 } 41 42 /** 43 * Only for constructing the prototype object. 44 */ NativeIterator()45 private NativeIterator() { 46 } 47 NativeIterator(Object objectIterator)48 private NativeIterator(Object objectIterator) { 49 this.objectIterator = objectIterator; 50 } 51 52 /** 53 * Get the value of the "StopIteration" object. Note that this value 54 * is stored in the top-level scope using "associateValue" so the 55 * value can still be found even if a script overwrites or deletes 56 * the global "StopIteration" property. 57 * @param scope a scope whose parent chain reaches a top-level scope 58 * @return the StopIteration object 59 */ getStopIterationObject(Scriptable scope)60 public static Object getStopIterationObject(Scriptable scope) { 61 Scriptable top = ScriptableObject.getTopLevelScope(scope); 62 return ScriptableObject.getTopScopeValue(top, ITERATOR_TAG); 63 } 64 65 private static final String STOP_ITERATION = "StopIteration"; 66 public static final String ITERATOR_PROPERTY_NAME = "__iterator__"; 67 68 static class StopIteration extends NativeObject { 69 private static final long serialVersionUID = 2485151085722377663L; 70 71 @Override getClassName()72 public String getClassName() { 73 return STOP_ITERATION; 74 } 75 76 /* StopIteration has custom instanceof behavior since it 77 * doesn't have a constructor. 78 */ 79 @Override hasInstance(Scriptable instance)80 public boolean hasInstance(Scriptable instance) { 81 return instance instanceof StopIteration; 82 } 83 } 84 85 @Override getClassName()86 public String getClassName() { 87 return "Iterator"; 88 } 89 90 @Override initPrototypeId(int id)91 protected void initPrototypeId(int id) { 92 String s; 93 int arity; 94 switch (id) { 95 case Id_constructor: arity=2; s="constructor"; break; 96 case Id_next: arity=0; s="next"; break; 97 case Id___iterator__: arity=1; s=ITERATOR_PROPERTY_NAME; break; 98 default: throw new IllegalArgumentException(String.valueOf(id)); 99 } 100 initPrototypeMethod(ITERATOR_TAG, id, s, arity); 101 } 102 103 @Override execIdCall(IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args)104 public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, 105 Scriptable thisObj, Object[] args) 106 { 107 if (!f.hasTag(ITERATOR_TAG)) { 108 return super.execIdCall(f, cx, scope, thisObj, args); 109 } 110 int id = f.methodId(); 111 112 if (id == Id_constructor) { 113 return jsConstructor(cx, scope, thisObj, args); 114 } 115 116 if (!(thisObj instanceof NativeIterator)) 117 throw incompatibleCallError(f); 118 119 NativeIterator iterator = (NativeIterator) thisObj; 120 121 switch (id) { 122 123 case Id_next: 124 return iterator.next(cx, scope); 125 126 case Id___iterator__: 127 /// XXX: what about argument? SpiderMonkey apparently ignores it 128 return thisObj; 129 130 default: 131 throw new IllegalArgumentException(String.valueOf(id)); 132 } 133 } 134 135 /* The JavaScript constructor */ jsConstructor(Context cx, Scriptable scope, Scriptable thisObj, Object[] args)136 private static Object jsConstructor(Context cx, Scriptable scope, 137 Scriptable thisObj, Object[] args) 138 { 139 if (args.length == 0 || args[0] == null || 140 args[0] == Undefined.instance) 141 { 142 Object argument = args.length == 0 ? Undefined.instance : args[0]; 143 throw ScriptRuntime.typeError1("msg.no.properties", 144 ScriptRuntime.toString(argument)); 145 } 146 Scriptable obj = ScriptRuntime.toObject(scope, args[0]); 147 boolean keyOnly = args.length > 1 && ScriptRuntime.toBoolean(args[1]); 148 if (thisObj != null) { 149 // Called as a function. Convert to iterator if possible. 150 151 // For objects that implement java.lang.Iterable or 152 // java.util.Iterator, have JavaScript Iterator call the underlying 153 // iteration methods 154 Iterator<?> iterator = 155 VMBridge.instance.getJavaIterator(cx, scope, obj); 156 if (iterator != null) { 157 scope = ScriptableObject.getTopLevelScope(scope); 158 return cx.getWrapFactory().wrap(cx, scope, 159 new WrappedJavaIterator(iterator, scope), 160 WrappedJavaIterator.class); 161 } 162 163 // Otherwise, just call the runtime routine 164 Scriptable jsIterator = ScriptRuntime.toIterator(cx, scope, obj, 165 keyOnly); 166 if (jsIterator != null) { 167 return jsIterator; 168 } 169 } 170 171 // Otherwise, just set up to iterate over the properties of the object. 172 // Do not call __iterator__ method. 173 Object objectIterator = ScriptRuntime.enumInit(obj, cx, 174 keyOnly ? ScriptRuntime.ENUMERATE_KEYS_NO_ITERATOR 175 : ScriptRuntime.ENUMERATE_ARRAY_NO_ITERATOR); 176 ScriptRuntime.setEnumNumbers(objectIterator, true); 177 NativeIterator result = new NativeIterator(objectIterator); 178 result.setPrototype(ScriptableObject.getClassPrototype(scope, 179 result.getClassName())); 180 result.setParentScope(scope); 181 return result; 182 } 183 next(Context cx, Scriptable scope)184 private Object next(Context cx, Scriptable scope) { 185 Boolean b = ScriptRuntime.enumNext(this.objectIterator); 186 if (!b.booleanValue()) { 187 // Out of values. Throw StopIteration. 188 throw new JavaScriptException( 189 NativeIterator.getStopIterationObject(scope), null, 0); 190 } 191 return ScriptRuntime.enumId(this.objectIterator, cx); 192 } 193 194 static public class WrappedJavaIterator 195 { WrappedJavaIterator(Iterator<?> iterator, Scriptable scope)196 WrappedJavaIterator(Iterator<?> iterator, Scriptable scope) { 197 this.iterator = iterator; 198 this.scope = scope; 199 } 200 next()201 public Object next() { 202 if (!iterator.hasNext()) { 203 // Out of values. Throw StopIteration. 204 throw new JavaScriptException( 205 NativeIterator.getStopIterationObject(scope), null, 0); 206 } 207 return iterator.next(); 208 } 209 __iterator__(boolean b)210 public Object __iterator__(boolean b) { 211 return this; 212 } 213 214 private Iterator<?> iterator; 215 private Scriptable scope; 216 } 217 218 // #string_id_map# 219 220 @Override findPrototypeId(String s)221 protected int findPrototypeId(String s) { 222 int id; 223 // #generated# Last update: 2007-06-11 09:43:19 EDT 224 L0: { id = 0; String X = null; 225 int s_length = s.length(); 226 if (s_length==4) { X="next";id=Id_next; } 227 else if (s_length==11) { X="constructor";id=Id_constructor; } 228 else if (s_length==12) { X="__iterator__";id=Id___iterator__; } 229 if (X!=null && X!=s && !X.equals(s)) id = 0; 230 break L0; 231 } 232 // #/generated# 233 return id; 234 } 235 236 private static final int 237 Id_constructor = 1, 238 Id_next = 2, 239 Id___iterator__ = 3, 240 MAX_PROTOTYPE_ID = 3; 241 242 // #/string_id_map# 243 244 private Object objectIterator; 245 } 246 247