This commit is contained in:
Athijegannathan Sundararajan 2013-04-22 19:57:57 +05:30
commit ae84082ad8
94 changed files with 5913 additions and 6885 deletions

View File

@ -26,7 +26,7 @@ if [ -z $ITERS ]; then
ITERS=7 ITERS=7
fi fi
NASHORN_JAR=dist/nashorn.jar 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}" JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}"
OCTANE_ARGS="--verbose --iterations ${ITERS}" OCTANE_ARGS="--verbose --iterations ${ITERS}"

View File

@ -397,10 +397,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
} }
setContextVariables(ctxt); setContextVariables(ctxt);
final Object val = ctxt.getAttribute(ScriptEngine.FILENAME); return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
final String fileName = (val != null) ? val.toString() : "<eval>";
Object res = ScriptRuntime.apply(script, ctxtGlobal);
return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(res, ctxtGlobal));
} catch (final Exception e) { } catch (final Exception e) {
throwAsScriptException(e); throwAsScriptException(e);
throw new AssertionError("should not reach here"); 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.EnumSet;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptObject;
@ -219,14 +221,14 @@ public class ClassEmitter implements Emitter {
private void defineCommonStatics(final boolean strictMode) { private void defineCommonStatics(final boolean strictMode) {
// source - used to store the source data (text) for this script. Shared across // source - used to store the source data (text) for this script. Shared across
// compile units. Set externally by the compiler. // 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 // constants - used to the constants array for this script. Shared across
// compile units. Set externally by the compiler. // 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. // 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)) { if (constantMethodNeeded.contains(String.class)) {
// $getString - get the ith entry from the constants table and cast to String. // $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.begin();
getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor()) getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0) .load(Type.INT, 0)
.arrayload() .arrayload()
.checkcast(String.class) .checkcast(String.class)
@ -250,7 +252,7 @@ public class ClassEmitter implements Emitter {
if (constantMethodNeeded.contains(PropertyMap.class)) { if (constantMethodNeeded.contains(PropertyMap.class)) {
// $getMap - get the ith entry from the constants table and cast to PropertyMap. // $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.begin();
getMapMethod.loadConstants() getMapMethod.loadConstants()
.load(Type.INT, 0) .load(Type.INT, 0)
@ -260,7 +262,7 @@ public class ClassEmitter implements Emitter {
getMapMethod.end(); getMapMethod.end();
// $setMap - overwrite an existing map. // $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.begin();
setMapMethod.loadConstants() setMapMethod.loadConstants()
.load(Type.INT, 0) .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); final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
getArrayMethod.begin(); getArrayMethod.begin();
getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor()) getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
.load(Type.INT, 0) .load(Type.INT, 0)
.arrayload() .arrayload()
.checkcast(cls) .checkcast(cls)
@ -307,7 +309,7 @@ public class ClassEmitter implements Emitter {
*/ */
static String getArrayMethodName(final Class<?> cls) { static String getArrayMethodName(final Class<?> cls) {
assert cls.isArray(); 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); 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 * 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 * @return method emitter to use for weaving this method
*/ */
MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 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> * @return method emitter to use for weaving <clinit>
*/ */
MethodEmitter 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 * @return method emitter to use for weaving <init>()V
*/ */
MethodEmitter init() { 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 * @return method emitter to use for weaving <init>()V
*/ */
MethodEmitter init(final Class<?>... ptypes) { 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 * @return method emitter to use for weaving <init>(...)V
*/ */
MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) { 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; 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.HashSet;
import java.util.Set; import java.util.Set;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
@ -29,8 +30,8 @@ import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.Timing; import jdk.nashorn.internal.runtime.Timing;
/** /**
* A compilation phase is a step in the processes of turning a JavaScript FunctionNode * A compilation phase is a step in the processes of turning a JavaScript
* into bytecode. It has an optional return value. * FunctionNode into bytecode. It has an optional return value.
*/ */
enum CompilationPhase { enum CompilationPhase {
@ -41,77 +42,87 @@ enum CompilationPhase {
*/ */
LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) { LAZY_INITIALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override @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 * For lazy compilation, we might be given a node previously marked
* to compile as the outermost function node in the compiler. Unmark it * as lazy to compile as the outermost function node in the
* so it can be compiled and not cause recursion. Make sure the return type * compiler. Unmark it so it can be compiled and not cause
* is unknown so it can be correctly deduced. Return types are always * recursion. Make sure the return type is unknown so it can be
* Objects in Lazy nodes as we haven't got a change to generate code for * correctly deduced. Return types are always Objects in Lazy nodes
* them and decude its parameter specialization * 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 * TODO: in the future specializations from a callsite will be
* so we can generate a better non-lazy version of a function from a trampoline * 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(); final FunctionNode outermostFunctionNode = compiler.getFunctionNode();
outermostFunctionNode.setIsLazy(false); assert outermostFunctionNode == fn0;
outermostFunctionNode.setReturnType(Type.UNKNOWN);
final Set<FunctionNode> neverLazy = new HashSet<>(); final Set<FunctionNode> neverLazy = new HashSet<>();
final Set<FunctionNode> lazy = new HashSet<>(); final Set<FunctionNode> lazy = new HashSet<>();
outermostFunctionNode.accept(new NodeVisitor() { FunctionNode newFunctionNode = outermostFunctionNode;
// self references are done with invokestatic and thus cannot have trampolines - never lazy
newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeVisitor() {
// self references are done with invokestatic and thus cannot
// have trampolines - never lazy
@Override @Override
public Node enterCallNode(final CallNode node) { public boolean enterCallNode(final CallNode node) {
final Node callee = node.getFunction(); final Node callee = node.getFunction();
if (callee instanceof FunctionNode) { if (callee instanceof FunctionNode) {
neverLazy.add(((FunctionNode)callee)); 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 @Override
public Node enterFunctionNode(final FunctionNode node) { public boolean enterFunctionNode(final FunctionNode node) {
if (node == outermostFunctionNode) {
return node;
}
assert compiler.isLazy(); assert compiler.isLazy();
lazy.add(node); lazy.add(node);
return true;
//also needs scope, potentially needs arguments etc etc
return node;
} }
}); });
//at least one method is non lazy - the outermost one
neverLazy.add(newFunctionNode);
for (final FunctionNode node : neverLazy) { for (final FunctionNode node : neverLazy) {
Compiler.LOG.fine("Marking " + node.getName() + " as non lazy, as it's a self reference"); Compiler.LOG.fine(
node.setIsLazy(false); "Marking ",
node.getName(),
" as non lazy, as it's a self reference");
lazy.remove(node); lazy.remove(node);
} }
outermostFunctionNode.accept(new NodeOperatorVisitor() { newFunctionNode = (FunctionNode)newFunctionNode.accept(new NodeOperatorVisitor() {
private final LexicalContext lexicalContext = new LexicalContext();
@Override @Override
public Node enterFunctionNode(FunctionNode functionNode) { public Node leaveFunctionNode(final FunctionNode functionNode) {
lexicalContext.push(functionNode); final LexicalContext lc = getLexicalContext();
if(lazy.contains(functionNode)) { if (lazy.contains(functionNode)) {
Compiler.LOG.fine("Marking " + functionNode.getName() + " as lazy"); Compiler.LOG.fine(
functionNode.setIsLazy(true); "Marking ",
lexicalContext.getParentFunction(functionNode).setHasLazyChildren(); 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;
} return functionNode.
@Override clearFlag(lc, FunctionNode.IS_LAZY).
public Node leaveFunctionNode(FunctionNode functionNode) { setReturnType(lc, Type.UNKNOWN);
lexicalContext.pop(functionNode);
return functionNode;
} }
}); });
return newFunctionNode;
} }
@Override @Override
@ -121,13 +132,13 @@ enum CompilationPhase {
}, },
/* /*
* Constant folding pass * Constant folding pass Simple constant folding that will make elementary
* Simple constant folding that will make elementary constructs go away * constructs go away
*/ */
CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) { CONSTANT_FOLDING_PHASE(EnumSet.of(INITIALIZED, PARSED)) {
@Override @Override
void transform(final Compiler compiler, final FunctionNode fn) { FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new FoldConstants()); return (FunctionNode)fn.accept(new FoldConstants());
} }
@Override @Override
@ -137,18 +148,16 @@ enum CompilationPhase {
}, },
/* /*
* Lower (Control flow pass) * Lower (Control flow pass) Finalizes the control flow. Clones blocks for
* Finalizes the control flow. Clones blocks for finally constructs and * finally constructs and similar things. Establishes termination criteria
* similar things. Establishes termination criteria for nodes * for nodes Guarantee return instructions to method making sure control
* Guarantee return instructions to method making sure control flow * flow cannot fall off the end. Replacing high level nodes with lower such
* cannot fall off the end. Replacing high level nodes with lower such * as runtime nodes where applicable.
* as runtime nodes where applicable.
*
*/ */
LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) { LOWERING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED)) {
@Override @Override
void transform(final Compiler compiler, final FunctionNode fn) { FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new Lower()); return (FunctionNode)fn.accept(new Lower());
} }
@Override @Override
@ -158,13 +167,27 @@ enum CompilationPhase {
}, },
/* /*
* Attribution * Attribution Assign symbols and types to all nodes.
* Assign symbols and types to all nodes.
*/ */
ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) { ATTRIBUTION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED)) {
@Override @Override
void transform(final Compiler compiler, final FunctionNode fn) { FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
fn.accept(new Attr()); 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 @Override
@ -174,25 +197,35 @@ enum CompilationPhase {
}, },
/* /*
* Splitter * Splitter Split the AST into several compile units based on a size
* Split the AST into several compile units based on a size heuristic * heuristic Splitter needs attributed AST for weight calculations (e.g. is
* Splitter needs attributed AST for weight calculations (e.g. is * a + b a ScriptRuntime.ADD with call overhead or a dadd with much less).
* a + b a ScriptRuntime.ADD with call overhead or a dadd with much * Split IR can lead to scope information being changed.
* less). Split IR can lead to scope information being changed.
*/ */
SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) { SPLITTING_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR)) {
@Override @Override
void transform(final Compiler compiler, final FunctionNode fn) { FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final CompileUnit outermostCompileUnit = compiler.addCompileUnit(compiler.firstCompileUnitName()); 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(); assert compiler.getStrictMode();
compiler.setStrictMode(true); 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 @Override
@ -204,30 +237,32 @@ enum CompilationPhase {
/* /*
* FinalizeTypes * FinalizeTypes
* *
* This pass finalizes the types for nodes. If Attr created wider types than * 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 * known during the first pass, convert nodes are inserted or access nodes
* are specialized where scope accesses. * are specialized where scope accesses.
* *
* Runtime nodes may be removed and primitivized or reintroduced depending * Runtime nodes may be removed and primitivized or reintroduced depending
* on information that was established in Attr. * on information that was established in Attr.
* *
* Contract: all variables must have slot assignments and scope assignments * Contract: all variables must have slot assignments and scope assignments
* before type finalization. * before type finalization.
*/ */
TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) { TYPE_FINALIZATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT)) {
@Override @Override
void transform(final Compiler compiler, final FunctionNode fn) { FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv(); final ScriptEnvironment env = compiler.getEnv();
fn.accept(new FinalizeTypes()); final FunctionNode newFunctionNode = (FunctionNode)fn.accept(new FinalizeTypes());
if (env._print_lower_ast) { if (env._print_lower_ast) {
env.getErr().println(new ASTWriter(fn)); env.getErr().println(new ASTWriter(newFunctionNode));
} }
if (env._print_lower_parse) { if (env._print_lower_parse) {
env.getErr().println(new PrintVisitor(fn)); env.getErr().println(new PrintVisitor(newFunctionNode));
} }
return newFunctionNode;
} }
@Override @Override
@ -239,31 +274,21 @@ enum CompilationPhase {
/* /*
* Bytecode generation: * 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)) { BYTECODE_GENERATION_PHASE(EnumSet.of(INITIALIZED, PARSED, CONSTANT_FOLDED, LOWERED, ATTR, SPLIT, FINALIZED)) {
@Override @Override
void transform(final Compiler compiler, final FunctionNode fn) { FunctionNode transform(final Compiler compiler, final FunctionNode fn) {
final ScriptEnvironment env = compiler.getEnv(); final ScriptEnvironment env = compiler.getEnv();
FunctionNode newFunctionNode = fn;
try { try {
final CodeGenerator codegen = new CodeGenerator(compiler); final CodeGenerator codegen = new CodeGenerator(compiler);
fn.accept(codegen); newFunctionNode = (FunctionNode)newFunctionNode.accept(codegen);
codegen.generateScopeCalls(); 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) { } catch (final VerifyError e) {
if (env._verify_code || env._print_code) { 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) { if (env._dump_on_error) {
e.printStackTrace(env.getErr()); e.printStackTrace(env.getErr());
} }
@ -283,25 +308,25 @@ enum CompilationPhase {
compiler.addClass(className, bytecode); 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) { if (env._print_code) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append("class: " + className). sb.append("class: " + className).append('\n')
append('\n'). .append(ClassEmitter.disassemble(bytecode))
append(ClassEmitter.disassemble(bytecode)). .append("=====");
append("=====");
env.getErr().println(sb); env.getErr().println(sb);
} }
//should we verify the generated code? // should we verify the generated code?
if (env._verify_code) { if (env._verify_code) {
compiler.getCodeInstaller().verify(bytecode); 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) { if (env._dest_dir != null && env._compile_only) {
final String fileName = className.replace('.', File.separatorChar) + ".class"; 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) { if (index != -1) {
final File dir = new File(fileName.substring(0, index)); final File dir = new File(fileName.substring(0, index));
@ -314,11 +339,18 @@ enum CompilationPhase {
fos.write(bytecode); fos.write(bytecode);
} }
} catch (final IOException e) { } 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 @Override
@ -340,26 +372,28 @@ enum CompilationPhase {
return functionNode.hasState(pre); return functionNode.hasState(pre);
} }
protected void begin(final FunctionNode functionNode) { protected FunctionNode begin(final FunctionNode functionNode) {
if (pre != null) { if (pre != null) {
//check that everything in pre is present // check that everything in pre is present
for (final CompilationState state : pre) { for (final CompilationState state : pre) {
assert functionNode.hasState(state); assert functionNode.hasState(state);
} }
//check that nothing else is present // check that nothing else is present
for (final CompilationState state : CompilationState.values()) { for (final CompilationState state : CompilationState.values()) {
assert !(functionNode.hasState(state) && !pre.contains(state)); assert !(functionNode.hasState(state) && !pre.contains(state));
} }
} }
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
return functionNode;
} }
protected void end(final FunctionNode functionNode) { protected FunctionNode end(final FunctionNode functionNode) {
endTime = System.currentTimeMillis(); endTime = System.currentTimeMillis();
Timing.accumulateTime(toString(), endTime - startTime); Timing.accumulateTime(toString(), endTime - startTime);
isFinished = true; isFinished = true;
return functionNode;
} }
boolean isFinished() { boolean isFinished() {
@ -374,15 +408,13 @@ enum CompilationPhase {
return endTime; 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)) { 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); return end(transform(compiler, begin(functionNode)));
transform(compiler, functionNode);
end(functionNode);
} }
} }

View File

@ -25,12 +25,16 @@
package jdk.nashorn.internal.codegen; 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.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME; 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.LAZY;
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -46,13 +50,12 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.logging.Level; import java.util.logging.Level;
import jdk.internal.dynalink.support.NameCodec; import jdk.internal.dynalink.support.NameCodec;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState; 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.CodeInstaller;
import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptEnvironment;
@ -80,8 +83,6 @@ public final class Compiler {
private final ConstantData constantData; private final ConstantData constantData;
private final FunctionNode functionNode;
private final CompilationSequence sequence; private final CompilationSequence sequence;
private final ScriptEnvironment env; private final ScriptEnvironment env;
@ -90,6 +91,8 @@ public final class Compiler {
private boolean strict; private boolean strict;
private FunctionNode functionNode;
private CodeInstaller<ScriptEnvironment> installer; private CodeInstaller<ScriptEnvironment> installer;
/** logger for compiler, trampolines, splits and related code generation events /** logger for compiler, trampolines, splits and related code generation events
@ -103,8 +106,12 @@ public final class Compiler {
* during a compile. * during a compile.
*/ */
private static String[] RESERVED_NAMES = { private static String[] RESERVED_NAMES = {
SCOPE.tag(), SCOPE.symbolName(),
THIS.tag() 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) { private static String lazyTag(final FunctionNode functionNode) {
if (functionNode.isLazy()) { if (functionNode.isLazy()) {
return '$' + LAZY.tag() + '$' + functionNode.getName(); return '$' + LAZY.symbolName() + '$' + functionNode.getName();
} }
return ""; return "";
} }
@ -205,13 +212,13 @@ public final class Compiler {
this.functionNode = functionNode; this.functionNode = functionNode;
this.sequence = sequence; this.sequence = sequence;
this.installer = installer; this.installer = installer;
this.strict = strict || functionNode.isStrictMode(); this.strict = strict || functionNode.isStrict();
this.constantData = new ConstantData(); this.constantData = new ConstantData();
this.compileUnits = new HashSet<>(); this.compileUnits = new HashSet<>();
this.bytecode = new HashMap<>(); this.bytecode = new HashMap<>();
final StringBuilder sb = new StringBuilder(); 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('$').
append(safeSourceName(functionNode.getSource())); append(safeSourceName(functionNode.getSource()));
@ -253,9 +260,9 @@ public final class Compiler {
* Execute the compilation this Compiler was created with * Execute the compilation this Compiler was created with
* @params param types if known, for specialization * @params param types if known, for specialization
* @throws CompilationException if something goes wrong * @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); return compile(null);
} }
@ -263,9 +270,9 @@ public final class Compiler {
* Execute the compilation this Compiler was created with * Execute the compilation this Compiler was created with
* @param paramTypes param types if known, for specialization * @param paramTypes param types if known, for specialization
* @throws CompilationException if something goes wrong * @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) { for (final String reservedName : RESERVED_NAMES) {
functionNode.uniqueName(reservedName); functionNode.uniqueName(reservedName);
} }
@ -276,7 +283,7 @@ public final class Compiler {
long time = 0L; long time = 0L;
for (final CompilationPhase phase : sequence) { 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; final long duration = Timing.isEnabled() ? (phase.getEndTime() - phase.getStartTime()) : 0L;
time += duration; time += duration;
@ -295,7 +302,7 @@ public final class Compiler {
append(" ms "); append(" ms ");
} }
LOG.fine(sb.toString()); LOG.fine(sb);
} }
} }
@ -311,14 +318,14 @@ public final class Compiler {
append(" ms"); append(" ms");
} }
LOG.info(sb.toString()); LOG.info(sb);
} }
return this; return functionNode;
} }
private Class<?> install(final String className, final byte[] code) { 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); final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
@ -330,8 +337,8 @@ public final class Compiler {
@Override @Override
public Void run() throws Exception { public Void run() throws Exception {
//use reflection to write source and constants table to installed classes //use reflection to write source and constants table to installed classes
final Field sourceField = clazz.getDeclaredField(SOURCE.tag()); final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag()); final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
sourceField.setAccessible(true); sourceField.setAccessible(true);
constantsField.setAccessible(true); constantsField.setAccessible(true);
sourceField.set(null, source); sourceField.set(null, source);
@ -380,17 +387,6 @@ public final class Compiler {
unit.setCode(installedClasses.get(unit.getUnitClassName())); 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; final StringBuilder sb;
if (LOG.isEnabled()) { if (LOG.isEnabled()) {
sb = new StringBuilder(); sb = new StringBuilder();
@ -416,7 +412,7 @@ public final class Compiler {
} }
if (sb != null) { if (sb != null) {
LOG.info(sb.toString()); LOG.info(sb);
} }
return rootClass; return rootClass;
@ -495,7 +491,7 @@ public final class Compiler {
private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) { private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight); final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
compileUnits.add(compileUnit); compileUnits.add(compileUnit);
LOG.fine("Added compile unit " + compileUnit); LOG.fine("Added compile unit ", compileUnit);
return compileUnit; return compileUnit;
} }

View File

@ -52,9 +52,6 @@ public enum CompilerConstants {
/** lazy prefix for classes of jitted methods */ /** lazy prefix for classes of jitted methods */
LAZY("Lazy"), LAZY("Lazy"),
/** leaf tag used for functions that require no scope */
LEAF("__leaf__"),
/** constructor name */ /** constructor name */
INIT("<init>"), INIT("<init>"),
@ -96,7 +93,7 @@ public enum CompilerConstants {
SCOPE("__scope__", ScriptObject.class, 2), SCOPE("__scope__", ScriptObject.class, 2),
/** the return value variable name were intermediate results are stored for scripts */ /** the return value variable name were intermediate results are stored for scripts */
SCRIPT_RETURN("__return__"), RETURN("__return__"),
/** the callee value variable when necessary */ /** the callee value variable when necessary */
CALLEE("__callee__", ScriptFunction.class), CALLEE("__callee__", ScriptFunction.class),
@ -167,30 +164,30 @@ public enum CompilerConstants {
/** get array suffix */ /** get array suffix */
GET_ARRAY_SUFFIX("$array"); GET_ARRAY_SUFFIX("$array");
private final String tag; private final String symbolName;
private final Class<?> type; private final Class<?> type;
private final int slot; private final int slot;
private CompilerConstants() { private CompilerConstants() {
this.tag = name(); this.symbolName = name();
this.type = null; this.type = null;
this.slot = -1; this.slot = -1;
} }
private CompilerConstants(final String tag) { private CompilerConstants(final String symbolName) {
this(tag, -1); this(symbolName, -1);
} }
private CompilerConstants(final String tag, final int slot) { private CompilerConstants(final String symbolName, final int slot) {
this(tag, null, slot); this(symbolName, null, slot);
} }
private CompilerConstants(final String tag, final Class<?> type) { private CompilerConstants(final String symbolName, final Class<?> type) {
this(tag, type, -1); this(symbolName, type, -1);
} }
private CompilerConstants(final String tag, final Class<?> type, final int slot) { private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
this.tag = tag; this.symbolName = symbolName;
this.type = type; this.type = type;
this.slot = slot; this.slot = slot;
} }
@ -202,8 +199,8 @@ public enum CompilerConstants {
* *
* @return the tag * @return the tag
*/ */
public final String tag() { public final String symbolName() {
return tag; return symbolName;
} }
/** /**
@ -277,7 +274,7 @@ public enum CompilerConstants {
* @return Call representing void constructor for type * @return Call representing void constructor for type
*/ */
public static Call constructorNoLookup(final Class<?> clazz) { 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 * @return Call representing constructor for type
*/ */
public static Call constructorNoLookup(final String className, final Class<?>... ptypes) { 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 * @return Call representing constructor for type
*/ */
public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) { 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; package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; 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.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT; 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 * @param method the method emitter to use
*/ */
protected void loadScope(final MethodEmitter method) { protected void loadScope(final MethodEmitter method) {
method.loadScope(); method.loadCompilerConstant(SCOPE);
} }
/** /**
@ -105,7 +106,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator {
loadScope(method); loadScope(method);
if (hasArguments()) { if (hasArguments()) {
method.loadArguments(); method.loadCompilerConstant(ARGUMENTS);
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type())); method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
} else { } else {
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class)); method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));

View File

@ -25,18 +25,22 @@
package jdk.nashorn.internal.codegen; 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.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.Assignment; import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CallNode.EvalArgs;
import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
@ -85,18 +89,11 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private static final DebugLogger LOG = new DebugLogger("finalize"); private static final DebugLogger LOG = new DebugLogger("finalize");
private final LexicalContext lexicalContext = new LexicalContext();
FinalizeTypes() { FinalizeTypes() {
} }
@Override @Override
public Node leaveCallNode(final CallNode callNode) { 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 // AccessSpecializer - call return type may change the access for this location
final Node function = callNode.getFunction(); final Node function = callNode.getFunction();
if (function instanceof FunctionNode) { if (function instanceof FunctionNode) {
@ -133,8 +130,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override @Override
public Node leaveNEW(final UnaryNode unaryNode) { public Node leaveNEW(final UnaryNode unaryNode) {
assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject(); assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
((CallNode)unaryNode.rhs()).setIsNew(); return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew());
return unaryNode;
} }
@Override @Override
@ -254,7 +250,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override @Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) { public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null; 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 // 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 // in that case, update the node type as well
propagateType(newBinaryNode, newBinaryNode.lhs().getType()); propagateType(newBinaryNode, newBinaryNode.lhs().getType());
@ -354,41 +350,30 @@ final class FinalizeTypes extends NodeOperatorVisitor {
} }
@Override @Override
public Node enterBlock(final Block block) { public boolean enterBlock(final Block block) {
lexicalContext.push(block);
updateSymbols(block); updateSymbols(block);
return block; return true;
} }
/*
@Override @Override
public Node leaveBlock(Block block) { public Node leaveBlock(final Block block) {
lexicalContext.pop(block); final LexicalContext lc = getLexicalContext();
return super.leaveBlock(block); return block;//.setFlag(lc, lc.getFlags(block));
} }*/
@Override @Override
public Node leaveCatchNode(final CatchNode catchNode) { public Node leaveCatchNode(final CatchNode catchNode) {
final Node exceptionCondition = catchNode.getExceptionCondition(); final Node exceptionCondition = catchNode.getExceptionCondition();
if (exceptionCondition != null) { if (exceptionCondition != null) {
catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
} }
return catchNode; return catchNode;
} }
@Override
public Node enterDoWhileNode(final DoWhileNode doWhileNode) {
return enterWhileNode(doWhileNode);
}
@Override
public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
return leaveWhileNode(doWhileNode);
}
@Override @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) { public Node leaveExecuteNode(final ExecuteNode executeNode) {
executeNode.setExpression(discard(executeNode.getExpression())); return executeNode.setExpression(discard(executeNode.getExpression()));
return executeNode;
} }
@Override @Override
@ -397,69 +382,54 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final Node test = forNode.getTest(); final Node test = forNode.getTest();
final Node modify = forNode.getModify(); final Node modify = forNode.getModify();
final LexicalContext lc = getLexicalContext();
if (forNode.isForIn()) { if (forNode.isForIn()) {
forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
return forNode;
} }
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction();
if (init != null) { return forNode.
forNode.setInit(discard(init)); setInit(lc, init == null ? null : discard(init)).
} setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)).
setModify(lc, modify == null ? null : discard(modify));
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;
} }
@Override @Override
public Node enterFunctionNode(final FunctionNode functionNode) { public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) { 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 // 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 // this earlier, as access to scoped variables, self symbol, etc. in previous phases can all trigger the
// need for the callee. // need for the callee.
if (!functionNode.needsCallee()) { 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 // 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 // own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than
// this phase. // this phase.
if (!(functionNode.needsScope() || functionNode.needsParentScope())) { if (!(functionNode.getBody().needsScope() || functionNode.needsParentScope())) {
functionNode.getScopeNode().getSymbol().setNeedsSlot(false); functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
} }
updateSymbols(functionNode); return true;
functionNode.setState(CompilationState.FINALIZED);
return functionNode;
} }
@Override @Override
public Node leaveFunctionNode(FunctionNode functionNode) { public Node leaveFunctionNode(final FunctionNode functionNode) {
lexicalContext.pop(functionNode); return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED);
return super.leaveFunctionNode(functionNode);
} }
@Override @Override
public Node leaveIfNode(final IfNode ifNode) { public Node leaveIfNode(final IfNode ifNode) {
ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN)); return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
return ifNode;
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public Node enterLiteralNode(final LiteralNode literalNode) { public boolean enterLiteralNode(final LiteralNode literalNode) {
if (literalNode instanceof ArrayLiteralNode) { if (literalNode instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
final Node[] array = arrayLiteralNode.getValue(); final Node[] array = arrayLiteralNode.getValue();
@ -473,14 +443,14 @@ final class FinalizeTypes extends NodeOperatorVisitor {
} }
} }
return null; return false;
} }
@Override @Override
public Node leaveReturnNode(final ReturnNode returnNode) { public Node leaveReturnNode(final ReturnNode returnNode) {
final Node expr = returnNode.getExpression(); final Node expr = returnNode.getExpression();
if (expr != null) { if (expr != null) {
returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType())); return returnNode.setExpression(convert(expr, getLexicalContext().getCurrentFunction().getReturnType()));
} }
return returnNode; return returnNode;
} }
@ -496,21 +466,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override @Override
public Node leaveSwitchNode(final SwitchNode switchNode) { public Node leaveSwitchNode(final SwitchNode switchNode) {
final Node expression = switchNode.getExpression(); final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
final List<CaseNode> cases = switchNode.getCases();
final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
if (!allInteger) { if (allInteger) {
switchNode.setExpression(convert(expression, Type.OBJECT)); return switchNode;
for (final CaseNode caseNode : cases) {
final Node test = caseNode.getTest();
if (test != null) {
caseNode.setTest(convert(test, Type.OBJECT));
}
}
} }
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 @Override
@ -520,8 +493,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
@Override @Override
public Node leaveThrowNode(final ThrowNode throwNode) { public Node leaveThrowNode(final ThrowNode throwNode) {
throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT)); return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
return throwNode;
} }
@Override @Override
@ -544,23 +516,24 @@ final class FinalizeTypes extends NodeOperatorVisitor {
public Node leaveWhileNode(final WhileNode whileNode) { public Node leaveWhileNode(final WhileNode whileNode) {
final Node test = whileNode.getTest(); final Node test = whileNode.getTest();
if (test != null) { if (test != null) {
whileNode.setTest(convert(test, Type.BOOLEAN)); return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN));
} }
return whileNode; return whileNode;
} }
@Override @Override
public Node leaveWithNode(final WithNode withNode) { public Node leaveWithNode(final WithNode withNode) {
withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT)); return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT));
return withNode;
} }
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
if (!symbol.isScope()) { if (LOG.isEnabled()) {
LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope"); 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 (loseSlot && symbol.hasSlot()) {
LOG.finest("updateSymbols: ", symbol, " => no slot, because all vars in ", functionNode.getName(), " are in scope");
}
} }
} }
@ -574,29 +547,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
return; // nothing to do return; // nothing to do
} }
final FunctionNode functionNode = lexicalContext.getFunction(block); final LexicalContext lc = getLexicalContext();
assert !(block instanceof FunctionNode) || functionNode == block; final FunctionNode functionNode = lc.getFunction(block);
final boolean allVarsInScope = functionNode.allVarsInScope();
final boolean isVarArg = functionNode.isVarArg();
final List<Symbol> symbols = block.getFrame().getSymbols(); for (final Iterator<Symbol> iter = block.symbolIterator(); iter.hasNext(); ) {
final boolean allVarsInScope = functionNode.allVarsInScope(); final Symbol symbol = iter.next();
final boolean isVarArg = functionNode.isVarArg(); if (symbol.isInternal() || symbol.isThis() || symbol.isTemp()) {
for (final Symbol symbol : symbols) {
if (symbol.isInternal() || symbol.isThis()) {
continue; continue;
} }
if (symbol.isVar()) { if (symbol.isVar()) {
if (allVarsInScope || symbol.isScope()) { if (allVarsInScope || symbol.isScope()) {
updateSymbolsLog(functionNode, symbol, true); updateSymbolsLog(functionNode, symbol, true);
symbol.setIsScope(); Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(false); symbol.setNeedsSlot(false);
} else { } else {
assert symbol.hasSlot() : symbol + " should have a slot only, no scope"; assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
} }
} else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) { } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
updateSymbolsLog(functionNode, symbol, isVarArg); updateSymbolsLog(functionNode, symbol, isVarArg);
symbol.setIsScope(); Symbol.setSymbolIsScope(lc, symbol);
symbol.setNeedsSlot(!isVarArg); symbol.setNeedsSlot(!isVarArg);
} }
} }
@ -636,11 +608,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
//fallthru //fallthru
default: default:
if (newRuntimeNode || widest.isObject()) { if (newRuntimeNode || widest.isObject()) {
final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request); return new RuntimeNode(binaryNode, request).setIsFinal(finalized);
if (finalized) {
runtimeNode.setIsFinal();
}
return runtimeNode;
} }
break; break;
} }
@ -667,7 +635,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
} }
private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) { 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 +652,28 @@ final class FinalizeTypes extends NodeOperatorVisitor {
node.accept(new NodeVisitor() { node.accept(new NodeVisitor() {
private void setCanBePrimitive(final Symbol symbol) { 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); symbol.setCanBePrimitive(to);
} }
@Override @Override
public Node enterIdentNode(final IdentNode identNode) { public boolean enterIdentNode(final IdentNode identNode) {
if (!exclude.contains(identNode)) { if (!exclude.contains(identNode)) {
setCanBePrimitive(identNode.getSymbol()); setCanBePrimitive(identNode.getSymbol());
} }
return null; return false;
} }
@Override @Override
public Node enterAccessNode(final AccessNode accessNode) { public boolean enterAccessNode(final AccessNode accessNode) {
setCanBePrimitive(accessNode.getProperty().getSymbol()); setCanBePrimitive(accessNode.getProperty().getSymbol());
return null; return false;
} }
@Override @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 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 +754,12 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private static <T extends Node> T setTypeOverride(final T node, final Type to) { private static <T extends Node> T setTypeOverride(final T node, final Type to) {
final Type from = node.getType(); final Type from = node.getType();
if (!node.getType().equals(to)) { 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()) { if (!to.isObject() && from.isObject()) {
setCanBePrimitive(node, to); 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); return ((TypeOverride<T>)node).setType(to);
} }
@ -814,8 +783,8 @@ final class FinalizeTypes extends NodeOperatorVisitor {
private Node convert(final Node node, final Type to) { private Node convert(final Node node, final Type to) {
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass(); assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
assert node != null : "node is null"; assert node != null : "node is null";
assert node.getSymbol() != null : "node " + node + " has no symbol!"; 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 " + getCurrentFunctionNode(); assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction();
final Type from = node.getType(); final Type from = node.getType();
@ -842,23 +811,23 @@ final class FinalizeTypes extends NodeOperatorVisitor {
resultNode = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.CONVERT), node); 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 //This is the only place in this file that can create new temporaries
//FinalizeTypes may not introduce ANY node that is not a conversion. //FinalizeTypes may not introduce ANY node that is not a conversion.
getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode); lc.getCurrentFunction().ensureSymbol(lc.getCurrentBlock(), to, resultNode);
resultNode.copyTerminalFlags(node);
assert !node.isTerminal();
return resultNode; return resultNode;
} }
private static Node discard(final Node node) { private static Node discard(final Node node) {
node.setDiscard(true);
if (node.getSymbol() != null) { if (node.getSymbol() != null) {
final Node discard = new UnaryNode(node.getSource(), Token.recast(node.getToken(), TokenType.DISCARD), node); 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 never has a symbol in the discard node - then it would be a nop
discard.copyTerminalFlags(node); assert !node.isTerminal();
return discard; return discard;
} }
@ -883,7 +852,7 @@ final class FinalizeTypes extends NodeOperatorVisitor {
final Symbol symbol = node.getSymbol(); final Symbol symbol = node.getSymbol();
if (symbol.isTemp()) { if (symbol.isTemp()) {
symbol.setTypeOverride(to); 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) { public Node leaveUnaryNode(final UnaryNode unaryNode) {
final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval(); final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
if (literalNode != null) { if (literalNode != null) {
LOG.info("Unary constant folded " + unaryNode + " to " + literalNode); LOG.info("Unary constant folded ", unaryNode, " to ", literalNode);
return literalNode; return literalNode;
} }
return unaryNode; return unaryNode;
@ -67,24 +67,20 @@ final class FoldConstants extends NodeVisitor {
public Node leaveBinaryNode(final BinaryNode binaryNode) { public Node leaveBinaryNode(final BinaryNode binaryNode) {
final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
if (literalNode != null) { if (literalNode != null) {
LOG.info("Binary constant folded " + binaryNode + " to " + literalNode); LOG.info("Binary constant folded ", binaryNode, " to ", literalNode);
return literalNode; return literalNode;
} }
return binaryNode; return binaryNode;
} }
@Override @Override
public Node enterFunctionNode(final FunctionNode functionNode) { public boolean enterFunctionNode(final FunctionNode functionNode) {
if (functionNode.isLazy()) { return !functionNode.isLazy();
return null;
}
return functionNode;
} }
@Override @Override
public Node leaveFunctionNode(final FunctionNode functionNode) { public Node leaveFunctionNode(final FunctionNode functionNode) {
functionNode.setState(CompilationState.CONSTANT_FOLDED); return functionNode.setState(getLexicalContext(), CompilationState.CONSTANT_FOLDED);
return functionNode;
} }
@Override @Override

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.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 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.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;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS_DEBUGGER; 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.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
@ -67,6 +70,8 @@ import java.lang.reflect.Array;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import jdk.internal.dynalink.support.NameCodec; import jdk.internal.dynalink.support.NameCodec;
import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.MethodVisitor;
@ -79,14 +84,14 @@ import jdk.nashorn.internal.codegen.types.NumericType;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.ArgumentSetter; import jdk.nashorn.internal.runtime.ArgumentSetter;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptEnvironment; import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.Bootstrap;
@ -116,10 +121,10 @@ public class MethodEmitter implements Emitter {
private final ClassEmitter classEmitter; private final ClassEmitter classEmitter;
/** FunctionNode representing this method, or null if none exists */ /** 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 */ /** Check whether this emitter ever has a function return point */
private SplitNode splitNode; private boolean hasReturn;
/** The script environment */ /** The script environment */
private final ScriptEnvironment env; private final ScriptEnvironment env;
@ -203,7 +208,7 @@ public class MethodEmitter implements Emitter {
@Override @Override
public String toString() { 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 +481,8 @@ public class MethodEmitter implements Emitter {
String name = symbol.getName(); String name = symbol.getName();
if (name.equals(THIS.tag())) { if (name.equals(THIS.symbolName())) {
name = THIS_DEBUGGER.tag(); name = THIS_DEBUGGER.symbolName();
} }
method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot()); method.visitLocalVariable(name, symbol.getSymbolType().getDescriptor(), null, start, end, symbol.getSlot());
@ -654,7 +659,7 @@ public class MethodEmitter implements Emitter {
* @return this method emitter * @return this method emitter
*/ */
MethodEmitter loadConstants() { MethodEmitter loadConstants() {
getStatic(classEmitter.getUnitClassName(), CONSTANTS.tag(), CONSTANTS.descriptor()); getStatic(classEmitter.getUnitClassName(), CONSTANTS.symbolName(), CONSTANTS.descriptor());
assert peekType().isArray() : peekType(); assert peekType().isArray() : peekType();
return this; return this;
} }
@ -835,13 +840,13 @@ public class MethodEmitter implements Emitter {
if (functionNode.needsArguments()) { if (functionNode.needsArguments()) {
// ScriptObject.getArgument(int) on arguments // ScriptObject.getArgument(int) on arguments
debug("load symbol", symbol.getName(), " arguments index=", index); debug("load symbol", symbol.getName(), " arguments index=", index);
loadArguments(); loadCompilerConstant(ARGUMENTS);
load(index); load(index);
ScriptObject.GET_ARGUMENT.invoke(this); ScriptObject.GET_ARGUMENT.invoke(this);
} else { } else {
// array load from __varargs__ // array load from __varargs__
debug("load symbol", symbol.getName(), " array index=", index); debug("load symbol", symbol.getName(), " array index=", index);
loadVarArgs(); loadCompilerConstant(VARARGS);
load(symbol.getFieldIndex()); load(symbol.getFieldIndex());
arrayload(); arrayload();
} }
@ -870,47 +875,12 @@ public class MethodEmitter implements Emitter {
if(functionNode == null) { if(functionNode == null) {
return slot == CompilerConstants.JAVA_THIS.slot(); 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 == 1; // needsCallee -> thisSlot == 1
assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0 assert functionNode.needsCallee() || thisSlot == 0; // !needsCallee -> thisSlot == 0
return slot == thisSlot; 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 * Push a method handle to the stack
* *
@ -927,62 +897,24 @@ public class MethodEmitter implements Emitter {
return this; return this;
} }
/** private Symbol compilerConstant(final CompilerConstants cc) {
* Push the varargs object to the stack return functionNode.getBody().getExistingSymbol(cc.symbolName());
*
* @return the method emitter
*/
MethodEmitter loadVarArgs() {
debug("load var args " + functionNode.getVarArgsNode().getSymbol());
return load(functionNode.getVarArgsNode().getSymbol());
} }
/** MethodEmitter loadCompilerConstant(final CompilerConstants cc) {
* Push the arguments array to the stack final Symbol symbol = compilerConstant(cc);
* if (cc == SCOPE && peekType() == Type.SCOPE) {
* @return the method emitter dup();
*/ return this;
MethodEmitter loadArguments() { }
debug("load arguments ", functionNode.getArgumentsNode().getSymbol()); debug("load compiler constant ", symbol);
assert functionNode.getArgumentsNode().getSymbol().getSlot() != 0; return load(symbol);
return load(functionNode.getArgumentsNode().getSymbol());
} }
/** void storeCompilerConstant(final CompilerConstants cc) {
* Push the callee object to the stack final Symbol symbol = compilerConstant(cc);
* debug("store compiler constant ", symbol);
* @return the method emitter store(symbol);
*/
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);
}
/**
* 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());
} }
/** /**
@ -1030,13 +962,13 @@ public class MethodEmitter implements Emitter {
final int index = symbol.getFieldIndex(); final int index = symbol.getFieldIndex();
if (functionNode.needsArguments()) { if (functionNode.needsArguments()) {
debug("store symbol", symbol.getName(), " arguments index=", index); debug("store symbol", symbol.getName(), " arguments index=", index);
loadArguments(); loadCompilerConstant(ARGUMENTS);
load(index); load(index);
ArgumentSetter.SET_ARGUMENT.invoke(this); ArgumentSetter.SET_ARGUMENT.invoke(this);
} else { } else {
// varargs without arguments object - just do array store to __varargs__ // varargs without arguments object - just do array store to __varargs__
debug("store symbol", symbol.getName(), " array index=", index); debug("store symbol", symbol.getName(), " array index=", index);
loadVarArgs(); loadCompilerConstant(VARARGS);
load(index); load(index);
ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this); ArgumentSetter.SET_ARRAY_ELEMENT.invoke(this);
} }
@ -1345,6 +1277,11 @@ public class MethodEmitter implements Emitter {
} }
} }
MethodEmitter registerReturn() {
this.hasReturn = true;
return this;
}
/** /**
* Perform a non void return, popping the type from the stack * Perform a non void return, popping the type from the stack
* *
@ -1385,22 +1322,7 @@ public class MethodEmitter implements Emitter {
* *
* @param label destination label * @param label destination label
*/ */
void splitAwareGoto(final Label label) { void splitAwareGoto(final LexicalContext lc, 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;
}
}
_goto(label); _goto(label);
} }
@ -2237,7 +2159,7 @@ public class MethodEmitter implements Emitter {
} }
if (env != null) { //early bootstrap code doesn't have inited context yet if (env != null) { //early bootstrap code doesn't have inited context yet
LOG.info(sb.toString()); LOG.info(sb);
if (DEBUG_TRACE_LINE == linePrefix) { if (DEBUG_TRACE_LINE == linePrefix) {
new Throwable().printStackTrace(LOG.getOutputStream()); new Throwable().printStackTrace(LOG.getOutputStream());
} }
@ -2254,21 +2176,12 @@ public class MethodEmitter implements Emitter {
this.functionNode = functionNode; this.functionNode = functionNode;
} }
/** boolean hasReturn() {
* Get the split node for this method emitter, if this is code return hasReturn;
* generation due to splitting large methods
*
* @return split node
*/
SplitNode getSplitNode() {
return splitNode;
} }
/** List<Label> getExternalTargets() {
* Set the split node for this method emitter return null;
* @param splitNode split node
*/
void setSplitNode(final SplitNode splitNode) {
this.splitNode = splitNode;
} }
} }

View File

@ -53,7 +53,7 @@ public class Namespace {
*/ */
public Namespace(final Namespace parent) { public Namespace(final Namespace parent) {
this.parent = parent; this.parent = parent;
directory = new HashMap<>(); this.directory = new HashMap<>();
} }
/** /**
@ -65,10 +65,6 @@ public class Namespace {
return parent; return parent;
} }
private HashMap<String, Integer> getDirectory() {
return directory;
}
/** /**
* Create a uniqueName name in the namespace in the form base$n where n varies * 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) { public String uniqueName(final String base) {
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) { 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); final Integer counter = namespaceDirectory.get(base);
if (counter != null) { if (counter != null) {

View File

@ -204,8 +204,8 @@ public final class ObjectClassGenerator {
* @return The class name. * @return The class name.
*/ */
public static String getClassName(final int fieldCount) { public static String getClassName(final int fieldCount) {
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount : return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag(); SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
} }
/** /**
@ -218,7 +218,7 @@ public final class ObjectClassGenerator {
* @return The class name. * @return The class name.
*/ */
public static String getClassName(final int fieldCount, final int paramCount) { 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;
} }
/** /**
@ -449,7 +449,7 @@ public final class ObjectClassGenerator {
* @param className Name of JavaScript class. * @param className Name of JavaScript class.
*/ */
private static void newAllocate(final ClassEmitter classEmitter, final String className) { 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.begin();
allocate._new(className); allocate._new(className);
allocate.dup(); allocate.dup();

View File

@ -36,7 +36,7 @@ import jdk.nashorn.internal.runtime.PropertyMap;
public abstract class ObjectCreator { public abstract class ObjectCreator {
/** Compile unit for this ObjectCreator, see CompileUnit */ /** Compile unit for this ObjectCreator, see CompileUnit */
protected final CompileUnit compileUnit; //protected final CompileUnit compileUnit;
/** List of keys to initiate in this ObjectCreator */ /** List of keys to initiate in this ObjectCreator */
protected final List<String> keys; 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) { protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean hasArguments) {
this.codegen = codegen; this.codegen = codegen;
this.compileUnit = codegen.getCurrentCompileUnit();
this.keys = keys; this.keys = keys;
this.symbols = symbols; this.symbols = symbols;
this.isScope = isScope; 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 static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import jdk.nashorn.internal.ir.Block; 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;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.SplitNode; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
@ -64,7 +53,7 @@ final class Splitter extends NodeVisitor {
private final Compiler compiler; private final Compiler compiler;
/** IR to be broken down. */ /** IR to be broken down. */
private final FunctionNode functionNode; private FunctionNode outermost;
/** Compile unit for the main script. */ /** Compile unit for the main script. */
private final CompileUnit outermostCompileUnit; private final CompileUnit outermostCompileUnit;
@ -72,8 +61,6 @@ final class Splitter extends NodeVisitor {
/** Cache for calculated block weights. */ /** Cache for calculated block weights. */
private final Map<Node, Long> weightCache = new HashMap<>(); private final Map<Node, Long> weightCache = new HashMap<>();
private final LexicalContext lexicalContext = new LexicalContext();
/** Weight threshold for when to start a split. */ /** Weight threshold for when to start a split. */
public static final long SPLIT_THRESHOLD = Options.getIntProperty("nashorn.compiler.splitter.threshold", 32 * 1024); 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) { public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit outermostCompileUnit) {
this.compiler = compiler; this.compiler = compiler;
this.functionNode = functionNode; this.outermost = functionNode;
this.outermostCompileUnit = outermostCompileUnit; this.outermostCompileUnit = outermostCompileUnit;
} }
/** /**
* Execute the split * Execute the split
*/ */
void split() { FunctionNode split(final FunctionNode fn) {
FunctionNode functionNode = fn;
if (functionNode.isLazy()) { if (functionNode.isLazy()) {
LOG.finest("Postponing split of '" + functionNode.getName() + "' as it's lazy"); LOG.finest("Postponing split of '", functionNode.getName(), "' as it's lazy");
return; 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); long weight = WeighNodes.weigh(functionNode);
final boolean top = compiler.getFunctionNode() == outermost;
if (weight >= SPLIT_THRESHOLD) { if (weight >= SPLIT_THRESHOLD) {
LOG.finest("Splitting '" + functionNode.getName() + "' as its weight " + weight + " exceeds split threshold " + SPLIT_THRESHOLD); LOG.finest("Splitting '", functionNode.getName(), "' as its weight ", weight, " exceeds split threshold ", SPLIT_THRESHOLD);
functionNode = (FunctionNode)functionNode.accept(this);
functionNode.accept(this);
if (functionNode.isSplit()) { if (functionNode.isSplit()) {
// Weight has changed so weigh again, this time using block weight cache // Weight has changed so weigh again, this time using block weight cache
weight = WeighNodes.weigh(functionNode, weightCache); weight = WeighNodes.weigh(functionNode, weightCache);
functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(lc));
} }
if (weight >= SPLIT_THRESHOLD) { if (weight >= SPLIT_THRESHOLD) {
weight = splitBlock(functionNode, functionNode); functionNode = functionNode.setBody(lc, splitBlock(functionNode.getBody(), functionNode));
} weight = WeighNodes.weigh(functionNode.getBody(), weightCache);
if (functionNode.isSplit()) {
functionNode.accept(new SplitFlowAnalyzer());
} }
} }
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"; assert outermostCompileUnit != null : "outermost compile unit is null";
functionNode = functionNode.setCompileUnit(lc, outermostCompileUnit);
functionNode.setCompileUnit(outermostCompileUnit);
outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT); outermostCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
} else { } else {
functionNode.setCompileUnit(findUnit(weight)); functionNode = functionNode.setCompileUnit(lc, findUnit(weight));
} }
// Recursively split nested functions final Block body = functionNode.getBody();
functionNode.accept(new NodeOperatorVisitor() { final List<FunctionNode> dc = directChildren(functionNode);
@Override
public Node enterFunctionNode(FunctionNode function) { final Block newBody = (Block)body.accept(new NodeVisitor() {
if(function == functionNode) { @Override
// Don't process outermost function (it was already processed) but descend into it to find nested public boolean enterFunctionNode(final FunctionNode nestedFunction) {
// functions. return dc.contains(nestedFunction);
return function;
} }
// Process a nested function
new Splitter(compiler, function, outermostCompileUnit).split(); @Override
// Don't descend into a a nested function; Splitter.split() has taken care of nested-in-nested functions. public Node leaveFunctionNode(final FunctionNode nestedFunction) {
return null; 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;
} }
}); });
return dc;
functionNode.setState(CompilationState.SPLIT);
} }
/** /**
@ -170,8 +179,8 @@ final class Splitter extends NodeVisitor {
* *
* @return new weight for the resulting block. * @return new weight for the resulting block.
*/ */
private long splitBlock(final Block block, final FunctionNode function) { private Block splitBlock(final Block block, final FunctionNode function) {
functionNode.setIsSplit(); getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
final List<Node> splits = new ArrayList<>(); final List<Node> splits = new ArrayList<>();
List<Node> statements = new ArrayList<>(); List<Node> statements = new ArrayList<>();
@ -186,7 +195,6 @@ final class Splitter extends NodeVisitor {
statements = new ArrayList<>(); statements = new ArrayList<>();
statementsWeight = 0; statementsWeight = 0;
} }
} }
if (statement.isTerminal()) { if (statement.isTerminal()) {
@ -201,9 +209,7 @@ final class Splitter extends NodeVisitor {
splits.add(createBlockSplitNode(block, function, statements, statementsWeight)); splits.add(createBlockSplitNode(block, function, statements, statementsWeight));
} }
block.setStatements(splits); return block.setStatements(getLexicalContext(), splits);
return WeighNodes.weigh(block, weightCache);
} }
/** /**
@ -218,51 +224,44 @@ final class Splitter extends NodeVisitor {
final Source source = parent.getSource(); final Source source = parent.getSource();
final long token = parent.getToken(); final long token = parent.getToken();
final int finish = parent.getFinish(); 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); final Block newBlock = new Block(source, token, finish, statements);
newBlock.setFrame(new Frame(parent.getFrame()));
newBlock.setStatements(statements);
final SplitNode splitNode = new SplitNode(name, functionNode, newBlock); return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
splitNode.setCompileUnit(compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
return splitNode;
} }
@Override @Override
public Node enterBlock(final Block block) { public boolean enterBlock(final Block block) {
if (block.isCatchBlock()) { if (block.isCatchBlock()) {
return null; return false;
} }
lexicalContext.push(block);
final long weight = WeighNodes.weigh(block, weightCache); final long weight = WeighNodes.weigh(block, weightCache);
if (weight < SPLIT_THRESHOLD) { if (weight < SPLIT_THRESHOLD) {
weightCache.put(block, weight); weightCache.put(block, weight);
lexicalContext.pop(block); return false;
return null;
} }
return block; return true;
} }
@Override @Override
public Node leaveBlock(final Block block) { public Node leaveBlock(final Block block) {
assert !block.isCatchBlock(); assert !block.isCatchBlock();
Block newBlock = block;
// Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have // Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
// been split already, so weigh again before splitting. // been split already, so weigh again before splitting.
long weight = WeighNodes.weigh(block, weightCache); long weight = WeighNodes.weigh(block, weightCache);
if (weight >= SPLIT_THRESHOLD) { 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); weightCache.put(newBlock, weight);
return newBlock;
lexicalContext.pop(block);
return block;
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -274,7 +273,7 @@ final class Splitter extends NodeVisitor {
return literal; return literal;
} }
functionNode.setIsSplit(); getLexicalContext().setFlag(getLexicalContext().getCurrentFunction(), FunctionNode.IS_SPLIT);
if (literal instanceof ArrayLiteralNode) { if (literal instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal; final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
@ -312,123 +311,12 @@ final class Splitter extends NodeVisitor {
} }
@Override @Override
public Node enterFunctionNode(final FunctionNode node) { public boolean enterFunctionNode(final FunctionNode node) {
if(node == functionNode && !node.isLazy()) { //only go into the function node for this splitter. any subfunctions are rejected
lexicalContext.push(node); if (node == outermost && !node.isLazy()) {
node.visitStatements(this); return true;
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);
}
}
} }
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.CallNode;
import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
@ -101,7 +100,7 @@ final class WeighNodes extends NodeOperatorVisitor {
* @param weightCache cache of already calculated block weights * @param weightCache cache of already calculated block weights
*/ */
private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) { private WeighNodes(FunctionNode topFunction, final Map<Node, Long> weightCache) {
super(null, null); super();
this.topFunction = topFunction; this.topFunction = topFunction;
this.weightCache = weightCache; this.weightCache = weightCache;
} }
@ -123,13 +122,13 @@ final class WeighNodes extends NodeOperatorVisitor {
} }
@Override @Override
public Node enterBlock(final Block block) { public boolean enterBlock(final Block block) {
if (weightCache != null && weightCache.containsKey(block)) { if (weightCache != null && weightCache.containsKey(block)) {
weight += weightCache.get(block); weight += weightCache.get(block);
return null; return false;
} }
return block; return true;
} }
@Override @Override
@ -156,12 +155,6 @@ final class WeighNodes extends NodeOperatorVisitor {
return continueNode; return continueNode;
} }
@Override
public Node leaveDoWhileNode(final DoWhileNode doWhileNode) {
weight += LOOP_WEIGHT;
return doWhileNode;
}
@Override @Override
public Node leaveExecuteNode(final ExecuteNode executeNode) { public Node leaveExecuteNode(final ExecuteNode executeNode) {
return executeNode; return executeNode;
@ -174,15 +167,15 @@ final class WeighNodes extends NodeOperatorVisitor {
} }
@Override @Override
public Node enterFunctionNode(final FunctionNode functionNode) { public boolean enterFunctionNode(final FunctionNode functionNode) {
if(functionNode == topFunction) { if (functionNode == topFunction) {
// the function being weighted; descend into its statements // the function being weighted; descend into its statements
functionNode.visitStatements(this); return true;
} else { // functionNode.visitStatements(this);
// just a reference to inner function from outer function
weight += FUNC_EXPR_WEIGHT;
} }
return null; // just a reference to inner function from outer function
weight += FUNC_EXPR_WEIGHT;
return false;
} }
@Override @Override
@ -205,7 +198,7 @@ final class WeighNodes extends NodeOperatorVisitor {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public Node enterLiteralNode(final LiteralNode literalNode) { public boolean enterLiteralNode(final LiteralNode literalNode) {
weight += LITERAL_WEIGHT; weight += LITERAL_WEIGHT;
if (literalNode instanceof ArrayLiteralNode) { if (literalNode instanceof ArrayLiteralNode) {
@ -224,10 +217,10 @@ final class WeighNodes extends NodeOperatorVisitor {
} }
} }
return null; return false;
} }
return literalNode; return true;
} }
@Override @Override
@ -249,9 +242,9 @@ final class WeighNodes extends NodeOperatorVisitor {
} }
@Override @Override
public Node enterSplitNode(final SplitNode splitNode) { public boolean enterSplitNode(final SplitNode splitNode) {
weight += SPLIT_WEIGHT; weight += SPLIT_WEIGHT;
return null; return false;
} }
@Override @Override

View File

@ -25,23 +25,18 @@
package jdk.nashorn.internal.ir; 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.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of a property access (period operator.) * 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. */ /** Property ident. */
private IdentNode property; private final IdentNode property;
/** Does this node have a type override */
private boolean hasCallSiteType;
/** /**
* Constructor * Constructor
@ -53,49 +48,13 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
* @param property property * @param property property
*/ */
public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) { public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) {
super(source, token, finish, base); super(source, token, finish, base, false, false);
this.start = base.getStart();
this.property = property.setIsPropertyName(); this.property = property.setIsPropertyName();
} }
/** private AccessNode(final AccessNode accessNode, final Node base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) {
* Copy constructor super(accessNode, base, isFunction, hasCallSiteType);
* this.property = property;
* @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();
} }
/** /**
@ -104,12 +63,11 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterAccessNode(this) != null) { if (visitor.enterAccessNode(this)) {
base = base.accept(visitor); return visitor.leaveAccessNode(
property = (IdentNode)property.accept(visitor); setBase(base.accept(visitor)).
return visitor.leaveAccessNode(this); setProperty((IdentNode)property.accept(visitor)));
} }
return this; return this;
} }
@ -117,7 +75,7 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
public void toString(final StringBuilder sb) { public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true); final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
if (hasCallSiteType) { if (hasCallSiteType()) {
sb.append('{'); sb.append('{');
final String desc = getType().getDescriptor(); 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());
@ -147,19 +105,34 @@ public class AccessNode extends BaseNode implements TypeOverride<AccessNode> {
return property; return property;
} }
@Override private AccessNode setBase(final Node base) {
public AccessNode setType(final Type type) { if (this.base == base) {
if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { return this;
ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
} }
property = property.setType(type); return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
getSymbol().setTypeOverride(type); //always a temp so this is fine. }
hasCallSiteType = true;
return this;
private AccessNode setProperty(final IdentNode property) {
if (this.property == property) {
return this;
}
return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
} }
@Override @Override
public boolean canHaveCallSiteType() { public AccessNode setType(final Type type) {
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 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; 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; import jdk.nashorn.internal.runtime.Source;
/** /**
@ -33,12 +37,15 @@ import jdk.nashorn.internal.runtime.Source;
* @see AccessNode * @see AccessNode
* @see IndexNode * @see IndexNode
*/ */
public abstract class BaseNode extends Node implements FunctionCall { @Immutable
public abstract class BaseNode extends Node implements FunctionCall, TypeOverride<BaseNode> {
/** Base Node. */ /** Base Node. */
protected Node base; protected final Node base;
private boolean function; private final boolean isFunction;
private final boolean hasCallSiteType;
/** /**
* Constructor * Constructor
@ -47,37 +54,28 @@ public abstract class BaseNode extends Node implements FunctionCall {
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param base base node * @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) { public BaseNode(final Source source, final long token, final int finish, final Node base, final boolean isFunction, final boolean hasCallSiteType) {
super(source, token, finish); super(source, token, base.getStart(), finish);
this.base = base; this.base = base;
setStart(base.getStart()); this.isFunction = isFunction;
this.hasCallSiteType = hasCallSiteType;
} }
/** /**
* Copy constructor * Copy constructor for immutable nodes
* * @param baseNode node to inherit from
* @param baseNode the base node * @param base base
* @param cs a copy state * @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); super(baseNode);
this.base = cs.existingOrCopy(baseNode.getBase()); this.base = base;
setStart(base.getStart()); this.isFunction = isFunction;
} this.hasCallSiteType = hasCallSiteType;
@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();
} }
/** /**
@ -88,25 +86,37 @@ public abstract class BaseNode extends Node implements FunctionCall {
return base; return base;
} }
/**
* Reset the base node for this access
* @param base new base node
*/
public void setBase(final Node base) {
this.base = base;
}
@Override @Override
public boolean isFunction() { public boolean isFunction() {
return function; return isFunction;
} }
/** /**
* Mark this node as being the callee operand of a {@link CallNode}. * 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. * @return a base node identical to this one in all aspects except with its function flag set.
*/ */
public BaseNode setIsFunction() { public abstract BaseNode setIsFunction();
function = true;
return this; @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; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.codegen.types.Type; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
@ -33,9 +34,12 @@ import jdk.nashorn.internal.runtime.Source;
/** /**
* BinaryNode nodes represent two operand operations. * 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. */ /** Left hand side argument. */
private Node lhs; private final Node lhs;
private final Node rhs;
/** /**
* Constructor * Constructor
@ -46,28 +50,15 @@ public class BinaryNode extends UnaryNode {
* @param rhs right hand side * @param rhs right hand side
*/ */
public BinaryNode(final Source source, final long token, final Node lhs, final Node rhs) { public BinaryNode(final Source source, final long token, final Node lhs, final Node rhs) {
super(source, token, rhs); super(source, token, lhs.getStart(), rhs.getFinish());
this.lhs = lhs;
start = lhs.getStart(); this.rhs = rhs;
finish = rhs.getFinish(); }
private BinaryNode(final BinaryNode binaryNode, final Node lhs, final Node rhs) {
super(binaryNode);
this.lhs = lhs; this.lhs = lhs;
} this.rhs = rhs;
/**
* 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);
} }
/** /**
@ -149,28 +140,14 @@ public class BinaryNode extends UnaryNode {
return rhs(); 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. * Assist in IR navigation.
* @param visitor IR navigating visitor. * @param visitor IR navigating visitor.
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterBinaryNode(this) != null) { if (visitor.enterBinaryNode(this)) {
// TODO: good cause for a separate visitMembers: we could delegate to UnaryNode.visitMembers return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor)));
return visitor.leaveBinaryNode((BinaryNode)setLHS(lhs.accept(visitor)).setRHS(rhs().accept(visitor)));
} }
return this; return this;
@ -230,15 +207,36 @@ public class BinaryNode extends UnaryNode {
return lhs; 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 * Set the left hand side expression for this node
* @param lhs new left hand side expression * @param lhs new left hand side expression
* @return a node equivalent to this one except for the requested change. * @return a node equivalent to this one except for the requested change.
*/ */
public BinaryNode setLHS(final Node lhs) { public BinaryNode setLHS(final Node lhs) {
if(this.lhs == lhs) return this; if (this.lhs == lhs) {
final BinaryNode n = (BinaryNode)clone(); return this;
n.lhs = lhs; }
return n; 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.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.Map;
import jdk.nashorn.internal.codegen.Frame;
import jdk.nashorn.internal.codegen.Label; import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; 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 * IR representation for a list of statements and functions. All provides the
* basis for script body. * basis for script body.
*/ */
public class Block extends Node { @Immutable
public class Block extends BreakableNode implements Flags<Block> {
/** List of statements */ /** List of statements */
protected List<Node> statements; protected final List<Node> statements;
/** Symbol table. */ /** Symbol table - keys must be returned in the order they were put in. */
protected final HashMap<String, Symbol> symbols; protected final Map<String, Symbol> symbols;
/** Variable frame. */
protected Frame frame;
/** Entry label. */ /** Entry label. */
protected final Label entryLabel; protected final Label entryLabel;
/** Break label. */
protected final Label breakLabel;
/** Does the block/function need a new scope? */ /** 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 * Constructor
* *
* @param source source code * @param source source code
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param statements statements
*/ */
public Block(final Source source, final long token, final int finish) { public Block(final Source source, final long token, final int finish, final Node... statements) {
super(source, token, finish); super(source, token, finish, new Label("block_break"));
this.statements = new ArrayList<>(); this.statements = Arrays.asList(statements);
this.symbols = new HashMap<>(); this.symbols = new LinkedHashMap<>();
this.entryLabel = new Label("block_entry"); 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 source source code
* @param cs the copy state * @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); super(block);
this.statements = statements;
this.statements = new ArrayList<>(); this.flags = flags;
for (final Node statement : block.getStatements()) { 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
statements.add(cs.existingOrCopy(statement));
}
this.symbols = new HashMap<>();
this.frame = block.frame == null ? null : block.frame.copy();
this.entryLabel = new Label(block.entryLabel); this.entryLabel = new Label(block.entryLabel);
this.breakLabel = new Label(block.breakLabel); this.finish = finish;
assert block.symbols.isEmpty() : "must not clone with symbols";
} }
@Override @Override
protected Node copy(final CopyState cs) { public Node ensureUniqueLabels(final LexicalContext lc) {
return new Block(this, cs); return Node.replaceInLexicalContext(lc, this, new Block(this, finish, statements, flags));
}
/**
* 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);
} }
/** /**
@ -142,19 +125,9 @@ public class Block extends Node {
* @return new or same node * @return new or same node
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
final Block saveBlock = visitor.getCurrentBlock(); if (visitor.enterBlock(this)) {
visitor.setCurrentBlock(this); return visitor.leaveBlock(setStatements(lc, Node.accept(visitor, Node.class, statements)));
try {
// Ignore parent to avoid recursion.
if (visitor.enterBlock(this) != null) {
visitStatements(visitor);
return visitor.leaveBlock(this);
}
} finally {
visitor.setCurrentBlock(saveBlock);
} }
return this; return this;
@ -222,11 +195,18 @@ public class Block extends Node {
} }
/** /**
* Get the break label for this block * Tag block as terminal or non terminal
* @return the break label * @param lc lexical context
* @param isTerminal is block terminal
* @return same block, or new if flag changed
*/ */
public Label getBreakLabel() { public Block setIsTerminal(final LexicalContext lc, final boolean isTerminal) {
return breakLabel; 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; 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 * Get the list of statements in this block
* *
@ -263,22 +226,22 @@ public class Block extends Node {
return Collections.unmodifiableList(statements); 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 * 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) { public Block setStatements(final LexicalContext lc, final List<Node> statements) {
this.statements = 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 * @return true if this function needs a scope
*/ */
public boolean needsScope() { 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. * Set the needs scope flag.
* @param lc lexicalContext
* @return new block if state changed, otherwise this
*/ */
public void setNeedsScope() { public Block setNeedsScope(final LexicalContext lc) {
needsScope = true; 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 * Computationally determine the next slot for this block,
* including the block defining the symbol will be marked as needing parent scope. The block defining the symbol * indexed from 0. Use this as a relative base when computing
* will be marked as one that needs to have its own scope. * frames
* @param symbol the symbol being used. * @return next slot
* @param ancestors the iterator over block's containing lexical context
*/ */
public void setUsesScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) { public int nextSlot() {
if(symbol.getBlock() == this) { final Iterator<Symbol> iter = symbolIterator();
setNeedsScope(); int next = 0;
} else { while (iter.hasNext()) {
setUsesParentScopeSymbol(symbol, ancestors); final Symbol symbol = iter.next();
if (symbol.hasSlot()) {
next += symbol.slotCount();
} }
}
return next;
} }
/** @Override
* Invoked when this block uses a scope symbol defined in one of its ancestors. protected boolean isBreakableWithoutLabel() {
* @param symbol the scope symbol being used return false;
* @param ancestors iterator over ancestor blocks
*/
void setUsesParentScopeSymbol(final Symbol symbol, Iterator<Block> ancestors) {
if(ancestors.hasNext()) {
ancestors.next().setUsesScopeSymbol(symbol, ancestors);
}
} }
} }

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; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for {@code break} statements. * IR representation for {@code break} statements.
*/ */
public class BreakNode extends LabeledNode { @Immutable
public final class BreakNode extends Node {
/** private final IdentNode label;
/**
* Constructor * Constructor
* *
* @param source source code * @param source source code
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param labelNode break label * @param label label for break or null if none
* @param targetNode node to break to
* @param tryChain surrounding try chain
*/ */
public BreakNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) { public BreakNode(final Source source, final long token, final int finish, final IdentNode label) {
super(source, token, finish, labelNode, targetNode, tryChain); super(source, token, finish);
setHasGoto(); this.label = label;
}
private BreakNode(final BreakNode breakNode, final CopyState cs) {
super(breakNode, cs);
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean hasGoto() {
return new BreakNode(this, cs); return true;
} }
/** /**
@ -64,7 +61,7 @@ public class BreakNode extends LabeledNode {
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterBreakNode(this) != null) { if (visitor.enterBreakNode(this)) {
return visitor.leaveBreakNode(this); return visitor.leaveBreakNode(this);
} }
@ -72,26 +69,20 @@ public class BreakNode extends LabeledNode {
} }
/** /**
* Return the target label of this break node. * Get the label for this break node
* @return the target label. * @return label, or null if none
*/ */
public Label getTargetLabel() { public IdentNode getLabel() {
if (targetNode instanceof BreakableNode) { return label;
return ((BreakableNode)targetNode).getBreakLabel();
} else if (targetNode instanceof Block) {
return ((Block)targetNode).getBreakLabel();
}
throw new AssertionError("Invalid break target " + targetNode.getClass());
} }
@Override @Override
public void toString(final StringBuilder sb) { public void toString(final StringBuilder sb) {
sb.append("break"); sb.append("break");
if (labelNode != null) { if (label != null) {
sb.append(' '); sb.append(' ');
labelNode.getLabel().toString(sb); label.toString(sb);
} }
} }
} }

View File

@ -25,27 +25,34 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import java.util.Arrays;
import java.util.List;
import jdk.nashorn.internal.codegen.Label; import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* This class represents a node from which control flow can execute * This class represents a node from which control flow can execute
* a {@code break} statement * a {@code break} statement
*/ */
public abstract class BreakableNode extends Node { @Immutable
public abstract class BreakableNode extends LexicalContextNode {
/** break label. */ /** break label. */
protected Label breakLabel; protected final Label breakLabel;
/** /**
* Constructor * Constructor
* *
* @param source source code * @param source source code
* @param token token * @param token token
* @param finish finish * @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); super(source, token, finish);
this.breakLabel = breakLabel;
} }
/** /**
@ -55,6 +62,19 @@ public abstract class BreakableNode extends Node {
*/ */
protected BreakableNode(final BreakableNode breakableNode) { protected BreakableNode(final BreakableNode breakableNode) {
super(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 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,51 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for a function call. * 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. */ /** Function identifier or function body. */
private Node function; private final Node function;
/** Call arguments. */ /** Call arguments. */
private List<Node> args; private final List<Node> args;
/** flag - is new expression */ /** Is this a "new" operation */
private boolean isNew; public static final int IS_NEW = 0x1;
/** flag - is in with block */ /** Is this call tagged as inside a with block */
private boolean inWithBlock; public static final int IN_WITH_BLOCK = 0x2;
private final int flags;
/** /**
* Arguments to be passed to builtin {@code eval} function * Arguments to be passed to builtin {@code eval} function
*/ */
public static class EvalArgs { public static class EvalArgs {
/** evaluated code */ /** evaluated code */
private Node code; private final Node code;
/** 'this' passed to evaluated code */ /** 'this' passed to evaluated code */
private IdentNode evalThis; private final IdentNode evalThis;
/** location string for the eval call */ /** location string for the eval call */
final private String location; private final String location;
/** is this call from a strict context? */ /** is this call from a strict context? */
final private boolean strictMode; private final boolean strictMode;
/** /**
* Constructor * Constructor
@ -92,12 +94,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
return code; return code;
} }
/** private EvalArgs setCode(final Node code) {
* Set the code that is to be eval.ed by this eval function if (this.code == code) {
* @param code the code as an AST node return this;
*/ }
public void setCode(final Node code) { return new EvalArgs(code, evalThis, location, strictMode);
this.code = code;
} }
/** /**
@ -108,12 +109,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
return this.evalThis; return this.evalThis;
} }
/** private EvalArgs setThis(final IdentNode evalThis) {
* Set the {@code this} symbol used to invoke this eval call if (this.evalThis == evalThis) {
* @param evalThis the {@code this} symbol return this;
*/ }
public void setThis(final IdentNode evalThis) { return new EvalArgs(code, evalThis, location, strictMode);
this.evalThis = evalThis;
} }
/** /**
@ -135,7 +135,7 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
/** arguments for 'eval' call. Non-null only if this call node is 'eval' */ /** arguments for 'eval' call. Non-null only if this call node is 'eval' */
@Ignore @Ignore
private EvalArgs evalArgs; private final EvalArgs evalArgs;
/** /**
* Constructors * Constructors
@ -145,32 +145,27 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* @param finish finish * @param finish finish
* @param function the function to call * @param function the function to call
* @param args args to the call * @param args args to the call
* @param flags flags
*/ */
public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args) { public CallNode(final Source source, final long token, final int finish, final Node function, final List<Node> args, final int flags) {
super(source, token, finish); super(source, token, finish);
setStart(function.getStart()); this.function = function;
this.args = args;
this.function = function; this.flags = flags;
this.args = args; 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); super(callNode);
this.function = function;
final List<Node> newArgs = new ArrayList<>(); this.args = args;
this.flags = flags;
for (final Node arg : callNode.args) { this.type = type;
newArgs.add(cs.existingOrCopy(arg)); this.evalArgs = evalArgs;
}
this.function = cs.existingOrCopy(callNode.function); //TODO existing or same?
this.args = newArgs;
this.isNew = callNode.isNew;
this.inWithBlock = callNode.inWithBlock;
} }
@Override @Override
public Type getType() { public Type getType() {
if (hasCallSiteType()) { if (hasCallSiteType()) {
@ -181,8 +176,10 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
@Override @Override
public CallNode setType(final Type type) { public CallNode setType(final Type type) {
this.type = type; if (this.type == type) {
return this; return this;
}
return new CallNode(this, function, args, flags, type, evalArgs);
} }
private boolean hasCallSiteType() { private boolean hasCallSiteType() {
@ -194,11 +191,6 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
return true; return true;
} }
@Override
protected Node copy(final CopyState cs) {
return new CallNode(this, cs);
}
/** /**
* Assist in IR navigation. * Assist in IR navigation.
* *
@ -207,15 +199,22 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* @return node or replacement * @return node or replacement
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
if (visitor.enterCallNode(this) != null) { if (visitor.enterCallNode(this)) {
function = function.accept(visitor); final CallNode newCallNode = (CallNode)visitor.leaveCallNode(
setFunction(function.accept(visitor)).
for (int i = 0, count = args.size(); i < count; i++) { setArgs(Node.accept(visitor, Node.class, args)).
args.set(i, args.get(i).accept(visitor)); 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; return this;
@ -226,7 +225,7 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
if (hasCallSiteType()) { if (hasCallSiteType()) {
sb.append('{'); sb.append('{');
final String desc = getType().getDescriptor(); 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('}'); sb.append('}');
} }
@ -261,8 +260,11 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* Reset the arguments for the call * Reset the arguments for the call
* @param args new arguments list * @param args new arguments list
*/ */
public void setArgs(final List<Node> args) { private CallNode setArgs(final List<Node> args) {
this.args = args; if (this.args == args) {
return this;
}
return new CallNode(this, function, args, flags, type, evalArgs);
} }
/** /**
@ -278,9 +280,13 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* {@code eval} * {@code eval}
* *
* @param evalArgs eval args * @param evalArgs eval args
* @return same node or new one on state change
*/ */
public void setEvalArgs(final EvalArgs evalArgs) { public CallNode setEvalArgs(final EvalArgs evalArgs) {
this.evalArgs = evalArgs; if (this.evalArgs == evalArgs) {
return this;
}
return new CallNode(this, function, args, flags, type, evalArgs);
} }
/** /**
@ -301,10 +307,14 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
/** /**
* Reset the function expression that this call invokes * 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) { public CallNode setFunction(final Node function) {
function = node; if (this.function == function) {
return this;
}
return new CallNode(this, function, args, flags, type, evalArgs);
} }
/** /**
@ -312,14 +322,15 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* @return true if this a new operation * @return true if this a new operation
*/ */
public boolean isNew() { public boolean isNew() {
return isNew; return (flags & IS_NEW) == IS_NEW;
} }
/** /**
* Flag this call as a new operation * Flag this call as a new operation
* @return same node or new one on state change
*/ */
public void setIsNew() { public CallNode setIsNew() {
this.isNew = true; return setFlags(IS_NEW);
} }
/** /**
@ -327,13 +338,13 @@ public class CallNode extends Node implements TypeOverride<CallNode> {
* @return true if the call is inside a {@code with} block * @return true if the call is inside a {@code with} block
*/ */
public boolean inWithBlock() { public boolean inWithBlock() {
return inWithBlock; return (flags & IN_WITH_BLOCK) == IN_WITH_BLOCK;
} }
/** private CallNode setFlags(final int flags) {
* Flag this call to be inside a {@code with} block if (this.flags == flags) {
*/ return this;
public void setInWithBlock() { }
this.inWithBlock = true; return new CallNode(this, function, args, flags, type, evalArgs);
} }
} }

View File

@ -26,19 +26,21 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.codegen.Label; import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of CASE clause. * 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. */ /** Test expression. */
private Node test; private final Node test;
/** Statements. */ /** Statements. */
private Block body; private final Block body;
/** Case entry label. */ /** Case entry label. */
private final Label entry; private final Label entry;
@ -60,17 +62,17 @@ public class CaseNode extends BreakableNode {
this.entry = new Label("entry"); 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); super(caseNode);
this.test = cs.existingOrCopy(caseNode.test); this.test = test;
this.body = (Block)cs.existingOrCopy(caseNode.body); this.body = body;
this.entry = new Label(caseNode.entry); this.entry = new Label(caseNode.entry);
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean isTerminal() {
return new CaseNode(this, cs); return body.isTerminal();
} }
/** /**
@ -79,15 +81,11 @@ public class CaseNode extends BreakableNode {
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterCaseNode(this) != null) { if (visitor.enterCaseNode(this)) {
if (test != null) { final Node newTest = test == null ? null : test.accept(visitor);
test = test.accept(visitor); final Block newBody = body == null ? null : (Block)body.accept(visitor);
}
if (body != null) {
body = (Block)body.accept(visitor);
}
return visitor.leaveCaseNode(this); return visitor.leaveCaseNode(setTest(newTest).setBody(newBody));
} }
return this; return this;
@ -131,8 +129,19 @@ public class CaseNode extends BreakableNode {
/** /**
* Reset the test expression for this case node * Reset the test expression for this case node
* @param test new test expression * @param test new test expression
* @return new or same CaseNode
*/ */
public void setTest(final Node test) { public CaseNode setTest(final Node test) {
this.test = 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; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of a catch clause. * IR representation of a catch clause.
*
*/ */
public class CatchNode extends Node { @Immutable
public final class CatchNode extends Node {
/** Exception identifier. */ /** Exception identifier. */
private IdentNode exception; private final IdentNode exception;
/** Exception condition. */ /** Exception condition. */
private Node exceptionCondition; private final Node exceptionCondition;
/** Catch body. */ /** Catch body. */
private Block body; private final 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;
/** /**
* Constructors * Constructors
@ -64,18 +61,12 @@ public class CatchNode extends Node {
this.body = body; 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); super(catchNode);
this.exception = (IdentNode)cs.existingOrCopy(catchNode.exception); this.exception = exception;
this.exceptionCondition = cs.existingOrCopy(catchNode.exceptionCondition); this.exceptionCondition = exceptionCondition;
this.body = (Block)cs.existingOrCopy(catchNode.body); this.body = body;
this.isSyntheticRethrow = catchNode.isSyntheticRethrow;
}
@Override
protected Node copy(final CopyState cs) {
return new CatchNode(this, cs);
} }
/** /**
@ -84,20 +75,21 @@ public class CatchNode extends Node {
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterCatchNode(this) != null) { if (visitor.enterCatchNode(this)) {
exception = (IdentNode)exception.accept(visitor); return visitor.leaveCatchNode(
setException((IdentNode)exception.accept(visitor)).
if (exceptionCondition != null) { setExceptionCondition(exceptionCondition == null ? null : exceptionCondition.accept(visitor)).
exceptionCondition = exceptionCondition.accept(visitor); setBody((Block)body.accept(visitor)));
}
body = (Block)body.accept(visitor);
return visitor.leaveCatchNode(this);
} }
return this; return this;
} }
@Override
public boolean isTerminal() {
return body.isTerminal();
}
@Override @Override
public void toString(final StringBuilder sb) { public void toString(final StringBuilder sb) {
sb.append(" catch ("); sb.append(" catch (");
@ -110,23 +102,6 @@ public class CatchNode extends Node {
sb.append(')'); 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 * Get the identifier representing the exception thrown
* @return the exception identifier * @return the exception identifier
@ -146,9 +121,13 @@ public class CatchNode extends Node {
/** /**
* Reset the exception condition for this catch block * Reset the exception condition for this catch block
* @param exceptionCondition the new exception condition * @param exceptionCondition the new exception condition
* @return new or same CatchNode
*/ */
public void setExceptionCondition(final Node exceptionCondition) { public CatchNode setExceptionCondition(final Node exceptionCondition) {
this.exceptionCondition = 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() { public Block getBody() {
return body; 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; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for CONTINUE statements. * IR representation for CONTINUE statements.
*
*/ */
public class ContinueNode extends LabeledNode { @Immutable
public class ContinueNode extends Node {
private IdentNode label;
/** /**
* Constructor * Constructor
* *
* @param source the source * @param source source code
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param labelNode the continue label * @param label label for break or null if none
* @param targetNode node to continue to
* @param tryChain surrounding try chain
*/ */
public ContinueNode(final Source source, final long token, final int finish, final LabelNode labelNode, final Node targetNode, final TryNode tryChain) { public ContinueNode(final Source source, final long token, final int finish, final IdentNode label) {
super(source, token, finish, labelNode, targetNode, tryChain); super(source, token, finish);
setHasGoto(); this.label = label;
}
private ContinueNode(final ContinueNode continueNode, final CopyState cs) {
super(continueNode, cs);
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean hasGoto() {
return new ContinueNode(this, cs); return true;
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterContinueNode(this) != null) { if (visitor.enterContinueNode(this)) {
return visitor.leaveContinueNode(this); return visitor.leaveContinueNode(this);
} }
@ -69,21 +65,20 @@ public class ContinueNode extends LabeledNode {
} }
/** /**
* Return the target label of this continue node. * Get the label for this break node
* @return the target label. * @return label, or null if none
*/ */
public Label getTargetLabel() { public IdentNode getLabel() {
assert targetNode instanceof WhileNode : "continue target must be a while node"; return label;
return ((WhileNode)targetNode).getContinueLabel();
} }
@Override @Override
public void toString(final StringBuilder sb) { public void toString(final StringBuilder sb) {
sb.append("continue"); sb.append("continue");
if (labelNode != null) { if (label != null) {
sb.append(' '); sb.append(' ');
labelNode.getLabel().toString(sb); label.toString(sb);
} }
} }
} }

View File

@ -25,14 +25,15 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for an empty statement. * IR representation for an empty statement.
*
*/ */
public class EmptyNode extends Node { @Immutable
public final class EmptyNode extends Node {
/** /**
* Constructor * Constructor
@ -57,7 +58,7 @@ public class EmptyNode extends Node {
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterEmptyNode(this) != null) { if (visitor.enterEmptyNode(this)) {
return visitor.leaveEmptyNode(this); return visitor.leaveEmptyNode(this);
} }
return this; return this;

View File

@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; 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 * node means "this code will be executed" and evaluating it results in
* statements being added to the IR * statements being added to the IR
*/ */
public class ExecuteNode extends Node { @Immutable
public final class ExecuteNode extends Node {
/** Expression to execute. */ /** Expression to execute. */
private Node expression; private final Node expression;
/** /**
* Constructor * Constructor
@ -50,6 +52,11 @@ public class ExecuteNode extends Node {
this.expression = expression; this.expression = expression;
} }
private ExecuteNode(final ExecuteNode executeNode, final Node expression) {
super(executeNode);
this.expression = expression;
}
/** /**
* Constructor * Constructor
* *
@ -60,34 +67,15 @@ public class ExecuteNode extends Node {
this.expression = expression; this.expression = expression;
} }
private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) {
super(executeNode);
this.expression = cs.existingOrCopy(executeNode.expression);
}
@Override @Override
protected Node copy(final CopyState cs) { public boolean isTerminal() {
return new ExecuteNode(this, cs); return expression.isTerminal();
}
@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();
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterExecuteNode(this) != null) { if (visitor.enterExecuteNode(this)) {
setExpression(expression.accept(visitor)); return visitor.leaveExecuteNode(setExpression(expression.accept(visitor)));
return visitor.leaveExecuteNode(this);
} }
return this; return this;
@ -109,8 +97,12 @@ public class ExecuteNode extends Node {
/** /**
* Reset the expression to be executed * Reset the expression to be executed
* @param expression the expression * @param expression the expression
* @return new or same execute node
*/ */
public void setExpression(final Node expression) { public ExecuteNode setExpression(final Node expression) {
this.expression = 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; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representing a FOR statement. * IR representing a FOR statement.
*
*/ */
public class ForNode extends WhileNode { @Immutable
public final class ForNode extends LoopNode {
/** Initialize expression. */ /** Initialize expression. */
private Node init; private final Node init;
/** Test expression. */ /** Test expression. */
private Node modify; private final Node modify;
/** Iterator symbol. */ /** Iterator symbol. */
private Symbol iterator; private Symbol iterator;
/** is for in */ /** Is this a normal for loop? */
private boolean isForIn; public static final int IS_FOR = 1 << 0;
/** is for each */ /** Is this a normal for in loop? */
private boolean isForEach; 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 * Constructor
* *
* @param source the source * @param source the source
* @param token token * @param token token
* @param finish finish * @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) { 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); super(source, token, finish, test, body, false);
this.init = init;
this.modify = modify;
this.flags = flags;
} }
private ForNode(final ForNode forNode, final CopyState cs) { 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, cs); super(forNode, test, body, controlFlowEscapes);
this.init = init;
this.init = cs.existingOrCopy(forNode.init); this.modify = modify;
this.modify = cs.existingOrCopy(forNode.modify); this.flags = flags;
this.iterator = forNode.iterator; this.iterator = forNode.iterator; //TODO is this acceptable? symbols are never cloned, just copied as references
this.isForIn = forNode.isForIn;
this.isForEach = forNode.isForEach;
} }
@Override @Override
protected Node copy(final CopyState cs) { public Node ensureUniqueLabels(LexicalContext lc) {
return new ForNode(this, cs); return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
if (visitor.enterForNode(this) != null) { if (visitor.enterForNode(this)) {
if (init != null) { return visitor.leaveForNode(
init = init.accept(visitor); setInit(lc, init == null ? null : init.accept(visitor)).
} setTest(lc, test == null ? null : test.accept(visitor)).
setModify(lc, modify == null ? null : modify.accept(visitor)).
if (test != null) { setBody(lc, (Block)body.accept(visitor)));
test = test.accept(visitor);
}
if (modify != null) {
modify = modify.accept(visitor);
}
body = (Block)body.accept(visitor);
return visitor.leaveForNode(this);
} }
return this; return this;
@ -122,6 +124,19 @@ public class ForNode extends WhileNode {
sb.append(')'); 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 * Get the initialization expression for this for loop
* @return the initialization expression * @return the initialization expression
@ -132,10 +147,15 @@ public class ForNode extends WhileNode {
/** /**
* Reset the initialization expression for this for loop * Reset the initialization expression for this for loop
* @param lc lexical context
* @param init new initialization expression * @param init new initialization expression
* @return new for node if changed or existing if not
*/ */
public void setInit(final Node init) { public ForNode setInit(final LexicalContext lc, final Node init) {
this.init = 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 * @return true if this is a for in constructor
*/ */
public boolean isForIn() { public boolean isForIn() {
return isForIn; return (flags & IS_FOR_IN) != 0;
} }
/** /**
* Flag this to be a for in construct * 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() { public ForNode setIsForIn(final LexicalContext lc) {
this.isForIn = true; 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 * @return true if this is a for each construct
*/ */
public boolean isForEach() { public boolean isForEach() {
return isForEach; return (flags & IS_FOR_EACH) != 0;
} }
/** /**
* Flag this to be a for each construct * 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() { public ForNode setIsForEach(final LexicalContext lc) {
this.isForEach = true; return setFlags(lc, flags | IS_FOR_EACH);
} }
/** /**
@ -195,10 +219,15 @@ public class ForNode extends WhileNode {
/** /**
* Reset the modification expression for this ForNode * Reset the modification expression for this ForNode
* @param lc lexical context
* @param modify new modification expression * @param modify new modification expression
* @return new for node if changed or existing if not
*/ */
public void setModify(final Node modify) { public ForNode setModify(final LexicalContext lc, final Node modify) {
this.modify = modify; if (this.modify == modify) {
return this;
}
return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes));
} }
@Override @Override
@ -207,7 +236,39 @@ public class ForNode extends WhileNode {
} }
@Override @Override
public void setTest(final Node test) { public ForNode setTest(final LexicalContext lc, final Node test) {
this.test = 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.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for an identifier. * 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 PROPERTY_NAME = 1 << 0;
private static final int INITIALIZED_HERE = 1 << 1; private static final int INITIALIZED_HERE = 1 << 1;
private static final int FUNCTION = 1 << 2; private static final int FUNCTION = 1 << 2;
@ -47,9 +49,9 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
private final String name; private final String name;
/** Type for a callsite, e.g. X in a get()X or a set(X)V */ /** 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 * 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) { public IdentNode(final Source source, final long token, final int finish, final String name) {
super(source, token, finish); super(source, token, finish);
this.name = name; 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) { public IdentNode(final IdentNode identNode) {
super(identNode); super(identNode);
this.name = identNode.getName(); this.name = identNode.getName();
this.flags = identNode.flags; this.callSiteType = null;
this.flags = identNode.flags;
} }
@Override @Override
@ -92,40 +104,15 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
@Override @Override
public IdentNode setType(final Type type) { 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 // 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; return this;
} }
final IdentNode n = (IdentNode)clone(); if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
n.callSiteType = type; ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
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);
} }
return false;
}
@Override return new IdentNode(this, name, type, flags);
public int hashCode() {
return name.hashCode();
} }
/** /**
@ -135,7 +122,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterIdentNode(this) != null) { if (visitor.enterIdentNode(this)) {
return visitor.leaveIdentNode(this); return visitor.leaveIdentNode(this);
} }
@ -147,7 +134,7 @@ public class IdentNode extends Node implements PropertyKey, TypeOverride<IdentNo
if (hasCallSiteType()) { if (hasCallSiteType()) {
sb.append('{'); sb.append('{');
final String desc = getType().getDescriptor(); 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('}'); 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. * @return a node equivalent to this one except for the requested change.
*/ */
public IdentNode setIsPropertyName() { public IdentNode setIsPropertyName() {
if(isPropertyName()) return this; if (isPropertyName()) {
final IdentNode n = (IdentNode)clone(); return this;
n.flags |= PROPERTY_NAME; }
return n; 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. * @return a node equivalent to this one except for the requested change.
*/ */
public IdentNode setIsInitializedHere() { public IdentNode setIsInitializedHere() {
if(isInitializedHere()) return this; if (isInitializedHere()) {
final IdentNode n = (IdentNode)clone(); return this;
n.flags |= INITIALIZED_HERE; }
return n; 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 * @return true if this IdentNode is special
*/ */
public boolean isSpecialIdentity() { 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 @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. * @return an ident node identical to this one in all aspects except with its function flag set.
*/ */
public IdentNode setIsFunction() { public IdentNode setIsFunction() {
if(isFunction()) return this; if (isFunction()) {
final IdentNode n = (IdentNode)clone(); return this;
n.flags |= FUNCTION; }
return n; return new IdentNode(this, name, callSiteType, flags | FUNCTION);
} }
} }

View File

@ -25,22 +25,23 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for an IF statement. * IR representation for an IF statement.
*
*/ */
public class IfNode extends Node { @Immutable
public final class IfNode extends Node {
/** Test expression. */ /** Test expression. */
private Node test; private final Node test;
/** Pass statements. */ /** Pass statements. */
private Block pass; private final Block pass;
/** Fail statements. */ /** Fail statements. */
private Block fail; private final Block fail;
/** /**
* Constructor * 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) { public IfNode(final Source source, final long token, final int finish, final Node test, final Block pass, final Block fail) {
super(source, token, finish); super(source, token, finish);
this.test = test; this.test = test;
this.pass = pass; this.pass = pass;
this.fail = fail; 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); super(ifNode);
this.test = test;
this.test = cs.existingOrCopy(ifNode.test); this.pass = pass;
this.pass = (Block)cs.existingOrCopy(ifNode.pass); this.fail = fail;
this.fail = (Block)cs.existingOrCopy(ifNode.fail);
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean isTerminal() {
return new IfNode(this, cs); return pass.isTerminal() && fail != null && fail.isTerminal();
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterIfNode(this) != null) { if (visitor.enterIfNode(this)) {
test = test.accept(visitor); return visitor.leaveIfNode(
setTest(test.accept(visitor)).
pass = (Block)pass.accept(visitor); setPass((Block)pass.accept(visitor)).
setFail(fail == null ? null : (Block)fail.accept(visitor)));
if (fail != null) {
fail = (Block)fail.accept(visitor);
}
return visitor.leaveIfNode(this);
} }
return this; return this;
@ -105,6 +99,13 @@ public class IfNode extends Node {
return fail; 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 * Get the then block for this IfNode
* @return the then block * @return the then block
@ -113,6 +114,13 @@ public class IfNode extends Node {
return pass; 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 * Get the test expression for this IfNode
* @return the test expression * @return the test expression
@ -124,8 +132,12 @@ public class IfNode extends Node {
/** /**
* Reset the test expression for this IfNode * Reset the test expression for this IfNode
* @param test a new test expression * @param test a new test expression
* @return new or same IfNode
*/ */
public void setTest(final Node test) { public IfNode setTest(final Node test) {
this.test = 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; 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.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of an indexed access (brackets operator.) * IR representation of an indexed access (brackets operator.)
*
*/ */
public class IndexNode extends BaseNode implements TypeOverride<IndexNode> { @Immutable
/** Property ident. */ public final class IndexNode extends BaseNode {
private Node index; /** Property index. */
private final Node index;
private boolean hasCallSiteType;
/** /**
* Constructors * Constructors
@ -52,50 +48,27 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
* @param index index for access * @param index index for access
*/ */
public IndexNode(final Source source, final long token, final int finish, final Node base, final Node index) { 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; this.index = index;
} }
/** private IndexNode(final IndexNode indexNode, final Node base, final Node index, final boolean isFunction, final boolean hasCallSiteType) {
* Copy constructor super(indexNode, base, isFunction, hasCallSiteType);
* this.index = index;
* @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();
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterIndexNode(this) != null) { if (visitor.enterIndexNode(this)) {
base = base.accept(visitor); final Node newBase = base.accept(visitor);
index = index.accept(visitor); final Node newIndex = index.accept(visitor);
return visitor.leaveIndexNode(this); 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; return this;
@ -105,7 +78,7 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
public void toString(final StringBuilder sb) { public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(base.tokenType(), true); final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
if (hasCallSiteType) { if (hasCallSiteType()) {
sb.append('{'); sb.append('{');
final String desc = getType().getDescriptor(); 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());
@ -135,27 +108,19 @@ public class IndexNode extends BaseNode implements TypeOverride<IndexNode> {
return index; return index;
} }
/** @Override
* Reset the index expression for this IndexNode public BaseNode setIsFunction() {
* @param index a new index expression if (isFunction()) {
*/ return this;
public void setIndex(final Node index) { }
this.index = index; return new IndexNode(this, base, index, true, hasCallSiteType());
} }
@Override @Override
public IndexNode setType(final Type type) { public IndexNode setType(final Type type) {
if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) { logTypeChange(type);
ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType()); getSymbol().setTypeOverride(type); //always a temp so this is fine.
} return new IndexNode(this, base, index, isFunction(), true);
hasCallSiteType = true;
getSymbol().setTypeOverride(type);
return this;
}
@Override
public boolean canHaveCallSiteType() {
return true; //carried by the symbol and always the same nodetype==symboltype
} }
} }

View File

@ -25,29 +25,20 @@
package jdk.nashorn.internal.ir; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for a labeled statement. * IR representation for a labeled statement.
*
*/ */
@Immutable
public class LabelNode extends Node { public final class LabelNode extends LexicalContextNode {
/** Label ident. */ /** Label ident. */
private IdentNode label; private final IdentNode label;
/** Statements. */ /** Statements. */
private Block body; private final Block body;
/** Node to break from. */
@Ignore
private Node breakNode;
/** Node to continue. */
@Ignore
private Node continueNode;
/** /**
* Constructor * Constructor
@ -65,26 +56,23 @@ public class LabelNode extends Node {
this.body = body; this.body = body;
} }
private LabelNode(final LabelNode labelNode, final CopyState cs) { private LabelNode(final LabelNode labelNode, final IdentNode label, final Block body) {
super(labelNode); super(labelNode);
this.label = label;
this.label = (IdentNode)cs.existingOrCopy(labelNode.label); this.body = body;
this.body = (Block)cs.existingOrCopy(labelNode.body);
this.breakNode = cs.existingOrSame(labelNode.breakNode);
this.continueNode = cs.existingOrSame(labelNode.continueNode);
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean isTerminal() {
return new LabelNode(this, cs); return body.isTerminal();
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
if (visitor.enterLabelNode(this) != null) { if (visitor.enterLabelNode(this)) {
label = (IdentNode)label.accept(visitor); return visitor.leaveLabelNode(
body = (Block)body.accept(visitor); setLabel(visitor.getLexicalContext(), (IdentNode)label.accept(visitor)).
return visitor.leaveLabelNode(this); setBody(visitor.getLexicalContext(), (Block)body.accept(visitor)));
} }
return this; return this;
@ -106,44 +94,15 @@ public class LabelNode extends Node {
/** /**
* Reset the body of the node * Reset the body of the node
* @param lc lexical context
* @param body new body * @param body new body
* @return new for node if changed or existing if not
*/ */
public void setBody(final Block body) { public LabelNode setBody(final LexicalContext lc, final Block body) {
this.body = body; if (this.body == body) {
} return this;
}
/** return Node.replaceInLexicalContext(lc, this, new LabelNode(this, label, 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;
} }
/** /**
@ -154,4 +113,11 @@ public class LabelNode extends Node {
return label; 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; package jdk.nashorn.internal.ir;
import java.util.ArrayDeque; import java.io.File;
import java.util.Deque;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; 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 * 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. * 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 { public class LexicalContext {
private final Deque<Block> lexicalContext; private LexicalContextNode[] stack;
private int[] flags;
private int sp;
/** /**
* Creates a new empty lexical context. * Creates a new empty lexical context.
*/ */
public LexicalContext() { 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. * 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) { public <T extends LexicalContextNode> T push(final T node) {
//new Exception(block.toString()).printStackTrace(); if (sp == stack.length) {
lexicalContext.push(block); 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. * Is the context empty?
* @param the block expected to be popped, used to detect unbalanced pushes/pops * @return true if empty
*/ */
public void pop(Block block) { public boolean isEmpty() {
final Block popped = lexicalContext.pop(); return sp == 0;
assert popped == block; }
/**
* 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];
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. * @return an iterator over all blocks in the context.
*/ */
public Iterator<Block> getBlocks() { 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. * @return an iterator over all functions in the context.
*/ */
public Iterator<FunctionNode> getFunctions() { 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; * Get the parent block for the current lexical context block
private FunctionNode next; * @return parent block
*/
FunctionIterator(Iterator<Block> it) { public Block getParentBlock() {
this.it = it; final Iterator<Block> iter = new NodeIterator<>(Block.class, getCurrentFunction());
next = findNext(); iter.next();
} return iter.hasNext() ? iter.next() : null;
@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();
}
} }
/** /**
@ -98,12 +252,12 @@ public class LexicalContext implements Cloneable {
* @param block the block whose ancestors are returned * @param block the block whose ancestors are returned
* @return an iterator over all ancestors block of the given block. * @return an iterator over all ancestors block of the given block.
*/ */
public Iterator<Block> getAncestorBlocks(Block block) { public Iterator<Block> getAncestorBlocks(final Block block) {
final Iterator<Block> it = getBlocks(); final Iterator<Block> iter = getBlocks();
while(it.hasNext()) { while (iter.hasNext()) {
final Block b = it.next(); final Block b = iter.next();
if(block == b) { if (block == b) {
return it; return iter;
} }
} }
throw new AssertionError("Block is not on the current lexical context stack"); 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. * @return an iterator over a block and all its ancestors.
*/ */
public Iterator<Block> getBlocks(final Block block) { public Iterator<Block> getBlocks(final Block block) {
final Iterator<Block> it = getAncestorBlocks(block); final Iterator<Block> iter = getAncestorBlocks(block);
return new Iterator<Block>() { return new Iterator<Block>() {
boolean blockReturned = false; boolean blockReturned = false;
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return it.hasNext() || !blockReturned; return iter.hasNext() || !blockReturned;
} }
@Override @Override
public Block next() { public Block next() {
if(blockReturned) { if (blockReturned) {
return it.next(); return iter.next();
} }
blockReturned = true; blockReturned = true;
return block; 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. * Get the function for this block. If the block is itself a function
* @param block the block * this returns identity
* @return the function closest to the block. * @param block block for which to get function
* @see #getParentFunction(Block) * @return function for block
*/ */
public FunctionNode getFunction(Block block) { public FunctionNode getFunction(final Block block) {
if(block instanceof FunctionNode) { final Iterator<LexicalContextNode> iter = new NodeIterator<>(LexicalContextNode.class);
return (FunctionNode)block; while (iter.hasNext()) {
} final LexicalContextNode next = iter.next();
return getParentFunction(block); if (next == block) {
} while (iter.hasNext()) {
final LexicalContextNode next2 = iter.next();
/** if (next2 instanceof FunctionNode) {
* Returns the closest function node to the block and all its ancestor functions. If the block is itself a function, return (FunctionNode)next2;
* 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;
} }
} }
assert false;
return null; return null;
} }
@ -185,7 +319,7 @@ public class LexicalContext implements Cloneable {
* @return the innermost block in the context. * @return the innermost block in the context.
*/ */
public Block getCurrentBlock() { public Block getCurrentBlock() {
return lexicalContext.element(); return getBlocks().next();
} }
/** /**
@ -193,6 +327,292 @@ public class LexicalContext implements Cloneable {
* @return the innermost function in the context. * @return the innermost function in the context.
*/ */
public FunctionNode getCurrentFunction() { 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;
}
/**
* Check if lexical context is currently inside a with block
* @return true if in a with block
*/
public boolean inWith() {
return getScopeNestingLevelTo(null) > 0;
}
/**
* 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 * or visit www.oracle.com if you need additional information or have any
* questions. * questions.
*/ */
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* Loop representing do while loops. This is mostly split from WhileNode * Superclass for nodes that can be part of the lexical context
* because of the different order of the Phi Traversals * @see LexicalContext
*
*/ */
public class DoWhileNode extends WhileNode { public abstract class LexicalContextNode extends Node {
/** /**
* Constructor * Constructor
* *
* @param source the source * @param source source
* @param token token * @param token token
* @param finish finish * @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); super(source, token, finish);
} }
/** /**
* Copy constructor * Copy constructor
* *
* @param doWhileNode source node * @param node source node
* @param cs copy state
*/ */
protected DoWhileNode(final DoWhileNode doWhileNode, final CopyState cs) { protected LexicalContextNode(final LexicalContextNode node) {
super(doWhileNode, cs); super(node);
} }
@Override /**
protected Node copy(final CopyState cs) { * Accept function for the node given a lexical context. It must be prepared
return new DoWhileNode(this, cs); * 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 @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterDoWhileNode(this) != null) { final LexicalContext lc = visitor.getLexicalContext();
body = (Block)body.accept(visitor); lc.push(this);
test = test.accept(visitor); final LexicalContextNode newNode = (LexicalContextNode)accept(lc, visitor);
return lc.pop(newNode);
return visitor.leaveDoWhileNode(this);
}
return this;
}
@Override
public void toString(final StringBuilder sb) {
sb.append("while (");
test.toString(sb);
sb.append(')');
} }
} }

View File

@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
@ -32,8 +33,8 @@ import jdk.nashorn.internal.runtime.Source;
/** /**
* IR Node representing a line number * IR Node representing a line number
*/ */
@Immutable
public class LineNumberNode extends Node { public final class LineNumberNode extends Node {
/** Line number */ /** Line number */
private final int lineNumber; private final int lineNumber;
@ -46,24 +47,17 @@ public class LineNumberNode extends Node {
*/ */
public LineNumberNode(final Source source, final long token, final int lineNumber) { public LineNumberNode(final Source source, final long token, final int lineNumber) {
super(source, token, Token.descPosition(token)); super(source, token, Token.descPosition(token));
this.lineNumber = lineNumber; this.lineNumber = lineNumber;
} }
private LineNumberNode(final LineNumberNode lineNumberNode) { private LineNumberNode(final LineNumberNode lineNumberNode) {
super(lineNumberNode); super(lineNumberNode);
this.lineNumber = lineNumberNode.getLineNumber(); this.lineNumber = lineNumberNode.getLineNumber();
} }
@Override
protected Node copy(final CopyState cs) {
return new LineNumberNode(this);
}
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterLineNumberNode(this) != null) { if (visitor.enterLineNumberNode(this)) {
return visitor.leaveLineNumberNode(this); return visitor.leaveLineNumberNode(this);
} }

View File

@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.Type; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.LexerToken; import jdk.nashorn.internal.parser.Lexer.LexerToken;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
@ -44,6 +45,7 @@ import jdk.nashorn.internal.runtime.Undefined;
* *
* @param <T> the literal type * @param <T> the literal type
*/ */
@Immutable
public abstract class LiteralNode<T> extends Node implements PropertyKey { public abstract class LiteralNode<T> extends Node implements PropertyKey {
/** Literal value */ /** Literal value */
protected final T value; protected final T value;
@ -93,23 +95,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return value == null; 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 * Check if the literal value is boolean true
* @return true if 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 @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterLiteralNode(this) != null) { if (visitor.enterLiteralNode(this)) {
return visitor.leaveLiteralNode(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()); 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) { 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); 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); super(literalNode);
} }
@Override
protected Node copy(final CopyState cs) {
return new BooleanLiteralNode(this);
}
@Override @Override
public boolean isTrue() { public boolean isTrue() {
return value; 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); 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); private final Type type = numberGetType(value);
@ -357,11 +339,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return null; return null;
} }
@Override
protected Node copy(final CopyState cs) {
return new NumberLiteralNode(this);
}
@Override @Override
public Type getType() { public Type getType() {
return type; return type;
@ -407,11 +384,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) { private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
super(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()); return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
} }
@Immutable
private static class StringLiteralNode extends LiteralNode<String> { private static class StringLiteralNode extends LiteralNode<String> {
private StringLiteralNode(final Source source, final long token, final int finish, final String value) { private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
super(source, Token.recast(token, TokenType.STRING), finish, 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); super(literalNode);
} }
@Override
protected Node copy(final CopyState cs) {
return new StringLiteralNode(this);
}
@Override @Override
public void toString(final StringBuilder sb) { public void toString(final StringBuilder sb) {
sb.append('\"'); 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); return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
} }
@Immutable
private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> { private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) { 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? 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); super(literalNode);
} }
@Override
protected Node copy(final CopyState cs) {
return new LexerTokenLiteralNode(this);
}
@Override @Override
public Type getType() { public Type getType() {
return Type.OBJECT; 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); 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) { private NodeLiteralNode(final Source source, final long token, final int finish) {
this(source, token, finish, null); this(source, token, finish, null);
@ -557,14 +521,9 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
super(literalNode, value); super(literalNode, value);
} }
@Override
protected Node copy(final CopyState cs) {
return new NodeLiteralNode(this);
}
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterLiteralNode(this) != null) { if (visitor.enterLiteralNode(this)) {
if (value != null) { if (value != null) {
final Node newValue = value.accept(visitor); final Node newValue = value.accept(visitor);
if(value != newValue) { if(value != newValue) {
@ -617,7 +576,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
/** /**
* Array literal node class. * Array literal node class.
*/ */
public static class ArrayLiteralNode extends LiteralNode<Node[]> { public static final class ArrayLiteralNode extends LiteralNode<Node[]> {
private static class PostsetMarker { private static class PostsetMarker {
//empty //empty
} }
@ -705,11 +664,6 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
this.elementType = node.elementType; 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 * 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 @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterLiteralNode(this) != null) { if (visitor.enterLiteralNode(this)) {
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
final Node element = value[i]; final Node element = value[i];
if (element != null) { if (element != null) {

View File

@ -25,16 +25,13 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import java.util.Objects;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* Used to locate an entity back to it's source file. * Used to locate an entity back to it's source file.
*
*/ */
public class Location implements Cloneable { public class Location implements Cloneable {
/** Source of entity. */ /** Source of entity. */
private final Source source; private final Source source;
@ -73,22 +70,13 @@ public class Location implements Cloneable {
} }
@Override @Override
public boolean equals(final Object other) { public final boolean equals(final Object other) {
if (other == null) { return super.equals(other);
return false;
}
if (other.getClass() != this.getClass()) {
return false;
}
final Location loc = (Location)other;
return token == loc.token && Objects.equals(source, loc.source);
} }
@Override @Override
public int hashCode() { public final int hashCode() {
return Token.hashCode(token) ^ Objects.hashCode(source); 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; 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.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
@ -33,30 +35,17 @@ import jdk.nashorn.internal.runtime.Source;
/** /**
* Nodes are used to compose Abstract Syntax Trees. * Nodes are used to compose Abstract Syntax Trees.
*
*/ */
public abstract class Node extends Location { public abstract class Node extends Location {
/** Node symbol. */ /** Node symbol. */
private Symbol symbol; private Symbol symbol;
/** Start of source range. */ /** Start of source range. */
protected int start; protected final int start;
/** End of source range. */ /** End of source range. */
protected int finish; 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 * Constructor
* *
@ -71,6 +60,21 @@ public abstract class Node extends Location {
this.finish = finish; 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 * Copy constructor
* *
@ -79,13 +83,9 @@ public abstract class Node extends Location {
protected Node(final Node node) { protected Node(final Node node) {
super(node); super(node);
this.symbol = node.symbol; this.symbol = node.symbol;
this.isResolved = node.isResolved; this.start = node.start;
this.isTerminal = node.isTerminal; this.finish = node.finish;
this.hasGoto = node.hasGoto;
this.shouldDiscard = node.shouldDiscard;
this.start = node.start;
this.finish = node.finish;
} }
/** /**
@ -155,28 +155,6 @@ public abstract class Node extends Location {
return Type.OBJECT; 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? * 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 { public Node ensureUniqueLabels(final LexicalContext lc) {
private final IdentityHashMap<Node, Node> cloneMap = new IdentityHashMap<>(); return this;
/**
* 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);
} }
/** /**
@ -283,35 +202,7 @@ public abstract class Node extends Location {
* @return true if terminal * @return true if terminal
*/ */
public boolean hasTerminalFlags() { public boolean hasTerminalFlags() {
return isTerminal || hasGoto; 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;
} }
/** /**
@ -336,29 +227,7 @@ public abstract class Node extends Location {
* @return true if node has goto semantics * @return true if node has goto semantics
*/ */
public boolean hasGoto() { public boolean hasGoto() {
return hasGoto; return false;
}
/**
* 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;
} }
/** /**
@ -369,14 +238,6 @@ public abstract class Node extends Location {
return start; 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 * Return the Symbol the compiler has assigned to this Node. The symbol
* is the place where it's expression value is stored after evaluation * 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 * @return true if this node is terminal
*/ */
public boolean isTerminal() { public boolean isTerminal() {
return isTerminal; return false;
} }
/** //on change, we have to replace the entire list, that's we can't simple do ListIterator.set
* Set this to be a terminal node, i.e. it terminates control flow as described static <T extends Node> List<T> accept(final NodeVisitor visitor, final Class<T> clazz, final List<T> list) {
* in {@link Node#isTerminal()} boolean changed = false;
* final List<T> newList = new ArrayList<>();
* @param isTerminal true if this is a terminal node, false otherwise
*/ for (final Node node : list) {
public void setIsTerminal(final boolean isTerminal) { final T newNode = clazz.cast(node.accept(visitor));
this.isTerminal = isTerminal; 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; package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of an object literal. * IR representation of an object literal.
*/ */
public class ObjectNode extends Node { @Immutable
public final class ObjectNode extends Node {
/** Literal elements. */ /** Literal elements. */
private final List<Node> 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) { public ObjectNode(final Source source, final long token, final int finish, final List<Node> elements) {
super(source, token, finish); super(source, token, finish);
this.elements = elements; this.elements = elements;
} }
private ObjectNode(final ObjectNode objectNode, final CopyState cs) { private ObjectNode(final ObjectNode objectNode, final List<Node> elements) {
super(objectNode); super(objectNode);
this.elements = elements;
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);
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterObjectNode(this) != null) { if (visitor.enterObjectNode(this)) {
for (int i = 0, count = elements.size(); i < count; i++) { return visitor.leaveObjectNode(setElements(Node.accept(visitor, Node.class, elements)));
elements.set(i, elements.get(i).accept(visitor));
}
return visitor.leaveObjectNode(this);
} }
return this; return this;
@ -112,4 +97,11 @@ public class ObjectNode extends Node {
public List<Node> getElements() { public List<Node> getElements() {
return Collections.unmodifiableList(elements); 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; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of an object literal property. * IR representation of an object literal property.
*/ */
public class PropertyNode extends Node { @Immutable
public final class PropertyNode extends Node {
/** Property key. */ /** Property key. */
private PropertyKey key; private final PropertyKey key;
/** Property value. */ /** Property value. */
private Node value; private final Node value;
/** Property getter. */ /** Property getter. */
@Reference private final FunctionNode getter;
private Node getter;
/** Property getter. */ /** Property getter. */
@Reference private final FunctionNode setter;
private Node setter;
/** /**
* Constructor * Constructor
@ -56,26 +55,23 @@ public class PropertyNode extends Node {
* @param finish finish * @param finish finish
* @param key the key of this property * @param key the key of this property
* @param value the value 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); super(source, token, finish);
this.key = key; this.key = key;
this.value = value; 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); super(propertyNode);
this.key = key;
this.key = (PropertyKey)cs.existingOrCopy((Node)propertyNode.key); this.value = value;
this.value = cs.existingOrCopy(propertyNode.value); this.getter = getter;
this.getter = cs.existingOrSame(propertyNode.getter); this.setter = setter;
this.setter = cs.existingOrSame(propertyNode.setter);
}
@Override
protected Node copy(final CopyState cs) {
return new PropertyNode(this, cs);
} }
/** /**
@ -88,22 +84,12 @@ public class PropertyNode extends Node {
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterPropertyNode(this) != null) { if (visitor.enterPropertyNode(this)) {
key = (PropertyKey)((Node)key).accept(visitor); return visitor.leavePropertyNode(
setKey((PropertyKey)((Node)key).accept(visitor)).
if (value != null) { setValue(value == null ? null : value.accept(visitor)).
value = value.accept(visitor); setGetter(getter == null ? null : (FunctionNode)getter.accept(visitor)).
} setSetter(setter == null ? null : (FunctionNode)setter.accept(visitor)));
if (getter != null) {
getter = getter.accept(visitor);
}
if (setter != null) {
setter = setter.accept(visitor);
}
return visitor.leavePropertyNode(this);
} }
return this; return this;
@ -136,16 +122,20 @@ public class PropertyNode extends Node {
* Get the getter for this property * Get the getter for this property
* @return getter or null if none exists * @return getter or null if none exists
*/ */
public Node getGetter() { public FunctionNode getGetter() {
return getter; return getter;
} }
/** /**
* Set the getter of this property, null if none * Set the getter of this property, null if none
* @param getter getter * @param getter getter
* @return same node or new node if state changed
*/ */
public void setGetter(final Node getter) { public PropertyNode setGetter(final FunctionNode getter) {
this.getter = 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; 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 * Get the setter for this property
* @return setter or null if none exists * @return setter or null if none exists
*/ */
public Node getSetter() { public FunctionNode getSetter() {
return setter; return setter;
} }
/** /**
* Set the setter for this property, null if none * Set the setter for this property, null if none
* @param setter setter * @param setter setter
* @return same node or new node if state changed
*/ */
public void setSetter(final Node setter) { public PropertyNode setSetter(final FunctionNode setter) {
this.setter = 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 * Set the value of this property
* @param value new value * @param value new value
* @return same node or new node if state changed
*/ */
public void setValue(final Node value) { public PropertyNode setValue(final Node value) {
this.value = 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.RETURN;
import static jdk.nashorn.internal.parser.TokenType.YIELD; import static jdk.nashorn.internal.parser.TokenType.YIELD;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for RETURN or YIELD statements. * IR representation for RETURN or YIELD statements.
*
*/ */
@Immutable
public class ReturnNode extends Node { public class ReturnNode extends Node {
/** Optional expression. */ /** Optional expression. */
private Node expression; private final Node expression;
/** Try chain. */
@Ignore
private final TryNode tryChain;
/** /**
* Constructor * Constructor
@ -51,27 +46,20 @@ public class ReturnNode extends Node {
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param expression expression to return * @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); super(source, token, finish);
this.expression = expression; 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); super(returnNode);
this.expression = expression;
this.expression = cs.existingOrCopy(returnNode.expression);
this.tryChain = (TryNode)cs.existingOrSame(returnNode.tryChain);
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean isTerminal() {
return new ReturnNode(this, cs); return true;
} }
/** /**
@ -100,11 +88,10 @@ public class ReturnNode extends Node {
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterReturnNode(this) != null) { if (visitor.enterReturnNode(this)) {
if (expression != null) { if (expression != null) {
expression = expression.accept(visitor); return visitor.leaveReturnNode(setExpression(expression.accept(visitor)));
} }
return visitor.leaveReturnNode(this); 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 * Get the expression this node returns
* @return return expression, or null if void return * @return return expression, or null if void return
@ -151,16 +119,13 @@ public class ReturnNode extends Node {
/** /**
* Reset the expression this node returns * Reset the expression this node returns
* @param expression new expression, or null if void return * @param expression new expression, or null if void return
* @return new or same return node
*/ */
public void setExpression(final Node expression) { public ReturnNode setExpression(final Node expression) {
this.expression = 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.Collections;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.codegen.types.Type; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for a runtime call. * IR representation for a runtime call.
*
*/ */
@Immutable
public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> { 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; private final List<Node> args;
/** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */ /** 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 */ /** is final - i.e. may not be removed again, lower in the code pipeline */
private boolean isFinal; private final boolean isFinal;
/** /**
* Constructor * Constructor
@ -290,6 +291,17 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
this.request = request; this.request = request;
this.args = args; 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) { public RuntimeNode(final Node parent, final Request request, final List<Node> args) {
super(parent); super(parent);
this.request = request; this.request = request;
this.args = args; 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()); 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 * Is this node final - i.e. it can never be replaced with other nodes again
* @return true if final * @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 * 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() { public RuntimeNode setIsFinal(final boolean isFinal) {
this.isFinal = true; if (this.isFinal == isFinal) {
} return this;
}
@Override return new RuntimeNode(this, request, callSiteType, isFinal, args);
protected Node copy(final CopyState cs) {
return new RuntimeNode(this, cs);
} }
/** /**
@ -394,8 +394,10 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
@Override @Override
public RuntimeNode setType(final Type type) { public RuntimeNode setType(final Type type) {
this.callSiteType = type; if (this.callSiteType == type) {
return this; return this;
}
return new RuntimeNode(this, request, type, isFinal, args);
} }
@Override @Override
@ -409,12 +411,12 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterRuntimeNode(this) != null) { if (visitor.enterRuntimeNode(this)) {
for (int i = 0, count = args.size(); i < count; i++) { final List<Node> newArgs = new ArrayList<>();
args.set(i, args.get(i).accept(visitor)); for (final Node arg : args) {
newArgs.add(arg.accept(visitor));
} }
return visitor.leaveRuntimeNode(setArgs(newArgs));
return visitor.leaveRuntimeNode(this);
} }
return this; return this;
@ -449,6 +451,13 @@ public class RuntimeNode extends Node implements TypeOverride<RuntimeNode> {
return Collections.unmodifiableList(args); 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 * Get the request that this runtime node implements
* @return the request * @return the request

View File

@ -25,99 +25,65 @@
package jdk.nashorn.internal.ir; 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.CompileUnit;
import jdk.nashorn.internal.codegen.Label; import jdk.nashorn.internal.ir.annotations.Immutable;
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.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
/** /**
* Node indicating code is split across classes. * Node indicating code is split across classes.
*/ */
public class SplitNode extends Node { @Immutable
public class SplitNode extends LexicalContextNode {
/** Split node method name. */ /** Split node method name. */
private final String name; private final String name;
/** Compilation unit. */ /** Compilation unit. */
private CompileUnit compileUnit; private final 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;
/** Body of split code. */ /** Body of split code. */
@Ignore private final Node body;
private Node body;
/** /**
* Constructor * Constructor
* *
* @param name name of split node * @param name name of split node
* @param functionNode function node to split in * @param body body of split code
* @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()); super(body.getSource(), body.getToken(), body.getFinish());
this.name = name;
this.name = name; this.body = body;
this.functionNode = functionNode; this.compileUnit = compileUnit;
this.body = body;
this.externalTargets = new ArrayList<>();
} }
private SplitNode(final SplitNode splitNode, final CopyState cs) { private SplitNode(final SplitNode splitNode, final Node body) {
super(splitNode); super(splitNode);
this.name = splitNode.name;
this.name = splitNode.name; this.body = body;
this.functionNode = (FunctionNode)cs.existingOrSame(splitNode.functionNode); this.compileUnit = splitNode.compileUnit;
this.body = cs.existingOrCopy(splitNode.body);
this.externalTargets = new ArrayList<>();
} }
@Override /**
protected Node copy(final CopyState cs) { * Get the body for this split node - i.e. the actual code it encloses
return new SplitNode(this, cs); * @return body for split node
*/
public Node getBody() {
return body;
} }
@Override private SplitNode setBody(final LexicalContext lc, final Node body) {
public Node accept(final NodeVisitor visitor) { if (this.body == body) {
final CompileUnit saveCompileUnit = visitor.getCurrentCompileUnit(); return this;
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);
} }
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; return this;
} }
@ -129,22 +95,6 @@ public class SplitNode extends Node {
body.toString(sb); 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 * Get the name for this split node
* @return name * @return name
@ -161,67 +111,4 @@ public class SplitNode extends Node {
return compileUnit; 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.codegen.Label; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of a SWITCH statement. * IR representation of a SWITCH statement.
*/ */
public class SwitchNode extends BreakableNode { @Immutable
public final class SwitchNode extends BreakableNode {
/** Switch expression. */ /** 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. */ /** Tag symbol. */
private Symbol tag; 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 * Constructor
* *
* @param source the source * @param source the source
* @param token token * @param token token
* @param finish finish * @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) { 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); super(source, token, finish, new Label("switch_break"));
this.breakLabel = 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); 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<>(); final List<CaseNode> newCases = new ArrayList<>();
for (final CaseNode caseNode : cases) {
for (final CaseNode caseNode : switchNode.getCases()) { newCases.add(new CaseNode(caseNode, caseNode.getTest(), caseNode.getBody()));
newCases.add((CaseNode)cs.existingOrCopy(caseNode));
} }
return Node.replaceInLexicalContext(lc, this, new SwitchNode(this, expression, newCases, defaultCaseIndex));
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());
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean isTerminal() {
return new SwitchNode(this, cs); //there must be a default case, and that including all other cases must terminate
} if (!cases.isEmpty() && defaultCaseIndex != -1) {
for (final CaseNode caseNode : cases) {
@Override if (!caseNode.isTerminal()) {
public Node accept(final NodeVisitor visitor) { return false;
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));
} }
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; return this;
@ -107,6 +118,14 @@ public class SwitchNode extends BreakableNode {
sb.append(')'); 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 * Get the cases in this switch
* @return a list of case nodes * @return a list of case nodes
@ -115,28 +134,34 @@ public class SwitchNode extends BreakableNode {
return Collections.unmodifiableList(cases); 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 * Set or reset the list of cases in this switch
* @param lc lexical context
* @param cases a list of cases, case nodes * @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) { public SwitchNode setCases(final LexicalContext lc, final List<CaseNode> cases, final CaseNode defaultCase) {
this.cases = cases; return setCases(lc, cases, defaultCase == null ? -1 : cases.indexOf(defaultCase));
}
/**
* 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;
} }
/** /**
@ -149,10 +174,15 @@ public class SwitchNode extends BreakableNode {
/** /**
* Set or reset the expression to switch on * Set or reset the expression to switch on
* @param lc lexical context
* @param expression switch expression * @param expression switch expression
* @return new switch node or same if no state was changed
*/ */
public void setExpression(final Node expression) { public SwitchNode setExpression(final LexicalContext lc, final Node expression) {
this.expression = 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.HashSet;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.options.Options; 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; public static final int IS_LET = 1 << 8;
/** Is this an internal symbol, never represented explicitly in source code */ /** Is this an internal symbol, never represented explicitly in source code */
public static final int IS_INTERNAL = 1 << 9; 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. */ /** Null or name identifying symbol. */
private final String name; private final String name;
@ -70,12 +74,6 @@ public final class Symbol implements Comparable<Symbol> {
/** Symbol flags. */ /** Symbol flags. */
private int flags; private int flags;
/** Defining node. */
private Node node;
/** Definition block. */
private final Block block;
/** Type of symbol. */ /** Type of symbol. */
private Type type; private Type type;
@ -121,16 +119,12 @@ public final class Symbol implements Comparable<Symbol> {
* *
* @param name name of symbol * @param name name of symbol
* @param flags symbol flags * @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 type type of this symbol
* @param slot bytecode slot for 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.name = name;
this.flags = flags; this.flags = flags;
this.node = node;
this.block = block;
this.type = type; this.type = type;
this.slot = slot; this.slot = slot;
this.fieldIndex = -1; this.fieldIndex = -1;
@ -142,11 +136,9 @@ public final class Symbol implements Comparable<Symbol> {
* *
* @param name name of symbol * @param name name of symbol
* @param flags symbol flags * @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) { public Symbol(final String name, final int flags) {
this(name, flags, node, block, Type.UNKNOWN, -1); this(name, flags, Type.UNKNOWN, -1);
} }
/** /**
@ -157,7 +149,7 @@ public final class Symbol implements Comparable<Symbol> {
* @param type type of this symbol * @param type type of this symbol
*/ */
public Symbol(final String name, final int flags, final Type type) { 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) { 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; 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) { private static String type(final String desc) {
switch (desc.charAt(desc.length() - 1)) { switch (desc.charAt(desc.length() - 1)) {
case ';': case ';':
@ -371,14 +349,14 @@ public final class Symbol implements Comparable<Symbol> {
/** /**
* Flag this symbol as scope as described in {@link Symbol#isScope()} * 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()) { if (!isScope()) {
trace("SET IS SCOPE"); trace("SET IS SCOPE");
} }
flags |= 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 * Flag this symbol as a function's self-referencing symbol.
* @return a block * @return true if this symbol as a function's self-referencing symbol.
*/ */
public Block getBlock() { public boolean isFunctionSelf() {
return block; return (flags & IS_FUNCTION_SELF) == IS_FUNCTION_SELF;
} }
/** /**
@ -492,7 +470,7 @@ public final class Symbol implements Comparable<Symbol> {
* @return field index * @return field index
*/ */
public int getFieldIndex() { public int getFieldIndex() {
assert fieldIndex != -1 : "fieldIndex must be initialized"; assert fieldIndex != -1 : "fieldIndex must be initialized " + fieldIndex;
return fieldIndex; return fieldIndex;
} }
@ -503,7 +481,6 @@ public final class Symbol implements Comparable<Symbol> {
* @param fieldIndex field index - a positive integer * @param fieldIndex field index - a positive integer
*/ */
public void setFieldIndex(final int fieldIndex) { public void setFieldIndex(final int fieldIndex) {
assert this.fieldIndex == -1 : "fieldIndex must be initialized only once";
this.fieldIndex = fieldIndex; this.fieldIndex = fieldIndex;
} }
@ -523,22 +500,6 @@ public final class Symbol implements Comparable<Symbol> {
this.flags = flags; 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 * Get the name of this symbol
* @return symbol name * @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 * From a lexical context, set this symbol as needing scope, which
* in the script * will set flags for the defining block that will be written when
* @return true if this this is a global scope symbol * 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() { public static void setSymbolIsScope(final LexicalContext lc, final Symbol symbol) {
return block instanceof FunctionNode && ((FunctionNode) block).isProgram(); symbol.setIsScope();
if (!symbol.isGlobal()) {
lc.setFlag(lc.getDefiningBlock(symbol), Block.NEEDS_SCOPE);
}
} }
private void trace(final String desc) { private void trace(final String desc) {
if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) { 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))) { if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
new Throwable().printStackTrace(Context.getCurrentErr()); new Throwable().printStackTrace(Context.getCurrentErr());
} }

View File

@ -25,15 +25,21 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* TernaryNode nodes represent three operand operations (?:). * 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. */ /** Third argument. */
private Node third; private final Node third;
/** /**
* Constructor * Constructor
@ -45,43 +51,26 @@ public class TernaryNode extends BinaryNode {
* @param third third node * @param third third node
*/ */
public TernaryNode(final Source source, final long token, final Node lhs, final Node rhs, final Node third) { public TernaryNode(final Source source, final long token, final Node lhs, final Node rhs, final Node third) {
super(source, token, lhs, rhs); super(source, token, third.getFinish());
this.lhs = lhs;
this.finish = third.getFinish(); this.rhs = rhs;
this.third = third; this.third = third;
} }
private TernaryNode(final TernaryNode ternaryNode, final CopyState cs) { private TernaryNode(final TernaryNode ternaryNode, final Node lhs, final Node rhs, final Node third) {
super(ternaryNode, cs); super(ternaryNode);
this.lhs = lhs;
this.third = cs.existingOrCopy(ternaryNode.third); this.rhs = rhs;
} this.third = 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();
} }
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterTernaryNode(this) != null) { if (visitor.enterTernaryNode(this)) {
final Node newLhs = lhs().accept(visitor); final Node newLhs = lhs().accept(visitor);
final Node newRhs = rhs().accept(visitor); final Node newRhs = rhs().accept(visitor);
final Node newThird = third.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; 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 * Get the "third" node for this ternary expression, i.e. "z" in x ? y : z
* @return a node * @return a node
@ -130,15 +135,39 @@ public class TernaryNode extends BinaryNode {
return third; 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 * Reset the "third" node for this ternary expression, i.e. "z" in x ? y : z
* @param third a node * @param third a node
* @return a node equivalent to this one except for the requested change. * @return a node equivalent to this one except for the requested change.
*/ */
public TernaryNode setThird(final Node third) { public TernaryNode setThird(final Node third) {
if(this.third == third) return this; if (this.third == third) {
final TernaryNode n = (TernaryNode)clone(); return this;
n.third = third; }
return n; return new TernaryNode(this, lhs, rhs, third);
} }
} }

View File

@ -25,20 +25,17 @@
package jdk.nashorn.internal.ir; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for THROW statements. * IR representation for THROW statements.
*/ */
public class ThrowNode extends Node { @Immutable
public final class ThrowNode extends Node {
/** Exception expression. */ /** Exception expression. */
private Node expression; private final Node expression;
/** Try chain. */
@Ignore
private final TryNode tryChain;
/** /**
* Constructor * Constructor
@ -47,26 +44,21 @@ public class ThrowNode extends Node {
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param expression expression to throw * @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); super(source, token, finish);
this.expression = expression; this.expression = expression;
this.tryChain = tryChain;
setIsTerminal(true);
} }
private ThrowNode(final ThrowNode throwNode, final CopyState cs) { private ThrowNode(final Node node, final Node expression) {
super(throwNode); super(node);
this.expression = expression;
this.expression = cs.existingOrCopy(throwNode.expression);
this.tryChain = (TryNode)cs.existingOrSame(throwNode.tryChain);
} }
@Override @Override
protected Node copy(final CopyState cs) { public boolean isTerminal() {
return new ThrowNode(this, cs); return true;
} }
/** /**
@ -75,9 +67,8 @@ public class ThrowNode extends Node {
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterThrowNode(this) != null) { if (visitor.enterThrowNode(this)) {
setExpression(expression.accept(visitor)); return visitor.leaveThrowNode(setExpression(expression.accept(visitor)));
return visitor.leaveThrowNode(this);
} }
return this; return this;
@ -103,16 +94,13 @@ public class ThrowNode extends Node {
/** /**
* Reset the expression being thrown by this node * Reset the expression being thrown by this node
* @param expression new expression * @param expression new expression
* @return new or same thrownode
*/ */
public void setExpression(final Node expression) { public ThrowNode setExpression(final Node expression) {
this.expression = 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.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.codegen.Label; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation of a TRY statement. * IR representation of a TRY statement.
*/ */
public class TryNode extends Node { @Immutable
/** Try chain. */ public final class TryNode extends Node {
@Ignore //don't print, will be apparent from the AST
private TryNode next;
/** Try statements. */ /** Try statements. */
private Block body; private final Block body;
/** List of catch clauses. */ /** List of catch clauses. */
private List<Block> catchBlocks; private final List<Block> catchBlocks;
/** Finally clause. */ /** Finally clause. */
private Block finallyBody; private final Block finallyBody;
/** Exit label. */ /** Exit label. */
private Label exit; private final Label exit;
/** Exception symbol. */ /** Exception symbol. */
private Symbol exception; private Symbol exception;
@ -62,37 +60,46 @@ public class TryNode extends Node {
/** /**
* Constructor * Constructor
* *
* @param source the source * @param source the source
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param next next try node in chain * @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); super(source, token, finish);
this.body = body;
this.next = next; this.catchBlocks = catchBlocks;
this.finallyBody = finallyBody;
this.exit = new Label("exit"); 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); super(tryNode);
this.body = body;
final List<Block> newCatchBlocks = new ArrayList<>(); this.catchBlocks = catchBlocks;
this.finallyBody = finallyBody;
for (final Block block : tryNode.catchBlocks) { this.exit = new Label(tryNode.exit);
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());
} }
@Override @Override
protected Node copy(final CopyState cs) { public Node ensureUniqueLabels(final LexicalContext lc) {
return new TryNode(this, cs); //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 @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterTryNode(this) != null) { if (visitor.enterTryNode(this)) {
// Need to do first for termination analysis. // Need to do finallybody first for termination analysis. TODO still necessary?
if (finallyBody != null) { final Block newFinallyBody = finallyBody == null ? null : (Block)finallyBody.accept(visitor);
finallyBody = (Block)finallyBody.accept(visitor); final Block newBody = (Block)body.accept(visitor);
} return visitor.leaveTryNode(
setBody(newBody).
body = (Block)body.accept(visitor); setFinallyBody(newFinallyBody).
setCatchBlocks(Node.accept(visitor, Block.class, catchBlocks)).
final List<Block> newCatchBlocks = new ArrayList<>(catchBlocks.size()); setException(exception).
for (final Block catchBlock : catchBlocks) { setFinallyCatchAll(finallyCatchAll));
newCatchBlocks.add((Block)catchBlock.accept(visitor));
}
this.catchBlocks = newCatchBlocks;
return visitor.leaveTryNode(this);
} }
return this; return this;
@ -123,7 +125,7 @@ public class TryNode extends Node {
@Override @Override
public void toString(final StringBuilder sb) { 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 * Reset the body of this try block
* @param body new body * @param body new body
* @return new TryNode or same if unchanged
*/ */
public void setBody(final Block body) { public TryNode setBody(final Block body) {
this.body = 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) { for (final Block catchBlock : catchBlocks) {
catches.add((CatchNode)catchBlock.getStatements().get(0)); catches.add((CatchNode)catchBlock.getStatements().get(0));
} }
return catches; return Collections.unmodifiableList(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);
} }
/** /**
@ -174,9 +171,13 @@ public class TryNode extends Node {
/** /**
* Set the catch blocks of this try * Set the catch blocks of this try
* @param catchBlocks list of catch blocks * @param catchBlocks list of catch blocks
* @return new TryNode or same if unchanged
*/ */
public void setCatchBlocks(final List<Block> catchBlocks) { public TryNode setCatchBlocks(final List<Block> catchBlocks) {
this.catchBlocks = 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 * Set the exception symbol for this try block
* @param exception a symbol for the compiler to store the exception in * @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; 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 * If a finally block exists, the synthetic catchall needs another symbol to
* store its throwable * store its throwable
* @param finallyCatchAll a symbol for the finally catch all exception * @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; this.finallyCatchAll = finallyCatchAll;
return this;
} }
/** /**
@ -220,14 +227,6 @@ public class TryNode extends Node {
return exit; 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 * Get the body of the finally clause for this try
* @return finally body, or null if no finally * @return finally body, or null if no finally
@ -239,24 +238,12 @@ public class TryNode extends Node {
/** /**
* Set the finally body of this try * Set the finally body of this try
* @param finallyBody new finally body * @param finallyBody new finally body
* @return new TryNode or same if unchanged
*/ */
public void setFinallyBody(final Block finallyBody) { public TryNode setFinallyBody(final Block finallyBody) {
this.finallyBody = finallyBody; if (this.finallyBody == finallyBody) {
} return this;
}
/** return new TryNode(this, body, catchBlocks, 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;
} }
} }

View File

@ -31,6 +31,7 @@ import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX; import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
import jdk.nashorn.internal.codegen.types.Type; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.parser.TokenType;
@ -39,9 +40,10 @@ import jdk.nashorn.internal.runtime.Source;
/** /**
* UnaryNode nodes represent single operand operations. * 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. */ /** Right hand side argument. */
private Node rhs; private final Node rhs;
/** /**
* Constructor * Constructor
@ -51,23 +53,26 @@ public class UnaryNode extends Node implements Assignment<Node> {
* @param rhs expression * @param rhs expression
*/ */
public UnaryNode(final Source source, final long token, final Node rhs) { public UnaryNode(final Source source, final long token, final Node rhs) {
super(source, token, Token.descPosition(token)); this(source, token, Math.min(rhs.getStart(), Token.descPosition(token)), Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish()), rhs);
this.start = Math.min(rhs.getStart(), Token.descPosition(token));
this.finish = Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish());
this.rhs = rhs;
} }
/** /**
* Copy constructor * Constructor
* * @param source the source
* @param unaryNode source node * @param token token
* @param cs copy state * @param start start
* @param finish finish
* @param rhs expression
*/ */
protected UnaryNode(final UnaryNode unaryNode, final CopyState cs) { public UnaryNode(final Source source, final long token, final int start, final int finish, final Node rhs) {
super(unaryNode); 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(); 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. * Assist in IR navigation.
* @param visitor IR navigating visitor. * @param visitor IR navigating visitor.
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterUnaryNode(this) != null) { if (visitor.enterUnaryNode(this)) {
return visitor.leaveUnaryNode(setRHS(rhs.accept(visitor))); 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. * @return a node equivalent to this one except for the requested change.
*/ */
public UnaryNode setRHS(final Node rhs) { public UnaryNode setRHS(final Node rhs) {
if(this.rhs == rhs) return this; if (this.rhs == rhs) {
final UnaryNode n = (UnaryNode)clone(); return this;
n.rhs = rhs; }
return n; return new UnaryNode(this, rhs);
} }
} }

View File

@ -25,21 +25,31 @@
package jdk.nashorn.internal.ir; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* Node represents a var/let declaration. * 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. */ /** Var name. */
private IdentNode name; private final IdentNode name;
/** Initialization expression. */ /** Initialization expression. */
private Node init; private final Node init;
/** Is this a var statement (as opposed to a "var" in a for loop statement) */ /** 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 * Constructor
@ -51,7 +61,14 @@ public class VarNode extends Node implements Assignment<IdentNode> {
* @param init init node or null if just a declaration * @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) { 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 finish finish
* @param name name of variable * @param name name of variable
* @param init init node or null if just a declaration * @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); super(source, token, finish);
this.name = init == null ? name : name.setIsInitializedHere(); this.name = init == null ? name : name.setIsInitializedHere();
this.init = init; this.init = init;
this.isStatement = isStatement; this.flags = flags;
}
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);
} }
@Override @Override
@ -114,46 +117,18 @@ public class VarNode extends Node implements Assignment<IdentNode> {
return init != null; 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. * Assist in IR navigation.
* @param visitor IR navigating visitor. * @param visitor IR navigating visitor.
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final NodeVisitor visitor) {
if (visitor.enterVarNode(this) != null) { if (visitor.enterVarNode(this)) {
final IdentNode newName = (IdentNode)name.accept(visitor); final IdentNode newName = (IdentNode)name.accept(visitor);
final Node newInit = init == null ? null : init.accept(visitor); final Node newInit = init == null ? null : init.accept(visitor);
final VarNode newThis; final VarNode newThis;
if(name != newName || init != newInit) { if (name != newName || init != newInit) {
newThis = (VarNode)clone(); newThis = new VarNode(this, newName, newInit, flags);
newThis.init = newInit;
newThis.name = newInit == null ? newName : newName.setIsInitializedHere();
} else { } else {
newThis = this; 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. * @return a node equivalent to this one except for the requested change.
*/ */
public VarNode setInit(final Node init) { public VarNode setInit(final Node init) {
if(this.init == init) return this; if (this.init == init) {
final VarNode n = (VarNode)clone(); return this;
n.init = init; }
return n; 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 * Reset the identifier for this VarNode
* @param name new IdentNode representing the variable being set or declared * @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) { public VarNode setName(final IdentNode name) {
if(this.name == name) return this; if (this.name == name) {
final VarNode n = (VarNode)clone(); return this;
n.name = name; }
return n; 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). * @return true if this is a var statement (as opposed to a var initializer in a for loop).
*/ */
public boolean isStatement() { public boolean isStatement() {
return isStatement; return (flags & IS_STATEMENT) != 0;
} }
/** /**

View File

@ -25,7 +25,7 @@
package jdk.nashorn.internal.ir; 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.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; 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 * IR representation for a WHILE statement. This is the superclass of all
* loop nodes * loop nodes
*/ */
public class WhileNode extends BreakableNode { @Immutable
/** Test expression. */ public final class WhileNode extends LoopNode {
protected Node test;
/** For body. */ /** is this a do while node ? */
protected Block body; private final boolean isDoWhile;
/** loop continue label. */
protected Label continueLabel;
/** /**
* Constructor * Constructor
* *
* @param source the source * @param source the source
* @param token token * @param token token
* @param finish finish * @param finish finish
* @param isDoWhile is this a do while loop?
*/ */
public WhileNode(final Source source, final long token, final int finish) { public WhileNode(final Source source, final long token, final int finish, final boolean isDoWhile) {
super(source, token, finish); super(source, token, finish, null, null, false);
this.isDoWhile = isDoWhile;
this.breakLabel = new Label("while_break");
this.continueLabel = new Label("while_continue");
} }
/** /**
* Copy constructor * Internal copy constructor
* *
* @param whileNode source node * @param whileNode while node
* @param cs copy state * @param test test
* @param body body
* @param controlFlowEscapes control flow escapes?
*/ */
protected WhileNode(final WhileNode whileNode, final CopyState cs) { protected WhileNode(final WhileNode whileNode, final Node test, final Block body, final boolean controlFlowEscapes) {
super(whileNode); super(whileNode, test, body, controlFlowEscapes);
this.isDoWhile = whileNode.isDoWhile;
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);
} }
@Override @Override
protected Node copy(final CopyState cs) { public Node ensureUniqueLabels(final LexicalContext lc) {
return new WhileNode(this, cs); return Node.replaceInLexicalContext(lc, this, new WhileNode(this, test, body, controlFlowEscapes));
} }
@Override @Override
public boolean isLoop() { public boolean hasGoto() {
return true; return test == null;
} }
/**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override @Override
public Node accept(final NodeVisitor visitor) { protected Node accept(final LexicalContext lc, final NodeVisitor visitor) {
if (visitor.enterWhileNode(this) != null) { if (visitor.enterWhileNode(this)) {
test = test.accept(visitor); if (isDoWhile()) {
body = (Block)body.accept(visitor); 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; return this;
} }
@Override @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() { public Node getTest() {
return test; 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 * Check if this is a do while loop or a normal while loop
* @param test test expression, null if infinite loop * @return true if do while
*/ */
public void setTest(final Node test) { public boolean isDoWhile() {
this.test = test; 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; package jdk.nashorn.internal.ir;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source; import jdk.nashorn.internal.runtime.Source;
/** /**
* IR representation for {@code with} statements. * IR representation for {@code with} statements.
*/ */
public class WithNode extends Node { @Immutable
public final class WithNode extends LexicalContextNode {
/** This expression. */ /** This expression. */
private Node expression; private final Node expression;
/** Statements. */ /** Statements. */
private Block body; private final Block body;
/** /**
* Constructor * Constructor
@ -44,44 +46,41 @@ public class WithNode extends Node {
* @param source the source * @param source the source
* @param token token * @param token token
* @param finish finish * @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); 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.expression = expression;
this.body = body; 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. * Assist in IR navigation.
* *
* @param visitor IR navigating visitor. * @param visitor IR navigating visitor.
*/ */
@Override @Override
public Node accept(final NodeVisitor visitor) { public Node accept(final LexicalContext lc, final NodeVisitor visitor) {
if (visitor.enterWithNode(this) != null) { if (visitor.enterWithNode(this)) {
expression = expression.accept(visitor); return visitor.leaveWithNode(
body = (Block)body.accept(visitor); setExpression(lc, expression.accept(visitor)).
return visitor.leaveWithNode(this); setBody(lc, (Block)body.accept(visitor)));
} }
return this; return this;
} }
@Override
public boolean isTerminal() {
return body.isTerminal();
}
@Override @Override
public void toString(final StringBuilder sb) { public void toString(final StringBuilder sb) {
sb.append("with ("); sb.append("with (");
@ -99,10 +98,15 @@ public class WithNode extends Node {
/** /**
* Reset the body of this with node * Reset the body of this with node
* @param lc lexical context
* @param body new body * @param body new body
* @return new or same withnode
*/ */
public void setBody(final Block body) { public WithNode setBody(final LexicalContext lc, final Block body) {
this.body = 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 * Reset the expression of this with node
* @param lc lexical context
* @param expression new expression * @param expression new expression
* @return new or same withnode
*/ */
public void setExpression(final Node expression) { public WithNode setExpression(final LexicalContext lc, final Node expression) {
this.expression = 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.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Ignore;
@ -113,6 +115,10 @@ public final class ASTWriter {
type += "#" + node.getSymbol(); type += "#" + node.getSymbol();
} }
if (node instanceof Block && ((Block)node).needsScope()) {
type += " <scope>";
}
final List<Field> children = new LinkedList<>(); final List<Field> children = new LinkedList<>();
if (!isReference) { if (!isReference) {
@ -121,10 +127,6 @@ public final class ASTWriter {
String status = ""; String status = "";
if (node.shouldDiscard()) {
status += " Discard";
}
if (node.isTerminal()) { if (node.isTerminal()) {
status += " Terminal"; 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.CaseNode;
import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode; 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 Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
final JSONWriter jsonWriter = new JSONWriter(includeLoc); final JSONWriter jsonWriter = new JSONWriter(includeLoc);
try { try {
final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.tag()); final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
functionNode.accept(jsonWriter); functionNode.accept(jsonWriter);
return jsonWriter.getString(); return jsonWriter.getString();
} catch (final ParserException e) { } catch (final ParserException e) {
@ -98,11 +97,16 @@ public final class JSONWriter extends NodeVisitor {
} }
@Override @Override
protected Node enterDefault(final Node node) { protected boolean enterDefault(final Node node) {
objectStart(); objectStart();
location(node); location(node);
return node; return true;
}
private boolean leave() {
objectEnd();
return false;
} }
@Override @Override
@ -112,7 +116,7 @@ public final class JSONWriter extends NodeVisitor {
} }
@Override @Override
public Node enterAccessNode(final AccessNode accessNode) { public boolean enterAccessNode(final AccessNode accessNode) {
enterDefault(accessNode); enterDefault(accessNode);
type("MemberExpression"); type("MemberExpression");
@ -128,11 +132,11 @@ public final class JSONWriter extends NodeVisitor {
property("computed", false); property("computed", false);
return leaveDefault(accessNode); return leave();
} }
@Override @Override
public Node enterBlock(final Block block) { public boolean enterBlock(final Block block) {
enterDefault(block); enterDefault(block);
type("BlockStatement"); type("BlockStatement");
@ -140,21 +144,21 @@ public final class JSONWriter extends NodeVisitor {
array("body", block.getStatements()); array("body", block.getStatements());
return leaveDefault(block); return leave();
} }
private static boolean isLogical(final TokenType tt) { private static boolean isLogical(final TokenType tt) {
switch (tt) { switch (tt) {
case AND: case AND:
case OR: case OR:
return true; return true;
default: default:
return false; return false;
} }
} }
@Override @Override
public Node enterBinaryNode(final BinaryNode binaryNode) { public boolean enterBinaryNode(final BinaryNode binaryNode) {
enterDefault(binaryNode); enterDefault(binaryNode);
final String name; final String name;
@ -179,29 +183,29 @@ public final class JSONWriter extends NodeVisitor {
property("right"); property("right");
binaryNode.rhs().accept(this); binaryNode.rhs().accept(this);
return leaveDefault(binaryNode); return leave();
} }
@Override @Override
public Node enterBreakNode(final BreakNode breakNode) { public boolean enterBreakNode(final BreakNode breakNode) {
enterDefault(breakNode); enterDefault(breakNode);
type("BreakStatement"); type("BreakStatement");
comma(); comma();
final LabelNode label = breakNode.getLabel(); final IdentNode label = breakNode.getLabel();
if (label != null) { if (label != null) {
property("label", label.getLabel().getName()); property("label", label.getName());
} else { } else {
property("label"); property("label");
nullValue(); nullValue();
} }
return leaveDefault(breakNode); return leave();
} }
@Override @Override
public Node enterCallNode(final CallNode callNode) { public boolean enterCallNode(final CallNode callNode) {
enterDefault(callNode); enterDefault(callNode);
type("CallExpression"); type("CallExpression");
@ -213,11 +217,11 @@ public final class JSONWriter extends NodeVisitor {
array("arguments", callNode.getArgs()); array("arguments", callNode.getArgs());
return leaveDefault(callNode); return leave();
} }
@Override @Override
public Node enterCaseNode(final CaseNode caseNode) { public boolean enterCaseNode(final CaseNode caseNode) {
enterDefault(caseNode); enterDefault(caseNode);
type("SwitchCase"); type("SwitchCase");
@ -234,11 +238,11 @@ public final class JSONWriter extends NodeVisitor {
array("consequent", caseNode.getBody().getStatements()); array("consequent", caseNode.getBody().getStatements());
return leaveDefault(caseNode); return leave();
} }
@Override @Override
public Node enterCatchNode(final CatchNode catchNode) { public boolean enterCatchNode(final CatchNode catchNode) {
enterDefault(catchNode); enterDefault(catchNode);
type("CatchClause"); type("CatchClause");
@ -260,55 +264,38 @@ public final class JSONWriter extends NodeVisitor {
property("body"); property("body");
catchNode.getBody().accept(this); catchNode.getBody().accept(this);
return leaveDefault(catchNode); return leave();
} }
@Override @Override
public Node enterContinueNode(final ContinueNode continueNode) { public boolean enterContinueNode(final ContinueNode continueNode) {
enterDefault(continueNode); enterDefault(continueNode);
type("ContinueStatement"); type("ContinueStatement");
comma(); comma();
final LabelNode label = continueNode.getLabel(); final IdentNode label = continueNode.getLabel();
if (label != null) { if (label != null) {
property("label", label.getLabel().getName()); property("label", label.getName());
} else { } else {
property("label"); property("label");
nullValue(); nullValue();
} }
return leaveDefault(continueNode); return leave();
} }
@Override @Override
public Node enterDoWhileNode(final DoWhileNode doWhileNode) { public boolean enterEmptyNode(final EmptyNode emptyNode) {
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) {
enterDefault(emptyNode); enterDefault(emptyNode);
type("EmptyStatement"); type("EmptyStatement");
return leaveDefault(emptyNode); return leave();
} }
@Override @Override
public Node enterExecuteNode(final ExecuteNode executeNode) { public boolean enterExecuteNode(final ExecuteNode executeNode) {
enterDefault(executeNode); enterDefault(executeNode);
type("ExpressionStatement"); type("ExpressionStatement");
@ -317,11 +304,11 @@ public final class JSONWriter extends NodeVisitor {
property("expression"); property("expression");
executeNode.getExpression().accept(this); executeNode.getExpression().accept(this);
return leaveDefault(executeNode); return leave();
} }
@Override @Override
public Node enterForNode(final ForNode forNode) { public boolean enterForNode(final ForNode forNode) {
enterDefault(forNode); enterDefault(forNode);
if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) { if (forNode.isForIn() || (forNode.isForEach() && forNode.getInit() != null)) {
@ -380,11 +367,11 @@ public final class JSONWriter extends NodeVisitor {
forNode.getBody().accept(this); forNode.getBody().accept(this);
} }
return leaveDefault(forNode); return leave();
} }
@Override @Override
public Node enterFunctionNode(final FunctionNode functionNode) { public boolean enterFunctionNode(final FunctionNode functionNode) {
enterDefault(functionNode); enterDefault(functionNode);
final boolean program = functionNode.isProgram(); final boolean program = functionNode.isProgram();
@ -419,7 +406,7 @@ public final class JSONWriter extends NodeVisitor {
} }
// body consists of nested functions and statements // 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(); final int size = stats.size();
int idx = 0; int idx = 0;
arrayStart("body"); arrayStart("body");
@ -435,11 +422,11 @@ public final class JSONWriter extends NodeVisitor {
} }
arrayEnd(); arrayEnd();
return leaveDefault(functionNode); return leave();
} }
@Override @Override
public Node enterIdentNode(final IdentNode identNode) { public boolean enterIdentNode(final IdentNode identNode) {
enterDefault(identNode); enterDefault(identNode);
final String name = identNode.getName(); final String name = identNode.getName();
@ -451,11 +438,11 @@ public final class JSONWriter extends NodeVisitor {
property("name", identNode.getName()); property("name", identNode.getName());
} }
return leaveDefault(identNode); return leave();
} }
@Override @Override
public Node enterIfNode(final IfNode ifNode) { public boolean enterIfNode(final IfNode ifNode) {
enterDefault(ifNode); enterDefault(ifNode);
type("IfStatement"); type("IfStatement");
@ -477,11 +464,11 @@ public final class JSONWriter extends NodeVisitor {
nullValue(); nullValue();
} }
return leaveDefault(ifNode); return leave();
} }
@Override @Override
public Node enterIndexNode(final IndexNode indexNode) { public boolean enterIndexNode(final IndexNode indexNode) {
enterDefault(indexNode); enterDefault(indexNode);
type("MemberExpression"); type("MemberExpression");
@ -497,11 +484,11 @@ public final class JSONWriter extends NodeVisitor {
property("computed", true); property("computed", true);
return leaveDefault(indexNode); return leave();
} }
@Override @Override
public Node enterLabelNode(final LabelNode labelNode) { public boolean enterLabelNode(final LabelNode labelNode) {
enterDefault(labelNode); enterDefault(labelNode);
type("LabeledStatement"); type("LabeledStatement");
@ -514,17 +501,17 @@ public final class JSONWriter extends NodeVisitor {
property("body"); property("body");
labelNode.getBody().accept(this); labelNode.getBody().accept(this);
return leaveDefault(labelNode); return leave();
} }
@Override @Override
public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
return null; return false;
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public Node enterLiteralNode(final LiteralNode literalNode) { public boolean enterLiteralNode(final LiteralNode literalNode) {
enterDefault(literalNode); enterDefault(literalNode);
if (literalNode instanceof LiteralNode.ArrayLiteralNode) { if (literalNode instanceof LiteralNode.ArrayLiteralNode) {
@ -556,11 +543,11 @@ public final class JSONWriter extends NodeVisitor {
} }
} }
return leaveDefault(literalNode); return leave();
} }
@Override @Override
public Node enterObjectNode(final ObjectNode objectNode) { public boolean enterObjectNode(final ObjectNode objectNode) {
enterDefault(objectNode); enterDefault(objectNode);
type("ObjectExpression"); type("ObjectExpression");
@ -568,11 +555,11 @@ public final class JSONWriter extends NodeVisitor {
array("properties", objectNode.getElements()); array("properties", objectNode.getElements());
return leaveDefault(objectNode); return leave();
} }
@Override @Override
public Node enterPropertyNode(final PropertyNode propertyNode) { public boolean enterPropertyNode(final PropertyNode propertyNode) {
final Node key = propertyNode.getKey(); final Node key = propertyNode.getKey();
final Node value = propertyNode.getValue(); final Node value = propertyNode.getValue();
@ -634,11 +621,11 @@ public final class JSONWriter extends NodeVisitor {
} }
} }
return null; return false;
} }
@Override @Override
public Node enterReturnNode(final ReturnNode returnNode) { public boolean enterReturnNode(final ReturnNode returnNode) {
enterDefault(returnNode); enterDefault(returnNode);
type("ReturnStatement"); type("ReturnStatement");
@ -652,31 +639,29 @@ public final class JSONWriter extends NodeVisitor {
nullValue(); nullValue();
} }
return leaveDefault(returnNode); return leave();
} }
@Override @Override
public Node enterRuntimeNode(final RuntimeNode runtimeNode) { public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
final RuntimeNode.Request req = runtimeNode.getRequest(); final RuntimeNode.Request req = runtimeNode.getRequest();
if (req == RuntimeNode.Request.DEBUGGER) { if (req == RuntimeNode.Request.DEBUGGER) {
enterDefault(runtimeNode); enterDefault(runtimeNode);
type("DebuggerStatement"); type("DebuggerStatement");
return leave();
return leaveDefault(runtimeNode);
} }
return null; return false;
} }
@Override @Override
public Node enterSplitNode(final SplitNode splitNode) { public boolean enterSplitNode(final SplitNode splitNode) {
return null; return false;
} }
@Override @Override
public Node enterSwitchNode(final SwitchNode switchNode) { public boolean enterSwitchNode(final SwitchNode switchNode) {
enterDefault(switchNode); enterDefault(switchNode);
type("SwitchStatement"); type("SwitchStatement");
@ -688,11 +673,11 @@ public final class JSONWriter extends NodeVisitor {
array("cases", switchNode.getCases()); array("cases", switchNode.getCases());
return leaveDefault(switchNode); return leave();
} }
@Override @Override
public Node enterTernaryNode(final TernaryNode ternaryNode) { public boolean enterTernaryNode(final TernaryNode ternaryNode) {
enterDefault(ternaryNode); enterDefault(ternaryNode);
type("ConditionalExpression"); type("ConditionalExpression");
@ -709,11 +694,11 @@ public final class JSONWriter extends NodeVisitor {
property("alternate"); property("alternate");
ternaryNode.third().accept(this); ternaryNode.third().accept(this);
return leaveDefault(ternaryNode); return leave();
} }
@Override @Override
public Node enterThrowNode(final ThrowNode throwNode) { public boolean enterThrowNode(final ThrowNode throwNode) {
enterDefault(throwNode); enterDefault(throwNode);
type("ThrowStatement"); type("ThrowStatement");
@ -722,11 +707,11 @@ public final class JSONWriter extends NodeVisitor {
property("argument"); property("argument");
throwNode.getExpression().accept(this); throwNode.getExpression().accept(this);
return leaveDefault(throwNode); return leave();
} }
@Override @Override
public Node enterTryNode(final TryNode tryNode) { public boolean enterTryNode(final TryNode tryNode) {
enterDefault(tryNode); enterDefault(tryNode);
type("TryStatement"); type("TryStatement");
@ -747,11 +732,11 @@ public final class JSONWriter extends NodeVisitor {
nullValue(); nullValue();
} }
return leaveDefault(tryNode); return leave();
} }
@Override @Override
public Node enterUnaryNode(final UnaryNode unaryNode) { public boolean enterUnaryNode(final UnaryNode unaryNode) {
enterDefault(unaryNode); enterDefault(unaryNode);
final TokenType tokenType = unaryNode.tokenType(); final TokenType tokenType = unaryNode.tokenType();
@ -769,25 +754,25 @@ public final class JSONWriter extends NodeVisitor {
final boolean prefix; final boolean prefix;
final String operator; final String operator;
switch (tokenType) { switch (tokenType) {
case INCPOSTFIX: case INCPOSTFIX:
prefix = false; prefix = false;
operator = "++"; operator = "++";
break; break;
case DECPOSTFIX: case DECPOSTFIX:
prefix = false; prefix = false;
operator = "--"; operator = "--";
break; break;
case INCPREFIX: case INCPREFIX:
operator = "++"; operator = "++";
prefix = true; prefix = true;
break; break;
case DECPREFIX: case DECPREFIX:
operator = "--"; operator = "--";
prefix = true; prefix = true;
break; break;
default: default:
prefix = false; prefix = false;
operator = tokenType.getName(); operator = tokenType.getName();
} }
type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression"); type(unaryNode.isAssignment()? "UpdateExpression" : "UnaryExpression");
@ -803,11 +788,11 @@ public final class JSONWriter extends NodeVisitor {
unaryNode.rhs().accept(this); unaryNode.rhs().accept(this);
} }
return leaveDefault(unaryNode); return leave();
} }
@Override @Override
public Node enterVarNode(final VarNode varNode) { public boolean enterVarNode(final VarNode varNode) {
enterDefault(varNode); enterDefault(varNode);
type("VariableDeclaration"); type("VariableDeclaration");
@ -839,28 +824,37 @@ public final class JSONWriter extends NodeVisitor {
// declarations // declarations
arrayEnd(); arrayEnd();
return leaveDefault(varNode); return leave();
} }
@Override @Override
public Node enterWhileNode(final WhileNode whileNode) { public boolean enterWhileNode(final WhileNode whileNode) {
enterDefault(whileNode); enterDefault(whileNode);
type("WhileStatement"); type(whileNode.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
comma(); comma();
property("test"); if (whileNode.isDoWhile()) {
whileNode.getTest().accept(this); property("body");
comma(); whileNode.getBody().accept(this);
comma();
property("block"); property("test");
whileNode.getBody().accept(this); whileNode.getTest().accept(this);
} else {
property("test");
whileNode.getTest().accept(this);
comma();
return leaveDefault(whileNode); property("block");
whileNode.getBody().accept(this);
}
return leave();
} }
@Override @Override
public Node enterWithNode(final WithNode withNode) { public boolean enterWithNode(final WithNode withNode) {
enterDefault(withNode); enterDefault(withNode);
type("WithStatement"); type("WithStatement");
@ -873,8 +867,8 @@ public final class JSONWriter extends NodeVisitor {
property("body"); property("body");
withNode.getBody().accept(this); withNode.getBody().accept(this);
return leaveDefault(withNode); return leave();
} }
// Internals below // Internals below

View File

@ -26,30 +26,22 @@
package jdk.nashorn.internal.ir.debug; package jdk.nashorn.internal.ir.debug;
import java.util.List; 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.Block;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode; 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.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.Node; 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.SplitNode;
import jdk.nashorn.internal.ir.SwitchNode; import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode; import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode; import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.WithNode;
@ -136,21 +128,20 @@ public final class PrintVisitor extends NodeVisitor {
/* /*
* Visits. * Visits.
*/ */
@Override @Override
public Node enterAccessNode(final AccessNode accessNode) { public boolean enterDefault(final Node node) {
accessNode.toString(sb); node.toString(sb);
return null; return false;
} }
@Override @Override
public Node enterBlock(final Block block) { public boolean enterBlock(final Block block) {
sb.append(' '); sb.append(' ');
sb.append('{'); sb.append('{');
indent += TABWIDTH; indent += TABWIDTH;
final boolean isFunction = block instanceof FunctionNode;
final List<Node> statements = block.getStatements(); final List<Node> statements = block.getStatements();
boolean lastLineNumber = false; boolean lastLineNumber = false;
@ -161,14 +152,14 @@ public final class PrintVisitor extends NodeVisitor {
indent(); indent();
} }
if (statement instanceof UnaryNode) { statement.accept(this);
statement.toString(sb);
} else {
statement.accept(this);
}
lastLineNumber = statement instanceof LineNumberNode; lastLineNumber = statement instanceof LineNumberNode;
if (statement instanceof FunctionNode) {
continue;
}
final Symbol symbol = statement.getSymbol(); final Symbol symbol = statement.getSymbol();
if (symbol != null) { if (symbol != null) {
@ -200,72 +191,42 @@ public final class PrintVisitor extends NodeVisitor {
indent(); indent();
sb.append("}"); sb.append("}");
if (isFunction) { return false;
sb.append(EOLN);
}
return null;
} }
@Override @Override
public Node enterBreakNode(final BreakNode breakNode) { public boolean enterBinaryNode(final BinaryNode binaryNode) {
breakNode.toString(sb); binaryNode.lhs().accept(this);
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);
sb.append(' '); sb.append(' ');
doWhileNode.toString(sb); sb.append(binaryNode.tokenType());
sb.append(' ');
return null; binaryNode.rhs().accept(this);
return false;
} }
@Override @Override
public Node enterExecuteNode(final ExecuteNode executeNode) { public boolean enterExecuteNode(final ExecuteNode executeNode) {
final Node expression = executeNode.getExpression(); executeNode.getExpression().accept(this);
return false;
if (expression instanceof UnaryNode) {
expression.toString(sb);
} else {
expression.accept(this);
}
return null;
} }
@Override @Override
public Node enterForNode(final ForNode forNode) { public boolean enterForNode(final ForNode forNode) {
forNode.toString(sb); forNode.toString(sb);
forNode.getBody().accept(this); forNode.getBody().accept(this);
return false;
return null;
} }
@Override @Override
public Node enterFunctionNode(final FunctionNode functionNode) { public boolean enterFunctionNode(final FunctionNode functionNode) {
functionNode.toString(sb); functionNode.toString(sb);
enterBlock(functionNode); enterBlock(functionNode.getBody());
sb.append(EOLN);
return null; return false;
} }
@Override @Override
public Node enterIfNode(final IfNode ifNode) { public boolean enterIfNode(final IfNode ifNode) {
ifNode.toString(sb); ifNode.toString(sb);
ifNode.getPass().accept(this); ifNode.getPass().accept(this);
@ -276,55 +237,36 @@ public final class PrintVisitor extends NodeVisitor {
fail.accept(this); fail.accept(this);
} }
return null; return false;
} }
@Override @Override
public Node enterIndexNode(final IndexNode indexNode) { public boolean enterLabelNode(final LabelNode labeledNode) {
indexNode.toString(sb);
return null;
}
@Override
public Node enterLabelNode(final LabelNode labeledNode) {
indent -= TABWIDTH; indent -= TABWIDTH;
indent(); indent();
indent += TABWIDTH; indent += TABWIDTH;
labeledNode.toString(sb); labeledNode.toString(sb);
labeledNode.getBody().accept(this); labeledNode.getBody().accept(this);
return null; return false;
} }
@Override @Override
public Node enterLineNumberNode(final LineNumberNode lineNumberNode) { public boolean enterLineNumberNode(final LineNumberNode lineNumberNode) {
if (printLineNumbers) { if (printLineNumbers) {
lineNumberNode.toString(sb); lineNumberNode.toString(sb);
} }
return null; return false;
}
@Override
public Node enterReturnNode(final ReturnNode returnNode) {
returnNode.toString(sb);
return null;
} }
@Override @Override
public Node enterRuntimeNode(final RuntimeNode runtimeNode) { public boolean enterSplitNode(final SplitNode splitNode) {
runtimeNode.toString(sb);
return null;
}
@Override
public Node enterSplitNode(final SplitNode splitNode) {
splitNode.toString(sb); splitNode.toString(sb);
sb.append(EOLN); sb.append(EOLN);
indent += TABWIDTH; indent += TABWIDTH;
indent(); indent();
return splitNode; return true;
} }
@Override @Override
@ -337,7 +279,7 @@ public final class PrintVisitor extends NodeVisitor {
} }
@Override @Override
public Node enterSwitchNode(final SwitchNode switchNode) { public boolean enterSwitchNode(final SwitchNode switchNode) {
switchNode.toString(sb); switchNode.toString(sb);
sb.append(" {"); sb.append(" {");
@ -357,24 +299,18 @@ public final class PrintVisitor extends NodeVisitor {
indent(); indent();
sb.append("}"); sb.append("}");
return null; return false;
}
@Override
public Node enterThrowNode(final ThrowNode throwNode) {
throwNode.toString(sb);
return null;
} }
@Override @Override
public Node enterTryNode(final TryNode tryNode) { public boolean enterTryNode(final TryNode tryNode) {
tryNode.toString(sb); tryNode.toString(sb);
tryNode.getBody().accept(this); tryNode.getBody().accept(this);
final List<Block> catchBlocks = tryNode.getCatchBlocks(); final List<Block> catchBlocks = tryNode.getCatchBlocks();
for (final Block catchBlock : catchBlocks) { 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.toString(sb);
catchNode.getBody().accept(this); catchNode.getBody().accept(this);
} }
@ -386,35 +322,42 @@ public final class PrintVisitor extends NodeVisitor {
finallyBody.accept(this); finallyBody.accept(this);
} }
return null; return false;
} }
@Override @Override
public Node enterVarNode(final VarNode varNode) { public boolean enterVarNode(final VarNode varNode) {
sb.append("var "); sb.append("var ");
varNode.getName().toString(sb); varNode.getName().toString(sb);
final Node init = varNode.getInit(); final Node init = varNode.getInit();
if(init != null) { if (init != null) {
sb.append(" = "); sb.append(" = ");
init.accept(this); init.accept(this);
} }
return null; return false;
} }
@Override @Override
public Node enterWhileNode(final WhileNode whileNode) { public boolean enterWhileNode(final WhileNode whileNode) {
whileNode.toString(sb); if (whileNode.isDoWhile()) {
whileNode.getBody().accept(this); 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 @Override
public Node enterWithNode(final WithNode withNode) { public boolean enterWithNode(final WithNode withNode) {
withNode.toString(sb); withNode.toString(sb);
withNode.getBody().accept(this); withNode.getBody().accept(this);
return null; return false;
} }
} }

View File

@ -25,9 +25,8 @@
package jdk.nashorn.internal.ir.visitor; 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.BinaryNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.UnaryNode;
@ -45,15 +44,14 @@ public class NodeOperatorVisitor extends NodeVisitor {
/** /**
* Constructor * Constructor
* *
* @param compileUnit compile unit * @param lc a custom lexical context
* @param method method emitter
*/ */
public NodeOperatorVisitor(final CompileUnit compileUnit, final MethodEmitter method) { public NodeOperatorVisitor(final LexicalContext lc) {
super(compileUnit, method); super(lc);
} }
@Override @Override
public final Node enterUnaryNode(final UnaryNode unaryNode) { public final boolean enterUnaryNode(final UnaryNode unaryNode) {
switch (unaryNode.tokenType()) { switch (unaryNode.tokenType()) {
case ADD: case ADD:
return enterADD(unaryNode); return enterADD(unaryNode);
@ -119,7 +117,7 @@ public class NodeOperatorVisitor extends NodeVisitor {
} }
@Override @Override
public final Node enterBinaryNode(final BinaryNode binaryNode) { public final boolean enterBinaryNode(final BinaryNode binaryNode) {
switch (binaryNode.tokenType()) { switch (binaryNode.tokenType()) {
case ADD: case ADD:
return enterADD(binaryNode); 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. * Unary entries and exists.
*/ */
@ -305,9 +292,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a unary + * Unary enter - callback for entering a unary +
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -325,9 +312,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a ~ operator * Unary enter - callback for entering a ~ operator
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -345,9 +332,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a conversion * Unary enter - callback for entering a conversion
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -365,9 +352,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a ++ or -- operator * Unary enter - callback for entering a ++ or -- operator
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -387,7 +374,7 @@ public class NodeOperatorVisitor extends NodeVisitor {
* @param unaryNode the node * @param unaryNode the node
* @return processed node * @return processed node
*/ */
public Node enterDELETE(final UnaryNode unaryNode) { public boolean enterDELETE(final UnaryNode unaryNode) {
return enterDefault(unaryNode); return enterDefault(unaryNode);
} }
@ -405,9 +392,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a discard operator * Unary enter - callback for entering a discard operator
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -425,9 +412,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a new operator * Unary enter - callback for entering a new operator
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -445,9 +432,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a ! operator * Unary enter - callback for entering a ! operator
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -465,9 +452,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a unary - * Unary enter - callback for entering a unary -
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -485,9 +472,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a typeof * Unary enter - callback for entering a typeof
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -505,9 +492,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Unary enter - callback for entering a void * Unary enter - callback for entering a void
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -525,9 +512,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering + operator * Binary enter - callback for entering + operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -545,9 +532,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal &&} operator * Binary enter - callback for entering {@literal &&} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -565,9 +552,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering an assignment * Binary enter - callback for entering an assignment
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -585,9 +572,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering += operator * Binary enter - callback for entering += operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -605,9 +592,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal &=} operator * Binary enter - callback for entering {@literal &=} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -625,9 +612,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering |= operator * Binary enter - callback for entering |= operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -645,9 +632,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering ^= operator * Binary enter - callback for entering ^= operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -665,9 +652,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering /= operator * Binary enter - callback for entering /= operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -685,9 +672,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering %= operator * Binary enter - callback for entering %= operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -705,9 +692,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering *= operator * Binary enter - callback for entering *= operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -725,9 +712,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>=} operator * Binary enter - callback for entering {@literal >>=} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -745,9 +732,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a {@literal <<=} operator * Binary enter - callback for entering a {@literal <<=} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -765,9 +752,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>>=} operator * Binary enter - callback for entering {@literal >>>=} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -785,9 +772,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering -= operator * Binary enter - callback for entering -= operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -805,9 +792,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a bind operator * Binary enter - callback for entering a bind operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -825,9 +812,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal &} operator * Binary enter - callback for entering {@literal &} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -845,9 +832,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering | operator * Binary enter - callback for entering | operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -865,9 +852,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering ^ operator * Binary enter - callback for entering ^ operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -886,9 +873,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* (a, b) where the result is a * (a, b) where the result is a
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -908,9 +895,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* (a, b) where the result is b * (a, b) where the result is b
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -929,9 +916,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a division * Binary enter - callback for entering a division
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -949,9 +936,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering == operator * Binary enter - callback for entering == operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -969,9 +956,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering === operator * Binary enter - callback for entering === operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -989,9 +976,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >=} operator * Binary enter - callback for entering {@literal >=} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1009,9 +996,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >} operator * Binary enter - callback for entering {@literal >} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1029,9 +1016,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering in operator * Binary enter - callback for entering in operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1049,9 +1036,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering instanceof operator * Binary enter - callback for entering instanceof operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1069,9 +1056,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal <=} operator * Binary enter - callback for entering {@literal <=} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1089,9 +1076,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal <} operator * Binary enter - callback for entering {@literal <} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1108,9 +1095,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering % operator * Binary enter - callback for entering % operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1128,9 +1115,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering * operator * Binary enter - callback for entering * operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1148,9 +1135,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering != operator * Binary enter - callback for entering != operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1168,9 +1155,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering a !== operator * Binary enter - callback for entering a !== operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1188,9 +1175,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering || operator * Binary enter - callback for entering || operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1208,9 +1195,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>} operator * Binary enter - callback for entering {@literal >>} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1228,9 +1215,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal <<} operator * Binary enter - callback for entering {@literal <<} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1247,9 +1234,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering {@literal >>>} operator * Binary enter - callback for entering {@literal >>>} operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }
@ -1267,9 +1254,9 @@ public class NodeOperatorVisitor extends NodeVisitor {
* Binary enter - callback for entering - operator * Binary enter - callback for entering - operator
* *
* @param binaryNode the node * @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); return enterDefault(binaryNode);
} }

View File

@ -25,8 +25,6 @@
package jdk.nashorn.internal.ir.visitor; 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.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block; 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.CaseNode;
import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.EmptyNode; import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode; 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.IfNode;
import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LineNumberNode; import jdk.nashorn.internal.ir.LineNumberNode;
import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.Node;
@ -65,41 +63,30 @@ import jdk.nashorn.internal.ir.WithNode;
* Visitor used to navigate the IR. * Visitor used to navigate the IR.
*/ */
public abstract class NodeVisitor { public abstract class NodeVisitor {
/** Current functionNode. */ private final LexicalContext lc;
private FunctionNode currentFunctionNode;
/** Current compile unit used for class generation. */
private CompileUnit compileUnit;
/** /**
* Current method visitor used for method generation. * Constructor
* <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.
*/ */
public NodeVisitor() { public NodeVisitor() {
this(null, null); this(new LexicalContext());
} }
/** /**
* Constructor * Constructor
* *
* @param compileUnit compile unit for this node visitor * @param lc a custom lexical context
* @param method method emitter for this node visitor
*/ */
public NodeVisitor(final CompileUnit compileUnit, final MethodEmitter method) { public NodeVisitor(final LexicalContext lc) {
super(); 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) * @see NodeVisitor#leaveDefault(Node)
* @param node the node to visit * @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) { protected boolean enterDefault(final Node node) {
return node; return true;
} }
/** /**
@ -150,9 +137,9 @@ public abstract class NodeVisitor {
* Callback for entering an AccessNode * Callback for entering an AccessNode
* *
* @param accessNode the node * @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); return enterDefault(accessNode);
} }
@ -170,9 +157,9 @@ public abstract class NodeVisitor {
* Callback for entering a Block * Callback for entering a Block
* *
* @param block the node * @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); return enterDefault(block);
} }
@ -192,7 +179,7 @@ public abstract class NodeVisitor {
* @param binaryNode the node * @param binaryNode the node
* @return processed node * @return processed node
*/ */
public Node enterBinaryNode(final BinaryNode binaryNode) { public boolean enterBinaryNode(final BinaryNode binaryNode) {
return enterDefault(binaryNode); return enterDefault(binaryNode);
} }
@ -210,9 +197,9 @@ public abstract class NodeVisitor {
* Callback for entering a BreakNode * Callback for entering a BreakNode
* *
* @param breakNode the node * @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); return enterDefault(breakNode);
} }
@ -230,9 +217,9 @@ public abstract class NodeVisitor {
* Callback for entering a CallNode * Callback for entering a CallNode
* *
* @param callNode the node * @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); return enterDefault(callNode);
} }
@ -250,9 +237,9 @@ public abstract class NodeVisitor {
* Callback for entering a CaseNode * Callback for entering a CaseNode
* *
* @param caseNode the node * @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); return enterDefault(caseNode);
} }
@ -270,9 +257,9 @@ public abstract class NodeVisitor {
* Callback for entering a CatchNode * Callback for entering a CatchNode
* *
* @param catchNode the node * @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); return enterDefault(catchNode);
} }
@ -290,9 +277,9 @@ public abstract class NodeVisitor {
* Callback for entering a ContinueNode * Callback for entering a ContinueNode
* *
* @param continueNode the node * @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); return enterDefault(continueNode);
} }
@ -306,33 +293,13 @@ public abstract class NodeVisitor {
return leaveDefault(continueNode); 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 * Callback for entering an EmptyNode
* *
* @param emptyNode the node * @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); return enterDefault(emptyNode);
} }
@ -350,9 +317,9 @@ public abstract class NodeVisitor {
* Callback for entering an ExecuteNode * Callback for entering an ExecuteNode
* *
* @param executeNode the node * @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); return enterDefault(executeNode);
} }
@ -370,9 +337,9 @@ public abstract class NodeVisitor {
* Callback for entering a ForNode * Callback for entering a ForNode
* *
* @param forNode the node * @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); return enterDefault(forNode);
} }
@ -390,9 +357,9 @@ public abstract class NodeVisitor {
* Callback for entering a FunctionNode * Callback for entering a FunctionNode
* *
* @param functionNode the node * @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); return enterDefault(functionNode);
} }
@ -410,9 +377,9 @@ public abstract class NodeVisitor {
* Callback for entering an IdentNode * Callback for entering an IdentNode
* *
* @param identNode the node * @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); return enterDefault(identNode);
} }
@ -429,10 +396,10 @@ public abstract class NodeVisitor {
/** /**
* Callback for entering an IfNode * Callback for entering an IfNode
* *
* @param ifNode the node * @param ifNode 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 enterIfNode(final IfNode ifNode) { public boolean enterIfNode(final IfNode ifNode) {
return enterDefault(ifNode); return enterDefault(ifNode);
} }
@ -450,9 +417,9 @@ public abstract class NodeVisitor {
* Callback for entering an IndexNode * Callback for entering an IndexNode
* *
* @param indexNode the node * @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); return enterDefault(indexNode);
} }
@ -470,9 +437,9 @@ public abstract class NodeVisitor {
* Callback for entering a LabelNode * Callback for entering a LabelNode
* *
* @param labelNode the node * @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); return enterDefault(labelNode);
} }
@ -490,9 +457,9 @@ public abstract class NodeVisitor {
* Callback for entering a LineNumberNode * Callback for entering a LineNumberNode
* *
* @param lineNumberNode the node * @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); return enterDefault(lineNumberNode);
} }
@ -510,9 +477,9 @@ public abstract class NodeVisitor {
* Callback for entering a LiteralNode * Callback for entering a LiteralNode
* *
* @param literalNode the node * @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); return enterDefault(literalNode);
} }
@ -530,9 +497,9 @@ public abstract class NodeVisitor {
* Callback for entering an ObjectNode * Callback for entering an ObjectNode
* *
* @param objectNode the node * @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); return enterDefault(objectNode);
} }
@ -550,9 +517,9 @@ public abstract class NodeVisitor {
* Callback for entering a PropertyNode * Callback for entering a PropertyNode
* *
* @param propertyNode the node * @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); return enterDefault(propertyNode);
} }
@ -570,9 +537,9 @@ public abstract class NodeVisitor {
* Callback for entering a ReturnNode * Callback for entering a ReturnNode
* *
* @param returnNode the node * @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); return enterDefault(returnNode);
} }
@ -590,9 +557,9 @@ public abstract class NodeVisitor {
* Callback for entering a RuntimeNode * Callback for entering a RuntimeNode
* *
* @param runtimeNode the node * @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); return enterDefault(runtimeNode);
} }
@ -610,9 +577,9 @@ public abstract class NodeVisitor {
* Callback for entering a SplitNode * Callback for entering a SplitNode
* *
* @param splitNode the node * @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); return enterDefault(splitNode);
} }
@ -630,9 +597,9 @@ public abstract class NodeVisitor {
* Callback for entering a SwitchNode * Callback for entering a SwitchNode
* *
* @param switchNode the node * @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); return enterDefault(switchNode);
} }
@ -650,9 +617,9 @@ public abstract class NodeVisitor {
* Callback for entering a TernaryNode * Callback for entering a TernaryNode
* *
* @param ternaryNode the node * @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); return enterDefault(ternaryNode);
} }
@ -670,9 +637,9 @@ public abstract class NodeVisitor {
* Callback for entering a ThrowNode * Callback for entering a ThrowNode
* *
* @param throwNode the node * @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); return enterDefault(throwNode);
} }
@ -690,9 +657,9 @@ public abstract class NodeVisitor {
* Callback for entering a TryNode * Callback for entering a TryNode
* *
* @param tryNode the node * @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); return enterDefault(tryNode);
} }
@ -710,9 +677,9 @@ public abstract class NodeVisitor {
* Callback for entering a UnaryNode * Callback for entering a UnaryNode
* *
* @param unaryNode the node * @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); return enterDefault(unaryNode);
} }
@ -730,9 +697,9 @@ public abstract class NodeVisitor {
* Callback for entering a VarNode * Callback for entering a VarNode
* *
* @param varNode the node * @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); return enterDefault(varNode);
} }
@ -750,9 +717,9 @@ public abstract class NodeVisitor {
* Callback for entering a WhileNode * Callback for entering a WhileNode
* *
* @param whileNode the node * @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); return enterDefault(whileNode);
} }
@ -770,9 +737,9 @@ public abstract class NodeVisitor {
* Callback for entering a WithNode * Callback for entering a WithNode
* *
* @param withNode the node * @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); return enterDefault(withNode);
} }
@ -786,74 +753,5 @@ public abstract class NodeVisitor {
return leaveDefault(withNode); 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) { static Object traceReturn(final DebugLogger logger, final Object value) {
final String str = "\treturn: " + stripName(value) + " [type=" + (value == null ? "null" : stripName(value.getClass()) + ']'); 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; return value;
} }
@ -173,7 +173,7 @@ public final class MethodHandleFactory {
} }
assert logger != null; assert logger != null;
logger.log(sb.toString(), TRACE_LEVEL); logger.log(TRACE_LEVEL, sb);
stacktrace(logger); stacktrace(logger);
} }
@ -184,7 +184,7 @@ public final class MethodHandleFactory {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos); final PrintStream ps = new PrintStream(baos);
new Throwable().printStackTrace(ps); new Throwable().printStackTrace(ps);
logger.log(baos.toString(), TRACE_LEVEL); logger.log(TRACE_LEVEL, baos.toString());
} }
private static String argString(final Object arg) { private static String argString(final Object arg) {
@ -614,7 +614,7 @@ public final class MethodHandleFactory {
@Override @Override
public SwitchPoint createSwitchPoint() { public SwitchPoint createSwitchPoint() {
final SwitchPoint sp = super.createSwitchPoint(); final SwitchPoint sp = super.createSwitchPoint();
LOG.log("createSwitchPoint " + sp, TRACE_LEVEL); LOG.log(TRACE_LEVEL, "createSwitchPoint ", sp);
return sp; return sp;
} }
@ -627,7 +627,7 @@ public final class MethodHandleFactory {
@Override @Override
public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) { public MethodType type(final Class<?> returnType, final Class<?>... paramTypes) {
final MethodType mt = super.type(returnType, 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; return mt;
} }
} }
@ -638,7 +638,7 @@ public final class MethodHandleFactory {
private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality { private static class TraceCreateMethodHandleFunctionality extends TraceMethodHandleFunctionality {
@Override @Override
public MethodHandle debug(final MethodHandle master, final String str, final Object... args) { 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); stacktrace(LOG);
return master; return master;
} }

View File

@ -851,11 +851,11 @@ public final class NativeDate extends ScriptObject {
} }
final ScriptObject sobj = (ScriptObject)selfObj; final ScriptObject sobj = (ScriptObject)selfObj;
final Object value = sobj.getDefaultValue(Number.class); final Object value = sobj.getDefaultValue(Number.class);
if (value instanceof Number) {
final double num = (value instanceof Number) ? ((Number)value).doubleValue() : NaN; final double num = ((Number)value).doubleValue();
if (isInfinite(num) || isNaN(num)) {
if (isInfinite(num) || isNaN(num)) { return null;
return null; }
} }
try { try {

View File

@ -148,11 +148,7 @@ public final class NativeJSAdapter extends ScriptObject {
if (overrides instanceof ScriptObject) { if (overrides instanceof ScriptObject) {
this.overrides = true; this.overrides = true;
final ScriptObject sobj = (ScriptObject)overrides; final ScriptObject sobj = (ScriptObject)overrides;
final Iterator<String> iter = sobj.propertyIterator(); this.addBoundProperties(sobj);
while (iter.hasNext()) {
final String prop = iter.next();
super.set(prop, sobj.get(prop), false);
}
} else { } else {
this.overrides = false; this.overrides = false;
} }

View File

@ -179,7 +179,6 @@ public final class NativeString extends ScriptObject {
return ((ScriptObject) Global.toObject(self)).get(key); return ((ScriptObject) Global.toObject(self)).get(key);
} }
@SuppressWarnings("unused")
private static Object get(final Object self, final int key) { private static Object get(final Object self, final int key) {
final CharSequence cs = JSType.toCharSequence(self); final CharSequence cs = JSType.toCharSequence(self);
if (key >= 0 && key < cs.length()) { if (key >= 0 && key < cs.length()) {

View File

@ -197,9 +197,10 @@ public abstract class AbstractParser {
* *
* @param message Error message. * @param message Error message.
* @param errorToken Offending token. * @param errorToken Offending token.
* @return ParserException upon failure. Caller should throw and not ignore
*/ */
protected final void error(final String message, final long errorToken) { protected final ParserException error(final String message, final long errorToken) {
error(JSErrorType.SYNTAX_ERROR, message, errorToken); return error(JSErrorType.SYNTAX_ERROR, message, errorToken);
} }
/** /**
@ -208,22 +209,24 @@ public abstract class AbstractParser {
* @param errorType The error type * @param errorType The error type
* @param message Error message. * @param message Error message.
* @param errorToken Offending token. * @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 position = Token.descPosition(errorToken);
final int lineNum = source.getLine(position); final int lineNum = source.getLine(position);
final int columnNum = source.getColumn(position); final int columnNum = source.getColumn(position);
final String formatted = ErrorManager.format(message, source, lineNum, columnNum, errorToken); 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. * Report an error.
* *
* @param message Error message. * @param message Error message.
* @return ParserException upon failure. Caller should throw and not ignore
*/ */
protected final void error(final String message) { protected final ParserException error(final String message) {
error(JSErrorType.SYNTAX_ERROR, message); return error(JSErrorType.SYNTAX_ERROR, message);
} }
/** /**
@ -231,13 +234,14 @@ public abstract class AbstractParser {
* *
* @param errorType The error type * @param errorType The error type
* @param message Error message. * @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. // TODO - column needs to account for tabs.
final int position = Token.descPosition(token); final int position = Token.descPosition(token);
final int column = position - linePosition; final int column = position - linePosition;
final String formatted = ErrorManager.format(message, source, line, column, token); 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);
} }
/** /**
@ -270,7 +274,7 @@ public abstract class AbstractParser {
*/ */
protected final void expect(final TokenType expected) throws ParserException { protected final void expect(final TokenType expected) throws ParserException {
if (type != expected) { if (type != expected) {
error(expectMessage(expected)); throw error(expectMessage(expected));
} }
next(); next();
@ -285,7 +289,7 @@ public abstract class AbstractParser {
*/ */
protected final Object expectValue(final TokenType expected) throws ParserException { protected final Object expectValue(final TokenType expected) throws ParserException {
if (type != expected) { if (type != expected) {
error(expectMessage(expected)); throw error(expectMessage(expected));
} }
final Object value = getValue(); final Object value = getValue();
@ -429,7 +433,7 @@ public abstract class AbstractParser {
try { try {
RegExpFactory.validate(regex.getExpression(), regex.getOptions()); RegExpFactory.validate(regex.getExpression(), regex.getOptions());
} catch (final ParserException e) { } catch (final ParserException e) {
error(e.getMessage()); throw error(e.getMessage());
} }
} }
node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value); node = LiteralNode.newInstance(source, literalToken, finish, (LexerToken)value);

View File

@ -170,8 +170,7 @@ public class JSONParser extends AbstractParser {
} }
case '"': case '"':
case '\\': case '\\':
error(AbstractParser.message("unexpected.token", str)); throw error(AbstractParser.message("unexpected.token", str));
break;
} }
} }
@ -222,14 +221,12 @@ public class JSONParser extends AbstractParser {
return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value)); return new UnaryNode(source, literalToken, LiteralNode.newInstance(source, realToken, finish, (Number)value));
} }
error(AbstractParser.message("expected", "number", type.getNameOrType())); throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
break;
default: default:
break; break;
} }
error(AbstractParser.message("expected", "json literal", type.getNameOrType())); throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
return null;
} }
/** /**
@ -265,7 +262,7 @@ loop:
elements.add(jsonLiteral()); elements.add(jsonLiteral());
// Comma between array elements is mandatory in JSON. // Comma between array elements is mandatory in JSON.
if (type != COMMARIGHT && type != RBRACKET) { if (type != COMMARIGHT && type != RBRACKET) {
error(AbstractParser.message("expected", ", or ]", type.getNameOrType())); throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
} }
break; break;
} }
@ -306,7 +303,7 @@ loop:
// Comma between property assigments is mandatory in JSON. // Comma between property assigments is mandatory in JSON.
if (type != RBRACE && type != COMMARIGHT) { if (type != RBRACE && type != COMMARIGHT) {
error(AbstractParser.message("expected", ", or }", type.getNameOrType())); throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
} }
break; break;
} }
@ -334,13 +331,11 @@ loop:
if (name != null) { if (name != null) {
expect(COLON); expect(COLON);
final Node value = jsonLiteral(); 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. // Raise an error.
error(AbstractParser.message("expected", "string", type.getNameOrType())); throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
return null;
} }
} }

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -56,8 +56,6 @@ import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.parser.Parser; import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.options.Options; 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. * This class manages the global state of execution. Context is immutable.
@ -114,24 +112,9 @@ public final class Context {
* Get the current global scope * Get the current global scope
* @return the current global scope * @return the current global scope
*/ */
@CallerSensitive
public static ScriptObject getGlobal() { public static ScriptObject getGlobal() {
final SecurityManager sm = System.getSecurityManager(); // This class in a package.access protected package.
if (sm != null) { // Trusted code only can call this method.
// 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"));
}
}
return getGlobalTrusted(); return getGlobalTrusted();
} }
@ -399,7 +382,7 @@ public final class Context {
// We need to get strict mode flag from compiled class. This is // We need to get strict mode flag from compiled class. This is
// because eval code may start with "use strict" directive. // because eval code may start with "use strict" directive.
try { try {
strictFlag = clazz.getField(STRICT_MODE.tag()).getBoolean(null); strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
//ignored //ignored
strictFlag = false; strictFlag = false;
@ -713,7 +696,7 @@ public final class Context {
MH.findStatic( MH.findStatic(
MethodHandles.lookup(), MethodHandles.lookup(),
script, script,
RUN_SCRIPT.tag(), RUN_SCRIPT.symbolName(),
MH.type( MH.type(
Object.class, Object.class,
ScriptFunction.class, ScriptFunction.class,
@ -722,13 +705,13 @@ public final class Context {
boolean strict; boolean strict;
try { try {
strict = script.getField(STRICT_MODE.tag()).getBoolean(null); strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
strict = false; strict = false;
} }
// Package as a JavaScript function and pass function back to shell. // 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) { private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
@ -746,13 +729,13 @@ public final class Context {
global = (GlobalObject)Context.getGlobalTrusted(); global = (GlobalObject)Context.getGlobalTrusted();
script = global.findCachedClass(source); script = global.findCachedClass(source);
if (script != null) { 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; return script;
} }
} }
final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse(); final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
if (errors.hasErrors() || env._parse_only) { if (errors.hasErrors()) {
return null; return null;
} }
@ -764,6 +747,10 @@ public final class Context {
getErr().println(new PrintVisitor(functionNode)); getErr().println(new PrintVisitor(functionNode));
} }
if (env._parse_only) {
return null;
}
final URL url = source.getURL(); final URL url = source.getURL();
final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null); 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 * @param str the string to log
*/ */
public void finest(final String str) { 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 * @param str the string to log
*/ */
public void finer(final String str) { 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 * @param str the string to log
*/ */
public void fine(final String str) { 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 * @param str the string to log
*/ */
public void config(final String str) { 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 * @param str the string to log
*/ */
public void info(final String str) { 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 * @param str the string to log
*/ */
public void warning(final String str) { 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 * @param str the string to log
*/ */
public void severe(final String str) { 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 * Output log line on this logger at a given level of verbosity
* @see java.util.logging.Level * @see java.util.logging.Level
* *
* @param str string to log
* @param level minimum log level required for logging to take place * @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) { if (isEnabled) {
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
for (int i = 0 ; i < indent ; i++) { for (int i = 0 ; i < indent ; i++) {
sb.append(' '); sb.append(' ');
} }
@ -210,4 +272,24 @@ public final class DebugLogger {
logger.log(level, sb.toString()); 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());
}
}
} }

View File

@ -104,6 +104,22 @@ public final class FindProperty {
return prototype; return prototype;
} }
/**
* Return the appropriate receiver for a getter.
* @return appropriate receiver
*/
public ScriptObject getGetterReceiver() {
return property != null && property.hasGetterFunction() ? self : prototype;
}
/**
* Return the appropriate receiver for a setter.
* @return appropriate receiver
*/
public ScriptObject getSetterReceiver() {
return property != null && property.hasSetterFunction() ? self : prototype;
}
/** /**
* Return the property that was found * Return the property that was found
* @return property * @return property

View File

@ -170,7 +170,7 @@ public final class NativeJavaPackage extends ScriptObject {
Class<?> javaClass = null; Class<?> javaClass = null;
try { try {
javaClass = context.findClass(fullName); javaClass = context.findClass(fullName);
} catch (final ClassNotFoundException e) { } catch (final NoClassDefFoundError | ClassNotFoundException e) {
//ignored //ignored
} }

View File

@ -25,10 +25,11 @@
package jdk.nashorn.internal.runtime; package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.FunctionSignature; import jdk.nashorn.internal.codegen.FunctionSignature;
@ -37,8 +38,6 @@ import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.parser.TokenType;
import static jdk.nashorn.internal.lookup.Lookup.MH;
/** /**
* This is a subclass that represents a script function that may be regenerated, * This is a subclass that represents a script function that may be regenerated,
* for example with specialization based on call site types, or lazily generated. * for example with specialization based on call site types, or lazily generated.
@ -47,7 +46,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
*/ */
public final class RecompilableScriptFunctionData extends ScriptFunctionData { public final class RecompilableScriptFunctionData extends ScriptFunctionData {
private final FunctionNode functionNode; private FunctionNode functionNode;
private final PropertyMap allocatorMap; private final PropertyMap allocatorMap;
private final CodeInstaller<ScriptEnvironment> installer; private final CodeInstaller<ScriptEnvironment> installer;
private final String allocatorClassName; private final String allocatorClassName;
@ -70,7 +69,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
"" : "" :
functionNode.getIdent().getName(), functionNode.getIdent().getName(),
functionNode.getParameters().size(), functionNode.getParameters().size(),
functionNode.isStrictMode(), functionNode.isStrict(),
false, false,
true); true);
@ -129,7 +128,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
private void ensureHasAllocator() throws ClassNotFoundException { private void ensureHasAllocator() throws ClassNotFoundException {
if (allocator == null && allocatorClassName != null) { if (allocator == null && allocatorClassName != null) {
this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.tag(), MH.type(ScriptObject.class, PropertyMap.class)); this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
} }
} }
@ -148,8 +147,11 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
// therefore, currently method specialization is disabled. TODO // therefore, currently method specialization is disabled. TODO
if (functionNode.isLazy()) { if (functionNode.isLazy()) {
Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '" + functionNode.getName() + "'"); Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
new Compiler(installer, functionNode).compile().install(); final Compiler compiler = new Compiler(installer, functionNode);
functionNode = compiler.compile();
assert !functionNode.isLazy();
compiler.install();
// we don't need to update any flags - varArgs and needsCallee are instrincic // we don't need to update any flags - varArgs and needsCallee are instrincic
// in the function world we need to get a destination node from the compile instead // in the function world we need to get a destination node from the compile instead
@ -159,7 +161,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
// we can't get here unless we have bytecode, either from eager compilation or from // we can't get here unless we have bytecode, either from eager compilation or from
// running a lazy compile on the lines above // running a lazy compile on the lines above
assert functionNode.hasState(CompilationState.INSTALLED); assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
// code exists - look it up and add it into the automatically sorted invoker list // code exists - look it up and add it into the automatically sorted invoker list
code.add( code.add(

View File

@ -901,7 +901,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(int.class); final MethodHandle getter = find.getGetter(int.class);
if (getter != null) { if (getter != null) {
try { try {
return (int)getter.invokeExact((Object)find.getOwner()); return (int)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) { } catch (final Error|RuntimeException e) {
throw e; throw e;
} catch (final Throwable e) { } catch (final Throwable e) {
@ -916,7 +916,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(long.class); final MethodHandle getter = find.getGetter(long.class);
if (getter != null) { if (getter != null) {
try { try {
return (long)getter.invokeExact((Object)find.getOwner()); return (long)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) { } catch (final Error|RuntimeException e) {
throw e; throw e;
} catch (final Throwable e) { } catch (final Throwable e) {
@ -931,7 +931,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(double.class); final MethodHandle getter = find.getGetter(double.class);
if (getter != null) { if (getter != null) {
try { try {
return (double)getter.invokeExact((Object)find.getOwner()); return (double)getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) { } catch (final Error|RuntimeException e) {
throw e; throw e;
} catch (final Throwable e) { } catch (final Throwable e) {
@ -953,7 +953,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
final MethodHandle getter = find.getGetter(Object.class); final MethodHandle getter = find.getGetter(Object.class);
if (getter != null) { if (getter != null) {
try { try {
return getter.invokeExact((Object)find.getOwner()); return getter.invokeExact((Object)find.getGetterReceiver());
} catch (final Error|RuntimeException e) { } catch (final Error|RuntimeException e) {
throw e; throw e;
} catch (final Throwable e) { } catch (final Throwable e) {
@ -1679,12 +1679,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
* @return GuardedInvocation to be invoked at call site. * @return GuardedInvocation to be invoked at call site.
*/ */
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) { protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final String name = desc.getNameToken(2); final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
if (request.isCallSiteUnstable()) {
return findMegaMorphicGetMethod(desc, name);
}
final FindProperty find = findProperty(name, true); final FindProperty find = findProperty(name, true);
MethodHandle methodHandle; MethodHandle methodHandle;
@ -1700,6 +1695,10 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
throw new AssertionError(); // never invoked with any other operation throw new AssertionError(); // never invoked with any other operation
} }
if (request.isCallSiteUnstable()) {
return findMegaMorphicGetMethod(desc, name);
}
final Class<?> returnType = desc.getMethodType().returnType(); final Class<?> returnType = desc.getMethodType().returnType();
final Property property = find.getProperty(); final Property property = find.getProperty();
methodHandle = find.getGetter(returnType); methodHandle = find.getGetter(returnType);
@ -1727,7 +1726,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
} }
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) { private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
final GuardedInvocation inv = findGetIndexMethod(desc.getMethodType().insertParameterTypes(1, Object.class)); final MethodType mhType = desc.getMethodType().insertParameterTypes(1, Object.class);
final GuardedInvocation inv = findGetIndexMethod(mhType);
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
} }
@ -1890,8 +1891,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
} }
private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) { private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
final GuardedInvocation inv = findSetIndexMethod(desc.getMethodType().insertParameterTypes(1, Object.class), final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
NashornCallSiteDescriptor.isStrict(desc)); final GuardedInvocation inv = findSetIndexMethod(type, NashornCallSiteDescriptor.isStrict(desc));
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard()); return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
} }
@ -1949,7 +1950,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
* @return GuardedInvocation to be invoked at call site. * @return GuardedInvocation to be invoked at call site.
*/ */
public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
final String name = desc.getNameToken(2); final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true); final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc); final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
@ -1973,6 +1974,24 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return createEmptyGetter(desc, name); return createEmptyGetter(desc, name);
} }
/**
* Invoke fall back if a property is not found.
* @param name Name of property.
* @return Result from call.
*/
private Object invokeNoSuchProperty(final String name) {
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
if (find != null) {
final Object func = getObjectValue(find);
if (func instanceof ScriptFunction) {
return ScriptRuntime.apply((ScriptFunction)func, this, name);
}
}
return UNDEFINED;
}
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) { private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(getMap())); return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(getMap()));
@ -2239,310 +2258,158 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
setArray(getArray().shrink(newLength)); setArray(getArray().shrink(newLength));
getArray().setLength(newLength); getArray().setLength(newLength);
} }
} }
private int getInt(final int index, final String key) {
for (ScriptObject object = this; object != null; object = object.getProto()) {
final ArrayData array = object.getArray();
if (array.has(index)) {
return array.getInt(index);
}
final FindProperty find = object.findProperty(key, false);
if (find != null) {
return getIntValue(new FindProperty(this, find.getOwner(), find.getProperty()));
}
}
return JSType.toInt32(invokeNoSuchProperty(key));
}
@Override @Override
public int getInt(final Object key) { public int getInt(final Object key) {
final int index = getArrayIndexNoThrow(key); return getInt(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getInt(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getIntValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getInt(key) : 0;
} }
@Override @Override
public int getInt(final double key) { public int getInt(final double key) {
final int index = getArrayIndexNoThrow(key); return getInt(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getInt(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getIntValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getInt(key) : 0;
} }
@Override @Override
public int getInt(final long key) { public int getInt(final long key) {
final int index = getArrayIndexNoThrow(key); return getInt(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getInt(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getIntValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getInt(key) : 0;
} }
@Override @Override
public int getInt(final int key) { public int getInt(final int key) {
final int index = getArrayIndexNoThrow(key); return getInt(getArrayIndexNoThrow(key), convertKey(key));
}
if (getArray().has(index)) { private long getLong(final int index, final String key) {
return getArray().getInt(index); for (ScriptObject object = this; object != null; object = object.getProto()) {
final ArrayData array = object.getArray();
if (array.has(index)) {
return array.getLong(index);
}
final FindProperty find = object.findProperty(key, false);
if (find != null) {
return getLongValue(new FindProperty(this, find.getOwner(), find.getProperty()));
}
} }
final FindProperty find = findProperty(convertKey(key), false); return JSType.toLong(invokeNoSuchProperty(key));
if (find != null) {
return getIntValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getInt(key) : 0;
} }
@Override @Override
public long getLong(final Object key) { public long getLong(final Object key) {
final int index = getArrayIndexNoThrow(key); return getLong(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getLong(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getLongValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getLong(key) : 0L;
} }
@Override @Override
public long getLong(final double key) { public long getLong(final double key) {
final int index = getArrayIndexNoThrow(key); return getLong(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getLong(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getLongValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getLong(key) : 0L;
} }
@Override @Override
public long getLong(final long key) { public long getLong(final long key) {
final int index = getArrayIndexNoThrow(key); return getLong(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getLong(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getLongValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getLong(key) : 0L;
} }
@Override @Override
public long getLong(final int key) { public long getLong(final int key) {
final int index = getArrayIndexNoThrow(key); return getLong(getArrayIndexNoThrow(key), convertKey(key));
}
if (getArray().has(index)) { private double getDouble(final int index, final String key) {
return getArray().getLong(index); for (ScriptObject object = this; object != null; object = object.getProto()) {
final ArrayData array = object.getArray();
if (array.has(index)) {
return array.getDouble(index);
}
final FindProperty find = object.findProperty(key, false);
if (find != null) {
return getDoubleValue(new FindProperty(this, find.getOwner(), find.getProperty()));
}
} }
final FindProperty find = findProperty(convertKey(key), false); return JSType.toNumber(invokeNoSuchProperty(key));
if (find != null) {
return getLongValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getLong(key) : 0L;
} }
@Override @Override
public double getDouble(final Object key) { public double getDouble(final Object key) {
final int index = getArrayIndexNoThrow(key); return getDouble(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getDouble(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getDoubleValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getDouble(key) : Double.NaN;
} }
@Override @Override
public double getDouble(final double key) { public double getDouble(final double key) {
final int index = getArrayIndexNoThrow(key); return getDouble(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getDouble(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getDoubleValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getDouble(key) : Double.NaN;
} }
@Override @Override
public double getDouble(final long key) { public double getDouble(final long key) {
final int index = getArrayIndexNoThrow(key); return getDouble(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getDouble(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getDoubleValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getDouble(key) : Double.NaN;
} }
@Override @Override
public double getDouble(final int key) { public double getDouble(final int key) {
final int index = getArrayIndexNoThrow(key); return getDouble(getArrayIndexNoThrow(key), convertKey(key));
}
if (getArray().has(index)) { private Object get(final int index, final String key) {
return getArray().getDouble(index); for (ScriptObject object = this; object != null; object = object.getProto()) {
final ArrayData array = object.getArray();
if (array.has(index)) {
return array.getObject(index);
}
final FindProperty find = object.findProperty(key, false);
if (find != null) {
return getObjectValue(new FindProperty(this, find.getOwner(), find.getProperty()));
}
} }
final FindProperty find = findProperty(convertKey(key), false); return invokeNoSuchProperty(key);
if (find != null) {
return getDoubleValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.getDouble(key) : Double.NaN;
} }
@Override @Override
public Object get(final Object key) { public Object get(final Object key) {
final int index = getArrayIndexNoThrow(key); return get(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getObject(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getObjectValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.get(key) : UNDEFINED;
} }
@Override @Override
public Object get(final double key) { public Object get(final double key) {
final int index = getArrayIndexNoThrow(key); return get(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getObject(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getObjectValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.get(key) : UNDEFINED;
} }
@Override @Override
public Object get(final long key) { public Object get(final long key) {
final int index = getArrayIndexNoThrow(key); return get(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getObject(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getObjectValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.get(key) : UNDEFINED;
} }
@Override @Override
public Object get(final int key) { public Object get(final int key) {
final int index = getArrayIndexNoThrow(key); return get(getArrayIndexNoThrow(key), convertKey(key));
if (getArray().has(index)) {
return getArray().getObject(index);
}
final FindProperty find = findProperty(convertKey(key), false);
if (find != null) {
return getObjectValue(find);
}
final ScriptObject proto = this.getProto();
return proto != null ? proto.get(key) : UNDEFINED;
} }
/** /**
@ -2613,8 +2480,6 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
f = null; f = null;
} }
MethodHandle setter;
if (f != null) { if (f != null) {
if (!f.getProperty().isWritable()) { if (!f.getProperty().isWritable()) {
if (strict) { if (strict) {
@ -2624,9 +2489,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return; return;
} }
setter = f.getSetter(Object.class, strict); //TODO specfields
try { try {
setter.invokeExact((Object)f.getOwner(), value); final MethodHandle setter = f.getSetter(Object.class, strict); //TODO specfields
setter.invokeExact((Object)f.getSetterReceiver(), value);
} catch (final Error|RuntimeException e) { } catch (final Error|RuntimeException e) {
throw e; throw e;
} catch (final Throwable e) { } catch (final Throwable e) {

View File

@ -47,7 +47,7 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator;
* *
*/ */
final class StructureLoader extends NashornLoader { final class StructureLoader extends NashornLoader {
private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.tag(); private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.symbolName();
private static final String OBJECTS_PACKAGE_EXTERNAL = binaryName(OBJECTS_PACKAGE); private static final String OBJECTS_PACKAGE_EXTERNAL = binaryName(OBJECTS_PACKAGE);
/** /**
@ -110,7 +110,7 @@ final class StructureLoader extends NashornLoader {
@Override @Override
protected Class<?> findClass(final String name) throws ClassNotFoundException { protected Class<?> findClass(final String name) throws ClassNotFoundException {
if (name.startsWith(JS_OBJECT_PREFIX_EXTERNAL)) { if (name.startsWith(JS_OBJECT_PREFIX_EXTERNAL)) {
final int start = name.indexOf(JS_OBJECT_PREFIX.tag()) + JS_OBJECT_PREFIX.tag().length(); final int start = name.indexOf(JS_OBJECT_PREFIX.symbolName()) + JS_OBJECT_PREFIX.symbolName().length();
return generateClass(name, name.substring(start, name.length())); return generateClass(name, name.substring(start, name.length()));
} }
return super.findClass(name); return super.findClass(name);

View File

@ -232,11 +232,18 @@ public final class WithObject extends ScriptObject implements Scope {
return (Scope) proto; return (Scope) proto;
} }
private static GuardedInvocation fixReceiverType(final GuardedInvocation link, final MethodHandle filter) {
// The receiver may be an Object or a ScriptObject.
final MethodType invType = link.getInvocation().type();
final MethodType newInvType = invType.changeParameterType(0, filter.type().returnType());
return link.asType(newInvType);
}
private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) { private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) {
// If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its // If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its
// expression. // expression.
if(!"getMethod".equals(desc.getFirstOperator())) { if(!"getMethod".equals(desc.getFirstOperator())) {
return link.filterArguments(0, WITHEXPRESSIONFILTER); return fixReceiverType(link, WITHEXPRESSIONFILTER).filterArguments(0, WITHEXPRESSIONFILTER);
} }
final MethodHandle linkInvocation = link.getInvocation(); final MethodHandle linkInvocation = link.getInvocation();
@ -252,7 +259,8 @@ public final class WithObject extends ScriptObject implements Scope {
} }
private static GuardedInvocation fixScopeCallSite(final GuardedInvocation link) { private static GuardedInvocation fixScopeCallSite(final GuardedInvocation link) {
return link.replaceMethods(filter(link.getInvocation(), WITHSCOPEFILTER), filterGuard(link, WITHSCOPEFILTER)); final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER), filterGuard(newLink, WITHSCOPEFILTER));
} }
private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) { private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) {

View File

@ -287,7 +287,10 @@ class Parser extends Lexer {
if (syntax.allowDoubleRangeOpInCC()) { if (syntax.allowDoubleRangeOpInCC()) {
env.ccEscWarn("-"); env.ccEscWarn("-");
parseCharClassSbChar(cc, arg); // goto sb_char /* [0-9-a] is allowed as [0-9\-a] */ arg.inType = CCVALTYPE.SB;
arg.v = '-';
arg.vIsRaw = false;
parseCharClassValEntry2(cc, arg); // goto val_entry2 /* [0-9-a] is allowed as [0-9\-a] */
break; break;
} }
newSyntaxException(ERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS); newSyntaxException(ERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS);

View File

@ -42,6 +42,8 @@ import java.util.ResourceBundle;
import jdk.nashorn.api.scripting.NashornException; import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.ir.FunctionNode; 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.parser.Parser;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager; import jdk.nashorn.internal.runtime.ErrorManager;
@ -254,6 +256,14 @@ public class Shell {
return COMPILATION_ERROR; return COMPILATION_ERROR;
} }
if (env._print_ast) {
context.getErr().println(new ASTWriter(functionNode));
}
if (env._print_parse) {
context.getErr().println(new PrintVisitor(functionNode));
}
//null - pass no code installer - this is compile only //null - pass no code installer - this is compile only
new Compiler(env, functionNode).compile(); new Compiler(env, functionNode).compile();
} }

View File

@ -0,0 +1,42 @@
/*
* 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.
*
* 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.
*/
/**
* JDK-8011578 : -Dnashorn.unstable.relink.threshold=1 causes tests to fail.
*
* @test
* @option -Dnashorn.unstable.relink.threshold=1
* @run
*/
load(__DIR__ + "NASHORN-296.js");
load(__DIR__ + "NASHORN-691.js");
load(__DIR__ + "calllink.js");
load(__DIR__ + "nosuchproperty.js");
__noSuchProperty__ = function(x) {
print(x);
return x;
}
print(this["find"]);

View File

@ -0,0 +1,22 @@
o.foo = 33
o.foo = 44
o.foo = 3
o.foo = hello
obj1.func called
obj2.func called
no such method: func
obj4's prototype func called
MyConstructor.prototype.func
MyConstructor.prototype.func
obj1.func called
obj2.func called
new obj3.func called
new obj4.func called
all new MyConstructor.prototype.func
all new MyConstructor.prototype.func
obj.__noSuchProperty__ for foo
new obj.__noSuchProperty__ for foo
proto.__noSuchProperty__ for foo
new proto.__noSuchProperty__ for foo
find
find

View File

@ -0,0 +1,59 @@
/*
* 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.
*
* 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.
*/
/**
* JDK-8012460: RegExp regression
*
* @test
* @run
*/
var semver = "\\s*[v=]*\\s*([0-9]+)" // major
+ "\\.([0-9]+)" // minor
+ "\\.([0-9]+)" // patch
+ "(-[0-9]+-?)?" // build
+ "([a-zA-Z-+][a-zA-Z0-9-\.:]*)?" // tag
, exprComparator = "^((<|>)?=?)\s*("+semver+")$|^$";
var validComparator = new RegExp("^"+exprComparator+"$");
print(exprComparator);
print(">=0.6.0-".match(validComparator));
print("=0.6.0-".match(validComparator));
print("0.6.0-".match(validComparator));
print("<=0.6.0-".match(validComparator));
print(">=0.6.0-a:b-c.d".match(validComparator));
print("=0.6.0-a:b-c.d".match(validComparator));
print("0.6.0+a:b-c.d".match(validComparator));
print("<=0.6.0+a:b-c.d".match(validComparator));
print(/[a-zA-Z-+]/.exec("a"));
print(/[a-zA-Z-+]/.exec("b"));
print(/[a-zA-Z-+]/.exec("y"));
print(/[a-zA-Z-+]/.exec("z"));
print(/[a-zA-Z-+]/.exec("B"));
print(/[a-zA-Z-+]/.exec("Y"));
print(/[a-zA-Z-+]/.exec("Z"));
print(/[a-zA-Z-+]/.exec("-"));
print(/[a-zA-Z-+]/.exec("+"));

View File

@ -0,0 +1,18 @@
^((<|>)?=?)s*(\s*[v=]*\s*([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9]+-?)?([a-zA-Z-+][a-zA-Z0-9-.:]*)?)$|^$
>=0.6.0-,>=,>,0.6.0-,0,6,0,,-
=0.6.0-,=,,0.6.0-,0,6,0,,-
0.6.0-,,,0.6.0-,0,6,0,,-
<=0.6.0-,<=,<,0.6.0-,0,6,0,,-
>=0.6.0-a:b-c.d,>=,>,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
=0.6.0-a:b-c.d,=,,0.6.0-a:b-c.d,0,6,0,,-a:b-c.d
0.6.0+a:b-c.d,,,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
<=0.6.0+a:b-c.d,<=,<,0.6.0+a:b-c.d,0,6,0,,+a:b-c.d
a
b
y
z
B
Y
Z
-
+

View File

@ -0,0 +1,47 @@
/*
* 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.
*
* 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.
*/
/**
* JDK-8012462: Date.prototype.toJSON does not handle non-Date 'this' as per the spec.
*
* @test
* @run
*/
var toJSON = Date.prototype.toJSON;
function checkJSON(value, expected) {
var res = toJSON.call({
valueOf: function() { return value; },
toISOString: function() { return value; }
});
if (res !== expected) {
fail("Date.prototype.toJSON does not work for non-Date 'this'");
}
}
checkJSON(NaN, null);
checkJSON(-Infinity, null);
checkJSON(Infinity, null);
checkJSON("foo", "foo");

View File

@ -0,0 +1,49 @@
/*
* 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.
*
* 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.
*/
/**
* Try throw test - nest finally
*
* @test
* @run
*/
function f() {
print("a");
try {
print("b");
} finally {
print("c");
try {
print("d");
} finally {
print("e");
}
print("f");
}
print("g");
}
f();
print("done");

View File

@ -0,0 +1,8 @@
a
b
c
d
e
f
g
done