8007062: Split Lower up into Lower/Attr/FinalizeTypes. Integrate AccessSpecalizer into FinalizeTypes

Lower suffered from being a "God class" trying to do everything at once.  As Nashorn code generation has grown, so has Lower. It does several post processing passes, tries to do several things at once even though all type information isn't in place, adjusting state afterwards and so on. It also performs control flow analysis, type attribution and constant folding, and  everything else code generation related before byte code emission. I have now separated the compilation process into Lower (create low level nodes from high level ones, copy code such as finally block inlining etc), Attr (assign types and symbols to all nodes - freeze slot and scope information) and FinalizeTypes (insert explicit casts, specialize invoke dynamic types for scope accesses). I've removed the kludgy AccessSpecializer, as this now integrates naturally with typing. Everything is now much easier to read and each module performs only one thing. I have added separate loggers for the separate tiers. In the process I have also fixed: (1) problems with type coercion (see test/script/basic/typecoercion.js, basically our coercion was too late and our symbol inference was erroneous. This only manifested itself in very rare occasions where toNumber coercion has side effects, such as for example when valueOf is overridden)  (2) copying literal nodes (literal copy did not use the superclass copy, which made all the Node specific fields not to be copied  (3) erroneous literal tokenization (literals shouldn't always just inherit token information from whatever node that creates them) (4) splitter weighnodes - unary nodes were considered weightless  (4) removed the hateful and kludgy "VarNode.shouldAppend", which really isn't needed when we have an attribution phase that determines self reference symbols (the only thing it was used for) (5) duplicate line number issues in the parser (6) convert bug in CodeGenerator for intermediate results of scope accesses (see test/script/basic/access-specializer.js) ... Several of these things just stopped being problems with the new architecture "can't happen anymore" and are not bug fixes per se. All tests run. No performance regressions exist that I've been able to measure. Some increases in performance were measured, but in the statistical margin of error (which is very wide as HotSpot currently has warmup issues with LambdaForms/invoke dynamic). Compile speed has not measurably increased.

Reviewed-by: jlaskey, attila
This commit is contained in:
Marcus Lagergren 2013-01-30 12:26:45 +01:00
parent b342e3c536
commit 30b950d2d2
49 changed files with 4525 additions and 3465 deletions

View File

@ -36,16 +36,6 @@ See the description of the access logger below. This flag is
equivalent to enabling the access logger with "info" level.
SYSTEM PROPERTY: -Dnashorn.compiler.ints.disable
This flag prevents ints and longs (non double values) from being used
for any primitive representation in the lowered IR. This is default
false, i.e Lower will attempt to use integer variables as long as it
can. For example, var x = 17 would try to use x as an integer, unless
other operations occur later that require coercion to wider type, for
example x *= 17.1;
SYSTEM PROPERTY: -Dnashorn.compiler.intarithmetic
Arithmetic operations in Nashorn (except bitwise ones) typically
@ -195,7 +185,8 @@ We still have to deal with objects vs primitives for local bytecode
slots, possibly through code copying and versioning.
SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=<x>
SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=[<x>[,*]],
-Dnashorn.compiler.symbol.stacktrace=[<x>[,*]]
When this property is set, creation and manipulation of any symbol
named "x" will show information about when the compiler changes its
@ -203,8 +194,16 @@ type assumption, bytecode local variable slot assignment and other
data. This is useful if, for example, a symbol shows up as an Object,
when you believe it should be a primitive. Usually there is an
explanation for this, for example that it exists in the global scope
and type analysis has to be more conservative. In that case, the stack
trace upon type change to object will usually tell us why.
and type analysis has to be more conservative.
Several symbols names to watch can be specified by comma separation.
If no variable name is specified (and no equals sign), all symbols
will be watched
By using "stacktrace" instead of or together with "trace", stack
traces will be displayed upon symbol changes according to the same
semantics.
SYSTEM PROPERTY: nashorn.lexer.xmlliterals
@ -349,6 +348,9 @@ this system property.
2. The loggers.
===============
It is very simple to create your own logger. Use the DebugLogger class
and give the subsystem name as a constructor argument.
The Nashorn loggers can be used to print per-module or per-subsystem
debug information with different levels of verbosity. The loggers for
a given subsystem are available are enabled by using
@ -382,7 +384,7 @@ into byte code, and installs the classes into a class loader
controlled from the Context. Log messages are, for example, about
things like new compile units being allocated. The compiler has global
settings that all the tiers of codegen (e.g. Lower and CodeGenerator)
use.
use.s
* codegen
@ -429,6 +431,18 @@ The --log=codegen option is equivalent to setting the system variable
* lower
This is the first lowering pass.
Lower is a code generation pass that turns high level IR nodes into
lower level one, for example substituting comparisons to RuntimeNodes
and inlining finally blocks.
Lower is also responsible for determining control flow information
like end points.
* attr
The lowering annotates a FunctionNode with symbols for each identifier
and transforms high level constructs into lower level ones, that the
CodeGenerator consumes.
@ -439,14 +453,11 @@ specialization information. Currently very little info is generated by
this logger. This will probably change.
* access
* finalize
The --log=access option is equivalent to setting the system variable
"nashorn.callsiteaccess.debug" to true. There are several levels of
the access logger, usually the default level "info" is enough
It is very simple to create your own logger. Use the DebugLogger class
and give the subsystem name as a constructor argument.
This --log=finalize log option outputs information for type finalization,
the third tier of the compiler. This means things like placement of
specialized scope nodes or explicit conversions.
* fields

View File

@ -48,6 +48,9 @@ import java.util.regex.Pattern;
*/
public final class Formatter {
private Formatter() {
}
/**
* Method which converts javascript types to java types for the
* String.format method (jrunscript function sprintf).
@ -149,7 +152,9 @@ public final class Formatter {
if (s != null) {
try {
index = Integer.parseInt(s.substring(0, s.length() - 1));
} catch (NumberFormatException e) { }
} catch (final NumberFormatException e) {
//ignored
}
} else {
index = 0;
}

View File

@ -327,6 +327,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
try {
final InputStream is = AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
@Override
public InputStream run() throws Exception {
final URL url = NashornScriptEngine.class.getResource(script);
return url.openStream();

View File

@ -1,399 +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.HashSet;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReferenceNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TypeOverride;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
/**
* This is a post pass for Lower, that removes casts for accessors to objects in
* Scope, and replaces the accessor type with a narrower one with possible.
*
* Any node that implements TypeOverride will be subject to access specialization.
*
* TypeOverride basically means "type inference has determined that the field x
* is an object, but if you do x & 17, it can be read as an int, which we hope
* coincides with its internal representation. In that case there is no boxing
* that may or may not be removed by the JVM and less data bandwidth.
*
* Ideally this should not be a post pass, but it requires slot AND scope info, and has
* to be run where it is, which is called from {@link CodeGenerator}.
*
* @see TypeOverride
*/
final class AccessSpecializer extends NodeOperatorVisitor {
/** Debug logger for access specialization. Enable it with --log=access:level
or -Dnashorn.callsiteaccess.debug */
private static final DebugLogger LOG = new DebugLogger("access", "nashorn.callsiteaccess.debug");
private static final boolean DEBUG = LOG.isEnabled();
@Override
public Node leave(final VarNode varNode) {
if (varNode.isAssignment()) {
return leaveAssign(varNode);
}
return varNode;
}
@Override
public Node leave(final CallNode callNode) {
final Node function = callNode.getFunction();
if (function instanceof ReferenceNode) {
changeType(callNode, ((ReferenceNode)function).getReference().getType());
}
return callNode;
}
@Override
public Node leaveASSIGN(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
return propagateResultType(binaryNode, binaryNode.lhs().getType());
}
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
return propagateResultType(binaryNode, binaryNode.rhs().getType());
}
@Override
public Node leaveCONVERT(final UnaryNode unaryNode) {
final Type castTo = unaryNode.getType();
Node rhs = unaryNode.rhs();
// Go through all conversions until we find the first non-convert node.
while (rhs.tokenType() == TokenType.CONVERT) {
rhs = ((UnaryNode)rhs).rhs();
}
// If this node can be type changed
if (canHaveCallSiteType(rhs) && isSupportedCallSiteType(castTo)) {
/*
* Just add a callsite type and throw away the cast, appropriate
* getter/setter is selected, which will do the conversion for us
*/
changeType(rhs, castTo);
LOG.fine("*** cast: converting " + debugNode(unaryNode) + " to " + debugNode(rhs));
return rhs;
}
// Micro optimization for node hygiene and pattern detection - remove unnecessary same type cast
if (unaryNode.getType().isEquivalentTo(rhs.getType())) {
return rhs;
}
return unaryNode;
}
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
assert unaryNode.isAssignment();
final Node dest = unaryNode.getAssignmentDest();
if (canHaveCallSiteType(dest) && isSupportedCallSiteType(unaryNode.getType())) {
changeTypeInAssignment(dest, unaryNode.getType());
}
return unaryNode;
}
/**
* Is this a node that can have its type overridden. This is true for
* AccessNodes, IndexNodes and IdentNodes
*
* @param node the node to check
* @return true if node can have a callsite type
*/
private static boolean canHaveCallSiteType(final Node node) {
return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType();
}
/**
* Is the specialization type supported. Currently we treat booleans as objects
* and have no special boolean type accessor, thus booleans are ignored.
* TODO - support booleans? NASHORN-590
*
* @param castTo the type to check
* @return true if call site type is supported
*/
private static boolean isSupportedCallSiteType(final Type castTo) {
return castTo.isNumeric(); // don't specializable for boolean
}
/**
* Turn a node into a covert node
*
* @param node the node
* @return the node as a convert node
*/
private static Node convert(final Node node) {
return new UnaryNode(node.getSource(),
Token.recast(node.getToken(), TokenType.CONVERT),
node);
}
private static Node leaveAssign(final Node node) {
assert node.isAssignment() : node + " is not an assignment";
final Node lhs = ((Assignment<?>)node).getAssignmentDest();
Node rhs = ((Assignment<?>)node).getAssignmentSource();
/**
* Nodes with local variable slots are assumed to be of their optimal type
* already and aren't affected here. This is not strictly true, for instance
* with doubles instead of in a bounded loop. TODO - range check: NASHORN-363
*
* This is also not strictly true for var y = x = 55; where y has no other uses
* Then y can be an int, but lower conservatively does an object of the assign to
* scope
*/
final Symbol lhsSymbol = lhs.getSymbol();
if (lhsSymbol.hasSlot() && !lhsSymbol.isScope()) {
LOG.finest(lhs.getSymbol() + " has slot!");
if (!lhs.getType().isEquivalentTo(rhs.getType())) {
LOG.finest("\tslot assignment: " +lhs.getType()+ " " +rhs.getType() + " " + debugNode(node));
final Node c = convert(rhs);
c.setSymbol(lhsSymbol);
((Assignment<?>)node).setAssignmentSource(c);
LOG.fine("*** slot assignment turned to : " + debugNode(node));
} else {
LOG.finest("aborted - type equivalence between lhs and rhs");
}
return node;
}
// e.g. __DIR__, __LINE__, __FILE__ - don't try to change these
if (lhs instanceof IdentNode && ((IdentNode)lhs).isSpecialIdentity()) {
return node;
}
/**
* Try to cast to the type of the right hand side, now pruned. E.g. an (object)17 should
* now be just 17, an int.
*/
Type castTo = rhs.getType();
// If LHS can't get a new type, neither can rhs - retain the convert
if (!canHaveCallSiteType(lhs)) {
return node;
}
// Take the narrowest type of the entire cast sequence
while (rhs.tokenType() == TokenType.CONVERT) {
rhs = ((UnaryNode)rhs).rhs();
castTo = Type.narrowest(rhs.getType(), castTo); //e.g. (object)(int) -> int even though object is outermost
}
// If castTo is wider than widestOperationType, castTo can be further slowed down
final Type widestOperationType = node.getWidestOperationType();
LOG.finest("node wants to be " + castTo + " and its widest operation is " + widestOperationType);
if (widestOperationType != castTo && Type.widest(castTo, widestOperationType) == castTo) {
LOG.info("###" + node + " castTo was " + castTo + " but could be downgraded to " + node.getWidestOperationType());
castTo = node.getWidestOperationType();
if (rhs instanceof TypeOverride) {
changeType(rhs, castTo);
}
}
/*
* If this is a self modifying op, we can't be narrower than the widest optype
* or e.g. x = x + 12 and x += 12 will turn into different things
*/
if (node.isSelfModifying()) {
castTo = Type.widest(widestOperationType, castTo);
}
// We only specialize for numerics, not for booleans.
if (isSupportedCallSiteType(castTo)) {
if (rhs.getType() != castTo) {
LOG.finest("cast was necessary, abort: " + node + " " + rhs.getType() + " != " + castTo);
return node;
}
LOG.finest("assign: " + debugNode(node));
changeTypeInAssignment(lhs, castTo);
((Assignment<?>)node).setAssignmentSource(rhs);
LOG.info("### modified to " + debugNode(node) + " (given type override " + castTo + ")");
propagateResultType(node, castTo);
}
return node;
}
private static Node propagateResultType(final Node node, final Type type) {
//warning! this CANNOT be done for non temporaries as they are used in other computations
if (isSupportedCallSiteType(type)) {
if (node.getSymbol().isTemp()) {
LOG.finest("changing temporary type: " + debugNode(node) + " to " + type);
node.getSymbol().setTypeOverride(type);
LOG.info("### node modified to " + debugNode(node) + " (given type override " + type + ")");
}
}
return node;
}
private static void changeTypeInAssignment(final Node dest, final Type newType) {
if (changeType(dest, newType)) {
LOG.finest("changed assignment " + dest + " " + dest.getSymbol());
assert !newType.isObject();
final HashSet<Node> exclude = new HashSet<>();
dest.accept(new NodeVisitor() {
private void setCanBePrimitive(final Symbol symbol) {
LOG.fine("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
symbol.setCanBePrimitive(newType);
}
@Override
public Node enter(final IdentNode identNode) {
if (!exclude.contains(identNode)) {
setCanBePrimitive(identNode.getSymbol());
}
return null;
}
@Override
public Node enter(final AccessNode accessNode) {
setCanBePrimitive(accessNode.getProperty().getSymbol());
return null;
}
@Override
public Node enter(final IndexNode indexNode) {
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
return indexNode;
}
});
}
}
private static boolean changeType(final Node node, final Type newType) {
if (!node.getType().equals(newType)) {
((TypeOverride)node).setType(newType);
return true;
}
return false;
}
private static String debugNode(final Node node) {
if (DEBUG) {
return node.toString();
}
return "";
}
}

File diff suppressed because it is too large Load Diff

View File

@ -56,7 +56,6 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@ -524,28 +523,6 @@ public final class CodeGenerator extends NodeOperatorVisitor {
return method;
}
/**
* Create a new function object, including generating its stub code.
*
* @param functionNode FunctionNode to utilize.
*/
private void newFunctionObject(final FunctionNode functionNode) {
// Turn thisProperties into keys and symbols for the FunctionAnalyzer
final Map<String, Node> thisProperties = functionNode.getThisProperties();
final List<String> keys = new ArrayList<>();
final List<Symbol> symbols = new ArrayList<>();
/* TODO - Predefine known properties.
for (final Entry<String, Node> entry : thisProperties.entrySet()) {
keys.add(entry.getKey());
symbols.add(entry.getValue().getSymbol());
}
*/
new FunctionObjectCreator(this, functionNode, keys, symbols).makeObject(method);
}
@Override
public Node enter(final CallNode callNode) {
if (callNode.testResolved()) {
@ -603,12 +580,12 @@ public final class CodeGenerator extends NodeOperatorVisitor {
final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
// load evaluated code
load(evalArgs.code);
load(evalArgs.getCode());
method.convert(Type.OBJECT);
// special/extra 'eval' arguments
load(evalArgs.evalThis);
method.load(evalArgs.location);
method.load(evalArgs.strictMode);
load(evalArgs.getThis());
method.load(evalArgs.getLocation());
method.load(evalArgs.getStrictMode());
method.convert(Type.OBJECT);
// direct call to Global.directEval
@ -683,7 +660,7 @@ public final class CodeGenerator extends NodeOperatorVisitor {
}
if (callee.needsCallee()) { // TODO: always true
newFunctionObject(callee); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead.
new FunctionObjectCreator(CodeGenerator.this, callee).makeObject(method); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead.
}
loadArgs(args, signature, isVarArg, argCount);
@ -1300,6 +1277,7 @@ public final class CodeGenerator extends NodeOperatorVisitor {
@SuppressWarnings("rawtypes")
@Override
public Node enter(final LiteralNode literalNode) {
assert literalNode.getSymbol() != null : literalNode + " has no symbol";
load(literalNode).store(literalNode.getSymbol());
return null;
}
@ -1410,7 +1388,7 @@ public final class CodeGenerator extends NodeOperatorVisitor {
return null;
}
newFunctionObject(referenceNode.getReference());
new FunctionObjectCreator(this, referenceNode.getReference()).makeObject(method);
return null;
}
@ -1561,15 +1539,12 @@ public final class CodeGenerator extends NodeOperatorVisitor {
/*
* First check if this should be something other than a runtime node
* AccessSpecializer might have changed the type
*
* TODO - remove this - Access Specializer will always know after Attr/Lower
*/
if (runtimeNode.isPrimitive()) {
if (runtimeNode.isPrimitive() && !runtimeNode.isFinal()) {
final Node lhs = runtimeNode.getArgs().get(0);
Node rhs = null;
if (runtimeNode.getArgs().size() > 1) {
rhs = runtimeNode.getArgs().get(1);
}
final Node rhs = runtimeNode.getArgs().size() > 1 ? runtimeNode.getArgs().get(1) : null;
final Type type = runtimeNode.getType();
final Symbol symbol = runtimeNode.getSymbol();
@ -1590,7 +1565,15 @@ public final class CodeGenerator extends NodeOperatorVisitor {
case GT:
return enterCmp(lhs, rhs, Condition.GT, type, symbol);
case ADD:
return enterNumericAdd(lhs, rhs, type, symbol);
Type widest = Type.widest(lhs.getType(), rhs.getType());
load(lhs);
method.convert(widest);
load(rhs);
method.convert(widest);
method.add();
method.convert(type);
method.store(symbol);
return null;
default:
// it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
// assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
@ -1605,7 +1588,7 @@ public final class CodeGenerator extends NodeOperatorVisitor {
return null;
}
if (specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
return null;
}
@ -2029,7 +2012,6 @@ public final class CodeGenerator extends NodeOperatorVisitor {
if (needsScope) {
method.loadScope();
}
load(init);
if (needsScope) {
@ -2324,12 +2306,11 @@ public final class CodeGenerator extends NodeOperatorVisitor {
}
private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type);
assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs);
load(lhs);
load(rhs);
method.add();
method.store(symbol);
return null;
}
@ -3152,17 +3133,19 @@ public final class CodeGenerator extends NodeOperatorVisitor {
/**
* Take the original target args from the stack and use them
* together with the value to be stored to emit the store code
*
* The case that targetSymbol is in scope (!hasSlot) and we actually
* need to do a conversion on non-equivalent types exists, but is
* very rare. See for example test/script/basic/access-specializer.js
*/
if (targetSymbol.hasSlot()) {
method.convert(target.getType());
}
method.convert(target.getType());
target.accept(new NodeVisitor(compileUnit, method) {
@Override
public Node enter(final IdentNode node) {
final Symbol symbol = target.getSymbol();
if (symbol.isScope()) {
if(symbol.isFastScope(currentFunction)) {
if (symbol.isFastScope(currentFunction)) {
storeFastScopeVar(target.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
} else {
method.dynamicSet(target.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());

View File

@ -70,8 +70,16 @@ public final class Compiler {
INITIALIZED,
/** method has been parsed */
PARSED,
/** constant folding pass */
CONSTANT_FOLDED,
/** method has been lowered */
LOWERED,
/** method hass been attributed */
ATTR,
/** method has been split */
SPLIT,
/** method has had its types finalized */
FINALIZED,
/** method has been emitted to bytecode */
EMITTED
}
@ -135,25 +143,6 @@ public final class Compiler {
/** Lazy jitting is disabled by default */
private static final boolean LAZY_JIT = false;
/**
* Should we use integers for literals and all operations
* that are based in constant and parameter assignment as
* long as they can be proven not to overflow? With this enabled
* var x = 17 would tag x as an integer, rather than a double,
* but as soon as it is used in an operation that may potentially
* overflow, such as an add, we conservatively widen it to double
* (number type). This is because overflow checks in the code
* are likely much more expensive that method specialization
*
* @return true if numbers should start as ints, false if they should
* start as doubles
*/
static boolean shouldUseIntegers() {
return USE_INTS;
}
private static final boolean USE_INTS;
/**
* Should we use integers for arithmetic operations as well?
* TODO: We currently generate no overflow checks so this is
@ -165,13 +154,12 @@ public final class Compiler {
* operands by default.
*/
static boolean shouldUseIntegerArithmetic() {
return Compiler.shouldUseIntegers() && USE_INT_ARITH;
return USE_INT_ARITH;
}
private static final boolean USE_INT_ARITH;
static {
USE_INTS = !Options.getBooleanProperty("nashorn.compiler.ints.disable");
USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic");
assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
@ -338,46 +326,93 @@ public final class Compiler {
try {
strict |= functionNode.isStrictMode();
if (!state.contains(State.LOWERED)) {
debugPrintAST();
/*
* These are the compile phases:
*
* Constant folding pass
* Simple constant folding that will make elementary constructs go away
*
* Lower (Control flow pass)
* Finalizes the control flow. Clones blocks for finally constructs and
* similar things. Establishes termination criteria for nodes
* Guarantee return instructions to method making sure control flow
* cannot fall off the end. Replacing high level nodes with lower such
* as runtime nodes where applicable.
*
* Attr
* Assign symbols and types to all nodes.
*
* Splitter
* Split the AST into several compile units based on a size heuristic
* Splitter needs attributed AST for weight calculations (e.g. is
* a + b a ScriptRuntime.ADD with call overhead or a dadd with much
* less). Split IR can lead to scope information being changed.
*
* Contract: all variables must have slot assignments and scope assignments
* before lowering.
*
* FinalizeTypes
* This pass finalizes the types for nodes. If Attr created wider types than
* known during the first pass, convert nodes are inserted or access nodes
* are specialized where scope accesses.
*
* Runtime nodes may be removed and primitivized or reintroduced depending
* on information that was established in Attr.
*
* CodeGeneration
* Emit bytecode
*
*/
debugPrintAST();
if (!state.contains(State.FINALIZED)) {
LOG.info("Folding constants in '" + functionNode.getName() + "'");
functionNode.accept(new FoldConstants());
state.add(State.CONSTANT_FOLDED);
LOG.info("Lowering '" + functionNode.getName() + "'");
functionNode.accept(new Lower(this));
state.add(State.LOWERED);
LOG.info("Attributing types '" + functionNode.getName() + "'");
functionNode.accept(new Attr(this));
state.add(State.ATTR);
this.scriptName = computeNames();
// Main script code always goes to this compile unit. Note that since we start this with zero weight
// and add script code last this class may end up slightly larger than others, but reserving one class
// just for the main script seems wasteful.
final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0L);
LOG.info("Splitting '" + functionNode.getName() + "'");
new Splitter(this, functionNode, scriptCompileUnit).split();
state.add(State.SPLIT);
assert functionNode.getCompileUnit() == scriptCompileUnit;
assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode();
if (functionNode.isStrictMode()) {
strict = true;
}
LOG.info("Finalizing types for '" + functionNode.getName() + "'");
functionNode.accept(new FinalizeTypes(this));
state.add(State.FINALIZED);
// print ast and parse if --print-lower-ast and/or --print-lower-parse are selected
debugPrintAST();
debugPrintParse();
if (errors.hasErrors()) {
return false;
}
}
scriptName = computeNames();
// Main script code always goes to this compile unit. Note that since we start this with zero weight
// and add script code last this class may end up slightly larger than others, but reserving one class
// just for the main script seems wasteful.
final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0l);
LOG.info("Splitting '" + functionNode.getName() + "'");
new Splitter(this, functionNode, scriptCompileUnit).split();
assert functionNode.getCompileUnit() == scriptCompileUnit;
/** Compute compile units */
assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode();
if (functionNode.isStrictMode()) {
strict = true;
}
LOG.info("Adjusting slot and scope information for symbols for '" + functionNode.getName() + "'");
functionNode.accept(new Lower.FinalizeSymbols());
LOG.info("Specializing callsite types for '" + functionNode.getName() + "'");
functionNode.accept(new AccessSpecializer());
try {
LOG.info("Emitting bytecode for '" + functionNode.getName() + "'");
final CodeGenerator codegen = new CodeGenerator(this);
functionNode.accept(codegen);
codegen.generateScopeCalls();
debugPrintAST();
debugPrintParse();
} catch (final VerifyError e) {
if (context._verify_code || context._print_code) {
context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());

View File

@ -0,0 +1,919 @@
/*
* 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.HashSet;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CallNode.EvalArgs;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReferenceNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TypeOverride;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Source;
/**
* Lower to more primitive operations. After lowering, an AST has symbols and
* types. Lowering may also add specialized versions of methods to the script if
* the optimizer is turned on.
*
* Any expression that requires temporary storage as part of computation will
* also be detected here and give a temporary symbol
*
* For any op that we process in FinalizeTypes it is an absolute guarantee
* that scope and slot information is correct. This enables e.g. AccessSpecialization
* and frame optimizations
*/
final class FinalizeTypes extends NodeOperatorVisitor {
/** Current source. */
private final Source source;
private static final DebugLogger LOG = new DebugLogger("finalize");
/**
* Constructor.
*
* @param compiler the compiler
*/
FinalizeTypes(final Compiler compiler) {
this.source = compiler.getSource();
}
@Override
public Node leave(final CallNode callNode) {
final EvalArgs evalArgs = callNode.getEvalArgs();
if (evalArgs != null) {
evalArgs.setCode(evalArgs.getCode().accept(this));
}
// AccessSpecializer - call return type may change the access for this location
final Node function = callNode.getFunction();
if (function instanceof ReferenceNode) {
setTypeOverride(callNode, ((ReferenceNode)function).getReference().getType());
}
return callNode;
}
private Node leaveUnary(final UnaryNode unaryNode) {
unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType()));
return unaryNode;
}
@Override
public Node leaveADD(final UnaryNode unaryNode) {
return leaveUnary(unaryNode);
}
@Override
public Node leaveBIT_NOT(final UnaryNode unaryNode) {
return leaveUnary(unaryNode);
}
@Override
public Node leaveCONVERT(final UnaryNode unaryNode) {
assert unaryNode.rhs().tokenType() != TokenType.CONVERT : "convert(convert encountered. check its origin and remove it";
return unaryNode;
}
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
specialize(unaryNode);
return unaryNode;
}
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
((CallNode)unaryNode.rhs()).setIsNew();
return unaryNode;
}
@Override
public Node leaveSUB(final UnaryNode unaryNode) {
return leaveUnary(unaryNode);
}
/**
* Add is a special binary, as it works not only on arithmetic, but for
* strings etc as well.
*/
@Override
public Node leaveADD(final BinaryNode binaryNode) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
final Type type = binaryNode.getType();
if (type.isObject()) {
if (!isAddString(binaryNode)) {
return new RuntimeNode(binaryNode, Request.ADD);
}
}
binaryNode.setLHS(convert(lhs, type));
binaryNode.setRHS(convert(rhs, type));
return binaryNode;
}
@Override
public Node leaveAND(final BinaryNode binaryNode) {
return binaryNode;
}
@Override
public Node leaveASSIGN(final BinaryNode binaryNode) {
Type destType = specialize(binaryNode);
if (destType == null) {
destType = binaryNode.getType();
}
binaryNode.setRHS(convert(binaryNode.rhs(), destType));
return binaryNode;
}
@Override
public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
return leaveASSIGN(binaryNode);
}
@Override
public Node leaveBIT_AND(BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : binaryNode.getSymbol();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveBIT_OR(BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveBIT_XOR(BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
binaryNode.setRHS(discard(binaryNode.rhs()));
// AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
// in that case, update the node type as well
propagateType(binaryNode, binaryNode.lhs().getType());
return binaryNode;
}
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null;
binaryNode.setLHS(discard(binaryNode.lhs()));
// AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed
// in that case, update the node type as well
propagateType(binaryNode, binaryNode.rhs().getType());
return binaryNode;
}
@Override
public Node leaveDIV(final BinaryNode binaryNode) {
return leaveBinaryArith(binaryNode);
}
@Override
public Node leaveEQ(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.EQ);
}
@Override
public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.EQ_STRICT);
}
@Override
public Node leaveGE(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.GE);
}
@Override
public Node leaveGT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.GT);
}
@Override
public Node leaveLE(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.LE);
}
@Override
public Node leaveLT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.LT);
}
@Override
public Node leaveMOD(final BinaryNode binaryNode) {
return leaveBinaryArith(binaryNode);
}
@Override
public Node leaveMUL(final BinaryNode binaryNode) {
return leaveBinaryArith(binaryNode);
}
@Override
public Node leaveNE(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.NE);
}
@Override
public Node leaveNE_STRICT(final BinaryNode binaryNode) {
return leaveCmp(binaryNode, Request.NE_STRICT);
}
@Override
public Node leaveOR(final BinaryNode binaryNode) {
return binaryNode;
}
@Override
public Node leaveSAR(final BinaryNode binaryNode) {
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveSHL(final BinaryNode binaryNode) {
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveSHR(final BinaryNode binaryNode) {
assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong();
return leaveBinary(binaryNode, Type.INT, Type.INT);
}
@Override
public Node leaveSUB(final BinaryNode binaryNode) {
return leaveBinaryArith(binaryNode);
}
@Override
public Node enter(final Block block) {
updateSymbols(block);
return block;
}
@Override
public Node leave(final CatchNode catchNode) {
final Node exceptionCondition = catchNode.getExceptionCondition();
if (exceptionCondition != null) {
catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
}
return catchNode;
}
@Override
public Node enter(final DoWhileNode doWhileNode) {
return enter((WhileNode)doWhileNode);
}
@Override
public Node leave(final DoWhileNode doWhileNode) {
return leave((WhileNode)doWhileNode);
}
@Override
public Node leave(final ExecuteNode executeNode) {
executeNode.setExpression(discard(executeNode.getExpression()));
return executeNode;
}
@Override
public Node leave(final ForNode forNode) {
final Node init = forNode.getInit();
final Node test = forNode.getTest();
final Node modify = forNode.getModify();
if (forNode.isForIn()) {
forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
return forNode;
}
if (init != null) {
forNode.setInit(discard(init));
}
if (test != null) {
forNode.setTest(convert(test, Type.BOOLEAN));
} else {
assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode();
}
if (modify != null) {
forNode.setModify(discard(modify));
}
return forNode;
}
@Override
public Node enter(final FunctionNode functionNode) {
updateSymbols(functionNode);
return functionNode;
}
@Override
public Node leave(final IfNode ifNode) {
final Node test = convert(ifNode.getTest(), Type.BOOLEAN);
ifNode.setTest(test);
return ifNode;
}
@SuppressWarnings("rawtypes")
@Override
public Node enter(final LiteralNode literalNode) {
if (literalNode instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
final Node[] array = arrayLiteralNode.getValue();
final Type elementType = arrayLiteralNode.getElementType();
for (int i = 0; i < array.length; i++) {
final Node element = array[i];
if (element != null) {
array[i] = convert(element.accept(this), elementType);
}
}
}
return null;
}
@Override
public Node leave(final ReturnNode returnNode) {
final Node expr = returnNode.getExpression();
if (expr != null) {
returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType()));
}
return returnNode;
}
@Override
public Node leave(final RuntimeNode runtimeNode) {
final List<Node> args = runtimeNode.getArgs();
for (final Node arg : args) {
assert !arg.getType().isUnknown();
}
return runtimeNode;
}
@Override
public Node leave(final SwitchNode switchNode) {
final Node expression = switchNode.getExpression();
final List<CaseNode> cases = switchNode.getCases();
final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
if (!allInteger) {
switchNode.setExpression(convert(expression, Type.OBJECT));
for (final CaseNode caseNode : cases) {
final Node test = caseNode.getTest();
if (test != null) {
caseNode.setTest(convert(test, Type.OBJECT));
}
}
}
return switchNode;
}
@Override
public Node leave(final TernaryNode ternaryNode) {
ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN));
return ternaryNode;
}
@Override
public Node leave(final ThrowNode throwNode) {
throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
return throwNode;
}
@Override
public Node leave(final VarNode varNode) {
final Node rhs = varNode.getInit();
if (rhs != null) {
Type destType = specialize(varNode);
if (destType == null) {
destType = varNode.getType();
}
assert varNode.hasType() : varNode + " doesn't have a type";
varNode.setInit(convert(rhs, destType));
}
return varNode;
}
@Override
public Node leave(final WhileNode whileNode) {
final Node test = whileNode.getTest();
if (test != null) {
whileNode.setTest(convert(test, Type.BOOLEAN));
}
return whileNode;
}
@Override
public Node leave(final WithNode withNode) {
withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT));
return withNode;
}
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
if (!symbol.isScope()) {
LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope");
}
if (loseSlot && symbol.hasSlot()) {
LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope");
}
}
/**
* Called after a block or function node (subclass of block) is finished. Guarantees
* that scope and slot information is correct for every symbol
* @param block block for which to to finalize type info.
*/
private static void updateSymbols(final Block block) {
if (!block.needsScope()) {
return; // nothing to do
}
assert !(block instanceof FunctionNode) || block.getFunction() == block;
final FunctionNode functionNode = block.getFunction();
final List<Symbol> symbols = block.getFrame().getSymbols();
final boolean allVarsInScope = functionNode.varsInScope();
final boolean isVarArg = functionNode.isVarArg();
for (final Symbol symbol : symbols) {
if (symbol.isInternal() || symbol.isThis()) {
continue;
}
if (symbol.isVar()) {
if (allVarsInScope || symbol.isScope()) {
updateSymbolsLog(functionNode, symbol, true);
symbol.setIsScope();
symbol.setNeedsSlot(false);
} else {
assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
}
} else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) {
updateSymbolsLog(functionNode, symbol, isVarArg);
symbol.setIsScope();
symbol.setNeedsSlot(!isVarArg);
}
}
}
/**
* Exit a comparison node and do the appropriate replacements. We need to introduce runtime
* nodes late for comparisons as types aren't known until the last minute
*
* Both compares and adds may turn into runtimes node at this level as when we first bump
* into the op in Attr, we may type it according to what we know there, which may be wrong later
*
* e.g. i (int) < 5 -> normal compare
* i = object
* then the post pass that would add the conversion to the 5 needs to
*
* @param binaryNode binary node to leave
* @param request runtime request
* @return lowered cmp node
*/
@SuppressWarnings("fallthrough")
private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
Type widest = Type.widest(lhs.getType(), rhs.getType());
boolean newRuntimeNode = false, finalized = false;
switch (request) {
case EQ_STRICT:
case NE_STRICT:
if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
newRuntimeNode = true;
widest = Type.OBJECT;
finalized = true;
}
//fallthru
default:
if (newRuntimeNode || widest.isObject()) {
final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request);
if (finalized) {
runtimeNode.setIsFinal();
}
return runtimeNode;
}
break;
}
binaryNode.setLHS(convert(lhs, widest));
binaryNode.setRHS(convert(rhs, widest));
return binaryNode;
}
/**
* Compute the binary arithmetic type given the lhs and an rhs of a binary expression
* @param lhsType the lhs type
* @param rhsType the rhs type
* @return the correct binary type
*/
private static Type binaryArithType(final Type lhsType, final Type rhsType) {
if (!Compiler.shouldUseIntegerArithmetic()) {
return Type.NUMBER;
}
return Type.widest(lhsType, rhsType, Type.NUMBER);
}
private Node leaveBinaryArith(final BinaryNode binaryNode) {
final Type type = binaryArithType(binaryNode.lhs().getType(), binaryNode.rhs().getType());
return leaveBinary(binaryNode, type, type);
}
private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) {
binaryNode.setLHS(convert(binaryNode.lhs(), lhsType));
binaryNode.setRHS(convert(binaryNode.rhs(), rhsType));
return binaryNode;
}
/**
* A symbol (and {@link Property}) can be tagged as "may be primitive". This is
* used a hint for dual fields that it is even worth it to try representing this
* field as something other than java.lang.Object.
*
* @param node node in which to tag symbols as primitive
* @param to which primitive type to use for tagging
*/
private static void setCanBePrimitive(final Node node, final Type to) {
final HashSet<Node> exclude = new HashSet<>();
node.accept(new NodeVisitor() {
private void setCanBePrimitive(final Symbol symbol) {
LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
symbol.setCanBePrimitive(to);
}
@Override
public Node enter(final IdentNode identNode) {
if (!exclude.contains(identNode)) {
setCanBePrimitive(identNode.getSymbol());
}
return null;
}
@Override
public Node enter(final AccessNode accessNode) {
setCanBePrimitive(accessNode.getProperty().getSymbol());
return null;
}
@Override
public Node enter(final IndexNode indexNode) {
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
return indexNode;
}
});
}
private static Type specialize(final Assignment<?> assignment) {
final Node node = ((Node)assignment);
final Node lhs = assignment.getAssignmentDest();
final Node rhs = assignment.getAssignmentSource();
if (!canHaveCallSiteType(lhs)) {
return null;
}
final Type to;
if (node.isSelfModifying()) {
to = node.getWidestOperationType();
} else {
to = rhs.getType();
}
if (!isSupportedCallSiteType(to)) {
//meaningless to specialize to boolean or object
return null;
}
setTypeOverride(lhs, to);
propagateType(node, to);
return to;
}
/**
* Is this a node that can have its type overridden. This is true for
* AccessNodes, IndexNodes and IdentNodes
*
* @param node the node to check
* @return true if node can have a callsite type
*/
private static boolean canHaveCallSiteType(final Node node) {
return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType();
}
/**
* Is the specialization type supported. Currently we treat booleans as objects
* and have no special boolean type accessor, thus booleans are ignored.
* TODO - support booleans? NASHORN-590
*
* @param castTo the type to check
* @return true if call site type is supported
*/
private static boolean isSupportedCallSiteType(final Type castTo) {
return castTo.isNumeric(); // don't specializable for boolean
}
/**
* Override the type of a node for e.g. access specialization of scope
* objects. Normally a variable can only get a wider type and narrower type
* sets are ignored. Not that a variable can still be on object type as
* per the type analysis, but a specific access may be narrower, e.g. if it
* is used in an arithmetic op. This overrides a type, regardless of
* type environment and is used primarily by the access specializer
*
* @param node node for which to change type
* @param to new type
*/
private static void setTypeOverride(final Node node, final Type to) {
final Type from = node.getType();
if (!node.getType().equals(to)) {
LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to);
if (!to.isObject() && from.isObject()) {
setCanBePrimitive(node, to);
}
}
LOG.info("Type override for lhs in '" + node + "' => " + to);
((TypeOverride)node).setType(to);
}
/**
* Add an explicit conversion. This is needed when attribution has created types
* that do not mesh into an op type, e.g. a = b, where b is object and a is double
* at the end of Attr, needs explicit conversion logic.
*
* An explicit conversion can be one of the following:
* + Convert a literal - just replace it with another literal
* + Convert a scope object - just replace the type of the access, e.g. get()D->get()I
* + Explicit convert placement, e.g. a = (double)b - all other cases
*
* No other part of the world after {@link Attr} may introduce new symbols. This
* is the only place.
*
* @param node node to convert
* @param to destination type
* @return conversion node
*/
private Node convert(final Node node, final Type to) {
assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass();
assert node != null : "node is null";
assert node.getSymbol() != null : "node " + node + " has no symbol!";
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode();
final Type from = node.getType();
if (Type.areEquivalent(from, to)) {
return node;
}
if (from.isObject() && to.isObject()) {
return node;
}
Node resultNode = node;
if (node instanceof LiteralNode && !to.isObject()) {
final LiteralNode<?> newNode = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, to).eval();
if (newNode != null) {
resultNode = newNode;
}
} else {
if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) {
setTypeOverride(node, to);
return resultNode;
}
resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node);
}
LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'");
//This is the only place in this file that can create new temporaries
//FinalizeTypes may not introduce ANY node that is not a conversion.
getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode);
resultNode.copyTerminalFlags(node);
return resultNode;
}
private Node discard(final Node node) {
node.setDiscard(true);
if (node.getSymbol() != null) {
final Node discard = new UnaryNode(source, Token.recast(node.getToken(), TokenType.DISCARD), node);
//discard never has a symbol in the discard node - then it would be a nop
discard.copyTerminalFlags(node);
return discard;
}
// node has no result (symbol) so we can keep it the way it is
return node;
}
/**
* Whenever an expression like an addition or an assignment changes type, it
* may be that case that {@link Attr} created a symbol for an intermediate
* result of the expression, say for an addition. This also has to be updated
* if the expression type changes.
*
* Assignments use their lhs as node symbol, and in this case we can't modify
* it. Then {@link CodeGenerator#Store} needs to do an explicit conversion.
* This is happens very rarely.
*
* @param node
* @param to
*/
private static void propagateType(final Node node, final Type to) {
final Symbol symbol = node.getSymbol();
if (symbol.isTemp()) {
symbol.setTypeOverride(to);
LOG.info("Type override for temporary in '" + node + "' => " + to);
}
}
/**
* Determine if the outcome of + operator is a string.
*
* @param node Node to test.
* @return true if a string result.
*/
private boolean isAddString(final Node node) {
if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) {
final BinaryNode binaryNode = (BinaryNode)node;
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
return isAddString(lhs) || isAddString(rhs);
}
return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString();
}
/**
* Whenever an explicit conversion is needed and the convertee is a literal, we can
* just change the literal
*/
static class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> {
private final Type type;
LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) {
super(parent);
this.type = type;
}
@Override
protected LiteralNode<?> eval() {
final Object value = ((LiteralNode<?>)parent).getValue();
LiteralNode<?> literalNode = null;
if (type.isString()) {
literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value));
} else if (type.isBoolean()) {
literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value));
} else if (type.isInteger()) {
literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
} else if (type.isLong()) {
literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
} else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) {
literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value));
}
if (literalNode != null) {
//inherit literal symbol for attr.
literalNode.setSymbol(parent.getSymbol());
}
return literalNode;
}
}
}

View File

@ -0,0 +1,278 @@
/*
* 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 jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
/**
* Simple constant folding pass, executed before IR is starting to be lowered.
*/
public class FoldConstants extends NodeVisitor {
private static final DebugLogger LOG = new DebugLogger("fold");
@Override
public Node leave(final UnaryNode unaryNode) {
final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval();
if (literalNode != null) {
LOG.info("Unary constant folded " + unaryNode + " to " + literalNode);
return literalNode;
}
return unaryNode;
}
@Override
public Node leave(final BinaryNode binaryNode) {
final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval();
if (literalNode != null) {
LOG.info("Binary constant folded " + binaryNode + " to " + literalNode);
return literalNode;
}
return binaryNode;
}
@Override
public Node leave(final IfNode ifNode) {
final Node test = ifNode.getTest();
if (test instanceof LiteralNode) {
final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail();
if (shortCut != null) {
return new ExecuteNode(shortCut);
}
return new EmptyNode(ifNode);
}
return ifNode;
}
@Override
public Node leave(final TernaryNode ternaryNode) {
final Node test = ternaryNode.lhs();
if (test instanceof LiteralNode) {
return ((LiteralNode<?>)test).isTrue() ? ternaryNode.rhs() : ternaryNode.third();
}
return ternaryNode;
}
/**
* Helper class to evaluate constant expressions at compile time This is
* also a simplifier used by BinaryNode visits, UnaryNode visits and
* conversions.
*/
abstract static class ConstantEvaluator<T extends Node> {
protected T parent;
protected final Source source;
protected final long token;
protected final int finish;
protected ConstantEvaluator(final T parent) {
this.parent = parent;
this.source = parent.getSource();
this.token = parent.getToken();
this.finish = parent.getFinish();
}
/**
* Returns a literal node that replaces the given parent node, or null if replacement
* is impossible
* @return the literal node
*/
protected abstract LiteralNode<?> eval();
}
private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> {
UnaryNodeConstantEvaluator(final UnaryNode parent) {
super(parent);
}
@Override
protected LiteralNode<?> eval() {
final Node rhsNode = parent.rhs();
if (!(rhsNode instanceof LiteralNode)) {
return null;
}
final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode;
final boolean rhsInteger = rhs.getType().isInteger();
LiteralNode<?> literalNode;
switch (parent.tokenType()) {
case ADD:
if (rhsInteger) {
literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32());
} else {
literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber());
}
break;
case SUB:
if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js
literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32());
} else {
literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber());
}
break;
case NOT:
literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean());
break;
case BIT_NOT:
literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32());
break;
default:
return null;
}
return literalNode;
}
}
//TODO add AND and OR with one constant parameter (bitwise)
private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> {
BinaryNodeConstantEvaluator(final BinaryNode parent) {
super(parent);
}
@Override
protected LiteralNode<?> eval() {
LiteralNode<?> result;
result = reduceTwoLiterals();
if (result != null) {
return result;
}
result = reduceOneLiteral();
if (result != null) {
return result;
}
return null;
}
@SuppressWarnings("static-method")
private LiteralNode<?> reduceOneLiteral() {
//TODO handle patterns like AND, OR, numeric ops that can be strength reduced but not replaced by a single literal node etc
return null;
}
private LiteralNode<?> reduceTwoLiterals() {
if (!(parent.lhs() instanceof LiteralNode && parent.rhs() instanceof LiteralNode)) {
return null;
}
final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs();
final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs();
final Type widest = Type.widest(lhs.getType(), rhs.getType());
boolean isInteger = widest.isInteger();
boolean isLong = widest.isLong();
double value;
switch (parent.tokenType()) {
case DIV:
value = lhs.getNumber() / rhs.getNumber();
break;
case ADD:
if ((lhs.isString() || rhs.isNumeric()) && (rhs.isString() || rhs.isNumeric())) {
Object res = ScriptRuntime.ADD(lhs.getObject(), rhs.getObject());
if (res instanceof Number) {
value = ((Number)res).doubleValue();
break;
}
assert res instanceof CharSequence : res + " was not a CharSequence, it was a " + res.getClass();
return LiteralNode.newInstance(source, token, finish, res.toString());
}
return null;
case MUL:
value = lhs.getNumber() * rhs.getNumber();
break;
case MOD:
value = lhs.getNumber() % rhs.getNumber();
break;
case SUB:
value = lhs.getNumber() - rhs.getNumber();
break;
case SHR:
return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL);
case SAR:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32());
case SHL:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32());
case BIT_XOR:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32());
case BIT_AND:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32());
case BIT_OR:
return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32());
case GE:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject()));
case LE:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject()));
case GT:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject()));
case LT:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject()));
case NE:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject()));
case NE_STRICT:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject()));
case EQ:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject()));
case EQ_STRICT:
return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject()));
default:
return null;
}
isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value);
isLong &= value != 0.0 && JSType.isRepresentableAsLong(value);
if (isInteger) {
return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value));
} else if (isLong) {
return LiteralNode.newInstance(source, token, finish, JSType.toLong(value));
}
return LiteralNode.newInstance(source, token, finish, value);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -929,7 +929,7 @@ public class MethodEmitter implements Emitter {
*/
public MethodEmitter loadCallee() {
debug("load callee " + functionNode.getCalleeNode().getSymbol());
assert functionNode.getCalleeNode().getSymbol().getSlot() != 0;
assert functionNode.getCalleeNode().getSymbol().getSlot() != 0 : "callee has wrong slot " + functionNode.getCalleeNode().getSymbol().getSlot() + " in " + functionNode.getName();
return load(functionNode.getCalleeNode().getSymbol());
}
@ -992,7 +992,7 @@ public class MethodEmitter implements Emitter {
* @param symbol symbol to store stack to
*/
public void store(final Symbol symbol) {
assert symbol != null;
assert symbol != null : "No symbol to store";
if (symbol.hasSlot()) {
debug("store", symbol);
popType(symbol.getSymbolType()).store(method, symbol.getSlot());
@ -1558,7 +1558,7 @@ public class MethodEmitter implements Emitter {
label.setStack(stack.clone());
return;
}
assert stacksEquivalent(stack, labelStack);
assert stacksEquivalent(stack, labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point";
}
/**

View File

@ -159,7 +159,9 @@ public class SharedScopeCall {
int slot = 2;
for (final Type type : paramTypes) {
method.load(type, slot++);
if (type == Type.NUMBER || type == Type.LONG) slot++;
if (type == Type.NUMBER || type == Type.LONG) {
slot++;
}
}
method.dynamicCall(returnType, paramTypes.length, flags);
}

View File

@ -27,6 +27,8 @@ package jdk.nashorn.internal.codegen;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
@ -53,17 +55,18 @@ import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
/**
* Computes the "byte code" weight of an AST segment. This is used
* for Splitting too large class files
*/
public class WeighNodes extends NodeVisitor {
public class WeighNodes extends NodeOperatorVisitor {
/*
* Weight constants.
*/
@ -77,6 +80,7 @@ public class WeighNodes extends NodeVisitor {
private static final long IF_WEIGHT = 2;
private static final long LITERAL_WEIGHT = 10;
private static final long LOOP_WEIGHT = 4;
private static final long NEW_WEIGHT = 6;
private static final long REFERENCE_WEIGHT = 20;
private static final long RETURN_WEIGHT = 2;
private static final long SPLIT_WEIGHT = 40;
@ -119,22 +123,8 @@ public class WeighNodes extends NodeVisitor {
return accessNode;
}
@Override
public Node leave(final BinaryNode binaryNode) {
final TokenType tokenType = binaryNode.tokenType();
if (tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) {
weight += ADD_WEIGHT;
} else {
weight += 1;
}
return binaryNode;
}
@Override
public Node enter(final Block block) {
if (weightCache != null && weightCache.containsKey(block)) {
weight += weightCache.get(block);
return null;
@ -305,4 +295,274 @@ public class WeighNodes extends NodeVisitor {
weight += WITH_WEIGHT;
return withNode;
}
@Override
public Node leaveADD(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveBIT_NOT(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveCONVERT(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveDELETE(final UnaryNode unaryNode) {
return runtimeNodeWeight(unaryNode);
}
@Override
public Node leaveDISCARD(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveNEW(final UnaryNode unaryNode) {
weight += NEW_WEIGHT;
return unaryNode;
}
@Override
public Node leaveNOT(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveSUB(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveTYPEOF(final UnaryNode unaryNode) {
return runtimeNodeWeight(unaryNode);
}
@Override
public Node leaveVOID(final UnaryNode unaryNode) {
return unaryNodeWeight(unaryNode);
}
@Override
public Node leaveADD(final BinaryNode binaryNode) {
weight += ADD_WEIGHT;
return binaryNode;
}
@Override
public Node leaveAND(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
weight += ADD_WEIGHT;
return binaryNode;
}
@Override
public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveBIND(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveBIT_AND(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveBIT_OR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveBIT_XOR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveDIV(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveEQ(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveGE(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveGT(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveIN(final BinaryNode binaryNode) {
weight += CALL_WEIGHT;
return binaryNode;
}
@Override
public Node leaveINSTANCEOF(final BinaryNode binaryNode) {
weight += CALL_WEIGHT;
return binaryNode;
}
@Override
public Node leaveLE(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveLT(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveMOD(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveMUL(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveNE(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveNE_STRICT(final BinaryNode binaryNode) {
return runtimeNodeWeight(binaryNode);
}
@Override
public Node leaveOR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveSAR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveSHL(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveSHR(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
@Override
public Node leaveSUB(final BinaryNode binaryNode) {
return binaryNodeWeight(binaryNode);
}
private Node unaryNodeWeight(final UnaryNode unaryNode) {
weight += 1;
return unaryNode;
}
private Node binaryNodeWeight(final BinaryNode binaryNode) {
weight += 1;
return binaryNode;
}
private Node runtimeNodeWeight(final UnaryNode unaryNode) {
weight += CALL_WEIGHT;
return unaryNode;
}
private Node runtimeNodeWeight(final BinaryNode binaryNode) {
weight += Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()).isObject() ? CALL_WEIGHT : 1;
return binaryNode;
}
}

View File

@ -33,8 +33,9 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import jdk.nashorn.internal.codegen.CodeGenerator;
import jdk.nashorn.internal.codegen.FunctionSignature;
import jdk.nashorn.internal.codegen.MethodEmitter;
@ -61,11 +62,9 @@ public class FunctionObjectCreator extends ObjectCreator {
*
* @param codegen the code generator
* @param functionNode the function node to turn into a ScriptFunction implementation
* @param keys initial keys for the object map
* @param symbols corresponding initial symbols for object map
*/
public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode, final List<String> keys, final List<Symbol> symbols) {
super(codegen, keys, symbols, false, false);
public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode) {
super(codegen, new ArrayList<String>(), new ArrayList<Symbol>(), false, false);
this.functionNode = functionNode;
}

View File

@ -35,11 +35,11 @@ import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.codegen.Frame;
import jdk.nashorn.internal.codegen.MethodEmitter.Label;
import jdk.nashorn.internal.ir.annotations.Ignore;
@ -287,7 +287,7 @@ public class Block extends Node {
for (Block block = this; block != null; block = block.getParent()) {
// Find name.
final Symbol symbol = block.getSymbols().get(name);
final Symbol symbol = block.symbols.get(name);
// If found then we are good.
if (symbol != null) {
return symbol;
@ -307,7 +307,7 @@ public class Block extends Node {
// Search up block chain to locate symbol.
for (Block block = this; block != null; block = block.getParent()) {
// Find name.
final Symbol symbol = block.getSymbols().get(name);
final Symbol symbol = block.symbols.get(name);
// If found then we are good.
if (symbol != null) {
return symbol;
@ -458,14 +458,22 @@ public class Block extends Node {
}
/**
* Print symbols in block (debugging.)
* Print symbols in block in alphabetical order, sorted on name
* Used for debugging, see the --print-symbols flag
*
* @param stream print writer to output symbols to
*
* @return true if symbols were found
*/
public boolean printSymbols(final PrintWriter stream) {
final Collection<Symbol> values = symbols.values();
final List<Symbol> values = new ArrayList<>(symbols.values());
Collections.sort(values, new Comparator<Symbol>() {
@Override
public int compare(final Symbol s0, final Symbol s1) {
return s0.getName().compareTo(s1.getName());
}
});
for (final Symbol symbol : values) {
symbol.print(stream);
@ -563,15 +571,6 @@ public class Block extends Node {
symbols.put(name, symbol);
}
/**
* Get the symbol table for this block
*
* @return a symbol table, which is a map from symbol name to symbol.
*/
private Map<String, Symbol> getSymbols() {
return symbols;
}
/**
* Check whether scope is necessary for this Block
*

View File

@ -28,7 +28,9 @@ package jdk.nashorn.internal.ir;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.annotations.Ignore;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
@ -57,16 +59,83 @@ public class CallNode extends Node implements TypeOverride {
*/
public static class EvalArgs {
/** evaluated code */
public Node code;
private Node code;
/** 'this' passed to evaluated code */
public Node evalThis;
private IdentNode evalThis;
/** location string for the eval call */
public String location;
final private String location;
/** is this call from a strict context? */
public boolean strictMode;
final private boolean strictMode;
/**
* Constructor
*
* @param code code to evaluate
* @param evalThis this node
* @param location location for the eval call
* @param strictMode is this a call from a strict context?
*/
public EvalArgs(final Node code, final IdentNode evalThis, final String location, final boolean strictMode) {
this.code = code;
this.evalThis = evalThis;
this.location = location;
this.strictMode = strictMode;
}
/**
* Return the code that is to be eval:ed by this eval function
* @return code as an AST node
*/
public Node getCode() {
return code;
}
/**
* Set the code that is to be eval.ed by this eval function
* @param code the code as an AST node
*/
public void setCode(final Node code) {
this.code = code;
}
/**
* Get the {@code this} symbol used to invoke this eval call
* @return the {@code this} symbol
*/
public IdentNode getThis() {
return this.evalThis;
}
/**
* Set the {@code this} symbol used to invoke this eval call
* @param evalThis the {@code this} symbol
*/
public void setThis(final IdentNode evalThis) {
this.evalThis = evalThis;
}
/**
* Get the human readable location for this eval call
* @return the location
*/
public String getLocation() {
return this.location;
}
/**
* Check whether this eval call is executed in strict mode
* @return true if executed in strict mode, false otherwise
*/
public boolean getStrictMode() {
return this.strictMode;
}
}
/** arguments for 'eval' call. Non-null only if this call node is 'eval' */
@Ignore
private EvalArgs evalArgs;
/**

View File

@ -57,7 +57,7 @@ public class CatchNode extends Node {
* @param body catch body
*/
public CatchNode(final Source source, final long token, final int finish, final IdentNode exception, final Node exceptionCondition, final Block body) {
super (source, token, finish);
super(source, token, finish);
this.exception = exception;
this.exceptionCondition = exceptionCondition;

View File

@ -47,14 +47,22 @@ public class ExecuteNode extends Node {
*/
public ExecuteNode(final Source source, final long token, final int finish, final Node expression) {
super(source, token, finish);
this.expression = expression;
}
/**
* Constructor
*
* @param expression an expression to wrap, from which source, tokens and finish are also inherited
*/
public ExecuteNode(final Node expression) {
super(expression.getSource(), expression.getToken(), expression.getFinish());
this.expression = expression;
}
private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) {
super(executeNode);
expression = cs.existingOrCopy(executeNode.expression);
this.expression = cs.existingOrCopy(executeNode.expression);
}
@Override

View File

@ -32,10 +32,8 @@ import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
@ -126,9 +124,6 @@ public class FunctionNode extends Block {
@Ignore
private IdentNode varArgsNode;
/** this access properties. */
private final LinkedHashMap<String, Node> thisProperties;
/** Pending label list. */
private final Stack<LabelNode> labelStack;
@ -147,6 +142,10 @@ public class FunctionNode extends Block {
@Ignore
private LineNumberNode funcVarLineNumberNode;
/** Initializer var func = __callee__, where applicable */
@Ignore
private Node selfSymbolInit;
/** Function flags. */
private int flags;
@ -183,7 +182,7 @@ public class FunctionNode extends Block {
private static final int NEEDS_SCOPE = HAS_ALL_VARS_IN_SCOPE | IS_VAR_ARG;
/** What is the return type of this function? */
private Type returnType = Type.OBJECT;
private Type returnType = Type.UNKNOWN;
/**
* Used to keep track of a function's parent blocks.
@ -216,7 +215,6 @@ public class FunctionNode extends Block {
this.firstToken = token;
this.lastToken = token;
this.namespace = new Namespace(compiler.getNamespace().getParent());
this.thisProperties = new LinkedHashMap<>();
this.labelStack = new Stack<>();
this.controlStack = new Stack<>();
this.declarations = new ArrayList<>();
@ -249,7 +247,6 @@ public class FunctionNode extends Block {
this.argumentsNode = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode);
this.varArgsNode = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode);
this.calleeNode = (IdentNode)cs.existingOrCopy(functionNode.calleeNode);
this.thisProperties = new LinkedHashMap<>();
this.labelStack = new Stack<>();
this.controlStack = new Stack<>();
this.declarations = new ArrayList<>();
@ -372,30 +369,44 @@ public class FunctionNode extends Block {
/**
* Create a temporary variable to the current frame.
*
* @param currentFrame Frame to add to - defaults to current function frame
* @param type Strong type of symbol.
* @param node Primary node to use symbol.
*
* @return Symbol used.
*/
public Symbol newTemporary(final Type type, final Node node) {
Symbol sym = node.getSymbol();
public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) {
assert currentFrame != null;
Symbol symbol = node.getSymbol();
// If no symbol already present.
if (sym == null) {
if (symbol == null) {
final String uname = uniqueName(TEMP_PREFIX.tag());
sym = new Symbol(uname, IS_TEMP, type);
sym.setNode(node);
symbol = new Symbol(uname, IS_TEMP, type);
symbol.setNode(node);
}
// Assign a slot if it doesn't have one.
if (!sym.hasSlot()) {
frames.addSymbol(sym);
if (!symbol.hasSlot()) {
currentFrame.addSymbol(symbol);
}
// Set symbol to node.
node.setSymbol(sym);
node.setSymbol(symbol);
return sym;
return symbol;
}
/**
* Add a new temporary variable to the current frame
*
* @param type Strong type of symbol
* @param node Primary node to use symbol
*
* @return symbol used
*/
public Symbol newTemporary(final Type type, final Node node) {
return newTemporary(frames, type, node);
}
/**
@ -414,22 +425,13 @@ public class FunctionNode extends Block {
return sym;
}
/**
* Add a property to the constructor (function) based on this.x usage.
*
* @param key Name of property.
* @param node Value node (has type.)
*/
public void addThisProperty(final String key, final Node node) {
if (node == null) {
return;
}
thisProperties.put(key, node);
}
@Override
public void toString(final StringBuilder sb) {
sb.append('[');
sb.append(returnType);
sb.append(']');
sb.append(' ');
sb.append("function");
if (ident != null) {
@ -871,12 +873,23 @@ public class FunctionNode extends Block {
return (flags & NEEDS_SELF_SYMBOL) != 0;
}
/**
* Get the initializer statement for the __callee__ variable, where applicable
* for self references
* @return initialization
*/
public Node getSelfSymbolInit() {
return this.selfSymbolInit;
}
/**
* Flag the function as needing a self symbol. This is needed only for
* self referring functions
* @param selfSymbolInit initialization expression for self symbol
*/
public void setNeedsSelfSymbol() {
public void setNeedsSelfSymbol(final Node selfSymbolInit) {
this.flags |= NEEDS_SELF_SYMBOL;
this.selfSymbolInit = selfSymbolInit;
}
/**
@ -941,16 +954,6 @@ public class FunctionNode extends Block {
funcVarLineNumberNode = lineNumber;
}
/**
* Get a all properties accessed with {@code this} used as a base in this
* function - the map is ordered upon assignment order in the control flow
*
* @return map a map of property name to node mapping for this accesses
*/
public Map<String, Node> getThisProperties() {
return Collections.unmodifiableMap(thisProperties);
}
/**
* Get the namespace this function uses for its symbols
* @return the namespace
@ -984,11 +987,7 @@ public class FunctionNode extends Block {
//we never bother with object types narrower than objects, that will lead to byte code verification errors
//as for instance even if we know we are returning a string from a method, the code generator will always
//treat it as an object, at least for now
this.returnType = returnType.isObject() ? Type.OBJECT : returnType;
// Adjust type of result node symbol
if (returnType != Type.UNKNOWN) {
resultNode.getSymbol().setTypeOverride(this.returnType);
}
this.returnType = Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType);
}
/**
@ -1010,15 +1009,13 @@ public class FunctionNode extends Block {
/**
* Set the lowered state
*
* @param isLowered lowered state
*/
public void setIsLowered(final boolean isLowered) {
flags = isLowered ? flags | IS_LOWERED : flags & ~IS_LOWERED;
public void setIsLowered() {
flags |= IS_LOWERED;
}
/**
* Get the lowered
* Get the lowered state
*
* @return true if function is lowered
*/
@ -1076,22 +1073,6 @@ public class FunctionNode extends Block {
return Collections.unmodifiableList(declarations);
}
/**
* @return the unit index
*/
// public int getUnit() {
// return unit;
// }
/**
* Set the index of this function's compile unit. Used by splitter.
* @see Splitter
* @param unit the unit index
*/
// public void setUnit(final int unit) {
// this.unit = unit;
// }
/**
* Get the compile unit used to compile this function
* @see Compiler

View File

@ -32,8 +32,10 @@ import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Lexer.LexerToken;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.Undefined;
@ -199,6 +201,15 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return value instanceof String;
}
/**
* Test if tha value is a number
*
* @return True if value is a number
*/
public boolean isNumeric() {
return value instanceof Number;
}
/**
* Assist in IR navigation.
*
@ -240,16 +251,49 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish) {
return new LiteralNode<Node>(source, token, finish, null) {
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish());
}
@Override
public Type getType() {
return Type.OBJECT;
}
};
return new NodeLiteralNode(source, token, finish);
}
/**
* Create a new null literal based on a parent node (source, token, finish)
*
* @param parent parent node
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent) {
return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
}
private static class BooleanLiteralNode extends LiteralNode<Boolean> {
private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) {
super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value);
}
private BooleanLiteralNode(final BooleanLiteralNode literalNode) {
super(literalNode);
}
@Override
protected Node copy(final CopyState cs) {
return new BooleanLiteralNode(this);
}
@Override
public boolean isTrue() {
return value;
}
@Override
public Type getType() {
return Type.BOOLEAN;
}
@Override
public Type getWidestOperationType() {
return Type.BOOLEAN;
}
}
/**
@ -263,29 +307,63 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<Boolean> newInstance(final Source source, final long token, final int finish, final boolean value) {
return new LiteralNode<Boolean>(source, token, finish, value) {
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
}
@Override
public boolean isTrue() {
return value;
}
@Override
public Type getType() {
return Type.BOOLEAN;
}
@Override
public Type getWidestOperationType() {
return Type.BOOLEAN;
}
};
return new BooleanLiteralNode(source, token, finish, value);
}
/**
* Create a new boolean literal based on a parent node (source, token, finish)
*
* @param parent parent node
* @param value true or false
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final boolean value) {
return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
private static class NumberLiteralNode extends LiteralNode<Number> {
private final Type type = numberGetType(value);
private NumberLiteralNode(final Source source, final long token, final int finish, final Number value) {
super(source, Token.recast(token, TokenType.DECIMAL), finish, value);
}
private NumberLiteralNode(final NumberLiteralNode literalNode) {
super(literalNode);
}
private static Type numberGetType(final Number number) {
if (number instanceof Integer) {
return Type.INT;
} else if (number instanceof Long) {
return Type.LONG;
} else if (number instanceof Double) {
return Type.NUMBER;
} else {
assert false;
}
return null;
}
@Override
protected Node copy(final CopyState cs) {
return new NumberLiteralNode(this);
}
@Override
public Type getType() {
return type;
}
@Override
public Type getWidestOperationType() {
return getType();
}
}
/**
* Create a new number literal
*
@ -297,40 +375,34 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<Number> newInstance(final Source source, final long token, final int finish, final Number value) {
return new LiteralNode<Number>(source, token, finish, value) {
return new NumberLiteralNode(source, token, finish, value);
}
private Type numberGetType(final Number number) {
if (number instanceof Integer) {
return Type.INT;
} else if (number instanceof Long) {
return Type.LONG;
} else if (number instanceof Double) {
return Type.NUMBER;
} else {
assert false;
}
/**
* Create a new number literal based on a parent node (source, token, finish)
*
* @param parent parent node
* @param value literal value
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final Number value) {
return new NumberLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
return null;
}
private static class UndefinedLiteralNode extends LiteralNode<Undefined> {
private UndefinedLiteralNode(final Source source, final long token, final int finish) {
super(source, Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED);
}
private final Type type = numberGetType(value);
private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) {
super(literalNode);
}
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
}
@Override
public Type getType() {
return type;
}
@Override
public Type getWidestOperationType() {
return getType();
}
};
@Override
protected Node copy(final CopyState cs) {
return new UndefinedLiteralNode(this);
}
}
/**
@ -344,12 +416,41 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<Undefined> newInstance(final Source source, final long token, final int finish, final Undefined value) {
return new LiteralNode<Undefined>(source, token, finish, value) {
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
}
};
return new UndefinedLiteralNode(source, token, finish);
}
/**
* Create a new null literal based on a parent node (source, token, finish)
*
* @param parent parent node
* @param value undefined value
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final Undefined value) {
return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish());
}
private static class StringLiteralNode extends LiteralNode<String> {
private StringLiteralNode(final Source source, final long token, final int finish, final String value) {
super(source, Token.recast(token, TokenType.STRING), finish, value);
}
private StringLiteralNode(final StringLiteralNode literalNode) {
super(literalNode);
}
@Override
protected Node copy(final CopyState cs) {
return new StringLiteralNode(this);
}
@Override
public void toString(final StringBuilder sb) {
sb.append('\"');
sb.append(value);
sb.append('\"');
}
}
/**
@ -363,19 +464,39 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<String> newInstance(final Source source, final long token, final int finish, final String value) {
return new LiteralNode<String>(source, token, finish, value) {
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
}
return new StringLiteralNode(source, token, finish, value);
}
@Override
public void toString(final StringBuilder sb) {
sb.append('\"');
sb.append(value);
sb.append('\"');
}
};
/**
* Create a new String literal based on a parent node (source, token, finish)
*
* @param parent parent node
* @param value string value
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final String value) {
return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> {
private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) {
super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here?
}
private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) {
super(literalNode);
}
@Override
protected Node copy(final CopyState cs) {
return new LexerTokenLiteralNode(this);
}
@Override
public void toString(final StringBuilder sb) {
sb.append(value.toString());
}
}
/**
@ -389,22 +510,65 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<LexerToken> newInstance(final Source source, final long token, final int finish, final LexerToken value) {
return new LiteralNode<LexerToken>(source, token, finish, value) {
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
}
@Override
public void toString(final StringBuilder sb) {
sb.append(value.toString());
}
};
return new LexerTokenLiteralNode(source, token, finish, value);
}
/**
* Create a new array for an arbitrary node
* Create a new lexer token literal based on a parent node (source, token, finish)
*
* @param parent parent node
* @param value lexer token
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) {
return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
private static class NodeLiteralNode extends LiteralNode<Node> {
private NodeLiteralNode(final Source source, final long token, final int finish) {
this(source, token, finish, null);
}
private NodeLiteralNode(final Source source, final long token, final int finish, final Node value) {
super(source, Token.recast(token, TokenType.OBJECT), finish, value);
}
private NodeLiteralNode(final LiteralNode<Node> literalNode) {
super(literalNode);
}
@Override
protected Node copy(final CopyState cs) {
return new NodeLiteralNode(this);
}
@Override
public Node accept(final NodeVisitor visitor) {
if (visitor.enter(this) != null) {
if (value != null) {
value = value.accept(visitor);
}
return visitor.leave(this);
}
return this;
}
@Override
public Type getType() {
return value == null ? Type.OBJECT : super.getType();
}
@Override
public Type getWidestOperationType() {
return value == null ? Type.OBJECT : value.getWidestOperationType();
}
}
/**
* Create a new node literal for an arbitrary node
*
* @param source the source
* @param token token
@ -414,33 +578,19 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish, final Node value) {
return new LiteralNode<Node>(source, token, finish, value) {
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
}
return new NodeLiteralNode(source, token, finish, value);
}
@Override
public Node accept(final NodeVisitor visitor) {
if (visitor.enter(this) != null) {
value = value.accept(visitor);
return visitor.leave(this);
}
return this;
}
@Override
public void toString(final StringBuilder sb) {
value.toString(sb);
}
@Override
public Type getWidestOperationType() {
return value.getWidestOperationType();
}
};
/**
* Create a new node literal based on a parent node (source, token, finish)
*
* @param parent parent node
* @param value node value
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final Node value) {
return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value);
}
/**
@ -521,8 +671,7 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* @param value array literal value, a Node array
*/
protected ArrayLiteralNode(final Source source, final long token, final int finish, final Node[] value) {
super(source, token, finish, value);
super(source, Token.recast(token, TokenType.ARRAY), finish, value);
this.elementType = Type.UNKNOWN;
}
@ -530,13 +679,14 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
* Copy constructor
* @param node source array literal node
*/
protected ArrayLiteralNode(final LiteralNode<Node[]> node) {
protected ArrayLiteralNode(final ArrayLiteralNode node) {
super(node);
this.elementType = node.elementType;
}
@Override
protected Node copy(final CopyState cs) {
return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue());
return new ArrayLiteralNode(this);
}
/**
@ -769,6 +919,19 @@ public abstract class LiteralNode<T> extends Node implements PropertyKey {
return new ArrayLiteralNode(source, token, finish, value.toArray(new Node[value.size()]));
}
/**
* Create a new array literal based on a parent node (source, token, finish)
*
* @param parent parent node
* @param value literal value list
*
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final List<Node> value) {
return new ArrayLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value.toArray(new Node[value.size()]));
}
/**
* Create a new array literal of Nodes
*

View File

@ -26,7 +26,7 @@
package jdk.nashorn.internal.ir;
import java.util.IdentityHashMap;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
@ -38,7 +38,7 @@ import jdk.nashorn.internal.runtime.Source;
*/
public abstract class Node extends Location {
/** Node symbol. */
private Symbol nodeSymbol;
private Symbol symbol;
/** Start of source range. */
protected int start;
@ -68,7 +68,7 @@ public abstract class Node extends Location {
public Node(final Source source, final long token, final int finish) {
super(source, token);
start = Token.descPosition(token);
this.start = Token.descPosition(token);
this.finish = finish;
}
@ -80,7 +80,7 @@ public abstract class Node extends Location {
protected Node(final Node node) {
super(node);
this.nodeSymbol = node.nodeSymbol;
this.symbol = node.symbol;
this.isResolved = node.isResolved;
this.isTerminal = node.isTerminal;
this.hasGoto = node.hasGoto;
@ -107,8 +107,8 @@ public abstract class Node extends Location {
* @return the type of the node.
*/
public Type getType() {
assert hasType();
return nodeSymbol.getSymbolType();
assert hasType() : this + " has no type";
return symbol.getSymbolType();
}
/**
@ -379,7 +379,7 @@ public abstract class Node extends Location {
* @return the symbol
*/
public Symbol getSymbol() {
return nodeSymbol;
return symbol;
}
/**
@ -389,7 +389,7 @@ public abstract class Node extends Location {
* @param symbol the symbol
*/
public void setSymbol(final Symbol symbol) {
nodeSymbol = symbol;
this.symbol = symbol;
}
/**
@ -412,21 +412,4 @@ public abstract class Node extends Location {
this.isTerminal = isTerminal;
}
/**
* Return last node in a statement list.
*
* @param statements Statement list.
*
* @return Last (non-debug) statement or null if empty block.
*/
public static Node lastStatement(final List<Node> statements) {
for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
final Node node = statements.get(lastIndex);
if (!node.isDebug()) {
return node;
}
}
return null;
}
}

View File

@ -257,6 +257,9 @@ public class RuntimeNode extends Node implements TypeOverride {
/** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */
private Type callSiteType;
/** is final - i.e. may not be removed again, lower in the code pipeline */
private boolean isFinal;
/**
* Constructor
*
@ -286,6 +289,51 @@ public class RuntimeNode extends Node implements TypeOverride {
this(source, token, finish, request, Arrays.asList(args));
}
/**
* Constructor
*
* @param parent parent node from which to inherit source, token, finish
* @param request the request
* @param args arguments to request
*/
public RuntimeNode(final Node parent, final Request request, final Node... args) {
this(parent, request, Arrays.asList(args));
}
/**
* Constructor
*
* @param parent parent node from which to inherit source, token, finish
* @param request the request
* @param args arguments to request
*/
public RuntimeNode(final Node parent, final Request request, final List<Node> args) {
super(parent);
this.request = request;
this.args = args;
}
/**
* Constructor
*
* @param parent parent node from which to inherit source, token, finish and arguments
* @param request the request
*/
public RuntimeNode(final UnaryNode parent, final Request request) {
this(parent, request, parent.rhs());
}
/**
* Constructor
*
* @param parent parent node from which to inherit source, token, finish and arguments
* @param request the request
*/
public RuntimeNode(final BinaryNode parent, final Request request) {
this(parent, request, parent.lhs(), parent.rhs());
}
private RuntimeNode(final RuntimeNode runtimeNode, final CopyState cs) {
super(runtimeNode);
@ -300,6 +348,21 @@ public class RuntimeNode extends Node implements TypeOverride {
this.callSiteType = runtimeNode.callSiteType;
}
/**
* Is this node final - i.e. it can never be replaced with other nodes again
* @return true if final
*/
public boolean isFinal() {
return isFinal;
}
/**
* Flag this node as final - i.e it may never be replaced with other nodes again
*/
public void setIsFinal() {
this.isFinal = true;
}
@Override
protected Node copy(final CopyState cs) {
return new RuntimeNode(this, cs);

View File

@ -26,6 +26,10 @@
package jdk.nashorn.internal.ir;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.options.Options;
@ -46,7 +50,7 @@ public final class Symbol implements Comparable<Symbol> {
/** Is this a constant */
public static final int IS_CONSTANT = 0b0000_0101;
static final int KINDMASK = 0b0000_1111;
static final int KINDMASK = 0b0000_1111;
/** Is this scope */
public static final int IS_SCOPE = 0b0000_0001_0000;
@ -85,8 +89,33 @@ public final class Symbol implements Comparable<Symbol> {
/** Number of times this symbol is used in code */
private int useCount;
/** Debugging option - dump info and stack trace when a symbol with a given name is manipulated */
private static final String TRACE_SYMBOL = Options.getStringProperty("nashorn.compiler.symbol.trace", null);
/** Debugging option - dump info and stack trace when symbols with given names are manipulated */
private static final Set<String> TRACE_SYMBOLS;
private static final Set<String> TRACE_SYMBOLS_STACKTRACE;
static {
final String stacktrace = Options.getStringProperty("nashorn.compiler.symbol.stacktrace", null);
final String trace;
if (stacktrace != null) {
trace = stacktrace; //stacktrace always implies trace as well
TRACE_SYMBOLS_STACKTRACE = new HashSet<>();
for (StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) {
TRACE_SYMBOLS_STACKTRACE.add(st.nextToken());
}
} else {
trace = Options.getStringProperty("nashorn.compiler.symbol.trace", null);
TRACE_SYMBOLS_STACKTRACE = null;
}
if (trace != null) {
TRACE_SYMBOLS = new HashSet<>();
for (StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) {
TRACE_SYMBOLS.add(st.nextToken());
}
} else {
TRACE_SYMBOLS = null;
}
}
/**
* Constructor
@ -106,6 +135,7 @@ public final class Symbol implements Comparable<Symbol> {
this.type = type;
this.slot = slot;
this.fieldIndex = -1;
trace("CREATE SYMBOL");
}
/**
@ -135,7 +165,7 @@ public final class Symbol implements Comparable<Symbol> {
final StringBuilder sb = new StringBuilder();
sb.append(string.substring(0, Math.min(string.length(), max)));
while (sb.length () < max) {
while (sb.length() < max) {
sb.append(' ');
}
return sb.toString();
@ -263,6 +293,23 @@ public final class Symbol implements Comparable<Symbol> {
return name.hashCode() ^ block.hashCode();
}
private static String type(final String desc) {
switch (desc.charAt(desc.length() - 1)) {
case ';':
return desc;//"obj";
case 'D':
return "double";
case 'I':
return "int";
case 'J':
return "long";
case 'Z':
return "boolean";
default:
return "UNKNOWN";
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
@ -270,8 +317,8 @@ public final class Symbol implements Comparable<Symbol> {
sb.append(name).
append(' ').
append("(type=").
append(desc.charAt(desc.length() - 1) == ';' ? 'O' : desc).
append('(').
append(type(desc)).
append(')');
if (hasSlot()) {
@ -602,10 +649,13 @@ public final class Symbol implements Comparable<Symbol> {
return block instanceof FunctionNode && ((FunctionNode) block).isScript();
}
private void trace(final String desc) {
if (TRACE_SYMBOL != null && TRACE_SYMBOL.equals(name)) {
if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) {
Context.err("SYMBOL: '" + name + "' " + desc);
new Throwable().printStackTrace(Context.getCurrentErr());
if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) {
new Throwable().printStackTrace(Context.getContext().getErr());
}
}
}
}

View File

@ -56,6 +56,9 @@ public class TryNode extends Node {
/** Exception symbol. */
private Symbol exception;
/** Catchall exception for finally expansion, where applicable */
private Symbol finallyCatchAll;
/**
* Constructor
*
@ -183,6 +186,23 @@ public class TryNode extends Node {
this.exception = exception;
}
/**
* Get the catch all symbol for this try block
* @return catch all symbol
*/
public Symbol getFinallyCatchAll() {
return this.finallyCatchAll;
}
/**
* If a finally block exists, the synthetic catchall needs another symbol to
* store its throwable
* @param finallyCatchAll a symbol for the finally catch all exception
*/
public void setFinallyCatchAll(final Symbol finallyCatchAll) {
this.finallyCatchAll = finallyCatchAll;
}
/**
* Get the exit label for this try block
* @return exit label

View File

@ -41,9 +41,6 @@ public class VarNode extends Node implements Assignment<IdentNode> {
/** Is this a function var node */
private boolean isFunctionVarNode;
/** Should append VarNode to statement list? */
private final boolean shouldAppend;
/**
* Constructor
*
@ -54,25 +51,10 @@ public class VarNode extends Node implements Assignment<IdentNode> {
* @param init init node or null if just a declaration
*/
public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) {
this(source, token, finish, name, init, true);
}
/**
* Constructor
*
* @param source the source
* @param token token
* @param finish finish
* @param name name of variable
* @param init init node or null if just a declaration
* @param shouldAppend should this turn into explicit code, like if it were an ExecuteNode
*/
public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, final boolean shouldAppend) {
super(source, token, finish);
this.name = name;
this.init = init;
this.shouldAppend = shouldAppend;
if (init != null) {
this.name.setIsInitializedHere();
}
@ -83,7 +65,6 @@ public class VarNode extends Node implements Assignment<IdentNode> {
this.name = (IdentNode)cs.existingOrCopy(varNode.name);
this.init = cs.existingOrCopy(varNode.init);
this.shouldAppend = varNode.shouldAppend;
}
@Override
@ -116,7 +97,6 @@ public class VarNode extends Node implements Assignment<IdentNode> {
setInit(source);
}
/**
* Does this variable declaration have an init value
* @return true if an init exists, false otherwise
@ -235,15 +215,4 @@ public class VarNode extends Node implements Assignment<IdentNode> {
this.isFunctionVarNode = true;
}
/**
* Is this the var for a for-in node or other construct that means
* manual or no appends of this varNode to the statement list in
* Lower? The default is yes as most VarNodes are auto-append to
* the end of the statement list when lowered
*
* @return should compiler append var node to statement list
*/
public boolean shouldAppend() {
return shouldAppend;
}
}

View File

@ -113,8 +113,8 @@ public final class ASTWriter {
status += " Goto ";
}
if (node.getSymbol() != null && node.getSymbol().hasSlot()) {
status += " Slot " + node.getSymbol();
if (node.getSymbol() != null) {
status += node.getSymbol();
}
status = status.trim();

View File

@ -265,7 +265,7 @@ public final class NativeJSON extends ScriptObject {
if (node instanceof LiteralNode) {
// check for array literal
if (node.tokenType() == TokenType.LBRACKET) {
if (node.tokenType() == TokenType.ARRAY) {
assert node instanceof ArrayLiteralNode;
final Node[] elements = ((ArrayLiteralNode)node).getValue();

View File

@ -789,6 +789,7 @@ public final class NativeString extends ScriptObject {
*
* @param self self reference
* @param start start position for slice
* @param end end position for slice
* @return sliced out substring
*/
@SpecializedFunction
@ -808,6 +809,7 @@ public final class NativeString extends ScriptObject {
*
* @param self self reference
* @param start start position for slice
* @param end end position for slice
* @return sliced out substring
*/
@SpecializedFunction
@ -843,9 +845,8 @@ public final class NativeString extends ScriptObject {
}
private static Object splitString(String str, String separator, long limit) {
if (separator.equals("")) {
Object[] array = new Object[str.length()];
if (separator.isEmpty()) {
final Object[] array = new Object[str.length()];
for (int i = 0; i < array.length; i++) {
array[i] = String.valueOf(str.charAt(i));
}
@ -856,18 +857,18 @@ public final class NativeString extends ScriptObject {
final int strLength = str.length();
final int sepLength = separator.length();
int pos = 0;
int count = 0;
int n = 0;
while (pos < strLength && count < limit) {
while (pos < strLength && n < limit) {
int found = str.indexOf(separator, pos);
if (found == -1) {
break;
}
elements.add(str.substring(pos, found));
count++;
n++;
pos = found + sepLength;
}
if (pos <= strLength && count < limit) {
if (pos <= strLength && n < limit) {
elements.add(str.substring(pos));
}
@ -963,9 +964,8 @@ public final class NativeString extends ScriptObject {
if (validStart < validEnd) {
return str.substring(validStart, validEnd);
} else {
return str.substring(validEnd, validStart);
}
return str.substring(validEnd, validStart);
}
/**

View File

@ -96,11 +96,6 @@ public abstract class AbstractParser {
this.token = Token.toDesc(EOL, 0, 1);
this.type = EOL;
this.last = EOL;
this.start = 0;
this.finish = 0;
this.line = 0;
this.linePosition = 0;
this.lexer = null;
this.isStrictMode = strict;
}

View File

@ -587,8 +587,6 @@ loop:
script.setLastToken(token);
script.setFinish(source.getLength() - 1);
block.addStatement(lineNumber());
return script;
}
@ -616,6 +614,24 @@ loop:
return null;
}
/**
* Return last node in a statement list.
*
* @param statements Statement list.
*
* @return Last (non-debug) statement or null if empty block.
*/
private static Node lastStatement(final List<Node> statements) {
for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
final Node node = statements.get(lastIndex);
if (!node.isDebug()) {
return node;
}
}
return null;
}
/**
* SourceElements :
* SourceElement
@ -645,7 +661,7 @@ loop:
// check for directive prologues
if (checkDirective) {
// skip any debug statement like line number to get actual first line
final Node lastStatement = Node.lastStatement(block.getStatements());
final Node lastStatement = lastStatement(block.getStatements());
// get directive prologue, if any
final String directive = getDirective(lastStatement);
@ -677,7 +693,7 @@ loop:
}
// verify that function name as well as parameter names
// satisfystrict mode restrictions.
// satisfy strict mode restrictions.
verifyStrictIdent(function.getIdent(), "function name");
for (final IdentNode param : function.getParameters()) {
verifyStrictIdent(param, "function parameter");
@ -2628,12 +2644,12 @@ loop:
// just expression as function body
final Node expr = expression();
// create a return statement
final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null);
final ExecuteNode executeNode = new ExecuteNode(source, returnNode.getToken(), finish, returnNode);
// create a return statement - this creates code in itself and does not need to be
// wrapped into an ExecuteNode
final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null);
// add the return statement
functionNode.addStatement(executeNode);
functionNode.addStatement(returnNode);
functionNode.setLastToken(token);
functionNode.setFinish(Token.descPosition(token) + Token.descLength(token));
@ -2648,8 +2664,6 @@ loop:
functionNode.setFinish(finish);
}
block.addStatement(lineNumber());
} finally {
restoreBlock();
}

View File

@ -171,6 +171,8 @@ public enum TokenType {
IDENT (LITERAL, null),
REGEX (LITERAL, null),
XML (LITERAL, null),
OBJECT (LITERAL, null),
ARRAY (LITERAL, null),
COMMALEFT (IR, null),
CONVERT (IR, null),

View File

@ -478,11 +478,11 @@ public final class Context {
return _timezone;
}
/*
/**
* Get the PropertyMap of the current global scope
* @return the property map of the current global scope
*/
public PropertyMap getGlobalMap() {
public static PropertyMap getGlobalMap() {
return Context.getGlobalTrusted().getMap();
}

View File

@ -41,6 +41,8 @@ public class DebugLogger {
private int indent;
private static final int INDENT_SPACE = 4;
/**
* Constructor
*
@ -93,7 +95,24 @@ public class DebugLogger {
*/
public void indent(final int pos) {
if (isEnabled) {
indent += pos * 4;
indent += pos * INDENT_SPACE;
}
}
/**
* Add an indent position
*/
public void indent() {
indent += INDENT_SPACE;
}
/**
* Unindent a position
*/
public void unindent() {
indent -= INDENT_SPACE;
if (indent < 0) {
indent = 0;
}
}

View File

@ -106,6 +106,11 @@ public final class OptionsObject {
/** time zone for this context */
public final TimeZone _timezone;
/**
* Constructor
*
* @param context a context
*/
public OptionsObject(final Context context) {
this._anon_functions = context._anon_functions;
this._callsite_flags = context._callsite_flags;

View File

@ -635,7 +635,13 @@ public final class PropertyMap implements Iterable<Object>, PropertyListener {
sb.append(ScriptRuntime.safeToString(property.getKey()));
final Class<?> ctype = property.getCurrentType();
sb.append(" <" + property.getClass().getSimpleName() + ":" + (ctype == null ? "undefined" : ctype.getSimpleName()) + ">");
sb.append(" <").
append(property.getClass().getSimpleName()).
append(':').
append(ctype == null ?
"undefined" :
ctype.getSimpleName()).
append('>');
}
sb.append(']');

View File

@ -1044,6 +1044,10 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
set(key, value, getContext()._strict);
}
/**
* Return true if the script object context is strict
* @return true if strict context
*/
public final boolean isStrictContext() {
return getContext()._strict;
}
@ -1419,7 +1423,10 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return (flags & IS_SCOPE) != 0;
}
// java.util.Map-like methods to help ScriptObjectMirror implementation
/**
* Clears the properties from a ScriptObject
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*/
public void clear() {
final boolean strict = getContext()._strict;
final Iterator<String> iter = propertyIterator();
@ -1428,10 +1435,24 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
/**
* Checks if a property with a given key is present in a ScriptObject
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @param key the key to check for
* @return true if a property with the given key exists, false otherwise
*/
public boolean containsKey(final Object key) {
return has(key);
}
/**
* Checks if a property with a given value is present in a ScriptObject
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @param value value to check for
* @return true if a property with the given value exists, false otherwise
*/
public boolean containsValue(final Object value) {
final Iterator<Object> iter = valueIterator();
while (iter.hasNext()) {
@ -1442,6 +1463,13 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return false;
}
/**
* Returns the set of <property, value> entries that make up this
* ScriptObject's properties
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @return an entry set of all the properties in this object
*/
public Set<Map.Entry<Object, Object>> entrySet() {
final Iterator<String> iter = propertyIterator();
final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
@ -1452,10 +1480,23 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return Collections.unmodifiableSet(entries);
}
/**
* Check whether a ScriptObject contains no properties
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @return true if object has no properties
*/
public boolean isEmpty() {
return !propertyIterator().hasNext();
}
/**
* Return the set of keys (property names) for all properties
* in this ScriptObject
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @return keySet of this ScriptObject
*/
public Set<Object> keySet() {
final Iterator<String> iter = propertyIterator();
final Set<Object> keySet = new HashSet<>();
@ -1465,12 +1506,27 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return Collections.unmodifiableSet(keySet);
}
/**
* Put a property in the ScriptObject
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @param key property key
* @param value property value
* @return oldValue if property with same key existed already
*/
public Object put(final Object key, final Object value) {
final Object oldValue = get(key);
set(key, value, getContext()._strict);
return oldValue;
}
/**
* Put several properties in the ScriptObject given a mapping
* of their keys to their values
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @param otherMap a <key,value> map of properties to add
*/
public void putAll(final Map<?, ?> otherMap) {
final boolean strict = getContext()._strict;
for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
@ -1478,12 +1534,26 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
}
}
/**
* Remove a property from the ScriptObject.
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @param key the key of the property
* @return the oldValue of the removed property
*/
public Object remove(final Object key) {
final Object oldValue = get(key);
delete(key, getContext()._strict);
return oldValue;
}
/**
* Return the size of the ScriptObject - i.e. the number of properties
* it contains
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @return number of properties in ScriptObject
*/
public int size() {
int n = 0;
for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) {
@ -1492,6 +1562,12 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
return n;
}
/**
* Return the values of the properties in the ScriptObject
* (java.util.Map-like method to help ScriptObjectMirror implementation)
*
* @return collection of values for the properties in this ScriptObject
*/
public Collection<Object> values() {
final List<Object> values = new ArrayList<>(size());
final Iterator<Object> iter = valueIterator();

View File

@ -41,13 +41,13 @@ import java.lang.invoke.MethodHandles;
*/
public class ScriptingFunctions {
/** Handle to implementation of {@link ScriptingFunctions#read} - Nashorn extension */
/** Handle to implementation of {@link ScriptingFunctions#readLine} - Nashorn extension */
public static final MethodHandle READLINE = findOwnMH("readLine", Object.class, Object.class);
/** Handle to implementation of {@link ScriptingFunctions#readFully} - Nashorn extension */
public static final MethodHandle READFULLY = findOwnMH("readFully", Object.class, Object.class, Object.class);
/** Handle to implementation of {@link ScriptingFunctions#read} - Nashorn extension */
/** Handle to implementation of {@link ScriptingFunctions#quit} - Nashorn extension */
public static final MethodHandle QUIT = findOwnMH("quit", Object.class, Object.class, Object.class);
private ScriptingFunctions() {

View File

@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime.arrays;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptObject;
/**

View File

@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import java.util.Iterator;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptObject;

View File

@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.GlobalObject;
import jdk.nashorn.internal.runtime.PropertyDescriptor;

View File

@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.GlobalObject;
import jdk.nashorn.internal.runtime.PropertyDescriptor;

View File

@ -446,7 +446,7 @@ public class LinkerCallSite extends ChainedCallSite {
*
* @throws Throwable if invocation fails or throws exception/error
*/
@SuppressWarnings("unused")
@SuppressWarnings({"unused", "resource"})
public Object traceObject(final MethodHandle mh, final Object... args) throws Throwable {
final PrintWriter out = Context.getCurrentErr();
tracePrint(out, "ENTER ", args, null);
@ -464,7 +464,7 @@ public class LinkerCallSite extends ChainedCallSite {
*
* @throws Throwable if invocation fails or throws exception/error
*/
@SuppressWarnings("unused")
@SuppressWarnings({"unused", "resource"})
public void traceVoid(final MethodHandle mh, final Object... args) throws Throwable {
final PrintWriter out = Context.getCurrentErr();
tracePrint(out, "ENTER ", args, null);

View File

@ -416,7 +416,7 @@ public final class Options {
System.setProperty(value.substring(0, eq), value.substring(eq + 1));
} else {
// -Dfoo is fine. Set System property "foo" with "" as it's value
if (!value.equals("")) {
if (!value.isEmpty()) {
System.setProperty(value, "");
} else {
// do not allow empty property name

View File

@ -178,7 +178,7 @@ public class Shell {
* @return null if there are problems with option parsing.
*/
@SuppressWarnings("resource")
private Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) {
private static Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) {
final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : new PrintStream(err);
final PrintWriter wout = new PrintWriter(pout, true);
@ -230,7 +230,7 @@ public class Shell {
* @return error code
* @throws IOException when any script file read results in I/O error
*/
private int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
private static int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
final ScriptObject oldGlobal = Context.getGlobal();
final boolean globalChanged = (oldGlobal != global);
try {
@ -330,7 +330,7 @@ public class Shell {
* @return return code
*/
@SuppressWarnings("resource")
private int readEvalPrint(final Context context, final ScriptObject global) {
private static int readEvalPrint(final Context context, final ScriptObject global) {
final String prompt = bundle.getString("shell.prompt");
final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
final PrintWriter err = context.getErr();

View File

@ -0,0 +1,43 @@
/*
* 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.
*/
/**
* This is a simple test that checks that access specialization in FinalizeTypes is consistent.
* Here, a2 = 0 will be turned int {I}a2 = 0, and all would be fine and well, only we can't change
* the symbol type for a2 from double, and we can't as it's not a temporary. Either we have to put
* a temporary in at the late finalize stage and add another assignment, or we genericize the check
* in CodeGenerator#Store so we detect whether a target is of the wrong type before storing. It
* is hopefully very rare, and will only be a problem when assignment results that have been
* specialized live on the stack
*
* @test
* @run
*/
function f() {
var a0 = a1 = a2 = 0;
a0 = 16.1;
a1 = 17.1;
a2 = 18.1;
}
f();

View File

@ -1,12 +1,36 @@
Compiling... box2d.js
Compiled OK: box2d.js
Compiling... code-load.js
Compiled OK: code-load.js
Compiling... crypto.js
Compiled OK: crypto.js
Compiling... deltablue.js
Compiled OK: deltablue.js
Compiling... earley-boyer.js
Compiled OK: earley-boyer.js
Compiling... gbemu.js
Compiled OK: gbemu.js
Compiling... navier-stokes.js
Compiled OK: navier-stokes.js
Compiling... pdfjs.js
Compiled OK: pdfjs.js
Compiling... raytrace.js
Compiled OK: raytrace.js
Compiling... regexp.js
Compiled OK: regexp.js
Compiling... richards.js
Compiled OK: richards.js
Compiling... splay.js
Compiled OK: splay.js

View File

@ -65,11 +65,7 @@ function endsWith(str, suffix) {
function run_one_benchmark(arg, iters) {
load(path + 'base.js');
load(arg);
var file_name;
var file = arg.split('/');
if (file.length == 1) {
file = arg.split('\\');
@ -80,9 +76,17 @@ function run_one_benchmark(arg, iters) {
file.pop();
}
file_name = file[file.length - 1];
if (typeof compile_only !== 'undefined') {
print("Compiling... " + file_name);
}
load(path + 'base.js');
load(arg);
if (typeof compile_only !== 'undefined') {
print("Compiled OK: " + file_name);
print("");
return;
}

View File

@ -0,0 +1,53 @@
/*
* 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.
*/
/**
* There was a bug in the old Lower that didn't fully propagate type information
* by way of assignment. 'q' in the example below would have finished a double
* even though it can get an object value through the assignment 'q = l'
*
* Furthermore, this caused type coercion to be done at q = l, and not a q = q * 2,
* which is a bug. This test ensures it happens in the correct order
*
* @test
* @run
*/
function createObject() {
var obj = { valueOf: function() { print("toNumber coercion"); return 17; }}
return obj;
}
function f() {
var l = 1.2; //number
var q = 2.3; //number
for (var i = 0; i < 2; i++) {
q = l; // q = toNumber(l), no coercion here
print("assignment done");
q = q * 2; // q = q * 2, coercion here
print("multiplication done");
l = createObject();
}
}
f();

View File

@ -0,0 +1,5 @@
assignment done
multiplication done
assignment done
toNumber coercion
multiplication done