aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java')
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java281
1 files changed, 281 insertions, 0 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java
new file mode 100644
index 0000000..0a8da9f
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeGenerator.java
@@ -0,0 +1,281 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements generator objects. See
+ * http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Generators
+ *
+ * @author Norris Boyd
+ */
+public final class NativeGenerator extends IdScriptableObject {
+ private static final Object GENERATOR_TAG = new Object();
+
+ static NativeGenerator init(ScriptableObject scope, boolean sealed) {
+ // Generator
+ // Can't use "NativeGenerator().exportAsJSClass" since we don't want
+ // to define "Generator" as a constructor in the top-level scope.
+
+ NativeGenerator prototype = new NativeGenerator();
+ if (scope != null) {
+ prototype.setParentScope(scope);
+ prototype.setPrototype(getObjectPrototype(scope));
+ }
+ prototype.activatePrototypeMap(MAX_PROTOTYPE_ID);
+ if (sealed) {
+ prototype.sealObject();
+ }
+
+ // Need to access Generator prototype when constructing
+ // Generator instances, but don't have a generator constructor
+ // to use to find the prototype. Use the "associateValue"
+ // approach instead.
+ if (scope != null) {
+ scope.associateValue(GENERATOR_TAG, prototype);
+ }
+
+ return prototype;
+ }
+
+ /**
+ * Only for constructing the prototype object.
+ */
+ private NativeGenerator() { }
+
+ public NativeGenerator(Scriptable scope, NativeFunction function,
+ Object savedState)
+ {
+ this.function = function;
+ this.savedState = savedState;
+ // Set parent and prototype properties. Since we don't have a
+ // "Generator" constructor in the top scope, we stash the
+ // prototype in the top scope's associated value.
+ Scriptable top = ScriptableObject.getTopLevelScope(scope);
+ this.setParentScope(top);
+ NativeGenerator prototype = (NativeGenerator)
+ ScriptableObject.getTopScopeValue(top, GENERATOR_TAG);
+ this.setPrototype(prototype);
+ }
+
+ public static final int GENERATOR_SEND = 0,
+ GENERATOR_THROW = 1,
+ GENERATOR_CLOSE = 2;
+
+ public String getClassName() {
+ return "Generator";
+ }
+
+ /**
+ * Close the generator if it is still open.
+ */
+ public void finalize() throws Throwable {
+ if (savedState != null) {
+ // This is a little tricky since we are most likely running in
+ // a different thread. We need to get a Context to run this, and
+ // we must call "doTopCall" since this will likely be the outermost
+ // JavaScript frame on this thread.
+ Context cx = Context.getCurrentContext();
+ ContextFactory factory = cx != null ? cx.getFactory()
+ : ContextFactory.getGlobal();
+ Scriptable scope = ScriptableObject.getTopLevelScope(this);
+ factory.call(new CloseGeneratorAction(this));
+ }
+ }
+
+ private static class CloseGeneratorAction implements ContextAction {
+ private NativeGenerator generator;
+
+ CloseGeneratorAction(NativeGenerator generator) {
+ this.generator = generator;
+ }
+
+ public Object run(Context cx) {
+ Scriptable scope = ScriptableObject.getTopLevelScope(generator);
+ Callable closeGenerator = new Callable() {
+ public Object call(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args) {
+ return ((NativeGenerator)thisObj).resume(cx, scope,
+ GENERATOR_CLOSE, new GeneratorClosedException());
+ }
+ };
+ return ScriptRuntime.doTopCall(closeGenerator, cx, scope,
+ generator, null);
+ }
+ }
+
+ protected void initPrototypeId(int id) {
+ String s;
+ int arity;
+ switch (id) {
+ case Id_close: arity=1; s="close"; break;
+ case Id_next: arity=1; s="next"; break;
+ case Id_send: arity=0; s="send"; break;
+ case Id_throw: arity=0; s="throw"; break;
+ case Id___iterator__: arity=1; s="__iterator__"; break;
+ default: throw new IllegalArgumentException(String.valueOf(id));
+ }
+ initPrototypeMethod(GENERATOR_TAG, id, s, arity);
+ }
+
+ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ if (!f.hasTag(GENERATOR_TAG)) {
+ return super.execIdCall(f, cx, scope, thisObj, args);
+ }
+ int id = f.methodId();
+
+ if (!(thisObj instanceof NativeGenerator))
+ throw incompatibleCallError(f);
+
+ NativeGenerator generator = (NativeGenerator) thisObj;
+
+ switch (id) {
+
+ case Id_close:
+ // need to run any pending finally clauses
+ return generator.resume(cx, scope, GENERATOR_CLOSE,
+ new GeneratorClosedException());
+
+ case Id_next:
+ // arguments to next() are ignored
+ generator.firstTime = false;
+ return generator.resume(cx, scope, GENERATOR_SEND,
+ Undefined.instance);
+
+ case Id_send: {
+ Object arg = args.length > 0 ? args[0] : Undefined.instance;
+ if (generator.firstTime && !arg.equals(Undefined.instance)) {
+ throw ScriptRuntime.typeError0("msg.send.newborn");
+ }
+ return generator.resume(cx, scope, GENERATOR_SEND, arg);
+ }
+
+ case Id_throw:
+ return generator.resume(cx, scope, GENERATOR_THROW,
+ args.length > 0 ? args[0] : Undefined.instance);
+
+ case Id___iterator__:
+ return thisObj;
+
+ default:
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+ }
+
+ private Object resume(Context cx, Scriptable scope, int operation,
+ Object value)
+ {
+ if (savedState == null) {
+ if (operation == GENERATOR_CLOSE)
+ return Undefined.instance;
+ Object thrown;
+ if (operation == GENERATOR_THROW) {
+ thrown = value;
+ } else {
+ thrown = NativeIterator.getStopIterationObject(scope);
+ }
+ throw new JavaScriptException(thrown, lineSource, lineNumber);
+ }
+ try {
+ synchronized (this) {
+ // generator execution is necessarily single-threaded and
+ // non-reentrant.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=349263
+ if (locked)
+ throw ScriptRuntime.typeError0("msg.already.exec.gen");
+ locked = true;
+ }
+ return function.resumeGenerator(cx, scope, operation, savedState,
+ value);
+ } catch (GeneratorClosedException e) {
+ // On closing a generator in the compile path, the generator
+ // throws a special exception. This ensures execution of all pending
+ // finalizers and will not get caught by user code.
+ return Undefined.instance;
+ } catch (RhinoException e) {
+ lineNumber = e.lineNumber();
+ lineSource = e.lineSource();
+ savedState = null;
+ throw e;
+ } finally {
+ synchronized (this) {
+ locked = false;
+ }
+ if (operation == GENERATOR_CLOSE)
+ savedState = null;
+ }
+ }
+
+// #string_id_map#
+
+ protected int findPrototypeId(String s) {
+ int id;
+// #generated# Last update: 2007-06-14 13:13:03 EDT
+ L0: { id = 0; String X = null; int c;
+ int s_length = s.length();
+ if (s_length==4) {
+ c=s.charAt(0);
+ if (c=='n') { X="next";id=Id_next; }
+ else if (c=='s') { X="send";id=Id_send; }
+ }
+ else if (s_length==5) {
+ c=s.charAt(0);
+ if (c=='c') { X="close";id=Id_close; }
+ else if (c=='t') { X="throw";id=Id_throw; }
+ }
+ else if (s_length==12) { X="__iterator__";id=Id___iterator__; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_close = 1,
+ Id_next = 2,
+ Id_send = 3,
+ Id_throw = 4,
+ Id___iterator__ = 5,
+ MAX_PROTOTYPE_ID = 5;
+
+// #/string_id_map#
+ private NativeFunction function;
+ private Object savedState;
+ private String lineSource;
+ private int lineNumber;
+ private boolean firstTime = true;
+ private boolean locked;
+
+ public static class GeneratorClosedException extends RuntimeException {
+ }
+}