001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * Contributor(s):
017: * Norris Boyd
018: *
019: * Alternatively, the contents of this file may be used under the terms of
020: * the GNU General Public License Version 2 or later (the "GPL"), in which
021: * case the provisions of the GPL are applicable instead of those above. If
022: * you wish to allow use of your version of this file only under the terms of
023: * the GPL and not to allow others to use your version of this file under the
024: * MPL, indicate your decision by deleting the provisions above and replacing
025: * them with the notice and other provisions required by the GPL. If you do
026: * not delete the provisions above, a recipient may use your version of this
027: * file under either the MPL or the GPL.
028: *
029: * ***** END LICENSE BLOCK ***** */
030:
031: package org.mozilla.javascript;
032:
033: /**
034: * This class implements generator objects. See
035: * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Generators
036: *
037: * @author Norris Boyd
038: */
039: public final class NativeGenerator extends IdScriptableObject {
040: private static final Object GENERATOR_TAG = new Object();
041:
042: static NativeGenerator init(ScriptableObject scope, boolean sealed) {
043: // Generator
044: // Can't use "NativeGenerator().exportAsJSClass" since we don't want
045: // to define "Generator" as a constructor in the top-level scope.
046:
047: NativeGenerator prototype = new NativeGenerator();
048: if (scope != null) {
049: prototype.setParentScope(scope);
050: prototype.setPrototype(getObjectPrototype(scope));
051: }
052: prototype.activatePrototypeMap(MAX_PROTOTYPE_ID);
053: if (sealed) {
054: prototype.sealObject();
055: }
056:
057: // Need to access Generator prototype when constructing
058: // Generator instances, but don't have a generator constructor
059: // to use to find the prototype. Use the "associateValue"
060: // approach instead.
061: if (scope != null) {
062: scope.associateValue(GENERATOR_TAG, prototype);
063: }
064:
065: return prototype;
066: }
067:
068: /**
069: * Only for constructing the prototype object.
070: */
071: private NativeGenerator() {
072: }
073:
074: public NativeGenerator(Scriptable scope, NativeFunction function,
075: Object savedState) {
076: this .function = function;
077: this .savedState = savedState;
078: // Set parent and prototype properties. Since we don't have a
079: // "Generator" constructor in the top scope, we stash the
080: // prototype in the top scope's associated value.
081: Scriptable top = ScriptableObject.getTopLevelScope(scope);
082: this .setParentScope(top);
083: NativeGenerator prototype = (NativeGenerator) ScriptableObject
084: .getTopScopeValue(top, GENERATOR_TAG);
085: this .setPrototype(prototype);
086: }
087:
088: public static final int GENERATOR_SEND = 0, GENERATOR_THROW = 1,
089: GENERATOR_CLOSE = 2;
090:
091: public String getClassName() {
092: return "Generator";
093: }
094:
095: /**
096: * Close the generator if it is still open.
097: */
098: public void finalize() throws Throwable {
099: if (savedState != null) {
100: // This is a little tricky since we are most likely running in
101: // a different thread. We need to get a Context to run this, and
102: // we must call "doTopCall" since this will likely be the outermost
103: // JavaScript frame on this thread.
104: Context cx = Context.getCurrentContext();
105: ContextFactory factory = cx != null ? cx.getFactory()
106: : ContextFactory.getGlobal();
107: Scriptable scope = ScriptableObject.getTopLevelScope(this );
108: factory.call(new CloseGeneratorAction(this ));
109: }
110: }
111:
112: private static class CloseGeneratorAction implements ContextAction {
113: private NativeGenerator generator;
114:
115: CloseGeneratorAction(NativeGenerator generator) {
116: this .generator = generator;
117: }
118:
119: public Object run(Context cx) {
120: Scriptable scope = ScriptableObject
121: .getTopLevelScope(generator);
122: Callable closeGenerator = new Callable() {
123: public Object call(Context cx, Scriptable scope,
124: Scriptable this Obj, Object[] args) {
125: return ((NativeGenerator) this Obj).resume(cx,
126: scope, GENERATOR_CLOSE,
127: new GeneratorClosedException());
128: }
129: };
130: return ScriptRuntime.doTopCall(closeGenerator, cx, scope,
131: generator, null);
132: }
133: }
134:
135: protected void initPrototypeId(int id) {
136: String s;
137: int arity;
138: switch (id) {
139: case Id_close:
140: arity = 1;
141: s = "close";
142: break;
143: case Id_next:
144: arity = 1;
145: s = "next";
146: break;
147: case Id_send:
148: arity = 0;
149: s = "send";
150: break;
151: case Id_throw:
152: arity = 0;
153: s = "throw";
154: break;
155: case Id___iterator__:
156: arity = 1;
157: s = "__iterator__";
158: break;
159: default:
160: throw new IllegalArgumentException(String.valueOf(id));
161: }
162: initPrototypeMethod(GENERATOR_TAG, id, s, arity);
163: }
164:
165: public Object execIdCall(IdFunctionObject f, Context cx,
166: Scriptable scope, Scriptable this Obj, Object[] args) {
167: if (!f.hasTag(GENERATOR_TAG)) {
168: return super .execIdCall(f, cx, scope, this Obj, args);
169: }
170: int id = f.methodId();
171:
172: if (!(this Obj instanceof NativeGenerator))
173: throw incompatibleCallError(f);
174:
175: NativeGenerator generator = (NativeGenerator) this Obj;
176:
177: switch (id) {
178:
179: case Id_close:
180: // need to run any pending finally clauses
181: return generator.resume(cx, scope, GENERATOR_CLOSE,
182: new GeneratorClosedException());
183:
184: case Id_next:
185: // arguments to next() are ignored
186: generator.firstTime = false;
187: return generator.resume(cx, scope, GENERATOR_SEND,
188: Undefined.instance);
189:
190: case Id_send: {
191: Object arg = args.length > 0 ? args[0] : Undefined.instance;
192: if (generator.firstTime && !arg.equals(Undefined.instance)) {
193: throw ScriptRuntime.typeError0("msg.send.newborn");
194: }
195: return generator.resume(cx, scope, GENERATOR_SEND, arg);
196: }
197:
198: case Id_throw:
199: return generator.resume(cx, scope, GENERATOR_THROW,
200: args.length > 0 ? args[0] : Undefined.instance);
201:
202: case Id___iterator__:
203: return this Obj;
204:
205: default:
206: throw new IllegalArgumentException(String.valueOf(id));
207: }
208: }
209:
210: private Object resume(Context cx, Scriptable scope, int operation,
211: Object value) {
212: if (savedState == null) {
213: if (operation == GENERATOR_CLOSE)
214: return Undefined.instance;
215: Object thrown;
216: if (operation == GENERATOR_THROW) {
217: thrown = value;
218: } else {
219: thrown = NativeIterator.getStopIterationObject(scope);
220: }
221: throw new JavaScriptException(thrown, lineSource,
222: lineNumber);
223: }
224: try {
225: synchronized (this ) {
226: // generator execution is necessarily single-threaded and
227: // non-reentrant.
228: // See https://bugzilla.mozilla.org/show_bug.cgi?id=349263
229: if (locked)
230: throw ScriptRuntime
231: .typeError0("msg.already.exec.gen");
232: locked = true;
233: }
234: return function.resumeGenerator(cx, scope, operation,
235: savedState, value);
236: } catch (GeneratorClosedException e) {
237: // On closing a generator in the compile path, the generator
238: // throws a special exception. This ensures execution of all pending
239: // finalizers and will not get caught by user code.
240: return Undefined.instance;
241: } catch (RhinoException e) {
242: lineNumber = e.lineNumber();
243: lineSource = e.lineSource();
244: savedState = null;
245: throw e;
246: } finally {
247: synchronized (this ) {
248: locked = false;
249: }
250: if (operation == GENERATOR_CLOSE)
251: savedState = null;
252: }
253: }
254:
255: // #string_id_map#
256:
257: protected int findPrototypeId(String s) {
258: int id;
259: // #generated# Last update: 2007-06-14 13:13:03 EDT
260: L0: {
261: id = 0;
262: String X = null;
263: int c;
264: int s_length = s.length();
265: if (s_length == 4) {
266: c = s.charAt(0);
267: if (c == 'n') {
268: X = "next";
269: id = Id_next;
270: } else if (c == 's') {
271: X = "send";
272: id = Id_send;
273: }
274: } else if (s_length == 5) {
275: c = s.charAt(0);
276: if (c == 'c') {
277: X = "close";
278: id = Id_close;
279: } else if (c == 't') {
280: X = "throw";
281: id = Id_throw;
282: }
283: } else if (s_length == 12) {
284: X = "__iterator__";
285: id = Id___iterator__;
286: }
287: if (X != null && X != s && !X.equals(s))
288: id = 0;
289: break L0;
290: }
291: // #/generated#
292: return id;
293: }
294:
295: private static final int Id_close = 1, Id_next = 2, Id_send = 3,
296: Id_throw = 4, Id___iterator__ = 5, MAX_PROTOTYPE_ID = 5;
297:
298: // #/string_id_map#
299: private NativeFunction function;
300: private Object savedState;
301: private String lineSource;
302: private int lineNumber;
303: private boolean firstTime = true;
304: private boolean locked;
305:
306: public static class GeneratorClosedException extends
307: RuntimeException {
308: }
309: }
|