8026137: Fix Issues with Binary Evaluation Order
Co-authored-by: Attila Szegedi <attila.szegedi@oracle.com> Reviewed-by: hannesw, jlaskey
This commit is contained in:
parent
3ef5f027ce
commit
16ce875fc6
@ -480,6 +480,10 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
}
|
||||
|
||||
//unknown parameters are promoted to object type.
|
||||
if (newFunctionNode.hasLazyChildren()) {
|
||||
//the final body has already been assigned as we have left the function node block body by now
|
||||
objectifySymbols(body);
|
||||
}
|
||||
newFunctionNode = finalizeParameters(newFunctionNode);
|
||||
newFunctionNode = finalizeTypes(newFunctionNode);
|
||||
for (final Symbol symbol : newFunctionNode.getDeclaredSymbols()) {
|
||||
@ -489,11 +493,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
}
|
||||
}
|
||||
|
||||
if (newFunctionNode.hasLazyChildren()) {
|
||||
//the final body has already been assigned as we have left the function node block body by now
|
||||
objectifySymbols(body);
|
||||
}
|
||||
|
||||
List<VarNode> syntheticInitializers = null;
|
||||
|
||||
if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
|
||||
@ -503,8 +502,8 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
syntheticInitializers.add(createSyntheticInitializer(newFunctionNode.getIdent(), CALLEE, newFunctionNode));
|
||||
}
|
||||
|
||||
if(newFunctionNode.needsArguments()) {
|
||||
if(syntheticInitializers == null) {
|
||||
if (newFunctionNode.needsArguments()) {
|
||||
if (syntheticInitializers == null) {
|
||||
syntheticInitializers = new ArrayList<>(1);
|
||||
}
|
||||
// "var arguments = :arguments"
|
||||
@ -512,12 +511,12 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
ARGUMENTS, newFunctionNode));
|
||||
}
|
||||
|
||||
if(syntheticInitializers != null) {
|
||||
final List<Statement> stmts = body.getStatements();
|
||||
if (syntheticInitializers != null) {
|
||||
final List<Statement> stmts = newFunctionNode.getBody().getStatements();
|
||||
final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
|
||||
newStatements.addAll(syntheticInitializers);
|
||||
newStatements.addAll(stmts);
|
||||
newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
|
||||
newFunctionNode = newFunctionNode.setBody(lc, newFunctionNode.getBody().setStatements(lc, newStatements));
|
||||
}
|
||||
|
||||
if (returnTypes.peek().isUnknown()) {
|
||||
@ -557,12 +556,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
return synthVar.setName((IdentNode)name.setSymbol(lc, nameSymbol));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCONVERT(final UnaryNode unaryNode) {
|
||||
assert false : "There should be no convert operators in IR during Attribution";
|
||||
return end(unaryNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIdentNode(final IdentNode identNode) {
|
||||
final String name = identNode.getName();
|
||||
@ -991,7 +984,7 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
|
||||
@Override
|
||||
public Node leaveNEW(final UnaryNode unaryNode) {
|
||||
return end(ensureSymbol(Type.OBJECT, unaryNode));
|
||||
return end(ensureSymbol(Type.OBJECT, unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1287,7 +1280,9 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
private Node leaveCmp(final BinaryNode binaryNode) {
|
||||
ensureTypeNotUnknown(binaryNode.lhs());
|
||||
ensureTypeNotUnknown(binaryNode.rhs());
|
||||
|
||||
Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
|
||||
ensureSymbol(widest, binaryNode.lhs());
|
||||
ensureSymbol(widest, binaryNode.rhs());
|
||||
return end(ensureSymbol(Type.BOOLEAN, binaryNode));
|
||||
}
|
||||
|
||||
@ -1630,7 +1625,7 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) {
|
||||
LOG.fine("Had to post pass widen '", node, "' ", Debug.id(node), " from ", node.getType(), " to ", to);
|
||||
Symbol symbol = node.getSymbol();
|
||||
if(symbol.isShared() && symbol.wouldChangeType(to)) {
|
||||
if (symbol.isShared() && symbol.wouldChangeType(to)) {
|
||||
symbol = temporarySymbols.getTypedTemporarySymbol(to);
|
||||
}
|
||||
newType(symbol, to);
|
||||
@ -1646,40 +1641,105 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
return !node.isLazy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Eg.
|
||||
*
|
||||
* var d = 17;
|
||||
* var e;
|
||||
* e = d; //initially typed as int for node type, should retype as double
|
||||
* e = object;
|
||||
*
|
||||
* var d = 17;
|
||||
* var e;
|
||||
* e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric
|
||||
* e = object;
|
||||
*
|
||||
*/
|
||||
//
|
||||
// Eg.
|
||||
//
|
||||
// var d = 17;
|
||||
// var e;
|
||||
// e = d; //initially typed as int for node type, should retype as double
|
||||
// e = object;
|
||||
//
|
||||
// var d = 17;
|
||||
// var e;
|
||||
// e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric
|
||||
// e = object;
|
||||
//
|
||||
@SuppressWarnings("fallthrough")
|
||||
@Override
|
||||
public Node leaveBinaryNode(final BinaryNode binaryNode) {
|
||||
final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType());
|
||||
BinaryNode newBinaryNode = binaryNode;
|
||||
switch (binaryNode.tokenType()) {
|
||||
default:
|
||||
if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
|
||||
|
||||
if (isAdd(binaryNode)) {
|
||||
newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
|
||||
if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) {
|
||||
return new RuntimeNode(newBinaryNode, Request.ADD);
|
||||
}
|
||||
} else if (binaryNode.isComparison()) {
|
||||
final Expression lhs = newBinaryNode.lhs();
|
||||
final Expression rhs = newBinaryNode.rhs();
|
||||
|
||||
Type cmpWidest = Type.widest(lhs.getType(), rhs.getType());
|
||||
|
||||
boolean newRuntimeNode = false, finalized = false;
|
||||
switch (newBinaryNode.tokenType()) {
|
||||
case EQ_STRICT:
|
||||
case NE_STRICT:
|
||||
if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
|
||||
newRuntimeNode = true;
|
||||
cmpWidest = Type.OBJECT;
|
||||
finalized = true;
|
||||
}
|
||||
//fallthru
|
||||
default:
|
||||
if (newRuntimeNode || cmpWidest.isObject()) {
|
||||
return new RuntimeNode(newBinaryNode, Request.requestFor(binaryNode)).setIsFinal(finalized);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return newBinaryNode;
|
||||
} else {
|
||||
if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
|
||||
return newBinaryNode;
|
||||
}
|
||||
checkThisAssignment(binaryNode);
|
||||
newBinaryNode = newBinaryNode.setLHS(widen(newBinaryNode.lhs(), widest));
|
||||
case ADD:
|
||||
newBinaryNode = (BinaryNode)widen(newBinaryNode, widest);
|
||||
}
|
||||
|
||||
return newBinaryNode;
|
||||
|
||||
}
|
||||
|
||||
private boolean isAdd(final Node node) {
|
||||
return node.isTokenType(TokenType.ADD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 && isAdd(node)) {
|
||||
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();
|
||||
}
|
||||
|
||||
private void checkThisAssignment(final BinaryNode binaryNode) {
|
||||
if (binaryNode.isAssignment()) {
|
||||
if (binaryNode.lhs() instanceof AccessNode) {
|
||||
final AccessNode accessNode = (AccessNode) binaryNode.lhs();
|
||||
|
||||
if (accessNode.getBase().getSymbol().isThis()) {
|
||||
lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
lc.replace(currentFunctionNode, newFunctionNode);
|
||||
currentFunctionNode = newFunctionNode;
|
||||
} while (!changed.isEmpty());
|
||||
|
||||
return currentFunctionNode;
|
||||
}
|
||||
|
||||
@ -1692,7 +1752,6 @@ final class Attr extends NodeOperatorVisitor<LexicalContext> {
|
||||
final Expression lhs = binaryNode.lhs();
|
||||
|
||||
newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType
|
||||
// ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine
|
||||
|
||||
return end(ensureSymbol(destType, binaryNode));
|
||||
}
|
||||
|
@ -56,10 +56,6 @@ final class BranchOptimizer {
|
||||
branchOptimizer(node, label, state);
|
||||
}
|
||||
|
||||
private void load(final Expression node) {
|
||||
codegen.load(node);
|
||||
}
|
||||
|
||||
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
|
||||
final Expression rhs = unaryNode.rhs();
|
||||
|
||||
@ -67,18 +63,16 @@ final class BranchOptimizer {
|
||||
case NOT:
|
||||
branchOptimizer(rhs, label, !state);
|
||||
return;
|
||||
case CONVERT:
|
||||
default:
|
||||
if (unaryNode.getType().isBoolean()) {
|
||||
branchOptimizer(rhs, label, state);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// convert to boolean
|
||||
load(unaryNode);
|
||||
codegen.load(unaryNode);
|
||||
method.convert(Type.BOOLEAN);
|
||||
if (state) {
|
||||
method.ifne(label);
|
||||
@ -90,6 +84,7 @@ final class BranchOptimizer {
|
||||
private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) {
|
||||
final Expression lhs = binaryNode.lhs();
|
||||
final Expression rhs = binaryNode.rhs();
|
||||
Type widest = Type.widest(lhs.getType(), rhs.getType());
|
||||
|
||||
switch (binaryNode.tokenType()) {
|
||||
case AND:
|
||||
@ -118,45 +113,33 @@ final class BranchOptimizer {
|
||||
|
||||
case EQ:
|
||||
case EQ_STRICT:
|
||||
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
|
||||
load(lhs);
|
||||
load(rhs);
|
||||
codegen.loadBinaryOperands(lhs, rhs, widest);
|
||||
method.conditionalJump(state ? EQ : NE, true, label);
|
||||
return;
|
||||
|
||||
case NE:
|
||||
case NE_STRICT:
|
||||
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
|
||||
load(lhs);
|
||||
load(rhs);
|
||||
codegen.loadBinaryOperands(lhs, rhs, widest);
|
||||
method.conditionalJump(state ? NE : EQ, true, label);
|
||||
return;
|
||||
|
||||
case GE:
|
||||
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
|
||||
load(lhs);
|
||||
load(rhs);
|
||||
codegen.loadBinaryOperands(lhs, rhs, widest);
|
||||
method.conditionalJump(state ? GE : LT, !state, label);
|
||||
return;
|
||||
|
||||
case GT:
|
||||
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
|
||||
load(lhs);
|
||||
load(rhs);
|
||||
codegen.loadBinaryOperands(lhs, rhs, widest);
|
||||
method.conditionalJump(state ? GT : LE, !state, label);
|
||||
return;
|
||||
|
||||
case LE:
|
||||
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
|
||||
load(lhs);
|
||||
load(rhs);
|
||||
codegen.loadBinaryOperands(lhs, rhs, widest);
|
||||
method.conditionalJump(state ? LE : GT, state, label);
|
||||
return;
|
||||
|
||||
case LT:
|
||||
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol() + " in " + binaryNode;
|
||||
load(lhs);
|
||||
load(rhs);
|
||||
codegen.loadBinaryOperands(lhs, rhs, widest);
|
||||
method.conditionalJump(state ? LT : GE, state, label);
|
||||
return;
|
||||
|
||||
@ -164,7 +147,7 @@ final class BranchOptimizer {
|
||||
break;
|
||||
}
|
||||
|
||||
load(binaryNode);
|
||||
codegen.load(binaryNode);
|
||||
method.convert(Type.BOOLEAN);
|
||||
if (state) {
|
||||
method.ifne(label);
|
||||
@ -187,7 +170,7 @@ final class BranchOptimizer {
|
||||
}
|
||||
}
|
||||
|
||||
load(node);
|
||||
codegen.load(node);
|
||||
method.convert(Type.BOOLEAN);
|
||||
if (state) {
|
||||
method.ifne(label);
|
||||
|
@ -43,7 +43,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
|
||||
@ -60,7 +59,6 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
||||
@ -111,7 +109,6 @@ 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.debug.ASTWriter;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.objects.Global;
|
||||
@ -217,12 +214,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* @param identNode an identity node to load
|
||||
* @return the method generator used
|
||||
*/
|
||||
private MethodEmitter loadIdent(final IdentNode identNode) {
|
||||
private MethodEmitter loadIdent(final IdentNode identNode, final Type type) {
|
||||
final Symbol symbol = identNode.getSymbol();
|
||||
|
||||
if (!symbol.isScope()) {
|
||||
assert symbol.hasSlot() || symbol.isParam();
|
||||
return method.load(symbol);
|
||||
return method.load(symbol).convert(type);
|
||||
}
|
||||
|
||||
final String name = symbol.getName();
|
||||
@ -243,11 +240,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
if (isFastScope(symbol)) {
|
||||
// Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
|
||||
if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) {
|
||||
return loadSharedScopeVar(identNode.getType(), symbol, flags);
|
||||
return loadSharedScopeVar(type, symbol, flags);
|
||||
}
|
||||
return loadFastScopeVar(identNode.getType(), symbol, flags, identNode.isFunction());
|
||||
return loadFastScopeVar(type, symbol, flags, identNode.isFunction());
|
||||
}
|
||||
return method.dynamicGet(identNode.getType(), identNode.getName(), flags, identNode.isFunction());
|
||||
return method.dynamicGet(type, identNode.getName(), flags, identNode.isFunction());
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,9 +310,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
|
||||
}
|
||||
|
||||
private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) {
|
||||
private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) {
|
||||
loadFastScopeProto(symbol, true);
|
||||
method.dynamicSet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE);
|
||||
method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE);
|
||||
return method;
|
||||
}
|
||||
|
||||
@ -359,14 +356,61 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* @return the method emitter used
|
||||
*/
|
||||
MethodEmitter load(final Expression node) {
|
||||
return load(node, false);
|
||||
return load(node, node.hasType() ? node.getType() : null, false);
|
||||
}
|
||||
|
||||
private MethodEmitter load(final Expression node, final boolean baseAlreadyOnStack) {
|
||||
private static boolean safeLiteral(final Expression rhs) {
|
||||
return rhs instanceof LiteralNode && !(rhs instanceof ArrayLiteralNode);
|
||||
}
|
||||
|
||||
MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type) {
|
||||
return loadBinaryOperands(lhs, rhs, type, false);
|
||||
}
|
||||
|
||||
private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type, final boolean baseAlreadyOnStack) {
|
||||
// ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary
|
||||
// expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT
|
||||
// RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we
|
||||
// can combine a LOAD with a CONVERT operation (e.g. use a dynamic getter with the conversion target type as its
|
||||
// return value). What we do here is reorder LOAD RIGHT and CONVERT LEFT when possible; it is possible only when
|
||||
// we can prove that executing CONVERT LEFT can't have a side effect that changes the value of LOAD RIGHT.
|
||||
// Basically, if we know that either LEFT is not an object, or RIGHT is a constant literal, then we can do the
|
||||
// reordering and collapse LOAD/CONVERT into a single operation; otherwise we need to do the more costly
|
||||
// separate operations to preserve specification semantics.
|
||||
final Type lhsType = lhs.getType();
|
||||
if (lhsType.isObject() && !safeLiteral(rhs)) {
|
||||
// Can't reorder. Load and convert separately.
|
||||
load(lhs, lhsType, baseAlreadyOnStack);
|
||||
load(rhs, rhs.getType(), false);
|
||||
// Avoid empty SWAP, SWAP bytecode sequence if CONVERT LEFT is a no-op
|
||||
if (!lhsType.isEquivalentTo(type)) {
|
||||
method.swap();
|
||||
method.convert(type);
|
||||
method.swap();
|
||||
}
|
||||
method.convert(type);
|
||||
} else {
|
||||
// Can reorder. Combine load and convert into single operations.
|
||||
load(lhs, type, baseAlreadyOnStack);
|
||||
load(rhs, type, false);
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
MethodEmitter loadBinaryOperands(final BinaryNode node) {
|
||||
return loadBinaryOperands(node.lhs(), node.rhs(), node.getType(), false);
|
||||
}
|
||||
|
||||
private MethodEmitter load(final Expression node, final Type type) {
|
||||
return load(node, type, false);
|
||||
}
|
||||
|
||||
private MethodEmitter load(final Expression node, final Type type, final boolean baseAlreadyOnStack) {
|
||||
final Symbol symbol = node.getSymbol();
|
||||
|
||||
// If we lack symbols, we just generate what we see.
|
||||
if (symbol == null) {
|
||||
if (symbol == null || type == null) {
|
||||
node.accept(this);
|
||||
return method;
|
||||
}
|
||||
@ -378,10 +422,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
*/
|
||||
final CodeGenerator codegen = this;
|
||||
|
||||
node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
node.accept(new NodeVisitor<LexicalContext>(lc) {
|
||||
@Override
|
||||
public boolean enterIdentNode(final IdentNode identNode) {
|
||||
loadIdent(identNode);
|
||||
loadIdent(identNode, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -391,7 +435,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
load(accessNode.getBase()).convert(Type.OBJECT);
|
||||
}
|
||||
assert method.peekType().isObject();
|
||||
method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
|
||||
method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -401,7 +445,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
load(indexNode.getBase()).convert(Type.OBJECT);
|
||||
load(indexNode.getIndex());
|
||||
}
|
||||
method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction());
|
||||
method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -410,13 +454,29 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
// function nodes will always leave a constructed function object on stack, no need to load the symbol
|
||||
// separately as in enterDefault()
|
||||
functionNode.accept(codegen);
|
||||
method.convert(type);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterCallNode(CallNode callNode) {
|
||||
return codegen.enterCallNode(callNode, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterLiteralNode(LiteralNode<?> literalNode) {
|
||||
return codegen.enterLiteralNode(literalNode, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterDefault(final Node otherNode) {
|
||||
final Node currentDiscard = codegen.lc.getCurrentDiscard();
|
||||
otherNode.accept(codegen); // generate code for whatever we are looking at.
|
||||
method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
|
||||
if(currentDiscard != otherNode) {
|
||||
method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
|
||||
assert method.peekType() != null;
|
||||
method.convert(type);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@ -583,15 +643,19 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return argCount;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
return enterCallNode(callNode, callNode.getType());
|
||||
}
|
||||
|
||||
private boolean enterCallNode(final CallNode callNode, final Type callNodeType) {
|
||||
lineNumber(callNode.getLineNumber());
|
||||
|
||||
final List<Expression> args = callNode.getArgs();
|
||||
final Expression function = callNode.getFunction();
|
||||
final Block currentBlock = lc.getCurrentBlock();
|
||||
final CodeGeneratorLexicalContext codegenLexicalContext = lc;
|
||||
final Type callNodeType = callNode.getType();
|
||||
|
||||
function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
|
||||
@ -612,16 +676,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private void scopeCall(final IdentNode node, final int flags) {
|
||||
load(node);
|
||||
method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
|
||||
load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
|
||||
// ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
|
||||
method.loadNull(); //the 'this'
|
||||
method.dynamicCall(callNodeType, 2 + loadArgs(args), flags);
|
||||
}
|
||||
|
||||
private void evalCall(final IdentNode node, final int flags) {
|
||||
load(node);
|
||||
method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
|
||||
load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
|
||||
|
||||
final Label not_eval = new Label("not_eval");
|
||||
final Label eval_done = new Label("eval_done");
|
||||
@ -638,8 +700,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
|
||||
// load evaluated code
|
||||
load(evalArgs.getCode());
|
||||
method.convert(Type.OBJECT);
|
||||
load(evalArgs.getCode(), Type.OBJECT);
|
||||
// special/extra 'eval' arguments
|
||||
load(evalArgs.getThis());
|
||||
method.load(evalArgs.getLocation());
|
||||
@ -690,13 +751,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterAccessNode(final AccessNode node) {
|
||||
load(node.getBase());
|
||||
method.convert(Type.OBJECT);
|
||||
load(node.getBase(), Type.OBJECT);
|
||||
method.dup();
|
||||
method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
|
||||
method.swap();
|
||||
method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
|
||||
assert method.peekType().equals(callNodeType);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -727,18 +786,17 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterIndexNode(final IndexNode node) {
|
||||
load(node.getBase());
|
||||
method.convert(Type.OBJECT);
|
||||
load(node.getBase(), Type.OBJECT);
|
||||
method.dup();
|
||||
load(node.getIndex());
|
||||
final Type indexType = node.getIndex().getType();
|
||||
if (indexType.isObject() || indexType.isBoolean()) {
|
||||
method.convert(Type.OBJECT); //TODO
|
||||
load(node.getIndex(), Type.OBJECT); //TODO
|
||||
} else {
|
||||
load(node.getIndex());
|
||||
}
|
||||
method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
|
||||
method.swap();
|
||||
method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
|
||||
assert method.peekType().equals(callNode.getType());
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -746,11 +804,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
@Override
|
||||
protected boolean enterDefault(final Node node) {
|
||||
// Load up function.
|
||||
load(function);
|
||||
method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
|
||||
load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions
|
||||
method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
|
||||
method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
|
||||
assert method.peekType().equals(callNode.getType());
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -853,8 +909,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
final Expression init = forNode.getInit();
|
||||
|
||||
load(modify);
|
||||
assert modify.getType().isObject();
|
||||
load(modify, Type.OBJECT);
|
||||
method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
|
||||
method.store(iter);
|
||||
method._goto(forNode.getContinueLabel());
|
||||
@ -1203,8 +1258,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
if (element == null) {
|
||||
method.loadEmpty(elementType);
|
||||
} else {
|
||||
assert elementType.isEquivalentTo(element.getType()) : "array element type doesn't match array type";
|
||||
load(element);
|
||||
load(element, elementType);
|
||||
}
|
||||
|
||||
method.arraystore();
|
||||
@ -1274,7 +1328,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
// literal values
|
||||
private MethodEmitter load(final LiteralNode<?> node) {
|
||||
private MethodEmitter loadLiteral(final LiteralNode<?> node, final Type type) {
|
||||
final Object value = node.getValue();
|
||||
|
||||
if (value == null) {
|
||||
@ -1294,15 +1348,26 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
} else if (value instanceof Boolean) {
|
||||
method.load((Boolean)value);
|
||||
} else if (value instanceof Integer) {
|
||||
method.load((Integer)value);
|
||||
if(type.isEquivalentTo(Type.NUMBER)) {
|
||||
method.load(((Integer)value).doubleValue());
|
||||
} else if(type.isEquivalentTo(Type.LONG)) {
|
||||
method.load(((Integer)value).longValue());
|
||||
} else {
|
||||
method.load((Integer)value);
|
||||
}
|
||||
} else if (value instanceof Long) {
|
||||
method.load((Long)value);
|
||||
if(type.isEquivalentTo(Type.NUMBER)) {
|
||||
method.load(((Long)value).doubleValue());
|
||||
} else {
|
||||
method.load((Long)value);
|
||||
}
|
||||
} else if (value instanceof Double) {
|
||||
method.load((Double)value);
|
||||
} else if (node instanceof ArrayLiteralNode) {
|
||||
final ArrayType type = (ArrayType)node.getType();
|
||||
loadArray((ArrayLiteralNode)node, type);
|
||||
globalAllocateArray(type);
|
||||
final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node;
|
||||
final ArrayType atype = arrayLiteral.getArrayType();
|
||||
loadArray(arrayLiteral, atype);
|
||||
globalAllocateArray(atype);
|
||||
} else {
|
||||
assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value;
|
||||
}
|
||||
@ -1346,8 +1411,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
|
||||
return enterLiteralNode(literalNode, literalNode.getType());
|
||||
}
|
||||
|
||||
private boolean enterLiteralNode(final LiteralNode<?> literalNode, final Type type) {
|
||||
assert literalNode.getSymbol() != null : literalNode + " has no symbol";
|
||||
load(literalNode).store(literalNode.getSymbol());
|
||||
loadLiteral(literalNode, type).convert(type).store(literalNode.getSymbol());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1622,10 +1691,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return enterCmp(lhs, rhs, Condition.GT, type, symbol);
|
||||
case ADD:
|
||||
Type widest = Type.widest(lhs.getType(), rhs.getType());
|
||||
load(lhs);
|
||||
method.convert(widest);
|
||||
load(rhs);
|
||||
method.convert(widest);
|
||||
load(lhs, widest);
|
||||
load(rhs, widest);
|
||||
method.add();
|
||||
method.convert(type);
|
||||
method.store(symbol);
|
||||
@ -1638,15 +1705,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final Expression arg : args) {
|
||||
load(arg).convert(Type.OBJECT); //TODO this should not be necessary below Lower
|
||||
load(arg).convert(Type.OBJECT);
|
||||
}
|
||||
|
||||
method.invokestatic(
|
||||
@ -1903,24 +1970,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
method.lookupswitch(defaultLabel, ints, labels);
|
||||
}
|
||||
} else {
|
||||
load(expression);
|
||||
|
||||
if (expression.getType().isInteger()) {
|
||||
method.convert(Type.NUMBER).dup();
|
||||
method.store(tag);
|
||||
method.conditionalJump(Condition.NE, true, defaultLabel);
|
||||
} else {
|
||||
assert tag.getSymbolType().isObject();
|
||||
method.convert(Type.OBJECT); //e.g. 1 literal pushed and tag is object
|
||||
method.store(tag);
|
||||
}
|
||||
load(expression, Type.OBJECT);
|
||||
method.store(tag);
|
||||
|
||||
for (final CaseNode caseNode : cases) {
|
||||
final Expression test = caseNode.getTest();
|
||||
|
||||
if (test != null) {
|
||||
method.load(tag);
|
||||
load(test);
|
||||
load(test, Type.OBJECT);
|
||||
method.invoke(ScriptRuntime.EQ_STRICT);
|
||||
method.ifne(caseNode.getEntry());
|
||||
}
|
||||
@ -1961,8 +2019,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final int line = throwNode.getLineNumber();
|
||||
final int column = source.getColumn(position);
|
||||
|
||||
load(expression);
|
||||
assert expression.getType().isObject();
|
||||
load(expression, Type.OBJECT);
|
||||
|
||||
method.load(source.getName());
|
||||
method.load(line);
|
||||
@ -2087,29 +2144,28 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
lineNumber(varNode);
|
||||
|
||||
final Symbol varSymbol = varNode.getName().getSymbol();
|
||||
assert varSymbol != null : "variable node " + varNode + " requires a name with a symbol";
|
||||
final IdentNode identNode = varNode.getName();
|
||||
final Symbol identSymbol = identNode.getSymbol();
|
||||
assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol";
|
||||
|
||||
assert method != null;
|
||||
|
||||
final boolean needsScope = varSymbol.isScope();
|
||||
final boolean needsScope = identSymbol.isScope();
|
||||
if (needsScope) {
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
}
|
||||
load(init);
|
||||
|
||||
if (needsScope) {
|
||||
load(init);
|
||||
int flags = CALLSITE_SCOPE | getCallSiteFlags();
|
||||
final IdentNode identNode = varNode.getName();
|
||||
final Type type = identNode.getType();
|
||||
if (isFastScope(varSymbol)) {
|
||||
storeFastScopeVar(type, varSymbol, flags);
|
||||
if (isFastScope(identSymbol)) {
|
||||
storeFastScopeVar(identSymbol, flags);
|
||||
} else {
|
||||
method.dynamicSet(type, identNode.getName(), flags);
|
||||
method.dynamicSet(identNode.getName(), flags);
|
||||
}
|
||||
} else {
|
||||
method.convert(varNode.getName().getType()); // aw: convert moved here
|
||||
method.store(varSymbol);
|
||||
load(init, identNode.getType());
|
||||
method.store(identSymbol);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -2168,8 +2224,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
tryLabel = null;
|
||||
}
|
||||
|
||||
load(expression);
|
||||
assert expression.getType().isObject() : "with expression needs to be object: " + expression;
|
||||
load(expression, Type.OBJECT);
|
||||
|
||||
if (hasScope) {
|
||||
// Construct a WithObject if we have a scope
|
||||
@ -2211,54 +2266,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterADD(final UnaryNode unaryNode) {
|
||||
load(unaryNode.rhs());
|
||||
assert unaryNode.rhs().getType().isNumber() : unaryNode.rhs().getType() + " "+ unaryNode.getSymbol();
|
||||
load(unaryNode.rhs(), unaryNode.getType());
|
||||
assert unaryNode.getType().isNumeric();
|
||||
method.store(unaryNode.getSymbol());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterBIT_NOT(final UnaryNode unaryNode) {
|
||||
load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol());
|
||||
return false;
|
||||
}
|
||||
|
||||
// do this better with convert calls to method. TODO
|
||||
@Override
|
||||
public boolean enterCONVERT(final UnaryNode unaryNode) {
|
||||
final Expression rhs = unaryNode.rhs();
|
||||
final Type to = unaryNode.getType();
|
||||
|
||||
if (to.isObject() && rhs instanceof LiteralNode) {
|
||||
final LiteralNode<?> literalNode = (LiteralNode<?>)rhs;
|
||||
final Object value = literalNode.getValue();
|
||||
|
||||
if (value instanceof Number) {
|
||||
assert !to.isArray() : "type hygiene - cannot convert number to array: (" + to.getTypeClass().getSimpleName() + ')' + value;
|
||||
if (value instanceof Integer) {
|
||||
method.load((Integer)value);
|
||||
} else if (value instanceof Long) {
|
||||
method.load((Long)value);
|
||||
} else if (value instanceof Double) {
|
||||
method.load((Double)value);
|
||||
} else {
|
||||
assert false;
|
||||
}
|
||||
method.convert(Type.OBJECT);
|
||||
} else if (value instanceof Boolean) {
|
||||
method.getField(staticField(Boolean.class, value.toString().toUpperCase(Locale.ENGLISH), Boolean.class));
|
||||
} else {
|
||||
load(rhs);
|
||||
method.convert(unaryNode.getType());
|
||||
}
|
||||
} else {
|
||||
load(rhs);
|
||||
method.convert(unaryNode.getType());
|
||||
}
|
||||
|
||||
method.store(unaryNode.getSymbol());
|
||||
|
||||
load(unaryNode.rhs(), Type.INT).load(-1).xor().store(unaryNode.getSymbol());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2276,9 +2292,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
load(rhs, true);
|
||||
|
||||
method.convert(type);
|
||||
load(rhs, type, true);
|
||||
if (!isPostfix) {
|
||||
if (type.isInteger()) {
|
||||
method.load(isIncrement ? 1 : -1);
|
||||
@ -2344,12 +2358,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
public boolean enterNOT(final UnaryNode unaryNode) {
|
||||
final Expression rhs = unaryNode.rhs();
|
||||
|
||||
load(rhs);
|
||||
load(rhs, Type.BOOLEAN);
|
||||
|
||||
final Label trueLabel = new Label("true");
|
||||
final Label afterLabel = new Label("after");
|
||||
|
||||
method.convert(Type.BOOLEAN);
|
||||
method.ifne(trueLabel);
|
||||
method.load(true);
|
||||
method._goto(afterLabel);
|
||||
@ -2363,8 +2376,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterSUB(final UnaryNode unaryNode) {
|
||||
load(unaryNode.rhs()).neg().store(unaryNode.getSymbol());
|
||||
|
||||
assert unaryNode.getType().isNumeric();
|
||||
load(unaryNode.rhs()).convert(unaryNode.getType()).neg().store(unaryNode.getSymbol());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2377,9 +2390,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private void enterNumericAdd(final Expression lhs, final Expression rhs, final Type type, final Symbol symbol) {
|
||||
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);
|
||||
loadBinaryOperands(lhs, rhs, type);
|
||||
method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack?
|
||||
method.store(symbol);
|
||||
}
|
||||
@ -2393,8 +2404,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
if (type.isNumeric()) {
|
||||
enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol());
|
||||
} else {
|
||||
load(lhs).convert(Type.OBJECT);
|
||||
load(rhs).convert(Type.OBJECT);
|
||||
loadBinaryOperands(binaryNode);
|
||||
method.add();
|
||||
method.store(binaryNode.getSymbol());
|
||||
}
|
||||
@ -2439,13 +2449,16 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
if (!lhsType.isEquivalentTo(rhsType)) {
|
||||
//this is OK if scoped, only locals are wrong
|
||||
assert !(lhs instanceof IdentNode) || lhs.getSymbol().isScope() : new ASTWriter(binaryNode);
|
||||
}
|
||||
|
||||
new Store<BinaryNode>(binaryNode, lhs) {
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
load(rhs);
|
||||
if ((lhs instanceof IdentNode) && !lhs.getSymbol().isScope()) {
|
||||
load(rhs, lhsType);
|
||||
} else {
|
||||
load(rhs);
|
||||
}
|
||||
}
|
||||
}.store();
|
||||
|
||||
@ -2484,8 +2497,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
load(assignNode.lhs(), true).convert(opType);
|
||||
load(assignNode.rhs()).convert(opType);
|
||||
loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), opType, true);
|
||||
op();
|
||||
method.convert(assignNode.getType());
|
||||
}
|
||||
@ -2656,8 +2668,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
protected abstract void op();
|
||||
|
||||
protected void evaluate(final BinaryNode node) {
|
||||
load(node.lhs());
|
||||
load(node.rhs());
|
||||
loadBinaryOperands(node);
|
||||
op();
|
||||
method.store(node.getSymbol());
|
||||
}
|
||||
@ -2739,11 +2750,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final Type widest = Type.widest(lhsType, rhsType);
|
||||
assert widest.isNumeric() || widest.isBoolean() : widest;
|
||||
|
||||
load(lhs);
|
||||
method.convert(widest);
|
||||
load(rhs);
|
||||
method.convert(widest);
|
||||
|
||||
loadBinaryOperands(lhs, rhs, widest);
|
||||
final Label trueLabel = new Label("trueLabel");
|
||||
final Label afterLabel = new Label("skip");
|
||||
|
||||
@ -2861,6 +2868,12 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
@Override
|
||||
public boolean enterSHR(final BinaryNode binaryNode) {
|
||||
new BinaryArith() {
|
||||
@Override
|
||||
protected void evaluate(final BinaryNode node) {
|
||||
loadBinaryOperands(node.lhs(), node.rhs(), Type.INT);
|
||||
op();
|
||||
method.store(node.getSymbol());
|
||||
}
|
||||
@Override
|
||||
protected void op() {
|
||||
method.shr();
|
||||
@ -2898,21 +2911,17 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
widest = Type.OBJECT;
|
||||
}
|
||||
|
||||
load(test);
|
||||
assert test.getType().isBoolean() : "lhs in ternary must be boolean";
|
||||
|
||||
load(test, Type.BOOLEAN);
|
||||
// we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17
|
||||
// will left as (Object)x=55 : (Object)17 by Lower. Then the first term can be {I}x=55 of type int, which breaks the
|
||||
// symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions
|
||||
// too early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to
|
||||
// do this property. Then we never need any conversions in CodeGenerator
|
||||
method.ifeq(falseLabel);
|
||||
load(trueExpr);
|
||||
method.convert(widest);
|
||||
load(trueExpr, widest);
|
||||
method._goto(exitLabel);
|
||||
method.label(falseLabel);
|
||||
load(falseExpr);
|
||||
method.convert(widest);
|
||||
load(falseExpr, widest);
|
||||
method.label(exitLabel);
|
||||
method.store(symbol);
|
||||
|
||||
@ -3044,8 +3053,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final BaseNode baseNode = (BaseNode)target;
|
||||
final Expression base = baseNode.getBase();
|
||||
|
||||
load(base);
|
||||
method.convert(Type.OBJECT);
|
||||
load(base, Type.OBJECT);
|
||||
depth += Type.OBJECT.getSlots();
|
||||
|
||||
if (isSelfModifying()) {
|
||||
@ -3064,10 +3072,11 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
enterBaseNode();
|
||||
|
||||
final Expression index = node.getIndex();
|
||||
// could be boolean here as well
|
||||
load(index);
|
||||
if (!index.getType().isNumeric()) {
|
||||
method.convert(Type.OBJECT);
|
||||
// could be boolean here as well
|
||||
load(index, Type.OBJECT);
|
||||
} else {
|
||||
load(index);
|
||||
}
|
||||
depth += index.getType().getSlots();
|
||||
|
||||
@ -3136,33 +3145,24 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* need to do a conversion on non-equivalent types exists, but is
|
||||
* very rare. See for example test/script/basic/access-specializer.js
|
||||
*/
|
||||
method.convert(target.getType());
|
||||
|
||||
target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
protected boolean enterDefault(Node node) {
|
||||
throw new AssertionError("Unexpected node " + node + " in store epilogue");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterUnaryNode(final UnaryNode node) {
|
||||
if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
|
||||
method.convert(node.rhs().getType());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterIdentNode(final IdentNode node) {
|
||||
final Symbol symbol = node.getSymbol();
|
||||
assert symbol != null;
|
||||
if (symbol.isScope()) {
|
||||
if (isFastScope(symbol)) {
|
||||
storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
|
||||
storeFastScopeVar(symbol, CALLSITE_SCOPE | getCallSiteFlags());
|
||||
} else {
|
||||
method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
|
||||
method.dynamicSet(node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
|
||||
}
|
||||
} else {
|
||||
method.convert(node.getType());
|
||||
method.store(symbol);
|
||||
}
|
||||
return false;
|
||||
@ -3171,7 +3171,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public boolean enterAccessNode(final AccessNode node) {
|
||||
method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
|
||||
method.dynamicSet(node.getProperty().getName(), getCallSiteFlags());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ package jdk.nashorn.internal.codegen;
|
||||
/**
|
||||
* Used to track split class compilation.
|
||||
*/
|
||||
public class CompileUnit {
|
||||
public class CompileUnit implements Comparable<CompileUnit> {
|
||||
/** Current class name */
|
||||
private final String className;
|
||||
|
||||
@ -116,4 +116,9 @@ public class CompileUnit {
|
||||
public String toString() {
|
||||
return "[classname=" + className + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CompileUnit o) {
|
||||
return className.compareTo(o.className);
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
|
||||
|
||||
import jdk.nashorn.internal.ir.TemporarySymbols;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.AccessController;
|
||||
@ -48,18 +46,20 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.logging.Level;
|
||||
import jdk.internal.dynalink.support.NameCodec;
|
||||
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.TemporarySymbols;
|
||||
import jdk.nashorn.internal.ir.debug.ClassHistogramElement;
|
||||
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
|
||||
import jdk.nashorn.internal.runtime.CodeInstaller;
|
||||
@ -256,8 +256,8 @@ public final class Compiler {
|
||||
this.sequence = sequence;
|
||||
this.installer = installer;
|
||||
this.constantData = new ConstantData();
|
||||
this.compileUnits = new HashSet<>();
|
||||
this.bytecode = new HashMap<>();
|
||||
this.compileUnits = new TreeSet<>();
|
||||
this.bytecode = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
private void initCompiler(final FunctionNode functionNode) {
|
||||
|
@ -28,49 +28,22 @@ package jdk.nashorn.internal.codegen;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.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.CaseNode;
|
||||
import jdk.nashorn.internal.ir.CatchNode;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
import jdk.nashorn.internal.ir.ExpressionStatement;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.IfNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LiteralNode;
|
||||
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
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.TemporarySymbols;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Lower to more primitive operations. After lowering, an AST has symbols and
|
||||
@ -97,272 +70,32 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCallNode(final CallNode callNode) {
|
||||
// AccessSpecializer - call return type may change the access for this location
|
||||
final Node function = callNode.getFunction();
|
||||
if (function instanceof FunctionNode) {
|
||||
return setTypeOverride(callNode, ((FunctionNode)function).getReturnType());
|
||||
}
|
||||
return callNode;
|
||||
}
|
||||
|
||||
private Node leaveUnary(final UnaryNode unaryNode) {
|
||||
return unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType()));
|
||||
}
|
||||
|
||||
@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) {
|
||||
return specialize(unaryNode).node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveNEW(final UnaryNode unaryNode) {
|
||||
assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject();
|
||||
return unaryNode.setRHS(((CallNode)unaryNode.rhs()).setIsNew());
|
||||
}
|
||||
|
||||
@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 Expression leaveADD(final BinaryNode binaryNode) {
|
||||
final Expression lhs = binaryNode.lhs();
|
||||
final Expression rhs = binaryNode.rhs();
|
||||
|
||||
final Type type = binaryNode.getType();
|
||||
|
||||
if (type.isObject()) {
|
||||
if (!isAddString(binaryNode)) {
|
||||
return new RuntimeNode(binaryNode, Request.ADD);
|
||||
}
|
||||
public Node leaveForNode(final ForNode forNode) {
|
||||
if (forNode.isForIn()) {
|
||||
return forNode;
|
||||
}
|
||||
|
||||
return binaryNode.setLHS(convert(lhs, type)).setRHS(convert(rhs, type));
|
||||
}
|
||||
final Expression init = forNode.getInit();
|
||||
final Expression test = forNode.getTest();
|
||||
final Expression modify = forNode.getModify();
|
||||
|
||||
@Override
|
||||
public Node leaveAND(final BinaryNode binaryNode) {
|
||||
return binaryNode;
|
||||
}
|
||||
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction();
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN(final BinaryNode binaryNode) {
|
||||
final SpecializedNode specialized = specialize(binaryNode);
|
||||
final BinaryNode specBinaryNode = (BinaryNode)specialized.node;
|
||||
Type destType = specialized.type;
|
||||
if (destType == null) {
|
||||
destType = specBinaryNode.getType();
|
||||
}
|
||||
// Register assignments to this object in case this is used as constructor
|
||||
if (binaryNode.lhs() instanceof AccessNode) {
|
||||
AccessNode accessNode = (AccessNode) binaryNode.lhs();
|
||||
|
||||
if (accessNode.getBase().getSymbol().isThis()) {
|
||||
lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName());
|
||||
}
|
||||
}
|
||||
return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType));
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
private boolean symbolIsInteger(final Expression node) {
|
||||
final Symbol symbol = node.getSymbol();
|
||||
assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + lc.getCurrentFunction().getSource();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBIT_AND(final BinaryNode binaryNode) {
|
||||
assert symbolIsInteger(binaryNode);
|
||||
return leaveBinary(binaryNode, Type.INT, Type.INT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBIT_OR(final BinaryNode binaryNode) {
|
||||
assert symbolIsInteger(binaryNode);
|
||||
return leaveBinary(binaryNode, Type.INT, Type.INT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveBIT_XOR(final BinaryNode binaryNode) {
|
||||
assert symbolIsInteger(binaryNode);
|
||||
return leaveBinary(binaryNode, Type.INT, Type.INT);
|
||||
return forNode.
|
||||
setInit(lc, init == null ? null : discard(init)).
|
||||
setModify(lc, modify == null ? null : discard(modify));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
|
||||
assert binaryNode.getSymbol() != null;
|
||||
final BinaryNode newBinaryNode = binaryNode.setRHS(discard(binaryNode.rhs()));
|
||||
// AccessSpecializer - the type of lhs, which is the remaining value of this node may have changed
|
||||
// in that case, update the node type as well
|
||||
return propagateType(newBinaryNode, newBinaryNode.lhs().getType());
|
||||
return binaryNode.setRHS(discard(binaryNode.rhs()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
|
||||
assert binaryNode.getSymbol() != null;
|
||||
final BinaryNode newBinaryNode = 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
|
||||
return propagateType(newBinaryNode, newBinaryNode.rhs().getType());
|
||||
}
|
||||
|
||||
@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() : "long coercion expected: " + binaryNode.getSymbol();
|
||||
return leaveBinary(binaryNode, Type.INT, Type.INT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSUB(final BinaryNode binaryNode) {
|
||||
return leaveBinaryArith(binaryNode);
|
||||
return binaryNode.setLHS(discard(binaryNode.lhs()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -371,38 +104,12 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCatchNode(final CatchNode catchNode) {
|
||||
final Expression exceptionCondition = catchNode.getExceptionCondition();
|
||||
if (exceptionCondition != null) {
|
||||
return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN));
|
||||
}
|
||||
return catchNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) {
|
||||
temporarySymbols.reuse();
|
||||
return expressionStatement.setExpression(discard(expressionStatement.getExpression()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveForNode(final ForNode forNode) {
|
||||
final Expression init = forNode.getInit();
|
||||
final Expression test = forNode.getTest();
|
||||
final Expression modify = forNode.getModify();
|
||||
|
||||
if (forNode.isForIn()) {
|
||||
return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400
|
||||
}
|
||||
assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction();
|
||||
|
||||
return forNode.
|
||||
setInit(lc, init == null ? null : discard(init)).
|
||||
setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)).
|
||||
setModify(lc, modify == null ? null : discard(modify));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (functionNode.isLazy()) {
|
||||
@ -430,113 +137,6 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
||||
return functionNode.setState(lc, CompilationState.FINALIZED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIfNode(final IfNode ifNode) {
|
||||
return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN));
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean enterLiteralNode(final LiteralNode literalNode) {
|
||||
if (literalNode instanceof ArrayLiteralNode) {
|
||||
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
|
||||
final Expression[] 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((Expression)element.accept(this), elementType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveReturnNode(final ReturnNode returnNode) {
|
||||
final Expression expr = returnNode.getExpression();
|
||||
if (expr != null) {
|
||||
return returnNode.setExpression(convert(expr, lc.getCurrentFunction().getReturnType()));
|
||||
}
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
|
||||
final List<Expression> args = runtimeNode.getArgs();
|
||||
for (final Expression arg : args) {
|
||||
assert !arg.getType().isUnknown();
|
||||
}
|
||||
return runtimeNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSwitchNode(final SwitchNode switchNode) {
|
||||
final boolean allInteger = switchNode.getTag().getSymbolType().isInteger();
|
||||
|
||||
if (allInteger) {
|
||||
return switchNode;
|
||||
}
|
||||
|
||||
final Expression expression = switchNode.getExpression();
|
||||
final List<CaseNode> cases = switchNode.getCases();
|
||||
final List<CaseNode> newCases = new ArrayList<>();
|
||||
|
||||
for (final CaseNode caseNode : cases) {
|
||||
final Expression test = caseNode.getTest();
|
||||
newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode);
|
||||
}
|
||||
|
||||
return switchNode.
|
||||
setExpression(lc, convert(expression, Type.OBJECT)).
|
||||
setCases(lc, newCases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveTernaryNode(final TernaryNode ternaryNode) {
|
||||
return ternaryNode.setTest(convert(ternaryNode.getTest(), Type.BOOLEAN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveThrowNode(final ThrowNode throwNode) {
|
||||
return throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveVarNode(final VarNode varNode) {
|
||||
final Expression init = varNode.getInit();
|
||||
if (init != null) {
|
||||
final SpecializedNode specialized = specialize(varNode);
|
||||
final VarNode specVarNode = (VarNode)specialized.node;
|
||||
Type destType = specialized.type;
|
||||
if (destType == null) {
|
||||
destType = specVarNode.getName().getType();
|
||||
}
|
||||
assert specVarNode.getName().hasType() : specVarNode + " doesn't have a type";
|
||||
final Expression convertedInit = convert(init, destType);
|
||||
temporarySymbols.reuse();
|
||||
return specVarNode.setInit(convertedInit);
|
||||
}
|
||||
temporarySymbols.reuse();
|
||||
return varNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveWhileNode(final WhileNode whileNode) {
|
||||
final Expression test = whileNode.getTest();
|
||||
if (test != null) {
|
||||
return whileNode.setTest(lc, convert(test, Type.BOOLEAN));
|
||||
}
|
||||
return whileNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveWithNode(final WithNode withNode) {
|
||||
return withNode.setExpression(lc, convert(withNode.getExpression(), Type.OBJECT));
|
||||
}
|
||||
|
||||
private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) {
|
||||
if (LOG.isEnabled()) {
|
||||
if (!symbol.isScope()) {
|
||||
@ -583,260 +183,6 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Expression lhs = binaryNode.lhs();
|
||||
final Expression 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()) {
|
||||
return new RuntimeNode(binaryNode, request).setIsFinal(finalized);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return binaryNode.setLHS(convert(lhs, widest)).setRHS(convert(rhs, widest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
Node b = binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)).setRHS(convert(binaryNode.rhs(), rhsType));
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* A symbol (and {@link jdk.nashorn.internal.runtime.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<LexicalContext>(new LexicalContext()) {
|
||||
private void setCanBePrimitive(final Symbol symbol) {
|
||||
LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol));
|
||||
symbol.setCanBePrimitive(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterIdentNode(final IdentNode identNode) {
|
||||
if (!exclude.contains(identNode)) {
|
||||
setCanBePrimitive(identNode.getSymbol());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterAccessNode(final AccessNode accessNode) {
|
||||
setCanBePrimitive(accessNode.getProperty().getSymbol());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterIndexNode(final IndexNode indexNode) {
|
||||
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class SpecializedNode {
|
||||
final Node node;
|
||||
final Type type;
|
||||
|
||||
SpecializedNode(Node node, Type type) {
|
||||
this.node = node;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
<T extends Expression> SpecializedNode specialize(final Assignment<T> assignment) {
|
||||
final Node node = ((Node)assignment);
|
||||
final T lhs = assignment.getAssignmentDest();
|
||||
final Expression rhs = assignment.getAssignmentSource();
|
||||
|
||||
if (!canHaveCallSiteType(lhs)) {
|
||||
return new SpecializedNode(node, 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 new SpecializedNode(node, null);
|
||||
}
|
||||
|
||||
final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to));
|
||||
final Node typePropagatedNode;
|
||||
if(newNode instanceof Expression) {
|
||||
typePropagatedNode = propagateType((Expression)newNode, to);
|
||||
} else if(newNode instanceof VarNode) {
|
||||
// VarNode, being a statement, doesn't have its own symbol; it uses the symbol of its name instead.
|
||||
final VarNode varNode = (VarNode)newNode;
|
||||
typePropagatedNode = varNode.setName((IdentNode)propagateType(varNode.getName(), to));
|
||||
} else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
return new SpecializedNode(typePropagatedNode, 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
<T extends Expression> T setTypeOverride(final T node, final Type to) {
|
||||
final Type from = node.getType();
|
||||
if (!node.getType().equals(to)) {
|
||||
LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to);
|
||||
if (!to.isObject() && from.isObject()) {
|
||||
setCanBePrimitive(node, to);
|
||||
}
|
||||
}
|
||||
LOG.info("Type override for lhs in '", node, "' => ", to);
|
||||
return ((TypeOverride<T>)node).setType(temporarySymbols, lc, 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 Expression convert(final Expression 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 + " " + node.getClass() + " has no symbol! " + lc.getCurrentFunction();
|
||||
assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + lc.getCurrentFunction();
|
||||
|
||||
final Type from = node.getType();
|
||||
|
||||
if (Type.areEquivalent(from, to)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (from.isObject() && to.isObject()) {
|
||||
return node;
|
||||
}
|
||||
|
||||
Expression resultNode = node;
|
||||
|
||||
if (node instanceof LiteralNode && !(node instanceof ArrayLiteralNode) && !to.isObject()) {
|
||||
final LiteralNode<?> newNode = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, to).eval();
|
||||
if (newNode != null) {
|
||||
resultNode = newNode;
|
||||
}
|
||||
} else {
|
||||
if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) {
|
||||
assert node instanceof TypeOverride;
|
||||
return setTypeOverride(node, to);
|
||||
}
|
||||
resultNode = new UnaryNode(Token.recast(node.getToken(), TokenType.CONVERT), node);
|
||||
}
|
||||
|
||||
LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'");
|
||||
|
||||
assert !node.isTerminal();
|
||||
|
||||
//This is the only place in this file that can create new temporaries
|
||||
//FinalizeTypes may not introduce ANY node that is not a conversion.
|
||||
return temporarySymbols.ensureSymbol(lc, to, resultNode);
|
||||
}
|
||||
|
||||
private static Expression discard(final Expression node) {
|
||||
if (node.getSymbol() != null) {
|
||||
final UnaryNode discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node);
|
||||
@ -849,82 +195,5 @@ final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> {
|
||||
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 Expression propagateType(final Expression node, final Type to) {
|
||||
Symbol symbol = node.getSymbol();
|
||||
if (symbol.isTemp() && symbol.getSymbolType() != to) {
|
||||
symbol = symbol.setTypeOverrideShared(to, temporarySymbols);
|
||||
LOG.info("Type override for temporary in '", node, "' => ", to);
|
||||
}
|
||||
return node.setSymbol(lc, symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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(token, finish, JSType.toString(value));
|
||||
} else if (type.isBoolean()) {
|
||||
literalNode = LiteralNode.newInstance(token, finish, JSType.toBoolean(value));
|
||||
} else if (type.isInteger()) {
|
||||
literalNode = LiteralNode.newInstance(token, finish, JSType.toInt32(value));
|
||||
} else if (type.isLong()) {
|
||||
literalNode = LiteralNode.newInstance(token, finish, JSType.toLong(value));
|
||||
} else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) {
|
||||
literalNode = LiteralNode.newInstance(token, finish, JSType.toNumber(value));
|
||||
}
|
||||
|
||||
if (literalNode != null) {
|
||||
//inherit literal symbol for attr.
|
||||
literalNode = (LiteralNode<?>)literalNode.setSymbol(lc, parent.getSymbol());
|
||||
}
|
||||
|
||||
return literalNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,6 @@ import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.dynalink.support.NameCodec;
|
||||
import jdk.internal.org.objectweb.asm.Handle;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
@ -1560,7 +1559,7 @@ public class MethodEmitter implements Emitter {
|
||||
MethodEmitter convert(final Type to) {
|
||||
final Type type = peekType().convert(method, to);
|
||||
if (type != null) {
|
||||
if (peekType() != to) {
|
||||
if (!peekType().isEquivalentTo(to)) {
|
||||
debug("convert", peekType(), "->", to);
|
||||
}
|
||||
popType();
|
||||
@ -1790,15 +1789,14 @@ public class MethodEmitter implements Emitter {
|
||||
* @param name name of property
|
||||
* @param flags call site flags
|
||||
*/
|
||||
void dynamicSet(final Type valueType, final String name, final int flags) {
|
||||
void dynamicSet(final String name, final int flags) {
|
||||
debug("dynamic_set", name, peekType());
|
||||
|
||||
Type type = valueType;
|
||||
if (type.isObject() || type.isBoolean()) { //promote strings to objects etc
|
||||
Type type = peekType();
|
||||
if (type.isObject()) { //promote strings to objects etc
|
||||
type = Type.OBJECT;
|
||||
convert(Type.OBJECT); //TODO bad- until we specialize boolean setters,
|
||||
}
|
||||
|
||||
popType(type);
|
||||
popType(Type.SCOPE);
|
||||
|
||||
|
@ -296,11 +296,6 @@ final class WeighNodes extends NodeOperatorVisitor<LexicalContext> {
|
||||
return unaryNodeWeight(unaryNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCONVERT(final UnaryNode unaryNode) {
|
||||
return unaryNodeWeight(unaryNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDECINC(final UnaryNode unaryNode) {
|
||||
return unaryNodeWeight(unaryNode);
|
||||
|
@ -136,8 +136,7 @@ public final class BooleanType extends Type {
|
||||
invokeStatic(method, JSType.TO_LONG);
|
||||
} else if (to.isString()) {
|
||||
invokeStatic(method, VALUE_OF);
|
||||
invokeStatic(method, JSType.TO_PRIMITIVE);
|
||||
invokeStatic(method, JSType.TO_STRING);
|
||||
invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING);
|
||||
} else if (to.isObject()) {
|
||||
invokeStatic(method, VALUE_OF);
|
||||
} else {
|
||||
|
@ -153,8 +153,7 @@ class ObjectType extends Type {
|
||||
} else if (to.isBoolean()) {
|
||||
invokeStatic(method, JSType.TO_BOOLEAN);
|
||||
} else if (to.isString()) {
|
||||
invokeStatic(method, JSType.TO_PRIMITIVE);
|
||||
invokeStatic(method, JSType.TO_STRING);
|
||||
invokeStatic(method, JSType.TO_PRIMITIVE_TO_STRING);
|
||||
} else {
|
||||
assert false : "Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString;
|
||||
}
|
||||
|
@ -441,7 +441,12 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
|
||||
if (type0.isArray() && type1.isArray()) {
|
||||
return ((ArrayType)type0).getElementType() == ((ArrayType)type1).getElementType() ? type0 : Type.OBJECT;
|
||||
} else if (type0.isArray() != type1.isArray()) {
|
||||
return Type.OBJECT; //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense
|
||||
//array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense
|
||||
return Type.OBJECT;
|
||||
} else if (type0.isObject() && type1.isObject() && ((ObjectType)type0).getTypeClass() != ((ObjectType)type1).getTypeClass()) {
|
||||
// Object<type=String> and Object<type=ScriptFunction> will produce Object
|
||||
// TODO: maybe find most specific common superclass?
|
||||
return Type.OBJECT;
|
||||
}
|
||||
return type0.weight() > type1.weight() ? type0 : type1;
|
||||
}
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
|
||||
@ -46,12 +45,12 @@ public final class AccessNode extends BaseNode {
|
||||
* @param property property
|
||||
*/
|
||||
public AccessNode(final long token, final int finish, final Expression base, final IdentNode property) {
|
||||
super(token, finish, base, false, false);
|
||||
super(token, finish, base, false);
|
||||
this.property = property.setIsPropertyName();
|
||||
}
|
||||
|
||||
private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
super(accessNode, base, isFunction, hasCallSiteType);
|
||||
private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction) {
|
||||
super(accessNode, base, isFunction);
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
@ -73,13 +72,6 @@ public final class AccessNode extends BaseNode {
|
||||
public void toString(final StringBuilder sb) {
|
||||
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
|
||||
|
||||
if (hasCallSiteType()) {
|
||||
sb.append('{');
|
||||
final String desc = getType().getDescriptor();
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
|
||||
sb.append('}');
|
||||
}
|
||||
|
||||
if (needsParen) {
|
||||
sb.append('(');
|
||||
}
|
||||
@ -107,21 +99,14 @@ public final class AccessNode extends BaseNode {
|
||||
if (this.base == base) {
|
||||
return this;
|
||||
}
|
||||
return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
|
||||
return new AccessNode(this, base, property, isFunction());
|
||||
}
|
||||
|
||||
private AccessNode setProperty(final IdentNode property) {
|
||||
if (this.property == property) {
|
||||
return this;
|
||||
}
|
||||
return new AccessNode(this, base, property, isFunction(), hasCallSiteType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
|
||||
logTypeChange(type);
|
||||
final AccessNode newAccessNode = (AccessNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts));
|
||||
return new AccessNode(newAccessNode, base, property.setType(ts, lc, type), isFunction(), hasCallSiteType());
|
||||
return new AccessNode(this, base, property, isFunction());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,7 +114,7 @@ public final class AccessNode extends BaseNode {
|
||||
if (isFunction()) {
|
||||
return this;
|
||||
}
|
||||
return new AccessNode(this, base, property, true, hasCallSiteType());
|
||||
return new AccessNode(this, base, property, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,10 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
|
||||
/**
|
||||
@ -38,15 +34,13 @@ import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
* @see IndexNode
|
||||
*/
|
||||
@Immutable
|
||||
public abstract class BaseNode extends Expression implements FunctionCall, TypeOverride<BaseNode> {
|
||||
public abstract class BaseNode extends Expression implements FunctionCall {
|
||||
|
||||
/** Base Node. */
|
||||
protected final Expression base;
|
||||
|
||||
private final boolean isFunction;
|
||||
|
||||
private final boolean hasCallSiteType;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -54,13 +48,11 @@ public abstract class BaseNode extends Expression implements FunctionCall, TypeO
|
||||
* @param finish finish
|
||||
* @param base base node
|
||||
* @param isFunction is this a function
|
||||
* @param hasCallSiteType does this access have a callsite type
|
||||
*/
|
||||
public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction) {
|
||||
super(token, base.getStart(), finish);
|
||||
this.base = base;
|
||||
this.isFunction = isFunction;
|
||||
this.hasCallSiteType = hasCallSiteType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,13 +60,11 @@ public abstract class BaseNode extends Expression implements FunctionCall, TypeO
|
||||
* @param baseNode node to inherit from
|
||||
* @param base base
|
||||
* @param isFunction is this a function
|
||||
* @param hasCallSiteType does this access have a callsite type
|
||||
*/
|
||||
protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction) {
|
||||
super(baseNode);
|
||||
this.base = base;
|
||||
this.isFunction = isFunction;
|
||||
this.hasCallSiteType = hasCallSiteType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,26 +86,4 @@ public abstract class BaseNode extends Expression implements FunctionCall, TypeO
|
||||
*/
|
||||
public abstract BaseNode setIsFunction();
|
||||
|
||||
@Override
|
||||
public boolean canHaveCallSiteType() {
|
||||
return true; //carried by the symbol and always the same nodetype==symboltype
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the access have a call site type override?
|
||||
* @return true if overridden
|
||||
*/
|
||||
protected boolean hasCallSiteType() {
|
||||
return hasCallSiteType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug type change
|
||||
* @param type new type
|
||||
*/
|
||||
protected final void logTypeChange(final Type type) {
|
||||
if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* IR representation for a function call.
|
||||
*/
|
||||
@Immutable
|
||||
public final class CallNode extends LexicalContextExpression implements TypeOverride<CallNode> {
|
||||
|
||||
private final Type type;
|
||||
public final class CallNode extends LexicalContextExpression {
|
||||
|
||||
/** Function identifier or function body. */
|
||||
private final Expression function;
|
||||
@ -150,18 +148,16 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
this.function = function;
|
||||
this.args = args;
|
||||
this.flags = 0;
|
||||
this.type = null;
|
||||
this.evalArgs = null;
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final Type type, final EvalArgs evalArgs) {
|
||||
private CallNode(final CallNode callNode, final Expression function, final List<Expression> args, final int flags, final EvalArgs evalArgs) {
|
||||
super(callNode);
|
||||
this.lineNumber = callNode.lineNumber;
|
||||
this.function = function;
|
||||
this.args = args;
|
||||
this.flags = flags;
|
||||
this.type = type;
|
||||
this.evalArgs = evalArgs;
|
||||
}
|
||||
|
||||
@ -175,29 +171,9 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
if (hasCallSiteType()) {
|
||||
return type;
|
||||
}
|
||||
return function instanceof FunctionNode ? ((FunctionNode)function).getReturnType() : Type.OBJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
|
||||
if (this.type == type) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
}
|
||||
|
||||
private boolean hasCallSiteType() {
|
||||
return this.type != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHaveCallSiteType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
*
|
||||
@ -212,7 +188,6 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
setFunction((Expression)function.accept(visitor)).
|
||||
setArgs(Node.accept(visitor, Expression.class, args)).
|
||||
setFlags(flags).
|
||||
setType(null, lc, type).
|
||||
setEvalArgs(evalArgs == null ?
|
||||
null :
|
||||
evalArgs.setCode((Expression)evalArgs.getCode().accept(visitor)).
|
||||
@ -229,13 +204,6 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
if (hasCallSiteType()) {
|
||||
sb.append('{');
|
||||
final String desc = getType().getDescriptor();
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? 'O' : getType().getDescriptor());
|
||||
sb.append('}');
|
||||
}
|
||||
|
||||
function.toString(sb);
|
||||
|
||||
sb.append('(');
|
||||
@ -271,7 +239,7 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
if (this.args == args) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
return new CallNode(this, function, args, flags, evalArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -293,7 +261,7 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
if (this.evalArgs == evalArgs) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
return new CallNode(this, function, args, flags, evalArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -321,7 +289,7 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
if (this.function == function) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
return new CallNode(this, function, args, flags, evalArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,6 +312,6 @@ public final class CallNode extends LexicalContextExpression implements TypeOver
|
||||
if (this.flags == flags) {
|
||||
return this;
|
||||
}
|
||||
return new CallNode(this, function, args, flags, type, evalArgs);
|
||||
return new CallNode(this, function, args, flags, evalArgs);
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,7 @@ package jdk.nashorn.internal.ir;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__DIR__;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__;
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
@ -39,7 +37,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
* IR representation for an identifier.
|
||||
*/
|
||||
@Immutable
|
||||
public final class IdentNode extends Expression implements PropertyKey, TypeOverride<IdentNode>, FunctionCall {
|
||||
public final class IdentNode extends Expression implements PropertyKey, FunctionCall {
|
||||
private static final int PROPERTY_NAME = 1 << 0;
|
||||
private static final int INITIALIZED_HERE = 1 << 1;
|
||||
private static final int FUNCTION = 1 << 2;
|
||||
@ -101,19 +99,6 @@ public final class IdentNode extends Expression implements PropertyKey, TypeOver
|
||||
return callSiteType != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
|
||||
// do NOT, repeat NOT touch the symbol here. it might be a local variable or whatever. This is the override if it isn't
|
||||
if (this.callSiteType == type) {
|
||||
return this;
|
||||
}
|
||||
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", type, " instead of ", getType());
|
||||
}
|
||||
|
||||
return new IdentNode(this, name, type, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assist in IR navigation.
|
||||
*
|
||||
@ -153,31 +138,6 @@ public final class IdentNode extends Expression implements PropertyKey, TypeOver
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* We can only override type if the symbol lives in the scope, as otherwise
|
||||
* it is strongly determined by the local variable already allocated.
|
||||
*
|
||||
* <p>We also return true if the symbol represents the return value of a function with a
|
||||
* non-generic return type as in this case we need to propagate the type instead of
|
||||
* converting to object, for example if the symbol is used as the left hand side of an
|
||||
* assignment such as in the code below.</p>
|
||||
*
|
||||
* <pre>
|
||||
* try {
|
||||
* return 2;
|
||||
* } finally {
|
||||
* return 3;
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return true if can have callsite type
|
||||
*/
|
||||
@Override
|
||||
public boolean canHaveCallSiteType() {
|
||||
return getSymbol() != null && (getSymbol().isScope() || getSymbol().isNonGenericReturn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this IdentNode is a property name
|
||||
* @return true if this is a property name
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
|
||||
@ -46,12 +45,12 @@ public final class IndexNode extends BaseNode {
|
||||
* @param index index for access
|
||||
*/
|
||||
public IndexNode(final long token, final int finish, final Expression base, final Expression index) {
|
||||
super(token, finish, base, false, false);
|
||||
super(token, finish, base, false);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction, final boolean hasCallSiteType) {
|
||||
super(indexNode, base, isFunction, hasCallSiteType);
|
||||
private IndexNode(final IndexNode indexNode, final Expression base, final Expression index, final boolean isFunction) {
|
||||
super(indexNode, base, isFunction);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@ -69,13 +68,6 @@ public final class IndexNode extends BaseNode {
|
||||
public void toString(final StringBuilder sb) {
|
||||
final boolean needsParen = tokenType().needsParens(base.tokenType(), true);
|
||||
|
||||
if (hasCallSiteType()) {
|
||||
sb.append('{');
|
||||
final String desc = getType().getDescriptor();
|
||||
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
|
||||
sb.append('}');
|
||||
}
|
||||
|
||||
if (needsParen) {
|
||||
sb.append('(');
|
||||
}
|
||||
@ -103,7 +95,7 @@ public final class IndexNode extends BaseNode {
|
||||
if (this.base == base) {
|
||||
return this;
|
||||
}
|
||||
return new IndexNode(this, base, index, isFunction(), hasCallSiteType());
|
||||
return new IndexNode(this, base, index, isFunction());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +107,7 @@ public final class IndexNode extends BaseNode {
|
||||
if(this.index == index) {
|
||||
return this;
|
||||
}
|
||||
return new IndexNode(this, base, index, isFunction(), hasCallSiteType());
|
||||
return new IndexNode(this, base, index, isFunction());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,14 +115,7 @@ public final class IndexNode extends BaseNode {
|
||||
if (isFunction()) {
|
||||
return this;
|
||||
}
|
||||
return new IndexNode(this, base, index, true, hasCallSiteType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
|
||||
logTypeChange(type);
|
||||
final IndexNode newIndexNode = (IndexNode)setSymbol(lc, getSymbol().setTypeOverrideShared(type, ts));
|
||||
return new IndexNode(newIndexNode, base, index, isFunction(), true);
|
||||
return new IndexNode(this, base, index, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,10 +28,13 @@ package jdk.nashorn.internal.ir;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompileUnit;
|
||||
import jdk.nashorn.internal.codegen.types.ArrayType;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.objects.NativeArray;
|
||||
import jdk.nashorn.internal.parser.Lexer.LexerToken;
|
||||
import jdk.nashorn.internal.parser.Token;
|
||||
import jdk.nashorn.internal.parser.TokenType;
|
||||
@ -526,12 +529,6 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
return object;
|
||||
} else if (object instanceof LiteralNode) {
|
||||
return objectAsConstant(((LiteralNode<?>)object).getValue());
|
||||
} else if (object instanceof UnaryNode) {
|
||||
final UnaryNode unaryNode = (UnaryNode)object;
|
||||
|
||||
if (unaryNode.isTokenType(TokenType.CONVERT) && unaryNode.getType().isObject()) {
|
||||
return objectAsConstant(unaryNode.rhs());
|
||||
}
|
||||
}
|
||||
|
||||
return POSTSET_MARKER;
|
||||
@ -782,8 +779,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
public ArrayType getArrayType() {
|
||||
if (elementType.isInteger()) {
|
||||
return Type.INT_ARRAY;
|
||||
} else if (elementType.isLong()) {
|
||||
@ -795,6 +791,11 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.typeFor(NativeArray.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element type of this array literal
|
||||
* @return element type
|
||||
|
@ -38,7 +38,7 @@ import jdk.nashorn.internal.parser.TokenType;
|
||||
* IR representation for a runtime call.
|
||||
*/
|
||||
@Immutable
|
||||
public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode> {
|
||||
public class RuntimeNode extends Expression {
|
||||
|
||||
/**
|
||||
* Request enum used for meta-information about the runtime request
|
||||
@ -158,6 +158,36 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a runtime node request type for a node
|
||||
* @param node the node
|
||||
* @return request type
|
||||
*/
|
||||
public static Request requestFor(final Node node) {
|
||||
assert node.isComparison();
|
||||
switch (node.tokenType()) {
|
||||
case EQ_STRICT:
|
||||
return Request.EQ_STRICT;
|
||||
case NE_STRICT:
|
||||
return Request.NE_STRICT;
|
||||
case EQ:
|
||||
return Request.EQ;
|
||||
case NE:
|
||||
return Request.NE;
|
||||
case LT:
|
||||
return Request.LT;
|
||||
case LE:
|
||||
return Request.LE;
|
||||
case GT:
|
||||
return Request.GT;
|
||||
case GE:
|
||||
return Request.GE;
|
||||
default:
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an EQ or EQ_STRICT?
|
||||
*
|
||||
@ -268,9 +298,6 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode>
|
||||
/** Call arguments. */
|
||||
private final List<Expression> args;
|
||||
|
||||
/** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */
|
||||
private final Type callSiteType;
|
||||
|
||||
/** is final - i.e. may not be removed again, lower in the code pipeline */
|
||||
private final boolean isFinal;
|
||||
|
||||
@ -287,16 +314,14 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode>
|
||||
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
this.callSiteType = null;
|
||||
this.isFinal = false;
|
||||
}
|
||||
|
||||
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final Type callSiteType, final boolean isFinal, final List<Expression> args) {
|
||||
private RuntimeNode(final RuntimeNode runtimeNode, final Request request, final boolean isFinal, final List<Expression> args) {
|
||||
super(runtimeNode);
|
||||
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
this.callSiteType = callSiteType;
|
||||
this.isFinal = isFinal;
|
||||
}
|
||||
|
||||
@ -335,7 +360,6 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode>
|
||||
|
||||
this.request = request;
|
||||
this.args = args;
|
||||
this.callSiteType = null;
|
||||
this.isFinal = false;
|
||||
}
|
||||
|
||||
@ -376,7 +400,7 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode>
|
||||
if (this.isFinal == isFinal) {
|
||||
return this;
|
||||
}
|
||||
return new RuntimeNode(this, request, callSiteType, isFinal, args);
|
||||
return new RuntimeNode(this, request, isFinal, args);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -384,24 +408,7 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode>
|
||||
*/
|
||||
@Override
|
||||
public Type getType() {
|
||||
return hasCallSiteType() ? callSiteType : request.getReturnType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeNode setType(final TemporarySymbols ts, final LexicalContext lc, final Type type) {
|
||||
if (this.callSiteType == type) {
|
||||
return this;
|
||||
}
|
||||
return new RuntimeNode(this, request, type, isFinal, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHaveCallSiteType() {
|
||||
return request == Request.ADD;
|
||||
}
|
||||
|
||||
private boolean hasCallSiteType() {
|
||||
return callSiteType != null;
|
||||
return request.getReturnType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -450,7 +457,7 @@ public class RuntimeNode extends Expression implements TypeOverride<RuntimeNode>
|
||||
if (this.args == args) {
|
||||
return this;
|
||||
}
|
||||
return new RuntimeNode(this, request, callSiteType, isFinal, args);
|
||||
return new RuntimeNode(this, request, isFinal, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
|
||||
/**
|
||||
* A type override makes it possible to change the return type of a node, if we know
|
||||
* that the linker can provide it directly. For example, an identity node that is
|
||||
* in the scope, can very well look like an object to the compiler of the method it
|
||||
* is in, but if someone does (int)x, it make senses to ask for it directly
|
||||
* with an int getter instead of loading it as an object and explicitly converting it
|
||||
* by using JSType.toInt32. Especially in scenarios where the field is already stored
|
||||
* as a primitive, this will be much faster than the "object is all I see" scope
|
||||
* available in the method
|
||||
* @param <T> the type of the node implementing the interface
|
||||
*/
|
||||
|
||||
public interface TypeOverride<T extends Node> {
|
||||
/**
|
||||
* Set the override type
|
||||
*
|
||||
* @param ts temporary symbols
|
||||
* @param lc the current lexical context
|
||||
* @param type the type
|
||||
* @return a node equivalent to this one except for the requested change.
|
||||
*/
|
||||
public T setType(final TemporarySymbols ts, final LexicalContext lc, final Type type);
|
||||
|
||||
/**
|
||||
* Returns true if this node can have a callsite override, e.g. all scope ident nodes
|
||||
* which lead to dynamic getters can have it, local variable nodes (slots) can't.
|
||||
* Call nodes can have it unconditionally and so on
|
||||
*
|
||||
* @return true if it is possible to assign a type override to this node
|
||||
*/
|
||||
public boolean canHaveCallSiteType();
|
||||
|
||||
}
|
@ -26,7 +26,6 @@
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.parser.TokenType.BIT_NOT;
|
||||
import static jdk.nashorn.internal.parser.TokenType.CONVERT;
|
||||
import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
|
||||
import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
|
||||
|
||||
@ -150,19 +149,10 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
final TokenType type = tokenType();
|
||||
final String name = type.getName();
|
||||
final boolean isPostfix = type == DECPOSTFIX || type == INCPOSTFIX;
|
||||
final boolean isConvert = type == CONVERT && getSymbol() != null;
|
||||
|
||||
boolean rhsParen = type.needsParens(rhs().tokenType(), false);
|
||||
int convertPos = 0;
|
||||
|
||||
if (isConvert) {
|
||||
convertPos = sb.length();
|
||||
sb.append("(");
|
||||
sb.append(getType());
|
||||
sb.append(")(");
|
||||
}
|
||||
|
||||
if (!isPostfix && !isConvert) {
|
||||
if (!isPostfix) {
|
||||
if (name == null) {
|
||||
sb.append(type.name());
|
||||
rhsParen = true;
|
||||
@ -186,16 +176,6 @@ public final class UnaryNode extends Expression implements Assignment<Expression
|
||||
if (isPostfix) {
|
||||
sb.append(type == DECPOSTFIX ? "--" : "++");
|
||||
}
|
||||
|
||||
if (isConvert) {
|
||||
// strip extra cast parenthesis which makes the printout harder to read
|
||||
final boolean endsWithParenthesis = sb.charAt(sb.length() - 1) == ')';
|
||||
if (!endsWithParenthesis) {
|
||||
sb.append(')');
|
||||
} else {
|
||||
sb.setCharAt(convertPos, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,8 +51,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T
|
||||
return enterADD(unaryNode);
|
||||
case BIT_NOT:
|
||||
return enterBIT_NOT(unaryNode);
|
||||
case CONVERT:
|
||||
return enterCONVERT(unaryNode);
|
||||
case DELETE:
|
||||
return enterDELETE(unaryNode);
|
||||
case DISCARD:
|
||||
@ -84,8 +82,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T
|
||||
return leaveADD(unaryNode);
|
||||
case BIT_NOT:
|
||||
return leaveBIT_NOT(unaryNode);
|
||||
case CONVERT:
|
||||
return leaveCONVERT(unaryNode);
|
||||
case DELETE:
|
||||
return leaveDELETE(unaryNode);
|
||||
case DISCARD:
|
||||
@ -322,26 +318,6 @@ public class NodeOperatorVisitor<T extends LexicalContext> extends NodeVisitor<T
|
||||
return leaveDefault(unaryNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unary enter - callback for entering a conversion
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return true if traversal should continue and node children be traversed, false otherwise
|
||||
*/
|
||||
public boolean enterCONVERT(final UnaryNode unaryNode) {
|
||||
return enterDefault(unaryNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unary leave - callback for leaving a conversion
|
||||
*
|
||||
* @param unaryNode the node
|
||||
* @return processed node, which will replace the original one, or the original node
|
||||
*/
|
||||
public Node leaveCONVERT(final UnaryNode unaryNode) {
|
||||
return leaveDefault(unaryNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unary enter - callback for entering a ++ or -- operator
|
||||
*
|
||||
|
@ -178,7 +178,6 @@ public enum TokenType {
|
||||
ARRAY (LITERAL, null),
|
||||
|
||||
COMMALEFT (IR, null),
|
||||
CONVERT (IR, null),
|
||||
DISCARD (IR, null),
|
||||
DECPOSTFIX (IR, null),
|
||||
INCPOSTFIX (IR, null);
|
||||
|
@ -625,11 +625,11 @@ public final class Context {
|
||||
* @param clazz Class object
|
||||
* @throw SecurityException if not accessible
|
||||
*/
|
||||
public static void checkPackageAccess(final Class clazz) {
|
||||
public static void checkPackageAccess(final Class<?> clazz) {
|
||||
final SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
Class bottomClazz = clazz;
|
||||
while(bottomClazz.isArray()) {
|
||||
Class<?> bottomClazz = clazz;
|
||||
while (bottomClazz.isArray()) {
|
||||
bottomClazz = bottomClazz.getComponentType();
|
||||
}
|
||||
checkPackageAccess(sm, bottomClazz.getName());
|
||||
@ -664,7 +664,7 @@ public final class Context {
|
||||
* @param clazz Class object
|
||||
* @return true if package is accessible, false otherwise
|
||||
*/
|
||||
private static boolean isAccessiblePackage(final Class clazz) {
|
||||
private static boolean isAccessiblePackage(final Class<?> clazz) {
|
||||
try {
|
||||
checkPackageAccess(clazz);
|
||||
return true;
|
||||
@ -838,7 +838,7 @@ public final class Context {
|
||||
return Context.getContextTrusted();
|
||||
}
|
||||
|
||||
private URL getResourceURL(final String resName) throws IOException {
|
||||
private URL getResourceURL(final String resName) {
|
||||
// try the classPathLoader if we have and then
|
||||
// try the appLoader if non-null.
|
||||
if (classPathLoader != null) {
|
||||
|
@ -104,14 +104,11 @@ public enum JSType {
|
||||
/** JavaScript compliant conversion function from number to int64 */
|
||||
public static final Call TO_INT64_D = staticCall(myLookup, JSType.class, "toInt64", long.class, double.class);
|
||||
|
||||
/** JavaScript compliant conversion function from Object to String */
|
||||
public static final Call TO_STRING = staticCall(myLookup, JSType.class, "toString", String.class, Object.class);
|
||||
|
||||
/** JavaScript compliant conversion function from number to String */
|
||||
public static final Call TO_STRING_D = staticCall(myLookup, JSType.class, "toString", String.class, double.class);
|
||||
|
||||
/** JavaScript compliant conversion function from Object to primitive */
|
||||
public static final Call TO_PRIMITIVE = staticCall(myLookup, JSType.class, "toPrimitive", Object.class, Object.class);
|
||||
/** Combined call to toPrimitive followed by toString. */
|
||||
public static final Call TO_PRIMITIVE_TO_STRING = staticCall(myLookup, JSType.class, "toPrimitiveToString", String.class, Object.class);
|
||||
|
||||
private static final double INT32_LIMIT = 4294967296.0;
|
||||
|
||||
@ -272,6 +269,17 @@ public enum JSType {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines a hintless toPrimitive and a toString call.
|
||||
*
|
||||
* @param obj an object
|
||||
*
|
||||
* @return the string form of the primitive form of the object
|
||||
*/
|
||||
public static String toPrimitiveToString(Object obj) {
|
||||
return toString(toPrimitive(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript compliant conversion of number to boolean
|
||||
*
|
||||
@ -874,7 +882,7 @@ public enum JSType {
|
||||
if (obj instanceof ScriptObject) {
|
||||
return convertArray(((ScriptObject)obj).getArray().asObjectArray(), componentType);
|
||||
} else if (obj instanceof JSObject) {
|
||||
final ArrayLikeIterator itr = ArrayLikeIterator.arrayLikeIterator(obj);
|
||||
final ArrayLikeIterator<?> itr = ArrayLikeIterator.arrayLikeIterator(obj);
|
||||
final int len = (int) itr.getLength();
|
||||
final Object[] res = new Object[len];
|
||||
int idx = 0;
|
||||
|
@ -77,4 +77,4 @@ class JavaArrayIterator extends ArrayLikeIterator<Object> {
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,4 +55,4 @@ final class ReverseJavaArrayIterator extends JavaArrayIterator {
|
||||
protected long bumpIndex() {
|
||||
return index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
|
||||
@SuppressWarnings("unused")
|
||||
private static Object get(final Object jsobj, final Object key) {
|
||||
if (key instanceof Integer) {
|
||||
return ((JSObject)jsobj).getSlot((int)(Integer)key);
|
||||
return ((JSObject)jsobj).getSlot((Integer)key);
|
||||
} else if (key instanceof Number) {
|
||||
final int index = getIndex((Number)key);
|
||||
if (index > -1) {
|
||||
@ -155,7 +155,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
|
||||
@SuppressWarnings("unused")
|
||||
private static void put(final Object jsobj, final Object key, final Object value) {
|
||||
if (key instanceof Integer) {
|
||||
((JSObject)jsobj).setSlot((int)(Integer)key, value);
|
||||
((JSObject)jsobj).setSlot((Integer)key, value);
|
||||
} else if (key instanceof Number) {
|
||||
((JSObject)jsobj).setSlot(getIndex((Number)key), value);
|
||||
} else if (key instanceof String) {
|
||||
|
44
nashorn/test/script/basic/JDK-8026137.js
Normal file
44
nashorn/test/script/basic/JDK-8026137.js
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* JDK-8026137: Binary evaluation order in JavaScript is load load
|
||||
* convert convert, not load convert load convert.
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
try {
|
||||
(function f() { Object.defineProperty({},"x",{get: function(){return {valueOf:function(){throw 0}}}}).x - Object.defineProperty({},"x",{get: function(){throw 1}}).x })()
|
||||
}
|
||||
catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
try {
|
||||
({valueOf: function(){throw 0}}) - ({valueOf: function(){throw 1}} - 1)
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
2
nashorn/test/script/basic/JDK-8026137.js.EXPECTED
Normal file
2
nashorn/test/script/basic/JDK-8026137.js.EXPECTED
Normal file
@ -0,0 +1,2 @@
|
||||
1
|
||||
1
|
Loading…
Reference in New Issue
Block a user