29 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; |
29 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; |
30 |
30 |
31 import java.util.ArrayList; |
31 import java.util.ArrayList; |
32 import java.util.HashSet; |
32 import java.util.HashSet; |
33 import java.util.List; |
33 import java.util.List; |
|
34 |
34 import jdk.nashorn.internal.codegen.types.Type; |
35 import jdk.nashorn.internal.codegen.types.Type; |
35 import jdk.nashorn.internal.ir.AccessNode; |
36 import jdk.nashorn.internal.ir.AccessNode; |
36 import jdk.nashorn.internal.ir.Assignment; |
37 import jdk.nashorn.internal.ir.Assignment; |
37 import jdk.nashorn.internal.ir.BinaryNode; |
38 import jdk.nashorn.internal.ir.BinaryNode; |
38 import jdk.nashorn.internal.ir.Block; |
39 import jdk.nashorn.internal.ir.Block; |
82 * For any op that we process in FinalizeTypes it is an absolute guarantee |
83 * For any op that we process in FinalizeTypes it is an absolute guarantee |
83 * that scope and slot information is correct. This enables e.g. AccessSpecialization |
84 * that scope and slot information is correct. This enables e.g. AccessSpecialization |
84 * and frame optimizations |
85 * and frame optimizations |
85 */ |
86 */ |
86 |
87 |
87 final class FinalizeTypes extends NodeOperatorVisitor { |
88 final class FinalizeTypes extends NodeOperatorVisitor<LexicalContext> { |
88 |
89 |
89 private static final DebugLogger LOG = new DebugLogger("finalize"); |
90 private static final DebugLogger LOG = new DebugLogger("finalize"); |
90 |
91 |
91 private final TemporarySymbols temporarySymbols; |
92 private final TemporarySymbols temporarySymbols; |
92 |
93 |
93 FinalizeTypes(final TemporarySymbols temporarySymbols) { |
94 FinalizeTypes(final TemporarySymbols temporarySymbols) { |
|
95 super(new LexicalContext()); |
94 this.temporarySymbols = temporarySymbols; |
96 this.temporarySymbols = temporarySymbols; |
95 } |
97 } |
96 |
98 |
97 @Override |
99 @Override |
98 public Node leaveCallNode(final CallNode callNode) { |
100 public Node leaveCallNode(final CallNode callNode) { |
231 return leaveASSIGN(binaryNode); |
233 return leaveASSIGN(binaryNode); |
232 } |
234 } |
233 |
235 |
234 private boolean symbolIsInteger(Node node) { |
236 private boolean symbolIsInteger(Node node) { |
235 final Symbol symbol = node.getSymbol(); |
237 final Symbol symbol = node.getSymbol(); |
236 assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + getLexicalContext().getCurrentFunction().getSource(); |
238 assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + lc.getCurrentFunction().getSource(); |
237 return true; |
239 return true; |
238 } |
240 } |
239 |
241 |
240 @Override |
242 @Override |
241 public Node leaveBIT_AND(final BinaryNode binaryNode) { |
243 public Node leaveBIT_AND(final BinaryNode binaryNode) { |
380 public Node leaveForNode(final ForNode forNode) { |
382 public Node leaveForNode(final ForNode forNode) { |
381 final Node init = forNode.getInit(); |
383 final Node init = forNode.getInit(); |
382 final Node test = forNode.getTest(); |
384 final Node test = forNode.getTest(); |
383 final Node modify = forNode.getModify(); |
385 final Node modify = forNode.getModify(); |
384 |
386 |
385 final LexicalContext lc = getLexicalContext(); |
|
386 |
|
387 if (forNode.isForIn()) { |
387 if (forNode.isForIn()) { |
388 return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 |
388 return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 |
389 } |
389 } |
390 assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getLexicalContext().getCurrentFunction(); |
390 assert test != null || forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + lc.getCurrentFunction(); |
391 |
391 |
392 return forNode. |
392 return forNode. |
393 setInit(lc, init == null ? null : discard(init)). |
393 setInit(lc, init == null ? null : discard(init)). |
394 setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)). |
394 setTest(lc, test == null ? null : convert(test, Type.BOOLEAN)). |
395 setModify(lc, modify == null ? null : discard(modify)); |
395 setModify(lc, modify == null ? null : discard(modify)); |
417 return true; |
417 return true; |
418 } |
418 } |
419 |
419 |
420 @Override |
420 @Override |
421 public Node leaveFunctionNode(final FunctionNode functionNode) { |
421 public Node leaveFunctionNode(final FunctionNode functionNode) { |
422 return functionNode.setState(getLexicalContext(), CompilationState.FINALIZED); |
422 return functionNode.setState(lc, CompilationState.FINALIZED); |
423 } |
423 } |
424 |
424 |
425 @Override |
425 @Override |
426 public Node leaveIfNode(final IfNode ifNode) { |
426 public Node leaveIfNode(final IfNode ifNode) { |
427 return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN)); |
427 return ifNode.setTest(convert(ifNode.getTest(), Type.BOOLEAN)); |
480 final Node test = caseNode.getTest(); |
480 final Node test = caseNode.getTest(); |
481 newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode); |
481 newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode); |
482 } |
482 } |
483 |
483 |
484 return switchNode. |
484 return switchNode. |
485 setExpression(getLexicalContext(), convert(expression, Type.OBJECT)). |
485 setExpression(lc, convert(expression, Type.OBJECT)). |
486 setCases(getLexicalContext(), newCases); |
486 setCases(lc, newCases); |
487 } |
487 } |
488 |
488 |
489 @Override |
489 @Override |
490 public Node leaveTernaryNode(final TernaryNode ternaryNode) { |
490 public Node leaveTernaryNode(final TernaryNode ternaryNode) { |
491 return ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN)); |
491 return ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN)); |
517 |
517 |
518 @Override |
518 @Override |
519 public Node leaveWhileNode(final WhileNode whileNode) { |
519 public Node leaveWhileNode(final WhileNode whileNode) { |
520 final Node test = whileNode.getTest(); |
520 final Node test = whileNode.getTest(); |
521 if (test != null) { |
521 if (test != null) { |
522 return whileNode.setTest(getLexicalContext(), convert(test, Type.BOOLEAN)); |
522 return whileNode.setTest(lc, convert(test, Type.BOOLEAN)); |
523 } |
523 } |
524 return whileNode; |
524 return whileNode; |
525 } |
525 } |
526 |
526 |
527 @Override |
527 @Override |
528 public Node leaveWithNode(final WithNode withNode) { |
528 public Node leaveWithNode(final WithNode withNode) { |
529 return withNode.setExpression(getLexicalContext(), convert(withNode.getExpression(), Type.OBJECT)); |
529 return withNode.setExpression(lc, convert(withNode.getExpression(), Type.OBJECT)); |
530 } |
530 } |
531 |
531 |
532 private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { |
532 private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { |
533 if (LOG.isEnabled()) { |
533 if (LOG.isEnabled()) { |
534 if (!symbol.isScope()) { |
534 if (!symbol.isScope()) { |
548 private void updateSymbols(final Block block) { |
548 private void updateSymbols(final Block block) { |
549 if (!block.needsScope()) { |
549 if (!block.needsScope()) { |
550 return; // nothing to do |
550 return; // nothing to do |
551 } |
551 } |
552 |
552 |
553 final LexicalContext lc = getLexicalContext(); |
|
554 final FunctionNode functionNode = lc.getFunction(block); |
553 final FunctionNode functionNode = lc.getFunction(block); |
555 final boolean allVarsInScope = functionNode.allVarsInScope(); |
554 final boolean allVarsInScope = functionNode.allVarsInScope(); |
556 final boolean isVarArg = functionNode.isVarArg(); |
555 final boolean isVarArg = functionNode.isVarArg(); |
557 |
556 |
558 for (final Symbol symbol : block.getSymbols()) { |
557 for (final Symbol symbol : block.getSymbols()) { |
650 * @param to which primitive type to use for tagging |
649 * @param to which primitive type to use for tagging |
651 */ |
650 */ |
652 private static void setCanBePrimitive(final Node node, final Type to) { |
651 private static void setCanBePrimitive(final Node node, final Type to) { |
653 final HashSet<Node> exclude = new HashSet<>(); |
652 final HashSet<Node> exclude = new HashSet<>(); |
654 |
653 |
655 node.accept(new NodeVisitor() { |
654 node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { |
656 private void setCanBePrimitive(final Symbol symbol) { |
655 private void setCanBePrimitive(final Symbol symbol) { |
657 LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol)); |
656 LOG.info("*** can be primitive symbol ", symbol, " ", Debug.id(symbol)); |
658 symbol.setCanBePrimitive(to); |
657 symbol.setCanBePrimitive(to); |
659 } |
658 } |
660 |
659 |
760 if (!to.isObject() && from.isObject()) { |
759 if (!to.isObject() && from.isObject()) { |
761 setCanBePrimitive(node, to); |
760 setCanBePrimitive(node, to); |
762 } |
761 } |
763 } |
762 } |
764 LOG.info("Type override for lhs in '", node, "' => ", to); |
763 LOG.info("Type override for lhs in '", node, "' => ", to); |
765 return ((TypeOverride<T>)node).setType(temporarySymbols, getLexicalContext(), to); |
764 return ((TypeOverride<T>)node).setType(temporarySymbols, lc, to); |
766 } |
765 } |
767 |
766 |
768 /** |
767 /** |
769 * Add an explicit conversion. This is needed when attribution has created types |
768 * Add an explicit conversion. This is needed when attribution has created types |
770 * that do not mesh into an op type, e.g. a = b, where b is object and a is double |
769 * that do not mesh into an op type, e.g. a = b, where b is object and a is double |
783 * @return conversion node |
782 * @return conversion node |
784 */ |
783 */ |
785 private Node convert(final Node node, final Type to) { |
784 private Node convert(final Node node, final Type to) { |
786 assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass(); |
785 assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass(); |
787 assert node != null : "node is null"; |
786 assert node != null : "node is null"; |
788 assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + getLexicalContext().getCurrentFunction(); |
787 assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + lc.getCurrentFunction(); |
789 assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getLexicalContext().getCurrentFunction(); |
788 assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + lc.getCurrentFunction(); |
790 |
789 |
791 final Type from = node.getType(); |
790 final Type from = node.getType(); |
792 |
791 |
793 if (Type.areEquivalent(from, to)) { |
792 if (Type.areEquivalent(from, to)) { |
794 return node; |
793 return node; |
815 |
814 |
816 LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'"); |
815 LOG.info("CONVERT('", node, "', ", to, ") => '", resultNode, "'"); |
817 |
816 |
818 assert !node.isTerminal(); |
817 assert !node.isTerminal(); |
819 |
818 |
820 final LexicalContext lc = getLexicalContext(); |
|
821 //This is the only place in this file that can create new temporaries |
819 //This is the only place in this file that can create new temporaries |
822 //FinalizeTypes may not introduce ANY node that is not a conversion. |
820 //FinalizeTypes may not introduce ANY node that is not a conversion. |
823 return temporarySymbols.ensureSymbol(lc, to, resultNode); |
821 return temporarySymbols.ensureSymbol(lc, to, resultNode); |
824 } |
822 } |
825 |
823 |
852 Symbol symbol = node.getSymbol(); |
850 Symbol symbol = node.getSymbol(); |
853 if (symbol.isTemp() && symbol.getSymbolType() != to) { |
851 if (symbol.isTemp() && symbol.getSymbolType() != to) { |
854 symbol = symbol.setTypeOverrideShared(to, temporarySymbols); |
852 symbol = symbol.setTypeOverrideShared(to, temporarySymbols); |
855 LOG.info("Type override for temporary in '", node, "' => ", to); |
853 LOG.info("Type override for temporary in '", node, "' => ", to); |
856 } |
854 } |
857 return node.setSymbol(getLexicalContext(), symbol); |
855 return node.setSymbol(lc, symbol); |
858 } |
856 } |
859 |
857 |
860 /** |
858 /** |
861 * Determine if the outcome of + operator is a string. |
859 * Determine if the outcome of + operator is a string. |
862 * |
860 * |
905 literalNode = LiteralNode.newInstance(token, finish, JSType.toNumber(value)); |
903 literalNode = LiteralNode.newInstance(token, finish, JSType.toNumber(value)); |
906 } |
904 } |
907 |
905 |
908 if (literalNode != null) { |
906 if (literalNode != null) { |
909 //inherit literal symbol for attr. |
907 //inherit literal symbol for attr. |
910 literalNode = (LiteralNode<?>)literalNode.setSymbol(getLexicalContext(), parent.getSymbol()); |
908 literalNode = (LiteralNode<?>)literalNode.setSymbol(lc, parent.getSymbol()); |
911 } |
909 } |
912 |
910 |
913 return literalNode; |
911 return literalNode; |
914 } |
912 } |
915 } |
913 } |