This commit is contained in:
Lana Steuck 2013-05-06 11:53:58 -07:00
commit 12203fae88
171 changed files with 9127 additions and 7267 deletions

View File

@ -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}"

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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"/>

View File

@ -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}

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)));
}
}

View File

@ -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;
}

View File

@ -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);
}
/**

View File

@ -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));

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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();

View File

@ -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;

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);

View File

@ -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";

View File

@ -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);

View File

@ -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;

View File

@ -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";

View File

@ -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());
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View 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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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);
}
}

View 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);
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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();
}
/**

View 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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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));
}
/**

View File

@ -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());
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
/**

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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
}

View File

@ -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";
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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());

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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));
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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()));
}
}

View File

@ -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

View File

@ -280,6 +280,11 @@ public enum TokenType {
return values;
}
@Override
public String toString() {
return name;
}
static {
// Avoid cloning of enumeration.
values = TokenType.values();

View File

@ -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));
}
}

View File

@ -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);

View File

@ -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