Merge
This commit is contained in:
commit
12203fae88
@ -26,7 +26,7 @@ if [ -z $ITERS ]; then
|
||||
ITERS=7
|
||||
fi
|
||||
NASHORN_JAR=dist/nashorn.jar
|
||||
JVM_FLAGS="-XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
|
||||
JVM_FLAGS="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:+TieredCompilation -server -jar ${NASHORN_JAR}"
|
||||
JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}"
|
||||
OCTANE_ARGS="--verbose --iterations ${ITERS}"
|
||||
|
||||
|
@ -38,7 +38,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE_DE
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
|
||||
@ -47,6 +46,8 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIM
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETPROTOTYPE_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
|
||||
@ -238,7 +239,7 @@ public class ConstructorGenerator extends ClassGenerator {
|
||||
mi.loadThis();
|
||||
mi.invokeStatic(PROTOTYPEOBJECT_TYPE, PROTOTYPEOBJECT_SETCONSTRUCTOR,
|
||||
PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC);
|
||||
mi.putField(SCRIPTFUNCTION_TYPE, PROTOTYPE, OBJECT_DESC);
|
||||
mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETPROTOTYPE, SCRIPTFUNCTION_SETPROTOTYPE_DESC);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,6 @@ public interface StringConstants {
|
||||
static final Type TYPE_SCRIPTFUNCTIONIMPL = Type.getType(ScriptFunctionImpl.class);
|
||||
static final Type TYPE_SCRIPTOBJECT = Type.getType(ScriptObject.class);
|
||||
|
||||
static final String PROTOTYPE = "prototype";
|
||||
static final String PROTOTYPE_SUFFIX = "$Prototype";
|
||||
static final String CONSTRUCTOR_SUFFIX = "$Constructor";
|
||||
// This field name is known to Nashorn runtime (Context).
|
||||
@ -88,6 +87,8 @@ public interface StringConstants {
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_METHODHANDLE_ARRAY);
|
||||
static final String SCRIPTFUNCTION_SETARITY = "setArity";
|
||||
static final String SCRIPTFUNCTION_SETARITY_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE);
|
||||
static final String SCRIPTFUNCTION_SETPROTOTYPE = "setPrototype";
|
||||
static final String SCRIPTFUNCTION_SETPROTOTYPE_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT);
|
||||
static final String PROTOTYPEOBJECT_TYPE = TYPE_PROTOTYPEOBJECT.getInternalName();
|
||||
static final String PROTOTYPEOBJECT_SETCONSTRUCTOR = "setConstructor";
|
||||
static final String PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, TYPE_OBJECT);
|
||||
|
@ -37,6 +37,7 @@
|
||||
<pathelement location="${basedir}/buildtools/nasgen/dist/nasgen.jar"/>
|
||||
<pathelement path="${basedir}/build/classes"/>
|
||||
</classpath>
|
||||
<jvmarg value="-Djava.ext.dirs="/>
|
||||
<arg value="${basedir}/build/classes"/>
|
||||
<arg value="jdk.nashorn.internal.objects"/>
|
||||
<arg value="${basedir}/build/classes"/>
|
||||
|
@ -214,7 +214,7 @@ run.test.xms=2G
|
||||
|
||||
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
|
||||
# add '-Dtest.js.outofprocess' to run each test in a new sub-process
|
||||
run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
|
||||
run.test.jvmargs.main=-server -Xmx${run.test.xmx} -XX:-TieredCompilation -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
|
||||
#-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
||||
run.test.jvmargs.octane.main=-Xms${run.test.xms} ${run.test.jvmargs.main}
|
||||
|
||||
|
@ -106,7 +106,11 @@ class StaticClassIntrospector extends FacetIntrospector {
|
||||
|
||||
@Override
|
||||
MethodHandle editMethodHandle(MethodHandle mh) {
|
||||
MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, Object.class);
|
||||
return dropReceiver(mh, Object.class);
|
||||
}
|
||||
|
||||
static MethodHandle dropReceiver(final MethodHandle mh, final Class<?> receiverClass) {
|
||||
MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, receiverClass);
|
||||
// NOTE: this is a workaround for the fact that dropArguments doesn't preserve vararg collector state.
|
||||
if(mh.isVarargsCollector() && !newHandle.isVarargsCollector()) {
|
||||
final MethodType type = mh.type();
|
||||
|
@ -144,7 +144,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
}
|
||||
|
||||
private static MethodHandle drop(MethodHandle mh) {
|
||||
return MethodHandles.dropArguments(mh, 0, StaticClass.class);
|
||||
return StaticClassIntrospector.dropReceiver(mh, StaticClass.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -397,10 +397,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
}
|
||||
|
||||
setContextVariables(ctxt);
|
||||
final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
|
||||
final String fileName = (val != null) ? val.toString() : "<eval>";
|
||||
Object res = ScriptRuntime.apply(script, ctxtGlobal);
|
||||
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
|
||||
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
|
||||
} catch (final Exception e) {
|
||||
throwAsScriptException(e);
|
||||
throw new AssertionError("should not reach here");
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -58,12 +58,14 @@ import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
@ -219,14 +221,14 @@ public class ClassEmitter implements Emitter {
|
||||
private void defineCommonStatics(final boolean strictMode) {
|
||||
// source - used to store the source data (text) for this script. Shared across
|
||||
// compile units. Set externally by the compiler.
|
||||
field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.tag(), Source.class);
|
||||
field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
|
||||
|
||||
// constants - used to the constants array for this script. Shared across
|
||||
// compile units. Set externally by the compiler.
|
||||
field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.tag(), Object[].class);
|
||||
field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
|
||||
|
||||
// strictMode - was this script compiled in strict mode. Set externally by the compiler.
|
||||
field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.tag(), boolean.class, strictMode);
|
||||
field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,9 +240,9 @@ public class ClassEmitter implements Emitter {
|
||||
|
||||
if (constantMethodNeeded.contains(String.class)) {
|
||||
// $getString - get the ith entry from the constants table and cast to String.
|
||||
final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.tag(), String.class, int.class);
|
||||
final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
|
||||
getStringMethod.begin();
|
||||
getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
|
||||
getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
|
||||
.load(Type.INT, 0)
|
||||
.arrayload()
|
||||
.checkcast(String.class)
|
||||
@ -250,7 +252,7 @@ public class ClassEmitter implements Emitter {
|
||||
|
||||
if (constantMethodNeeded.contains(PropertyMap.class)) {
|
||||
// $getMap - get the ith entry from the constants table and cast to PropertyMap.
|
||||
final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
|
||||
final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
|
||||
getMapMethod.begin();
|
||||
getMapMethod.loadConstants()
|
||||
.load(Type.INT, 0)
|
||||
@ -260,7 +262,7 @@ public class ClassEmitter implements Emitter {
|
||||
getMapMethod.end();
|
||||
|
||||
// $setMap - overwrite an existing map.
|
||||
final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
|
||||
final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
|
||||
setMapMethod.begin();
|
||||
setMapMethod.loadConstants()
|
||||
.load(Type.INT, 0)
|
||||
@ -289,7 +291,7 @@ public class ClassEmitter implements Emitter {
|
||||
final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
|
||||
|
||||
getArrayMethod.begin();
|
||||
getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
|
||||
getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
|
||||
.load(Type.INT, 0)
|
||||
.arrayload()
|
||||
.checkcast(cls)
|
||||
@ -307,7 +309,7 @@ public class ClassEmitter implements Emitter {
|
||||
*/
|
||||
static String getArrayMethodName(final Class<?> cls) {
|
||||
assert cls.isArray();
|
||||
return GET_ARRAY_PREFIX.tag() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.tag();
|
||||
return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -409,6 +411,10 @@ public class ClassEmitter implements Emitter {
|
||||
methodsStarted.remove(method);
|
||||
}
|
||||
|
||||
SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new method to the class - defaults to public method
|
||||
*
|
||||
@ -433,7 +439,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving this method
|
||||
*/
|
||||
MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, methodDescriptor(rtype, ptypes), null, null));
|
||||
return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -484,7 +490,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving <clinit>
|
||||
*/
|
||||
MethodEmitter clinit() {
|
||||
return method(EnumSet.of(Flag.STATIC), CLINIT.tag(), void.class);
|
||||
return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -493,7 +499,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving <init>()V
|
||||
*/
|
||||
MethodEmitter init() {
|
||||
return method(INIT.tag(), void.class);
|
||||
return method(INIT.symbolName(), void.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -503,7 +509,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving <init>()V
|
||||
*/
|
||||
MethodEmitter init(final Class<?>... ptypes) {
|
||||
return method(INIT.tag(), void.class, ptypes);
|
||||
return method(INIT.symbolName(), void.class, ptypes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -515,7 +521,7 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving <init>(...)V
|
||||
*/
|
||||
MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
|
||||
return method(flags, INIT.tag(), void.class, ptypes);
|
||||
return method(flags, INIT.symbolName(), void.class, ptypes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -628,4 +634,9 @@ public class ClassEmitter implements Emitter {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
private MethodVisitor methodVisitor(EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
@ -29,8 +30,8 @@ import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
import jdk.nashorn.internal.runtime.Timing;
|
||||
|
||||
/**
|
||||
* A compilation phase is a step in the processes of turning a JavaScript FunctionNode
|
||||
* into bytecode. It has an optional return value.
|
||||
* A compilation phase is a step in the processes of turning a JavaScript
|
||||
* FunctionNode into bytecode. It has an optional return value.
|
||||
*/
|
||||
enum CompilationPhase {
|
||||
|
||||
@ -41,77 +42,87 @@ enum CompilationPhase {
|
||||
*/
|
||||
LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
|
||||
@Override
|
||||
void transform(final Compiler compiler, final FunctionNode fn) {
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn0) {
|
||||
|
||||
/*
|
||||
* For lazy compilation, we might be given a node previously marked as lazy
|
||||
* to compile as the outermost function node in the compiler. Unmark it
|
||||
* so it can be compiled and not cause recursion. Make sure the return type
|
||||
* is unknown so it can be correctly deduced. Return types are always
|
||||
* Objects in Lazy nodes as we haven't got a change to generate code for
|
||||
* them and decude its parameter specialization
|
||||
* For lazy compilation, we might be given a node previously marked
|
||||
* as lazy to compile as the outermost function node in the
|
||||
* compiler. Unmark it so it can be compiled and not cause
|
||||
* recursion. Make sure the return type is unknown so it can be
|
||||
* correctly deduced. Return types are always Objects in Lazy nodes
|
||||
* as we haven't got a change to generate code for them and decude
|
||||
* its parameter specialization
|
||||
*
|
||||
* TODO: in the future specializations from a callsite will be passed here
|
||||
* so we can generate a better non-lazy version of a function from a trampoline
|
||||
* TODO: in the future specializations from a callsite will be
|
||||
* passed here so we can generate a better non-lazy version of a
|
||||
* function from a trampoline
|
||||
*/
|
||||
//compute the signature from the callsite - todo - now just clone object params
|
||||
|
||||
final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
|
||||
outermostFunctionNode.setIsLazy(false);
|
||||
outermostFunctionNode.setReturnType(Type.UNKNOWN);
|
||||
assert outermostFunctionNode == fn0;
|
||||
|
||||
final Set<FunctionNode> neverLazy = new HashSet<>();
|
||||
final Set<FunctionNode> lazy = new HashSet<>();
|
||||
final Set<FunctionNode> lazy = new HashSet<>();
|
||||
|
||||
outermostFunctionNode.accept(new NodeVisitor() {
|
||||
// self references are done with invokestatic and thus cannot have trampolines - never lazy
|
||||
FunctionNode newFunctionNode = outermostFunctionNode;
|
||||
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
|
||||
// self references are done with invokestatic and thus cannot
|
||||
// have trampolines - never lazy
|
||||
@Override
|
||||
public Node enterCallNode(final CallNode node) {
|
||||
public boolean enterCallNode(final CallNode node) {
|
||||
final Node callee = node.getFunction();
|
||||
if (callee instanceof FunctionNode) {
|
||||
neverLazy.add(((FunctionNode)callee));
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
return node;
|
||||
return true;
|
||||
}
|
||||
|
||||
//any function that isn't the outermost one must be marked as lazy
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode node) {
|
||||
if (node == outermostFunctionNode) {
|
||||
return node;
|
||||
}
|
||||
public boolean enterFunctionNode(final FunctionNode node) {
|
||||
assert compiler.isLazy();
|
||||
lazy.add(node);
|
||||
|
||||
//also needs scope, potentially needs arguments etc etc
|
||||
|
||||
return node;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
//at least one method is non lazy - the outermost one
|
||||
neverLazy.add(newFunctionNode);
|
||||
|
||||
for (final FunctionNode node : neverLazy) {
|
||||
Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference");
|
||||
node.setIsLazy(false);
|
||||
Compiler.LOG.fine(
|
||||
"Marking ",
|
||||
node.getName(),
|
||||
" as non lazy, as it's a self reference");
|
||||
lazy.remove(node);
|
||||
}
|
||||
|
||||
outermostFunctionNode.accept(new NodeOperatorVisitor() {
|
||||
private final LexicalContext lexicalContext = new LexicalContext();
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
|
||||
@Override
|
||||
public Node enterFunctionNode(FunctionNode functionNode) {
|
||||
lexicalContext.push(functionNode);
|
||||
if(lazy.contains(functionNode)) {
|
||||
Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy");
|
||||
functionNode.setIsLazy(true);
|
||||
lexicalContext.getParentFunction(functionNode).setHasLazyChildren();
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
if (lazy.contains(functionNode)) {
|
||||
Compiler.LOG.fine(
|
||||
"Marking ",
|
||||
functionNode.getName(),
|
||||
" as lazy");
|
||||
final FunctionNode parent = lc.getParentFunction(functionNode);
|
||||
assert parent != null;
|
||||
lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN);
|
||||
lc.setFlag(parent.getBody(), Block.NEEDS_SCOPE);
|
||||
lc.setFlag(functionNode, FunctionNode.IS_LAZY);
|
||||
return functionNode;
|
||||
}
|
||||
return functionNode;
|
||||
}
|
||||
@Override
|
||||
public Node leaveFunctionNode(FunctionNode functionNode) {
|
||||
lexicalContext.pop(functionNode);
|
||||
return functionNode;
|
||||
|
||||
return functionNode.
|
||||
clearFlag(lc, FunctionNode.IS_LAZY).
|
||||
setReturnType(lc, Type.UNKNOWN);
|
||||
}
|
||||
});
|
||||
|
||||
return newFunctionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -121,13 +132,13 @@ enum CompilationPhase {
|
||||
},
|
||||
|
||||
/*
|
||||
* Constant folding pass
|
||||
* Simple constant folding that will make elementary constructs go away
|
||||
* Constant folding pass Simple constant folding that will make elementary
|
||||
* constructs go away
|
||||
*/
|
||||
CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
|
||||
@Override
|
||||
void transform(final Compiler compiler, final FunctionNode fn) {
|
||||
fn.accept(new FoldConstants());
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
return (FunctionNode)fn.accept(new FoldConstants());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -137,18 +148,16 @@ enum CompilationPhase {
|
||||
},
|
||||
|
||||
/*
|
||||
* Lower (Control flow pass)
|
||||
* Finalizes the control flow. Clones blocks for finally constructs and
|
||||
* similar things. Establishes termination criteria for nodes
|
||||
* Guarantee return instructions to method making sure control flow
|
||||
* cannot fall off the end. Replacing high level nodes with lower such
|
||||
* as runtime nodes where applicable.
|
||||
*
|
||||
* Lower (Control flow pass) Finalizes the control flow. Clones blocks for
|
||||
* finally constructs and similar things. Establishes termination criteria
|
||||
* for nodes Guarantee return instructions to method making sure control
|
||||
* flow cannot fall off the end. Replacing high level nodes with lower such
|
||||
* as runtime nodes where applicable.
|
||||
*/
|
||||
LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
|
||||
@Override
|
||||
void transform(final Compiler compiler, final FunctionNode fn) {
|
||||
fn.accept(new Lower());
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
return (FunctionNode)fn.accept(new Lower());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -158,13 +167,27 @@ enum CompilationPhase {
|
||||
},
|
||||
|
||||
/*
|
||||
* Attribution
|
||||
* Assign symbols and types to all nodes.
|
||||
* Attribution Assign symbols and types to all nodes.
|
||||
*/
|
||||
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
|
||||
@Override
|
||||
void transform(final Compiler compiler, final FunctionNode fn) {
|
||||
fn.accept(new Attr());
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
return (FunctionNode)initReturnTypes(fn).accept(new Attr());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pessimistically set all lazy functions' return types to Object
|
||||
* @param functionNode node where to start iterating
|
||||
*/
|
||||
private FunctionNode initReturnTypes(final FunctionNode functionNode) {
|
||||
return (FunctionNode)functionNode.accept(new NodeVisitor() {
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode node) {
|
||||
return node.isLazy() ?
|
||||
node.setReturnType(getLexicalContext(), Type.OBJECT) :
|
||||
node.setReturnType(getLexicalContext(), Type.UNKNOWN);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -174,25 +197,35 @@ enum CompilationPhase {
|
||||
},
|
||||
|
||||
/*
|
||||
* Splitter
|
||||
* Split the AST into several compile units based on a size heuristic
|
||||
* Splitter needs attributed AST for weight calculations (e.g. is
|
||||
* a + b a ScriptRuntime.ADD with call overhead or a dadd with much
|
||||
* less). Split IR can lead to scope information being changed.
|
||||
* Splitter Split the AST into several compile units based on a size
|
||||
* heuristic Splitter needs attributed AST for weight calculations (e.g. is
|
||||
* a + b a ScriptRuntime.ADD with call overhead or a dadd with much less).
|
||||
* Split IR can lead to scope information being changed.
|
||||
*/
|
||||
SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
|
||||
@Override
|
||||
void transform(final Compiler compiler, final FunctionNode fn) {
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName());
|
||||
|
||||
new Splitter(compiler, fn, outermostCompileUnit).split();
|
||||
final FunctionNode newFunctionNode = new Splitter(compiler, fn, outermostCompileUnit).split(fn);
|
||||
|
||||
assert fn.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + fn.getCompileUnit() + ") != " + outermostCompileUnit;
|
||||
assert newFunctionNode.getCompileUnit() == outermostCompileUnit : "fn.compileUnit (" + newFunctionNode.getCompileUnit() + ") != " + outermostCompileUnit;
|
||||
|
||||
if (fn.isStrictMode()) {
|
||||
if (newFunctionNode.isStrict()) {
|
||||
assert compiler.getStrictMode();
|
||||
compiler.setStrictMode(true);
|
||||
}
|
||||
|
||||
/*
|
||||
newFunctionNode.accept(new NodeVisitor() {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
assert functionNode.getCompileUnit() != null : functionNode.getName() + " " + Debug.id(functionNode) + " has no compile unit";
|
||||
return true;
|
||||
}
|
||||
});*/
|
||||
|
||||
return newFunctionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -204,30 +237,32 @@ enum CompilationPhase {
|
||||
/*
|
||||
* FinalizeTypes
|
||||
*
|
||||
* This pass finalizes the types for nodes. If Attr created wider types than
|
||||
* known during the first pass, convert nodes are inserted or access nodes
|
||||
* are specialized where scope accesses.
|
||||
* This pass finalizes the types for nodes. If Attr created wider types than
|
||||
* known during the first pass, convert nodes are inserted or access nodes
|
||||
* are specialized where scope accesses.
|
||||
*
|
||||
* Runtime nodes may be removed and primitivized or reintroduced depending
|
||||
* on information that was established in Attr.
|
||||
* Runtime nodes may be removed and primitivized or reintroduced depending
|
||||
* on information that was established in Attr.
|
||||
*
|
||||
* Contract: all variables must have slot assignments and scope assignments
|
||||
* before type finalization.
|
||||
*/
|
||||
TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
|
||||
@Override
|
||||
void transform(final Compiler compiler, final FunctionNode fn) {
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
final ScriptEnvironment env = compiler.getEnv();
|
||||
|
||||
fn.accept(new FinalizeTypes());
|
||||
final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
|
||||
|
||||
if (env._print_lower_ast) {
|
||||
env.getErr().println(new ASTWriter(fn));
|
||||
env.getErr().println(new ASTWriter(newFunctionNode));
|
||||
}
|
||||
|
||||
if (env._print_lower_parse) {
|
||||
env.getErr().println(new PrintVisitor(fn));
|
||||
}
|
||||
env.getErr().println(new PrintVisitor(newFunctionNode));
|
||||
}
|
||||
|
||||
return newFunctionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -239,31 +274,21 @@ enum CompilationPhase {
|
||||
/*
|
||||
* Bytecode generation:
|
||||
*
|
||||
* Generate the byte code class(es) resulting from the compiled FunctionNode
|
||||
* Generate the byte code class(es) resulting from the compiled FunctionNode
|
||||
*/
|
||||
BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
|
||||
@Override
|
||||
void transform(final Compiler compiler, final FunctionNode fn) {
|
||||
FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
|
||||
final ScriptEnvironment env = compiler.getEnv();
|
||||
FunctionNode newFunctionNode = fn;
|
||||
|
||||
try {
|
||||
final CodeGenerator codegen = new CodeGenerator(compiler);
|
||||
fn.accept(codegen);
|
||||
newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
|
||||
codegen.generateScopeCalls();
|
||||
fn.accept(new NodeOperatorVisitor() {
|
||||
@Override
|
||||
public Node enterFunctionNode(FunctionNode functionNode) {
|
||||
if(functionNode.isLazy()) {
|
||||
functionNode.resetResolved();
|
||||
return null;
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
});
|
||||
|
||||
} catch (final VerifyError e) {
|
||||
if (env._verify_code || env._print_code) {
|
||||
env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
|
||||
env.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
|
||||
if (env._dump_on_error) {
|
||||
e.printStackTrace(env.getErr());
|
||||
}
|
||||
@ -283,25 +308,25 @@ enum CompilationPhase {
|
||||
|
||||
compiler.addClass(className, bytecode);
|
||||
|
||||
//should could be printed to stderr for generate class?
|
||||
// should could be printed to stderr for generate class?
|
||||
if (env._print_code) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("class: " + className).
|
||||
append('\n').
|
||||
append(ClassEmitter.disassemble(bytecode)).
|
||||
append("=====");
|
||||
sb.append("class: " + className).append('\n')
|
||||
.append(ClassEmitter.disassemble(bytecode))
|
||||
.append("=====");
|
||||
env.getErr().println(sb);
|
||||
}
|
||||
|
||||
//should we verify the generated code?
|
||||
// should we verify the generated code?
|
||||
if (env._verify_code) {
|
||||
compiler.getCodeInstaller().verify(bytecode);
|
||||
}
|
||||
|
||||
//should code be dumped to disk - only valid in compile_only mode?
|
||||
// should code be dumped to disk - only valid in compile_only
|
||||
// mode?
|
||||
if (env._dest_dir != null && env._compile_only) {
|
||||
final String fileName = className.replace('.', File.separatorChar) + ".class";
|
||||
final int index = fileName.lastIndexOf(File.separatorChar);
|
||||
final int index = fileName.lastIndexOf(File.separatorChar);
|
||||
|
||||
if (index != -1) {
|
||||
final File dir = new File(fileName.substring(0, index));
|
||||
@ -314,11 +339,18 @@ enum CompilationPhase {
|
||||
fos.write(bytecode);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
Compiler.LOG.warning("Skipping class dump for " + className + ": " + ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
|
||||
Compiler.LOG.warning("Skipping class dump for ",
|
||||
className,
|
||||
": ",
|
||||
ECMAErrors.getMessage(
|
||||
"io.error.cant.write",
|
||||
dir.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newFunctionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -340,26 +372,28 @@ enum CompilationPhase {
|
||||
return functionNode.hasState(pre);
|
||||
}
|
||||
|
||||
protected void begin(final FunctionNode functionNode) {
|
||||
protected FunctionNode begin(final FunctionNode functionNode) {
|
||||
if (pre != null) {
|
||||
//check that everything in pre is present
|
||||
// check that everything in pre is present
|
||||
for (final CompilationState state : pre) {
|
||||
assert functionNode.hasState(state);
|
||||
}
|
||||
//check that nothing else is present
|
||||
// check that nothing else is present
|
||||
for (final CompilationState state : CompilationState.values()) {
|
||||
assert !(functionNode.hasState(state) && !pre.contains(state));
|
||||
}
|
||||
}
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
protected void end(final FunctionNode functionNode) {
|
||||
protected FunctionNode end(final FunctionNode functionNode) {
|
||||
endTime = System.currentTimeMillis();
|
||||
Timing.accumulateTime(toString(), endTime - startTime);
|
||||
|
||||
isFinished = true;
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
boolean isFinished() {
|
||||
@ -374,15 +408,13 @@ enum CompilationPhase {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
abstract void transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
|
||||
abstract FunctionNode transform(final Compiler compiler, final FunctionNode functionNode) throws CompilationException;
|
||||
|
||||
final void apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
|
||||
final FunctionNode apply(final Compiler compiler, final FunctionNode functionNode) throws CompilationException {
|
||||
if (!isApplicable(functionNode)) {
|
||||
throw new CompilationException("compile phase not applicable: " + this);
|
||||
throw new CompilationException("compile phase not applicable: " + this + " to " + functionNode.getName() + " state=" + functionNode.getState());
|
||||
}
|
||||
begin(functionNode);
|
||||
transform(compiler, functionNode);
|
||||
end(functionNode);
|
||||
return end(transform(compiler, begin(functionNode)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,12 +25,16 @@
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
@ -46,13 +50,12 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import jdk.internal.dynalink.support.NameCodec;
|
||||
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.CodeInstaller;
|
||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
@ -80,8 +83,6 @@ public final class Compiler {
|
||||
|
||||
private final ConstantData constantData;
|
||||
|
||||
private final FunctionNode functionNode;
|
||||
|
||||
private final CompilationSequence sequence;
|
||||
|
||||
private final ScriptEnvironment env;
|
||||
@ -90,6 +91,8 @@ public final class Compiler {
|
||||
|
||||
private boolean strict;
|
||||
|
||||
private FunctionNode functionNode;
|
||||
|
||||
private CodeInstaller<ScriptEnvironment> installer;
|
||||
|
||||
/** logger for compiler, trampolines, splits and related code generation events
|
||||
@ -103,8 +106,12 @@ public final class Compiler {
|
||||
* during a compile.
|
||||
*/
|
||||
private static String[] RESERVED_NAMES = {
|
||||
SCOPE.tag(),
|
||||
THIS.tag()
|
||||
SCOPE.symbolName(),
|
||||
THIS.symbolName(),
|
||||
RETURN.symbolName(),
|
||||
CALLEE.symbolName(),
|
||||
VARARGS.symbolName(),
|
||||
ARGUMENTS.symbolName()
|
||||
};
|
||||
|
||||
/**
|
||||
@ -186,7 +193,7 @@ public final class Compiler {
|
||||
|
||||
private static String lazyTag(final FunctionNode functionNode) {
|
||||
if (functionNode.isLazy()) {
|
||||
return '$' + LAZY.tag() + '$' + functionNode.getName();
|
||||
return '$' + LAZY.symbolName() + '$' + functionNode.getName();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@ -205,13 +212,13 @@ public final class Compiler {
|
||||
this.functionNode = functionNode;
|
||||
this.sequence = sequence;
|
||||
this.installer = installer;
|
||||
this.strict = strict || functionNode.isStrictMode();
|
||||
this.strict = strict || functionNode.isStrict();
|
||||
this.constantData = new ConstantData();
|
||||
this.compileUnits = new HashSet<>();
|
||||
this.bytecode = new HashMap<>();
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag() + lazyTag(functionNode))).
|
||||
sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.symbolName() + lazyTag(functionNode))).
|
||||
append('$').
|
||||
append(safeSourceName(functionNode.getSource()));
|
||||
|
||||
@ -253,9 +260,9 @@ public final class Compiler {
|
||||
* Execute the compilation this Compiler was created with
|
||||
* @params param types if known, for specialization
|
||||
* @throws CompilationException if something goes wrong
|
||||
* @return this compiler, for possible chaining
|
||||
* @return function node that results from code transforms
|
||||
*/
|
||||
public Compiler compile() throws CompilationException {
|
||||
public FunctionNode compile() throws CompilationException {
|
||||
return compile(null);
|
||||
}
|
||||
|
||||
@ -263,9 +270,9 @@ public final class Compiler {
|
||||
* Execute the compilation this Compiler was created with
|
||||
* @param paramTypes param types if known, for specialization
|
||||
* @throws CompilationException if something goes wrong
|
||||
* @return this compiler, for possible chaining
|
||||
* @return function node that results from code transforms
|
||||
*/
|
||||
public Compiler compile(final Class<?> paramTypes) throws CompilationException {
|
||||
public FunctionNode compile(final Class<?> paramTypes) throws CompilationException {
|
||||
for (final String reservedName : RESERVED_NAMES) {
|
||||
functionNode.uniqueName(reservedName);
|
||||
}
|
||||
@ -276,7 +283,7 @@ public final class Compiler {
|
||||
long time = 0L;
|
||||
|
||||
for (final CompilationPhase phase : sequence) {
|
||||
phase.apply(this, functionNode);
|
||||
this.functionNode = phase.apply(this, functionNode);
|
||||
|
||||
final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
|
||||
time += duration;
|
||||
@ -295,7 +302,7 @@ public final class Compiler {
|
||||
append(" ms ");
|
||||
}
|
||||
|
||||
LOG.fine(sb.toString());
|
||||
LOG.fine(sb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,14 +318,14 @@ public final class Compiler {
|
||||
append(" ms");
|
||||
}
|
||||
|
||||
LOG.info(sb.toString());
|
||||
LOG.info(sb);
|
||||
}
|
||||
|
||||
return this;
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
private Class<?> install(final String className, final byte[] code) {
|
||||
LOG.fine("Installing class " + className);
|
||||
LOG.fine("Installing class ", className);
|
||||
|
||||
final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
|
||||
|
||||
@ -330,8 +337,8 @@ public final class Compiler {
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
//use reflection to write source and constants table to installed classes
|
||||
final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
|
||||
final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
|
||||
final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
|
||||
final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
|
||||
sourceField.setAccessible(true);
|
||||
constantsField.setAccessible(true);
|
||||
sourceField.set(null, source);
|
||||
@ -380,17 +387,6 @@ public final class Compiler {
|
||||
unit.setCode(installedClasses.get(unit.getUnitClassName()));
|
||||
}
|
||||
|
||||
functionNode.accept(new NodeVisitor() {
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode node) {
|
||||
if (node.isLazy()) {
|
||||
return null;
|
||||
}
|
||||
node.setState(CompilationState.INSTALLED);
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
final StringBuilder sb;
|
||||
if (LOG.isEnabled()) {
|
||||
sb = new StringBuilder();
|
||||
@ -416,7 +412,7 @@ public final class Compiler {
|
||||
}
|
||||
|
||||
if (sb != null) {
|
||||
LOG.info(sb.toString());
|
||||
LOG.info(sb);
|
||||
}
|
||||
|
||||
return rootClass;
|
||||
@ -495,7 +491,7 @@ public final class Compiler {
|
||||
private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
|
||||
final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
|
||||
compileUnits.add(compileUnit);
|
||||
LOG.fine("Added compile unit " + compileUnit);
|
||||
LOG.fine("Added compile unit ", compileUnit);
|
||||
return compileUnit;
|
||||
}
|
||||
|
||||
|
@ -52,9 +52,6 @@ public enum CompilerConstants {
|
||||
/** lazy prefix for classes of jitted methods */
|
||||
LAZY("Lazy"),
|
||||
|
||||
/** leaf tag used for functions that require no scope */
|
||||
LEAF("__leaf__"),
|
||||
|
||||
/** constructor name */
|
||||
INIT("<init>"),
|
||||
|
||||
@ -90,55 +87,55 @@ public enum CompilerConstants {
|
||||
THIS("this"),
|
||||
|
||||
/** this debugger symbol */
|
||||
THIS_DEBUGGER("__this__"),
|
||||
THIS_DEBUGGER(":this"),
|
||||
|
||||
/** scope name, type and slot */
|
||||
SCOPE("__scope__", ScriptObject.class, 2),
|
||||
SCOPE(":scope", ScriptObject.class, 2),
|
||||
|
||||
/** the return value variable name were intermediate results are stored for scripts */
|
||||
SCRIPT_RETURN("__return__"),
|
||||
RETURN(":return"),
|
||||
|
||||
/** the callee value variable when necessary */
|
||||
CALLEE("__callee__", ScriptFunction.class),
|
||||
CALLEE(":callee", ScriptFunction.class),
|
||||
|
||||
/** the varargs variable when necessary */
|
||||
VARARGS("__varargs__"),
|
||||
VARARGS(":varargs"),
|
||||
|
||||
/** the arguments vector when necessary and the slot */
|
||||
ARGUMENTS("arguments", Object.class, 2),
|
||||
|
||||
/** prefix for iterators for for (x in ...) */
|
||||
ITERATOR_PREFIX("$iter"),
|
||||
ITERATOR_PREFIX(":iter"),
|
||||
|
||||
/** prefix for tag variable used for switch evaluation */
|
||||
SWITCH_TAG_PREFIX("$tag"),
|
||||
SWITCH_TAG_PREFIX(":tag"),
|
||||
|
||||
/** prefix for all exceptions */
|
||||
EXCEPTION_PREFIX("$exception"),
|
||||
EXCEPTION_PREFIX(":exception"),
|
||||
|
||||
/** prefix for quick slots generated in Store */
|
||||
QUICK_PREFIX("$quick"),
|
||||
QUICK_PREFIX(":quick"),
|
||||
|
||||
/** prefix for temporary variables */
|
||||
TEMP_PREFIX("$temp"),
|
||||
TEMP_PREFIX(":temp"),
|
||||
|
||||
/** prefix for literals */
|
||||
LITERAL_PREFIX("$lit"),
|
||||
|
||||
/** prefix for map */
|
||||
MAP("$map", 1),
|
||||
LITERAL_PREFIX(":lit"),
|
||||
|
||||
/** prefix for regexps */
|
||||
REGEX_PREFIX("$regex"),
|
||||
REGEX_PREFIX(":regex"),
|
||||
|
||||
/** "this" used in non-static Java methods; always in slot 0 */
|
||||
JAVA_THIS("this", 0),
|
||||
JAVA_THIS(null, 0),
|
||||
|
||||
/** init scope */
|
||||
INIT_SCOPE("$scope", 2),
|
||||
/** Map parameter in scope object constructors; always in slot 1 */
|
||||
INIT_MAP(null, 1),
|
||||
|
||||
/** init arguments */
|
||||
INIT_ARGUMENTS("$arguments", 3),
|
||||
/** Parent scope parameter in scope object constructors; always in slot 2 */
|
||||
INIT_SCOPE(null, 2),
|
||||
|
||||
/** Arguments parameter in scope object constructors; in slot 3 when present */
|
||||
INIT_ARGUMENTS(null, 3),
|
||||
|
||||
/** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */
|
||||
JS_OBJECT_PREFIX("JO"),
|
||||
@ -167,30 +164,30 @@ public enum CompilerConstants {
|
||||
/** get array suffix */
|
||||
GET_ARRAY_SUFFIX("$array");
|
||||
|
||||
private final String tag;
|
||||
private final String symbolName;
|
||||
private final Class<?> type;
|
||||
private final int slot;
|
||||
|
||||
private CompilerConstants() {
|
||||
this.tag = name();
|
||||
this.symbolName = name();
|
||||
this.type = null;
|
||||
this.slot = -1;
|
||||
}
|
||||
|
||||
private CompilerConstants(final String tag) {
|
||||
this(tag, -1);
|
||||
private CompilerConstants(final String symbolName) {
|
||||
this(symbolName, -1);
|
||||
}
|
||||
|
||||
private CompilerConstants(final String tag, final int slot) {
|
||||
this(tag, null, slot);
|
||||
private CompilerConstants(final String symbolName, final int slot) {
|
||||
this(symbolName, null, slot);
|
||||
}
|
||||
|
||||
private CompilerConstants(final String tag, final Class<?> type) {
|
||||
this(tag, type, -1);
|
||||
private CompilerConstants(final String symbolName, final Class<?> type) {
|
||||
this(symbolName, type, -1);
|
||||
}
|
||||
|
||||
private CompilerConstants(final String tag, final Class<?> type, final int slot) {
|
||||
this.tag = tag;
|
||||
private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
|
||||
this.symbolName = symbolName;
|
||||
this.type = type;
|
||||
this.slot = slot;
|
||||
}
|
||||
@ -202,8 +199,8 @@ public enum CompilerConstants {
|
||||
*
|
||||
* @return the tag
|
||||
*/
|
||||
public final String tag() {
|
||||
return tag;
|
||||
public final String symbolName() {
|
||||
return symbolName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,7 +274,7 @@ public enum CompilerConstants {
|
||||
* @return Call representing void constructor for type
|
||||
*/
|
||||
public static Call constructorNoLookup(final Class<?> clazz) {
|
||||
return specialCallNoLookup(clazz, INIT.tag(), void.class);
|
||||
return specialCallNoLookup(clazz, INIT.symbolName(), void.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -290,7 +287,7 @@ public enum CompilerConstants {
|
||||
* @return Call representing constructor for type
|
||||
*/
|
||||
public static Call constructorNoLookup(final String className, final Class<?>... ptypes) {
|
||||
return specialCallNoLookup(className, INIT.tag(), methodDescriptor(void.class, ptypes));
|
||||
return specialCallNoLookup(className, INIT.symbolName(), methodDescriptor(void.class, ptypes));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -303,7 +300,7 @@ public enum CompilerConstants {
|
||||
* @return Call representing constructor for type
|
||||
*/
|
||||
public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) {
|
||||
return specialCallNoLookup(clazz, INIT.tag(), void.class, ptypes);
|
||||
return specialCallNoLookup(clazz, INIT.symbolName(), void.class, ptypes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
|
||||
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
|
||||
@ -86,7 +87,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
||||
* @param method the method emitter to use
|
||||
*/
|
||||
protected void loadScope(final MethodEmitter method) {
|
||||
method.loadScope();
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +106,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
|
||||
loadScope(method);
|
||||
|
||||
if (hasArguments()) {
|
||||
method.loadArguments();
|
||||
method.loadCompilerConstant(ARGUMENTS);
|
||||
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
|
||||
} else {
|
||||
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
|
||||
|
@ -25,7 +25,12 @@
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
@ -33,10 +38,8 @@ import jdk.nashorn.internal.ir.Assignment;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.CallNode.EvalArgs;
|
||||
import jdk.nashorn.internal.ir.CaseNode;
|
||||
import jdk.nashorn.internal.ir.CatchNode;
|
||||
import jdk.nashorn.internal.ir.DoWhileNode;
|
||||
import jdk.nashorn.internal.ir.ExecuteNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
@ -85,18 +88,11 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
private static final DebugLogger LOG = new DebugLogger("finalize");
|
||||
|
||||
private final LexicalContext lexicalContext = new LexicalContext();
|
||||
|
||||
FinalizeTypes() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCallNode(final CallNode callNode) {
|
||||
final EvalArgs evalArgs = callNode.getEvalArgs();
|
||||
if (evalArgs != null) {
|
||||
evalArgs.setCode(evalArgs.getCode().accept(this));
|
||||
}
|
||||
|
||||
// AccessSpecializer - call return type may change the access for this location
|
||||
final Node function = callNode.getFunction();
|
||||
if (function instanceof FunctionNode) {
|
||||
@ -133,8 +129,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leaveNEW(final UnaryNode unaryNode) {
|
||||
assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
|
||||
((CallNode)unaryNode.rhs()).setIsNew();
|
||||
return unaryNode;
|
||||
return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -254,7 +249,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
@Override
|
||||
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
|
||||
assert binaryNode.getSymbol() != null;
|
||||
final BinaryNode newBinaryNode = (BinaryNode)binaryNode.setRHS(discard(binaryNode.rhs()));
|
||||
final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
|
||||
// AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
|
||||
// in that case, update the node type as well
|
||||
propagateType(newBinaryNode, newBinaryNode.lhs().getType());
|
||||
@ -354,41 +349,30 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBlock(final Block block) {
|
||||
lexicalContext.push(block);
|
||||
public boolean enterBlock(final Block block) {
|
||||
updateSymbols(block);
|
||||
return block;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public Node leaveBlock(Block block) {
|
||||
lexicalContext.pop(block);
|
||||
return super.leaveBlock(block);
|
||||
}
|
||||
public Node leaveBlock(final Block block) {
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
return block;//.setFlag(lc, lc.getFlags(block));
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public Node leaveCatchNode(final CatchNode catchNode) {
|
||||
final Node exceptionCondition = catchNode.getExceptionCondition();
|
||||
if (exceptionCondition != null) {
|
||||
catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
|
||||
return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
|
||||
}
|
||||
return catchNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
return enterWhileNode(doWhileNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
return leaveWhileNode(doWhileNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveExecuteNode(final ExecuteNode executeNode) {
|
||||
executeNode.setExpression(discard(executeNode.getExpression()));
|
||||
return executeNode;
|
||||
return executeNode.setExpression(discard(executeNode.getExpression()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -397,69 +381,54 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
final Node test = forNode.getTest();
|
||||
final Node modify = forNode.getModify();
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
|
||||
if (forNode.isForIn()) {
|
||||
forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
|
||||
return forNode;
|
||||
return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
|
||||
}
|
||||
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
|
||||
|
||||
if (init != null) {
|
||||
forNode.setInit(discard(init));
|
||||
}
|
||||
|
||||
if (test != null) {
|
||||
forNode.setTest(convert(test, Type.BOOLEAN));
|
||||
} else {
|
||||
assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode();
|
||||
}
|
||||
|
||||
if (modify != null) {
|
||||
forNode.setModify(discard(modify));
|
||||
}
|
||||
|
||||
return forNode;
|
||||
return forNode.
|
||||
setInit(lc, init == null ? null : discard(init)).
|
||||
setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)).
|
||||
setModify(lc, modify == null ? null : discard(modify));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode functionNode) {
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (functionNode.isLazy()) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
lexicalContext.push(functionNode);
|
||||
// If the function doesn't need a callee, we ensure its __callee__ symbol doesn't get a slot. We can't do
|
||||
// this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
|
||||
// need for the callee.
|
||||
if (!functionNode.needsCallee()) {
|
||||
functionNode.getCalleeNode().getSymbol().setNeedsSlot(false);
|
||||
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
|
||||
}
|
||||
// Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its
|
||||
// own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
|
||||
// this phase.
|
||||
if (!(functionNode.needsScope() || functionNode.needsParentScope())) {
|
||||
functionNode.getScopeNode().getSymbol().setNeedsSlot(false);
|
||||
if (!(functionNode.getBody().needsScope() || functionNode.needsParentScope())) {
|
||||
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
|
||||
}
|
||||
|
||||
updateSymbols(functionNode);
|
||||
functionNode.setState(CompilationState.FINALIZED);
|
||||
|
||||
return functionNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(FunctionNode functionNode) {
|
||||
lexicalContext.pop(functionNode);
|
||||
return super.leaveFunctionNode(functionNode);
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIfNode(final IfNode ifNode) {
|
||||
ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
|
||||
return ifNode;
|
||||
return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Node enterLiteralNode(final LiteralNode literalNode) {
|
||||
public boolean enterLiteralNode(final LiteralNode literalNode) {
|
||||
if (literalNode instanceof ArrayLiteralNode) {
|
||||
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
|
||||
final Node[] array = arrayLiteralNode.getValue();
|
||||
@ -473,14 +442,14 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveReturnNode(final ReturnNode returnNode) {
|
||||
final Node expr = returnNode.getExpression();
|
||||
if (expr != null) {
|
||||
returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
|
||||
return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
@ -496,21 +465,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
@Override
|
||||
public Node leaveSwitchNode(final SwitchNode switchNode) {
|
||||
final Node expression = switchNode.getExpression();
|
||||
final List<CaseNode> cases = switchNode.getCases();
|
||||
final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
|
||||
final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
|
||||
|
||||
if (!allInteger) {
|
||||
switchNode.setExpression(convert(expression, Type.OBJECT));
|
||||
for (final CaseNode caseNode : cases) {
|
||||
final Node test = caseNode.getTest();
|
||||
if (test != null) {
|
||||
caseNode.setTest(convert(test, Type.OBJECT));
|
||||
}
|
||||
}
|
||||
if (allInteger) {
|
||||
return switchNode;
|
||||
}
|
||||
|
||||
return switchNode;
|
||||
final Node expression = switchNode.getExpression();
|
||||
final List<CaseNode> cases = switchNode.getCases();
|
||||
final List<CaseNode> newCases = new ArrayList<>();
|
||||
|
||||
for (final CaseNode caseNode : cases) {
|
||||
final Node test = caseNode.getTest();
|
||||
newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode);
|
||||
}
|
||||
|
||||
return switchNode.
|
||||
setExpression(getLexicalContext(), convert(expression, Type.OBJECT)).
|
||||
setCases(getLexicalContext(), newCases);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -520,8 +492,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
@Override
|
||||
public Node leaveThrowNode(final ThrowNode throwNode) {
|
||||
throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
|
||||
return throwNode;
|
||||
return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -544,23 +515,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
public Node leaveWhileNode(final WhileNode whileNode) {
|
||||
final Node test = whileNode.getTest();
|
||||
if (test != null) {
|
||||
whileNode.setTest(convert(test, Type.BOOLEAN));
|
||||
return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
|
||||
}
|
||||
return whileNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveWithNode(final WithNode withNode) {
|
||||
withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
|
||||
return withNode;
|
||||
return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
|
||||
}
|
||||
|
||||
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
|
||||
if (!symbol.isScope()) {
|
||||
LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
|
||||
}
|
||||
if (loseSlot && symbol.hasSlot()) {
|
||||
LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
|
||||
if (LOG.isEnabled()) {
|
||||
if (!symbol.isScope()) {
|
||||
LOG.finest("updateSymbols: ", symbol, " => scope, because all vars in ", functionNode.getName(), " are in scope");
|
||||
}
|
||||
if (loseSlot && symbol.hasSlot()) {
|
||||
LOG.finest("updateSymbols: ", symbol, " => no slot, because all vars in ", functionNode.getName(), " are in scope");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,29 +546,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
final FunctionNode functionNode = lexicalContext.getFunction(block);
|
||||
assert !(block instanceof FunctionNode) || functionNode == block;
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
final FunctionNode functionNode = lc.getFunction(block);
|
||||
final boolean allVarsInScope = functionNode.allVarsInScope();
|
||||
final boolean isVarArg = functionNode.isVarArg();
|
||||
|
||||
final List<Symbol> symbols = block.getFrame().getSymbols();
|
||||
final boolean allVarsInScope = functionNode.allVarsInScope();
|
||||
final boolean isVarArg = functionNode.isVarArg();
|
||||
|
||||
for (final Symbol symbol : symbols) {
|
||||
if (symbol.isInternal() || symbol.isThis()) {
|
||||
for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
|
||||
final Symbol symbol = iter.next();
|
||||
if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol.isVar()) {
|
||||
if (allVarsInScope || symbol.isScope()) {
|
||||
updateSymbolsLog(functionNode, symbol, true);
|
||||
symbol.setIsScope();
|
||||
Symbol.setSymbolIsScope(lc, symbol);
|
||||
symbol.setNeedsSlot(false);
|
||||
} else {
|
||||
assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
|
||||
}
|
||||
} else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
|
||||
updateSymbolsLog(functionNode, symbol, isVarArg);
|
||||
symbol.setIsScope();
|
||||
Symbol.setSymbolIsScope(lc, symbol);
|
||||
symbol.setNeedsSlot(!isVarArg);
|
||||
}
|
||||
}
|
||||
@ -636,11 +607,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
//fallthru
|
||||
default:
|
||||
if (newRuntimeNode || widest.isObject()) {
|
||||
final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request);
|
||||
if (finalized) {
|
||||
runtimeNode.setIsFinal();
|
||||
}
|
||||
return runtimeNode;
|
||||
return new RuntimeNode(binaryNode, request).setIsFinal(finalized);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -667,7 +634,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
|
||||
return binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
|
||||
Node b = binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -683,28 +651,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
|
||||
node.accept(new NodeVisitor() {
|
||||
private void setCanBePrimitive(final Symbol symbol) {
|
||||
LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
|
||||
LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
|
||||
symbol.setCanBePrimitive(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterIdentNode(final IdentNode identNode) {
|
||||
public boolean enterIdentNode(final IdentNode identNode) {
|
||||
if (!exclude.contains(identNode)) {
|
||||
setCanBePrimitive(identNode.getSymbol());
|
||||
}
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterAccessNode(final AccessNode accessNode) {
|
||||
public boolean enterAccessNode(final AccessNode accessNode) {
|
||||
setCanBePrimitive(accessNode.getProperty().getSymbol());
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterIndexNode(final IndexNode indexNode) {
|
||||
public boolean enterIndexNode(final IndexNode indexNode) {
|
||||
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
|
||||
return indexNode;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -785,12 +753,12 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
private static <T extends Node> T setTypeOverride(final T node, final Type to) {
|
||||
final Type from = node.getType();
|
||||
if (!node.getType().equals(to)) {
|
||||
LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
|
||||
LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
|
||||
if (!to.isObject() && from.isObject()) {
|
||||
setCanBePrimitive(node, to);
|
||||
}
|
||||
}
|
||||
LOG.info("Type override for lhs in '" + node + "' => " + to);
|
||||
LOG.info("Type override for lhs in '", node, "' => ", to);
|
||||
return ((TypeOverride<T>)node).setType(to);
|
||||
}
|
||||
|
||||
@ -814,8 +782,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
private Node convert(final Node node, final Type to) {
|
||||
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
|
||||
assert node != null : "node is null";
|
||||
assert node.getSymbol() != null : "node " + node + " has no symbol!";
|
||||
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode();
|
||||
assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction() + " " + node.getSource();
|
||||
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
|
||||
|
||||
final Type from = node.getType();
|
||||
|
||||
@ -842,23 +810,23 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node);
|
||||
}
|
||||
|
||||
LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
|
||||
LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
//This is the only place in this file that can create new temporaries
|
||||
//FinalizeTypes may not introduce ANY node that is not a conversion.
|
||||
getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode);
|
||||
resultNode.copyTerminalFlags(node);
|
||||
lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
|
||||
|
||||
assert !node.isTerminal();
|
||||
|
||||
return resultNode;
|
||||
}
|
||||
|
||||
private static Node discard(final Node node) {
|
||||
node.setDiscard(true);
|
||||
|
||||
if (node.getSymbol() != null) {
|
||||
final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node);
|
||||
//discard never has a symbol in the discard node - then it would be a nop
|
||||
discard.copyTerminalFlags(node);
|
||||
assert !node.isTerminal();
|
||||
return discard;
|
||||
}
|
||||
|
||||
@ -883,7 +851,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
|
||||
final Symbol symbol = node.getSymbol();
|
||||
if (symbol.isTemp()) {
|
||||
symbol.setTypeOverride(to);
|
||||
LOG.info("Type override for temporary in '" + node + "' => " + to);
|
||||
LOG.info("Type override for temporary in '", node, "' => ", to);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ final class FoldConstants extends NodeVisitor {
|
||||
public Node leaveUnaryNode(final UnaryNode unaryNode) {
|
||||
final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
|
||||
if (literalNode != null) {
|
||||
LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
|
||||
LOG.info("Unary constant folded ", unaryNode, " to ", literalNode);
|
||||
return literalNode;
|
||||
}
|
||||
return unaryNode;
|
||||
@ -67,24 +67,20 @@ final class FoldConstants extends NodeVisitor {
|
||||
public Node leaveBinaryNode(final BinaryNode binaryNode) {
|
||||
final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
|
||||
if (literalNode != null) {
|
||||
LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
|
||||
LOG.info("Binary constant folded ", binaryNode, " to ", literalNode);
|
||||
return literalNode;
|
||||
}
|
||||
return binaryNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (functionNode.isLazy()) {
|
||||
return null;
|
||||
}
|
||||
return functionNode;
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
return !functionNode.isLazy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
functionNode.setState(CompilationState.CONSTANT_FOLDED);
|
||||
return functionNode;
|
||||
return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -251,7 +247,7 @@ final class FoldConstants extends NodeVisitor {
|
||||
value = lhs.getNumber() - rhs.getNumber();
|
||||
break;
|
||||
case SHR:
|
||||
return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
|
||||
return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & JSType.MAX_UINT);
|
||||
case SAR:
|
||||
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
|
||||
case SHL:
|
||||
|
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
|
||||
/**
|
||||
* Tracks the variable area state.
|
||||
*
|
||||
*/
|
||||
public final class Frame {
|
||||
/** Previous frame. */
|
||||
private Frame previous;
|
||||
|
||||
/** Current variables. */
|
||||
private final ArrayList<Symbol> symbols;
|
||||
|
||||
/** Number of slots in previous frame. */
|
||||
private int baseCount;
|
||||
|
||||
/** Number of slots in this frame. */
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param previous frame, the parent variable frame
|
||||
*/
|
||||
public Frame(final Frame previous) {
|
||||
this.previous = previous;
|
||||
this.symbols = new ArrayList<>();
|
||||
this.baseCount = getBaseCount();
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
* @param frame
|
||||
* @param symbols
|
||||
*/
|
||||
private Frame(final Frame frame, final List<Symbol> symbols) {
|
||||
this.previous = frame.getPrevious() == null ? null : new Frame(frame.getPrevious(), frame.getPrevious().getSymbols());
|
||||
this.symbols = new ArrayList<>(frame.getSymbols());
|
||||
this.baseCount = frame.getBaseCount();
|
||||
this.count = frame.getCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the frame
|
||||
*
|
||||
* @return a new frame with the identical contents
|
||||
*/
|
||||
public Frame copy() {
|
||||
return new Frame(this, getSymbols());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new variable to the frame.
|
||||
* @param symbol Symbol representing variable.
|
||||
*/
|
||||
public void addSymbol(final Symbol symbol) {
|
||||
final int slot = symbol.getSlot();
|
||||
if (slot < 0) {
|
||||
symbols.add(symbol);
|
||||
count += symbol.slotCount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Realign slot numbering prior to code generation.
|
||||
* @return Number of slots in frame.
|
||||
*/
|
||||
public int realign() {
|
||||
baseCount = getBaseCount();
|
||||
count = 0;
|
||||
|
||||
for (final Symbol symbol : symbols) {
|
||||
if (symbol.hasSlot()) {
|
||||
symbol.setSlot(baseCount + count);
|
||||
count += symbol.slotCount();
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the slot count of previous frames.
|
||||
* @return Number of slots in previous frames.
|
||||
*/
|
||||
private int getBaseCount() {
|
||||
return previous != null ? previous.getSlotCount() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the number of slots to top of frame.
|
||||
* @return Number of slots in total.
|
||||
*/
|
||||
public int getSlotCount() {
|
||||
return baseCount + count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
Frame f = this;
|
||||
boolean hasPrev = false;
|
||||
int pos = 0;
|
||||
|
||||
do {
|
||||
if (hasPrev) {
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
sb.append("#").
|
||||
append(pos++).
|
||||
append(" {baseCount:").
|
||||
append(baseCount).
|
||||
append(", ").
|
||||
append("count:").
|
||||
append(count).
|
||||
append("} ");
|
||||
|
||||
for (final Symbol var : f.getSymbols()) {
|
||||
sb.append('[').
|
||||
append(var.toString()).
|
||||
append(' ').
|
||||
append(var.hashCode()).
|
||||
append("] ");
|
||||
}
|
||||
|
||||
f = f.getPrevious();
|
||||
hasPrev = true;
|
||||
} while (f != null);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get variable count for this frame
|
||||
* @return variable count
|
||||
*/
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get previous frame
|
||||
* @return previous frame
|
||||
*/
|
||||
public Frame getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set previous frame
|
||||
* @param previous previous frame
|
||||
*/
|
||||
public void setPrevious(final Frame previous) {
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get symbols in frame
|
||||
* @return a list of symbols in this frame
|
||||
*/
|
||||
public List<Symbol> getSymbols() {
|
||||
return Collections.unmodifiableList(symbols);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -53,9 +53,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
|
||||
@ -67,6 +70,7 @@ import java.lang.reflect.Array;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import jdk.internal.dynalink.support.NameCodec;
|
||||
import jdk.internal.org.objectweb.asm.Handle;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
@ -79,14 +83,14 @@ import jdk.nashorn.internal.codegen.types.NumericType;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.RuntimeNode;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
import jdk.nashorn.internal.runtime.ArgumentSetter;
|
||||
import jdk.nashorn.internal.runtime.Debug;
|
||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.Scope;
|
||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
@ -116,10 +120,10 @@ public class MethodEmitter implements Emitter {
|
||||
private final ClassEmitter classEmitter;
|
||||
|
||||
/** FunctionNode representing this method, or null if none exists */
|
||||
private FunctionNode functionNode;
|
||||
protected FunctionNode functionNode;
|
||||
|
||||
/** SplitNode representing the current split, or null if none exists */
|
||||
private SplitNode splitNode;
|
||||
/** Check whether this emitter ever has a function return point */
|
||||
private boolean hasReturn;
|
||||
|
||||
/** The script environment */
|
||||
private final ScriptEnvironment env;
|
||||
@ -203,7 +207,7 @@ public class MethodEmitter implements Emitter {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + stack;
|
||||
return "methodEmitter: " + (functionNode == null ? method : functionNode.getName()).toString() + ' ' + Debug.id(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,8 +480,8 @@ public class MethodEmitter implements Emitter {
|
||||
|
||||
String name = symbol.getName();
|
||||
|
||||
if (name.equals(THIS.tag())) {
|
||||
name = THIS_DEBUGGER.tag();
|
||||
if (name.equals(THIS.symbolName())) {
|
||||
name = THIS_DEBUGGER.symbolName();
|
||||
}
|
||||
|
||||
method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot());
|
||||
@ -654,7 +658,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return this method emitter
|
||||
*/
|
||||
MethodEmitter loadConstants() {
|
||||
getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor());
|
||||
getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
|
||||
assert peekType().isArray() : peekType();
|
||||
return this;
|
||||
}
|
||||
@ -669,7 +673,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter loadUndefined(final Type type) {
|
||||
debug("load undefined " + type);
|
||||
debug("load undefined ", type);
|
||||
pushType(type.loadUndefined(method));
|
||||
return this;
|
||||
}
|
||||
@ -681,7 +685,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter loadEmpty(final Type type) {
|
||||
debug("load empty " + type);
|
||||
debug("load empty ", type);
|
||||
pushType(type.loadEmpty(method));
|
||||
return this;
|
||||
}
|
||||
@ -814,7 +818,7 @@ public class MethodEmitter implements Emitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an local variable to the stack. If the symbol representing
|
||||
* Push a local variable to the stack. If the symbol representing
|
||||
* the local variable doesn't have a slot, this is a NOP
|
||||
*
|
||||
* @param symbol the symbol representing the local variable.
|
||||
@ -835,13 +839,13 @@ public class MethodEmitter implements Emitter {
|
||||
if (functionNode.needsArguments()) {
|
||||
// ScriptObject.getArgument(int) on arguments
|
||||
debug("load symbol", symbol.getName(), " arguments index=", index);
|
||||
loadArguments();
|
||||
loadCompilerConstant(ARGUMENTS);
|
||||
load(index);
|
||||
ScriptObject.GET_ARGUMENT.invoke(this);
|
||||
} else {
|
||||
// array load from __varargs__
|
||||
debug("load symbol", symbol.getName(), " array index=", index);
|
||||
loadVarArgs();
|
||||
loadCompilerConstant(VARARGS);
|
||||
load(symbol.getFieldIndex());
|
||||
arrayload();
|
||||
}
|
||||
@ -870,47 +874,12 @@ public class MethodEmitter implements Emitter {
|
||||
if(functionNode == null) {
|
||||
return slot == CompilerConstants.JAVA_THIS.slot();
|
||||
}
|
||||
final int thisSlot = functionNode.getThisNode().getSymbol().getSlot();
|
||||
final int thisSlot = compilerConstant(THIS).getSlot();
|
||||
assert !functionNode.needsCallee() || thisSlot == 1; // needsCallee -> thisSlot == 1
|
||||
assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
|
||||
return slot == thisSlot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the this object to the stack.
|
||||
*
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter loadThis() {
|
||||
load(functionNode.getThisNode().getSymbol());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the scope object to the stack.
|
||||
*
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter loadScope() {
|
||||
if (peekType() == Type.SCOPE) {
|
||||
dup();
|
||||
return this;
|
||||
}
|
||||
load(functionNode.getScopeNode().getSymbol());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the return object to the stack.
|
||||
*
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter loadResult() {
|
||||
load(functionNode.getResultNode().getSymbol());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Push a method handle to the stack
|
||||
*
|
||||
@ -927,62 +896,33 @@ public class MethodEmitter implements Emitter {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the varargs object to the stack
|
||||
*
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter loadVarArgs() {
|
||||
debug("load var args " + functionNode.getVarArgsNode().getSymbol());
|
||||
return load(functionNode.getVarArgsNode().getSymbol());
|
||||
private Symbol compilerConstant(final CompilerConstants cc) {
|
||||
return functionNode.getBody().getExistingSymbol(cc.symbolName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the arguments array to the stack
|
||||
*
|
||||
* @return the method emitter
|
||||
* True if this method has a slot allocated for the scope variable (meaning, something in the method actually needs
|
||||
* the scope).
|
||||
* @return if this method has a slot allocated for the scope variable.
|
||||
*/
|
||||
MethodEmitter loadArguments() {
|
||||
debug("load arguments ", functionNode.getArgumentsNode().getSymbol());
|
||||
assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0;
|
||||
return load(functionNode.getArgumentsNode().getSymbol());
|
||||
boolean hasScope() {
|
||||
return compilerConstant(SCOPE).hasSlot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push the callee object to the stack
|
||||
*
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter loadCallee() {
|
||||
final Symbol calleeSymbol = functionNode.getCalleeNode().getSymbol();
|
||||
debug("load callee ", calleeSymbol);
|
||||
assert calleeSymbol.getSlot() == 0 : "callee has wrong slot " + calleeSymbol.getSlot() + " in " + functionNode.getName();
|
||||
|
||||
return load(calleeSymbol);
|
||||
MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
|
||||
final Symbol symbol = compilerConstant(cc);
|
||||
if (cc == SCOPE && peekType() == Type.SCOPE) {
|
||||
dup();
|
||||
return this;
|
||||
}
|
||||
debug("load compiler constant ", symbol);
|
||||
return load(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the scope from the stack and store it in its predefined slot
|
||||
*/
|
||||
void storeScope() {
|
||||
debug("store scope");
|
||||
store(functionNode.getScopeNode().getSymbol());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the return from the stack and store it in its predefined slot
|
||||
*/
|
||||
void storeResult() {
|
||||
debug("store result");
|
||||
store(functionNode.getResultNode().getSymbol());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop the arguments array from the stack and store it in its predefined slot
|
||||
*/
|
||||
void storeArguments() {
|
||||
debug("store arguments");
|
||||
store(functionNode.getArgumentsNode().getSymbol());
|
||||
void storeCompilerConstant(final CompilerConstants cc) {
|
||||
final Symbol symbol = compilerConstant(cc);
|
||||
debug("store compiler constant ", symbol);
|
||||
store(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1030,13 +970,13 @@ public class MethodEmitter implements Emitter {
|
||||
final int index = symbol.getFieldIndex();
|
||||
if (functionNode.needsArguments()) {
|
||||
debug("store symbol", symbol.getName(), " arguments index=", index);
|
||||
loadArguments();
|
||||
loadCompilerConstant(ARGUMENTS);
|
||||
load(index);
|
||||
ArgumentSetter.SET_ARGUMENT.invoke(this);
|
||||
} else {
|
||||
// varargs without arguments object - just do array store to __varargs__
|
||||
debug("store symbol", symbol.getName(), " array index=", index);
|
||||
loadVarArgs();
|
||||
loadCompilerConstant(VARARGS);
|
||||
load(index);
|
||||
ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
|
||||
}
|
||||
@ -1144,7 +1084,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter newarray(final ArrayType arrayType) {
|
||||
debug("newarray ", "arrayType=" + arrayType);
|
||||
debug("newarray ", "arrayType=", arrayType);
|
||||
popType(Type.INT); //LENGTH
|
||||
pushType(arrayType.newarray(method));
|
||||
return this;
|
||||
@ -1223,7 +1163,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter invokespecial(final String className, final String methodName, final String methodDescriptor) {
|
||||
debug("invokespecial", className + "." + methodName + methodDescriptor);
|
||||
debug("invokespecial", className, ".", methodName, methodDescriptor);
|
||||
return invoke(INVOKESPECIAL, className, methodName, methodDescriptor, true);
|
||||
}
|
||||
|
||||
@ -1237,7 +1177,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter invokevirtual(final String className, final String methodName, final String methodDescriptor) {
|
||||
debug("invokevirtual", className + "." + methodName + methodDescriptor + " " + stack);
|
||||
debug("invokevirtual", className, ".", methodName, methodDescriptor, " ", stack);
|
||||
return invoke(INVOKEVIRTUAL, className, methodName, methodDescriptor, true);
|
||||
}
|
||||
|
||||
@ -1251,7 +1191,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter invokestatic(final String className, final String methodName, final String methodDescriptor) {
|
||||
debug("invokestatic", className + "." + methodName + methodDescriptor);
|
||||
debug("invokestatic", className, ".", methodName, methodDescriptor);
|
||||
invoke(INVOKESTATIC, className, methodName, methodDescriptor, false);
|
||||
return this;
|
||||
}
|
||||
@ -1284,7 +1224,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter invokeinterface(final String className, final String methodName, final String methodDescriptor) {
|
||||
debug("invokeinterface", className + "." + methodName + methodDescriptor);
|
||||
debug("invokeinterface", className, ".", methodName, methodDescriptor);
|
||||
return invoke(INVOKEINTERFACE, className, methodName, methodDescriptor, true);
|
||||
}
|
||||
|
||||
@ -1336,15 +1276,20 @@ public class MethodEmitter implements Emitter {
|
||||
*/
|
||||
void conditionalJump(final Condition cond, final boolean isCmpG, final Label trueLabel) {
|
||||
if (peekType().isCategory2()) {
|
||||
debug("[ld]cmp isCmpG=" + isCmpG);
|
||||
debug("[ld]cmp isCmpG=", isCmpG);
|
||||
pushType(get2n().cmp(method, isCmpG));
|
||||
jump(Condition.toUnary(cond), trueLabel, 1);
|
||||
} else {
|
||||
debug("if" + cond);
|
||||
debug("if", cond);
|
||||
jump(Condition.toBinary(cond, peekType().isObject()), trueLabel, 2);
|
||||
}
|
||||
}
|
||||
|
||||
MethodEmitter registerReturn() {
|
||||
this.hasReturn = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a non void return, popping the type from the stack
|
||||
*
|
||||
@ -1385,22 +1330,7 @@ public class MethodEmitter implements Emitter {
|
||||
*
|
||||
* @param label destination label
|
||||
*/
|
||||
void splitAwareGoto(final Label label) {
|
||||
|
||||
if (splitNode != null) {
|
||||
final int index = splitNode.getExternalTargets().indexOf(label);
|
||||
|
||||
if (index > -1) {
|
||||
loadScope();
|
||||
checkcast(Scope.class);
|
||||
load(index + 1);
|
||||
invoke(Scope.SET_SPLIT_STATE);
|
||||
loadUndefined(Type.OBJECT);
|
||||
_return(functionNode.getReturnType());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void splitAwareGoto(final LexicalContext lc, final Label label) {
|
||||
_goto(label);
|
||||
}
|
||||
|
||||
@ -1595,7 +1525,7 @@ public class MethodEmitter implements Emitter {
|
||||
*/
|
||||
private void mergeStackTo(final Label label) {
|
||||
final ArrayDeque<Type> labelStack = label.getStack();
|
||||
//debug(labelStack == null ? " >> Control flow - first visit " + label : " >> Control flow - JOIN with " + labelStack + " at " + label);
|
||||
//debug(labelStack == null ? " >> Control flow - first visit ", label : " >> Control flow - JOIN with ", labelStack, " at ", label);
|
||||
if (labelStack == null) {
|
||||
assert stack != null;
|
||||
label.setStack(stack.clone());
|
||||
@ -1788,7 +1718,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter dynamicNew(final int argCount, final int flags) {
|
||||
debug("dynamic_new", "argcount=" + argCount);
|
||||
debug("dynamic_new", "argcount=", argCount);
|
||||
final String signature = getDynamicSignature(Type.OBJECT, argCount);
|
||||
method.visitInvokeDynamicInsn("dyn:new", signature, LINKERBOOTSTRAP, flags);
|
||||
pushType(Type.OBJECT); //TODO fix result type
|
||||
@ -1805,7 +1735,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter dynamicCall(final Type returnType, final int argCount, final int flags) {
|
||||
debug("dynamic_call", "args=" + argCount, "returnType=" + returnType);
|
||||
debug("dynamic_call", "args=", argCount, "returnType=", returnType);
|
||||
final String signature = getDynamicSignature(returnType, argCount); // +1 because the function itself is the 1st parameter for dynamic calls (what you call - call target)
|
||||
debug(" signature", signature);
|
||||
method.visitInvokeDynamicInsn("dyn:call", signature, LINKERBOOTSTRAP, flags);
|
||||
@ -1824,7 +1754,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter dynamicRuntimeCall(final String name, final Type returnType, final RuntimeNode.Request request) {
|
||||
debug("dynamic_runtime_call", name, "args=" + request.getArity(), "returnType=" + returnType);
|
||||
debug("dynamic_runtime_call", name, "args=", request.getArity(), "returnType=", returnType);
|
||||
final String signature = getDynamicSignature(returnType, request.getArity());
|
||||
debug(" signature", signature);
|
||||
method.visitInvokeDynamicInsn(name, signature, RUNTIMEBOOTSTRAP);
|
||||
@ -1895,7 +1825,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter dynamicGetIndex(final Type result, final int flags, final boolean isMethod) {
|
||||
debug("dynamic_get_index", peekType(1) + "[" + peekType() + "]");
|
||||
debug("dynamic_get_index", peekType(1), "[", peekType(), "]");
|
||||
|
||||
Type resultType = result;
|
||||
if (result.isBoolean()) {
|
||||
@ -1931,7 +1861,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @param flags call site flags for setter
|
||||
*/
|
||||
void dynamicSetIndex(final int flags) {
|
||||
debug("dynamic_set_index", peekType(2) + "[" + peekType(1) + "] =", peekType());
|
||||
debug("dynamic_set_index", peekType(2), "[", peekType(1), "] =", peekType());
|
||||
|
||||
Type value = peekType();
|
||||
if (value.isObject() || value.isBoolean()) {
|
||||
@ -2031,7 +1961,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter getField(final String className, final String fieldName, final String fieldDescriptor) {
|
||||
debug("getfield", "receiver=" + peekType(), className + "." + fieldName + fieldDescriptor);
|
||||
debug("getfield", "receiver=", peekType(), className, ".", fieldName, fieldDescriptor);
|
||||
final Type receiver = popType();
|
||||
assert receiver.isObject();
|
||||
method.visitFieldInsn(GETFIELD, className, fieldName, fieldDescriptor);
|
||||
@ -2049,7 +1979,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @return the method emitter
|
||||
*/
|
||||
MethodEmitter getStatic(final String className, final String fieldName, final String fieldDescriptor) {
|
||||
debug("getstatic", className + "." + fieldName + "." + fieldDescriptor);
|
||||
debug("getstatic", className, ".", fieldName, ".", fieldDescriptor);
|
||||
method.visitFieldInsn(GETSTATIC, className, fieldName, fieldDescriptor);
|
||||
pushType(fieldType(fieldDescriptor));
|
||||
return this;
|
||||
@ -2063,7 +1993,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @param fieldDescriptor field descriptor
|
||||
*/
|
||||
void putField(final String className, final String fieldName, final String fieldDescriptor) {
|
||||
debug("putfield", "receiver=" + peekType(1), "value=" + peekType());
|
||||
debug("putfield", "receiver=", peekType(1), "value=", peekType());
|
||||
popType(fieldType(fieldDescriptor));
|
||||
popType(Type.OBJECT);
|
||||
method.visitFieldInsn(PUTFIELD, className, fieldName, fieldDescriptor);
|
||||
@ -2077,7 +2007,7 @@ public class MethodEmitter implements Emitter {
|
||||
* @param fieldDescriptor field descriptor
|
||||
*/
|
||||
void putStatic(final String className, final String fieldName, final String fieldDescriptor) {
|
||||
debug("putfield", "value=" + peekType());
|
||||
debug("putfield", "value=", peekType());
|
||||
popType(fieldType(fieldDescriptor));
|
||||
method.visitFieldInsn(PUTSTATIC, className, fieldName, fieldDescriptor);
|
||||
}
|
||||
@ -2237,7 +2167,7 @@ public class MethodEmitter implements Emitter {
|
||||
}
|
||||
|
||||
if (env != null) { //early bootstrap code doesn't have inited context yet
|
||||
LOG.info(sb.toString());
|
||||
LOG.info(sb);
|
||||
if (DEBUG_TRACE_LINE == linePrefix) {
|
||||
new Throwable().printStackTrace(LOG.getOutputStream());
|
||||
}
|
||||
@ -2254,21 +2184,12 @@ public class MethodEmitter implements Emitter {
|
||||
this.functionNode = functionNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the split node for this method emitter, if this is code
|
||||
* generation due to splitting large methods
|
||||
*
|
||||
* @return split node
|
||||
*/
|
||||
SplitNode getSplitNode() {
|
||||
return splitNode;
|
||||
boolean hasReturn() {
|
||||
return hasReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the split node for this method emitter
|
||||
* @param splitNode split node
|
||||
*/
|
||||
void setSplitNode(final SplitNode splitNode) {
|
||||
this.splitNode = splitNode;
|
||||
List<Label> getExternalTargets() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public class Namespace {
|
||||
*/
|
||||
public Namespace(final Namespace parent) {
|
||||
this.parent = parent;
|
||||
directory = new HashMap<>();
|
||||
this.directory = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,10 +65,6 @@ public class Namespace {
|
||||
return parent;
|
||||
}
|
||||
|
||||
private HashMap<String, Integer> getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a uniqueName name in the namespace in the form base$n where n varies
|
||||
* .
|
||||
@ -78,7 +74,7 @@ public class Namespace {
|
||||
*/
|
||||
public String uniqueName(final String base) {
|
||||
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
|
||||
final HashMap<String, Integer> namespaceDirectory = namespace.getDirectory();
|
||||
final HashMap<String, Integer> namespaceDirectory = namespace.directory;
|
||||
final Integer counter = namespaceDirectory.get(base);
|
||||
|
||||
if (counter != null) {
|
||||
|
@ -28,10 +28,10 @@ package jdk.nashorn.internal.codegen;
|
||||
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.MAP;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
@ -204,8 +204,8 @@ public final class ObjectClassGenerator {
|
||||
* @return The class name.
|
||||
*/
|
||||
public static String getClassName(final int fieldCount) {
|
||||
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount :
|
||||
SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag();
|
||||
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
|
||||
SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,7 +218,23 @@ public final class ObjectClassGenerator {
|
||||
* @return The class name.
|
||||
*/
|
||||
public static String getClassName(final int fieldCount, final int paramCount) {
|
||||
return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount + SCOPE_MARKER + paramCount;
|
||||
return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
|
||||
* {@link #getClassName(int)} or {@link #getClassName(int, int)}.
|
||||
* @param clazz the JavaScript scope class.
|
||||
* @return the number of fields in the scope class.
|
||||
*/
|
||||
public static int getFieldCount(Class<?> clazz) {
|
||||
final String name = clazz.getSimpleName();
|
||||
final String prefix = JS_OBJECT_PREFIX.symbolName();
|
||||
if(prefix.equals(name)) {
|
||||
return 0;
|
||||
}
|
||||
final int scopeMarker = name.indexOf(SCOPE_MARKER);
|
||||
return Integer.parseInt(scopeMarker == -1 ? name.substring(prefix.length()) : name.substring(prefix.length(), scopeMarker));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -387,7 +403,7 @@ public final class ObjectClassGenerator {
|
||||
final MethodEmitter init = classEmitter.init(PropertyMap.class);
|
||||
init.begin();
|
||||
init.load(Type.OBJECT, JAVA_THIS.slot());
|
||||
init.load(Type.OBJECT, MAP.slot());
|
||||
init.load(Type.OBJECT, INIT_MAP.slot());
|
||||
init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
|
||||
|
||||
return init;
|
||||
@ -402,7 +418,7 @@ public final class ObjectClassGenerator {
|
||||
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
|
||||
init.begin();
|
||||
init.load(Type.OBJECT, JAVA_THIS.slot());
|
||||
init.load(Type.OBJECT, MAP.slot());
|
||||
init.load(Type.OBJECT, INIT_MAP.slot());
|
||||
init.load(Type.OBJECT, INIT_SCOPE.slot());
|
||||
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
|
||||
|
||||
@ -418,7 +434,7 @@ public final class ObjectClassGenerator {
|
||||
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, Object.class);
|
||||
init.begin();
|
||||
init.load(Type.OBJECT, JAVA_THIS.slot());
|
||||
init.load(Type.OBJECT, MAP.slot());
|
||||
init.load(Type.OBJECT, INIT_MAP.slot());
|
||||
init.load(Type.OBJECT, INIT_SCOPE.slot());
|
||||
init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
|
||||
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, Object.class));
|
||||
@ -449,7 +465,7 @@ public final class ObjectClassGenerator {
|
||||
* @param className Name of JavaScript class.
|
||||
*/
|
||||
private static void newAllocate(final ClassEmitter classEmitter, final String className) {
|
||||
final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.tag(), ScriptObject.class, PropertyMap.class);
|
||||
final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
|
||||
allocate.begin();
|
||||
allocate._new(className);
|
||||
allocate.dup();
|
||||
|
@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
public abstract class ObjectCreator {
|
||||
|
||||
/** Compile unit for this ObjectCreator, see CompileUnit */
|
||||
protected final CompileUnit compileUnit;
|
||||
//protected final CompileUnit compileUnit;
|
||||
|
||||
/** List of keys to initiate in this ObjectCreator */
|
||||
protected final List<String> keys;
|
||||
@ -66,7 +66,6 @@ public abstract class ObjectCreator {
|
||||
*/
|
||||
protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
|
||||
this.codegen = codegen;
|
||||
this.compileUnit = codegen.getCurrentCompileUnit();
|
||||
this.keys = keys;
|
||||
this.symbols = symbols;
|
||||
this.isScope = isScope;
|
||||
|
100
nashorn/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
Normal file
100
nashorn/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.runtime.Scope;
|
||||
|
||||
/**
|
||||
* Emitter used for splitting methods. Needs to keep track of if there are jump targets
|
||||
* outside the current split node. All external jump targets encountered at method
|
||||
* emission are logged, and {@code CodeGenerator#leaveSplitNode(SplitNode)} creates
|
||||
* an appropriate jump table when the SplitNode has been iterated through
|
||||
*/
|
||||
public class SplitMethodEmitter extends MethodEmitter {
|
||||
|
||||
private final SplitNode splitNode;
|
||||
|
||||
private final List<Label> externalTargets = new ArrayList<>();
|
||||
|
||||
SplitMethodEmitter(final ClassEmitter classEmitter, final MethodVisitor mv, SplitNode splitNode) {
|
||||
super(classEmitter, mv);
|
||||
this.splitNode = splitNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
void splitAwareGoto(final LexicalContext lc, final Label label) {
|
||||
assert splitNode != null;
|
||||
final int index = findExternalTarget(lc, label);
|
||||
if (index >= 0) {
|
||||
loadCompilerConstant(SCOPE);
|
||||
checkcast(Scope.class);
|
||||
load(index + 1);
|
||||
invoke(Scope.SET_SPLIT_STATE);
|
||||
loadUndefined(Type.OBJECT);
|
||||
_return(functionNode.getReturnType());
|
||||
return;
|
||||
}
|
||||
super.splitAwareGoto(lc, label);
|
||||
}
|
||||
|
||||
private int findExternalTarget(final LexicalContext lc, final Label label) {
|
||||
final int index = externalTargets.indexOf(label);
|
||||
|
||||
if (index >= 0) {
|
||||
return index;
|
||||
}
|
||||
|
||||
if (lc.isExternalTarget(splitNode, label)) {
|
||||
externalTargets.add(label);
|
||||
return externalTargets.size() - 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodEmitter registerReturn() {
|
||||
super.registerReturn();
|
||||
loadCompilerConstant(SCOPE);
|
||||
checkcast(Scope.class);
|
||||
load(0);
|
||||
invoke(Scope.SET_SPLIT_STATE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
final List<Label> getExternalTargets() {
|
||||
return externalTargets;
|
||||
}
|
||||
}
|
@ -28,29 +28,18 @@ package jdk.nashorn.internal.codegen;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.BreakNode;
|
||||
import jdk.nashorn.internal.ir.ContinueNode;
|
||||
import jdk.nashorn.internal.ir.DoWhileNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.LabelNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.ReturnNode;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.SwitchNode;
|
||||
import jdk.nashorn.internal.ir.WhileNode;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
@ -64,7 +53,7 @@ final class Splitter extends NodeVisitor {
|
||||
private final Compiler compiler;
|
||||
|
||||
/** IR to be broken down. */
|
||||
private final FunctionNode functionNode;
|
||||
private FunctionNode outermost;
|
||||
|
||||
/** Compile unit for the main script. */
|
||||
private final CompileUnit outermostCompileUnit;
|
||||
@ -72,8 +61,6 @@ final class Splitter extends NodeVisitor {
|
||||
/** Cache for calculated block weights. */
|
||||
private final Map<Node, Long> weightCache = new HashMap<>();
|
||||
|
||||
private final LexicalContext lexicalContext = new LexicalContext();
|
||||
|
||||
/** Weight threshold for when to start a split. */
|
||||
public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024);
|
||||
|
||||
@ -88,70 +75,92 @@ final class Splitter extends NodeVisitor {
|
||||
*/
|
||||
public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
|
||||
this.compiler = compiler;
|
||||
this.functionNode = functionNode;
|
||||
this.outermost = functionNode;
|
||||
this.outermostCompileUnit = outermostCompileUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the split
|
||||
*/
|
||||
void split() {
|
||||
FunctionNode split(final FunctionNode fn) {
|
||||
FunctionNode functionNode = fn;
|
||||
|
||||
if (functionNode.isLazy()) {
|
||||
LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy");
|
||||
return;
|
||||
LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
LOG.finest("Initiating split of '" + functionNode.getName() + "'");
|
||||
LOG.finest("Initiating split of '", functionNode.getName(), "'");
|
||||
|
||||
final LexicalContext lc = getLexicalContext();
|
||||
|
||||
long weight = WeighNodes.weigh(functionNode);
|
||||
final boolean top = compiler.getFunctionNode() == outermost;
|
||||
|
||||
if (weight >= SPLIT_THRESHOLD) {
|
||||
LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD);
|
||||
|
||||
functionNode.accept(this);
|
||||
LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
|
||||
functionNode = (FunctionNode)functionNode.accept(this);
|
||||
|
||||
if (functionNode.isSplit()) {
|
||||
// Weight has changed so weigh again, this time using block weight cache
|
||||
weight = WeighNodes.weigh(functionNode, weightCache);
|
||||
functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(lc));
|
||||
}
|
||||
|
||||
if (weight >= SPLIT_THRESHOLD) {
|
||||
weight = splitBlock(functionNode, functionNode);
|
||||
}
|
||||
|
||||
if (functionNode.isSplit()) {
|
||||
functionNode.accept(new SplitFlowAnalyzer());
|
||||
functionNode = functionNode.setBody(lc, splitBlock(functionNode.getBody(), functionNode));
|
||||
weight = WeighNodes.weigh(functionNode.getBody(), weightCache);
|
||||
}
|
||||
}
|
||||
|
||||
assert functionNode.getCompileUnit() == null : "compile unit already set";
|
||||
assert functionNode.getCompileUnit() == null : "compile unit already set for " + functionNode.getName();
|
||||
|
||||
if (compiler.getFunctionNode() == functionNode) { //functionNode.isScript()) {
|
||||
if (top) {
|
||||
assert outermostCompileUnit != null : "outermost compile unit is null";
|
||||
|
||||
functionNode.setCompileUnit(outermostCompileUnit);
|
||||
functionNode = functionNode.setCompileUnit(lc, outermostCompileUnit);
|
||||
outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
|
||||
} else {
|
||||
functionNode.setCompileUnit(findUnit(weight));
|
||||
functionNode = functionNode.setCompileUnit(lc, findUnit(weight));
|
||||
}
|
||||
|
||||
// Recursively split nested functions
|
||||
functionNode.accept(new NodeOperatorVisitor() {
|
||||
@Override
|
||||
public Node enterFunctionNode(FunctionNode function) {
|
||||
if(function == functionNode) {
|
||||
// Don't process outermost function (it was already processed) but descend into it to find nested
|
||||
// functions.
|
||||
return function;
|
||||
final Block body = functionNode.getBody();
|
||||
final List<FunctionNode> dc = directChildren(functionNode);
|
||||
|
||||
final Block newBody = (Block)body.accept(new NodeVisitor() {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode nestedFunction) {
|
||||
return dc.contains(nestedFunction);
|
||||
}
|
||||
// Process a nested function
|
||||
new Splitter(compiler, function, outermostCompileUnit).split();
|
||||
// Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions.
|
||||
return null;
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode nestedFunction) {
|
||||
FunctionNode split = new Splitter(compiler, nestedFunction, outermostCompileUnit).split(nestedFunction);
|
||||
getLexicalContext().replace(nestedFunction, split);
|
||||
return split;
|
||||
}
|
||||
});
|
||||
functionNode = functionNode.setBody(lc, newBody);
|
||||
|
||||
assert functionNode.getCompileUnit() != null;
|
||||
|
||||
return functionNode.setState(lc, CompilationState.SPLIT);
|
||||
}
|
||||
|
||||
private static List<FunctionNode> directChildren(final FunctionNode functionNode) {
|
||||
final List<FunctionNode> dc = new ArrayList<>();
|
||||
functionNode.accept(new NodeVisitor() {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode child) {
|
||||
if (child == functionNode) {
|
||||
return true;
|
||||
}
|
||||
if (getLexicalContext().getParentFunction(child) == functionNode) {
|
||||
dc.add(child);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
functionNode.setState(CompilationState.SPLIT);
|
||||
return dc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,8 +179,8 @@ final class Splitter extends NodeVisitor {
|
||||
*
|
||||
* @return new weight for the resulting block.
|
||||
*/
|
||||
private long splitBlock(final Block block, final FunctionNode function) {
|
||||
functionNode.setIsSplit();
|
||||
private Block splitBlock(final Block block, final FunctionNode function) {
|
||||
getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
|
||||
|
||||
final List<Node> splits = new ArrayList<>();
|
||||
List<Node> statements = new ArrayList<>();
|
||||
@ -186,7 +195,6 @@ final class Splitter extends NodeVisitor {
|
||||
statements = new ArrayList<>();
|
||||
statementsWeight = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (statement.isTerminal()) {
|
||||
@ -201,9 +209,7 @@ final class Splitter extends NodeVisitor {
|
||||
splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
|
||||
}
|
||||
|
||||
block.setStatements(splits);
|
||||
|
||||
return WeighNodes.weigh(block, weightCache);
|
||||
return block.setStatements(getLexicalContext(), splits);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,51 +224,44 @@ final class Splitter extends NodeVisitor {
|
||||
final Source source = parent.getSource();
|
||||
final long token = parent.getToken();
|
||||
final int finish = parent.getFinish();
|
||||
final String name = function.uniqueName(SPLIT_PREFIX.tag());
|
||||
final String name = function.uniqueName(SPLIT_PREFIX.symbolName());
|
||||
|
||||
final Block newBlock = new Block(source, token, finish);
|
||||
newBlock.setFrame(new Frame(parent.getFrame()));
|
||||
newBlock.setStatements(statements);
|
||||
final Block newBlock = new Block(source, token, finish, statements);
|
||||
|
||||
final SplitNode splitNode = new SplitNode(name, functionNode, newBlock);
|
||||
|
||||
splitNode.setCompileUnit(compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
|
||||
|
||||
return splitNode;
|
||||
return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBlock(final Block block) {
|
||||
public boolean enterBlock(final Block block) {
|
||||
if (block.isCatchBlock()) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
lexicalContext.push(block);
|
||||
|
||||
final long weight = WeighNodes.weigh(block, weightCache);
|
||||
|
||||
if (weight < SPLIT_THRESHOLD) {
|
||||
weightCache.put(block, weight);
|
||||
lexicalContext.pop(block);
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return block;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBlock(final Block block) {
|
||||
assert !block.isCatchBlock();
|
||||
|
||||
Block newBlock = block;
|
||||
|
||||
// Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
|
||||
// been split already, so weigh again before splitting.
|
||||
long weight = WeighNodes.weigh(block, weightCache);
|
||||
if (weight >= SPLIT_THRESHOLD) {
|
||||
weight = splitBlock(block, lexicalContext.getFunction(block));
|
||||
newBlock = splitBlock(block, getLexicalContext().getFunction(block));
|
||||
weight = WeighNodes.weigh(newBlock, weightCache);
|
||||
}
|
||||
weightCache.put(block, weight);
|
||||
|
||||
lexicalContext.pop(block);
|
||||
return block;
|
||||
weightCache.put(newBlock, weight);
|
||||
return newBlock;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@ -274,7 +273,7 @@ final class Splitter extends NodeVisitor {
|
||||
return literal;
|
||||
}
|
||||
|
||||
functionNode.setIsSplit();
|
||||
getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
|
||||
|
||||
if (literal instanceof ArrayLiteralNode) {
|
||||
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
|
||||
@ -312,123 +311,12 @@ final class Splitter extends NodeVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode node) {
|
||||
if(node == functionNode && !node.isLazy()) {
|
||||
lexicalContext.push(node);
|
||||
node.visitStatements(this);
|
||||
lexicalContext.pop(node);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static class SplitFlowAnalyzer extends NodeVisitor {
|
||||
|
||||
/** Stack of visited Split nodes, deepest node first. */
|
||||
private final Deque<SplitNode> splitStack;
|
||||
|
||||
/** Map of possible jump targets to containing split node */
|
||||
private final Map<Node,SplitNode> targetNodes = new HashMap<>();
|
||||
|
||||
SplitFlowAnalyzer() {
|
||||
this.splitStack = new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterLabelNode(final LabelNode labelNode) {
|
||||
registerJumpTarget(labelNode.getBreakNode());
|
||||
registerJumpTarget(labelNode.getContinueNode());
|
||||
return labelNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterWhileNode(final WhileNode whileNode) {
|
||||
registerJumpTarget(whileNode);
|
||||
return whileNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
registerJumpTarget(doWhileNode);
|
||||
return doWhileNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterForNode(final ForNode forNode) {
|
||||
registerJumpTarget(forNode);
|
||||
return forNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterSwitchNode(final SwitchNode switchNode) {
|
||||
registerJumpTarget(switchNode);
|
||||
return switchNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterReturnNode(final ReturnNode returnNode) {
|
||||
for (final SplitNode split : splitStack) {
|
||||
split.setHasReturn(true);
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterContinueNode(final ContinueNode continueNode) {
|
||||
searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
|
||||
return continueNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBreakNode(final BreakNode breakNode) {
|
||||
searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
|
||||
return breakNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterSplitNode(final SplitNode splitNode) {
|
||||
splitStack.addFirst(splitNode);
|
||||
return splitNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSplitNode(final SplitNode splitNode) {
|
||||
assert splitNode == splitStack.peekFirst();
|
||||
splitStack.removeFirst();
|
||||
return splitNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the split node containing a potential jump target.
|
||||
* @param targetNode a potential target node.
|
||||
*/
|
||||
private void registerJumpTarget(final Node targetNode) {
|
||||
final SplitNode splitNode = splitStack.peekFirst();
|
||||
if (splitNode != null) {
|
||||
targetNodes.put(targetNode, splitNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a jump target is outside the current split node and its parent split nodes.
|
||||
* @param targetNode the jump target node.
|
||||
* @param targetLabel the jump target label.
|
||||
*/
|
||||
private void searchJumpTarget(final Node targetNode, final Label targetLabel) {
|
||||
|
||||
final SplitNode targetSplit = targetNodes.get(targetNode);
|
||||
// Note that targetSplit may be null, indicating that targetNode is in top level method.
|
||||
// In this case we have to add the external jump target to all split nodes.
|
||||
|
||||
for (final SplitNode split : splitStack) {
|
||||
if (split == targetSplit) {
|
||||
break;
|
||||
}
|
||||
final List<Label> externalTargets = split.getExternalTargets();
|
||||
if (!externalTargets.contains(targetLabel)) {
|
||||
split.addExternalTarget(targetLabel);
|
||||
}
|
||||
}
|
||||
public boolean enterFunctionNode(final FunctionNode node) {
|
||||
//only go into the function node for this splitter. any subfunctions are rejected
|
||||
if (node == outermost && !node.isLazy()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,6 @@ import jdk.nashorn.internal.ir.BreakNode;
|
||||
import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.CatchNode;
|
||||
import jdk.nashorn.internal.ir.ContinueNode;
|
||||
import jdk.nashorn.internal.ir.DoWhileNode;
|
||||
import jdk.nashorn.internal.ir.ExecuteNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
@ -101,7 +100,7 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
* @param weightCache cache of already calculated block weights
|
||||
*/
|
||||
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
|
||||
super(null, null);
|
||||
super();
|
||||
this.topFunction = topFunction;
|
||||
this.weightCache = weightCache;
|
||||
}
|
||||
@ -123,13 +122,13 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBlock(final Block block) {
|
||||
public boolean enterBlock(final Block block) {
|
||||
if (weightCache != null && weightCache.containsKey(block)) {
|
||||
weight += weightCache.get(block);
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return block;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,12 +155,6 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
return continueNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
weight += LOOP_WEIGHT;
|
||||
return doWhileNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveExecuteNode(final ExecuteNode executeNode) {
|
||||
return executeNode;
|
||||
@ -174,15 +167,15 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode functionNode) {
|
||||
if(functionNode == topFunction) {
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (functionNode == topFunction) {
|
||||
// the function being weighted; descend into its statements
|
||||
functionNode.visitStatements(this);
|
||||
} else {
|
||||
// just a reference to inner function from outer function
|
||||
weight += FUNC_EXPR_WEIGHT;
|
||||
return true;
|
||||
// functionNode.visitStatements(this);
|
||||
}
|
||||
return null;
|
||||
// just a reference to inner function from outer function
|
||||
weight += FUNC_EXPR_WEIGHT;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -205,7 +198,7 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Node enterLiteralNode(final LiteralNode literalNode) {
|
||||
public boolean enterLiteralNode(final LiteralNode literalNode) {
|
||||
weight += LITERAL_WEIGHT;
|
||||
|
||||
if (literalNode instanceof ArrayLiteralNode) {
|
||||
@ -224,10 +217,10 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return literalNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -249,9 +242,9 @@ final class WeighNodes extends NodeOperatorVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterSplitNode(final SplitNode splitNode) {
|
||||
public boolean enterSplitNode(final SplitNode splitNode) {
|
||||
weight += SPLIT_WEIGHT;
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,12 +92,6 @@ public final class BooleanType extends Type {
|
||||
return BOOLEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type loadEmpty(final MethodVisitor method) {
|
||||
assert false : "unsupported operation";
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _return(final MethodVisitor method) {
|
||||
method.visitInsn(IRETURN);
|
||||
|
@ -240,12 +240,6 @@ class IntType extends BitwiseType {
|
||||
return INT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type loadEmpty(final MethodVisitor method) {
|
||||
assert false : "unsupported operation";
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
|
||||
assert false : "unsupported operation";
|
||||
|
@ -216,12 +216,6 @@ class LongType extends BitwiseType {
|
||||
return LONG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type loadEmpty(final MethodVisitor method) {
|
||||
assert false : "unsupported operation";
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
|
||||
return cmp(method);
|
||||
|
@ -88,12 +88,6 @@ class NumberType extends NumericType {
|
||||
return NUMBER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type loadEmpty(final MethodVisitor method) {
|
||||
assert false : "unsupported operation";
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type ldc(final MethodVisitor method, final Object c) {
|
||||
assert c instanceof Double;
|
||||
|
@ -616,6 +616,12 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type loadEmpty(final MethodVisitor method) {
|
||||
assert false : "unsupported operation";
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Superclass logic for pop for all types
|
||||
*
|
||||
@ -663,7 +669,6 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
method.visitInsn(SWAP);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -840,12 +845,6 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type loadEmpty(final MethodVisitor method) {
|
||||
assert false : "unsupported operation";
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type convert(final MethodVisitor method, final Type to) {
|
||||
assert false : "unsupported operation";
|
||||
|
@ -25,23 +25,18 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of a property access (period operator.)
|
||||
*
|
||||
*/
|
||||
public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
|
||||
@Immutable
|
||||
public final class AccessNode extends BaseNode {
|
||||
/** Property ident. */
|
||||
private IdentNode property;
|
||||
|
||||
/** Does this node have a type override */
|
||||
private boolean hasCallSiteType;
|
||||
private final IdentNode property;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -53,49 +48,13 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
|
||||
* @param property property
|
||||
*/
|
||||
public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) {
|
||||
super(source, token, finish, base);
|
||||
|
||||
this.start = base.getStart();
|
||||
super(source, token, finish, base, false, false);
|
||||
this.property = property.setIsPropertyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param accessNode source node
|
||||
*/
|
||||
public AccessNode(final AccessNode accessNode) {
|
||||
this(accessNode, new CopyState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal copy constructor
|
||||
*
|
||||
* @param accessNode source node
|
||||
* @param cs copy state
|
||||
*/
|
||||
protected AccessNode(final AccessNode accessNode, final CopyState cs) {
|
||||
super(accessNode, cs);
|
||||
this.property = (IdentNode)cs.existingOrCopy(accessNode.getProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new AccessNode(this, cs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
final AccessNode accessNode = (AccessNode)other;
|
||||
return property.equals(accessNode.getProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ property.hashCode();
|
||||
private AccessNode(final AccessNode accessNode, final Node base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
super(accessNode, base, isFunction, hasCallSiteType);
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,12 +63,11 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterAccessNode(this) != null) {
|
||||
base = base.accept(visitor);
|
||||
property = (IdentNode)property.accept(visitor);
|
||||
return visitor.leaveAccessNode(this);
|
||||
if (visitor.enterAccessNode(this)) {
|
||||
return visitor.leaveAccessNode(
|
||||
setBase(base.accept(visitor)).
|
||||
setProperty((IdentNode)property.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -117,7 +75,7 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
|
||||
public void toString(final StringBuilder sb) {
|
||||
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
|
||||
|
||||
if (hasCallSiteType) {
|
||||
if (hasCallSiteType()) {
|
||||
sb.append('{');
|
||||
final String desc = getType().getDescriptor();
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
|
||||
@ -147,19 +105,34 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
|
||||
return property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessNode setType(final Type type) {
|
||||
if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
|
||||
private AccessNode setBase(final Node base) {
|
||||
if (this.base == base) {
|
||||
return this;
|
||||
}
|
||||
property = property.setType(type);
|
||||
getSymbol().setTypeOverride(type); //always a temp so this is fine.
|
||||
hasCallSiteType = true;
|
||||
return this;
|
||||
return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
|
||||
}
|
||||
|
||||
|
||||
private AccessNode setProperty(final IdentNode property) {
|
||||
if (this.property == property) {
|
||||
return this;
|
||||
}
|
||||
return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHaveCallSiteType() {
|
||||
return true; //carried by the symbol and always the same nodetype==symboltype
|
||||
public AccessNode setType(final Type type) {
|
||||
logTypeChange(type);
|
||||
getSymbol().setTypeOverride(type); //always a temp so this is fine.
|
||||
return new AccessNode(this, base, property.setType(type), isFunction(), hasCallSiteType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseNode setIsFunction() {
|
||||
if (isFunction()) {
|
||||
return this;
|
||||
}
|
||||
return new AccessNode(this, base, property, true, hasCallSiteType());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,10 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
@ -33,12 +37,15 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
* @see AccessNode
|
||||
* @see IndexNode
|
||||
*/
|
||||
public abstract class BaseNode extends Node implements FunctionCall {
|
||||
@Immutable
|
||||
public abstract class BaseNode extends Node implements FunctionCall, TypeOverride<BaseNode> {
|
||||
|
||||
/** Base Node. */
|
||||
protected Node base;
|
||||
protected final Node base;
|
||||
|
||||
private boolean function;
|
||||
private final boolean isFunction;
|
||||
|
||||
private final boolean hasCallSiteType;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -47,37 +54,28 @@ public abstract class BaseNode extends Node implements FunctionCall {
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param base base node
|
||||
* @param isFunction is this a function
|
||||
* @param hasCallSiteType does this access have a callsite type
|
||||
*/
|
||||
public BaseNode(final Source source, final long token, final int finish, final Node base) {
|
||||
super(source, token, finish);
|
||||
this.base = base;
|
||||
setStart(base.getStart());
|
||||
public BaseNode(final Source source, final long token, final int finish, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
super(source, token, base.getStart(), finish);
|
||||
this.base = base;
|
||||
this.isFunction = isFunction;
|
||||
this.hasCallSiteType = hasCallSiteType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param baseNode the base node
|
||||
* @param cs a copy state
|
||||
* Copy constructor for immutable nodes
|
||||
* @param baseNode node to inherit from
|
||||
* @param base base
|
||||
* @param isFunction is this a function
|
||||
* @param hasCallSiteType does this access have a callsite type
|
||||
*/
|
||||
protected BaseNode(final BaseNode baseNode, final CopyState cs) {
|
||||
protected BaseNode(final BaseNode baseNode, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
super(baseNode);
|
||||
this.base = cs.existingOrCopy(baseNode.getBase());
|
||||
setStart(base.getStart());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
final BaseNode baseNode = (BaseNode)other;
|
||||
return base.equals(baseNode.getBase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return base.hashCode();
|
||||
this.base = base;
|
||||
this.isFunction = isFunction;
|
||||
this.hasCallSiteType = hasCallSiteType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,25 +86,37 @@ public abstract class BaseNode extends Node implements FunctionCall {
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the base node for this access
|
||||
* @param base new base node
|
||||
*/
|
||||
public void setBase(final Node base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFunction() {
|
||||
return function;
|
||||
return isFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this node as being the callee operand of a {@link CallNode}.
|
||||
* @return a base node identical to this one in all aspects except with its function flag set.
|
||||
*/
|
||||
public BaseNode setIsFunction() {
|
||||
function = true;
|
||||
return this;
|
||||
public abstract BaseNode setIsFunction();
|
||||
|
||||
@Override
|
||||
public boolean canHaveCallSiteType() {
|
||||
return true; //carried by the symbol and always the same nodetype==symboltype
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the access have a call site type override?
|
||||
* @return true if overridden
|
||||
*/
|
||||
protected boolean hasCallSiteType() {
|
||||
return hasCallSiteType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug type change
|
||||
* @param type new type
|
||||
*/
|
||||
protected final void logTypeChange(final Type type) {
|
||||
if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
@ -33,9 +34,12 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
/**
|
||||
* BinaryNode nodes represent two operand operations.
|
||||
*/
|
||||
public class BinaryNode extends UnaryNode {
|
||||
@Immutable
|
||||
public final class BinaryNode extends Node implements Assignment<Node> {
|
||||
/** Left hand side argument. */
|
||||
private Node lhs;
|
||||
private final Node lhs;
|
||||
|
||||
private final Node rhs;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -46,28 +50,15 @@ public class BinaryNode extends UnaryNode {
|
||||
* @param rhs right hand side
|
||||
*/
|
||||
public BinaryNode(final Source source, final long token, final Node lhs, final Node rhs) {
|
||||
super(source, token, rhs);
|
||||
|
||||
start = lhs.getStart();
|
||||
finish = rhs.getFinish();
|
||||
super(source, token, lhs.getStart(), rhs.getFinish());
|
||||
this.lhs = lhs;
|
||||
this.rhs = rhs;
|
||||
}
|
||||
|
||||
private BinaryNode(final BinaryNode binaryNode, final Node lhs, final Node rhs) {
|
||||
super(binaryNode);
|
||||
this.lhs = lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param binaryNode the binary node
|
||||
* @param cs copy state
|
||||
*/
|
||||
protected BinaryNode(final BinaryNode binaryNode, final CopyState cs) {
|
||||
super(binaryNode, cs);
|
||||
lhs = cs.existingOrCopy(binaryNode.lhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new BinaryNode(this, cs);
|
||||
this.rhs = rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,28 +140,14 @@ public class BinaryNode extends UnaryNode {
|
||||
return rhs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return lhs.equals(((BinaryNode)other).lhs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ lhs().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterBinaryNode(this) != null) {
|
||||
// TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers
|
||||
return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor)));
|
||||
if (visitor.enterBinaryNode(this)) {
|
||||
return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -230,15 +207,36 @@ public class BinaryNode extends UnaryNode {
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the right hand side expression for this node
|
||||
* @return the left hand side expression
|
||||
*/
|
||||
public Node rhs() {
|
||||
return rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the left hand side expression for this node
|
||||
* @param lhs new left hand side expression
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public BinaryNode setLHS(final Node lhs) {
|
||||
if(this.lhs == lhs) return this;
|
||||
final BinaryNode n = (BinaryNode)clone();
|
||||
n.lhs = lhs;
|
||||
return n;
|
||||
if (this.lhs == lhs) {
|
||||
return this;
|
||||
}
|
||||
return new BinaryNode(this, lhs, rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the right hand side expression for this node
|
||||
* @param rhs new left hand side expression
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public BinaryNode setRHS(final Node rhs) {
|
||||
if (this.rhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
return new BinaryNode(this, lhs, rhs);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,14 +27,15 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import jdk.nashorn.internal.codegen.Frame;
|
||||
import java.util.Map;
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
@ -42,97 +43,79 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
* IR representation for a list of statements and functions. All provides the
|
||||
* basis for script body.
|
||||
*/
|
||||
public class Block extends Node {
|
||||
@Immutable
|
||||
public class Block extends BreakableNode implements Flags<Block> {
|
||||
/** List of statements */
|
||||
protected List<Node> statements;
|
||||
protected final List<Node> statements;
|
||||
|
||||
/** Symbol table. */
|
||||
protected final HashMap<String, Symbol> symbols;
|
||||
|
||||
/** Variable frame. */
|
||||
protected Frame frame;
|
||||
/** Symbol table - keys must be returned in the order they were put in. */
|
||||
protected final Map<String, Symbol> symbols;
|
||||
|
||||
/** Entry label. */
|
||||
protected final Label entryLabel;
|
||||
|
||||
/** Break label. */
|
||||
protected final Label breakLabel;
|
||||
|
||||
/** Does the block/function need a new scope? */
|
||||
protected boolean needsScope;
|
||||
protected final int flags;
|
||||
|
||||
/** Flag indicating that this block needs scope */
|
||||
public static final int NEEDS_SCOPE = 1 << 0;
|
||||
|
||||
/**
|
||||
* Flag indicating whether this block needs
|
||||
* self symbol assignment at the start. This is used only for
|
||||
* blocks that are the bodies of function nodes who refer to themselves
|
||||
* by name. It causes codegen to insert a var [fn_name] = __callee__
|
||||
* at the start of the body
|
||||
*/
|
||||
public static final int NEEDS_SELF_SYMBOL = 1 << 1;
|
||||
|
||||
/**
|
||||
* Is this block tagged as terminal based on its contents
|
||||
* (usually the last statement)
|
||||
*/
|
||||
public static final int IS_TERMINAL = 1 << 2;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param statements statements
|
||||
*/
|
||||
public Block(final Source source, final long token, final int finish) {
|
||||
super(source, token, finish);
|
||||
public Block(final Source source, final long token, final int finish, final Node... statements) {
|
||||
super(source, token, finish, new Label("block_break"));
|
||||
|
||||
this.statements = new ArrayList<>();
|
||||
this.symbols = new HashMap<>();
|
||||
this.statements = Arrays.asList(statements);
|
||||
this.symbols = new LinkedHashMap<>();
|
||||
this.entryLabel = new Label("block_entry");
|
||||
this.breakLabel = new Label("block_break");
|
||||
this.flags = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal copy constructor
|
||||
* Constructor
|
||||
*
|
||||
* @param block the source block
|
||||
* @param cs the copy state
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param statements statements
|
||||
*/
|
||||
protected Block(final Block block, final CopyState cs) {
|
||||
public Block(final Source source, final long token, final int finish, final List<Node> statements) {
|
||||
this(source, token, finish, statements.toArray(new Node[statements.size()]));
|
||||
}
|
||||
|
||||
private Block(final Block block, final int finish, final List<Node> statements, final int flags) {
|
||||
super(block);
|
||||
|
||||
this.statements = new ArrayList<>();
|
||||
for (final Node statement : block.getStatements()) {
|
||||
statements.add(cs.existingOrCopy(statement));
|
||||
}
|
||||
this.symbols = new HashMap<>();
|
||||
this.frame = block.frame == null ? null : block.frame.copy();
|
||||
this.statements = statements;
|
||||
this.flags = flags;
|
||||
this.symbols = block.symbols; //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now
|
||||
this.entryLabel = new Label(block.entryLabel);
|
||||
this.breakLabel = new Label(block.breakLabel);
|
||||
|
||||
assert block.symbols.isEmpty() : "must not clone with symbols";
|
||||
this.finish = finish;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new Block(this, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new statement to the statement list.
|
||||
*
|
||||
* @param statement Statement node to add.
|
||||
*/
|
||||
public void addStatement(final Node statement) {
|
||||
if (statement != null) {
|
||||
statements.add(statement);
|
||||
if (getFinish() < statement.getFinish()) {
|
||||
setFinish(statement.getFinish());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend statements to the statement list
|
||||
*
|
||||
* @param prepended statement to add
|
||||
*/
|
||||
public void prependStatements(final List<Node> prepended) {
|
||||
statements.addAll(0, prepended);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a list of statements to the statement list.
|
||||
*
|
||||
* @param statementList Statement nodes to add.
|
||||
*/
|
||||
public void addStatements(final List<Node> statementList) {
|
||||
statements.addAll(statementList);
|
||||
public Node ensureUniqueLabels(final LexicalContext lc) {
|
||||
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,19 +125,9 @@ public class Block extends Node {
|
||||
* @return new or same node
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
final Block saveBlock = visitor.getCurrentBlock();
|
||||
visitor.setCurrentBlock(this);
|
||||
|
||||
try {
|
||||
// Ignore parent to avoid recursion.
|
||||
|
||||
if (visitor.enterBlock(this) != null) {
|
||||
visitStatements(visitor);
|
||||
return visitor.leaveBlock(this);
|
||||
}
|
||||
} finally {
|
||||
visitor.setCurrentBlock(saveBlock);
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterBlock(this)) {
|
||||
return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Node.class, statements)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -222,11 +195,18 @@ public class Block extends Node {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the break label for this block
|
||||
* @return the break label
|
||||
* Tag block as terminal or non terminal
|
||||
* @param lc lexical context
|
||||
* @param isTerminal is block terminal
|
||||
* @return same block, or new if flag changed
|
||||
*/
|
||||
public Label getBreakLabel() {
|
||||
return breakLabel;
|
||||
public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
|
||||
return isTerminal ? setFlag(lc, IS_TERMINAL) : clearFlag(lc, IS_TERMINAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminal() {
|
||||
return getFlag(IS_TERMINAL);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,23 +217,6 @@ public class Block extends Node {
|
||||
return entryLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frame for this block
|
||||
* @return the frame
|
||||
*/
|
||||
public Frame getFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the frame for this block
|
||||
*
|
||||
* @param frame the new frame
|
||||
*/
|
||||
public void setFrame(final Frame frame) {
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of statements in this block
|
||||
*
|
||||
@ -263,22 +226,22 @@ public class Block extends Node {
|
||||
return Collections.unmodifiableList(statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified visitor to all statements in the block.
|
||||
* @param visitor the visitor.
|
||||
*/
|
||||
public void visitStatements(NodeVisitor visitor) {
|
||||
for (ListIterator<Node> stmts = statements.listIterator(); stmts.hasNext();) {
|
||||
stmts.set(stmts.next().accept(visitor));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Reset the statement list for this block
|
||||
*
|
||||
* @param statements new statement list
|
||||
* @param lc lexical context
|
||||
* @param statements new statement list
|
||||
* @return new block if statements changed, identity of statements == block.statements
|
||||
*/
|
||||
public void setStatements(final List<Node> statements) {
|
||||
this.statements = statements;
|
||||
public Block setStatements(final LexicalContext lc, final List<Node> statements) {
|
||||
if (this.statements == statements) {
|
||||
return this;
|
||||
}
|
||||
int lastFinish = 0;
|
||||
if (!statements.isEmpty()) {
|
||||
lastFinish = statements.get(statements.size() - 1).getFinish();
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new Block(this, Math.max(finish, lastFinish), statements, flags));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -297,39 +260,65 @@ public class Block extends Node {
|
||||
* @return true if this function needs a scope
|
||||
*/
|
||||
public boolean needsScope() {
|
||||
return needsScope;
|
||||
return (flags & NEEDS_SCOPE) == NEEDS_SCOPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block setFlags(final LexicalContext lc, int flags) {
|
||||
if (this.flags == flags) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block clearFlag(final LexicalContext lc, int flag) {
|
||||
return setFlags(lc, flags & ~flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block setFlag(final LexicalContext lc, int flag) {
|
||||
return setFlags(lc, flags | flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getFlag(final int flag) {
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the needs scope flag.
|
||||
* @param lc lexicalContext
|
||||
* @return new block if state changed, otherwise this
|
||||
*/
|
||||
public void setNeedsScope() {
|
||||
needsScope = true;
|
||||
public Block setNeedsScope(final LexicalContext lc) {
|
||||
if (needsScope()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags | NEEDS_SCOPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this block as using a specified scoped symbol. The block and its parent blocks up to but not
|
||||
* including the block defining the symbol will be marked as needing parent scope. The block defining the symbol
|
||||
* will be marked as one that needs to have its own scope.
|
||||
* @param symbol the symbol being used.
|
||||
* @param ancestors the iterator over block's containing lexical context
|
||||
* Computationally determine the next slot for this block,
|
||||
* indexed from 0. Use this as a relative base when computing
|
||||
* frames
|
||||
* @return next slot
|
||||
*/
|
||||
public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
|
||||
if(symbol.getBlock() == this) {
|
||||
setNeedsScope();
|
||||
} else {
|
||||
setUsesParentScopeSymbol(symbol, ancestors);
|
||||
public int nextSlot() {
|
||||
final Iterator<Symbol> iter = symbolIterator();
|
||||
int next = 0;
|
||||
while (iter.hasNext()) {
|
||||
final Symbol symbol = iter.next();
|
||||
if (symbol.hasSlot()) {
|
||||
next += symbol.slotCount();
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when this block uses a scope symbol defined in one of its ancestors.
|
||||
* @param symbol the scope symbol being used
|
||||
* @param ancestors iterator over ancestor blocks
|
||||
*/
|
||||
void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
|
||||
if(ancestors.hasNext()) {
|
||||
ancestors.next().setUsesScopeSymbol(symbol, ancestors);
|
||||
}
|
||||
@Override
|
||||
protected boolean isBreakableWithoutLabel() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
120
nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
Normal file
120
nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* This is a subclass of lexical context used for filling
|
||||
* blocks (and function nodes) with statements. When popping
|
||||
* a block from the lexical context, any statements that have
|
||||
* been generated in it are commited to the block. This saves
|
||||
* unnecessary object mutations and lexical context replacement
|
||||
*/
|
||||
public class BlockLexicalContext extends LexicalContext {
|
||||
/** statement stack, each block on the lexical context maintains one of these, which is
|
||||
* committed to the block on pop */
|
||||
private Deque<List<Node>> sstack = new ArrayDeque<>();
|
||||
|
||||
/** Last non debug statement emitted in this context */
|
||||
protected Node lastStatement;
|
||||
|
||||
@Override
|
||||
public <T extends LexicalContextNode> T push(final T node) {
|
||||
T pushed = super.push(node);
|
||||
if (node instanceof Block) {
|
||||
sstack.push(new ArrayList<Node>());
|
||||
}
|
||||
return pushed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the statement list from the stack, possibly filtered
|
||||
* @return statement list
|
||||
*/
|
||||
protected List<Node> popStatements() {
|
||||
return sstack.pop();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends LexicalContextNode> T pop(final T node) {
|
||||
T expected = node;
|
||||
if (node instanceof Block) {
|
||||
final List<Node> newStatements = popStatements();
|
||||
expected = (T)((Block)node).setStatements(this, newStatements);
|
||||
if (!sstack.isEmpty()) {
|
||||
lastStatement = lastStatement(sstack.peek());
|
||||
}
|
||||
}
|
||||
return super.pop(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a statement to the block being generated
|
||||
* @param statement statement to add
|
||||
*/
|
||||
public void appendStatement(final Node statement) {
|
||||
assert statement != null;
|
||||
sstack.peek().add(statement);
|
||||
if (!statement.isDebug()) {
|
||||
lastStatement = statement;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a statement to the block being generated
|
||||
* @param statement statement to prepend
|
||||
* @return the prepended statement
|
||||
*/
|
||||
public Node prependStatement(final Node statement) {
|
||||
assert statement != null;
|
||||
sstack.peek().add(0, statement);
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last (non debug) statement that was emitted into a block
|
||||
* @return the last statement emitted
|
||||
*/
|
||||
public Node getLastStatement() {
|
||||
return lastStatement;
|
||||
}
|
||||
|
||||
private static Node lastStatement(final List<Node> statements) {
|
||||
for (final ListIterator<Node> iter = statements.listIterator(statements.size()); iter.hasPrevious(); ) {
|
||||
final Node node = iter.previous();
|
||||
if (!node.isDebug()) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -25,37 +25,34 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for {@code break} statements.
|
||||
*/
|
||||
public class BreakNode extends LabeledNode {
|
||||
@Immutable
|
||||
public final class BreakNode extends Node {
|
||||
|
||||
/**
|
||||
private final IdentNode label;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param labelNode break label
|
||||
* @param targetNode node to break to
|
||||
* @param tryChain surrounding try chain
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param label label for break or null if none
|
||||
*/
|
||||
public BreakNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
|
||||
super(source, token, finish, labelNode, targetNode, tryChain);
|
||||
setHasGoto();
|
||||
}
|
||||
|
||||
private BreakNode(final BreakNode breakNode, final CopyState cs) {
|
||||
super(breakNode, cs);
|
||||
public BreakNode(final Source source, final long token, final int finish, final IdentNode label) {
|
||||
super(source, token, finish);
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new BreakNode(this, cs);
|
||||
public boolean hasGoto() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,7 +61,7 @@ public class BreakNode extends LabeledNode {
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterBreakNode(this) != null) {
|
||||
if (visitor.enterBreakNode(this)) {
|
||||
return visitor.leaveBreakNode(this);
|
||||
}
|
||||
|
||||
@ -72,26 +69,20 @@ public class BreakNode extends LabeledNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target label of this break node.
|
||||
* @return the target label.
|
||||
* Get the label for this break node
|
||||
* @return label, or null if none
|
||||
*/
|
||||
public Label getTargetLabel() {
|
||||
if (targetNode instanceof BreakableNode) {
|
||||
return ((BreakableNode)targetNode).getBreakLabel();
|
||||
} else if (targetNode instanceof Block) {
|
||||
return ((Block)targetNode).getBreakLabel();
|
||||
}
|
||||
|
||||
throw new AssertionError("Invalid break target " + targetNode.getClass());
|
||||
public IdentNode getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append("break");
|
||||
|
||||
if (labelNode != null) {
|
||||
if (label != null) {
|
||||
sb.append(' ');
|
||||
labelNode.getLabel().toString(sb);
|
||||
label.toString(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,27 +25,34 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* This class represents a node from which control flow can execute
|
||||
* a {@code break} statement
|
||||
*/
|
||||
public abstract class BreakableNode extends Node {
|
||||
@Immutable
|
||||
public abstract class BreakableNode extends LexicalContextNode {
|
||||
|
||||
/** break label. */
|
||||
protected Label breakLabel;
|
||||
protected final Label breakLabel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param breakLabel break label
|
||||
*/
|
||||
public BreakableNode(final Source source, final long token, final int finish) {
|
||||
protected BreakableNode(final Source source, final long token, final int finish, final Label breakLabel) {
|
||||
super(source, token, finish);
|
||||
this.breakLabel = breakLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,6 +62,19 @@ public abstract class BreakableNode extends Node {
|
||||
*/
|
||||
protected BreakableNode(final BreakableNode breakableNode) {
|
||||
super(breakableNode);
|
||||
this.breakLabel = new Label(breakableNode.getBreakLabel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Node ensureUniqueLabels(final LexicalContext lc);
|
||||
|
||||
/**
|
||||
* Check whether this can be broken out from without using a label,
|
||||
* e.g. everything but Blocks, basically
|
||||
* @return true if breakable without label
|
||||
*/
|
||||
protected boolean isBreakableWithoutLabel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,4 +85,14 @@ public abstract class BreakableNode extends Node {
|
||||
return breakLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the labels associated with this node. Breakable nodes that
|
||||
* aren't LoopNodes only have a break label -> the location immediately
|
||||
* afterwards the node in code
|
||||
* @return list of labels representing locations around this node
|
||||
*/
|
||||
public List<Label> getLabels() {
|
||||
return Arrays.asList(breakLabel);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,49 +25,48 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for a function call.
|
||||
*
|
||||
*/
|
||||
public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
@Immutable
|
||||
public final class CallNode extends LexicalContextNode implements TypeOverride<CallNode> {
|
||||
|
||||
private Type type;
|
||||
private final Type type;
|
||||
|
||||
/** Function identifier or function body. */
|
||||
private Node function;
|
||||
private final Node function;
|
||||
|
||||
/** Call arguments. */
|
||||
private List<Node> args;
|
||||
private final List<Node> args;
|
||||
|
||||
/** flag - is new expression */
|
||||
private boolean isNew;
|
||||
/** Is this a "new" operation */
|
||||
public static final int IS_NEW = 0x1;
|
||||
|
||||
/** flag - is in with block */
|
||||
private boolean inWithBlock;
|
||||
private final int flags;
|
||||
|
||||
/**
|
||||
* Arguments to be passed to builtin {@code eval} function
|
||||
*/
|
||||
public static class EvalArgs {
|
||||
/** evaluated code */
|
||||
private Node code;
|
||||
private final Node code;
|
||||
|
||||
/** 'this' passed to evaluated code */
|
||||
private IdentNode evalThis;
|
||||
private final IdentNode evalThis;
|
||||
|
||||
/** location string for the eval call */
|
||||
final private String location;
|
||||
private final String location;
|
||||
|
||||
/** is this call from a strict context? */
|
||||
final private boolean strictMode;
|
||||
private final boolean strictMode;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -92,12 +91,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the code that is to be eval.ed by this eval function
|
||||
* @param code the code as an AST node
|
||||
*/
|
||||
public void setCode(final Node code) {
|
||||
this.code = code;
|
||||
private EvalArgs setCode(final Node code) {
|
||||
if (this.code == code) {
|
||||
return this;
|
||||
}
|
||||
return new EvalArgs(code, evalThis, location, strictMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,12 +106,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
return this.evalThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code this} symbol used to invoke this eval call
|
||||
* @param evalThis the {@code this} symbol
|
||||
*/
|
||||
public void setThis(final IdentNode evalThis) {
|
||||
this.evalThis = evalThis;
|
||||
private EvalArgs setThis(final IdentNode evalThis) {
|
||||
if (this.evalThis == evalThis) {
|
||||
return this;
|
||||
}
|
||||
return new EvalArgs(code, evalThis, location, strictMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,7 +132,7 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
|
||||
/** arguments for 'eval' call. Non-null only if this call node is 'eval' */
|
||||
@Ignore
|
||||
private EvalArgs evalArgs;
|
||||
private final EvalArgs evalArgs;
|
||||
|
||||
/**
|
||||
* Constructors
|
||||
@ -149,28 +146,22 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args) {
|
||||
super(source, token, finish);
|
||||
|
||||
setStart(function.getStart());
|
||||
|
||||
this.function = function;
|
||||
this.args = args;
|
||||
this.function = function;
|
||||
this.args = args;
|
||||
this.flags = 0;
|
||||
this.type = null;
|
||||
this.evalArgs = null;
|
||||
}
|
||||
|
||||
private CallNode(final CallNode callNode, final CopyState cs) {
|
||||
private CallNode(final CallNode callNode, final Node function, final List<Node> args, final int flags, final Type type, final EvalArgs evalArgs) {
|
||||
super(callNode);
|
||||
|
||||
final List<Node> newArgs = new ArrayList<>();
|
||||
|
||||
for (final Node arg : callNode.args) {
|
||||
newArgs.add(cs.existingOrCopy(arg));
|
||||
}
|
||||
|
||||
this.function = cs.existingOrCopy(callNode.function); //TODO existing or same?
|
||||
this.args = newArgs;
|
||||
this.isNew = callNode.isNew;
|
||||
this.inWithBlock = callNode.inWithBlock;
|
||||
this.function = function;
|
||||
this.args = args;
|
||||
this.flags = flags;
|
||||
this.type = type;
|
||||
this.evalArgs = evalArgs;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
if (hasCallSiteType()) {
|
||||
@ -181,8 +172,10 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
|
||||
@Override
|
||||
public CallNode setType(final Type type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
if (this.type == type) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
}
|
||||
|
||||
private boolean hasCallSiteType() {
|
||||
@ -194,11 +187,6 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new CallNode(this, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
*
|
||||
@ -207,15 +195,22 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
* @return node or replacement
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterCallNode(this) != null) {
|
||||
function = function.accept(visitor);
|
||||
|
||||
for (int i = 0, count = args.size(); i < count; i++) {
|
||||
args.set(i, args.get(i).accept(visitor));
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterCallNode(this)) {
|
||||
final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
|
||||
setFunction(function.accept(visitor)).
|
||||
setArgs(Node.accept(visitor, Node.class, args)).
|
||||
setFlags(flags).
|
||||
setType(type).
|
||||
setEvalArgs(evalArgs == null ?
|
||||
null :
|
||||
evalArgs.setCode(evalArgs.getCode().accept(visitor)).
|
||||
setThis((IdentNode)evalArgs.getThis().accept(visitor))));
|
||||
// Theoretically, we'd need to instead pass lc to every setter and do a replacement on each. In practice,
|
||||
// setType from TypeOverride can't accept a lc, and we don't necessarily want to go there now.
|
||||
if(this != newCallNode) {
|
||||
return Node.replaceInLexicalContext(lc, this, newCallNode);
|
||||
}
|
||||
|
||||
return visitor.leaveCallNode(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -226,7 +221,7 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
if (hasCallSiteType()) {
|
||||
sb.append('{');
|
||||
final String desc = getType().getDescriptor();
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
|
||||
sb.append('}');
|
||||
}
|
||||
|
||||
@ -261,8 +256,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
* Reset the arguments for the call
|
||||
* @param args new arguments list
|
||||
*/
|
||||
public void setArgs(final List<Node> args) {
|
||||
this.args = args;
|
||||
private CallNode setArgs(final List<Node> args) {
|
||||
if (this.args == args) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -278,9 +276,13 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
* {@code eval}
|
||||
*
|
||||
* @param evalArgs eval args
|
||||
* @return same node or new one on state change
|
||||
*/
|
||||
public void setEvalArgs(final EvalArgs evalArgs) {
|
||||
this.evalArgs = evalArgs;
|
||||
public CallNode setEvalArgs(final EvalArgs evalArgs) {
|
||||
if (this.evalArgs == evalArgs) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,10 +303,14 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
|
||||
/**
|
||||
* Reset the function expression that this call invokes
|
||||
* @param node the function
|
||||
* @param function the function
|
||||
* @return same node or new one on state change
|
||||
*/
|
||||
public void setFunction(final Node node) {
|
||||
function = node;
|
||||
public CallNode setFunction(final Node function) {
|
||||
if (this.function == function) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -312,28 +318,21 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
|
||||
* @return true if this a new operation
|
||||
*/
|
||||
public boolean isNew() {
|
||||
return isNew;
|
||||
return (flags & IS_NEW) == IS_NEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag this call as a new operation
|
||||
* @return same node or new one on state change
|
||||
*/
|
||||
public void setIsNew() {
|
||||
this.isNew = true;
|
||||
public CallNode setIsNew() {
|
||||
return setFlags(IS_NEW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this call is inside a {@code with} block
|
||||
* @return true if the call is inside a {@code with} block
|
||||
*/
|
||||
public boolean inWithBlock() {
|
||||
return inWithBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag this call to be inside a {@code with} block
|
||||
*/
|
||||
public void setInWithBlock() {
|
||||
this.inWithBlock = true;
|
||||
private CallNode setFlags(final int flags) {
|
||||
if (this.flags == flags) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
}
|
||||
}
|
||||
|
@ -26,19 +26,21 @@
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of CASE clause.
|
||||
*
|
||||
* Case nodes are not BreakableNodes, but the SwitchNode is
|
||||
*/
|
||||
public class CaseNode extends BreakableNode {
|
||||
@Immutable
|
||||
public final class CaseNode extends Node {
|
||||
/** Test expression. */
|
||||
private Node test;
|
||||
private final Node test;
|
||||
|
||||
/** Statements. */
|
||||
private Block body;
|
||||
private final Block body;
|
||||
|
||||
/** Case entry label. */
|
||||
private final Label entry;
|
||||
@ -60,17 +62,17 @@ public class CaseNode extends BreakableNode {
|
||||
this.entry = new Label("entry");
|
||||
}
|
||||
|
||||
private CaseNode(final CaseNode caseNode, final CopyState cs) {
|
||||
CaseNode(final CaseNode caseNode, final Node test, final Block body) {
|
||||
super(caseNode);
|
||||
|
||||
this.test = cs.existingOrCopy(caseNode.test);
|
||||
this.body = (Block)cs.existingOrCopy(caseNode.body);
|
||||
this.test = test;
|
||||
this.body = body;
|
||||
this.entry = new Label(caseNode.entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new CaseNode(this, cs);
|
||||
public boolean isTerminal() {
|
||||
return body.isTerminal();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,15 +81,11 @@ public class CaseNode extends BreakableNode {
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterCaseNode(this) != null) {
|
||||
if (test != null) {
|
||||
test = test.accept(visitor);
|
||||
}
|
||||
if (body != null) {
|
||||
body = (Block)body.accept(visitor);
|
||||
}
|
||||
if (visitor.enterCaseNode(this)) {
|
||||
final Node newTest = test == null ? null : test.accept(visitor);
|
||||
final Block newBody = body == null ? null : (Block)body.accept(visitor);
|
||||
|
||||
return visitor.leaveCaseNode(this);
|
||||
return visitor.leaveCaseNode(setTest(newTest).setBody(newBody));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -131,8 +129,19 @@ public class CaseNode extends BreakableNode {
|
||||
/**
|
||||
* Reset the test expression for this case node
|
||||
* @param test new test expression
|
||||
* @return new or same CaseNode
|
||||
*/
|
||||
public void setTest(final Node test) {
|
||||
this.test = test;
|
||||
public CaseNode setTest(final Node test) {
|
||||
if (this.test == test) {
|
||||
return this;
|
||||
}
|
||||
return new CaseNode(this, test, body);
|
||||
}
|
||||
|
||||
private CaseNode setBody(final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return new CaseNode(this, test, body);
|
||||
}
|
||||
}
|
||||
|
@ -25,26 +25,23 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of a catch clause.
|
||||
*
|
||||
*/
|
||||
public class CatchNode extends Node {
|
||||
@Immutable
|
||||
public final class CatchNode extends Node {
|
||||
/** Exception identifier. */
|
||||
private IdentNode exception;
|
||||
private final IdentNode exception;
|
||||
|
||||
/** Exception condition. */
|
||||
private Node exceptionCondition;
|
||||
private final Node exceptionCondition;
|
||||
|
||||
/** Catch body. */
|
||||
private Block body;
|
||||
|
||||
/** Is rethrow - e.g. synthetic catch block for e.g. finallies, the parser case where
|
||||
* there has to be at least on catch for syntactic validity */
|
||||
private boolean isSyntheticRethrow;
|
||||
private final Block body;
|
||||
|
||||
/**
|
||||
* Constructors
|
||||
@ -64,18 +61,12 @@ public class CatchNode extends Node {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
private CatchNode(final CatchNode catchNode, final CopyState cs) {
|
||||
private CatchNode(final CatchNode catchNode, final IdentNode exception, final Node exceptionCondition, final Block body) {
|
||||
super(catchNode);
|
||||
|
||||
this.exception = (IdentNode)cs.existingOrCopy(catchNode.exception);
|
||||
this.exceptionCondition = cs.existingOrCopy(catchNode.exceptionCondition);
|
||||
this.body = (Block)cs.existingOrCopy(catchNode.body);
|
||||
this.isSyntheticRethrow = catchNode.isSyntheticRethrow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new CatchNode(this, cs);
|
||||
this.exception = exception;
|
||||
this.exceptionCondition = exceptionCondition;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,20 +75,21 @@ public class CatchNode extends Node {
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterCatchNode(this) != null) {
|
||||
exception = (IdentNode)exception.accept(visitor);
|
||||
|
||||
if (exceptionCondition != null) {
|
||||
exceptionCondition = exceptionCondition.accept(visitor);
|
||||
}
|
||||
|
||||
body = (Block)body.accept(visitor);
|
||||
return visitor.leaveCatchNode(this);
|
||||
if (visitor.enterCatchNode(this)) {
|
||||
return visitor.leaveCatchNode(
|
||||
setException((IdentNode)exception.accept(visitor)).
|
||||
setExceptionCondition(exceptionCondition == null ? null : exceptionCondition.accept(visitor)).
|
||||
setBody((Block)body.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminal() {
|
||||
return body.isTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append(" catch (");
|
||||
@ -110,23 +102,6 @@ public class CatchNode extends Node {
|
||||
sb.append(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this catch is a synthetic rethrow
|
||||
* @return true if this is a synthetic rethrow
|
||||
*/
|
||||
public boolean isSyntheticRethrow() {
|
||||
return isSyntheticRethrow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag this as deliberatly generated catch all that rethrows the
|
||||
* caught exception. This is used for example for generating finally
|
||||
* expressions
|
||||
*/
|
||||
public void setIsSyntheticRethrow() {
|
||||
this.isSyntheticRethrow = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier representing the exception thrown
|
||||
* @return the exception identifier
|
||||
@ -146,9 +121,13 @@ public class CatchNode extends Node {
|
||||
/**
|
||||
* Reset the exception condition for this catch block
|
||||
* @param exceptionCondition the new exception condition
|
||||
* @return new or same CatchNode
|
||||
*/
|
||||
public void setExceptionCondition(final Node exceptionCondition) {
|
||||
this.exceptionCondition = exceptionCondition;
|
||||
public CatchNode setExceptionCondition(final Node exceptionCondition) {
|
||||
if (this.exceptionCondition == exceptionCondition) {
|
||||
return this;
|
||||
}
|
||||
return new CatchNode(this, exception, exceptionCondition, body);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,4 +137,18 @@ public class CatchNode extends Node {
|
||||
public Block getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
private CatchNode setException(final IdentNode exception) {
|
||||
if (this.exception == exception) {
|
||||
return this;
|
||||
}
|
||||
return new CatchNode(this, exception, exceptionCondition, body);
|
||||
}
|
||||
|
||||
private CatchNode setBody(final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return new CatchNode(this, exception, exceptionCondition, body);
|
||||
}
|
||||
}
|
||||
|
@ -25,43 +25,39 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for CONTINUE statements.
|
||||
*
|
||||
*/
|
||||
public class ContinueNode extends LabeledNode {
|
||||
@Immutable
|
||||
public class ContinueNode extends Node {
|
||||
|
||||
private IdentNode label;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param labelNode the continue label
|
||||
* @param targetNode node to continue to
|
||||
* @param tryChain surrounding try chain
|
||||
* @param source source code
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param label label for break or null if none
|
||||
*/
|
||||
public ContinueNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
|
||||
super(source, token, finish, labelNode, targetNode, tryChain);
|
||||
setHasGoto();
|
||||
}
|
||||
|
||||
private ContinueNode(final ContinueNode continueNode, final CopyState cs) {
|
||||
super(continueNode, cs);
|
||||
public ContinueNode(final Source source, final long token, final int finish, final IdentNode label) {
|
||||
super(source, token, finish);
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new ContinueNode(this, cs);
|
||||
public boolean hasGoto() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterContinueNode(this) != null) {
|
||||
if (visitor.enterContinueNode(this)) {
|
||||
return visitor.leaveContinueNode(this);
|
||||
}
|
||||
|
||||
@ -69,21 +65,20 @@ public class ContinueNode extends LabeledNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the target label of this continue node.
|
||||
* @return the target label.
|
||||
* Get the label for this break node
|
||||
* @return label, or null if none
|
||||
*/
|
||||
public Label getTargetLabel() {
|
||||
assert targetNode instanceof WhileNode : "continue target must be a while node";
|
||||
return ((WhileNode)targetNode).getContinueLabel();
|
||||
public IdentNode getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append("continue");
|
||||
|
||||
if (labelNode != null) {
|
||||
if (label != null) {
|
||||
sb.append(' ');
|
||||
labelNode.getLabel().toString(sb);
|
||||
label.toString(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,15 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for an empty statement.
|
||||
*
|
||||
*/
|
||||
public class EmptyNode extends Node {
|
||||
@Immutable
|
||||
public final class EmptyNode extends Node {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -57,7 +58,7 @@ public class EmptyNode extends Node {
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterEmptyNode(this) != null) {
|
||||
if (visitor.enterEmptyNode(this)) {
|
||||
return visitor.leaveEmptyNode(this);
|
||||
}
|
||||
return this;
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
@ -33,9 +34,10 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
* node means "this code will be executed" and evaluating it results in
|
||||
* statements being added to the IR
|
||||
*/
|
||||
public class ExecuteNode extends Node {
|
||||
@Immutable
|
||||
public final class ExecuteNode extends Node {
|
||||
/** Expression to execute. */
|
||||
private Node expression;
|
||||
private final Node expression;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -50,6 +52,11 @@ public class ExecuteNode extends Node {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
private ExecuteNode(final ExecuteNode executeNode, final Node expression) {
|
||||
super(executeNode);
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -60,34 +67,15 @@ public class ExecuteNode extends Node {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) {
|
||||
super(executeNode);
|
||||
this.expression = cs.existingOrCopy(executeNode.expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new ExecuteNode(this, cs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return expression.equals(((ExecuteNode)other).getExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ expression.hashCode();
|
||||
public boolean isTerminal() {
|
||||
return expression.isTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterExecuteNode(this) != null) {
|
||||
setExpression(expression.accept(visitor));
|
||||
return visitor.leaveExecuteNode(this);
|
||||
if (visitor.enterExecuteNode(this)) {
|
||||
return visitor.leaveExecuteNode(setExpression(expression.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -109,8 +97,12 @@ public class ExecuteNode extends Node {
|
||||
/**
|
||||
* Reset the expression to be executed
|
||||
* @param expression the expression
|
||||
* @return new or same execute node
|
||||
*/
|
||||
public void setExpression(final Node expression) {
|
||||
this.expression = expression;
|
||||
public ExecuteNode setExpression(final Node expression) {
|
||||
if (this.expression == expression) {
|
||||
return this;
|
||||
}
|
||||
return new ExecuteNode(this, expression);
|
||||
}
|
||||
}
|
||||
|
69
nashorn/src/jdk/nashorn/internal/ir/Flags.java
Normal file
69
nashorn/src/jdk/nashorn/internal/ir/Flags.java
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
/**
|
||||
* Interface implemented by all nodes that have flags in
|
||||
* a lexical context. This is needed as we sometimes have to save
|
||||
* the setting of flags in the lexical context until a block
|
||||
* is completely finished and its final version (after multiple
|
||||
* copy on writes) is placed in the lexical context
|
||||
*
|
||||
* @param <T> lexical context node that can have flags set during code generation
|
||||
*/
|
||||
public interface Flags<T extends LexicalContextNode> {
|
||||
|
||||
/**
|
||||
* Check if a flag is set in a lexical context node
|
||||
* @param flag flag to check
|
||||
* @return flags
|
||||
*/
|
||||
public boolean getFlag(int flag);
|
||||
|
||||
/**
|
||||
* Clear a flag of a LexicalContextNode
|
||||
* @param lc lexical context
|
||||
* @param flag flag to clear
|
||||
* @return the new LexicalContext node if flags were changed, same otherwise
|
||||
*/
|
||||
public T clearFlag(final LexicalContext lc, int flag);
|
||||
|
||||
/**
|
||||
* Set a flag of a LexicalContextNode
|
||||
* @param lc lexical context
|
||||
* @param flag flag to set
|
||||
* @return the new LexicalContext node if flags were changed, same otherwise
|
||||
*/
|
||||
public T setFlag(final LexicalContext lc, int flag);
|
||||
|
||||
/**
|
||||
* Set all flags of a LexicalContextNode, overwriting previous flags
|
||||
* @param lc lexical context
|
||||
* @param flags new flags value
|
||||
* @return the new LexicalContext node if flags were changed, same otherwise
|
||||
*/
|
||||
public T setFlags(final LexicalContext lc, int flags);
|
||||
}
|
@ -25,73 +25,75 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representing a FOR statement.
|
||||
*
|
||||
*/
|
||||
public class ForNode extends WhileNode {
|
||||
@Immutable
|
||||
public final class ForNode extends LoopNode {
|
||||
/** Initialize expression. */
|
||||
private Node init;
|
||||
private final Node init;
|
||||
|
||||
/** Test expression. */
|
||||
private Node modify;
|
||||
private final Node modify;
|
||||
|
||||
/** Iterator symbol. */
|
||||
private Symbol iterator;
|
||||
|
||||
/** is for in */
|
||||
private boolean isForIn;
|
||||
/** Is this a normal for loop? */
|
||||
public static final int IS_FOR = 1 << 0;
|
||||
|
||||
/** is for each */
|
||||
private boolean isForEach;
|
||||
/** Is this a normal for in loop? */
|
||||
public static final int IS_FOR_IN = 1 << 1;
|
||||
|
||||
/** Is this a normal for each in loop? */
|
||||
public static final int IS_FOR_EACH = 1 << 2;
|
||||
|
||||
private final int flags;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param init init
|
||||
* @param test test
|
||||
* @param body body
|
||||
* @param modify modify
|
||||
* @param flags flags
|
||||
*/
|
||||
public ForNode(final Source source, final long token, final int finish) {
|
||||
super(source, token, finish);
|
||||
public ForNode(final Source source, final long token, final int finish, final Node init, final Node test, final Block body, final Node modify, final int flags) {
|
||||
super(source, token, finish, test, body, false);
|
||||
this.init = init;
|
||||
this.modify = modify;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
private ForNode(final ForNode forNode, final CopyState cs) {
|
||||
super(forNode, cs);
|
||||
|
||||
this.init = cs.existingOrCopy(forNode.init);
|
||||
this.modify = cs.existingOrCopy(forNode.modify);
|
||||
this.iterator = forNode.iterator;
|
||||
this.isForIn = forNode.isForIn;
|
||||
this.isForEach = forNode.isForEach;
|
||||
private ForNode(final ForNode forNode, final Node init, final Node test, final Block body, final Node modify, final int flags, final boolean controlFlowEscapes) {
|
||||
super(forNode, test, body, controlFlowEscapes);
|
||||
this.init = init;
|
||||
this.modify = modify;
|
||||
this.flags = flags;
|
||||
this.iterator = forNode.iterator; //TODO is this acceptable? symbols are never cloned, just copied as references
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new ForNode(this, cs);
|
||||
public Node ensureUniqueLabels(LexicalContext lc) {
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterForNode(this) != null) {
|
||||
if (init != null) {
|
||||
init = init.accept(visitor);
|
||||
}
|
||||
|
||||
if (test != null) {
|
||||
test = test.accept(visitor);
|
||||
}
|
||||
|
||||
if (modify != null) {
|
||||
modify = modify.accept(visitor);
|
||||
}
|
||||
|
||||
body = (Block)body.accept(visitor);
|
||||
|
||||
return visitor.leaveForNode(this);
|
||||
protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterForNode(this)) {
|
||||
return visitor.leaveForNode(
|
||||
setInit(lc, init == null ? null : init.accept(visitor)).
|
||||
setTest(lc, test == null ? null : test.accept(visitor)).
|
||||
setModify(lc, modify == null ? null : modify.accept(visitor)).
|
||||
setBody(lc, (Block)body.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -122,6 +124,19 @@ public class ForNode extends WhileNode {
|
||||
sb.append(')');
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGoto() {
|
||||
return !isForIn() && test == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mustEnter() {
|
||||
if (isForIn()) {
|
||||
return false; //may be an empty set to iterate over, then we skip the loop
|
||||
}
|
||||
return test == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initialization expression for this for loop
|
||||
* @return the initialization expression
|
||||
@ -132,10 +147,15 @@ public class ForNode extends WhileNode {
|
||||
|
||||
/**
|
||||
* Reset the initialization expression for this for loop
|
||||
* @param lc lexical context
|
||||
* @param init new initialization expression
|
||||
* @return new for node if changed or existing if not
|
||||
*/
|
||||
public void setInit(final Node init) {
|
||||
this.init = init;
|
||||
public ForNode setInit(final LexicalContext lc, final Node init) {
|
||||
if (this.init == init) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,14 +163,16 @@ public class ForNode extends WhileNode {
|
||||
* @return true if this is a for in constructor
|
||||
*/
|
||||
public boolean isForIn() {
|
||||
return isForIn;
|
||||
return (flags & IS_FOR_IN) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag this to be a for in construct
|
||||
* @param lc lexical context
|
||||
* @return new for node if changed or existing if not
|
||||
*/
|
||||
public void setIsForIn() {
|
||||
this.isForIn = true;
|
||||
public ForNode setIsForIn(final LexicalContext lc) {
|
||||
return setFlags(lc, flags | IS_FOR_IN);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,14 +181,16 @@ public class ForNode extends WhileNode {
|
||||
* @return true if this is a for each construct
|
||||
*/
|
||||
public boolean isForEach() {
|
||||
return isForEach;
|
||||
return (flags & IS_FOR_EACH) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag this to be a for each construct
|
||||
* @param lc lexical context
|
||||
* @return new for node if changed or existing if not
|
||||
*/
|
||||
public void setIsForEach() {
|
||||
this.isForEach = true;
|
||||
public ForNode setIsForEach(final LexicalContext lc) {
|
||||
return setFlags(lc, flags | IS_FOR_EACH);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,10 +219,15 @@ public class ForNode extends WhileNode {
|
||||
|
||||
/**
|
||||
* Reset the modification expression for this ForNode
|
||||
* @param lc lexical context
|
||||
* @param modify new modification expression
|
||||
* @return new for node if changed or existing if not
|
||||
*/
|
||||
public void setModify(final Node modify) {
|
||||
this.modify = modify;
|
||||
public ForNode setModify(final LexicalContext lc, final Node modify) {
|
||||
if (this.modify == modify) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -207,7 +236,39 @@ public class ForNode extends WhileNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTest(final Node test) {
|
||||
this.test = test;
|
||||
public ForNode setTest(final LexicalContext lc, final Node test) {
|
||||
if (this.test == test) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForNode setBody(final LexicalContext lc, final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
|
||||
if (this.controlFlowEscapes == controlFlowEscapes) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
|
||||
}
|
||||
|
||||
private ForNode setFlags(final LexicalContext lc, final int flags) {
|
||||
if (this.flags == flags) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,13 +32,15 @@ import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for an identifier.
|
||||
*/
|
||||
public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
|
||||
@Immutable
|
||||
public final class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
|
||||
private static final int PROPERTY_NAME = 1 << 0;
|
||||
private static final int INITIALIZED_HERE = 1 << 1;
|
||||
private static final int FUNCTION = 1 << 2;
|
||||
@ -47,9 +49,9 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
private final String name;
|
||||
|
||||
/** Type for a callsite, e.g. X in a get()X or a set(X)V */
|
||||
private Type callSiteType;
|
||||
private final Type callSiteType;
|
||||
|
||||
private byte flags;
|
||||
private final int flags;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -62,6 +64,15 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
public IdentNode(final Source source, final long token, final int finish, final String name) {
|
||||
super(source, token, finish);
|
||||
this.name = name;
|
||||
this.callSiteType = null;
|
||||
this.flags = 0;
|
||||
}
|
||||
|
||||
private IdentNode(final IdentNode identNode, final String name, final Type callSiteType, final int flags) {
|
||||
super(identNode);
|
||||
this.name = name;
|
||||
this.callSiteType = callSiteType;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,8 +82,9 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
*/
|
||||
public IdentNode(final IdentNode identNode) {
|
||||
super(identNode);
|
||||
this.name = identNode.getName();
|
||||
this.flags = identNode.flags;
|
||||
this.name = identNode.getName();
|
||||
this.callSiteType = null;
|
||||
this.flags = identNode.flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -92,40 +104,15 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
|
||||
@Override
|
||||
public IdentNode setType(final Type type) {
|
||||
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
|
||||
}
|
||||
// do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
|
||||
if(this.callSiteType == type) {
|
||||
if (this.callSiteType == type) {
|
||||
return this;
|
||||
}
|
||||
final IdentNode n = (IdentNode)clone();
|
||||
n.callSiteType = type;
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new IdentNode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if two IdentNode are the same.
|
||||
*
|
||||
* @param other Other ident.
|
||||
* @return true if the idents are the same.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other instanceof IdentNode) {
|
||||
return name.equals(((IdentNode)other).name);
|
||||
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
return new IdentNode(this, name, type, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,7 +122,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterIdentNode(this) != null) {
|
||||
if (visitor.enterIdentNode(this)) {
|
||||
return visitor.leaveIdentNode(this);
|
||||
}
|
||||
|
||||
@ -147,7 +134,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
if (hasCallSiteType()) {
|
||||
sb.append('{');
|
||||
final String desc = getType().getDescriptor();
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
|
||||
sb.append('}');
|
||||
}
|
||||
|
||||
@ -191,10 +178,10 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public IdentNode setIsPropertyName() {
|
||||
if(isPropertyName()) return this;
|
||||
final IdentNode n = (IdentNode)clone();
|
||||
n.flags |= PROPERTY_NAME;
|
||||
return n;
|
||||
if (isPropertyName()) {
|
||||
return this;
|
||||
}
|
||||
return new IdentNode(this, name, callSiteType, flags | PROPERTY_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,10 +197,10 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public IdentNode setIsInitializedHere() {
|
||||
if(isInitializedHere()) return this;
|
||||
final IdentNode n = (IdentNode)clone();
|
||||
n.flags |= INITIALIZED_HERE;
|
||||
return n;
|
||||
if (isInitializedHere()) {
|
||||
return this;
|
||||
}
|
||||
return new IdentNode(this, name, callSiteType, flags | INITIALIZED_HERE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -223,7 +210,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
* @return true if this IdentNode is special
|
||||
*/
|
||||
public boolean isSpecialIdentity() {
|
||||
return name.equals(__DIR__.tag()) || name.equals(__FILE__.tag()) || name.equals(__LINE__.tag());
|
||||
return name.equals(__DIR__.symbolName()) || name.equals(__FILE__.symbolName()) || name.equals(__LINE__.symbolName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -236,9 +223,9 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
|
||||
* @return an ident node identical to this one in all aspects except with its function flag set.
|
||||
*/
|
||||
public IdentNode setIsFunction() {
|
||||
if(isFunction()) return this;
|
||||
final IdentNode n = (IdentNode)clone();
|
||||
n.flags |= FUNCTION;
|
||||
return n;
|
||||
if (isFunction()) {
|
||||
return this;
|
||||
}
|
||||
return new IdentNode(this, name, callSiteType, flags | FUNCTION);
|
||||
}
|
||||
}
|
||||
|
@ -25,22 +25,23 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for an IF statement.
|
||||
*
|
||||
*/
|
||||
public class IfNode extends Node {
|
||||
@Immutable
|
||||
public final class IfNode extends Node {
|
||||
/** Test expression. */
|
||||
private Node test;
|
||||
private final Node test;
|
||||
|
||||
/** Pass statements. */
|
||||
private Block pass;
|
||||
private final Block pass;
|
||||
|
||||
/** Fail statements. */
|
||||
private Block fail;
|
||||
private final Block fail;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -54,37 +55,30 @@ public class IfNode extends Node {
|
||||
*/
|
||||
public IfNode(final Source source, final long token, final int finish, final Node test, final Block pass, final Block fail) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.test = test;
|
||||
this.pass = pass;
|
||||
this.fail = fail;
|
||||
}
|
||||
|
||||
private IfNode(final IfNode ifNode, final CopyState cs) {
|
||||
private IfNode(final IfNode ifNode, final Node test, final Block pass, final Block fail) {
|
||||
super(ifNode);
|
||||
|
||||
this.test = cs.existingOrCopy(ifNode.test);
|
||||
this.pass = (Block)cs.existingOrCopy(ifNode.pass);
|
||||
this.fail = (Block)cs.existingOrCopy(ifNode.fail);
|
||||
this.test = test;
|
||||
this.pass = pass;
|
||||
this.fail = fail;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new IfNode(this, cs);
|
||||
public boolean isTerminal() {
|
||||
return pass.isTerminal() && fail != null && fail.isTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterIfNode(this) != null) {
|
||||
test = test.accept(visitor);
|
||||
|
||||
pass = (Block)pass.accept(visitor);
|
||||
|
||||
if (fail != null) {
|
||||
fail = (Block)fail.accept(visitor);
|
||||
}
|
||||
|
||||
return visitor.leaveIfNode(this);
|
||||
if (visitor.enterIfNode(this)) {
|
||||
return visitor.leaveIfNode(
|
||||
setTest(test.accept(visitor)).
|
||||
setPass((Block)pass.accept(visitor)).
|
||||
setFail(fail == null ? null : (Block)fail.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -105,6 +99,13 @@ public class IfNode extends Node {
|
||||
return fail;
|
||||
}
|
||||
|
||||
private IfNode setFail(final Block fail) {
|
||||
if (this.fail == fail) {
|
||||
return this;
|
||||
}
|
||||
return new IfNode(this, test, pass, fail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the then block for this IfNode
|
||||
* @return the then block
|
||||
@ -113,6 +114,13 @@ public class IfNode extends Node {
|
||||
return pass;
|
||||
}
|
||||
|
||||
private IfNode setPass(final Block pass) {
|
||||
if (this.pass == pass) {
|
||||
return this;
|
||||
}
|
||||
return new IfNode(this, test, pass, fail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test expression for this IfNode
|
||||
* @return the test expression
|
||||
@ -124,8 +132,12 @@ public class IfNode extends Node {
|
||||
/**
|
||||
* Reset the test expression for this IfNode
|
||||
* @param test a new test expression
|
||||
* @return new or same IfNode
|
||||
*/
|
||||
public void setTest(final Node test) {
|
||||
this.test = test;
|
||||
public IfNode setTest(final Node test) {
|
||||
if (this.test == test) {
|
||||
return this;
|
||||
}
|
||||
return new IfNode(this, test, pass, fail);
|
||||
}
|
||||
}
|
||||
|
@ -25,22 +25,18 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of an indexed access (brackets operator.)
|
||||
*
|
||||
*/
|
||||
public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
|
||||
/** Property ident. */
|
||||
private Node index;
|
||||
|
||||
private boolean hasCallSiteType;
|
||||
@Immutable
|
||||
public final class IndexNode extends BaseNode {
|
||||
/** Property index. */
|
||||
private final Node index;
|
||||
|
||||
/**
|
||||
* Constructors
|
||||
@ -52,50 +48,27 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
|
||||
* @param index index for access
|
||||
*/
|
||||
public IndexNode(final Source source, final long token, final int finish, final Node base, final Node index) {
|
||||
super(source, token, finish, base);
|
||||
|
||||
super(source, token, finish, base, false, false);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param indexNode source node
|
||||
*/
|
||||
public IndexNode(final IndexNode indexNode) {
|
||||
this(indexNode, new CopyState());
|
||||
}
|
||||
|
||||
private IndexNode(final IndexNode indexNode, final CopyState cs) {
|
||||
super(indexNode, cs);
|
||||
|
||||
index = cs.existingOrCopy(indexNode.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new IndexNode(this, cs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return index.equals(((IndexNode)other).getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ getIndex().hashCode();
|
||||
private IndexNode(final IndexNode indexNode, final Node base, final Node index, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
super(indexNode, base, isFunction, hasCallSiteType);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterIndexNode(this) != null) {
|
||||
base = base.accept(visitor);
|
||||
index = index.accept(visitor);
|
||||
return visitor.leaveIndexNode(this);
|
||||
if (visitor.enterIndexNode(this)) {
|
||||
final Node newBase = base.accept(visitor);
|
||||
final Node newIndex = index.accept(visitor);
|
||||
final IndexNode newNode;
|
||||
if (newBase != base || newIndex != index) {
|
||||
newNode = new IndexNode(this, newBase, newIndex, isFunction(), hasCallSiteType());
|
||||
} else {
|
||||
newNode = this;
|
||||
}
|
||||
return visitor.leaveIndexNode(newNode);
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -105,7 +78,7 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
|
||||
public void toString(final StringBuilder sb) {
|
||||
final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
|
||||
|
||||
if (hasCallSiteType) {
|
||||
if (hasCallSiteType()) {
|
||||
sb.append('{');
|
||||
final String desc = getType().getDescriptor();
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
|
||||
@ -135,27 +108,19 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the index expression for this IndexNode
|
||||
* @param index a new index expression
|
||||
*/
|
||||
public void setIndex(final Node index) {
|
||||
this.index = index;
|
||||
@Override
|
||||
public BaseNode setIsFunction() {
|
||||
if (isFunction()) {
|
||||
return this;
|
||||
}
|
||||
return new IndexNode(this, base, index, true, hasCallSiteType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexNode setType(final Type type) {
|
||||
if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
|
||||
}
|
||||
hasCallSiteType = true;
|
||||
getSymbol().setTypeOverride(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHaveCallSiteType() {
|
||||
return true; //carried by the symbol and always the same nodetype==symboltype
|
||||
logTypeChange(type);
|
||||
getSymbol().setTypeOverride(type); //always a temp so this is fine.
|
||||
return new IndexNode(this, base, index, isFunction(), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,29 +25,20 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for a labeled statement.
|
||||
*
|
||||
*/
|
||||
|
||||
public class LabelNode extends Node {
|
||||
@Immutable
|
||||
public final class LabelNode extends LexicalContextNode {
|
||||
/** Label ident. */
|
||||
private IdentNode label;
|
||||
private final IdentNode label;
|
||||
|
||||
/** Statements. */
|
||||
private Block body;
|
||||
|
||||
/** Node to break from. */
|
||||
@Ignore
|
||||
private Node breakNode;
|
||||
|
||||
/** Node to continue. */
|
||||
@Ignore
|
||||
private Node continueNode;
|
||||
private final Block body;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -65,26 +56,23 @@ public class LabelNode extends Node {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
private LabelNode(final LabelNode labelNode, final CopyState cs) {
|
||||
private LabelNode(final LabelNode labelNode, final IdentNode label, final Block body) {
|
||||
super(labelNode);
|
||||
|
||||
this.label = (IdentNode)cs.existingOrCopy(labelNode.label);
|
||||
this.body = (Block)cs.existingOrCopy(labelNode.body);
|
||||
this.breakNode = cs.existingOrSame(labelNode.breakNode);
|
||||
this.continueNode = cs.existingOrSame(labelNode.continueNode);
|
||||
this.label = label;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new LabelNode(this, cs);
|
||||
public boolean isTerminal() {
|
||||
return body.isTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterLabelNode(this) != null) {
|
||||
label = (IdentNode)label.accept(visitor);
|
||||
body = (Block)body.accept(visitor);
|
||||
return visitor.leaveLabelNode(this);
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterLabelNode(this)) {
|
||||
return visitor.leaveLabelNode(
|
||||
setLabel(visitor.getLexicalContext(), (IdentNode)label.accept(visitor)).
|
||||
setBody(visitor.getLexicalContext(), (Block)body.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -106,44 +94,15 @@ public class LabelNode extends Node {
|
||||
|
||||
/**
|
||||
* Reset the body of the node
|
||||
* @param lc lexical context
|
||||
* @param body new body
|
||||
* @return new for node if changed or existing if not
|
||||
*/
|
||||
public void setBody(final Block body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the break node for this node
|
||||
* @return the break node
|
||||
*/
|
||||
public Node getBreakNode() {
|
||||
return breakNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the break node for this node
|
||||
* @param breakNode the break node
|
||||
*/
|
||||
public void setBreakNode(final Node breakNode) {
|
||||
assert breakNode instanceof BreakableNode || breakNode instanceof Block : "Invalid break node: " + breakNode;
|
||||
this.breakNode = breakNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the continue node for this node
|
||||
* @return the continue node
|
||||
*/
|
||||
public Node getContinueNode() {
|
||||
return continueNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the continue node for this node
|
||||
* @param continueNode the continue node
|
||||
*/
|
||||
public void setContinueNode(final Node continueNode) {
|
||||
assert continueNode instanceof WhileNode : "invalid continue node: " + continueNode;
|
||||
this.continueNode = continueNode;
|
||||
public LabelNode setBody(final LexicalContext lc, final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,4 +113,11 @@ public class LabelNode extends Node {
|
||||
return label;
|
||||
}
|
||||
|
||||
private LabelNode setLabel(final LexicalContext lc, final IdentNode label) {
|
||||
if (this.label == label) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, body));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR base class for break and continue.
|
||||
*
|
||||
*/
|
||||
public abstract class LabeledNode extends Node {
|
||||
/** Optional label. */
|
||||
@Ignore
|
||||
protected final LabelNode labelNode;
|
||||
|
||||
/** Target control node. */
|
||||
@Ignore
|
||||
protected final Node targetNode;
|
||||
|
||||
/** Try chain. */
|
||||
@Ignore
|
||||
protected final TryNode tryChain;
|
||||
|
||||
/** scope nesting level */
|
||||
protected int scopeNestingLevel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param labelNode the label node
|
||||
* @param targetNode the place to break to
|
||||
* @param tryChain the try chain
|
||||
*/
|
||||
public LabeledNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.labelNode = labelNode;
|
||||
this.targetNode = targetNode;
|
||||
this.tryChain = tryChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param labeledNode source node
|
||||
* @param cs copy state
|
||||
*/
|
||||
protected LabeledNode(final LabeledNode labeledNode, final CopyState cs) {
|
||||
super(labeledNode);
|
||||
|
||||
this.labelNode = (LabelNode)cs.existingOrCopy(labeledNode.labelNode);
|
||||
this.targetNode = cs.existingOrSame(labeledNode.targetNode);
|
||||
this.tryChain = (TryNode)cs.existingOrSame(labeledNode.tryChain);
|
||||
this.scopeNestingLevel = labeledNode.scopeNestingLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label
|
||||
* @return the label
|
||||
*/
|
||||
public LabelNode getLabel() {
|
||||
return labelNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target node
|
||||
* @return the target node
|
||||
*/
|
||||
public Node getTargetNode() {
|
||||
return targetNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the surrounding try chain
|
||||
* @return the try chain
|
||||
*/
|
||||
public TryNode getTryChain() {
|
||||
return tryChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scope nesting level
|
||||
* @return nesting level
|
||||
*/
|
||||
public int getScopeNestingLevel() {
|
||||
return scopeNestingLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set scope nesting level
|
||||
* @param level the new level
|
||||
*/
|
||||
public void setScopeNestingLevel(final int level) {
|
||||
scopeNestingLevel = level;
|
||||
}
|
||||
}
|
@ -1,40 +1,224 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.io.File;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.runtime.Debug;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* A class that tracks the current lexical context of node visitation as a stack of {@link Block} nodes. Has special
|
||||
* methods to retrieve useful subsets of the context.
|
||||
*
|
||||
* This is implemented with a primitive array and a stack pointer, because it really makes a difference
|
||||
* performance wise. None of the collection classes were optimal
|
||||
*/
|
||||
public class LexicalContext implements Cloneable {
|
||||
private final Deque<Block> lexicalContext;
|
||||
public class LexicalContext {
|
||||
private LexicalContextNode[] stack;
|
||||
|
||||
private int[] flags;
|
||||
private int sp;
|
||||
|
||||
/**
|
||||
* Creates a new empty lexical context.
|
||||
*/
|
||||
public LexicalContext() {
|
||||
lexicalContext = new ArrayDeque<>();
|
||||
stack = new LexicalContextNode[16];
|
||||
flags = new int[16];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flags for a lexical context node on the stack. Does not
|
||||
* replace the flags, but rather adds to them
|
||||
*
|
||||
* @param node node
|
||||
* @param flag new flag to set
|
||||
*/
|
||||
public void setFlag(final LexicalContextNode node, final int flag) {
|
||||
if (flag != 0) {
|
||||
for (int i = sp - 1; i >= 0; i--) {
|
||||
if (stack[i] == node) {
|
||||
flags[i] |= flag;
|
||||
//System.err.println("Setting flag " + node + " " + flag);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the flags for a lexical context node on the stack
|
||||
* @param node node
|
||||
* @return the flags for the node
|
||||
*/
|
||||
public int getFlags(final LexicalContextNode node) {
|
||||
for (int i = sp - 1; i >= 0; i--) {
|
||||
if (stack[i] == node) {
|
||||
return flags[i];
|
||||
}
|
||||
}
|
||||
throw new AssertionError("flag node not on context stack");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the function body of a function node on the lexical context
|
||||
* stack. This will trigger an assertion if node isn't present
|
||||
* @param functionNode function node
|
||||
* @return body of function node
|
||||
*/
|
||||
public Block getFunctionBody(final FunctionNode functionNode) {
|
||||
for (int i = sp - 1; i >= 0 ; i--) {
|
||||
if (stack[i] == functionNode) {
|
||||
return (Block)stack[i + 1];
|
||||
}
|
||||
}
|
||||
throw new AssertionError(functionNode.getName() + " not on context stack");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all nodes in the LexicalContext
|
||||
* @return all nodes
|
||||
*/
|
||||
public Iterator<LexicalContextNode> getAllNodes() {
|
||||
return new NodeIterator<>(LexicalContextNode.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the outermost function in this context. It is either the program, or a lazily compiled function.
|
||||
* @return the outermost function in this context.
|
||||
*/
|
||||
public FunctionNode getOutermostFunction() {
|
||||
return (FunctionNode)stack[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Pushes a new block on top of the context, making it the innermost open block.
|
||||
* @param block the new block
|
||||
* @param node the new node
|
||||
* @return the node that was pushed
|
||||
*/
|
||||
public void push(Block block) {
|
||||
//new Exception(block.toString()).printStackTrace();
|
||||
lexicalContext.push(block);
|
||||
public <T extends LexicalContextNode> T push(final T node) {
|
||||
if (sp == stack.length) {
|
||||
final LexicalContextNode[] newStack = new LexicalContextNode[sp * 2];
|
||||
System.arraycopy(stack, 0, newStack, 0, sp);
|
||||
stack = newStack;
|
||||
|
||||
final int[] newFlags = new int[sp * 2];
|
||||
System.arraycopy(flags, 0, newFlags, 0, sp);
|
||||
flags = newFlags;
|
||||
|
||||
}
|
||||
stack[sp] = node;
|
||||
flags[sp] = 0;
|
||||
|
||||
sp++;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the innermost block off the context.
|
||||
* @param the block expected to be popped, used to detect unbalanced pushes/pops
|
||||
* Is the context empty?
|
||||
* @return true if empty
|
||||
*/
|
||||
public void pop(Block block) {
|
||||
final Block popped = lexicalContext.pop();
|
||||
assert popped == block;
|
||||
public boolean isEmpty() {
|
||||
return sp == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The depth of the lexical context
|
||||
* @return depth
|
||||
*/
|
||||
public int size() {
|
||||
return sp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the innermost block off the context and all nodes that has been contributed
|
||||
* since it was put there
|
||||
*
|
||||
* @param node the node expected to be popped, used to detect unbalanced pushes/pops
|
||||
* @return the node that was popped
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends LexicalContextNode> T pop(final T node) {
|
||||
--sp;
|
||||
final LexicalContextNode popped = stack[sp];
|
||||
stack[sp] = null;
|
||||
if (popped instanceof Flags) {
|
||||
return (T)((Flags<?>)popped).setFlag(this, flags[sp]);
|
||||
}
|
||||
|
||||
return (T)popped;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the top element in the context
|
||||
* @return the node that was pushed last
|
||||
*/
|
||||
public LexicalContextNode peek() {
|
||||
return stack[sp - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node is in the lexical context
|
||||
* @param node node to check for
|
||||
* @return true if in the context
|
||||
*/
|
||||
public boolean contains(final LexicalContextNode node) {
|
||||
for (int i = 0; i < sp; i++) {
|
||||
if (stack[i] == node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a node on the lexical context with a new one. Normally
|
||||
* you should try to engineer IR traversals so this isn't needed
|
||||
*
|
||||
* @param oldNode old node
|
||||
* @param newNode new node
|
||||
* @return the new node
|
||||
*/
|
||||
public LexicalContextNode replace(final LexicalContextNode oldNode, final LexicalContextNode newNode) {
|
||||
//System.err.println("REPLACE old=" + Debug.id(oldNode) + " new=" + Debug.id(newNode));
|
||||
for (int i = sp - 1; i >= 0; i--) {
|
||||
if (stack[i] == oldNode) {
|
||||
assert i == (sp - 1) : "violation of contract - we always expect to find the replacement node on top of the lexical context stack: " + newNode + " has " + stack[i + 1].getClass() + " above it";
|
||||
stack[i] = newNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,7 +226,7 @@ public class LexicalContext implements Cloneable {
|
||||
* @return an iterator over all blocks in the context.
|
||||
*/
|
||||
public Iterator<Block> getBlocks() {
|
||||
return lexicalContext.iterator();
|
||||
return new NodeIterator<>(Block.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,47 +234,17 @@ public class LexicalContext implements Cloneable {
|
||||
* @return an iterator over all functions in the context.
|
||||
*/
|
||||
public Iterator<FunctionNode> getFunctions() {
|
||||
return new FunctionIterator(getBlocks());
|
||||
return new NodeIterator<>(FunctionNode.class);
|
||||
}
|
||||
|
||||
private static final class FunctionIterator implements Iterator<FunctionNode> {
|
||||
private final Iterator<Block> it;
|
||||
private FunctionNode next;
|
||||
|
||||
FunctionIterator(Iterator<Block> it) {
|
||||
this.it = it;
|
||||
next = findNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionNode next() {
|
||||
if(next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
FunctionNode lnext = next;
|
||||
next = findNext();
|
||||
return lnext;
|
||||
}
|
||||
|
||||
private FunctionNode findNext() {
|
||||
while(it.hasNext()) {
|
||||
final Block block = it.next();
|
||||
if(block instanceof FunctionNode) {
|
||||
return ((FunctionNode)block);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
/**
|
||||
* Get the parent block for the current lexical context block
|
||||
* @return parent block
|
||||
*/
|
||||
public Block getParentBlock() {
|
||||
final Iterator<Block> iter = new NodeIterator<>(Block.class, getCurrentFunction());
|
||||
iter.next();
|
||||
return iter.hasNext() ? iter.next() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,12 +252,12 @@ public class LexicalContext implements Cloneable {
|
||||
* @param block the block whose ancestors are returned
|
||||
* @return an iterator over all ancestors block of the given block.
|
||||
*/
|
||||
public Iterator<Block> getAncestorBlocks(Block block) {
|
||||
final Iterator<Block> it = getBlocks();
|
||||
while(it.hasNext()) {
|
||||
final Block b = it.next();
|
||||
if(block == b) {
|
||||
return it;
|
||||
public Iterator<Block> getAncestorBlocks(final Block block) {
|
||||
final Iterator<Block> iter = getBlocks();
|
||||
while (iter.hasNext()) {
|
||||
final Block b = iter.next();
|
||||
if (block == b) {
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Block is not on the current lexical context stack");
|
||||
@ -115,17 +269,17 @@ public class LexicalContext implements Cloneable {
|
||||
* @return an iterator over a block and all its ancestors.
|
||||
*/
|
||||
public Iterator<Block> getBlocks(final Block block) {
|
||||
final Iterator<Block> it = getAncestorBlocks(block);
|
||||
final Iterator<Block> iter = getAncestorBlocks(block);
|
||||
return new Iterator<Block>() {
|
||||
boolean blockReturned = false;
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasNext() || !blockReturned;
|
||||
return iter.hasNext() || !blockReturned;
|
||||
}
|
||||
@Override
|
||||
public Block next() {
|
||||
if(blockReturned) {
|
||||
return it.next();
|
||||
if (blockReturned) {
|
||||
return iter.next();
|
||||
}
|
||||
blockReturned = true;
|
||||
return block;
|
||||
@ -138,45 +292,25 @@ public class LexicalContext implements Cloneable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest function node to the block. If the block is itself a function, it is returned.
|
||||
* @param block the block
|
||||
* @return the function closest to the block.
|
||||
* @see #getParentFunction(Block)
|
||||
* Get the function for this block. If the block is itself a function
|
||||
* this returns identity
|
||||
* @param block block for which to get function
|
||||
* @return function for block
|
||||
*/
|
||||
public FunctionNode getFunction(Block block) {
|
||||
if(block instanceof FunctionNode) {
|
||||
return (FunctionNode)block;
|
||||
}
|
||||
return getParentFunction(block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest function node to the block and all its ancestor functions. If the block is itself a function,
|
||||
* it is returned too.
|
||||
* @param block the block
|
||||
* @return the closest function node to the block and all its ancestor functions.
|
||||
*/
|
||||
public Iterator<FunctionNode> getFunctions(final Block block) {
|
||||
return new FunctionIterator(getBlocks(block));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the containing function of the block. If the block is itself a function, its parent function is returned.
|
||||
* @param block the block
|
||||
* @return the containing function of the block.
|
||||
* @see #getFunction(Block)
|
||||
*/
|
||||
public FunctionNode getParentFunction(Block block) {
|
||||
return getFirstFunction(getAncestorBlocks(block));
|
||||
}
|
||||
|
||||
private static FunctionNode getFirstFunction(Iterator<Block> it) {
|
||||
while(it.hasNext()) {
|
||||
final Block ancestor = it.next();
|
||||
if(ancestor instanceof FunctionNode) {
|
||||
return (FunctionNode)ancestor;
|
||||
public FunctionNode getFunction(final Block block) {
|
||||
final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class);
|
||||
while (iter.hasNext()) {
|
||||
final LexicalContextNode next = iter.next();
|
||||
if (next == block) {
|
||||
while (iter.hasNext()) {
|
||||
final LexicalContextNode next2 = iter.next();
|
||||
if (next2 instanceof FunctionNode) {
|
||||
return (FunctionNode)next2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -185,7 +319,7 @@ public class LexicalContext implements Cloneable {
|
||||
* @return the innermost block in the context.
|
||||
*/
|
||||
public Block getCurrentBlock() {
|
||||
return lexicalContext.element();
|
||||
return getBlocks().next();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -193,6 +327,284 @@ public class LexicalContext implements Cloneable {
|
||||
* @return the innermost function in the context.
|
||||
*/
|
||||
public FunctionNode getCurrentFunction() {
|
||||
return getFirstFunction(getBlocks());
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new NodeIterator<>(FunctionNode.class).next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block in which a symbol is defined
|
||||
* @param symbol symbol
|
||||
* @return block in which the symbol is defined, assert if no such block in context
|
||||
*/
|
||||
public Block getDefiningBlock(final Symbol symbol) {
|
||||
if (symbol.isTemp()) {
|
||||
return null;
|
||||
}
|
||||
final String name = symbol.getName();
|
||||
for (final Iterator<Block> it = getBlocks(); it.hasNext();) {
|
||||
final Block next = it.next();
|
||||
if (next.getExistingSymbol(name) == symbol) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Couldn't find symbol " + name + " in the context");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the function in which a symbol is defined
|
||||
* @param symbol symbol
|
||||
* @return function node in which this symbol is defined, assert if no such symbol exists in context
|
||||
*/
|
||||
public FunctionNode getDefiningFunction(Symbol symbol) {
|
||||
if (symbol.isTemp()) {
|
||||
return null;
|
||||
}
|
||||
final String name = symbol.getName();
|
||||
for (final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class); iter.hasNext();) {
|
||||
final LexicalContextNode next = iter.next();
|
||||
if (next instanceof Block && ((Block)next).getExistingSymbol(name) == symbol) {
|
||||
while (iter.hasNext()) {
|
||||
final LexicalContextNode next2 = iter.next();
|
||||
if (next2 instanceof FunctionNode) {
|
||||
return ((FunctionNode)next2);
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Defining block for symbol " + name + " has no function in the context");
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Couldn't find symbol " + name + " in the context");
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the topmost lexical context element a function body?
|
||||
* @return true if function body
|
||||
*/
|
||||
public boolean isFunctionBody() {
|
||||
return getParentBlock() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the expression defining the function is a callee of a CallNode that should be the second
|
||||
* element on the stack, e.g. <code>(function(){})()</code>. That is, if the stack ends with
|
||||
* {@code [..., CallNode, FunctionNode]} then {@code callNode.getFunction()} should be equal to
|
||||
* {@code functionNode}, and the top of the stack should itself be a variant of {@code functionNode}.
|
||||
* @param functionNode the function node being tested
|
||||
* @return true if the expression defining the current function is a callee of a call expression.
|
||||
*/
|
||||
public boolean isFunctionDefinedInCurrentCall(FunctionNode functionNode) {
|
||||
final LexicalContextNode parent = stack[sp - 2];
|
||||
if(parent instanceof CallNode && ((CallNode)parent).getFunction() == functionNode) {
|
||||
assert functionNode.getSource() == peek().getSource();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent function for a function in the lexical context
|
||||
* @param functionNode function for which to get parent
|
||||
* @return parent function of functionNode or null if none (e.g. if functionNode is the program)
|
||||
*/
|
||||
public FunctionNode getParentFunction(final FunctionNode functionNode) {
|
||||
final Iterator<FunctionNode> iter = new NodeIterator<>(FunctionNode.class);
|
||||
while (iter.hasNext()) {
|
||||
final FunctionNode next = iter.next();
|
||||
if (next == functionNode) {
|
||||
return iter.hasNext() ? iter.next() : null;
|
||||
}
|
||||
}
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of with scopes until a given node
|
||||
* @param until node to stop counting at, or null if all nodes should be counted
|
||||
* @return number of with scopes encountered in the context
|
||||
*/
|
||||
public int getScopeNestingLevelTo(final LexicalContextNode until) {
|
||||
//count the number of with nodes until "until" is hit
|
||||
int n = 0;
|
||||
for (final Iterator<WithNode> iter = new NodeIterator<>(WithNode.class, until); iter.hasNext(); iter.next()) {
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
private BreakableNode getBreakable() {
|
||||
for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, getCurrentFunction()); iter.hasNext(); ) {
|
||||
final BreakableNode next = iter.next();
|
||||
if (next.isBreakableWithoutLabel()) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the breakable node corresponding to this label.
|
||||
* @param label label to search for, if null the closest breakable node will be returned unconditionally, e.g. a while loop with no label
|
||||
* @return closest breakable node
|
||||
*/
|
||||
public BreakableNode getBreakable(final IdentNode label) {
|
||||
if (label != null) {
|
||||
final LabelNode foundLabel = findLabel(label.getName());
|
||||
if (foundLabel != null) {
|
||||
// iterate to the nearest breakable to the foundLabel
|
||||
BreakableNode breakable = null;
|
||||
for (final NodeIterator<BreakableNode> iter = new NodeIterator<>(BreakableNode.class, foundLabel); iter.hasNext(); ) {
|
||||
breakable = iter.next();
|
||||
}
|
||||
return breakable;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return getBreakable();
|
||||
}
|
||||
|
||||
private LoopNode getContinueTo() {
|
||||
final Iterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, getCurrentFunction());
|
||||
return iter.hasNext() ? iter.next() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the continue target node corresponding to this label.
|
||||
* @param label label to search for, if null the closest loop node will be returned unconditionally, e.g. a while loop with no label
|
||||
* @return closest continue target node
|
||||
*/
|
||||
public LoopNode getContinueTo(final IdentNode label) {
|
||||
if (label != null) {
|
||||
final LabelNode foundLabel = findLabel(label.getName());
|
||||
if (foundLabel != null) {
|
||||
// iterate to the nearest loop to the foundLabel
|
||||
LoopNode loop = null;
|
||||
for (final NodeIterator<LoopNode> iter = new NodeIterator<>(LoopNode.class, foundLabel); iter.hasNext(); ) {
|
||||
loop = iter.next();
|
||||
}
|
||||
return loop;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return getContinueTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the lexical context for a given label node by name
|
||||
* @param name name of the label
|
||||
* @return LabelNode if found, null otherwise
|
||||
*/
|
||||
public LabelNode findLabel(final String name) {
|
||||
for (final Iterator<LabelNode> iter = new NodeIterator<>(LabelNode.class, getCurrentFunction()); iter.hasNext(); ) {
|
||||
final LabelNode next = iter.next();
|
||||
if (next.getLabel().getName().equals(name)) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given label is a jump destination that lies outside a given
|
||||
* split node
|
||||
* @param splitNode the split node
|
||||
* @param label the label
|
||||
* @return true if label resides outside the split node
|
||||
*/
|
||||
public boolean isExternalTarget(final SplitNode splitNode, final Label label) {
|
||||
boolean targetFound = false;
|
||||
for (int i = sp - 1; i >= 0; i--) {
|
||||
final LexicalContextNode next = stack[i];
|
||||
if (next == splitNode) {
|
||||
return !targetFound;
|
||||
}
|
||||
|
||||
if (next instanceof BreakableNode) {
|
||||
for (final Label l : ((BreakableNode)next).getLabels()) {
|
||||
if (l == label) {
|
||||
targetFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert false : label + " was expected in lexical context " + LexicalContext.this + " but wasn't";
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
sb.append("[ ");
|
||||
for (int i = 0; i < sp; i++) {
|
||||
final Node node = stack[i];
|
||||
sb.append(node.getClass().getSimpleName());
|
||||
sb.append('@');
|
||||
sb.append(Debug.id(node));
|
||||
sb.append(':');
|
||||
final Source source = node.getSource();
|
||||
String src = source.toString();
|
||||
if (src.indexOf(File.pathSeparator) != -1) {
|
||||
src = src.substring(src.lastIndexOf(File.pathSeparator));
|
||||
}
|
||||
src += ' ';
|
||||
src += source.getLine(node.getStart());
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(" ==> ]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private class NodeIterator <T extends LexicalContextNode> implements Iterator<T> {
|
||||
private int index;
|
||||
private T next;
|
||||
private final Class<T> clazz;
|
||||
private LexicalContextNode until;
|
||||
|
||||
NodeIterator(final Class<T> clazz) {
|
||||
this(clazz, null);
|
||||
}
|
||||
|
||||
NodeIterator(final Class<T> clazz, final LexicalContextNode until) {
|
||||
this.index = sp - 1;
|
||||
this.clazz = clazz;
|
||||
this.until = until;
|
||||
this.next = findNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
T lnext = next;
|
||||
next = findNext();
|
||||
return lnext;
|
||||
}
|
||||
|
||||
private T findNext() {
|
||||
for (int i = index; i >= 0; i--) {
|
||||
final Node node = stack[i];
|
||||
if (node == until) {
|
||||
return null;
|
||||
}
|
||||
if (clazz.isAssignableFrom(node.getClass())) {
|
||||
index = i - 1;
|
||||
return clazz.cast(node);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,61 +22,52 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* Loop representing do while loops. This is mostly split from WhileNode
|
||||
* because of the different order of the Phi Traversals
|
||||
*
|
||||
* Superclass for nodes that can be part of the lexical context
|
||||
* @see LexicalContext
|
||||
*/
|
||||
public class DoWhileNode extends WhileNode {
|
||||
|
||||
public abstract class LexicalContextNode extends Node {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param source source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
*/
|
||||
public DoWhileNode(final Source source, final long token, final int finish) {
|
||||
protected LexicalContextNode(final Source source, final long token, final int finish) {
|
||||
super(source, token, finish);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param doWhileNode source node
|
||||
* @param cs copy state
|
||||
* @param node source node
|
||||
*/
|
||||
protected DoWhileNode(final DoWhileNode doWhileNode, final CopyState cs) {
|
||||
super(doWhileNode, cs);
|
||||
protected LexicalContextNode(final LexicalContextNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new DoWhileNode(this, cs);
|
||||
}
|
||||
/**
|
||||
* Accept function for the node given a lexical context. It must be prepared
|
||||
* to replace itself if present in the lexical context
|
||||
*
|
||||
* @param lc lexical context
|
||||
* @param visitor node visitor
|
||||
*
|
||||
* @return new node or same node depending on state change
|
||||
*/
|
||||
protected abstract Node accept(final LexicalContext lc, final NodeVisitor visitor);
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterDoWhileNode(this) != null) {
|
||||
body = (Block)body.accept(visitor);
|
||||
test = test.accept(visitor);
|
||||
|
||||
return visitor.leaveDoWhileNode(this);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append("while (");
|
||||
test.toString(sb);
|
||||
sb.append(')');
|
||||
final LexicalContext lc = visitor.getLexicalContext();
|
||||
lc.push(this);
|
||||
final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
|
||||
return lc.pop(newNode);
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
@ -32,8 +33,8 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
/**
|
||||
* IR Node representing a line number
|
||||
*/
|
||||
|
||||
public class LineNumberNode extends Node {
|
||||
@Immutable
|
||||
public final class LineNumberNode extends Node {
|
||||
/** Line number */
|
||||
private final int lineNumber;
|
||||
|
||||
@ -46,24 +47,17 @@ public class LineNumberNode extends Node {
|
||||
*/
|
||||
public LineNumberNode(final Source source, final long token, final int lineNumber) {
|
||||
super(source, token, Token.descPosition(token));
|
||||
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
private LineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
private LineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
super(lineNumberNode);
|
||||
|
||||
this.lineNumber = lineNumberNode.getLineNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new LineNumberNode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterLineNumberNode(this) != null) {
|
||||
if (visitor.enterLineNumberNode(this)) {
|
||||
return visitor.leaveLineNumberNode(this);
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Lexer.LexerToken;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
@ -44,6 +45,7 @@ import jdk.nashorn.internal.runtime.Undefined;
|
||||
*
|
||||
* @param <T> the literal type
|
||||
*/
|
||||
@Immutable
|
||||
public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
/** Literal value */
|
||||
protected final T value;
|
||||
@ -93,23 +95,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return value == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value == null ? 0 : value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!(other instanceof LiteralNode<?>)) {
|
||||
return false;
|
||||
}
|
||||
final LiteralNode<?> otherNode = (LiteralNode<?>)other;
|
||||
if (otherNode.isNull()) {
|
||||
return isNull();
|
||||
}
|
||||
return ((LiteralNode<?>)other).getValue().equals(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the literal value is boolean true
|
||||
* @return true if literal value is boolean true
|
||||
@ -226,7 +211,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterLiteralNode(this) != null) {
|
||||
if (visitor.enterLiteralNode(this)) {
|
||||
return visitor.leaveLiteralNode(this);
|
||||
}
|
||||
|
||||
@ -274,7 +259,8 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
|
||||
}
|
||||
|
||||
private static class BooleanLiteralNode extends LiteralNode<Boolean> {
|
||||
@Immutable
|
||||
private static final class BooleanLiteralNode extends LiteralNode<Boolean> {
|
||||
|
||||
private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) {
|
||||
super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
|
||||
@ -284,11 +270,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
super(literalNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new BooleanLiteralNode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrue() {
|
||||
return value;
|
||||
@ -331,7 +312,8 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
|
||||
}
|
||||
|
||||
private static class NumberLiteralNode extends LiteralNode<Number> {
|
||||
@Immutable
|
||||
private static final class NumberLiteralNode extends LiteralNode<Number> {
|
||||
|
||||
private final Type type = numberGetType(value);
|
||||
|
||||
@ -357,11 +339,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new NumberLiteralNode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
@ -407,11 +384,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
|
||||
super(literalNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new UndefinedLiteralNode(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -440,6 +412,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private static class StringLiteralNode extends LiteralNode<String> {
|
||||
private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
|
||||
super(source, Token.recast(token, TokenType.STRING), finish, value);
|
||||
@ -449,11 +422,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
super(literalNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new StringLiteralNode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append('\"');
|
||||
@ -488,6 +456,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
|
||||
private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) {
|
||||
super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
|
||||
@ -497,11 +466,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
super(literalNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new LexerTokenLiteralNode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.OBJECT;
|
||||
@ -539,7 +503,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
|
||||
}
|
||||
|
||||
private static class NodeLiteralNode extends LiteralNode<Node> {
|
||||
private static final class NodeLiteralNode extends LiteralNode<Node> {
|
||||
|
||||
private NodeLiteralNode(final Source source, final long token, final int finish) {
|
||||
this(source, token, finish, null);
|
||||
@ -557,14 +521,9 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
super(literalNode, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new NodeLiteralNode(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterLiteralNode(this) != null) {
|
||||
if (visitor.enterLiteralNode(this)) {
|
||||
if (value != null) {
|
||||
final Node newValue = value.accept(visitor);
|
||||
if(value != newValue) {
|
||||
@ -617,7 +576,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
/**
|
||||
* Array literal node class.
|
||||
*/
|
||||
public static class ArrayLiteralNode extends LiteralNode<Node[]> {
|
||||
public static final class ArrayLiteralNode extends LiteralNode<Node[]> {
|
||||
private static class PostsetMarker {
|
||||
//empty
|
||||
}
|
||||
@ -705,11 +664,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
this.elementType = node.elementType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new ArrayLiteralNode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute things like widest element type needed. Internal use from compiler only
|
||||
*/
|
||||
@ -894,7 +848,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterLiteralNode(this) != null) {
|
||||
if (visitor.enterLiteralNode(this)) {
|
||||
for (int i = 0; i < value.length; i++) {
|
||||
final Node element = value[i];
|
||||
if (element != null) {
|
||||
|
@ -25,16 +25,13 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.Objects;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* Used to locate an entity back to it's source file.
|
||||
*
|
||||
*/
|
||||
|
||||
public class Location implements Cloneable {
|
||||
/** Source of entity. */
|
||||
private final Source source;
|
||||
@ -73,22 +70,13 @@ public class Location implements Cloneable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (other.getClass() != this.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Location loc = (Location)other;
|
||||
return token == loc.token && Objects.equals(source, loc.source);
|
||||
public final boolean equals(final Object other) {
|
||||
return super.equals(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Token.hashCode(token) ^ Objects.hashCode(source);
|
||||
public final int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
176
nashorn/src/jdk/nashorn/internal/ir/LoopNode.java
Normal file
176
nashorn/src/jdk/nashorn/internal/ir/LoopNode.java
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* A loop node, for example a while node, do while node or for node
|
||||
*/
|
||||
public abstract class LoopNode extends BreakableNode {
|
||||
/** loop continue label. */
|
||||
protected final Label continueLabel;
|
||||
|
||||
/** Loop test node, null if infinite */
|
||||
protected final Node test;
|
||||
|
||||
/** Loop body */
|
||||
protected final Block body;
|
||||
|
||||
/** Can control flow escape from loop, e.g. through breaks or continues to outer loops? */
|
||||
protected final boolean controlFlowEscapes;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param test test, or null if infinite loop
|
||||
* @param body loop body
|
||||
* @param controlFlowEscapes controlFlowEscapes
|
||||
*/
|
||||
protected LoopNode(final Source source, final long token, final int finish, final Node test, final Block body, final boolean controlFlowEscapes) {
|
||||
super(source, token, finish, new Label("while_break"));
|
||||
this.continueLabel = new Label("while_continue");
|
||||
this.test = test;
|
||||
this.body = body;
|
||||
this.controlFlowEscapes = controlFlowEscapes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param loopNode loop node
|
||||
* @param test new test
|
||||
* @param body new body
|
||||
* @param controlFlowEscapes controlFlowEscapes
|
||||
*/
|
||||
protected LoopNode(final LoopNode loopNode, final Node test, final Block body, final boolean controlFlowEscapes) {
|
||||
super(loopNode);
|
||||
this.continueLabel = new Label(loopNode.continueLabel);
|
||||
this.test = test;
|
||||
this.body = body;
|
||||
this.controlFlowEscapes = controlFlowEscapes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Node ensureUniqueLabels(final LexicalContext lc);
|
||||
|
||||
/**
|
||||
* Does the control flow escape from this loop, i.e. through breaks or
|
||||
* continues to outer loops?
|
||||
* @return true if control flow escapes
|
||||
*/
|
||||
public boolean controlFlowEscapes() {
|
||||
return controlFlowEscapes;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isTerminal() {
|
||||
if (!mustEnter()) {
|
||||
return false;
|
||||
}
|
||||
//must enter but control flow may escape - then not terminal
|
||||
if (controlFlowEscapes) {
|
||||
return false;
|
||||
}
|
||||
//must enter, but body ends with return - then terminal
|
||||
if (body.isTerminal()) {
|
||||
return true;
|
||||
}
|
||||
//no breaks or returns, it is still terminal if we can never exit
|
||||
return test == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conservative check: does this loop have to be entered?
|
||||
* @return true if body will execute at least once
|
||||
*/
|
||||
public abstract boolean mustEnter();
|
||||
|
||||
/**
|
||||
* Get the continue label for this while node, i.e. location to go to on continue
|
||||
* @return continue label
|
||||
*/
|
||||
public Label getContinueLabel() {
|
||||
return continueLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Label> getLabels() {
|
||||
return Arrays.asList(breakLabel, continueLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoop() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body for this for node
|
||||
* @return the body
|
||||
*/
|
||||
public abstract Block getBody();
|
||||
|
||||
/**
|
||||
* @param lc lexical context
|
||||
* @param body new body
|
||||
* @return new for node if changed or existing if not
|
||||
*/
|
||||
public abstract LoopNode setBody(final LexicalContext lc, final Block body);
|
||||
|
||||
/**
|
||||
* Get the test for this for node
|
||||
* @return the test
|
||||
*/
|
||||
public abstract Node getTest();
|
||||
|
||||
/**
|
||||
* Set the test for this for node
|
||||
*
|
||||
* @param lc lexical context
|
||||
* @param test new test
|
||||
* @return same or new node depending on if test was changed
|
||||
*/
|
||||
public abstract LoopNode setTest(final LexicalContext lc, final Node test);
|
||||
|
||||
/**
|
||||
* Set the control flow escapes flag for this node.
|
||||
* TODO - integrate this with Lowering in a better way
|
||||
*
|
||||
* @param lc lexical context
|
||||
* @param controlFlowEscapes control flow escapes value
|
||||
* @return new loop node if changed otherwise the same
|
||||
*/
|
||||
public abstract LoopNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes);
|
||||
|
||||
}
|
@ -25,7 +25,9 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
@ -33,30 +35,17 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* Nodes are used to compose Abstract Syntax Trees.
|
||||
*
|
||||
*/
|
||||
public abstract class Node extends Location {
|
||||
/** Node symbol. */
|
||||
private Symbol symbol;
|
||||
|
||||
/** Start of source range. */
|
||||
protected int start;
|
||||
protected final int start;
|
||||
|
||||
/** End of source range. */
|
||||
protected int finish;
|
||||
|
||||
/** Has this node been resolved - i.e. emitted code already */
|
||||
private boolean isResolved;
|
||||
|
||||
/** Is this node terminal */
|
||||
private boolean isTerminal;
|
||||
|
||||
/** Is this a goto node */
|
||||
private boolean hasGoto;
|
||||
|
||||
/** Is this a discard */
|
||||
private boolean shouldDiscard;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -71,6 +60,21 @@ public abstract class Node extends Location {
|
||||
this.finish = finish;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source source
|
||||
* @param token token
|
||||
* @param start start
|
||||
* @param finish finish
|
||||
*/
|
||||
protected Node(final Source source, final long token, final int start, final int finish) {
|
||||
super(source, token);
|
||||
|
||||
this.start = start;
|
||||
this.finish = finish;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
@ -79,13 +83,9 @@ public abstract class Node extends Location {
|
||||
protected Node(final Node node) {
|
||||
super(node);
|
||||
|
||||
this.symbol = node.symbol;
|
||||
this.isResolved = node.isResolved;
|
||||
this.isTerminal = node.isTerminal;
|
||||
this.hasGoto = node.hasGoto;
|
||||
this.shouldDiscard = node.shouldDiscard;
|
||||
this.start = node.start;
|
||||
this.finish = node.finish;
|
||||
this.symbol = node.symbol;
|
||||
this.start = node.start;
|
||||
this.finish = node.finish;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,28 +155,6 @@ public abstract class Node extends Location {
|
||||
return Type.OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if code been generated for this node. Set isResolved if not.
|
||||
*
|
||||
* @return True if node has already been resolved.
|
||||
*/
|
||||
public boolean testResolved() {
|
||||
if (isResolved()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
setIsResolved(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the resolved flag.
|
||||
*/
|
||||
public void resetResolved() {
|
||||
setIsResolved(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a debug info node like LineNumberNode etc?
|
||||
*
|
||||
@ -187,72 +165,13 @@ public abstract class Node extends Location {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class used for node cloning
|
||||
* For reference copies - ensure that labels in the copy node are unique
|
||||
* using an appropriate copy constructor
|
||||
* @param lc lexical context
|
||||
* @return new node or same of no labels
|
||||
*/
|
||||
public static final class CopyState {
|
||||
private final IdentityHashMap<Node, Node> cloneMap = new IdentityHashMap<>();
|
||||
|
||||
/**
|
||||
* Find existing or create new copy of the node.
|
||||
*
|
||||
* @param node Node to copy.
|
||||
*
|
||||
* @return New object.
|
||||
*/
|
||||
public Node existingOrCopy(final Node node) {
|
||||
if (node != null) {
|
||||
Node copy = cloneMap.get(node);
|
||||
|
||||
if (copy == null) {
|
||||
copy = node.copy(this);
|
||||
cloneMap.put(node, copy);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find existing or use old copy of the node.
|
||||
*
|
||||
* @param node Node to copy.
|
||||
*
|
||||
* @return new object.
|
||||
*/
|
||||
public Node existingOrSame(final Node node) {
|
||||
if (node != null) {
|
||||
Node copy = cloneMap.get(node);
|
||||
|
||||
if (copy == null) {
|
||||
copy = node;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep copy the node.
|
||||
*
|
||||
* @return Deep copy of the Node.
|
||||
*/
|
||||
public final Node copy() {
|
||||
return copy(new CopyState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep copy the node.
|
||||
*
|
||||
* @param cs CopyState passed around to re-use certain nodes.
|
||||
* @return Deep copy of the Node.
|
||||
*/
|
||||
protected Node copy(final CopyState cs) {
|
||||
return cs.existingOrCopy(this);
|
||||
public Node ensureUniqueLabels(final LexicalContext lc) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -283,35 +202,7 @@ public abstract class Node extends Location {
|
||||
* @return true if terminal
|
||||
*/
|
||||
public boolean hasTerminalFlags() {
|
||||
return isTerminal || hasGoto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the terminal flags state of a node to another node
|
||||
*
|
||||
* @param other source node
|
||||
*/
|
||||
public void copyTerminalFlags(final Node other) {
|
||||
isTerminal = other.isTerminal;
|
||||
hasGoto = other.hasGoto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the return value of this expression should be discarded
|
||||
* @return true if return value is discarded
|
||||
*/
|
||||
public boolean shouldDiscard() {
|
||||
return shouldDiscard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter that determines whether this node's return value should be discarded
|
||||
* or not
|
||||
*
|
||||
* @param shouldDiscard true if return value is discarded, false otherwise
|
||||
*/
|
||||
public void setDiscard(final boolean shouldDiscard) {
|
||||
this.shouldDiscard = shouldDiscard;
|
||||
return isTerminal() || hasGoto();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,29 +227,7 @@ public abstract class Node extends Location {
|
||||
* @return true if node has goto semantics
|
||||
*/
|
||||
public boolean hasGoto() {
|
||||
return hasGoto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag this node as having goto semantics as described in {@link Node#hasGoto()}
|
||||
*/
|
||||
public void setHasGoto() {
|
||||
this.hasGoto = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this node is resolved, i.e. code has been generated for it
|
||||
* @return true if node is resolved
|
||||
*/
|
||||
public boolean isResolved() {
|
||||
return isResolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag this node as resolved or not, i.e. code has been generated for it
|
||||
*/
|
||||
private void setIsResolved(boolean isResolved) {
|
||||
this.isResolved = isResolved;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -369,14 +238,6 @@ public abstract class Node extends Location {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set start position for node
|
||||
* @param start start position
|
||||
*/
|
||||
public void setStart(final int start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Symbol the compiler has assigned to this Node. The symbol
|
||||
* is the place where it's expression value is stored after evaluation
|
||||
@ -404,17 +265,29 @@ public abstract class Node extends Location {
|
||||
* @return true if this node is terminal
|
||||
*/
|
||||
public boolean isTerminal() {
|
||||
return isTerminal;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to be a terminal node, i.e. it terminates control flow as described
|
||||
* in {@link Node#isTerminal()}
|
||||
*
|
||||
* @param isTerminal true if this is a terminal node, false otherwise
|
||||
*/
|
||||
public void setIsTerminal(final boolean isTerminal) {
|
||||
this.isTerminal = isTerminal;
|
||||
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
|
||||
static <T extends Node> List<T> accept(final NodeVisitor visitor, final Class<T> clazz, final List<T> list) {
|
||||
boolean changed = false;
|
||||
final List<T> newList = new ArrayList<>();
|
||||
|
||||
for (final Node node : list) {
|
||||
final T newNode = clazz.cast(node.accept(visitor));
|
||||
if (newNode != node) {
|
||||
changed = true;
|
||||
}
|
||||
newList.add(newNode);
|
||||
}
|
||||
|
||||
return changed ? newList : list;
|
||||
}
|
||||
|
||||
static <T extends LexicalContextNode> T replaceInLexicalContext(final LexicalContext lc, final T oldNode, final T newNode) {
|
||||
if (lc != null) {
|
||||
lc.replace(oldNode, newNode);
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
}
|
||||
|
@ -25,16 +25,18 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of an object literal.
|
||||
*/
|
||||
public class ObjectNode extends Node {
|
||||
@Immutable
|
||||
public final class ObjectNode extends Node {
|
||||
|
||||
/** Literal elements. */
|
||||
private final List<Node> elements;
|
||||
@ -49,35 +51,18 @@ public class ObjectNode extends Node {
|
||||
*/
|
||||
public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
private ObjectNode(final ObjectNode objectNode, final CopyState cs) {
|
||||
private ObjectNode(final ObjectNode objectNode, final List<Node> elements) {
|
||||
super(objectNode);
|
||||
|
||||
final List<Node> newElements = new ArrayList<>();
|
||||
|
||||
for (final Node element : objectNode.elements) {
|
||||
newElements.add(cs.existingOrCopy(element));
|
||||
}
|
||||
|
||||
this.elements = newElements;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new ObjectNode(this, cs);
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterObjectNode(this) != null) {
|
||||
for (int i = 0, count = elements.size(); i < count; i++) {
|
||||
elements.set(i, elements.get(i).accept(visitor));
|
||||
}
|
||||
|
||||
return visitor.leaveObjectNode(this);
|
||||
if (visitor.enterObjectNode(this)) {
|
||||
return visitor.leaveObjectNode(setElements(Node.accept(visitor, Node.class, elements)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -112,4 +97,11 @@ public class ObjectNode extends Node {
|
||||
public List<Node> getElements() {
|
||||
return Collections.unmodifiableList(elements);
|
||||
}
|
||||
|
||||
private ObjectNode setElements(final List<Node> elements) {
|
||||
if (this.elements == elements) {
|
||||
return this;
|
||||
}
|
||||
return new ObjectNode(this, elements);
|
||||
}
|
||||
}
|
||||
|
@ -25,28 +25,27 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Reference;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of an object literal property.
|
||||
*/
|
||||
public class PropertyNode extends Node {
|
||||
@Immutable
|
||||
public final class PropertyNode extends Node {
|
||||
|
||||
/** Property key. */
|
||||
private PropertyKey key;
|
||||
private final PropertyKey key;
|
||||
|
||||
/** Property value. */
|
||||
private Node value;
|
||||
private final Node value;
|
||||
|
||||
/** Property getter. */
|
||||
@Reference
|
||||
private Node getter;
|
||||
private final FunctionNode getter;
|
||||
|
||||
/** Property getter. */
|
||||
@Reference
|
||||
private Node setter;
|
||||
private final FunctionNode setter;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -56,26 +55,23 @@ public class PropertyNode extends Node {
|
||||
* @param finish finish
|
||||
* @param key the key of this property
|
||||
* @param value the value of this property
|
||||
* @param getter getter function body
|
||||
* @param setter setter function body
|
||||
*/
|
||||
public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value) {
|
||||
public PropertyNode(final Source source, final long token, final int finish, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
private PropertyNode(final PropertyNode propertyNode, final CopyState cs) {
|
||||
private PropertyNode(final PropertyNode propertyNode, final PropertyKey key, final Node value, final FunctionNode getter, final FunctionNode setter) {
|
||||
super(propertyNode);
|
||||
|
||||
this.key = (PropertyKey)cs.existingOrCopy((Node)propertyNode.key);
|
||||
this.value = cs.existingOrCopy(propertyNode.value);
|
||||
this.getter = cs.existingOrSame(propertyNode.getter);
|
||||
this.setter = cs.existingOrSame(propertyNode.setter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new PropertyNode(this, cs);
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,22 +84,12 @@ public class PropertyNode extends Node {
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterPropertyNode(this) != null) {
|
||||
key = (PropertyKey)((Node)key).accept(visitor);
|
||||
|
||||
if (value != null) {
|
||||
value = value.accept(visitor);
|
||||
}
|
||||
|
||||
if (getter != null) {
|
||||
getter = getter.accept(visitor);
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
setter = setter.accept(visitor);
|
||||
}
|
||||
|
||||
return visitor.leavePropertyNode(this);
|
||||
if (visitor.enterPropertyNode(this)) {
|
||||
return visitor.leavePropertyNode(
|
||||
setKey((PropertyKey)((Node)key).accept(visitor)).
|
||||
setValue(value == null ? null : value.accept(visitor)).
|
||||
setGetter(getter == null ? null : (FunctionNode)getter.accept(visitor)).
|
||||
setSetter(setter == null ? null : (FunctionNode)setter.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -136,16 +122,20 @@ public class PropertyNode extends Node {
|
||||
* Get the getter for this property
|
||||
* @return getter or null if none exists
|
||||
*/
|
||||
public Node getGetter() {
|
||||
public FunctionNode getGetter() {
|
||||
return getter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the getter of this property, null if none
|
||||
* @param getter getter
|
||||
* @return same node or new node if state changed
|
||||
*/
|
||||
public void setGetter(final Node getter) {
|
||||
this.getter = getter;
|
||||
public PropertyNode setGetter(final FunctionNode getter) {
|
||||
if (this.getter == getter) {
|
||||
return this;
|
||||
}
|
||||
return new PropertyNode(this, key, value, getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,20 +146,31 @@ public class PropertyNode extends Node {
|
||||
return (Node)key;
|
||||
}
|
||||
|
||||
private PropertyNode setKey(final PropertyKey key) {
|
||||
if (this.key == key) {
|
||||
return this;
|
||||
}
|
||||
return new PropertyNode(this, key, value, getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the setter for this property
|
||||
* @return setter or null if none exists
|
||||
*/
|
||||
public Node getSetter() {
|
||||
public FunctionNode getSetter() {
|
||||
return setter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the setter for this property, null if none
|
||||
* @param setter setter
|
||||
* @return same node or new node if state changed
|
||||
*/
|
||||
public void setSetter(final Node setter) {
|
||||
this.setter = setter;
|
||||
public PropertyNode setSetter(final FunctionNode setter) {
|
||||
if (this.setter == setter) {
|
||||
return this;
|
||||
}
|
||||
return new PropertyNode(this, key, value, getter, setter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,8 +184,12 @@ public class PropertyNode extends Node {
|
||||
/**
|
||||
* Set the value of this property
|
||||
* @param value new value
|
||||
* @return same node or new node if state changed
|
||||
*/
|
||||
public void setValue(final Node value) {
|
||||
this.value = value;
|
||||
}
|
||||
public PropertyNode setValue(final Node value) {
|
||||
if (this.value == value) {
|
||||
return this;
|
||||
}
|
||||
return new PropertyNode(this, key, value, getter, setter);
|
||||
}
|
||||
}
|
||||
|
@ -27,22 +27,17 @@ package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.parser.TokenType.RETURN;
|
||||
import static jdk.nashorn.internal.parser.TokenType.YIELD;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for RETURN or YIELD statements.
|
||||
*
|
||||
*/
|
||||
@Immutable
|
||||
public class ReturnNode extends Node {
|
||||
/** Optional expression. */
|
||||
private Node expression;
|
||||
|
||||
/** Try chain. */
|
||||
@Ignore
|
||||
private final TryNode tryChain;
|
||||
private final Node expression;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -51,27 +46,20 @@ public class ReturnNode extends Node {
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param expression expression to return
|
||||
* @param tryChain surrounding try chain.
|
||||
*/
|
||||
public ReturnNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
|
||||
public ReturnNode(final Source source, final long token, final int finish, final Node expression) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.expression = expression;
|
||||
this.tryChain = tryChain;
|
||||
|
||||
setIsTerminal(true);
|
||||
}
|
||||
|
||||
private ReturnNode(final ReturnNode returnNode, final CopyState cs) {
|
||||
private ReturnNode(final ReturnNode returnNode, final Node expression) {
|
||||
super(returnNode);
|
||||
|
||||
this.expression = cs.existingOrCopy(returnNode.expression);
|
||||
this.tryChain = (TryNode)cs.existingOrSame(returnNode.tryChain);
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new ReturnNode(this, cs);
|
||||
public boolean isTerminal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,11 +88,10 @@ public class ReturnNode extends Node {
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterReturnNode(this) != null) {
|
||||
if (visitor.enterReturnNode(this)) {
|
||||
if (expression != null) {
|
||||
expression = expression.accept(visitor);
|
||||
return visitor.leaveReturnNode(setExpression(expression.accept(visitor)));
|
||||
}
|
||||
|
||||
return visitor.leaveReturnNode(this);
|
||||
}
|
||||
|
||||
@ -121,25 +108,6 @@ public class ReturnNode extends Node {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other instanceof ReturnNode) {
|
||||
final ReturnNode otherReturn = (ReturnNode)other;
|
||||
if (hasExpression() != otherReturn.hasExpression()) {
|
||||
return false;
|
||||
} else if (hasExpression()) {
|
||||
return otherReturn.getExpression().equals(getExpression());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0x4711_17 ^ (expression == null ? 0 : expression.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expression this node returns
|
||||
* @return return expression, or null if void return
|
||||
@ -151,16 +119,13 @@ public class ReturnNode extends Node {
|
||||
/**
|
||||
* Reset the expression this node returns
|
||||
* @param expression new expression, or null if void return
|
||||
* @return new or same return node
|
||||
*/
|
||||
public void setExpression(final Node expression) {
|
||||
this.expression = expression;
|
||||
public ReturnNode setExpression(final Node expression) {
|
||||
if (this.expression == expression) {
|
||||
return this;
|
||||
}
|
||||
return new ReturnNode(this, expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the surrounding try chain for this return node
|
||||
* @return try chain
|
||||
*/
|
||||
public TryNode getTryChain() {
|
||||
return tryChain;
|
||||
}
|
||||
}
|
||||
|
@ -30,14 +30,15 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for a runtime call.
|
||||
*
|
||||
*/
|
||||
@Immutable
|
||||
public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
|
||||
/**
|
||||
@ -271,10 +272,10 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
private final List<Node> args;
|
||||
|
||||
/** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */
|
||||
private Type callSiteType;
|
||||
private final Type callSiteType;
|
||||
|
||||
/** is final - i.e. may not be removed again, lower in the code pipeline */
|
||||
private boolean isFinal;
|
||||
private final boolean isFinal;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -290,6 +291,17 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
this.callSiteType = null;
|
||||
this.isFinal = false;
|
||||
}
|
||||
|
||||
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final Type callSiteType, final boolean isFinal, final List<Node> args) {
|
||||
super(runtimeNode);
|
||||
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
this.callSiteType = callSiteType;
|
||||
this.isFinal = isFinal;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -326,8 +338,10 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
public RuntimeNode(final Node parent, final Request request, final List<Node> args) {
|
||||
super(parent);
|
||||
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
this.callSiteType = null;
|
||||
this.isFinal = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -350,20 +364,6 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
this(parent, request, parent.lhs(), parent.rhs());
|
||||
}
|
||||
|
||||
private RuntimeNode(final RuntimeNode runtimeNode, final CopyState cs) {
|
||||
super(runtimeNode);
|
||||
|
||||
final List<Node> newArgs = new ArrayList<>();
|
||||
|
||||
for (final Node arg : runtimeNode.args) {
|
||||
newArgs.add(cs.existingOrCopy(arg));
|
||||
}
|
||||
|
||||
this.request = runtimeNode.request;
|
||||
this.args = newArgs;
|
||||
this.callSiteType = runtimeNode.callSiteType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this node final - i.e. it can never be replaced with other nodes again
|
||||
* @return true if final
|
||||
@ -374,14 +374,14 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
|
||||
/**
|
||||
* Flag this node as final - i.e it may never be replaced with other nodes again
|
||||
* @param isFinal is the node final, i.e. can not be removed and replaced by a less generic one later in codegen
|
||||
* @return same runtime node if already final, otherwise a new one
|
||||
*/
|
||||
public void setIsFinal() {
|
||||
this.isFinal = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new RuntimeNode(this, cs);
|
||||
public RuntimeNode setIsFinal(final boolean isFinal) {
|
||||
if (this.isFinal == isFinal) {
|
||||
return this;
|
||||
}
|
||||
return new RuntimeNode(this, request, callSiteType, isFinal, args);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -394,8 +394,10 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
|
||||
@Override
|
||||
public RuntimeNode setType(final Type type) {
|
||||
this.callSiteType = type;
|
||||
return this;
|
||||
if (this.callSiteType == type) {
|
||||
return this;
|
||||
}
|
||||
return new RuntimeNode(this, request, type, isFinal, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -409,12 +411,12 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterRuntimeNode(this) != null) {
|
||||
for (int i = 0, count = args.size(); i < count; i++) {
|
||||
args.set(i, args.get(i).accept(visitor));
|
||||
if (visitor.enterRuntimeNode(this)) {
|
||||
final List<Node> newArgs = new ArrayList<>();
|
||||
for (final Node arg : args) {
|
||||
newArgs.add(arg.accept(visitor));
|
||||
}
|
||||
|
||||
return visitor.leaveRuntimeNode(this);
|
||||
return visitor.leaveRuntimeNode(setArgs(newArgs));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -449,6 +451,13 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
|
||||
return Collections.unmodifiableList(args);
|
||||
}
|
||||
|
||||
private RuntimeNode setArgs(final List<Node> args) {
|
||||
if (this.args == args) {
|
||||
return this;
|
||||
}
|
||||
return new RuntimeNode(this, request, callSiteType, isFinal, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request that this runtime node implements
|
||||
* @return the request
|
||||
|
@ -25,99 +25,65 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.codegen.MethodEmitter;
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Reference;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
|
||||
|
||||
/**
|
||||
* Node indicating code is split across classes.
|
||||
*/
|
||||
public class SplitNode extends Node {
|
||||
@Immutable
|
||||
public class SplitNode extends LexicalContextNode {
|
||||
/** Split node method name. */
|
||||
private final String name;
|
||||
|
||||
/** Compilation unit. */
|
||||
private CompileUnit compileUnit;
|
||||
|
||||
/** Method emitter for current method. */
|
||||
private MethodEmitter method;
|
||||
|
||||
/** Method emitter for caller method. */
|
||||
private MethodEmitter caller;
|
||||
|
||||
/** Containing function. */
|
||||
@Reference
|
||||
private final FunctionNode functionNode;
|
||||
|
||||
/** A list of target labels in parent methods this split node may encounter. */
|
||||
@Ignore
|
||||
private final List<Label> externalTargets;
|
||||
|
||||
/** True if this split node or any of its children contain a return statement. */
|
||||
private boolean hasReturn;
|
||||
private final CompileUnit compileUnit;
|
||||
|
||||
/** Body of split code. */
|
||||
@Ignore
|
||||
private Node body;
|
||||
private final Node body;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param name name of split node
|
||||
* @param functionNode function node to split in
|
||||
* @param body body of split code
|
||||
* @param name name of split node
|
||||
* @param body body of split code
|
||||
* @param compileUnit compile unit to use for the body
|
||||
*/
|
||||
public SplitNode(final String name, final FunctionNode functionNode, final Node body) {
|
||||
public SplitNode(final String name, final Node body, final CompileUnit compileUnit) {
|
||||
super(body.getSource(), body.getToken(), body.getFinish());
|
||||
|
||||
this.name = name;
|
||||
this.functionNode = functionNode;
|
||||
this.body = body;
|
||||
this.externalTargets = new ArrayList<>();
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
this.compileUnit = compileUnit;
|
||||
}
|
||||
|
||||
private SplitNode(final SplitNode splitNode, final CopyState cs) {
|
||||
private SplitNode(final SplitNode splitNode, final Node body) {
|
||||
super(splitNode);
|
||||
|
||||
this.name = splitNode.name;
|
||||
this.functionNode = (FunctionNode)cs.existingOrSame(splitNode.functionNode);
|
||||
this.body = cs.existingOrCopy(splitNode.body);
|
||||
this.externalTargets = new ArrayList<>();
|
||||
this.name = splitNode.name;
|
||||
this.body = body;
|
||||
this.compileUnit = splitNode.compileUnit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new SplitNode(this, cs);
|
||||
/**
|
||||
* Get the body for this split node - i.e. the actual code it encloses
|
||||
* @return body for split node
|
||||
*/
|
||||
public Node getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit();
|
||||
final MethodEmitter saveMethod = visitor.getCurrentMethodEmitter();
|
||||
|
||||
setCaller(saveMethod);
|
||||
|
||||
visitor.setCurrentCompileUnit(getCompileUnit());
|
||||
visitor.setCurrentMethodEmitter(getMethodEmitter());
|
||||
|
||||
try {
|
||||
if (visitor.enterSplitNode(this) != null) {
|
||||
body = body.accept(visitor);
|
||||
|
||||
return visitor.leaveSplitNode(this);
|
||||
}
|
||||
} finally {
|
||||
visitor.setCurrentCompileUnit(saveCompileUnit);
|
||||
visitor.setCurrentMethodEmitter(saveMethod);
|
||||
private SplitNode setBody(final LexicalContext lc, final Node body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new SplitNode(this, body));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterSplitNode(this)) {
|
||||
return visitor.leaveSplitNode(setBody(lc, body.accept(visitor)));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -129,22 +95,6 @@ public class SplitNode extends Node {
|
||||
body.toString(sb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the method emitter of the caller for this split node
|
||||
* @return caller method emitter
|
||||
*/
|
||||
public MethodEmitter getCaller() {
|
||||
return caller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the caller method emitter for this split node
|
||||
* @param caller method emitter
|
||||
*/
|
||||
public void setCaller(final MethodEmitter caller) {
|
||||
this.caller = caller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name for this split node
|
||||
* @return name
|
||||
@ -161,67 +111,4 @@ public class SplitNode extends Node {
|
||||
return compileUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the compile unit for this split node
|
||||
* @param compileUnit compile unit
|
||||
*/
|
||||
public void setCompileUnit(final CompileUnit compileUnit) {
|
||||
this.compileUnit = compileUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the method emitter for this split node
|
||||
* @return method emitter
|
||||
*/
|
||||
public MethodEmitter getMethodEmitter() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the method emitter for this split node
|
||||
* @param method method emitter
|
||||
*/
|
||||
public void setMethodEmitter(final MethodEmitter method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the function node this SplitNode splits
|
||||
* @return function node reference
|
||||
*/
|
||||
public FunctionNode getFunctionNode() {
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the external targets for this SplitNode
|
||||
* @return list of external targets
|
||||
*/
|
||||
public List<Label> getExternalTargets() {
|
||||
return Collections.unmodifiableList(externalTargets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an external target for this SplitNode
|
||||
* @param targetLabel target label
|
||||
*/
|
||||
public void addExternalTarget(final Label targetLabel) {
|
||||
externalTargets.add(targetLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this SplitNode returns a value
|
||||
* @return true if return
|
||||
*/
|
||||
public boolean hasReturn() {
|
||||
return hasReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this SplitNode returns a value or not
|
||||
* @param hasReturn true if return exists, false otherwise
|
||||
*/
|
||||
public void setHasReturn(final boolean hasReturn) {
|
||||
this.hasReturn = hasReturn;
|
||||
}
|
||||
}
|
||||
|
@ -28,73 +28,84 @@ package jdk.nashorn.internal.ir;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of a SWITCH statement.
|
||||
*/
|
||||
public class SwitchNode extends BreakableNode {
|
||||
@Immutable
|
||||
public final class SwitchNode extends BreakableNode {
|
||||
/** Switch expression. */
|
||||
private Node expression;
|
||||
private final Node expression;
|
||||
|
||||
/** Switch cases. */
|
||||
private final List<CaseNode> cases;
|
||||
|
||||
/** Switch default index. */
|
||||
private final int defaultCaseIndex;
|
||||
|
||||
/** Tag symbol. */
|
||||
private Symbol tag;
|
||||
|
||||
/** Switch cases. */
|
||||
private List<CaseNode> cases;
|
||||
|
||||
/** Switch default. */
|
||||
@Ignore //points to one of the members in the list above, don't traverse twice
|
||||
private CaseNode defaultCase;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param expression switch expression
|
||||
* @param cases cases
|
||||
* @param defaultCase the default case node - null if none, otherwise has to be present in cases list
|
||||
*/
|
||||
public SwitchNode(final Source source, final long token, final int finish) {
|
||||
super(source, token, finish);
|
||||
this.breakLabel = new Label("switch_break");
|
||||
public SwitchNode(final Source source, final long token, final int finish, final Node expression, final List<CaseNode> cases, final CaseNode defaultCase) {
|
||||
super(source, token, finish, new Label("switch_break"));
|
||||
this.expression = expression;
|
||||
this.cases = cases;
|
||||
this.defaultCaseIndex = defaultCase == null ? -1 : cases.indexOf(defaultCase);
|
||||
}
|
||||
|
||||
private SwitchNode(final SwitchNode switchNode, final CopyState cs) {
|
||||
private SwitchNode(final SwitchNode switchNode, final Node expression, final List<CaseNode> cases, final int defaultCase) {
|
||||
super(switchNode);
|
||||
this.expression = expression;
|
||||
this.cases = cases;
|
||||
this.defaultCaseIndex = defaultCase;
|
||||
this.tag = switchNode.getTag(); //TODO are symbols inhereted as references?
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node ensureUniqueLabels(final LexicalContext lc) {
|
||||
final List<CaseNode> newCases = new ArrayList<>();
|
||||
|
||||
for (final CaseNode caseNode : switchNode.getCases()) {
|
||||
newCases.add((CaseNode)cs.existingOrCopy(caseNode));
|
||||
for (final CaseNode caseNode : cases) {
|
||||
newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody()));
|
||||
}
|
||||
|
||||
this.expression = cs.existingOrCopy(switchNode.getExpression());
|
||||
this.tag = switchNode.getTag();
|
||||
this.cases = newCases;
|
||||
this.defaultCase = (CaseNode)cs.existingOrCopy(switchNode.getDefaultCase());
|
||||
this.breakLabel = new Label(switchNode.getBreakLabel());
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new SwitchNode(this, cs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterSwitchNode(this) != null) {
|
||||
expression = expression.accept(visitor);
|
||||
|
||||
for (int i = 0, count = cases.size(); i < count; i++) {
|
||||
cases.set(i, (CaseNode)cases.get(i).accept(visitor));
|
||||
public boolean isTerminal() {
|
||||
//there must be a default case, and that including all other cases must terminate
|
||||
if (!cases.isEmpty() && defaultCaseIndex != -1) {
|
||||
for (final CaseNode caseNode : cases) {
|
||||
if (!caseNode.isTerminal()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
//the default case is in the cases list and should not be explicitly traversed!
|
||||
}
|
||||
|
||||
return visitor.leaveSwitchNode(this);
|
||||
@Override
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterSwitchNode(this)) {
|
||||
return visitor.leaveSwitchNode(
|
||||
setExpression(visitor.getLexicalContext(), expression.accept(visitor)).
|
||||
setCases(visitor.getLexicalContext(), Node.accept(visitor, CaseNode.class, cases), defaultCaseIndex));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -107,6 +118,14 @@ public class SwitchNode extends BreakableNode {
|
||||
sb.append(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the case node that is default case
|
||||
* @return default case or null if none
|
||||
*/
|
||||
public CaseNode getDefaultCase() {
|
||||
return defaultCaseIndex == -1 ? null : cases.get(defaultCaseIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cases in this switch
|
||||
* @return a list of case nodes
|
||||
@ -115,28 +134,34 @@ public class SwitchNode extends BreakableNode {
|
||||
return Collections.unmodifiableList(cases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace case nodes with new list. the cases have to be the same
|
||||
* and the default case index the same. This is typically used
|
||||
* by NodeVisitors who perform operations on every case node
|
||||
* @param lc lexical context
|
||||
* @param cases list of cases
|
||||
* @return new switcy node or same if no state was changed
|
||||
*/
|
||||
public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases) {
|
||||
return setCases(lc, cases, defaultCaseIndex);
|
||||
}
|
||||
|
||||
private SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final int defaultCaseIndex) {
|
||||
if (this.cases == cases) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or reset the list of cases in this switch
|
||||
* @param lc lexical context
|
||||
* @param cases a list of cases, case nodes
|
||||
* @param defaultCase a case in the list that is the default - must be in the list or class will assert
|
||||
* @return new switch node or same if no state was changed
|
||||
*/
|
||||
public void setCases(final List<CaseNode> cases) {
|
||||
this.cases = cases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default case for this switch
|
||||
* @return default case node
|
||||
*/
|
||||
public CaseNode getDefaultCase() {
|
||||
return defaultCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default case for this switch
|
||||
* @param defaultCase default case node
|
||||
*/
|
||||
public void setDefaultCase(final CaseNode defaultCase) {
|
||||
this.defaultCase = defaultCase;
|
||||
public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) {
|
||||
return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,10 +174,15 @@ public class SwitchNode extends BreakableNode {
|
||||
|
||||
/**
|
||||
* Set or reset the expression to switch on
|
||||
* @param lc lexical context
|
||||
* @param expression switch expression
|
||||
* @return new switch node or same if no state was changed
|
||||
*/
|
||||
public void setExpression(final Node expression) {
|
||||
this.expression = expression;
|
||||
public SwitchNode setExpression(final LexicalContext lc, final Node expression) {
|
||||
if (this.expression == expression) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, cases, defaultCaseIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,8 +29,10 @@ import java.io.PrintWriter;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.Debug;
|
||||
import jdk.nashorn.internal.runtime.options.Options;
|
||||
|
||||
/**
|
||||
@ -63,6 +65,8 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
public static final int IS_LET = 1 << 8;
|
||||
/** Is this an internal symbol, never represented explicitly in source code */
|
||||
public static final int IS_INTERNAL = 1 << 9;
|
||||
/** Is this a function self-reference symbol */
|
||||
public static final int IS_FUNCTION_SELF = 1 << 10;
|
||||
|
||||
/** Null or name identifying symbol. */
|
||||
private final String name;
|
||||
@ -70,12 +74,6 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
/** Symbol flags. */
|
||||
private int flags;
|
||||
|
||||
/** Defining node. */
|
||||
private Node node;
|
||||
|
||||
/** Definition block. */
|
||||
private final Block block;
|
||||
|
||||
/** Type of symbol. */
|
||||
private Type type;
|
||||
|
||||
@ -121,16 +119,12 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
*
|
||||
* @param name name of symbol
|
||||
* @param flags symbol flags
|
||||
* @param node node this symbol is in
|
||||
* @param block block this symbol is in
|
||||
* @param type type of this symbol
|
||||
* @param slot bytecode slot for this symbol
|
||||
*/
|
||||
protected Symbol(final String name, final int flags, final Node node, final Block block, final Type type, final int slot) {
|
||||
protected Symbol(final String name, final int flags, final Type type, final int slot) {
|
||||
this.name = name;
|
||||
this.flags = flags;
|
||||
this.node = node;
|
||||
this.block = block;
|
||||
this.type = type;
|
||||
this.slot = slot;
|
||||
this.fieldIndex = -1;
|
||||
@ -142,11 +136,9 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
*
|
||||
* @param name name of symbol
|
||||
* @param flags symbol flags
|
||||
* @param node node this symbol is in
|
||||
* @param block block this symbol is in
|
||||
*/
|
||||
public Symbol(final String name, final int flags, final Node node, final Block block) {
|
||||
this(name, flags, node, block, Type.UNKNOWN, -1);
|
||||
public Symbol(final String name, final int flags) {
|
||||
this(name, flags, Type.UNKNOWN, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,7 +149,7 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
* @param type type of this symbol
|
||||
*/
|
||||
public Symbol(final String name, final int flags, final Type type) {
|
||||
this(name, flags, null, null, type, -1);
|
||||
this(name, flags, type, -1);
|
||||
}
|
||||
|
||||
private static String align(final String string, final int max) {
|
||||
@ -269,20 +261,6 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
return type.isCategory2() ? 2 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!(other instanceof Symbol)) {
|
||||
return false;
|
||||
}
|
||||
final Symbol symbol = (Symbol) other;
|
||||
return name.equals(symbol.name) && block.equals(symbol.block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode() ^ block.hashCode();
|
||||
}
|
||||
|
||||
private static String type(final String desc) {
|
||||
switch (desc.charAt(desc.length() - 1)) {
|
||||
case ';':
|
||||
@ -371,14 +349,14 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
/**
|
||||
* Flag this symbol as scope as described in {@link Symbol#isScope()}
|
||||
*/
|
||||
public void setIsScope() {
|
||||
/**
|
||||
* Flag this symbol as scope as described in {@link Symbol#isScope()}
|
||||
*/
|
||||
public void setIsScope() {
|
||||
if (!isScope()) {
|
||||
trace("SET IS SCOPE");
|
||||
}
|
||||
flags |= IS_SCOPE;
|
||||
if(!isGlobal()) {
|
||||
getBlock().setNeedsScope();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -478,11 +456,11 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the block in which the symbol is defined
|
||||
* @return a block
|
||||
* Flag this symbol as a function's self-referencing symbol.
|
||||
* @return true if this symbol as a function's self-referencing symbol.
|
||||
*/
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
public boolean isFunctionSelf() {
|
||||
return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -492,7 +470,7 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
* @return field index
|
||||
*/
|
||||
public int getFieldIndex() {
|
||||
assert fieldIndex != -1 : "fieldIndex must be initialized";
|
||||
assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex;
|
||||
return fieldIndex;
|
||||
}
|
||||
|
||||
@ -503,7 +481,6 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
* @param fieldIndex field index - a positive integer
|
||||
*/
|
||||
public void setFieldIndex(final int fieldIndex) {
|
||||
assert this.fieldIndex == -1 : "fieldIndex must be initialized only once";
|
||||
this.fieldIndex = fieldIndex;
|
||||
}
|
||||
|
||||
@ -523,22 +500,6 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node this symbol stores the result for
|
||||
* @return node
|
||||
*/
|
||||
public Node getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the node this symbol stores the result for
|
||||
* @param node node
|
||||
*/
|
||||
public void setNode(final Node node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this symbol
|
||||
* @return symbol name
|
||||
@ -616,18 +577,25 @@ public final class Symbol implements Comparable<Symbol> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this symbol is in the global scope, i.e. it is on the outermost level
|
||||
* in the script
|
||||
* @return true if this this is a global scope symbol
|
||||
* From a lexical context, set this symbol as needing scope, which
|
||||
* will set flags for the defining block that will be written when
|
||||
* block is popped from the lexical context stack, used by codegen
|
||||
* when flags need to be tagged, but block is in the
|
||||
* middle of evaluation and cannot be modified.
|
||||
*
|
||||
* @param lc lexical context
|
||||
* @param symbol symbol
|
||||
*/
|
||||
public boolean isTopLevel() {
|
||||
return block instanceof FunctionNode && ((FunctionNode) block).isProgram();
|
||||
public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
|
||||
symbol.setIsScope();
|
||||
if (!symbol.isGlobal()) {
|
||||
lc.setFlag(lc.getDefiningBlock(symbol), Block.NEEDS_SCOPE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void trace(final String desc) {
|
||||
if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) {
|
||||
Context.err("SYMBOL: '" + name + "' " + desc);
|
||||
Context.err(Debug.id(this) + " SYMBOL: '" + name + "' " + desc);
|
||||
if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
|
||||
new Throwable().printStackTrace(Context.getCurrentErr());
|
||||
}
|
||||
|
@ -25,15 +25,21 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* TernaryNode nodes represent three operand operations (?:).
|
||||
*/
|
||||
public class TernaryNode extends BinaryNode {
|
||||
@Immutable
|
||||
public final class TernaryNode extends Node {
|
||||
private final Node lhs;
|
||||
|
||||
private final Node rhs;
|
||||
|
||||
/** Third argument. */
|
||||
private Node third;
|
||||
private final Node third;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -45,43 +51,26 @@ public class TernaryNode extends BinaryNode {
|
||||
* @param third third node
|
||||
*/
|
||||
public TernaryNode(final Source source, final long token, final Node lhs, final Node rhs, final Node third) {
|
||||
super(source, token, lhs, rhs);
|
||||
|
||||
this.finish = third.getFinish();
|
||||
super(source, token, third.getFinish());
|
||||
this.lhs = lhs;
|
||||
this.rhs = rhs;
|
||||
this.third = third;
|
||||
}
|
||||
|
||||
private TernaryNode(final TernaryNode ternaryNode, final CopyState cs) {
|
||||
super(ternaryNode, cs);
|
||||
|
||||
this.third = cs.existingOrCopy(ternaryNode.third);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new TernaryNode(this, cs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return third.equals(((TernaryNode)other).third());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ third().hashCode();
|
||||
private TernaryNode(final TernaryNode ternaryNode, final Node lhs, final Node rhs, final Node third) {
|
||||
super(ternaryNode);
|
||||
this.lhs = lhs;
|
||||
this.rhs = rhs;
|
||||
this.third = third;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterTernaryNode(this) != null) {
|
||||
if (visitor.enterTernaryNode(this)) {
|
||||
final Node newLhs = lhs().accept(visitor);
|
||||
final Node newRhs = rhs().accept(visitor);
|
||||
final Node newThird = third.accept(visitor);
|
||||
return visitor.leaveTernaryNode((TernaryNode)setThird(newThird).setLHS(newLhs).setRHS(newRhs));
|
||||
return visitor.leaveTernaryNode(setThird(newThird).setLHS(newLhs).setRHS(newRhs));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -122,6 +111,22 @@ public class TernaryNode extends BinaryNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lhs node for this ternary expression, i.e. "x" in x ? y : z
|
||||
* @return a node
|
||||
*/
|
||||
public Node lhs() {
|
||||
return lhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rhs node for this ternary expression, i.e. "y" in x ? y : z
|
||||
* @return a node
|
||||
*/
|
||||
public Node rhs() {
|
||||
return rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "third" node for this ternary expression, i.e. "z" in x ? y : z
|
||||
* @return a node
|
||||
@ -130,15 +135,39 @@ public class TernaryNode extends BinaryNode {
|
||||
return third;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the left hand side expression for this node
|
||||
* @param lhs new left hand side expression
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public TernaryNode setLHS(final Node lhs) {
|
||||
if (this.lhs == lhs) {
|
||||
return this;
|
||||
}
|
||||
return new TernaryNode(this, lhs, rhs, third);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the right hand side expression for this node
|
||||
* @param rhs new left hand side expression
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public TernaryNode setRHS(final Node rhs) {
|
||||
if (this.rhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
return new TernaryNode(this, lhs, rhs, third);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z
|
||||
* @param third a node
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public TernaryNode setThird(final Node third) {
|
||||
if(this.third == third) return this;
|
||||
final TernaryNode n = (TernaryNode)clone();
|
||||
n.third = third;
|
||||
return n;
|
||||
if (this.third == third) {
|
||||
return this;
|
||||
}
|
||||
return new TernaryNode(this, lhs, rhs, third);
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,17 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for THROW statements.
|
||||
*/
|
||||
public class ThrowNode extends Node {
|
||||
@Immutable
|
||||
public final class ThrowNode extends Node {
|
||||
/** Exception expression. */
|
||||
private Node expression;
|
||||
|
||||
/** Try chain. */
|
||||
@Ignore
|
||||
private final TryNode tryChain;
|
||||
private final Node expression;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -47,26 +44,21 @@ public class ThrowNode extends Node {
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param expression expression to throw
|
||||
* @param tryChain surrounding try chain
|
||||
*/
|
||||
public ThrowNode(final Source source, final long token, final int finish, final Node expression, final TryNode tryChain) {
|
||||
public ThrowNode(final Source source, final long token, final int finish, final Node expression) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.expression = expression;
|
||||
this.tryChain = tryChain;
|
||||
setIsTerminal(true);
|
||||
}
|
||||
|
||||
private ThrowNode(final ThrowNode throwNode, final CopyState cs) {
|
||||
super(throwNode);
|
||||
|
||||
this.expression = cs.existingOrCopy(throwNode.expression);
|
||||
this.tryChain = (TryNode)cs.existingOrSame(throwNode.tryChain);
|
||||
private ThrowNode(final Node node, final Node expression) {
|
||||
super(node);
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new ThrowNode(this, cs);
|
||||
public boolean isTerminal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -75,9 +67,8 @@ public class ThrowNode extends Node {
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterThrowNode(this) != null) {
|
||||
setExpression(expression.accept(visitor));
|
||||
return visitor.leaveThrowNode(this);
|
||||
if (visitor.enterThrowNode(this)) {
|
||||
return visitor.leaveThrowNode(setExpression(expression.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -103,16 +94,13 @@ public class ThrowNode extends Node {
|
||||
/**
|
||||
* Reset the expression being thrown by this node
|
||||
* @param expression new expression
|
||||
* @return new or same thrownode
|
||||
*/
|
||||
public void setExpression(final Node expression) {
|
||||
this.expression = expression;
|
||||
public ThrowNode setExpression(final Node expression) {
|
||||
if (this.expression == expression) {
|
||||
return this;
|
||||
}
|
||||
return new ThrowNode(this, expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get surrounding tryChain for this node
|
||||
* @return try chain
|
||||
*/
|
||||
public TryNode getTryChain() {
|
||||
return tryChain;
|
||||
}
|
||||
}
|
||||
|
@ -28,30 +28,28 @@ package jdk.nashorn.internal.ir;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation of a TRY statement.
|
||||
*/
|
||||
public class TryNode extends Node {
|
||||
/** Try chain. */
|
||||
@Ignore //don't print, will be apparent from the AST
|
||||
private TryNode next;
|
||||
|
||||
@Immutable
|
||||
public final class TryNode extends Node {
|
||||
/** Try statements. */
|
||||
private Block body;
|
||||
private final Block body;
|
||||
|
||||
/** List of catch clauses. */
|
||||
private List<Block> catchBlocks;
|
||||
private final List<Block> catchBlocks;
|
||||
|
||||
/** Finally clause. */
|
||||
private Block finallyBody;
|
||||
private final Block finallyBody;
|
||||
|
||||
/** Exit label. */
|
||||
private Label exit;
|
||||
private final Label exit;
|
||||
|
||||
/** Exception symbol. */
|
||||
private Symbol exception;
|
||||
@ -62,37 +60,46 @@ public class TryNode extends Node {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param next next try node in chain
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param body try node body
|
||||
* @param catchBlocks list of catch blocks in order
|
||||
* @param finallyBody body of finally block or null if none
|
||||
*/
|
||||
public TryNode(final Source source, final long token, final int finish, final TryNode next) {
|
||||
public TryNode(final Source source, final long token, final int finish, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.next = next;
|
||||
this.body = body;
|
||||
this.catchBlocks = catchBlocks;
|
||||
this.finallyBody = finallyBody;
|
||||
this.exit = new Label("exit");
|
||||
}
|
||||
|
||||
private TryNode(final TryNode tryNode, final CopyState cs) {
|
||||
private TryNode(final TryNode tryNode, final Block body, final List<Block> catchBlocks, final Block finallyBody) {
|
||||
super(tryNode);
|
||||
|
||||
final List<Block> newCatchBlocks = new ArrayList<>();
|
||||
|
||||
for (final Block block : tryNode.catchBlocks) {
|
||||
newCatchBlocks.add((Block)cs.existingOrCopy(block));
|
||||
}
|
||||
|
||||
this.next = (TryNode)cs.existingOrSame(tryNode.getNext());
|
||||
this.body = (Block)cs.existingOrCopy(tryNode.getBody());
|
||||
this.catchBlocks = newCatchBlocks;
|
||||
this.finallyBody = (Block)cs.existingOrCopy(tryNode.getFinallyBody());
|
||||
this.exit = new Label(tryNode.getExit());
|
||||
this.body = body;
|
||||
this.catchBlocks = catchBlocks;
|
||||
this.finallyBody = finallyBody;
|
||||
this.exit = new Label(tryNode.exit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new TryNode(this, cs);
|
||||
public Node ensureUniqueLabels(final LexicalContext lc) {
|
||||
//try nodes are never in lex context
|
||||
return new TryNode(this, body, catchBlocks, finallyBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminal() {
|
||||
if (body.isTerminal()) {
|
||||
for (final Block catchBlock : getCatchBlocks()) {
|
||||
if (!catchBlock.isTerminal()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,21 +108,16 @@ public class TryNode extends Node {
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterTryNode(this) != null) {
|
||||
// Need to do first for termination analysis.
|
||||
if (finallyBody != null) {
|
||||
finallyBody = (Block)finallyBody.accept(visitor);
|
||||
}
|
||||
|
||||
body = (Block)body.accept(visitor);
|
||||
|
||||
final List<Block> newCatchBlocks = new ArrayList<>(catchBlocks.size());
|
||||
for (final Block catchBlock : catchBlocks) {
|
||||
newCatchBlocks.add((Block)catchBlock.accept(visitor));
|
||||
}
|
||||
this.catchBlocks = newCatchBlocks;
|
||||
|
||||
return visitor.leaveTryNode(this);
|
||||
if (visitor.enterTryNode(this)) {
|
||||
// Need to do finallybody first for termination analysis. TODO still necessary?
|
||||
final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
|
||||
final Block newBody = (Block)body.accept(visitor);
|
||||
return visitor.leaveTryNode(
|
||||
setBody(newBody).
|
||||
setFinallyBody(newFinallyBody).
|
||||
setCatchBlocks(Node.accept(visitor, Block.class, catchBlocks)).
|
||||
setException(exception).
|
||||
setFinallyCatchAll(finallyCatchAll));
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -123,7 +125,7 @@ public class TryNode extends Node {
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append("try");
|
||||
sb.append("try ");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,9 +139,13 @@ public class TryNode extends Node {
|
||||
/**
|
||||
* Reset the body of this try block
|
||||
* @param body new body
|
||||
* @return new TryNode or same if unchanged
|
||||
*/
|
||||
public void setBody(final Block body) {
|
||||
this.body = body;
|
||||
public TryNode setBody(final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return new TryNode(this, body, catchBlocks, finallyBody);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,16 +157,7 @@ public class TryNode extends Node {
|
||||
for (final Block catchBlock : catchBlocks) {
|
||||
catches.add((CatchNode)catchBlock.getStatements().get(0));
|
||||
}
|
||||
return catches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified block is the body of this try block, or any of its catch blocks.
|
||||
* @param block the block
|
||||
* @return true if the specified block is the body of this try block, or any of its catch blocks.
|
||||
*/
|
||||
public boolean isChildBlock(Block block) {
|
||||
return body == block || catchBlocks.contains(block);
|
||||
return Collections.unmodifiableList(catches);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,9 +171,13 @@ public class TryNode extends Node {
|
||||
/**
|
||||
* Set the catch blocks of this try
|
||||
* @param catchBlocks list of catch blocks
|
||||
* @return new TryNode or same if unchanged
|
||||
*/
|
||||
public void setCatchBlocks(final List<Block> catchBlocks) {
|
||||
this.catchBlocks = catchBlocks;
|
||||
public TryNode setCatchBlocks(final List<Block> catchBlocks) {
|
||||
if (this.catchBlocks == catchBlocks) {
|
||||
return this;
|
||||
}
|
||||
return new TryNode(this, body, catchBlocks, finallyBody);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,9 +191,11 @@ public class TryNode extends Node {
|
||||
/**
|
||||
* Set the exception symbol for this try block
|
||||
* @param exception a symbol for the compiler to store the exception in
|
||||
* @return new TryNode or same if unchanged
|
||||
*/
|
||||
public void setException(final Symbol exception) {
|
||||
public TryNode setException(final Symbol exception) {
|
||||
this.exception = exception;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,9 +210,13 @@ public class TryNode extends Node {
|
||||
* If a finally block exists, the synthetic catchall needs another symbol to
|
||||
* store its throwable
|
||||
* @param finallyCatchAll a symbol for the finally catch all exception
|
||||
* @return new TryNode or same if unchanged
|
||||
*
|
||||
* TODO can this still be stateful?
|
||||
*/
|
||||
public void setFinallyCatchAll(final Symbol finallyCatchAll) {
|
||||
public TryNode setFinallyCatchAll(final Symbol finallyCatchAll) {
|
||||
this.finallyCatchAll = finallyCatchAll;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,14 +227,6 @@ public class TryNode extends Node {
|
||||
return exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exit label for this try block
|
||||
* @param exit label
|
||||
*/
|
||||
public void setExit(final Label exit) {
|
||||
this.exit = exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body of the finally clause for this try
|
||||
* @return finally body, or null if no finally
|
||||
@ -239,24 +238,12 @@ public class TryNode extends Node {
|
||||
/**
|
||||
* Set the finally body of this try
|
||||
* @param finallyBody new finally body
|
||||
* @return new TryNode or same if unchanged
|
||||
*/
|
||||
public void setFinallyBody(final Block finallyBody) {
|
||||
this.finallyBody = finallyBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next try node in try chain
|
||||
* @return next try node
|
||||
*/
|
||||
public TryNode getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next try node in try chain
|
||||
* @param next next try node
|
||||
*/
|
||||
public void setNext(final TryNode next) {
|
||||
this.next = next;
|
||||
public TryNode setFinallyBody(final Block finallyBody) {
|
||||
if (this.finallyBody == finallyBody) {
|
||||
return this;
|
||||
}
|
||||
return new TryNode(this, body, catchBlocks, finallyBody);
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
|
||||
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
@ -39,9 +40,10 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
/**
|
||||
* UnaryNode nodes represent single operand operations.
|
||||
*/
|
||||
public class UnaryNode extends Node implements Assignment<Node> {
|
||||
@Immutable
|
||||
public final class UnaryNode extends Node implements Assignment<Node> {
|
||||
/** Right hand side argument. */
|
||||
private Node rhs;
|
||||
private final Node rhs;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -51,23 +53,26 @@ public class UnaryNode extends Node implements Assignment<Node> {
|
||||
* @param rhs expression
|
||||
*/
|
||||
public UnaryNode(final Source source, final long token, final Node rhs) {
|
||||
super(source, token, Token.descPosition(token));
|
||||
|
||||
this.start = Math.min(rhs.getStart(), Token.descPosition(token));
|
||||
this.finish = Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish());
|
||||
this.rhs = rhs;
|
||||
this(source, token, Math.min(rhs.getStart(), Token.descPosition(token)), Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish()), rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param unaryNode source node
|
||||
* @param cs copy state
|
||||
* Constructor
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param start start
|
||||
* @param finish finish
|
||||
* @param rhs expression
|
||||
*/
|
||||
protected UnaryNode(final UnaryNode unaryNode, final CopyState cs) {
|
||||
super(unaryNode);
|
||||
public UnaryNode(final Source source, final long token, final int start, final int finish, final Node rhs) {
|
||||
super(source, token, start, finish);
|
||||
this.rhs = rhs;
|
||||
}
|
||||
|
||||
this.rhs = cs.existingOrCopy(unaryNode.rhs);
|
||||
|
||||
private UnaryNode(final UnaryNode unaryNode, final Node rhs) {
|
||||
super(unaryNode);
|
||||
this.rhs = rhs;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,31 +118,13 @@ public class UnaryNode extends Node implements Assignment<Node> {
|
||||
return getAssignmentDest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return rhs.equals(((UnaryNode)other).rhs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() ^ rhs().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new UnaryNode(this, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterUnaryNode(this) != null) {
|
||||
if (visitor.enterUnaryNode(this)) {
|
||||
return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor)));
|
||||
}
|
||||
|
||||
@ -219,9 +206,9 @@ public class UnaryNode extends Node implements Assignment<Node> {
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public UnaryNode setRHS(final Node rhs) {
|
||||
if(this.rhs == rhs) return this;
|
||||
final UnaryNode n = (UnaryNode)clone();
|
||||
n.rhs = rhs;
|
||||
return n;
|
||||
if (this.rhs == rhs) {
|
||||
return this;
|
||||
}
|
||||
return new UnaryNode(this, rhs);
|
||||
}
|
||||
}
|
||||
|
@ -25,21 +25,31 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* Node represents a var/let declaration.
|
||||
*/
|
||||
public class VarNode extends Node implements Assignment<IdentNode> {
|
||||
@Immutable
|
||||
public final class VarNode extends Node implements Assignment<IdentNode> {
|
||||
/** Var name. */
|
||||
private IdentNode name;
|
||||
private final IdentNode name;
|
||||
|
||||
/** Initialization expression. */
|
||||
private Node init;
|
||||
private final Node init;
|
||||
|
||||
/** Is this a var statement (as opposed to a "var" in a for loop statement) */
|
||||
private final boolean isStatement;
|
||||
private final int flags;
|
||||
|
||||
/** Flag that determines if this function node is a statement */
|
||||
public static final int IS_STATEMENT = 1 << 0;
|
||||
|
||||
/** Flag that determines if this is the last function declaration in a function
|
||||
* This is used to micro optimize the placement of return value assignments for
|
||||
* a program node */
|
||||
public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 1;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -51,7 +61,14 @@ public class VarNode extends Node implements Assignment<IdentNode> {
|
||||
* @param init init node or null if just a declaration
|
||||
*/
|
||||
public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
|
||||
this(source, token, finish, name, init, true);
|
||||
this(source, token, finish, name, init, IS_STATEMENT);
|
||||
}
|
||||
|
||||
private VarNode(final VarNode varNode, final IdentNode name, final Node init, final int flags) {
|
||||
super(varNode);
|
||||
this.name = init == null ? name : name.setIsInitializedHere();
|
||||
this.init = init;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,28 +79,14 @@ public class VarNode extends Node implements Assignment<IdentNode> {
|
||||
* @param finish finish
|
||||
* @param name name of variable
|
||||
* @param init init node or null if just a declaration
|
||||
* @param isStatement if this is a var statement (true), or a for-loop initializer (false)
|
||||
* @param flags flags
|
||||
*/
|
||||
public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, boolean isStatement) {
|
||||
public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, final int flags) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.name = init == null ? name : name.setIsInitializedHere();
|
||||
this.init = init;
|
||||
this.isStatement = isStatement;
|
||||
}
|
||||
|
||||
|
||||
private VarNode(final VarNode varNode, final CopyState cs) {
|
||||
super(varNode);
|
||||
|
||||
this.name = (IdentNode)cs.existingOrCopy(varNode.name);
|
||||
this.init = cs.existingOrCopy(varNode.init);
|
||||
this.isStatement = varNode.isStatement;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new VarNode(this, cs);
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,46 +117,18 @@ public class VarNode extends Node implements Assignment<IdentNode> {
|
||||
return init != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if two VarNodes are the same.
|
||||
* @param other Other VarNode.
|
||||
* @return True if the VarNodes are the same.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (other instanceof VarNode) {
|
||||
final VarNode otherNode = (VarNode)other;
|
||||
final boolean nameMatches = name.equals(otherNode.name);
|
||||
if (hasInit() != otherNode.hasInit()) {
|
||||
return false;
|
||||
} else if (init == null) {
|
||||
return nameMatches;
|
||||
} else {
|
||||
return nameMatches && init.equals(otherNode.init);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode() ^ (init == null ? 0 : init.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterVarNode(this) != null) {
|
||||
if (visitor.enterVarNode(this)) {
|
||||
final IdentNode newName = (IdentNode)name.accept(visitor);
|
||||
final Node newInit = init == null ? null : init.accept(visitor);
|
||||
final VarNode newThis;
|
||||
if(name != newName || init != newInit) {
|
||||
newThis = (VarNode)clone();
|
||||
newThis.init = newInit;
|
||||
newThis.name = newInit == null ? newName : newName.setIsInitializedHere();
|
||||
final Node newInit = init == null ? null : init.accept(visitor);
|
||||
final VarNode newThis;
|
||||
if (name != newName || init != newInit) {
|
||||
newThis = new VarNode(this, newName, newInit, flags);
|
||||
} else {
|
||||
newThis = this;
|
||||
}
|
||||
@ -187,10 +162,10 @@ public class VarNode extends Node implements Assignment<IdentNode> {
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public VarNode setInit(final Node init) {
|
||||
if(this.init == init) return this;
|
||||
final VarNode n = (VarNode)clone();
|
||||
n.init = init;
|
||||
return n;
|
||||
if (this.init == init) {
|
||||
return this;
|
||||
}
|
||||
return new VarNode(this, name, init, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,12 +179,38 @@ public class VarNode extends Node implements Assignment<IdentNode> {
|
||||
/**
|
||||
* Reset the identifier for this VarNode
|
||||
* @param name new IdentNode representing the variable being set or declared
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
private VarNode setName(final IdentNode name) {
|
||||
if(this.name == name) return this;
|
||||
final VarNode n = (VarNode)clone();
|
||||
n.name = name;
|
||||
return n;
|
||||
public VarNode setName(final IdentNode name) {
|
||||
if (this.name == name) {
|
||||
return this;
|
||||
}
|
||||
return new VarNode(this, name, init, flags);
|
||||
}
|
||||
|
||||
private VarNode setFlags(final int flags) {
|
||||
if (this.flags == flags) {
|
||||
return this;
|
||||
}
|
||||
return new VarNode(this, name, init, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a flag is set for this var node
|
||||
* @param flag flag
|
||||
* @return true if flag is set
|
||||
*/
|
||||
public boolean getFlag(final int flag) {
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flag for this var node
|
||||
* @param flag flag
|
||||
* @return new node if flags changed, same otherwise
|
||||
*/
|
||||
public VarNode setFlag(final int flag) {
|
||||
return setFlags(flags | flag);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,7 +218,7 @@ public class VarNode extends Node implements Assignment<IdentNode> {
|
||||
* @return true if this is a var statement (as opposed to a var initializer in a for loop).
|
||||
*/
|
||||
public boolean isStatement() {
|
||||
return isStatement;
|
||||
return (flags & IS_STATEMENT) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.Label;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
@ -33,130 +33,126 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
* IR representation for a WHILE statement. This is the superclass of all
|
||||
* loop nodes
|
||||
*/
|
||||
public class WhileNode extends BreakableNode {
|
||||
/** Test expression. */
|
||||
protected Node test;
|
||||
@Immutable
|
||||
public final class WhileNode extends LoopNode {
|
||||
|
||||
/** For body. */
|
||||
protected Block body;
|
||||
|
||||
/** loop continue label. */
|
||||
protected Label continueLabel;
|
||||
/** is this a do while node ? */
|
||||
private final boolean isDoWhile;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param isDoWhile is this a do while loop?
|
||||
*/
|
||||
public WhileNode(final Source source, final long token, final int finish) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.breakLabel = new Label("while_break");
|
||||
this.continueLabel = new Label("while_continue");
|
||||
public WhileNode(final Source source, final long token, final int finish, final boolean isDoWhile) {
|
||||
super(source, token, finish, null, null, false);
|
||||
this.isDoWhile = isDoWhile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
* Internal copy constructor
|
||||
*
|
||||
* @param whileNode source node
|
||||
* @param cs copy state
|
||||
* @param whileNode while node
|
||||
* @param test test
|
||||
* @param body body
|
||||
* @param controlFlowEscapes control flow escapes?
|
||||
*/
|
||||
protected WhileNode(final WhileNode whileNode, final CopyState cs) {
|
||||
super(whileNode);
|
||||
|
||||
this.test = cs.existingOrCopy(whileNode.test);
|
||||
this.body = (Block)cs.existingOrCopy(whileNode.body);
|
||||
this.breakLabel = new Label(whileNode.breakLabel);
|
||||
this.continueLabel = new Label(whileNode.continueLabel);
|
||||
protected WhileNode(final WhileNode whileNode, final Node test, final Block body, final boolean controlFlowEscapes) {
|
||||
super(whileNode, test, body, controlFlowEscapes);
|
||||
this.isDoWhile = whileNode.isDoWhile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new WhileNode(this, cs);
|
||||
public Node ensureUniqueLabels(final LexicalContext lc) {
|
||||
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoop() {
|
||||
return true;
|
||||
public boolean hasGoto() {
|
||||
return test == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterWhileNode(this) != null) {
|
||||
test = test.accept(visitor);
|
||||
body = (Block)body.accept(visitor);
|
||||
protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterWhileNode(this)) {
|
||||
if (isDoWhile()) {
|
||||
return visitor.leaveWhileNode(
|
||||
setTest(lc, test.accept(visitor)).
|
||||
setBody(lc, (Block)body.accept(visitor)));
|
||||
}
|
||||
return visitor.leaveWhileNode(
|
||||
setBody(lc, (Block)body.accept(visitor)).
|
||||
setTest(lc, test.accept(visitor)));
|
||||
|
||||
return visitor.leaveWhileNode(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append("while (");
|
||||
test.toString(sb);
|
||||
sb.append(')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loop body
|
||||
* @return body
|
||||
*/
|
||||
public Block getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the loop body
|
||||
* @param body new body
|
||||
*/
|
||||
public void setBody(final Block body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the break label (described in {@link WhileNode#getBreakLabel()} for this while node
|
||||
* @param breakLabel break label
|
||||
*/
|
||||
public void setBreakLabel(final Label breakLabel) {
|
||||
this.breakLabel = breakLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the continue label for this while node, i.e. location to go to on continue
|
||||
* @return continue label
|
||||
*/
|
||||
public Label getContinueLabel() {
|
||||
return continueLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the continue label (described in {@link WhileNode#getContinueLabel()} for this while node
|
||||
* @param continueLabel continue label
|
||||
*/
|
||||
public void setContinueLabel(final Label continueLabel) {
|
||||
this.continueLabel = continueLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test expression for this loop, that upon evaluation to true does another iteration
|
||||
* @return test expression
|
||||
*/
|
||||
public Node getTest() {
|
||||
return test;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WhileNode setTest(final LexicalContext lc, final Node test) {
|
||||
if (this.test == test) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WhileNode setBody(final LexicalContext lc, final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WhileNode setControlFlowEscapes(final LexicalContext lc, final boolean controlFlowEscapes) {
|
||||
if (this.controlFlowEscapes == controlFlowEscapes) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the test expression for this loop
|
||||
* @param test test expression, null if infinite loop
|
||||
* Check if this is a do while loop or a normal while loop
|
||||
* @return true if do while
|
||||
*/
|
||||
public void setTest(final Node test) {
|
||||
this.test = test;
|
||||
public boolean isDoWhile() {
|
||||
return isDoWhile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
if (isDoWhile()) {
|
||||
sb.append("do {");
|
||||
body.toString(sb);
|
||||
sb.append("} while (");
|
||||
test.toString(sb);
|
||||
sb.append(')');
|
||||
} else {
|
||||
sb.append("while (");
|
||||
test.toString(sb);
|
||||
sb.append(')');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mustEnter() {
|
||||
if (isDoWhile()) {
|
||||
return true;
|
||||
}
|
||||
return test == null;
|
||||
}
|
||||
}
|
||||
|
@ -25,18 +25,20 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
/**
|
||||
* IR representation for {@code with} statements.
|
||||
*/
|
||||
public class WithNode extends Node {
|
||||
@Immutable
|
||||
public final class WithNode extends LexicalContextNode {
|
||||
/** This expression. */
|
||||
private Node expression;
|
||||
private final Node expression;
|
||||
|
||||
/** Statements. */
|
||||
private Block body;
|
||||
private final Block body;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -44,44 +46,41 @@ public class WithNode extends Node {
|
||||
* @param source the source
|
||||
* @param token token
|
||||
* @param finish finish
|
||||
* @param expression expression in parenthesis
|
||||
* @param body with node body
|
||||
*/
|
||||
public WithNode(final Source source, final long token, final int finish, final Node expression, final Block body) {
|
||||
public WithNode(final Source source, final long token, final int finish) {
|
||||
super(source, token, finish);
|
||||
|
||||
this.expression = null;
|
||||
this.body = null;
|
||||
}
|
||||
|
||||
private WithNode(final WithNode node, final Node expression, final Block body) {
|
||||
super(node);
|
||||
|
||||
this.expression = expression;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
private WithNode(final WithNode withNode, final CopyState cs) {
|
||||
super(withNode);
|
||||
|
||||
this.expression = cs.existingOrCopy(withNode.expression);
|
||||
this.body = (Block)cs.existingOrCopy(withNode.body);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node copy(final CopyState cs) {
|
||||
return new WithNode(this, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
*
|
||||
* @param visitor IR navigating visitor.
|
||||
*/
|
||||
@Override
|
||||
public Node accept(final NodeVisitor visitor) {
|
||||
if (visitor.enterWithNode(this) != null) {
|
||||
expression = expression.accept(visitor);
|
||||
body = (Block)body.accept(visitor);
|
||||
return visitor.leaveWithNode(this);
|
||||
public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
|
||||
if (visitor.enterWithNode(this)) {
|
||||
return visitor.leaveWithNode(
|
||||
setExpression(lc, expression.accept(visitor)).
|
||||
setBody(lc, (Block)body.accept(visitor)));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminal() {
|
||||
return body.isTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append("with (");
|
||||
@ -99,10 +98,15 @@ public class WithNode extends Node {
|
||||
|
||||
/**
|
||||
* Reset the body of this with node
|
||||
* @param lc lexical context
|
||||
* @param body new body
|
||||
* @return new or same withnode
|
||||
*/
|
||||
public void setBody(final Block body) {
|
||||
this.body = body;
|
||||
public WithNode setBody(final LexicalContext lc, final Block body) {
|
||||
if (this.body == body) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,10 +119,15 @@ public class WithNode extends Node {
|
||||
|
||||
/**
|
||||
* Reset the expression of this with node
|
||||
* @param lc lexical context
|
||||
* @param expression new expression
|
||||
* @return new or same withnode
|
||||
*/
|
||||
public void setExpression(final Node expression) {
|
||||
this.expression = expression;
|
||||
public WithNode setExpression(final LexicalContext lc, final Node expression) {
|
||||
if (this.expression == expression) {
|
||||
return this;
|
||||
}
|
||||
return Node.replaceInLexicalContext(lc, this, new WithNode(this, expression, body));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.ir.annotations;
|
||||
|
||||
/**
|
||||
* Tag for nodes that are immutable. To be immutable all fields must be
|
||||
* final and copy on write semantics must be in place
|
||||
*/
|
||||
public @interface Immutable {
|
||||
//empty
|
||||
}
|
@ -33,7 +33,9 @@ import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.TernaryNode;
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
@ -113,6 +115,10 @@ public final class ASTWriter {
|
||||
type += "#" + node.getSymbol();
|
||||
}
|
||||
|
||||
if (node instanceof Block && ((Block)node).needsScope()) {
|
||||
type += " <scope>";
|
||||
}
|
||||
|
||||
final List<Field> children = new LinkedList<>();
|
||||
|
||||
if (!isReference) {
|
||||
@ -121,10 +127,6 @@ public final class ASTWriter {
|
||||
|
||||
String status = "";
|
||||
|
||||
if (node.shouldDiscard()) {
|
||||
status += " Discard";
|
||||
}
|
||||
|
||||
if (node.isTerminal()) {
|
||||
status += " Terminal";
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.CaseNode;
|
||||
import jdk.nashorn.internal.ir.CatchNode;
|
||||
import jdk.nashorn.internal.ir.ContinueNode;
|
||||
import jdk.nashorn.internal.ir.DoWhileNode;
|
||||
import jdk.nashorn.internal.ir.EmptyNode;
|
||||
import jdk.nashorn.internal.ir.ExecuteNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
@ -88,7 +87,7 @@ public final class JSONWriter extends NodeVisitor {
|
||||
final Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
|
||||
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
|
||||
try {
|
||||
final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.tag());
|
||||
final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
|
||||
functionNode.accept(jsonWriter);
|
||||
return jsonWriter.getString();
|
||||
} catch (final ParserException e) {
|
||||
@ -98,11 +97,16 @@ public final class JSONWriter extends NodeVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Node enterDefault(final Node node) {
|
||||
protected boolean enterDefault(final Node node) {
|
||||
objectStart();
|
||||
location(node);
|
||||
|
||||
return node;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean leave() {
|
||||
objectEnd();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -112,7 +116,7 @@ public final class JSONWriter extends NodeVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterAccessNode(final AccessNode accessNode) {
|
||||
public boolean enterAccessNode(final AccessNode accessNode) {
|
||||
enterDefault(accessNode);
|
||||
|
||||
type("MemberExpression");
|
||||
@ -128,11 +132,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
|
||||
property("computed", false);
|
||||
|
||||
return leaveDefault(accessNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBlock(final Block block) {
|
||||
public boolean enterBlock(final Block block) {
|
||||
enterDefault(block);
|
||||
|
||||
type("BlockStatement");
|
||||
@ -140,21 +144,21 @@ public final class JSONWriter extends NodeVisitor {
|
||||
|
||||
array("body", block.getStatements());
|
||||
|
||||
return leaveDefault(block);
|
||||
return leave();
|
||||
}
|
||||
|
||||
private static boolean isLogical(final TokenType tt) {
|
||||
switch (tt) {
|
||||
case AND:
|
||||
case OR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
case AND:
|
||||
case OR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBinaryNode(final BinaryNode binaryNode) {
|
||||
public boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
enterDefault(binaryNode);
|
||||
|
||||
final String name;
|
||||
@ -179,29 +183,29 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("right");
|
||||
binaryNode.rhs().accept(this);
|
||||
|
||||
return leaveDefault(binaryNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBreakNode(final BreakNode breakNode) {
|
||||
public boolean enterBreakNode(final BreakNode breakNode) {
|
||||
enterDefault(breakNode);
|
||||
|
||||
type("BreakStatement");
|
||||
comma();
|
||||
|
||||
final LabelNode label = breakNode.getLabel();
|
||||
final IdentNode label = breakNode.getLabel();
|
||||
if (label != null) {
|
||||
property("label", label.getLabel().getName());
|
||||
property("label", label.getName());
|
||||
} else {
|
||||
property("label");
|
||||
nullValue();
|
||||
}
|
||||
|
||||
return leaveDefault(breakNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterCallNode(final CallNode callNode) {
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
enterDefault(callNode);
|
||||
|
||||
type("CallExpression");
|
||||
@ -213,11 +217,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
|
||||
array("arguments", callNode.getArgs());
|
||||
|
||||
return leaveDefault(callNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterCaseNode(final CaseNode caseNode) {
|
||||
public boolean enterCaseNode(final CaseNode caseNode) {
|
||||
enterDefault(caseNode);
|
||||
|
||||
type("SwitchCase");
|
||||
@ -234,11 +238,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
|
||||
array("consequent", caseNode.getBody().getStatements());
|
||||
|
||||
return leaveDefault(caseNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterCatchNode(final CatchNode catchNode) {
|
||||
public boolean enterCatchNode(final CatchNode catchNode) {
|
||||
enterDefault(catchNode);
|
||||
|
||||
type("CatchClause");
|
||||
@ -260,55 +264,38 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("body");
|
||||
catchNode.getBody().accept(this);
|
||||
|
||||
return leaveDefault(catchNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterContinueNode(final ContinueNode continueNode) {
|
||||
public boolean enterContinueNode(final ContinueNode continueNode) {
|
||||
enterDefault(continueNode);
|
||||
|
||||
type("ContinueStatement");
|
||||
comma();
|
||||
|
||||
final LabelNode label = continueNode.getLabel();
|
||||
final IdentNode label = continueNode.getLabel();
|
||||
if (label != null) {
|
||||
property("label", label.getLabel().getName());
|
||||
property("label", label.getName());
|
||||
} else {
|
||||
property("label");
|
||||
nullValue();
|
||||
}
|
||||
|
||||
return leaveDefault(continueNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
enterDefault(doWhileNode);
|
||||
|
||||
type("DoWhileStatement");
|
||||
comma();
|
||||
|
||||
property("body");
|
||||
doWhileNode.getBody().accept(this);
|
||||
comma();
|
||||
|
||||
property("test");
|
||||
doWhileNode.getTest().accept(this);
|
||||
|
||||
return leaveDefault(doWhileNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterEmptyNode(final EmptyNode emptyNode) {
|
||||
public boolean enterEmptyNode(final EmptyNode emptyNode) {
|
||||
enterDefault(emptyNode);
|
||||
|
||||
type("EmptyStatement");
|
||||
|
||||
return leaveDefault(emptyNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterExecuteNode(final ExecuteNode executeNode) {
|
||||
public boolean enterExecuteNode(final ExecuteNode executeNode) {
|
||||
enterDefault(executeNode);
|
||||
|
||||
type("ExpressionStatement");
|
||||
@ -317,11 +304,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("expression");
|
||||
executeNode.getExpression().accept(this);
|
||||
|
||||
return leaveDefault(executeNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterForNode(final ForNode forNode) {
|
||||
public boolean enterForNode(final ForNode forNode) {
|
||||
enterDefault(forNode);
|
||||
|
||||
if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
|
||||
@ -380,11 +367,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
forNode.getBody().accept(this);
|
||||
}
|
||||
|
||||
return leaveDefault(forNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode functionNode) {
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
enterDefault(functionNode);
|
||||
|
||||
final boolean program = functionNode.isProgram();
|
||||
@ -419,7 +406,7 @@ public final class JSONWriter extends NodeVisitor {
|
||||
}
|
||||
|
||||
// body consists of nested functions and statements
|
||||
final List<Node> stats = functionNode.getStatements();
|
||||
final List<Node> stats = functionNode.getBody().getStatements();
|
||||
final int size = stats.size();
|
||||
int idx = 0;
|
||||
arrayStart("body");
|
||||
@ -435,11 +422,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
}
|
||||
arrayEnd();
|
||||
|
||||
return leaveDefault(functionNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterIdentNode(final IdentNode identNode) {
|
||||
public boolean enterIdentNode(final IdentNode identNode) {
|
||||
enterDefault(identNode);
|
||||
|
||||
final String name = identNode.getName();
|
||||
@ -451,11 +438,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("name", identNode.getName());
|
||||
}
|
||||
|
||||
return leaveDefault(identNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterIfNode(final IfNode ifNode) {
|
||||
public boolean enterIfNode(final IfNode ifNode) {
|
||||
enterDefault(ifNode);
|
||||
|
||||
type("IfStatement");
|
||||
@ -477,11 +464,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
nullValue();
|
||||
}
|
||||
|
||||
return leaveDefault(ifNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterIndexNode(final IndexNode indexNode) {
|
||||
public boolean enterIndexNode(final IndexNode indexNode) {
|
||||
enterDefault(indexNode);
|
||||
|
||||
type("MemberExpression");
|
||||
@ -497,11 +484,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
|
||||
property("computed", true);
|
||||
|
||||
return leaveDefault(indexNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterLabelNode(final LabelNode labelNode) {
|
||||
public boolean enterLabelNode(final LabelNode labelNode) {
|
||||
enterDefault(labelNode);
|
||||
|
||||
type("LabeledStatement");
|
||||
@ -514,17 +501,17 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("body");
|
||||
labelNode.getBody().accept(this);
|
||||
|
||||
return leaveDefault(labelNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
return null;
|
||||
public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Node enterLiteralNode(final LiteralNode literalNode) {
|
||||
public boolean enterLiteralNode(final LiteralNode literalNode) {
|
||||
enterDefault(literalNode);
|
||||
|
||||
if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
|
||||
@ -556,11 +543,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
return leaveDefault(literalNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterObjectNode(final ObjectNode objectNode) {
|
||||
public boolean enterObjectNode(final ObjectNode objectNode) {
|
||||
enterDefault(objectNode);
|
||||
|
||||
type("ObjectExpression");
|
||||
@ -568,11 +555,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
|
||||
array("properties", objectNode.getElements());
|
||||
|
||||
return leaveDefault(objectNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterPropertyNode(final PropertyNode propertyNode) {
|
||||
public boolean enterPropertyNode(final PropertyNode propertyNode) {
|
||||
final Node key = propertyNode.getKey();
|
||||
|
||||
final Node value = propertyNode.getValue();
|
||||
@ -634,11 +621,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterReturnNode(final ReturnNode returnNode) {
|
||||
public boolean enterReturnNode(final ReturnNode returnNode) {
|
||||
enterDefault(returnNode);
|
||||
|
||||
type("ReturnStatement");
|
||||
@ -652,31 +639,29 @@ public final class JSONWriter extends NodeVisitor {
|
||||
nullValue();
|
||||
}
|
||||
|
||||
return leaveDefault(returnNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
|
||||
public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
|
||||
final RuntimeNode.Request req = runtimeNode.getRequest();
|
||||
|
||||
if (req == RuntimeNode.Request.DEBUGGER) {
|
||||
enterDefault(runtimeNode);
|
||||
|
||||
type("DebuggerStatement");
|
||||
|
||||
return leaveDefault(runtimeNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterSplitNode(final SplitNode splitNode) {
|
||||
return null;
|
||||
public boolean enterSplitNode(final SplitNode splitNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterSwitchNode(final SwitchNode switchNode) {
|
||||
public boolean enterSwitchNode(final SwitchNode switchNode) {
|
||||
enterDefault(switchNode);
|
||||
|
||||
type("SwitchStatement");
|
||||
@ -688,11 +673,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
|
||||
array("cases", switchNode.getCases());
|
||||
|
||||
return leaveDefault(switchNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterTernaryNode(final TernaryNode ternaryNode) {
|
||||
public boolean enterTernaryNode(final TernaryNode ternaryNode) {
|
||||
enterDefault(ternaryNode);
|
||||
|
||||
type("ConditionalExpression");
|
||||
@ -709,11 +694,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("alternate");
|
||||
ternaryNode.third().accept(this);
|
||||
|
||||
return leaveDefault(ternaryNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterThrowNode(final ThrowNode throwNode) {
|
||||
public boolean enterThrowNode(final ThrowNode throwNode) {
|
||||
enterDefault(throwNode);
|
||||
|
||||
type("ThrowStatement");
|
||||
@ -722,11 +707,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("argument");
|
||||
throwNode.getExpression().accept(this);
|
||||
|
||||
return leaveDefault(throwNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterTryNode(final TryNode tryNode) {
|
||||
public boolean enterTryNode(final TryNode tryNode) {
|
||||
enterDefault(tryNode);
|
||||
|
||||
type("TryStatement");
|
||||
@ -747,11 +732,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
nullValue();
|
||||
}
|
||||
|
||||
return leaveDefault(tryNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterUnaryNode(final UnaryNode unaryNode) {
|
||||
public boolean enterUnaryNode(final UnaryNode unaryNode) {
|
||||
enterDefault(unaryNode);
|
||||
|
||||
final TokenType tokenType = unaryNode.tokenType();
|
||||
@ -769,25 +754,25 @@ public final class JSONWriter extends NodeVisitor {
|
||||
final boolean prefix;
|
||||
final String operator;
|
||||
switch (tokenType) {
|
||||
case INCPOSTFIX:
|
||||
prefix = false;
|
||||
operator = "++";
|
||||
break;
|
||||
case DECPOSTFIX:
|
||||
prefix = false;
|
||||
operator = "--";
|
||||
break;
|
||||
case INCPREFIX:
|
||||
operator = "++";
|
||||
prefix = true;
|
||||
break;
|
||||
case DECPREFIX:
|
||||
operator = "--";
|
||||
prefix = true;
|
||||
break;
|
||||
default:
|
||||
prefix = false;
|
||||
operator = tokenType.getName();
|
||||
case INCPOSTFIX:
|
||||
prefix = false;
|
||||
operator = "++";
|
||||
break;
|
||||
case DECPOSTFIX:
|
||||
prefix = false;
|
||||
operator = "--";
|
||||
break;
|
||||
case INCPREFIX:
|
||||
operator = "++";
|
||||
prefix = true;
|
||||
break;
|
||||
case DECPREFIX:
|
||||
operator = "--";
|
||||
prefix = true;
|
||||
break;
|
||||
default:
|
||||
prefix = false;
|
||||
operator = tokenType.getName();
|
||||
}
|
||||
|
||||
type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
|
||||
@ -803,11 +788,11 @@ public final class JSONWriter extends NodeVisitor {
|
||||
unaryNode.rhs().accept(this);
|
||||
}
|
||||
|
||||
return leaveDefault(unaryNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterVarNode(final VarNode varNode) {
|
||||
public boolean enterVarNode(final VarNode varNode) {
|
||||
enterDefault(varNode);
|
||||
|
||||
type("VariableDeclaration");
|
||||
@ -839,28 +824,37 @@ public final class JSONWriter extends NodeVisitor {
|
||||
// declarations
|
||||
arrayEnd();
|
||||
|
||||
return leaveDefault(varNode);
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterWhileNode(final WhileNode whileNode) {
|
||||
public boolean enterWhileNode(final WhileNode whileNode) {
|
||||
enterDefault(whileNode);
|
||||
|
||||
type("WhileStatement");
|
||||
type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
|
||||
comma();
|
||||
|
||||
property("test");
|
||||
whileNode.getTest().accept(this);
|
||||
comma();
|
||||
if (whileNode.isDoWhile()) {
|
||||
property("body");
|
||||
whileNode.getBody().accept(this);
|
||||
comma();
|
||||
|
||||
property("block");
|
||||
whileNode.getBody().accept(this);
|
||||
property("test");
|
||||
whileNode.getTest().accept(this);
|
||||
} else {
|
||||
property("test");
|
||||
whileNode.getTest().accept(this);
|
||||
comma();
|
||||
|
||||
return leaveDefault(whileNode);
|
||||
property("block");
|
||||
whileNode.getBody().accept(this);
|
||||
}
|
||||
|
||||
return leave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterWithNode(final WithNode withNode) {
|
||||
public boolean enterWithNode(final WithNode withNode) {
|
||||
enterDefault(withNode);
|
||||
|
||||
type("WithStatement");
|
||||
@ -873,8 +867,8 @@ public final class JSONWriter extends NodeVisitor {
|
||||
property("body");
|
||||
withNode.getBody().accept(this);
|
||||
|
||||
return leaveDefault(withNode);
|
||||
}
|
||||
return leave();
|
||||
}
|
||||
|
||||
// Internals below
|
||||
|
||||
|
@ -26,30 +26,22 @@
|
||||
package jdk.nashorn.internal.ir.debug;
|
||||
|
||||
import java.util.List;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.BreakNode;
|
||||
import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.CaseNode;
|
||||
import jdk.nashorn.internal.ir.CatchNode;
|
||||
import jdk.nashorn.internal.ir.ContinueNode;
|
||||
import jdk.nashorn.internal.ir.DoWhileNode;
|
||||
import jdk.nashorn.internal.ir.ExecuteNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
import jdk.nashorn.internal.ir.LabelNode;
|
||||
import jdk.nashorn.internal.ir.LineNumberNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.ReturnNode;
|
||||
import jdk.nashorn.internal.ir.RuntimeNode;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.SwitchNode;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
import jdk.nashorn.internal.ir.ThrowNode;
|
||||
import jdk.nashorn.internal.ir.TryNode;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
import jdk.nashorn.internal.ir.VarNode;
|
||||
import jdk.nashorn.internal.ir.WhileNode;
|
||||
import jdk.nashorn.internal.ir.WithNode;
|
||||
@ -136,21 +128,20 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
/*
|
||||
* Visits.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Node enterAccessNode(final AccessNode accessNode) {
|
||||
accessNode.toString(sb);
|
||||
return null;
|
||||
public boolean enterDefault(final Node node) {
|
||||
node.toString(sb);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBlock(final Block block) {
|
||||
public boolean enterBlock(final Block block) {
|
||||
sb.append(' ');
|
||||
sb.append('{');
|
||||
|
||||
indent += TABWIDTH;
|
||||
|
||||
final boolean isFunction = block instanceof FunctionNode;
|
||||
|
||||
final List<Node> statements = block.getStatements();
|
||||
|
||||
boolean lastLineNumber = false;
|
||||
@ -161,14 +152,14 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
indent();
|
||||
}
|
||||
|
||||
if (statement instanceof UnaryNode) {
|
||||
statement.toString(sb);
|
||||
} else {
|
||||
statement.accept(this);
|
||||
}
|
||||
statement.accept(this);
|
||||
|
||||
lastLineNumber = statement instanceof LineNumberNode;
|
||||
|
||||
if (statement instanceof FunctionNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Symbol symbol = statement.getSymbol();
|
||||
|
||||
if (symbol != null) {
|
||||
@ -200,72 +191,42 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
indent();
|
||||
sb.append("}");
|
||||
|
||||
if (isFunction) {
|
||||
sb.append(EOLN);
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterBreakNode(final BreakNode breakNode) {
|
||||
breakNode.toString(sb);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterCallNode(final CallNode callNode) {
|
||||
callNode.toString(sb);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterContinueNode(final ContinueNode continueNode) {
|
||||
continueNode.toString(sb);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
sb.append("do");
|
||||
doWhileNode.getBody().accept(this);
|
||||
public boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
binaryNode.lhs().accept(this);
|
||||
sb.append(' ');
|
||||
doWhileNode.toString(sb);
|
||||
|
||||
return null;
|
||||
sb.append(binaryNode.tokenType());
|
||||
sb.append(' ');
|
||||
binaryNode.rhs().accept(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterExecuteNode(final ExecuteNode executeNode) {
|
||||
final Node expression = executeNode.getExpression();
|
||||
|
||||
if (expression instanceof UnaryNode) {
|
||||
expression.toString(sb);
|
||||
} else {
|
||||
expression.accept(this);
|
||||
}
|
||||
|
||||
return null;
|
||||
public boolean enterExecuteNode(final ExecuteNode executeNode) {
|
||||
executeNode.getExpression().accept(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterForNode(final ForNode forNode) {
|
||||
public boolean enterForNode(final ForNode forNode) {
|
||||
forNode.toString(sb);
|
||||
forNode.getBody().accept(this);
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterFunctionNode(final FunctionNode functionNode) {
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
functionNode.toString(sb);
|
||||
enterBlock(functionNode);
|
||||
|
||||
return null;
|
||||
enterBlock(functionNode.getBody());
|
||||
sb.append(EOLN);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterIfNode(final IfNode ifNode) {
|
||||
public boolean enterIfNode(final IfNode ifNode) {
|
||||
ifNode.toString(sb);
|
||||
ifNode.getPass().accept(this);
|
||||
|
||||
@ -276,55 +237,36 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
fail.accept(this);
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterIndexNode(final IndexNode indexNode) {
|
||||
indexNode.toString(sb);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterLabelNode(final LabelNode labeledNode) {
|
||||
public boolean enterLabelNode(final LabelNode labeledNode) {
|
||||
indent -= TABWIDTH;
|
||||
indent();
|
||||
indent += TABWIDTH;
|
||||
labeledNode.toString(sb);
|
||||
labeledNode.getBody().accept(this);
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
if (printLineNumbers) {
|
||||
lineNumberNode.toString(sb);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Node enterReturnNode(final ReturnNode returnNode) {
|
||||
returnNode.toString(sb);
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
|
||||
runtimeNode.toString(sb);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterSplitNode(final SplitNode splitNode) {
|
||||
public boolean enterSplitNode(final SplitNode splitNode) {
|
||||
splitNode.toString(sb);
|
||||
sb.append(EOLN);
|
||||
indent += TABWIDTH;
|
||||
indent();
|
||||
return splitNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -337,7 +279,7 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterSwitchNode(final SwitchNode switchNode) {
|
||||
public boolean enterSwitchNode(final SwitchNode switchNode) {
|
||||
switchNode.toString(sb);
|
||||
sb.append(" {");
|
||||
|
||||
@ -357,24 +299,18 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
indent();
|
||||
sb.append("}");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterThrowNode(final ThrowNode throwNode) {
|
||||
throwNode.toString(sb);
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterTryNode(final TryNode tryNode) {
|
||||
public boolean enterTryNode(final TryNode tryNode) {
|
||||
tryNode.toString(sb);
|
||||
tryNode.getBody().accept(this);
|
||||
|
||||
final List<Block> catchBlocks = tryNode.getCatchBlocks();
|
||||
|
||||
for (final Block catchBlock : catchBlocks) {
|
||||
final CatchNode catchNode = (CatchNode) catchBlock.getStatements().get(0);
|
||||
final CatchNode catchNode = (CatchNode)catchBlock.getStatements().get(0);
|
||||
catchNode.toString(sb);
|
||||
catchNode.getBody().accept(this);
|
||||
}
|
||||
@ -386,35 +322,42 @@ public final class PrintVisitor extends NodeVisitor {
|
||||
finallyBody.accept(this);
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterVarNode(final VarNode varNode) {
|
||||
public boolean enterVarNode(final VarNode varNode) {
|
||||
sb.append("var ");
|
||||
varNode.getName().toString(sb);
|
||||
final Node init = varNode.getInit();
|
||||
if(init != null) {
|
||||
if (init != null) {
|
||||
sb.append(" = ");
|
||||
init.accept(this);
|
||||
}
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterWhileNode(final WhileNode whileNode) {
|
||||
whileNode.toString(sb);
|
||||
whileNode.getBody().accept(this);
|
||||
public boolean enterWhileNode(final WhileNode whileNode) {
|
||||
if (whileNode.isDoWhile()) {
|
||||
sb.append("do");
|
||||
whileNode.getBody().accept(this);
|
||||
sb.append(' ');
|
||||
whileNode.toString(sb);
|
||||
} else {
|
||||
whileNode.toString(sb);
|
||||
whileNode.getBody().accept(this);
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node enterWithNode(final WithNode withNode) {
|
||||
public boolean enterWithNode(final WithNode withNode) {
|
||||
withNode.toString(sb);
|
||||
withNode.getBody().accept(this);
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,9 +25,8 @@
|
||||
|
||||
package jdk.nashorn.internal.ir.visitor;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.codegen.MethodEmitter;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
|
||||
@ -45,15 +44,14 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param compileUnit compile unit
|
||||
* @param method method emitter
|
||||
* @param lc a custom lexical context
|
||||
*/
|
||||
public NodeOperatorVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
|
||||
super(compileUnit, method);
|
||||
public NodeOperatorVisitor(final LexicalContext lc) {
|
||||
super(lc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Node enterUnaryNode(final UnaryNode unaryNode) {
|
||||
public final boolean enterUnaryNode(final UnaryNode unaryNode) {
|
||||
switch (unaryNode.tokenType()) {
|
||||
case ADD:
|
||||
return enterADD(unaryNode);
|
||||
@ -119,7 +117,7 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Node enterBinaryNode(final BinaryNode binaryNode) {
|
||||
public final boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
switch (binaryNode.tokenType()) {
|
||||
case ADD:
|
||||
return enterADD(binaryNode);
|
||||
@ -286,17 +284,6 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public Node enter(final TernaryNode ternaryNode) {
|
||||
return enterDefault(ternaryNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leave(final TernaryNode ternaryNode) {
|
||||
return leaveDefault(ternaryNode);
|
||||
}*/
|
||||
|
||||
/*
|
||||
* Unary entries and exists.
|
||||
*/
|
||||
@ -305,9 +292,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a unary +
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterADD(final UnaryNode unaryNode) {
|
||||
public boolean enterADD(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -325,9 +312,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a ~ operator
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterBIT_NOT(final UnaryNode unaryNode) {
|
||||
public boolean enterBIT_NOT(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -345,9 +332,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a conversion
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterCONVERT(final UnaryNode unaryNode) {
|
||||
public boolean enterCONVERT(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -365,9 +352,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a ++ or -- operator
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterDECINC(final UnaryNode unaryNode) {
|
||||
public boolean enterDECINC(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -387,7 +374,7 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
*/
|
||||
public Node enterDELETE(final UnaryNode unaryNode) {
|
||||
public boolean enterDELETE(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -405,9 +392,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a discard operator
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterDISCARD(final UnaryNode unaryNode) {
|
||||
public boolean enterDISCARD(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -425,9 +412,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a new operator
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterNEW(final UnaryNode unaryNode) {
|
||||
public boolean enterNEW(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -445,9 +432,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a ! operator
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterNOT(final UnaryNode unaryNode) {
|
||||
public boolean enterNOT(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -465,9 +452,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a unary -
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterSUB(final UnaryNode unaryNode) {
|
||||
public boolean enterSUB(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -485,9 +472,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a typeof
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterTYPEOF(final UnaryNode unaryNode) {
|
||||
public boolean enterTYPEOF(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -505,9 +492,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Unary enter - callback for entering a void
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterVOID(final UnaryNode unaryNode) {
|
||||
public boolean enterVOID(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -525,9 +512,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering + operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterADD(final BinaryNode binaryNode) {
|
||||
public boolean enterADD(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -545,9 +532,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal &&} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterAND(final BinaryNode binaryNode) {
|
||||
public boolean enterAND(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -565,9 +552,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering an assignment
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -585,9 +572,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering += operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -605,9 +592,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal &=} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -625,9 +612,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering |= operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -645,9 +632,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering ^= operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -665,9 +652,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering /= operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -685,9 +672,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering %= operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -705,9 +692,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering *= operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -725,9 +712,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal >>=} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -745,9 +732,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering a {@literal <<=} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -765,9 +752,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal >>>=} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -785,9 +772,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering -= operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
|
||||
public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -805,9 +792,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering a bind operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterBIND(final BinaryNode binaryNode) {
|
||||
public boolean enterBIND(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -825,9 +812,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal &} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterBIT_AND(final BinaryNode binaryNode) {
|
||||
public boolean enterBIT_AND(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -845,9 +832,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering | operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterBIT_OR(final BinaryNode binaryNode) {
|
||||
public boolean enterBIT_OR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -865,9 +852,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering ^ operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterBIT_XOR(final BinaryNode binaryNode) {
|
||||
public boolean enterBIT_XOR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -886,9 +873,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* (a, b) where the result is a
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterCOMMALEFT(final BinaryNode binaryNode) {
|
||||
public boolean enterCOMMALEFT(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -908,9 +895,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* (a, b) where the result is b
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
|
||||
public boolean enterCOMMARIGHT(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -929,9 +916,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering a division
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterDIV(final BinaryNode binaryNode) {
|
||||
public boolean enterDIV(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -949,9 +936,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering == operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterEQ(final BinaryNode binaryNode) {
|
||||
public boolean enterEQ(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -969,9 +956,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering === operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterEQ_STRICT(final BinaryNode binaryNode) {
|
||||
public boolean enterEQ_STRICT(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -989,9 +976,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal >=} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterGE(final BinaryNode binaryNode) {
|
||||
public boolean enterGE(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1009,9 +996,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal >} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterGT(final BinaryNode binaryNode) {
|
||||
public boolean enterGT(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1029,9 +1016,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering in operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterIN(final BinaryNode binaryNode) {
|
||||
public boolean enterIN(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1049,9 +1036,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering instanceof operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterINSTANCEOF(final BinaryNode binaryNode) {
|
||||
public boolean enterINSTANCEOF(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1069,9 +1056,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal <=} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterLE(final BinaryNode binaryNode) {
|
||||
public boolean enterLE(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1089,9 +1076,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal <} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterLT(final BinaryNode binaryNode) {
|
||||
public boolean enterLT(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1108,9 +1095,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering % operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterMOD(final BinaryNode binaryNode) {
|
||||
public boolean enterMOD(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1128,9 +1115,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering * operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterMUL(final BinaryNode binaryNode) {
|
||||
public boolean enterMUL(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1148,9 +1135,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering != operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterNE(final BinaryNode binaryNode) {
|
||||
public boolean enterNE(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1168,9 +1155,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering a !== operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterNE_STRICT(final BinaryNode binaryNode) {
|
||||
public boolean enterNE_STRICT(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1188,9 +1175,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering || operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterOR(final BinaryNode binaryNode) {
|
||||
public boolean enterOR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1208,9 +1195,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal >>} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterSAR(final BinaryNode binaryNode) {
|
||||
public boolean enterSAR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1228,9 +1215,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal <<} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterSHL(final BinaryNode binaryNode) {
|
||||
public boolean enterSHL(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1247,9 +1234,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering {@literal >>>} operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterSHR(final BinaryNode binaryNode) {
|
||||
public boolean enterSHR(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -1267,9 +1254,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
|
||||
* Binary enter - callback for entering - operator
|
||||
*
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterSUB(final BinaryNode binaryNode) {
|
||||
public boolean enterSUB(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.ir.visitor;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.codegen.MethodEmitter;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
@ -35,7 +33,6 @@ import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.CaseNode;
|
||||
import jdk.nashorn.internal.ir.CatchNode;
|
||||
import jdk.nashorn.internal.ir.ContinueNode;
|
||||
import jdk.nashorn.internal.ir.DoWhileNode;
|
||||
import jdk.nashorn.internal.ir.EmptyNode;
|
||||
import jdk.nashorn.internal.ir.ExecuteNode;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
@ -44,6 +41,7 @@ import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
import jdk.nashorn.internal.ir.LabelNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LineNumberNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
@ -65,41 +63,30 @@ import jdk.nashorn.internal.ir.WithNode;
|
||||
* Visitor used to navigate the IR.
|
||||
*/
|
||||
public abstract class NodeVisitor {
|
||||
/** Current functionNode. */
|
||||
private FunctionNode currentFunctionNode;
|
||||
|
||||
/** Current compile unit used for class generation. */
|
||||
private CompileUnit compileUnit;
|
||||
private final LexicalContext lc;
|
||||
|
||||
/**
|
||||
* Current method visitor used for method generation.
|
||||
* <p>
|
||||
* TODO: protected is just for convenience and readability, so that
|
||||
* subclasses can directly use 'method' - might want to change that
|
||||
*/
|
||||
protected MethodEmitter method;
|
||||
|
||||
/** Current block. */
|
||||
private Block currentBlock;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Constructor
|
||||
*/
|
||||
public NodeVisitor() {
|
||||
this(null, null);
|
||||
this(new LexicalContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param compileUnit compile unit for this node visitor
|
||||
* @param method method emitter for this node visitor
|
||||
* @param lc a custom lexical context
|
||||
*/
|
||||
public NodeVisitor(final CompileUnit compileUnit, final MethodEmitter method) {
|
||||
super();
|
||||
public NodeVisitor(final LexicalContext lc) {
|
||||
this.lc = lc;
|
||||
}
|
||||
|
||||
this.compileUnit = compileUnit;
|
||||
this.method = method;
|
||||
/**
|
||||
* Get the lexical context of this node visitor
|
||||
* @return lexical context
|
||||
*/
|
||||
public LexicalContext getLexicalContext() {
|
||||
return lc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,10 +105,10 @@ public abstract class NodeVisitor {
|
||||
*
|
||||
* @see NodeVisitor#leaveDefault(Node)
|
||||
* @param node the node to visit
|
||||
* @return the node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
protected Node enterDefault(final Node node) {
|
||||
return node;
|
||||
protected boolean enterDefault(final Node node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,9 +137,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering an AccessNode
|
||||
*
|
||||
* @param accessNode the node
|
||||
* @return processed node, null if traversal should end, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterAccessNode(final AccessNode accessNode) {
|
||||
public boolean enterAccessNode(final AccessNode accessNode) {
|
||||
return enterDefault(accessNode);
|
||||
}
|
||||
|
||||
@ -170,9 +157,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a Block
|
||||
*
|
||||
* @param block the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterBlock(final Block block) {
|
||||
public boolean enterBlock(final Block block) {
|
||||
return enterDefault(block);
|
||||
}
|
||||
|
||||
@ -192,7 +179,7 @@ public abstract class NodeVisitor {
|
||||
* @param binaryNode the node
|
||||
* @return processed node
|
||||
*/
|
||||
public Node enterBinaryNode(final BinaryNode binaryNode) {
|
||||
public boolean enterBinaryNode(final BinaryNode binaryNode) {
|
||||
return enterDefault(binaryNode);
|
||||
}
|
||||
|
||||
@ -210,9 +197,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a BreakNode
|
||||
*
|
||||
* @param breakNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterBreakNode(final BreakNode breakNode) {
|
||||
public boolean enterBreakNode(final BreakNode breakNode) {
|
||||
return enterDefault(breakNode);
|
||||
}
|
||||
|
||||
@ -230,9 +217,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a CallNode
|
||||
*
|
||||
* @param callNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterCallNode(final CallNode callNode) {
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
return enterDefault(callNode);
|
||||
}
|
||||
|
||||
@ -250,9 +237,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a CaseNode
|
||||
*
|
||||
* @param caseNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterCaseNode(final CaseNode caseNode) {
|
||||
public boolean enterCaseNode(final CaseNode caseNode) {
|
||||
return enterDefault(caseNode);
|
||||
}
|
||||
|
||||
@ -270,9 +257,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a CatchNode
|
||||
*
|
||||
* @param catchNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterCatchNode(final CatchNode catchNode) {
|
||||
public boolean enterCatchNode(final CatchNode catchNode) {
|
||||
return enterDefault(catchNode);
|
||||
}
|
||||
|
||||
@ -290,9 +277,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a ContinueNode
|
||||
*
|
||||
* @param continueNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterContinueNode(final ContinueNode continueNode) {
|
||||
public boolean enterContinueNode(final ContinueNode continueNode) {
|
||||
return enterDefault(continueNode);
|
||||
}
|
||||
|
||||
@ -306,33 +293,13 @@ public abstract class NodeVisitor {
|
||||
return leaveDefault(continueNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for entering a DoWhileNode
|
||||
*
|
||||
* @param doWhileNode the node
|
||||
* @return processed node
|
||||
*/
|
||||
public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
return enterDefault(doWhileNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for leaving a DoWhileNode
|
||||
*
|
||||
* @param doWhileNode the node
|
||||
* @return processed node, which will replace the original one, or the original node
|
||||
*/
|
||||
public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
|
||||
return leaveDefault(doWhileNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for entering an EmptyNode
|
||||
*
|
||||
* @param emptyNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterEmptyNode(final EmptyNode emptyNode) {
|
||||
public boolean enterEmptyNode(final EmptyNode emptyNode) {
|
||||
return enterDefault(emptyNode);
|
||||
}
|
||||
|
||||
@ -350,9 +317,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering an ExecuteNode
|
||||
*
|
||||
* @param executeNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterExecuteNode(final ExecuteNode executeNode) {
|
||||
public boolean enterExecuteNode(final ExecuteNode executeNode) {
|
||||
return enterDefault(executeNode);
|
||||
}
|
||||
|
||||
@ -370,9 +337,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a ForNode
|
||||
*
|
||||
* @param forNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterForNode(final ForNode forNode) {
|
||||
public boolean enterForNode(final ForNode forNode) {
|
||||
return enterDefault(forNode);
|
||||
}
|
||||
|
||||
@ -390,9 +357,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a FunctionNode
|
||||
*
|
||||
* @param functionNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterFunctionNode(final FunctionNode functionNode) {
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
return enterDefault(functionNode);
|
||||
}
|
||||
|
||||
@ -410,9 +377,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering an IdentNode
|
||||
*
|
||||
* @param identNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterIdentNode(final IdentNode identNode) {
|
||||
public boolean enterIdentNode(final IdentNode identNode) {
|
||||
return enterDefault(identNode);
|
||||
}
|
||||
|
||||
@ -429,10 +396,10 @@ public abstract class NodeVisitor {
|
||||
/**
|
||||
* Callback for entering an IfNode
|
||||
*
|
||||
* @param ifNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @param ifNode the node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterIfNode(final IfNode ifNode) {
|
||||
public boolean enterIfNode(final IfNode ifNode) {
|
||||
return enterDefault(ifNode);
|
||||
}
|
||||
|
||||
@ -450,9 +417,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering an IndexNode
|
||||
*
|
||||
* @param indexNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterIndexNode(final IndexNode indexNode) {
|
||||
public boolean enterIndexNode(final IndexNode indexNode) {
|
||||
return enterDefault(indexNode);
|
||||
}
|
||||
|
||||
@ -470,9 +437,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a LabelNode
|
||||
*
|
||||
* @param labelNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterLabelNode(final LabelNode labelNode) {
|
||||
public boolean enterLabelNode(final LabelNode labelNode) {
|
||||
return enterDefault(labelNode);
|
||||
}
|
||||
|
||||
@ -490,9 +457,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a LineNumberNode
|
||||
*
|
||||
* @param lineNumberNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterLineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
|
||||
return enterDefault(lineNumberNode);
|
||||
}
|
||||
|
||||
@ -510,9 +477,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a LiteralNode
|
||||
*
|
||||
* @param literalNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterLiteralNode(final LiteralNode<?> literalNode) {
|
||||
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
|
||||
return enterDefault(literalNode);
|
||||
}
|
||||
|
||||
@ -530,9 +497,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering an ObjectNode
|
||||
*
|
||||
* @param objectNode the node
|
||||
* @return processed node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterObjectNode(final ObjectNode objectNode) {
|
||||
public boolean enterObjectNode(final ObjectNode objectNode) {
|
||||
return enterDefault(objectNode);
|
||||
}
|
||||
|
||||
@ -550,9 +517,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a PropertyNode
|
||||
*
|
||||
* @param propertyNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterPropertyNode(final PropertyNode propertyNode) {
|
||||
public boolean enterPropertyNode(final PropertyNode propertyNode) {
|
||||
return enterDefault(propertyNode);
|
||||
}
|
||||
|
||||
@ -570,9 +537,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a ReturnNode
|
||||
*
|
||||
* @param returnNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterReturnNode(final ReturnNode returnNode) {
|
||||
public boolean enterReturnNode(final ReturnNode returnNode) {
|
||||
return enterDefault(returnNode);
|
||||
}
|
||||
|
||||
@ -590,9 +557,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a RuntimeNode
|
||||
*
|
||||
* @param runtimeNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterRuntimeNode(final RuntimeNode runtimeNode) {
|
||||
public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
|
||||
return enterDefault(runtimeNode);
|
||||
}
|
||||
|
||||
@ -610,9 +577,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a SplitNode
|
||||
*
|
||||
* @param splitNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterSplitNode(final SplitNode splitNode) {
|
||||
public boolean enterSplitNode(final SplitNode splitNode) {
|
||||
return enterDefault(splitNode);
|
||||
}
|
||||
|
||||
@ -630,9 +597,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a SwitchNode
|
||||
*
|
||||
* @param switchNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterSwitchNode(final SwitchNode switchNode) {
|
||||
public boolean enterSwitchNode(final SwitchNode switchNode) {
|
||||
return enterDefault(switchNode);
|
||||
}
|
||||
|
||||
@ -650,9 +617,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a TernaryNode
|
||||
*
|
||||
* @param ternaryNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterTernaryNode(final TernaryNode ternaryNode) {
|
||||
public boolean enterTernaryNode(final TernaryNode ternaryNode) {
|
||||
return enterDefault(ternaryNode);
|
||||
}
|
||||
|
||||
@ -670,9 +637,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a ThrowNode
|
||||
*
|
||||
* @param throwNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterThrowNode(final ThrowNode throwNode) {
|
||||
public boolean enterThrowNode(final ThrowNode throwNode) {
|
||||
return enterDefault(throwNode);
|
||||
}
|
||||
|
||||
@ -690,9 +657,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a TryNode
|
||||
*
|
||||
* @param tryNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterTryNode(final TryNode tryNode) {
|
||||
public boolean enterTryNode(final TryNode tryNode) {
|
||||
return enterDefault(tryNode);
|
||||
}
|
||||
|
||||
@ -710,9 +677,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a UnaryNode
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterUnaryNode(final UnaryNode unaryNode) {
|
||||
public boolean enterUnaryNode(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
@ -730,9 +697,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a VarNode
|
||||
*
|
||||
* @param varNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterVarNode(final VarNode varNode) {
|
||||
public boolean enterVarNode(final VarNode varNode) {
|
||||
return enterDefault(varNode);
|
||||
}
|
||||
|
||||
@ -750,9 +717,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a WhileNode
|
||||
*
|
||||
* @param whileNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterWhileNode(final WhileNode whileNode) {
|
||||
public boolean enterWhileNode(final WhileNode whileNode) {
|
||||
return enterDefault(whileNode);
|
||||
}
|
||||
|
||||
@ -770,9 +737,9 @@ public abstract class NodeVisitor {
|
||||
* Callback for entering a WithNode
|
||||
*
|
||||
* @param withNode the node
|
||||
* @return processed node, null if traversal should end
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public Node enterWithNode(final WithNode withNode) {
|
||||
public boolean enterWithNode(final WithNode withNode) {
|
||||
return enterDefault(withNode);
|
||||
}
|
||||
|
||||
@ -786,74 +753,5 @@ public abstract class NodeVisitor {
|
||||
return leaveDefault(withNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current function node for this NodeVisitor
|
||||
* @see FunctionNode
|
||||
* @return the function node being visited
|
||||
*/
|
||||
public FunctionNode getCurrentFunctionNode() {
|
||||
return currentFunctionNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the current function node being visited for this NodeVisitor
|
||||
* @see FunctionNode
|
||||
* @param currentFunctionNode a new function node to traverse
|
||||
*/
|
||||
public void setCurrentFunctionNode(final FunctionNode currentFunctionNode) {
|
||||
this.currentFunctionNode = currentFunctionNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current compile unit for this NodeVisitor
|
||||
* @see CompileUnit
|
||||
* @return a compile unit, or null if not a compiling NodeVisitor
|
||||
*/
|
||||
public CompileUnit getCurrentCompileUnit() {
|
||||
return compileUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current compile unit for this NodeVisitor
|
||||
* @see CompileUnit
|
||||
* @param compileUnit a new compile unit
|
||||
*/
|
||||
public void setCurrentCompileUnit(final CompileUnit compileUnit) {
|
||||
this.compileUnit = compileUnit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current method emitter for this NodeVisitor
|
||||
* @see MethodEmitter
|
||||
* @return the method emitter
|
||||
*/
|
||||
public MethodEmitter getCurrentMethodEmitter() {
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the current method emitter for this NodeVisitor
|
||||
* @see MethodEmitter
|
||||
* @param method a new method emitter
|
||||
*/
|
||||
public void setCurrentMethodEmitter(final MethodEmitter method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Block being traversed for this NodeVisitor
|
||||
* @return the current block
|
||||
*/
|
||||
public Block getCurrentBlock() {
|
||||
return currentBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the Block to be traversed for this NodeVisitor
|
||||
* @param currentBlock the new current block
|
||||
*/
|
||||
public void setCurrentBlock(final Block currentBlock) {
|
||||
this.currentBlock = currentBlock;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ public final class MethodHandleFactory {
|
||||
*/
|
||||
static Object traceReturn(final DebugLogger logger, final Object value) {
|
||||
final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']');
|
||||
logger.log(str, TRACE_LEVEL);
|
||||
logger.log(TRACE_LEVEL, str);
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ public final class MethodHandleFactory {
|
||||
}
|
||||
|
||||
assert logger != null;
|
||||
logger.log(sb.toString(), TRACE_LEVEL);
|
||||
logger.log(TRACE_LEVEL, sb);
|
||||
stacktrace(logger);
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@ public final class MethodHandleFactory {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
final PrintStream ps = new PrintStream(baos);
|
||||
new Throwable().printStackTrace(ps);
|
||||
logger.log(baos.toString(), TRACE_LEVEL);
|
||||
logger.log(TRACE_LEVEL, baos.toString());
|
||||
}
|
||||
|
||||
private static String argString(final Object arg) {
|
||||
@ -614,7 +614,7 @@ public final class MethodHandleFactory {
|
||||
@Override
|
||||
public SwitchPoint createSwitchPoint() {
|
||||
final SwitchPoint sp = super.createSwitchPoint();
|
||||
LOG.log("createSwitchPoint " + sp, TRACE_LEVEL);
|
||||
LOG.log(TRACE_LEVEL, "createSwitchPoint ", sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
@ -627,7 +627,7 @@ public final class MethodHandleFactory {
|
||||
@Override
|
||||
public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
|
||||
final MethodType mt = super.type(returnType, paramTypes);
|
||||
LOG.log("methodType " + returnType + ' ' + Arrays.toString(paramTypes) + ' ' + mt, TRACE_LEVEL);
|
||||
LOG.log(TRACE_LEVEL, "methodType ", returnType, " ", Arrays.toString(paramTypes), " ", mt);
|
||||
return mt;
|
||||
}
|
||||
}
|
||||
@ -638,7 +638,7 @@ public final class MethodHandleFactory {
|
||||
private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality {
|
||||
@Override
|
||||
public MethodHandle debug(final MethodHandle master, final String str, final Object... args) {
|
||||
LOG.log(str + ' ' + describe(args), TRACE_LEVEL);
|
||||
LOG.log(TRACE_LEVEL, str, " ", describe(args));
|
||||
stacktrace(LOG);
|
||||
return master;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class BoundScriptFunctionImpl extends ScriptFunctionImpl {
|
||||
|
||||
BoundScriptFunctionImpl(ScriptFunctionData data, ScriptFunction targetFunction) {
|
||||
super(data);
|
||||
this.prototype = ScriptRuntime.UNDEFINED;
|
||||
setPrototype(ScriptRuntime.UNDEFINED);
|
||||
this.targetFunction = targetFunction;
|
||||
}
|
||||
|
||||
|
@ -418,7 +418,7 @@ public final class NativeArray extends ScriptObject {
|
||||
long length;
|
||||
if (len instanceof Integer || len instanceof Long) {
|
||||
length = ((Number) len).longValue();
|
||||
if (length >= 0 && length < 0xffff_ffffL) {
|
||||
if (length >= 0 && length < JSType.MAX_UINT) {
|
||||
return new NativeArray(length);
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,8 @@ public final class NativeDate extends ScriptObject {
|
||||
|
||||
@Override
|
||||
public String safeToString() {
|
||||
return "[Date " + toISOStringImpl(this) + "]";
|
||||
final String str = isValidDate() ? toISOStringImpl(this) : INVALID_DATE;
|
||||
return "[Date " + str + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -518,7 +519,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object getTimezoneOffset(final Object self) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd != null) {
|
||||
if (nd != null && nd.isValidDate()) {
|
||||
final long msec = (long) nd.getTime();
|
||||
return - nd.getTimeZone().getOffset(msec) / msPerMinute;
|
||||
}
|
||||
@ -534,8 +535,8 @@ public final class NativeDate extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object setTime(final Object self, final Object time) {
|
||||
final double num = timeClip(JSType.toNumber(time));
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
final double num = timeClip(JSType.toNumber(time));
|
||||
nd.setTime(num);
|
||||
return num;
|
||||
}
|
||||
@ -550,9 +551,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
|
||||
public static Object setMilliseconds(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, MILLISECOND, args, true);
|
||||
}
|
||||
setFields(nd, MILLISECOND, args, true);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -566,9 +565,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
|
||||
public static Object setUTCMilliseconds(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, MILLISECOND, args, false);
|
||||
}
|
||||
setFields(nd, MILLISECOND, args, false);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -582,9 +579,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
|
||||
public static Object setSeconds(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, SECOND, args, true);
|
||||
}
|
||||
setFields(nd, SECOND, args, true);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -598,9 +593,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
|
||||
public static Object setUTCSeconds(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, SECOND, args, false);
|
||||
}
|
||||
setFields(nd, SECOND, args, false);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -614,9 +607,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
|
||||
public static Object setMinutes(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, MINUTE, args, true);
|
||||
}
|
||||
setFields(nd, MINUTE, args, true);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -630,9 +621,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 3)
|
||||
public static Object setUTCMinutes(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, MINUTE, args, false);
|
||||
}
|
||||
setFields(nd, MINUTE, args, false);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -646,9 +635,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
|
||||
public static Object setHours(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, HOUR, args, true);
|
||||
}
|
||||
setFields(nd, HOUR, args, true);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -662,9 +649,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 4)
|
||||
public static Object setUTCHours(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, HOUR, args, false);
|
||||
}
|
||||
setFields(nd, HOUR, args, false);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -678,9 +663,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
|
||||
public static Object setDate(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, DAY, args, true);
|
||||
}
|
||||
setFields(nd, DAY, args, true);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -694,9 +677,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
|
||||
public static Object setUTCDate(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, DAY, args, false);
|
||||
}
|
||||
setFields(nd, DAY, args, false);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -710,9 +691,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
|
||||
public static Object setMonth(final Object self, final Object... args) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, MONTH, args, true);
|
||||
}
|
||||
setFields(nd, MONTH, args, true);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -726,9 +705,7 @@ public final class NativeDate extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
|
||||
public static Object setUTCMonth(final Object self, final Object... args) {
|
||||
final NativeDate nd = ensureNativeDate(self);
|
||||
if (nd.isValidDate()) {
|
||||
setFields(nd, MONTH, args, false);
|
||||
}
|
||||
setFields(nd, MONTH, args, false);
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
@ -746,7 +723,11 @@ public final class NativeDate extends ScriptObject {
|
||||
setFields(nd, YEAR, args, true);
|
||||
} else {
|
||||
final double[] d = convertArgs(args, 0, YEAR, YEAR, 3);
|
||||
nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
|
||||
if (d != null) {
|
||||
nd.setTime(timeClip(utc(makeDate(makeDay(d[0], d[1], d[2]), 0), nd.getTimeZone())));
|
||||
} else {
|
||||
nd.setTime(NaN);
|
||||
}
|
||||
}
|
||||
return nd.getTime();
|
||||
}
|
||||
@ -781,13 +762,13 @@ public final class NativeDate extends ScriptObject {
|
||||
public static Object setYear(final Object self, final Object year) {
|
||||
final NativeDate nd = getNativeDate(self);
|
||||
if (isNaN(nd.getTime())) {
|
||||
return null;
|
||||
nd.setTime(utc(0, nd.getTimeZone()));
|
||||
}
|
||||
|
||||
final double yearNum = JSType.toNumber(year);
|
||||
if (isNaN(yearNum)) {
|
||||
nd.setTime(NaN);
|
||||
return nd;
|
||||
return nd.getTime();
|
||||
}
|
||||
int yearInt = JSType.toInteger(yearNum);
|
||||
if (0 <= yearInt && yearInt <= 99) {
|
||||
@ -795,7 +776,7 @@ public final class NativeDate extends ScriptObject {
|
||||
}
|
||||
setFields(nd, YEAR, new Object[] {yearInt}, true);
|
||||
|
||||
return nd;
|
||||
return nd.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -851,11 +832,11 @@ public final class NativeDate extends ScriptObject {
|
||||
}
|
||||
final ScriptObject sobj = (ScriptObject)selfObj;
|
||||
final Object value = sobj.getDefaultValue(Number.class);
|
||||
|
||||
final double num = (value instanceof Number) ? ((Number)value).doubleValue() : NaN;
|
||||
|
||||
if (isInfinite(num) || isNaN(num)) {
|
||||
return null;
|
||||
if (value instanceof Number) {
|
||||
final double num = ((Number)value).doubleValue();
|
||||
if (isInfinite(num) || isNaN(num)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@ -1297,6 +1278,10 @@ public final class NativeDate extends ScriptObject {
|
||||
final double time = local ? nd.getLocalTime() : nd.getTime();
|
||||
final double d[] = convertArgs(args, time, fieldId, start, length);
|
||||
|
||||
if (! nd.isValidDate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
double newTime;
|
||||
if (d == null) {
|
||||
newTime = NaN;
|
||||
|
@ -247,7 +247,6 @@ public final class NativeDebug extends ScriptObject {
|
||||
out.println("Scope count " + ScriptObject.getScopeCount());
|
||||
out.println("ScriptObject listeners added " + PropertyListenerManager.getListenersAdded());
|
||||
out.println("ScriptObject listeners removed " + PropertyListenerManager.getListenersRemoved());
|
||||
out.println("ScriptObject listeners dead " + PropertyListenerManager.getListenersDead());
|
||||
out.println("ScriptFunction count " + ScriptObject.getCount());
|
||||
out.println("ScriptFunction invokes " + ScriptFunction.getInvokes());
|
||||
out.println("ScriptFunction allocations " + ScriptFunction.getAllocations());
|
||||
|
@ -81,23 +81,13 @@ public final class NativeFunction {
|
||||
|
||||
Object[] args = null;
|
||||
|
||||
if (ScriptObject.isArray(array)) {
|
||||
args = ((NativeArray)array).asObjectArray();
|
||||
} else if (array instanceof ScriptObject) {
|
||||
if (array instanceof ScriptObject) {
|
||||
// look for array-like object
|
||||
final ScriptObject sobj = (ScriptObject)array;
|
||||
final Object len = sobj.getLength();
|
||||
|
||||
if (len == UNDEFINED || len == null) {
|
||||
throw typeError("function.apply.expects.array");
|
||||
}
|
||||
|
||||
final int n = (int)JSType.toUint32(len);
|
||||
if (n != JSType.toNumber(len)) {
|
||||
throw typeError("function.apply.expects.array");
|
||||
}
|
||||
|
||||
args = new Object[(int)JSType.toUint32(len)];
|
||||
args = new Object[n];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
args[i] = sobj.get(i);
|
||||
}
|
||||
|
@ -148,11 +148,7 @@ public final class NativeJSAdapter extends ScriptObject {
|
||||
if (overrides instanceof ScriptObject) {
|
||||
this.overrides = true;
|
||||
final ScriptObject sobj = (ScriptObject)overrides;
|
||||
final Iterator<String> iter = sobj.propertyIterator();
|
||||
while (iter.hasNext()) {
|
||||
final String prop = iter.next();
|
||||
super.set(prop, sobj.get(prop), false);
|
||||
}
|
||||
this.addBoundProperties(sobj);
|
||||
} else {
|
||||
this.overrides = false;
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ public final class NativeJSON extends ScriptObject {
|
||||
final JSType type = JSType.of(value);
|
||||
if (type == JSType.OBJECT) {
|
||||
if (isArray(value)) {
|
||||
return JA((NativeArray)value, state);
|
||||
return JA((ScriptObject)value, state);
|
||||
} else if (value instanceof ScriptObject) {
|
||||
return JO((ScriptObject)value, state);
|
||||
}
|
||||
@ -315,7 +315,7 @@ public final class NativeJSON extends ScriptObject {
|
||||
}
|
||||
|
||||
// Spec: The abstract operation JA(value) serializes an array.
|
||||
private static Object JA(final NativeArray value, final StringifyState state) {
|
||||
private static Object JA(final ScriptObject value, final StringifyState state) {
|
||||
if (state.stack.containsKey(value)) {
|
||||
throw typeError("JSON.stringify.cyclic");
|
||||
}
|
||||
|
@ -523,23 +523,28 @@ public final class NativeRegExp extends ScriptObject {
|
||||
}
|
||||
|
||||
private RegExpResult execInner(final String string) {
|
||||
final boolean isGlobal = regexp.isGlobal();
|
||||
int start = getLastIndex();
|
||||
if (! regexp.isGlobal()) {
|
||||
if (!isGlobal) {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (start < 0 || start > string.length()) {
|
||||
setLastIndex(0);
|
||||
if (isGlobal) {
|
||||
setLastIndex(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final RegExpMatcher matcher = regexp.match(string);
|
||||
if (matcher == null || !matcher.search(start)) {
|
||||
setLastIndex(0);
|
||||
if (isGlobal) {
|
||||
setLastIndex(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (regexp.isGlobal()) {
|
||||
if (isGlobal) {
|
||||
setLastIndex(matcher.end());
|
||||
}
|
||||
|
||||
@ -548,6 +553,22 @@ public final class NativeRegExp extends ScriptObject {
|
||||
return match;
|
||||
}
|
||||
|
||||
// String.prototype.split method ignores the global flag and should not update lastIndex property.
|
||||
private RegExpResult execSplit(final String string, int start) {
|
||||
if (start < 0 || start > string.length()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final RegExpMatcher matcher = regexp.match(string);
|
||||
if (matcher == null || !matcher.search(start)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final RegExpResult match = new RegExpResult(string, matcher.start(), groups(matcher));
|
||||
globalObject.setLastRegExpResult(match);
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert java.util.regex.Matcher groups to JavaScript groups.
|
||||
* That is, replace null and groups that didn't match with undefined.
|
||||
@ -600,7 +621,7 @@ public final class NativeRegExp extends ScriptObject {
|
||||
* @return True if a match is found.
|
||||
*/
|
||||
public Object test(final String string) {
|
||||
return exec(string) != null;
|
||||
return execInner(string) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -765,35 +786,31 @@ public final class NativeRegExp extends ScriptObject {
|
||||
* @return Array of substrings.
|
||||
*/
|
||||
Object split(final String string, final long limit) {
|
||||
return split(this, string, limit);
|
||||
}
|
||||
|
||||
private static Object split(final NativeRegExp regexp0, final String input, final long limit) {
|
||||
final List<Object> matches = new ArrayList<>();
|
||||
|
||||
final NativeRegExp regexp = new NativeRegExp(regexp0);
|
||||
regexp.setGlobal(true);
|
||||
|
||||
if (limit == 0L) {
|
||||
return new NativeArray();
|
||||
}
|
||||
|
||||
final List<Object> matches = new ArrayList<>();
|
||||
|
||||
RegExpResult match;
|
||||
final int inputLength = input.length();
|
||||
final int inputLength = string.length();
|
||||
int lastLength = -1;
|
||||
int lastIndex = 0;
|
||||
int lastLastIndex = 0;
|
||||
|
||||
while ((match = regexp.execInner(input)) != null) {
|
||||
final int lastIndex = match.getIndex() + match.length();
|
||||
while ((match = execSplit(string, lastIndex)) != null) {
|
||||
lastIndex = match.getIndex() + match.length();
|
||||
|
||||
if (lastIndex > lastLastIndex) {
|
||||
matches.add(input.substring(lastLastIndex, match.getIndex()));
|
||||
if (match.getGroups().length > 1 && match.getIndex() < inputLength) {
|
||||
matches.addAll(Arrays.asList(match.getGroups()).subList(1, match.getGroups().length));
|
||||
matches.add(string.substring(lastLastIndex, match.getIndex()));
|
||||
final Object[] groups = match.getGroups();
|
||||
if (groups.length > 1 && match.getIndex() < inputLength) {
|
||||
for (int index = 1; index < groups.length && matches.size() < limit; index++) {
|
||||
matches.add(groups[index]);
|
||||
}
|
||||
}
|
||||
|
||||
lastLength = match.length();
|
||||
lastLastIndex = lastIndex;
|
||||
|
||||
if (matches.size() >= limit) {
|
||||
break;
|
||||
@ -801,8 +818,10 @@ public final class NativeRegExp extends ScriptObject {
|
||||
}
|
||||
|
||||
// bump the index to avoid infinite loop
|
||||
if (regexp.getLastIndex() == match.getIndex()) {
|
||||
regexp.setLastIndex(match.getIndex() + 1);
|
||||
if (lastIndex == lastLastIndex) {
|
||||
lastIndex++;
|
||||
} else {
|
||||
lastLastIndex = lastIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@ -810,12 +829,12 @@ public final class NativeRegExp extends ScriptObject {
|
||||
// check special case if we need to append an empty string at the
|
||||
// end of the match
|
||||
// if the lastIndex was the entire string
|
||||
if (lastLastIndex == input.length()) {
|
||||
if (lastLength > 0 || regexp.test("") == Boolean.FALSE) {
|
||||
if (lastLastIndex == string.length()) {
|
||||
if (lastLength > 0 || execSplit("", 0) == null) {
|
||||
matches.add("");
|
||||
}
|
||||
} else {
|
||||
matches.add(input.substring(lastLastIndex, inputLength));
|
||||
matches.add(string.substring(lastLastIndex, inputLength));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ public final class NativeRegExpExecResult extends ScriptObject {
|
||||
|
||||
NativeRegExpExecResult(final RegExpResult result) {
|
||||
setProto(Global.instance().getArrayPrototype());
|
||||
setIsArray();
|
||||
this.setArray(ArrayData.allocate(result.getGroups().clone()));
|
||||
this.index = result.getIndex();
|
||||
this.input = result.getInput();
|
||||
|
@ -25,11 +25,11 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -41,6 +41,7 @@ import java.util.List;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.nashorn.internal.lookup.MethodHandleFactory;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
@ -55,7 +56,6 @@ import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
|
||||
import jdk.nashorn.internal.lookup.MethodHandleFactory;
|
||||
import jdk.nashorn.internal.runtime.linker.NashornGuards;
|
||||
import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
|
||||
|
||||
@ -179,7 +179,6 @@ public final class NativeString extends ScriptObject {
|
||||
return ((ScriptObject) Global.toObject(self)).get(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static Object get(final Object self, final int key) {
|
||||
final CharSequence cs = JSType.toCharSequence(self);
|
||||
if (key >= 0 && key < cs.length()) {
|
||||
@ -842,7 +841,7 @@ public final class NativeString extends ScriptObject {
|
||||
final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit);
|
||||
|
||||
if (separator == UNDEFINED) {
|
||||
return new NativeArray(new Object[]{str});
|
||||
return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str});
|
||||
}
|
||||
|
||||
if (separator instanceof NativeRegExp) {
|
||||
@ -855,8 +854,9 @@ public final class NativeString extends ScriptObject {
|
||||
|
||||
private static Object splitString(String str, String separator, long limit) {
|
||||
if (separator.isEmpty()) {
|
||||
final Object[] array = new Object[str.length()];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
final int length = (int) Math.min(str.length(), limit);
|
||||
final Object[] array = new Object[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = String.valueOf(str.charAt(i));
|
||||
}
|
||||
return new NativeArray(array);
|
||||
|
@ -29,6 +29,7 @@ import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
|
||||
@ -71,17 +72,17 @@ public final class NativeUint32Array extends ArrayBufferView {
|
||||
|
||||
@Override
|
||||
protected long getLongImpl(final int key) {
|
||||
return getIntImpl(key) & 0xffff_ffffL;
|
||||
return getIntImpl(key) & JSType.MAX_UINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getDoubleImpl(final int key) {
|
||||
return getIntImpl(key) & 0xffff_ffffL;
|
||||
return getIntImpl(key) & JSType.MAX_UINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getObjectImpl(final int key) {
|
||||
return getIntImpl(key) & 0xffff_ffffL;
|
||||
return getIntImpl(key) & JSType.MAX_UINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,6 +42,10 @@ import jdk.nashorn.internal.lookup.Lookup;
|
||||
* function objects -- to expose properties like "prototype", "length" etc.
|
||||
*/
|
||||
public class ScriptFunctionImpl extends ScriptFunction {
|
||||
|
||||
/** Reference to constructor prototype. */
|
||||
private Object prototype;
|
||||
|
||||
// property map for strict mode functions
|
||||
private static final PropertyMap strictmodemap$;
|
||||
// property map for bound functions
|
||||
@ -49,6 +53,9 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
// property map for non-strict, non-bound functions.
|
||||
private static final PropertyMap nasgenmap$;
|
||||
|
||||
// Marker object for lazily initialized prototype object
|
||||
private static final Object LAZY_PROTOTYPE = new Object();
|
||||
|
||||
/**
|
||||
* Constructor called by Nasgen generated code, no membercount, use the default map.
|
||||
* Creates builtin functions only.
|
||||
@ -83,8 +90,8 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
* @param methodHandle handle for invocation
|
||||
* @param scope scope object
|
||||
* @param specs specialized versions of this method, if available, null otherwise
|
||||
* @param strict are we in strict mode
|
||||
* @param builtin is this a built-in function
|
||||
* @param isStrict are we in strict mode
|
||||
* @param isBuiltin is this a built-in function
|
||||
* @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted).
|
||||
*/
|
||||
ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) {
|
||||
@ -235,10 +242,23 @@ public class ScriptFunctionImpl extends ScriptFunction {
|
||||
return Global.objectPrototype();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object getPrototype() {
|
||||
if (prototype == LAZY_PROTOTYPE) {
|
||||
prototype = new PrototypeObject(this);
|
||||
}
|
||||
return prototype;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setPrototype(final Object prototype) {
|
||||
this.prototype = prototype;
|
||||
}
|
||||
|
||||
// Internals below..
|
||||
private void init() {
|
||||
this.setProto(Global.instance().getFunctionPrototype());
|
||||
this.setPrototype(new PrototypeObject(this));
|
||||
this.prototype = LAZY_PROTOTYPE;
|
||||
|
||||
if (isStrict()) {
|
||||
final ScriptFunction func = getTypeErrorThrower();
|
||||
|
@ -37,8 +37,8 @@ import jdk.nashorn.internal.runtime.ECMAErrors;
|
||||
import jdk.nashorn.internal.runtime.ErrorManager;
|
||||
import jdk.nashorn.internal.runtime.JSErrorType;
|
||||
import jdk.nashorn.internal.runtime.ParserException;
|
||||
import jdk.nashorn.internal.runtime.regexp.RegExpFactory;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
import jdk.nashorn.internal.runtime.regexp.RegExpFactory;
|
||||
|
||||
/**
|
||||
* Base class for parsers.
|
||||
@ -197,9 +197,10 @@ public abstract class AbstractParser {
|
||||
*
|
||||
* @param message Error message.
|
||||
* @param errorToken Offending token.
|
||||
* @return ParserException upon failure. Caller should throw and not ignore
|
||||
*/
|
||||
protected final void error(final String message, final long errorToken) {
|
||||
error(JSErrorType.SYNTAX_ERROR, message, errorToken);
|
||||
protected final ParserException error(final String message, final long errorToken) {
|
||||
return error(JSErrorType.SYNTAX_ERROR, message, errorToken);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -208,22 +209,24 @@ public abstract class AbstractParser {
|
||||
* @param errorType The error type
|
||||
* @param message Error message.
|
||||
* @param errorToken Offending token.
|
||||
* @return ParserException upon failure. Caller should throw and not ignore
|
||||
*/
|
||||
protected final void error(final JSErrorType errorType, final String message, final long errorToken) {
|
||||
protected final ParserException error(final JSErrorType errorType, final String message, final long errorToken) {
|
||||
final int position = Token.descPosition(errorToken);
|
||||
final int lineNum = source.getLine(position);
|
||||
final int columnNum = source.getColumn(position);
|
||||
final String formatted = ErrorManager.format(message, source, lineNum, columnNum, errorToken);
|
||||
throw new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
|
||||
return new ParserException(errorType, formatted, source, lineNum, columnNum, errorToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an error.
|
||||
*
|
||||
* @param message Error message.
|
||||
* @return ParserException upon failure. Caller should throw and not ignore
|
||||
*/
|
||||
protected final void error(final String message) {
|
||||
error(JSErrorType.SYNTAX_ERROR, message);
|
||||
protected final ParserException error(final String message) {
|
||||
return error(JSErrorType.SYNTAX_ERROR, message);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -231,13 +234,24 @@ public abstract class AbstractParser {
|
||||
*
|
||||
* @param errorType The error type
|
||||
* @param message Error message.
|
||||
* @return ParserException upon failure. Caller should throw and not ignore
|
||||
*/
|
||||
protected final void error(final JSErrorType errorType, final String message) {
|
||||
protected final ParserException error(final JSErrorType errorType, final String message) {
|
||||
// TODO - column needs to account for tabs.
|
||||
final int position = Token.descPosition(token);
|
||||
final int column = position - linePosition;
|
||||
final String formatted = ErrorManager.format(message, source, line, column, token);
|
||||
throw new ParserException(errorType, formatted, source, line, column, token);
|
||||
return new ParserException(errorType, formatted, source, line, column, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a warning to the error manager.
|
||||
*
|
||||
* @param errorType The error type of the warning
|
||||
* @param message Warning message.
|
||||
*/
|
||||
protected final void warning(final JSErrorType errorType, final String message, final long errorToken) {
|
||||
errors.warning(error(errorType, message, errorToken));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -270,7 +284,7 @@ public abstract class AbstractParser {
|
||||
*/
|
||||
protected final void expect(final TokenType expected) throws ParserException {
|
||||
if (type != expected) {
|
||||
error(expectMessage(expected));
|
||||
throw error(expectMessage(expected));
|
||||
}
|
||||
|
||||
next();
|
||||
@ -285,7 +299,7 @@ public abstract class AbstractParser {
|
||||
*/
|
||||
protected final Object expectValue(final TokenType expected) throws ParserException {
|
||||
if (type != expected) {
|
||||
error(expectMessage(expected));
|
||||
throw error(expectMessage(expected));
|
||||
}
|
||||
|
||||
final Object value = getValue();
|
||||
@ -429,7 +443,7 @@ public abstract class AbstractParser {
|
||||
try {
|
||||
RegExpFactory.validate(regex.getExpression(), regex.getOptions());
|
||||
} catch (final ParserException e) {
|
||||
error(e.getMessage());
|
||||
throw error(e.getMessage());
|
||||
}
|
||||
}
|
||||
node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value);
|
||||
|
@ -170,8 +170,7 @@ public class JSONParser extends AbstractParser {
|
||||
}
|
||||
case '"':
|
||||
case '\\':
|
||||
error(AbstractParser.message("unexpected.token", str));
|
||||
break;
|
||||
throw error(AbstractParser.message("unexpected.token", str));
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,14 +221,12 @@ public class JSONParser extends AbstractParser {
|
||||
return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value));
|
||||
}
|
||||
|
||||
error(AbstractParser.message("expected", "number", type.getNameOrType()));
|
||||
break;
|
||||
throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
|
||||
return null;
|
||||
throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,7 +262,7 @@ loop:
|
||||
elements.add(jsonLiteral());
|
||||
// Comma between array elements is mandatory in JSON.
|
||||
if (type != COMMARIGHT && type != RBRACKET) {
|
||||
error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
|
||||
throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -306,7 +303,7 @@ loop:
|
||||
|
||||
// Comma between property assigments is mandatory in JSON.
|
||||
if (type != RBRACE && type != COMMARIGHT) {
|
||||
error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
|
||||
throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -334,13 +331,11 @@ loop:
|
||||
if (name != null) {
|
||||
expect(COLON);
|
||||
final Node value = jsonLiteral();
|
||||
return new PropertyNode(source, propertyToken, value.getFinish(), name, value);
|
||||
return new PropertyNode(source, propertyToken, value.getFinish(), name, value, null, null);
|
||||
}
|
||||
|
||||
// Raise an error.
|
||||
error(AbstractParser.message("expected", "string", type.getNameOrType()));
|
||||
|
||||
return null;
|
||||
throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,6 +57,9 @@ import jdk.nashorn.internal.runtime.options.Options;
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
public class Lexer extends Scanner {
|
||||
private static final long MIN_INT_L = Integer.MIN_VALUE;
|
||||
private static final long MAX_INT_L = Integer.MAX_VALUE;
|
||||
|
||||
private static final boolean XML_LITERALS = Options.getBooleanProperty("nashorn.lexer.xmlliterals");
|
||||
|
||||
/** Content source. */
|
||||
@ -984,27 +987,27 @@ public class Lexer extends Scanner {
|
||||
*/
|
||||
private static Number valueOf(final String valueString, final int radix) throws NumberFormatException {
|
||||
try {
|
||||
return Integer.valueOf(valueString, radix);
|
||||
} catch (final NumberFormatException e) {
|
||||
try {
|
||||
return Long.valueOf(valueString, radix);
|
||||
} catch (final NumberFormatException e2) {
|
||||
if (radix == 10) {
|
||||
return Double.valueOf(valueString);
|
||||
}
|
||||
|
||||
double value = 0.0;
|
||||
|
||||
for (int i = 0; i < valueString.length(); i++) {
|
||||
final char ch = valueString.charAt(i);
|
||||
// Preverified, should always be a valid digit.
|
||||
final int digit = convertDigit(ch, radix);
|
||||
value *= radix;
|
||||
value += digit;
|
||||
}
|
||||
|
||||
return value;
|
||||
final long value = Long.parseLong(valueString, radix);
|
||||
if(value >= MIN_INT_L && value <= MAX_INT_L) {
|
||||
return Integer.valueOf((int)value);
|
||||
}
|
||||
return Long.valueOf(value);
|
||||
} catch (final NumberFormatException e) {
|
||||
if (radix == 10) {
|
||||
return Double.valueOf(valueString);
|
||||
}
|
||||
|
||||
double value = 0.0;
|
||||
|
||||
for (int i = 0; i < valueString.length(); i++) {
|
||||
final char ch = valueString.charAt(i);
|
||||
// Preverified, should always be a valid digit.
|
||||
final int digit = convertDigit(ch, radix);
|
||||
value *= radix;
|
||||
value += digit;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -280,6 +280,11 @@ public enum TokenType {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
static {
|
||||
// Avoid cloning of enumeration.
|
||||
values = TokenType.values();
|
||||
|
@ -54,10 +54,24 @@ import jdk.nashorn.internal.lookup.MethodHandleFactory;
|
||||
* @see SpillProperty
|
||||
*/
|
||||
public class AccessorProperty extends Property {
|
||||
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class);
|
||||
|
||||
private static final int NOOF_TYPES = getNumberOfAccessorTypes();
|
||||
|
||||
/**
|
||||
* Properties in different maps for the same structure class will share their field getters and setters. This could
|
||||
* be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now
|
||||
* these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler
|
||||
* for them.
|
||||
*/
|
||||
private static ClassValue<GettersSetters> GETTERS_SETTERS = new ClassValue<GettersSetters>() {
|
||||
@Override
|
||||
protected GettersSetters computeValue(Class<?> structure) {
|
||||
return new GettersSetters(structure);
|
||||
}
|
||||
};
|
||||
|
||||
/** Property getter cache */
|
||||
private MethodHandle[] getters = new MethodHandle[NOOF_TYPES];
|
||||
|
||||
@ -152,6 +166,22 @@ public class AccessorProperty extends Property {
|
||||
setCurrentType(getterType);
|
||||
}
|
||||
|
||||
private static class GettersSetters {
|
||||
final MethodHandle[] getters;
|
||||
final MethodHandle[] setters;
|
||||
|
||||
public GettersSetters(Class<?> structure) {
|
||||
final int fieldCount = ObjectClassGenerator.getFieldCount(structure);
|
||||
getters = new MethodHandle[fieldCount];
|
||||
setters = new MethodHandle[fieldCount];
|
||||
for(int i = 0; i < fieldCount; ++i) {
|
||||
final String fieldName = ObjectClassGenerator.getFieldName(i, Type.OBJECT);
|
||||
getters[i] = MH.getter(lookup, structure, fieldName, Type.OBJECT.getTypeClass());
|
||||
setters[i] = MH.setter(lookup, structure, fieldName, Type.OBJECT.getTypeClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for dual field AccessorPropertys.
|
||||
*
|
||||
@ -171,22 +201,19 @@ public class AccessorProperty extends Property {
|
||||
primitiveGetter = null;
|
||||
primitiveSetter = null;
|
||||
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
if (isParameter() && hasArguments()) {
|
||||
final MethodHandle arguments = MH.getter(MethodHandles.lookup(), structure, "arguments", Object.class);
|
||||
final MethodHandle arguments = MH.getter(lookup, structure, "arguments", Object.class);
|
||||
final MethodHandle argumentsSO = MH.asType(arguments, arguments.type().changeReturnType(ScriptObject.class));
|
||||
|
||||
objectGetter = MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
|
||||
objectSetter = MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
|
||||
} else {
|
||||
final String fieldNameObject = ObjectClassGenerator.getFieldName(slot, Type.OBJECT);
|
||||
final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
|
||||
|
||||
objectGetter = MH.getter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
|
||||
objectSetter = MH.setter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
|
||||
final GettersSetters gs = GETTERS_SETTERS.get(structure);
|
||||
objectGetter = gs.getters[slot];
|
||||
objectSetter = gs.setters[slot];
|
||||
|
||||
if (!OBJECT_FIELDS_ONLY) {
|
||||
final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
|
||||
primitiveGetter = MH.getter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
|
||||
primitiveSetter = MH.setter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
|
||||
}
|
||||
@ -365,7 +392,7 @@ public class AccessorProperty extends Property {
|
||||
}
|
||||
|
||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
return MH.findStatic(MethodHandles.lookup(), AccessorProperty.class, name, MH.type(rtype, types));
|
||||
return MH.findStatic(lookup, AccessorProperty.class, name, MH.type(rtype, types));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -54,10 +54,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.debug.ASTWriter;
|
||||
import jdk.nashorn.internal.ir.debug.PrintVisitor;
|
||||
import jdk.nashorn.internal.parser.Parser;
|
||||
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
|
||||
import jdk.nashorn.internal.runtime.options.Options;
|
||||
import sun.reflect.CallerSensitive;
|
||||
import sun.reflect.Reflection;
|
||||
|
||||
/**
|
||||
* This class manages the global state of execution. Context is immutable.
|
||||
@ -114,24 +111,9 @@ public final class Context {
|
||||
* Get the current global scope
|
||||
* @return the current global scope
|
||||
*/
|
||||
@CallerSensitive
|
||||
public static ScriptObject getGlobal() {
|
||||
final SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
// skip getCallerClass and getGlobal and get to the real caller
|
||||
Class<?> caller = Reflection.getCallerClass();
|
||||
ClassLoader callerLoader = caller.getClassLoader();
|
||||
|
||||
// Allow this method only for nashorn's own classes, objects
|
||||
// package classes and Java adapter classes. Rest should
|
||||
// have the necessary security permission.
|
||||
if (callerLoader != myLoader &&
|
||||
!(callerLoader instanceof StructureLoader) &&
|
||||
!(JavaAdapterFactory.isAdapterClass(caller))) {
|
||||
sm.checkPermission(new RuntimePermission("nashorn.getGlobal"));
|
||||
}
|
||||
}
|
||||
|
||||
// This class in a package.access protected package.
|
||||
// Trusted code only can call this method.
|
||||
return getGlobalTrusted();
|
||||
}
|
||||
|
||||
@ -399,7 +381,7 @@ public final class Context {
|
||||
// We need to get strict mode flag from compiled class. This is
|
||||
// because eval code may start with "use strict" directive.
|
||||
try {
|
||||
strictFlag = clazz.getField(STRICT_MODE.tag()).getBoolean(null);
|
||||
strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
|
||||
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
|
||||
//ignored
|
||||
strictFlag = false;
|
||||
@ -432,6 +414,28 @@ public final class Context {
|
||||
return ScriptRuntime.apply(func, evalThis);
|
||||
}
|
||||
|
||||
private Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
|
||||
if (srcStr.startsWith(prefix)) {
|
||||
final String resource = resourcePath + srcStr.substring(prefix.length());
|
||||
// NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
|
||||
// These scripts are always available and are loaded from nashorn.jar's resources.
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedAction<Source>() {
|
||||
@Override
|
||||
public Source run() {
|
||||
try {
|
||||
final URL resURL = Context.class.getResource(resource);
|
||||
return (resURL != null)? new Source(srcStr, resURL) : null;
|
||||
} catch (final IOException exp) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@code load} Nashorn extension. Load a script file from a source
|
||||
* expression
|
||||
@ -444,33 +448,18 @@ public final class Context {
|
||||
* @throws IOException if source cannot be found or loaded
|
||||
*/
|
||||
public Object load(final ScriptObject scope, final Object from) throws IOException {
|
||||
Object src = (from instanceof ConsString)? from.toString() : from;
|
||||
final Object src = (from instanceof ConsString)? from.toString() : from;
|
||||
Source source = null;
|
||||
|
||||
// load accepts a String (which could be a URL or a file name), a File, a URL
|
||||
// or a ScriptObject that has "name" and "source" (string valued) properties.
|
||||
if (src instanceof String) {
|
||||
final String srcStr = (String)src;
|
||||
final File file = new File(srcStr);
|
||||
final File file = new File(srcStr);
|
||||
if (srcStr.indexOf(':') != -1) {
|
||||
if (srcStr.startsWith("nashorn:")) {
|
||||
final String resource = "resources/" + srcStr.substring("nashorn:".length());
|
||||
// NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
|
||||
// These scripts are always available and are loaded from nashorn.jar's resources.
|
||||
source = AccessController.doPrivileged(
|
||||
new PrivilegedAction<Source>() {
|
||||
@Override
|
||||
public Source run() {
|
||||
try {
|
||||
final URL resURL = Context.class.getResource(resource);
|
||||
return (resURL != null)? new Source(srcStr, resURL) : null;
|
||||
} catch (final IOException exp) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
URL url = null;
|
||||
if ((source = loadInternal(srcStr, "nashorn:", "resources/")) == null &&
|
||||
(source = loadInternal(srcStr, "fx:", "resources/fx/")) == null) {
|
||||
URL url;
|
||||
try {
|
||||
//check for malformed url. if malformed, it may still be a valid file
|
||||
url = new URL(srcStr);
|
||||
@ -713,7 +702,7 @@ public final class Context {
|
||||
MH.findStatic(
|
||||
MethodHandles.lookup(),
|
||||
script,
|
||||
RUN_SCRIPT.tag(),
|
||||
RUN_SCRIPT.symbolName(),
|
||||
MH.type(
|
||||
Object.class,
|
||||
ScriptFunction.class,
|
||||
@ -722,13 +711,13 @@ public final class Context {
|
||||
boolean strict;
|
||||
|
||||
try {
|
||||
strict = script.getField(STRICT_MODE.tag()).getBoolean(null);
|
||||
strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
|
||||
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
|
||||
strict = false;
|
||||
}
|
||||
|
||||
// Package as a JavaScript function and pass function back to shell.
|
||||
return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.tag(), runMethodHandle, scope, strict);
|
||||
return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict);
|
||||
}
|
||||
|
||||
private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
|
||||
@ -746,13 +735,13 @@ public final class Context {
|
||||
global = (GlobalObject)Context.getGlobalTrusted();
|
||||
script = global.findCachedClass(source);
|
||||
if (script != null) {
|
||||
Compiler.LOG.fine("Code cache hit for " + source + " avoiding recompile.");
|
||||
Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile.");
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
|
||||
if (errors.hasErrors() || env._parse_only) {
|
||||
if (errors.hasErrors()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -764,6 +753,10 @@ public final class Context {
|
||||
getErr().println(new PrintVisitor(functionNode));
|
||||
}
|
||||
|
||||
if (env._parse_only) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final URL url = source.getURL();
|
||||
final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
|
||||
final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null);
|
||||
|
@ -135,7 +135,16 @@ public final class DebugLogger {
|
||||
* @param str the string to log
|
||||
*/
|
||||
public void finest(final String str) {
|
||||
log(str, Level.FINEST);
|
||||
log(Level.FINEST, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for outputting a log string as log level
|
||||
* {@link java.util.logging.Level#FINEST} on this logger
|
||||
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
|
||||
*/
|
||||
public void finest(final Object... objs) {
|
||||
log(Level.FINEST, objs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,7 +153,16 @@ public final class DebugLogger {
|
||||
* @param str the string to log
|
||||
*/
|
||||
public void finer(final String str) {
|
||||
log(str, Level.FINER);
|
||||
log(Level.FINER, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for outputting a log string as log level
|
||||
* {@link java.util.logging.Level#FINER} on this logger
|
||||
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
|
||||
*/
|
||||
public void finer(final Object... objs) {
|
||||
log(Level.FINER, objs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,7 +171,16 @@ public final class DebugLogger {
|
||||
* @param str the string to log
|
||||
*/
|
||||
public void fine(final String str) {
|
||||
log(str, Level.FINE);
|
||||
log(Level.FINE, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for outputting a log string as log level
|
||||
* {@link java.util.logging.Level#FINE} on this logger
|
||||
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
|
||||
*/
|
||||
public void fine(final Object... objs) {
|
||||
log(Level.FINE, objs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,7 +189,16 @@ public final class DebugLogger {
|
||||
* @param str the string to log
|
||||
*/
|
||||
public void config(final String str) {
|
||||
log(str, Level.CONFIG);
|
||||
log(Level.CONFIG, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for outputting a log string as log level
|
||||
* {@link java.util.logging.Level#CONFIG} on this logger
|
||||
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
|
||||
*/
|
||||
public void config(final Object... objs) {
|
||||
log(Level.CONFIG, objs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,7 +207,16 @@ public final class DebugLogger {
|
||||
* @param str the string to log
|
||||
*/
|
||||
public void info(final String str) {
|
||||
log(str, Level.INFO);
|
||||
log(Level.INFO, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for outputting a log string as log level
|
||||
* {@link java.util.logging.Level#FINE} on this logger
|
||||
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
|
||||
*/
|
||||
public void info(final Object... objs) {
|
||||
log(Level.INFO, objs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,7 +225,16 @@ public final class DebugLogger {
|
||||
* @param str the string to log
|
||||
*/
|
||||
public void warning(final String str) {
|
||||
log(str, Level.WARNING);
|
||||
log(Level.WARNING, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for outputting a log string as log level
|
||||
* {@link java.util.logging.Level#FINE} on this logger
|
||||
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
|
||||
*/
|
||||
public void warning(final Object... objs) {
|
||||
log(Level.WARNING, objs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,20 +243,28 @@ public final class DebugLogger {
|
||||
* @param str the string to log
|
||||
*/
|
||||
public void severe(final String str) {
|
||||
log(str, Level.SEVERE);
|
||||
log(Level.SEVERE, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorthand for outputting a log string as log level
|
||||
* {@link java.util.logging.Level#FINE} on this logger
|
||||
* @param objs object array to log - use this to perform lazy concatenation to avoid unconditional toString overhead
|
||||
*/
|
||||
public void severe(final Object... objs) {
|
||||
log(Level.SEVERE, objs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output log line on this logger at a given level of verbosity
|
||||
* @see java.util.logging.Level
|
||||
*
|
||||
* @param str string to log
|
||||
* @param level minimum log level required for logging to take place
|
||||
* @param str string to log
|
||||
*/
|
||||
public void log(final String str, final Level level) {
|
||||
public void log(final Level level, final String str) {
|
||||
if (isEnabled) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0 ; i < indent ; i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
@ -210,4 +272,24 @@ public final class DebugLogger {
|
||||
logger.log(level, sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output log line on this logger at a given level of verbosity
|
||||
* @see java.util.logging.Level
|
||||
*
|
||||
* @param level minimum log level required for logging to take place
|
||||
* @param objs objects for which to invoke toString and concatenate to log
|
||||
*/
|
||||
public void log(final Level level, final Object... objs) {
|
||||
if (isEnabled) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0 ; i < indent ; i++) {
|
||||
sb.append(' ');
|
||||
}
|
||||
for (final Object obj : objs) {
|
||||
sb.append(obj);
|
||||
}
|
||||
logger.log(level, sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user