Initial checkin for Rhino 1.7. Add JavaScript 1.7 version number.
Initial implementation of 1.7 generators for interpretive mode. See bug 379377. git-svn-id: svn://10.0.0.236/trunk@227537 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
496b8fa553
commit
d71e9843a3
@ -36,9 +36,9 @@
|
||||
|
||||
name: rhino
|
||||
Name: Rhino
|
||||
version: 1_6R6pre
|
||||
version: 1_7R1pre
|
||||
# See Context#getImplementationVersion() for format of this!
|
||||
implementation.version: Rhino 1.6 release 6 Pre ${implementation.date}
|
||||
implementation.version: Rhino 1.7 release 1 Pre ${implementation.date}
|
||||
|
||||
build.dir: build
|
||||
rhino.jar: js.jar
|
||||
|
||||
@ -133,6 +133,11 @@ public class Context
|
||||
*/
|
||||
public static final int VERSION_1_6 = 160;
|
||||
|
||||
/**
|
||||
* JavaScript 1.7
|
||||
*/
|
||||
public static final int VERSION_1_7 = 170;
|
||||
|
||||
/**
|
||||
* Controls behaviour of <tt>Date.prototype.getYear()</tt>.
|
||||
* If <tt>hasFeature(FEATURE_NON_ECMA_GET_YEAR)</tt> returns true,
|
||||
@ -758,6 +763,7 @@ public class Context
|
||||
case VERSION_1_4:
|
||||
case VERSION_1_5:
|
||||
case VERSION_1_6:
|
||||
case VERSION_1_7:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -747,6 +747,10 @@ public class Decompiler
|
||||
case Token.CONST:
|
||||
result.append("const ");
|
||||
break;
|
||||
|
||||
case Token.YIELD:
|
||||
result.append("yield ");
|
||||
break;
|
||||
|
||||
case Token.NOT:
|
||||
result.append('!');
|
||||
|
||||
@ -57,6 +57,10 @@ public class FunctionNode extends ScriptOrFnNode {
|
||||
public boolean getIgnoreDynamicScope() {
|
||||
return itsIgnoreDynamicScope;
|
||||
}
|
||||
|
||||
public boolean isGenerator() {
|
||||
return itsIsGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* There are three types of functions that can be defined. The first
|
||||
@ -82,7 +86,8 @@ public class FunctionNode extends ScriptOrFnNode {
|
||||
}
|
||||
|
||||
String functionName;
|
||||
boolean itsNeedsActivation;
|
||||
int itsFunctionType;
|
||||
boolean itsNeedsActivation;
|
||||
boolean itsIgnoreDynamicScope;
|
||||
boolean itsIsGenerator;
|
||||
}
|
||||
|
||||
@ -950,6 +950,19 @@ final class IRFactory
|
||||
return new Node(nodeType, child);
|
||||
}
|
||||
|
||||
Node createYield(Node child, int lineno)
|
||||
{
|
||||
if (!parser.insideFunction()) {
|
||||
parser.reportError("msg.bad.yield");
|
||||
}
|
||||
setRequiresActivation();
|
||||
setIsGenerator();
|
||||
if (child != null)
|
||||
return new Node(Token.YIELD, child, lineno);
|
||||
else
|
||||
return new Node(Token.YIELD, lineno);
|
||||
}
|
||||
|
||||
Node createCallOrNew(int nodeType, Node child)
|
||||
{
|
||||
int type = Node.NON_SPECIALCALL;
|
||||
@ -1417,6 +1430,13 @@ final class IRFactory
|
||||
}
|
||||
}
|
||||
|
||||
private void setIsGenerator()
|
||||
{
|
||||
if (parser.insideFunction()) {
|
||||
((FunctionNode)parser.currentScriptOrFn).itsIsGenerator = true;
|
||||
}
|
||||
}
|
||||
|
||||
private Parser parser;
|
||||
|
||||
private static final int LOOP_DO_WHILE = 0;
|
||||
|
||||
@ -187,6 +187,12 @@ final class InterpretedFunction extends NativeFunction implements Script
|
||||
return idata;
|
||||
}
|
||||
|
||||
Object resumeGenerator(Context cx, Scriptable scope, int operation,
|
||||
Object state, Object value)
|
||||
{
|
||||
return Interpreter.resumeGenerator(cx, scope, operation, state, value);
|
||||
}
|
||||
|
||||
protected int getLanguageVersion()
|
||||
{
|
||||
return idata.languageVersion;
|
||||
|
||||
@ -160,20 +160,24 @@ public class Interpreter
|
||||
|
||||
Icode_TAIL_CALL = -55,
|
||||
|
||||
// Clear local to allow GC its context
|
||||
// Clear local to allow GC its context
|
||||
Icode_LOCAL_CLEAR = -56,
|
||||
|
||||
// Literal get/set
|
||||
// Literal get/set
|
||||
Icode_LITERAL_GETTER = -57,
|
||||
Icode_LITERAL_SETTER = -58,
|
||||
|
||||
// const
|
||||
// const
|
||||
Icode_SETCONST = -59,
|
||||
Icode_SETCONSTVAR = -60,
|
||||
Icode_SETCONSTVAR1 = -61,
|
||||
|
||||
// Generator opcodes (along with Token.YIELD)
|
||||
Icode_GENERATOR = -62,
|
||||
Icode_GENERATOR_END = -63,
|
||||
|
||||
// Last icode
|
||||
MIN_ICODE = -61;
|
||||
MIN_ICODE = -63;
|
||||
|
||||
// data for parsing
|
||||
|
||||
@ -335,7 +339,18 @@ public class Interpreter
|
||||
Kit.codeBug();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static CallFrame captureFrameForGenerator(CallFrame frame) {
|
||||
frame.frozen = true;
|
||||
CallFrame result = frame.cloneFrozen();
|
||||
frame.frozen = false;
|
||||
|
||||
// now isolate this frame from its previous context
|
||||
result.parentFrame = null;
|
||||
result.frameIndex = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static {
|
||||
@ -429,6 +444,8 @@ public class Interpreter
|
||||
case Icode_SETCONST: return "SETCONST";
|
||||
case Icode_SETCONSTVAR: return "SETCONSTVAR";
|
||||
case Icode_SETCONSTVAR1: return "SETCONSTVAR1";
|
||||
case Icode_GENERATOR: return "GENERATOR";
|
||||
case Icode_GENERATOR_END: return "GENERATOR_END";
|
||||
}
|
||||
|
||||
// icode without name
|
||||
@ -517,6 +534,10 @@ public class Interpreter
|
||||
itsData.useDynamicScope = true;
|
||||
}
|
||||
}
|
||||
if (theFunction.isGenerator()) {
|
||||
addIcode(Icode_GENERATOR);
|
||||
addUint16(theFunction.getBaseLineno() & 0xFFFF);
|
||||
}
|
||||
|
||||
generateICodeFromTree(theFunction.getLastChild());
|
||||
}
|
||||
@ -863,7 +884,11 @@ public class Interpreter
|
||||
|
||||
case Token.RETURN:
|
||||
updateLineNumber(node);
|
||||
if (child != null) {
|
||||
if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
|
||||
// We're in a generator, so change RETURN to GENERATOR_END
|
||||
addIcode(Icode_GENERATOR_END);
|
||||
addUint16(itsLineNumber & 0xFFFF);
|
||||
} else if (child != null) {
|
||||
visitExpression(child, ECF_TAIL);
|
||||
addToken(Token.RETURN);
|
||||
stackChange(-1);
|
||||
@ -884,6 +909,9 @@ public class Interpreter
|
||||
stackChange(-1);
|
||||
break;
|
||||
|
||||
case Icode_GENERATOR:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw badTree(node);
|
||||
}
|
||||
@ -1322,6 +1350,17 @@ public class Interpreter
|
||||
addToken(type);
|
||||
break;
|
||||
|
||||
case Token.YIELD:
|
||||
if (child != null) {
|
||||
visitExpression(child, 0);
|
||||
} else {
|
||||
addIcode(Icode_UNDEF);
|
||||
stackChange(1);
|
||||
}
|
||||
addToken(Token.YIELD);
|
||||
addUint16(node.getLineno() & 0xFFFF);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw badTree(node);
|
||||
}
|
||||
@ -1605,7 +1644,7 @@ public class Interpreter
|
||||
byte[] array = itsData.itsICode;
|
||||
int top = itsICodeTop;
|
||||
if (top == array.length) {
|
||||
array = increaseICodeCapasity(1);
|
||||
array = increaseICodeCapacity(1);
|
||||
}
|
||||
array[top] = (byte)value;
|
||||
itsICodeTop = top + 1;
|
||||
@ -1617,7 +1656,7 @@ public class Interpreter
|
||||
byte[] array = itsData.itsICode;
|
||||
int top = itsICodeTop;
|
||||
if (top + 2 > array.length) {
|
||||
array = increaseICodeCapasity(2);
|
||||
array = increaseICodeCapacity(2);
|
||||
}
|
||||
array[top] = (byte)(value >>> 8);
|
||||
array[top + 1] = (byte)value;
|
||||
@ -1629,7 +1668,7 @@ public class Interpreter
|
||||
byte[] array = itsData.itsICode;
|
||||
int top = itsICodeTop;
|
||||
if (top + 4 > array.length) {
|
||||
array = increaseICodeCapasity(4);
|
||||
array = increaseICodeCapacity(4);
|
||||
}
|
||||
array[top] = (byte)(i >>> 24);
|
||||
array[top + 1] = (byte)(i >>> 16);
|
||||
@ -1658,7 +1697,7 @@ public class Interpreter
|
||||
byte[] array = itsData.itsICode;
|
||||
int top = itsICodeTop;
|
||||
if (top + 3 > array.length) {
|
||||
array = increaseICodeCapasity(3);
|
||||
array = increaseICodeCapacity(3);
|
||||
}
|
||||
array[top] = (byte)gotoOp;
|
||||
// Offset would written later
|
||||
@ -1774,7 +1813,7 @@ public class Interpreter
|
||||
itsExceptionTableTop = top + EXCEPTION_SLOT_SIZE;
|
||||
}
|
||||
|
||||
private byte[] increaseICodeCapasity(int extraSize)
|
||||
private byte[] increaseICodeCapacity(int extraSize)
|
||||
{
|
||||
int capacity = itsData.itsICode.length;
|
||||
int top = itsICodeTop;
|
||||
@ -1958,7 +1997,11 @@ public class Interpreter
|
||||
case Token.NEW :
|
||||
out.println(tname+' '+indexReg);
|
||||
break;
|
||||
case Token.THROW : {
|
||||
case Token.THROW :
|
||||
case Token.YIELD :
|
||||
case Icode_GENERATOR :
|
||||
case Icode_GENERATOR_END :
|
||||
{
|
||||
int line = getIndex(iCode, pc);
|
||||
out.println(tname + " : " + line);
|
||||
pc += 2;
|
||||
@ -2006,6 +2049,24 @@ public class Interpreter
|
||||
pc += 4;
|
||||
break;
|
||||
}
|
||||
case Icode_REG_IND_C0:
|
||||
indexReg = 0;
|
||||
break;
|
||||
case Icode_REG_IND_C1:
|
||||
indexReg = 1;
|
||||
break;
|
||||
case Icode_REG_IND_C2:
|
||||
indexReg = 2;
|
||||
break;
|
||||
case Icode_REG_IND_C3:
|
||||
indexReg = 3;
|
||||
break;
|
||||
case Icode_REG_IND_C4:
|
||||
indexReg = 4;
|
||||
break;
|
||||
case Icode_REG_IND_C5:
|
||||
indexReg = 5;
|
||||
break;
|
||||
case Icode_REG_IND1: {
|
||||
indexReg = 0xFF & iCode[pc];
|
||||
out.println(tname+" "+indexReg);
|
||||
@ -2062,6 +2123,9 @@ public class Interpreter
|
||||
{
|
||||
switch (bytecode) {
|
||||
case Token.THROW :
|
||||
case Token.YIELD:
|
||||
case Icode_GENERATOR:
|
||||
case Icode_GENERATOR_END:
|
||||
// source line
|
||||
return 1 + 2;
|
||||
|
||||
@ -2326,6 +2390,40 @@ public class Interpreter
|
||||
return interpretLoop(cx, frame, null);
|
||||
}
|
||||
|
||||
static class GeneratorState {
|
||||
GeneratorState(int operation, Object value) {
|
||||
this.operation = operation;
|
||||
this.value = value;
|
||||
}
|
||||
int operation;
|
||||
Object value;
|
||||
RuntimeException returnedException;
|
||||
}
|
||||
|
||||
public static Object resumeGenerator(Context cx,
|
||||
Scriptable scope,
|
||||
int operation,
|
||||
Object savedState,
|
||||
Object value)
|
||||
{
|
||||
CallFrame frame = (CallFrame) savedState;
|
||||
GeneratorState generatorState = new GeneratorState(operation, value);
|
||||
if (operation == NativeGenerator.GENERATOR_CLOSE) {
|
||||
try {
|
||||
return interpretLoop(cx, frame, generatorState);
|
||||
} catch (RuntimeException e) {
|
||||
// Only propagate exceptions other than closingException
|
||||
if (e != value)
|
||||
throw e;
|
||||
}
|
||||
return Undefined.instance;
|
||||
}
|
||||
Object result = interpretLoop(cx, frame, generatorState);
|
||||
if (generatorState.returnedException != null)
|
||||
throw generatorState.returnedException;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Object restartContinuation(Continuation c, Context cx,
|
||||
Scriptable scope, Object[] args)
|
||||
{
|
||||
@ -2388,9 +2486,15 @@ public class Interpreter
|
||||
// catch bugs with using indeReg to access array eleemnts before
|
||||
// initializing indexReg.
|
||||
|
||||
GeneratorState generatorState = null;
|
||||
if (throwable != null) {
|
||||
// Assert assumptions
|
||||
if (!(throwable instanceof ContinuationJump)) {
|
||||
if (throwable instanceof GeneratorState) {
|
||||
generatorState = (GeneratorState) throwable;
|
||||
|
||||
// reestablish this call frame
|
||||
enterFrame(cx, frame, ScriptRuntime.emptyArgs, true);
|
||||
throwable = null;
|
||||
} else if (!(throwable instanceof ContinuationJump)) {
|
||||
// It should be continuation
|
||||
Kit.codeBug();
|
||||
}
|
||||
@ -2493,7 +2597,7 @@ public class Interpreter
|
||||
}
|
||||
|
||||
} else {
|
||||
if (frame.frozen) Kit.codeBug();
|
||||
if (generatorState == null && frame.frozen) Kit.codeBug();
|
||||
}
|
||||
|
||||
// Use local variables for constant values in frame
|
||||
@ -2508,7 +2612,7 @@ public class Interpreter
|
||||
|
||||
// Use local for stackTop as well. Since execption handlers
|
||||
// can only exist at statement level where stack is empty,
|
||||
// it is necessary to save/restore stackTop only accross
|
||||
// it is necessary to save/restore stackTop only across
|
||||
// function calls and normal returns.
|
||||
int stackTop = frame.savedStackTop;
|
||||
|
||||
@ -2523,8 +2627,76 @@ public class Interpreter
|
||||
int op = iCode[frame.pc++];
|
||||
jumplessRun: {
|
||||
|
||||
// Back indent to ease imlementation reading
|
||||
// Back indent to ease implementation reading
|
||||
switch (op) {
|
||||
case Icode_GENERATOR: {
|
||||
if (!frame.frozen) {
|
||||
// First time encountering this opcode: create new generator
|
||||
// object and return
|
||||
frame.pc--; // we want to come back here when we resume
|
||||
CallFrame generatorFrame = captureFrameForGenerator(frame);
|
||||
generatorFrame.frozen = true;
|
||||
NativeGenerator generator
|
||||
= new NativeGenerator(generatorFrame.fnOrScript, generatorFrame);
|
||||
ScriptRuntime.setObjectProtoAndParent(generator,
|
||||
ScriptRuntime.getTopCallScope(cx));
|
||||
frame.result = generator;
|
||||
break Loop;
|
||||
} else {
|
||||
// We are now resuming execution. Fall through to YIELD case.
|
||||
}
|
||||
}
|
||||
// fall through...
|
||||
case Token.YIELD: {
|
||||
if (!frame.frozen) {
|
||||
if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
|
||||
// Error: no yields when generator is closing
|
||||
throw ScriptRuntime.typeError0("msg.yield.closing");
|
||||
}
|
||||
// return to our caller (which should be a method of NativeGenerator)
|
||||
frame.frozen = true;
|
||||
frame.result = stack[stackTop];
|
||||
frame.resultDbl = sDbl[stackTop];
|
||||
frame.savedStackTop = stackTop;
|
||||
frame.pc--; // we want to come back here when we resume
|
||||
ScriptRuntime.exitActivationFunction(cx);
|
||||
return (frame.result != DBL_MRK)
|
||||
? frame.result
|
||||
: ScriptRuntime.wrapNumber(frame.resultDbl);
|
||||
} else {
|
||||
// we are resuming execution
|
||||
frame.frozen = false;
|
||||
int sourceLine = getIndex(iCode, frame.pc);
|
||||
frame.pc += 2; // skip line number data
|
||||
if (generatorState.operation == NativeGenerator.GENERATOR_THROW) {
|
||||
// processing a call to <generator>.throw(exception): must
|
||||
// act as if exception was thrown from resumption point
|
||||
throwable = new JavaScriptException(generatorState.value,
|
||||
frame.idata.itsSourceFile,
|
||||
sourceLine);
|
||||
break withoutExceptions;
|
||||
}
|
||||
if (generatorState.operation == NativeGenerator.GENERATOR_CLOSE) {
|
||||
throwable = generatorState.value;
|
||||
break withoutExceptions;
|
||||
}
|
||||
if (generatorState.operation != NativeGenerator.GENERATOR_SEND)
|
||||
throw Kit.codeBug();
|
||||
if (op == Token.YIELD)
|
||||
stack[stackTop] = generatorState.value;
|
||||
continue Loop;
|
||||
}
|
||||
}
|
||||
case Icode_GENERATOR_END: {
|
||||
// throw StopIteration
|
||||
frame.frozen = true;
|
||||
Scriptable top = ScriptableObject.getTopLevelScope(frame.scope);
|
||||
Object e = top.get(NativeGenerator.STOP_ITERATION, frame.scope);
|
||||
int sourceLine = getIndex(iCode, frame.pc);
|
||||
generatorState.returnedException =
|
||||
new JavaScriptException(e, frame.idata.itsSourceFile, sourceLine);
|
||||
break Loop;
|
||||
}
|
||||
case Token.THROW: {
|
||||
Object value = stack[stackTop];
|
||||
if (value == DBL_MRK) value = ScriptRuntime.wrapNumber(sDbl[stackTop]);
|
||||
@ -3124,7 +3296,8 @@ switch (op) {
|
||||
// optimization will create a "hole" in the context stack.
|
||||
// The correct thing to do may be to disable tail call
|
||||
// optimization if the code is being debugged.
|
||||
exitFrame(cx, frame, null); }
|
||||
exitFrame(cx, frame, null);
|
||||
}
|
||||
initFrame(cx, calleeScope, funThisObj, stack, sDbl,
|
||||
stackTop + 2, indexReg, ifun, callParentFrame,
|
||||
calleeFrame);
|
||||
@ -3704,7 +3877,12 @@ switch (op) {
|
||||
int exState;
|
||||
ContinuationJump cjump = null;
|
||||
|
||||
if (throwable instanceof JavaScriptException) {
|
||||
if (generatorState != null &&
|
||||
generatorState.operation == NativeGenerator.GENERATOR_CLOSE &&
|
||||
throwable == generatorState.value)
|
||||
{
|
||||
exState = EX_FINALLY_STATE;
|
||||
} else if (throwable instanceof JavaScriptException) {
|
||||
exState = EX_CATCH_STATE;
|
||||
} else if (throwable instanceof EcmaError) {
|
||||
// an offical ECMA error object,
|
||||
@ -3762,7 +3940,7 @@ switch (op) {
|
||||
continue StateLoop;
|
||||
}
|
||||
}
|
||||
// No allowed execption handlers in this frame, unwind
|
||||
// No allowed exception handlers in this frame, unwind
|
||||
// to parent and try to look there
|
||||
|
||||
exitFrame(cx, frame, throwable);
|
||||
@ -3988,7 +4166,8 @@ switch (op) {
|
||||
return frame.debuggerFrame != null || frame.idata.itsNeedsActivation;
|
||||
}
|
||||
|
||||
private static void enterFrame(Context cx, CallFrame frame, Object[] args, boolean continuationRestart)
|
||||
private static void enterFrame(Context cx, CallFrame frame, Object[] args,
|
||||
boolean continuationRestart)
|
||||
{
|
||||
boolean usesActivation = frame.idata.itsNeedsActivation;
|
||||
boolean isDebugged = frame.debuggerFrame != null;
|
||||
@ -3996,7 +4175,7 @@ switch (op) {
|
||||
Scriptable scope = frame.scope;
|
||||
if(scope == null) {
|
||||
Kit.codeBug();
|
||||
} else if(continuationRestart) {
|
||||
} else if (continuationRestart) {
|
||||
// Walk the parent chain of frame.scope until a NativeCall is
|
||||
// found. Normally, frame.scope is a NativeCall when called
|
||||
// from initFrame() for a debugged or activatable function.
|
||||
@ -4010,7 +4189,9 @@ switch (op) {
|
||||
break;
|
||||
} else {
|
||||
scope = scope.getParentScope();
|
||||
if(scope == null || (frame.parentFrame != null && frame.parentFrame.scope == scope)) {
|
||||
if (scope == null || (frame.parentFrame != null &&
|
||||
frame.parentFrame.scope == scope))
|
||||
{
|
||||
// If we get here, we didn't find a NativeCall in
|
||||
// the call chain before reaching parent frame's
|
||||
// scope. This should not be possible.
|
||||
|
||||
@ -116,6 +116,16 @@ public abstract class NativeFunction extends BaseFunction
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resumes execution of a JS 1.7 generator.
|
||||
*/
|
||||
/*abstract*/ Object resumeGenerator(Context cx, Scriptable scope,
|
||||
int operation, Object state, Object value)
|
||||
{
|
||||
// TODO(js1.7gen): make abstract once bytecode generation is done
|
||||
throw new EvaluatorException("Not yet implemented");
|
||||
}
|
||||
|
||||
protected abstract int getLanguageVersion();
|
||||
|
||||
/**
|
||||
|
||||
227
mozilla/js/rhino/src/org/mozilla/javascript/NativeGenerator.java
Executable file
227
mozilla/js/rhino/src/org/mozilla/javascript/NativeGenerator.java
Executable file
@ -0,0 +1,227 @@
|
||||
/* -*- 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.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* 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 void init(Scriptable scope, boolean sealed) {
|
||||
// Generator
|
||||
new NativeGenerator().exportAsJSClass(MAX_PROTOTYPE_ID, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for constructing the prototype object.
|
||||
*/
|
||||
private NativeGenerator() { }
|
||||
|
||||
NativeGenerator(NativeFunction function, Object savedState) {
|
||||
this.function = function;
|
||||
this.savedState = savedState;
|
||||
}
|
||||
|
||||
public static final String STOP_ITERATION = "StopIteration";
|
||||
|
||||
static class StopIteration extends NativeObject {
|
||||
public String getClassName() { return STOP_ITERATION; }
|
||||
}
|
||||
|
||||
public static final int GENERATOR_SEND = 0,
|
||||
GENERATOR_THROW = 1,
|
||||
GENERATOR_CLOSE = 2;
|
||||
|
||||
public String getClassName() {
|
||||
return "Generator";
|
||||
}
|
||||
|
||||
protected void initPrototypeId(int id) {
|
||||
String s;
|
||||
int arity;
|
||||
switch (id) {
|
||||
case Id_constructor: arity=1; s="constructor"; break;
|
||||
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;
|
||||
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_constructor:
|
||||
// TODO(js1.7gen): Shouldn't have a constructor. Currently need
|
||||
// one to get Generator.prototype
|
||||
return null;
|
||||
|
||||
case Id_close:
|
||||
// need to run any pending finally clauses
|
||||
return generator.resume(cx, scope, GENERATOR_CLOSE,
|
||||
new RuntimeException());
|
||||
|
||||
case Id_next:
|
||||
// arguments to next() are ignored
|
||||
generator.firstTime = false;
|
||||
return generator.resume(cx, scope, GENERATOR_SEND,
|
||||
Undefined.instance);
|
||||
|
||||
case Id_send:
|
||||
if (generator.firstTime) {
|
||||
throw ScriptRuntime.typeError0("msg.send.newborn");
|
||||
}
|
||||
return generator.resume(cx, scope, GENERATOR_SEND,
|
||||
args.length > 0 ? args[0] : Undefined.instance);
|
||||
|
||||
case Id_throw:
|
||||
return generator.resume(cx, scope, GENERATOR_THROW,
|
||||
args.length > 0 ? args[0] : Undefined.instance);
|
||||
|
||||
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 = operation == GENERATOR_THROW
|
||||
? value
|
||||
: ScriptableObject.getTopLevelScope(scope).get(STOP_ITERATION,
|
||||
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 (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-05-09 08:23:27 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==11) { X="constructor";id=Id_constructor; }
|
||||
if (X!=null && X!=s && !X.equals(s)) id = 0;
|
||||
break L0;
|
||||
}
|
||||
// #/generated#
|
||||
return id;
|
||||
}
|
||||
|
||||
private static final int
|
||||
Id_constructor = 1,
|
||||
Id_close = 2,
|
||||
Id_next = 3,
|
||||
Id_send = 4,
|
||||
Id_throw = 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;
|
||||
}
|
||||
|
||||
@ -82,7 +82,8 @@ public class Node
|
||||
NAME_PROP = 17, // property name
|
||||
CONTROL_BLOCK_PROP = 18, // flags a control block that can drop off
|
||||
PARENTHESIZED_PROP = 19, // expression is parenthesized
|
||||
LAST_PROP = 19;
|
||||
GENERATOR_END_PROP = 20,
|
||||
LAST_PROP = 20;
|
||||
|
||||
// values of ISNUMBER_PROP to specify
|
||||
// which of the children are Number types
|
||||
@ -460,6 +461,7 @@ public class Node
|
||||
case NAME_PROP: return "name_prop";
|
||||
case CONTROL_BLOCK_PROP: return "control_block_prop";
|
||||
case PARENTHESIZED_PROP: return "parenthesized_prop";
|
||||
case GENERATOR_END_PROP: return "generator_end";
|
||||
|
||||
default: Kit.codeBug();
|
||||
}
|
||||
|
||||
@ -135,6 +135,19 @@ public class NodeTransformer
|
||||
|
||||
case Token.RETURN:
|
||||
{
|
||||
boolean isGenerator = tree.getType() == Token.FUNCTION
|
||||
&& ((FunctionNode)tree).isGenerator();
|
||||
if (isGenerator) {
|
||||
node.putIntProp(Node.GENERATOR_END_PROP, 1);
|
||||
/*
|
||||
// Replace returns inside generators with throw STOP_ITERATION
|
||||
node = replaceCurrent(parent, previous, node,
|
||||
new Node(Token.THROW,
|
||||
Node.newString(Token.NAME,
|
||||
NativeGenerator.STOP_ITERATION),
|
||||
node.getLineno()));
|
||||
*/
|
||||
}
|
||||
/* If we didn't support try/finally, it wouldn't be
|
||||
* necessary to put LEAVEWITH nodes here... but as
|
||||
* we do need a series of JSR FINALLY nodes before
|
||||
@ -169,7 +182,7 @@ public class NodeTransformer
|
||||
Node returnNode = node;
|
||||
Node returnExpr = returnNode.getFirstChild();
|
||||
node = replaceCurrent(parent, previous, node, unwindBlock);
|
||||
if (returnExpr == null) {
|
||||
if (returnExpr == null || isGenerator) {
|
||||
unwindBlock.addChildToBack(returnNode);
|
||||
} else {
|
||||
Node store = new Node(Token.EXPR_RESULT, returnExpr);
|
||||
|
||||
@ -1069,44 +1069,9 @@ public class Parser
|
||||
break;
|
||||
}
|
||||
|
||||
case Token.RETURN: {
|
||||
if (!insideFunction()) {
|
||||
reportError("msg.bad.return");
|
||||
}
|
||||
consumeToken();
|
||||
decompiler.addToken(Token.RETURN);
|
||||
int lineno = ts.getLineno();
|
||||
|
||||
Node retExpr;
|
||||
/* This is ugly, but we don't want to require a semicolon. */
|
||||
tt = peekTokenOrEOL();
|
||||
switch (tt) {
|
||||
case Token.SEMI:
|
||||
case Token.RC:
|
||||
case Token.EOF:
|
||||
case Token.EOL:
|
||||
case Token.ERROR:
|
||||
retExpr = null;
|
||||
break;
|
||||
default:
|
||||
retExpr = expr(false);
|
||||
hasReturnValue = true;
|
||||
}
|
||||
pn = nf.createReturn(retExpr, lineno);
|
||||
|
||||
// see if we need a strict mode warning
|
||||
if (retExpr == null) {
|
||||
if (functionEndFlags == Node.END_RETURNS_VALUE)
|
||||
addStrictWarning("msg.return.inconsistent", "");
|
||||
|
||||
functionEndFlags |= Node.END_RETURNS;
|
||||
} else {
|
||||
if (functionEndFlags == Node.END_RETURNS)
|
||||
addStrictWarning("msg.return.inconsistent", "");
|
||||
|
||||
functionEndFlags |= Node.END_RETURNS_VALUE;
|
||||
}
|
||||
|
||||
case Token.RETURN:
|
||||
case Token.YIELD: {
|
||||
pn = returnOrYield(tt, false);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1241,6 +1206,58 @@ public class Parser
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
private Node returnOrYield(int tt, boolean exprContext)
|
||||
throws IOException, ParserException
|
||||
{
|
||||
if (!insideFunction()) {
|
||||
reportError(tt == Token.RETURN ? "msg.bad.return"
|
||||
: "msg.bad.yield");
|
||||
}
|
||||
consumeToken();
|
||||
decompiler.addToken(tt);
|
||||
int lineno = ts.getLineno();
|
||||
|
||||
Node e;
|
||||
/* This is ugly, but we don't want to require a semicolon. */
|
||||
switch (peekTokenOrEOL()) {
|
||||
case Token.SEMI:
|
||||
case Token.RC:
|
||||
case Token.EOF:
|
||||
case Token.EOL:
|
||||
case Token.ERROR:
|
||||
case Token.RB:
|
||||
case Token.RP:
|
||||
case Token.YIELD:
|
||||
e = null;
|
||||
break;
|
||||
default:
|
||||
e = expr(false);
|
||||
break;
|
||||
}
|
||||
if (tt == Token.RETURN) {
|
||||
// see if we need a strict mode warning
|
||||
// TODO(js1.7gen): check for mixture of yield and value returns
|
||||
if (e == null) {
|
||||
if (functionEndFlags == Node.END_RETURNS_VALUE)
|
||||
addStrictWarning("msg.return.inconsistent", "");
|
||||
|
||||
functionEndFlags |= Node.END_RETURNS;
|
||||
} else {
|
||||
hasReturnValue = true;
|
||||
if (functionEndFlags == Node.END_RETURNS)
|
||||
addStrictWarning("msg.return.inconsistent", "");
|
||||
|
||||
functionEndFlags |= Node.END_RETURNS_VALUE;
|
||||
}
|
||||
return nf.createReturn(e, lineno);
|
||||
} else {
|
||||
Node n = nf.createYield(e, lineno);
|
||||
if (exprContext)
|
||||
return n;
|
||||
return new Node(Token.EXPR_VOID, n, lineno);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a 'var' or 'const' statement, or a 'var' init list in a for
|
||||
@ -1321,6 +1338,9 @@ public class Parser
|
||||
decompiler.addToken(Token.COMMA);
|
||||
if (!pn.hasSideEffects())
|
||||
addStrictWarning("msg.no.side.effects", "");
|
||||
if (peekToken() == Token.YIELD) {
|
||||
reportError("msg.yield.parenthesized");
|
||||
}
|
||||
pn = nf.createBinary(Token.COMMA, pn, assignExpr(inForInit));
|
||||
}
|
||||
return pn;
|
||||
@ -1329,9 +1349,14 @@ public class Parser
|
||||
private Node assignExpr(boolean inForInit)
|
||||
throws IOException, ParserException
|
||||
{
|
||||
int tt = peekToken();
|
||||
if (tt == Token.YIELD) {
|
||||
consumeToken();
|
||||
return returnOrYield(tt, true);
|
||||
}
|
||||
Node pn = condExpr(inForInit);
|
||||
|
||||
int tt = peekToken();
|
||||
tt = peekToken();
|
||||
if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) {
|
||||
consumeToken();
|
||||
decompiler.addToken(tt);
|
||||
@ -1687,6 +1712,9 @@ public class Parser
|
||||
if (!first)
|
||||
decompiler.addToken(Token.COMMA);
|
||||
first = false;
|
||||
if (peekToken() == Token.YIELD) {
|
||||
reportError("msg.yield.parenthesized");
|
||||
}
|
||||
nf.addChildToBack(listNode, assignExpr(false));
|
||||
} while (matchToken(Token.COMMA));
|
||||
|
||||
@ -1769,6 +1797,13 @@ public class Parser
|
||||
|
||||
tt = nextToken();
|
||||
switch (tt) {
|
||||
|
||||
// needed for generator.throw();
|
||||
case Token.THROW:
|
||||
decompiler.addName("throw");
|
||||
pn = propertyName(pn, "throw", memberTypeFlags);
|
||||
break;
|
||||
|
||||
// handles: name, ns::name, ns::*, ns::[expr]
|
||||
case Token.NAME:
|
||||
s = ts.getString();
|
||||
|
||||
@ -202,6 +202,10 @@ public class ScriptRuntime {
|
||||
NativeWith.init(scope, sealed);
|
||||
NativeCall.init(scope, sealed);
|
||||
NativeScript.init(scope, sealed);
|
||||
|
||||
// TODO(js1.7gen): jsshell marks generators as JSCLASS_IS_ANONYMOUS,
|
||||
// meaning that "Generator" is not defined in the global scope
|
||||
NativeGenerator.init(scope, sealed);
|
||||
|
||||
boolean withXml = cx.hasFeature(Context.FEATURE_E4X) && cx.getE4xImplementationFactory() != null;
|
||||
|
||||
|
||||
@ -144,113 +144,115 @@ public class Token
|
||||
DEL_REF = 67, // delete reference
|
||||
REF_CALL = 68, // f(args) = something or f(args)++
|
||||
REF_SPECIAL = 69, // reference for special properties like __proto
|
||||
YIELD = 70, // JS 1.7 yield pseudo keyword
|
||||
|
||||
// For XML support:
|
||||
DEFAULTNAMESPACE = 70, // default xml namespace =
|
||||
ESCXMLATTR = 71,
|
||||
ESCXMLTEXT = 72,
|
||||
REF_MEMBER = 73, // Reference for x.@y, x..y etc.
|
||||
REF_NS_MEMBER = 74, // Reference for x.ns::y, x..ns::y etc.
|
||||
REF_NAME = 75, // Reference for @y, @[y] etc.
|
||||
REF_NS_NAME = 76; // Reference for ns::y, @ns::y@[y] etc.
|
||||
DEFAULTNAMESPACE = 71, // default xml namespace =
|
||||
ESCXMLATTR = 72,
|
||||
ESCXMLTEXT = 73,
|
||||
REF_MEMBER = 74, // Reference for x.@y, x..y etc.
|
||||
REF_NS_MEMBER = 75, // Reference for x.ns::y, x..ns::y etc.
|
||||
REF_NAME = 76, // Reference for @y, @[y] etc.
|
||||
REF_NS_NAME = 77; // Reference for ns::y, @ns::y@[y] etc.
|
||||
|
||||
// End of interpreter bytecodes
|
||||
public final static int
|
||||
LAST_BYTECODE_TOKEN = REF_NS_NAME,
|
||||
|
||||
TRY = 77,
|
||||
SEMI = 78, // semicolon
|
||||
LB = 79, // left and right brackets
|
||||
RB = 80,
|
||||
LC = 81, // left and right curlies (braces)
|
||||
RC = 82,
|
||||
LP = 83, // left and right parentheses
|
||||
RP = 84,
|
||||
COMMA = 85, // comma operator
|
||||
TRY = 78,
|
||||
SEMI = 79, // semicolon
|
||||
LB = 80, // left and right brackets
|
||||
RB = 81,
|
||||
LC = 82, // left and right curlies (braces)
|
||||
RC = 83,
|
||||
LP = 84, // left and right parentheses
|
||||
RP = 85,
|
||||
COMMA = 86, // comma operator
|
||||
|
||||
ASSIGN = 86, // simple assignment (=)
|
||||
ASSIGN_BITOR = 87, // |=
|
||||
ASSIGN_BITXOR = 88, // ^=
|
||||
ASSIGN_BITAND = 89, // |=
|
||||
ASSIGN_LSH = 90, // <<=
|
||||
ASSIGN_RSH = 91, // >>=
|
||||
ASSIGN_URSH = 92, // >>>=
|
||||
ASSIGN_ADD = 93, // +=
|
||||
ASSIGN_SUB = 94, // -=
|
||||
ASSIGN_MUL = 95, // *=
|
||||
ASSIGN_DIV = 96, // /=
|
||||
ASSIGN_MOD = 97; // %=
|
||||
ASSIGN = 87, // simple assignment (=)
|
||||
ASSIGN_BITOR = 88, // |=
|
||||
ASSIGN_BITXOR = 89, // ^=
|
||||
ASSIGN_BITAND = 90, // |=
|
||||
ASSIGN_LSH = 91, // <<=
|
||||
ASSIGN_RSH = 92, // >>=
|
||||
ASSIGN_URSH = 93, // >>>=
|
||||
ASSIGN_ADD = 94, // +=
|
||||
ASSIGN_SUB = 95, // -=
|
||||
ASSIGN_MUL = 96, // *=
|
||||
ASSIGN_DIV = 97, // /=
|
||||
ASSIGN_MOD = 98; // %=
|
||||
|
||||
public final static int
|
||||
FIRST_ASSIGN = ASSIGN,
|
||||
LAST_ASSIGN = ASSIGN_MOD,
|
||||
|
||||
HOOK = 98, // conditional (?:)
|
||||
COLON = 99,
|
||||
OR = 100, // logical or (||)
|
||||
AND = 101, // logical and (&&)
|
||||
INC = 102, // increment/decrement (++ --)
|
||||
DEC = 103,
|
||||
DOT = 104, // member operator (.)
|
||||
FUNCTION = 105, // function keyword
|
||||
EXPORT = 106, // export keyword
|
||||
IMPORT = 107, // import keyword
|
||||
IF = 108, // if keyword
|
||||
ELSE = 109, // else keyword
|
||||
SWITCH = 110, // switch keyword
|
||||
CASE = 111, // case keyword
|
||||
DEFAULT = 112, // default keyword
|
||||
WHILE = 113, // while keyword
|
||||
DO = 114, // do keyword
|
||||
FOR = 115, // for keyword
|
||||
BREAK = 116, // break keyword
|
||||
CONTINUE = 117, // continue keyword
|
||||
VAR = 118, // var keyword
|
||||
WITH = 119, // with keyword
|
||||
CATCH = 120, // catch keyword
|
||||
FINALLY = 121, // finally keyword
|
||||
VOID = 122, // void keyword
|
||||
RESERVED = 123, // reserved keywords
|
||||
HOOK = 99, // conditional (?:)
|
||||
COLON = 100,
|
||||
OR = 101, // logical or (||)
|
||||
AND = 102, // logical and (&&)
|
||||
INC = 103, // increment/decrement (++ --)
|
||||
DEC = 104,
|
||||
DOT = 105, // member operator (.)
|
||||
FUNCTION = 106, // function keyword
|
||||
EXPORT = 107, // export keyword
|
||||
IMPORT = 108, // import keyword
|
||||
IF = 109, // if keyword
|
||||
ELSE = 110, // else keyword
|
||||
SWITCH = 111, // switch keyword
|
||||
CASE = 112, // case keyword
|
||||
DEFAULT = 113, // default keyword
|
||||
WHILE = 114, // while keyword
|
||||
DO = 115, // do keyword
|
||||
FOR = 116, // for keyword
|
||||
BREAK = 117, // break keyword
|
||||
CONTINUE = 118, // continue keyword
|
||||
VAR = 119, // var keyword
|
||||
WITH = 120, // with keyword
|
||||
CATCH = 121, // catch keyword
|
||||
FINALLY = 122, // finally keyword
|
||||
VOID = 123, // void keyword
|
||||
RESERVED = 124, // reserved keywords
|
||||
|
||||
EMPTY = 124,
|
||||
EMPTY = 125,
|
||||
|
||||
/* types used for the parse tree - these never get returned
|
||||
* by the scanner.
|
||||
*/
|
||||
|
||||
BLOCK = 125, // statement block
|
||||
LABEL = 126, // label
|
||||
TARGET = 127,
|
||||
LOOP = 128,
|
||||
EXPR_VOID = 129, // expression statement in functions
|
||||
EXPR_RESULT = 130, // expression statement in scripts
|
||||
JSR = 131,
|
||||
SCRIPT = 132, // top-level node for entire script
|
||||
TYPEOFNAME = 133, // for typeof(simple-name)
|
||||
USE_STACK = 134,
|
||||
SETPROP_OP = 135, // x.y op= something
|
||||
SETELEM_OP = 136, // x[y] op= something
|
||||
LOCAL_BLOCK = 137,
|
||||
SET_REF_OP = 138, // *reference op= something
|
||||
BLOCK = 126, // statement block
|
||||
LABEL = 127, // label
|
||||
TARGET = 128,
|
||||
LOOP = 129,
|
||||
EXPR_VOID = 130, // expression statement in functions
|
||||
EXPR_RESULT = 131, // expression statement in scripts
|
||||
JSR = 132,
|
||||
SCRIPT = 133, // top-level node for entire script
|
||||
TYPEOFNAME = 134, // for typeof(simple-name)
|
||||
USE_STACK = 135,
|
||||
SETPROP_OP = 136, // x.y op= something
|
||||
SETELEM_OP = 137, // x[y] op= something
|
||||
LOCAL_BLOCK = 138,
|
||||
SET_REF_OP = 139, // *reference op= something
|
||||
|
||||
// For XML support:
|
||||
DOTDOT = 139, // member operator (..)
|
||||
COLONCOLON = 140, // namespace::name
|
||||
XML = 141, // XML type
|
||||
DOTQUERY = 142, // .() -- e.g., x.emps.emp.(name == "terry")
|
||||
XMLATTR = 143, // @
|
||||
XMLEND = 144,
|
||||
DOTDOT = 140, // member operator (..)
|
||||
COLONCOLON = 141, // namespace::name
|
||||
XML = 142, // XML type
|
||||
DOTQUERY = 143, // .() -- e.g., x.emps.emp.(name == "terry")
|
||||
XMLATTR = 144, // @
|
||||
XMLEND = 145,
|
||||
|
||||
// Optimizer-only-tokens
|
||||
TO_OBJECT = 145,
|
||||
TO_DOUBLE = 146,
|
||||
TO_OBJECT = 146,
|
||||
TO_DOUBLE = 147,
|
||||
|
||||
GET = 147, // JS 1.5 get pseudo keyword
|
||||
SET = 148, // JS 1.5 set pseudo keyword
|
||||
CONST = 149,
|
||||
SETCONST = 150,
|
||||
SETCONSTVAR = 151,
|
||||
LAST_TOKEN = 152;
|
||||
GET = 148, // JS 1.5 get pseudo keyword
|
||||
SET = 149, // JS 1.5 set pseudo keyword
|
||||
LET = 150, // JS 1.7 let pseudo keyword
|
||||
CONST = 151,
|
||||
SETCONST = 152,
|
||||
SETCONSTVAR = 153,
|
||||
LAST_TOKEN = 154;
|
||||
|
||||
public static String name(int token)
|
||||
{
|
||||
@ -407,6 +409,8 @@ public class Token
|
||||
case TO_DOUBLE: return "TO_DOUBLE";
|
||||
case GET: return "GET";
|
||||
case SET: return "SET";
|
||||
case LET: return "LET";
|
||||
case YIELD: return "YIELD";
|
||||
case CONST: return "CONST";
|
||||
case SETCONST: return "SETCONST";
|
||||
}
|
||||
|
||||
@ -133,6 +133,7 @@ class TokenStream
|
||||
Id_function = Token.FUNCTION,
|
||||
Id_if = Token.IF,
|
||||
Id_in = Token.IN,
|
||||
Id_let = Token.LET,
|
||||
Id_new = Token.NEW,
|
||||
Id_null = Token.NULL,
|
||||
Id_return = Token.RETURN,
|
||||
@ -144,6 +145,7 @@ class TokenStream
|
||||
Id_void = Token.VOID,
|
||||
Id_while = Token.WHILE,
|
||||
Id_with = Token.WITH,
|
||||
Id_yield = Token.YIELD,
|
||||
|
||||
// the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
|
||||
Id_abstract = Token.RESERVED,
|
||||
@ -184,7 +186,7 @@ class TokenStream
|
||||
|
||||
int id;
|
||||
String s = name;
|
||||
// #generated# Last update: 2001-06-01 17:45:01 CEST
|
||||
// #generated# Last update: 2007-04-18 13:53:30 PDT
|
||||
L0: { id = 0; String X = null; int c;
|
||||
L: switch (s.length()) {
|
||||
case 2: c=s.charAt(1);
|
||||
@ -195,6 +197,7 @@ class TokenStream
|
||||
case 3: switch (s.charAt(0)) {
|
||||
case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') {id=Id_for; break L0;} break L;
|
||||
case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') {id=Id_int; break L0;} break L;
|
||||
case 'l': if (s.charAt(2)=='t' && s.charAt(1)=='e') {id=Id_let; break L0;} break L;
|
||||
case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') {id=Id_new; break L0;} break L;
|
||||
case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') {id=Id_try; break L0;} break L;
|
||||
case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') {id=Id_var; break L0;} break L;
|
||||
@ -221,7 +224,10 @@ class TokenStream
|
||||
} break L;
|
||||
case 5: switch (s.charAt(2)) {
|
||||
case 'a': X="class";id=Id_class; break L;
|
||||
case 'e': X="break";id=Id_break; break L;
|
||||
case 'e': c=s.charAt(0);
|
||||
if (c=='b') { X="break";id=Id_break; }
|
||||
else if (c=='y') { X="yield";id=Id_yield; }
|
||||
break L;
|
||||
case 'i': X="while";id=Id_while; break L;
|
||||
case 'l': X="false";id=Id_false; break L;
|
||||
case 'n': c=s.charAt(0);
|
||||
@ -394,6 +400,13 @@ class TokenStream
|
||||
// Return the corresponding token if it's a keyword
|
||||
int result = stringToKeyword(str);
|
||||
if (result != Token.EOF) {
|
||||
if ((result == Token.LET || result == Token.YIELD) &&
|
||||
parser.compilerEnv.getLanguageVersion()
|
||||
< Context.VERSION_1_7)
|
||||
{
|
||||
// LET and YIELD are tokens only in 1.7 and later
|
||||
result = Token.NAME;
|
||||
}
|
||||
if (result != Token.RESERVED) {
|
||||
return result;
|
||||
} else if (!parser.compilerEnv.
|
||||
|
||||
@ -372,6 +372,10 @@ public class Codegen extends Interpreter
|
||||
cfw.stopMethod((short)(firstLocal + 1));
|
||||
}
|
||||
|
||||
// TODO(js1.7gen): make name a parameter, generalize to
|
||||
// work for "call" and for "resumeGenerator".
|
||||
// The "call" method should call "resumeGenerator" with a "start" operation
|
||||
// (need to add "args" parameter to "resumeGenerator").
|
||||
private void generateCallMethod(ClassFileWriter cfw)
|
||||
{
|
||||
cfw.startMethod("call",
|
||||
@ -1271,6 +1275,14 @@ class BodyCodegen
|
||||
*/
|
||||
private void generatePrologue()
|
||||
{
|
||||
/*
|
||||
* TODO(js1.7gen):
|
||||
* For resumeGenerator, generate prologue that looks at the value
|
||||
* of the operation argument, does some setup, and jumps to the
|
||||
* appropriate resumption point.
|
||||
* Can assume inDirectCallFunction = false and hasVarsInRegs = false
|
||||
* for generators.
|
||||
*/
|
||||
if (inDirectCallFunction) {
|
||||
int directParameterCount = scriptOrFn.getParamCount();
|
||||
// 0 is reserved for function Object 'this'
|
||||
@ -2355,6 +2367,60 @@ class BodyCodegen
|
||||
+")Ljava/lang/Object;");
|
||||
break;
|
||||
|
||||
case Token.YIELD:
|
||||
/*
|
||||
* TODO(js1.7gen): Generate code to save the execution state
|
||||
* of the JVM. This may not be too bad: since generators
|
||||
* require an activation, all variables are in the activation
|
||||
* object. Look through the prologue code at the java
|
||||
* registers that are initialized. Look to see if any of those
|
||||
* values are altered during execution; if not, you can
|
||||
* just rely on shared prologue code for the generator.
|
||||
* Hardest part will be the stack. You'll need to create a
|
||||
* compile-time data structure that tracks the type of every
|
||||
* element on the stack. Then when you generate code for the
|
||||
* yield, you'll generate code to take the contents of the
|
||||
* stack and save it to an in-memory object (Object[] or pair
|
||||
* of Object[] double[], I'm not sure which). You'll also
|
||||
* need the inverse operation of taking an object like that
|
||||
* and pushing all its saved values onto the stack. It may
|
||||
* make sense to alter ClassFileWriter to maintain stack
|
||||
* information, or maintain a parallel data structure in
|
||||
* Codegen.
|
||||
*
|
||||
* Each yield point will need to have a unique integer number
|
||||
* determined at compile time. When a program encounters a
|
||||
* yield, the yield number will be saved in the state object.
|
||||
* The "resumeGenerator" prologue code will need to have a
|
||||
* code to take the integer and then jump to the appropriate
|
||||
* yield. You'll need to run the validator with yields in
|
||||
* various points in control flow to make sure that the
|
||||
* validator doesn't object to these jumps into the middle of
|
||||
* various control structures. I think it should be okay since
|
||||
* the register save code will ensure that you never jump
|
||||
* into a place with different stack height or uninitialized
|
||||
* variables.
|
||||
*
|
||||
* The biggest unknown area for me is exception handling.
|
||||
* There are cases of yields in finally clauses that suspend
|
||||
* a function with an in-flight exception. It may have to be
|
||||
* that Rhino doesn't generate any finally clauses but instead
|
||||
* catches all exceptions, processes finally clauses (including
|
||||
* yields) and rethrows. Then in-flight exceptions will just
|
||||
* be variables that can be captured. The other question is
|
||||
* whether the validator will object to jumps in and out of
|
||||
* try and catch blocks.
|
||||
*
|
||||
* Probably don't need analog to Icode_GENERATOR: all setup
|
||||
* code can be handled in "resumeGenerator" prologue.
|
||||
*
|
||||
* Probably do need analog to Icode_GENERATOR_END. Perhaps
|
||||
* during code generation for returns, can check to see if
|
||||
* in generator and if so generate code for the analog
|
||||
* to Icode_GENERATOR_END.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Unexpected node type "+type);
|
||||
}
|
||||
|
||||
@ -126,6 +126,12 @@ msg.bad.decr =\
|
||||
msg.bad.incr =\
|
||||
Invalid increment operand.
|
||||
|
||||
msg.bad.yield =\
|
||||
yield must be in a function.
|
||||
|
||||
msg.yield.parenthesized =\
|
||||
yield expression must be parenthesized.
|
||||
|
||||
# NativeGlobal
|
||||
msg.cant.call.indirect =\
|
||||
Function "{0}" must be called directly, and not by way of a \
|
||||
@ -448,7 +454,7 @@ msg.assn.create.strict =\
|
||||
Assignment to undeclared variable {0}
|
||||
|
||||
msg.ref.undefined.prop =\
|
||||
Referenced to undefined property "{0}"
|
||||
Referenced to undefined property "{0}"
|
||||
|
||||
msg.prop.not.found =\
|
||||
Property {0} not found.
|
||||
@ -691,3 +697,14 @@ msg.bad.uri =\
|
||||
# Number
|
||||
msg.bad.precision =\
|
||||
Precision {0} out of range.
|
||||
|
||||
# NativeGenerator
|
||||
msg.send.newborn =\
|
||||
Attempt to send value to newborn generator
|
||||
|
||||
msg.already.exec.gen =\
|
||||
Already executing generator
|
||||
|
||||
# Interpreter
|
||||
msg.yield.closing =\
|
||||
Yield from closing generator
|
||||
|
||||
@ -167,8 +167,29 @@ js1_7/geniter/regress-347739.js
|
||||
js1_7/geniter/regress-349012-01.js
|
||||
js1_7/geniter/regress-349331.js
|
||||
|
||||
# Generator functionality not yet implemented:
|
||||
# "Iterator" constructor
|
||||
js1_7/geniter/builtin-Iterator-function.js
|
||||
# array comprehensions
|
||||
js1_7/geniter/regress-345736.js
|
||||
# let statements
|
||||
js1_7/geniter/regress-347593.js
|
||||
# yield and xml-filtering predicate
|
||||
js1_7/geniter/regress-352605.js
|
||||
# destructuring assignment
|
||||
js1_7/geniter/regress-366941.js
|
||||
# for (i in <generator>)
|
||||
js1_7/geniter/regress-350621.js
|
||||
|
||||
# JS 1.7 not yet implemented
|
||||
js1_7
|
||||
js1_7/block
|
||||
js1_7/decompilation
|
||||
js1_7/expressions
|
||||
js1_7/extensions
|
||||
js1_7/iterable
|
||||
js1_7/lexical
|
||||
js1_7/regexp
|
||||
js1_7/regress
|
||||
|
||||
# JS 1.8 not yet implemented
|
||||
js1_8
|
||||
|
||||
@ -13,11 +13,14 @@ js1_5/Regress/regress-80981.js
|
||||
js1_5/Regress/regress-89443.js
|
||||
js1_5/extensions/regress-226507.js
|
||||
|
||||
# Needs investigation
|
||||
#js1_5/Exceptions/regress-257751.js
|
||||
#js1_5/Regress/regress-111557.js
|
||||
#js1_5/Regress/regress-155081-2.js
|
||||
#js1_5/Regress/regress-155081.js
|
||||
#js1_5/Regress/regress-167328.js
|
||||
#js1_5/extensions/regress-50447.js
|
||||
#js1_5/extensions/regress-311161.js
|
||||
# program too large/complex; could have better error message
|
||||
js1_5/Regress/regress-111557.js
|
||||
js1_5/Regress/regress-155081-2.js
|
||||
js1_5/Regress/regress-155081.js
|
||||
js1_5/extensions/regress-311161.js
|
||||
|
||||
# Missing line number information on error
|
||||
js1_5/Regress/regress-167328.js
|
||||
js1_5/extensions/regress-50447.js
|
||||
js1_5/Exceptions/regress-257751.js
|
||||
|
||||
|
||||
@ -57,7 +57,7 @@ msg.shell.usage =\
|
||||
Valid options are:\n\
|
||||
\ -?, -help Displays help messages.\n\
|
||||
\ -w Enable warnings.\n\
|
||||
\ -version 100|110|120|130|140|150|160\n\
|
||||
\ -version 100|110|120|130|140|150|160|170\n\
|
||||
\ Set a specific language version.\n\
|
||||
\ -opt [-1|0-9] Set optimization level.\n\
|
||||
\ -f script-filename Execute script file.\n\
|
||||
@ -66,7 +66,6 @@ msg.shell.usage =\
|
||||
\ -strict Enable strict mode warnings.\n\
|
||||
\ -fatal-warnings Treat warnings as errors.
|
||||
|
||||
|
||||
msg.help =\
|
||||
\n\
|
||||
Command Description \n\
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user