Wed, 30 Jan 2013 12:26:45 +0100
8007062: Split Lower up into Lower/Attr/FinalizeTypes. Integrate AccessSpecalizer into FinalizeTypes.
Summary: Lower suffered from being a "God class" trying to do everything at once. As Nashorn code generation has grown, so has Lower. It does several post processing passes, tries to do several things at once even though all type information isn't in place, adjusting state afterwards and so on. It also performs control flow analysis, type attribution and constant folding, and everything else code generation related before byte code emission. I have now separated the compilation process into Lower (create low level nodes from high level ones, copy code such as finally block inlining etc), Attr (assign types and symbols to all nodes - freeze slot and scope information) and FinalizeTypes (insert explicit casts, specialize invoke dynamic types for scope accesses). I've removed the kludgy AccessSpecializer, as this now integrates naturally with typing. Everything is now much easier to read and each module performs only one thing. I have added separate loggers for the separate tiers. In the process I have also fixed: (1) problems with type coercion (see test/script/basic/typecoercion.js, basically our coercion was too late and our symbol inference was erroneous. This only manifested itself in very rare occasions where toNumber coercion has side effects, such as for example when valueOf is overridden) (2) copying literal nodes (literal copy did not use the superclass copy, which made all the Node specific fields not to be copied (3) erroneous literal tokenization (literals shouldn't always just inherit token information from whatever node that creates them) (4) splitter weighnodes - unary nodes were considered weightless (4) removed the hateful and kludgy "VarNode.shouldAppend", which really isn't needed when we have an attribution phase that determines self reference symbols (the only thing it was used for) (5) duplicate line number issues in the parser (6) convert bug in CodeGenerator for intermediate results of scope accesses (see test/script/basic/access-specializer.js) ... Several of these things just stopped being problems with the new architecture "can't happen anymore" and are not bug fixes per se. All tests run. No performance regressions exist that I've been able to measure. Some increases in performance were measured, but in the statistical margin of error (which is very wide as HotSpot currently has warmup issues with LambdaForms/invoke dynamic). Compile speed has not measurably increased.
Reviewed-by: jlaskey, attila
1.1 --- a/docs/DEVELOPER_README Tue Jan 29 14:25:39 2013 -0400 1.2 +++ b/docs/DEVELOPER_README Wed Jan 30 12:26:45 2013 +0100 1.3 @@ -36,16 +36,6 @@ 1.4 equivalent to enabling the access logger with "info" level. 1.5 1.6 1.7 -SYSTEM PROPERTY: -Dnashorn.compiler.ints.disable 1.8 - 1.9 -This flag prevents ints and longs (non double values) from being used 1.10 -for any primitive representation in the lowered IR. This is default 1.11 -false, i.e Lower will attempt to use integer variables as long as it 1.12 -can. For example, var x = 17 would try to use x as an integer, unless 1.13 -other operations occur later that require coercion to wider type, for 1.14 -example x *= 17.1; 1.15 - 1.16 - 1.17 SYSTEM PROPERTY: -Dnashorn.compiler.intarithmetic 1.18 1.19 Arithmetic operations in Nashorn (except bitwise ones) typically 1.20 @@ -195,7 +185,8 @@ 1.21 slots, possibly through code copying and versioning. 1.22 1.23 1.24 -SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=<x> 1.25 +SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=[<x>[,*]], 1.26 + -Dnashorn.compiler.symbol.stacktrace=[<x>[,*]] 1.27 1.28 When this property is set, creation and manipulation of any symbol 1.29 named "x" will show information about when the compiler changes its 1.30 @@ -203,8 +194,16 @@ 1.31 data. This is useful if, for example, a symbol shows up as an Object, 1.32 when you believe it should be a primitive. Usually there is an 1.33 explanation for this, for example that it exists in the global scope 1.34 -and type analysis has to be more conservative. In that case, the stack 1.35 -trace upon type change to object will usually tell us why. 1.36 +and type analysis has to be more conservative. 1.37 + 1.38 +Several symbols names to watch can be specified by comma separation. 1.39 + 1.40 +If no variable name is specified (and no equals sign), all symbols 1.41 +will be watched 1.42 + 1.43 +By using "stacktrace" instead of or together with "trace", stack 1.44 +traces will be displayed upon symbol changes according to the same 1.45 +semantics. 1.46 1.47 1.48 SYSTEM PROPERTY: nashorn.lexer.xmlliterals 1.49 @@ -349,6 +348,9 @@ 1.50 2. The loggers. 1.51 =============== 1.52 1.53 +It is very simple to create your own logger. Use the DebugLogger class 1.54 +and give the subsystem name as a constructor argument. 1.55 + 1.56 The Nashorn loggers can be used to print per-module or per-subsystem 1.57 debug information with different levels of verbosity. The loggers for 1.58 a given subsystem are available are enabled by using 1.59 @@ -382,7 +384,7 @@ 1.60 controlled from the Context. Log messages are, for example, about 1.61 things like new compile units being allocated. The compiler has global 1.62 settings that all the tiers of codegen (e.g. Lower and CodeGenerator) 1.63 -use. 1.64 +use.s 1.65 1.66 1.67 * codegen 1.68 @@ -429,6 +431,18 @@ 1.69 1.70 * lower 1.71 1.72 +This is the first lowering pass. 1.73 + 1.74 +Lower is a code generation pass that turns high level IR nodes into 1.75 +lower level one, for example substituting comparisons to RuntimeNodes 1.76 +and inlining finally blocks. 1.77 + 1.78 +Lower is also responsible for determining control flow information 1.79 +like end points. 1.80 + 1.81 + 1.82 +* attr 1.83 + 1.84 The lowering annotates a FunctionNode with symbols for each identifier 1.85 and transforms high level constructs into lower level ones, that the 1.86 CodeGenerator consumes. 1.87 @@ -439,14 +453,11 @@ 1.88 this logger. This will probably change. 1.89 1.90 1.91 -* access 1.92 +* finalize 1.93 1.94 -The --log=access option is equivalent to setting the system variable 1.95 -"nashorn.callsiteaccess.debug" to true. There are several levels of 1.96 -the access logger, usually the default level "info" is enough 1.97 - 1.98 -It is very simple to create your own logger. Use the DebugLogger class 1.99 -and give the subsystem name as a constructor argument. 1.100 +This --log=finalize log option outputs information for type finalization, 1.101 +the third tier of the compiler. This means things like placement of 1.102 +specialized scope nodes or explicit conversions. 1.103 1.104 1.105 * fields
2.1 --- a/src/jdk/nashorn/api/scripting/Formatter.java Tue Jan 29 14:25:39 2013 -0400 2.2 +++ b/src/jdk/nashorn/api/scripting/Formatter.java Wed Jan 30 12:26:45 2013 +0100 2.3 @@ -48,6 +48,9 @@ 2.4 */ 2.5 public final class Formatter { 2.6 2.7 + private Formatter() { 2.8 + } 2.9 + 2.10 /** 2.11 * Method which converts javascript types to java types for the 2.12 * String.format method (jrunscript function sprintf). 2.13 @@ -149,7 +152,9 @@ 2.14 if (s != null) { 2.15 try { 2.16 index = Integer.parseInt(s.substring(0, s.length() - 1)); 2.17 - } catch (NumberFormatException e) { } 2.18 + } catch (final NumberFormatException e) { 2.19 + //ignored 2.20 + } 2.21 } else { 2.22 index = 0; 2.23 }
3.1 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Tue Jan 29 14:25:39 2013 -0400 3.2 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed Jan 30 12:26:45 2013 +0100 3.3 @@ -327,6 +327,7 @@ 3.4 try { 3.5 final InputStream is = AccessController.doPrivileged( 3.6 new PrivilegedExceptionAction<InputStream>() { 3.7 + @Override 3.8 public InputStream run() throws Exception { 3.9 final URL url = NashornScriptEngine.class.getResource(script); 3.10 return url.openStream();
4.1 --- a/src/jdk/nashorn/internal/codegen/AccessSpecializer.java Tue Jan 29 14:25:39 2013 -0400 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,399 +0,0 @@ 4.4 -/* 4.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 4.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 - * 4.8 - * This code is free software; you can redistribute it and/or modify it 4.9 - * under the terms of the GNU General Public License version 2 only, as 4.10 - * published by the Free Software Foundation. Oracle designates this 4.11 - * particular file as subject to the "Classpath" exception as provided 4.12 - * by Oracle in the LICENSE file that accompanied this code. 4.13 - * 4.14 - * This code is distributed in the hope that it will be useful, but WITHOUT 4.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.16 - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.17 - * version 2 for more details (a copy is included in the LICENSE file that 4.18 - * accompanied this code). 4.19 - * 4.20 - * You should have received a copy of the GNU General Public License version 4.21 - * 2 along with this work; if not, write to the Free Software Foundation, 4.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.23 - * 4.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.25 - * or visit www.oracle.com if you need additional information or have any 4.26 - * questions. 4.27 - */ 4.28 - 4.29 -package jdk.nashorn.internal.codegen; 4.30 - 4.31 -import java.util.HashSet; 4.32 -import jdk.nashorn.internal.codegen.types.Type; 4.33 -import jdk.nashorn.internal.ir.AccessNode; 4.34 -import jdk.nashorn.internal.ir.Assignment; 4.35 -import jdk.nashorn.internal.ir.BinaryNode; 4.36 -import jdk.nashorn.internal.ir.CallNode; 4.37 -import jdk.nashorn.internal.ir.IdentNode; 4.38 -import jdk.nashorn.internal.ir.IndexNode; 4.39 -import jdk.nashorn.internal.ir.Node; 4.40 -import jdk.nashorn.internal.ir.ReferenceNode; 4.41 -import jdk.nashorn.internal.ir.Symbol; 4.42 -import jdk.nashorn.internal.ir.TypeOverride; 4.43 -import jdk.nashorn.internal.ir.UnaryNode; 4.44 -import jdk.nashorn.internal.ir.VarNode; 4.45 -import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 4.46 -import jdk.nashorn.internal.ir.visitor.NodeVisitor; 4.47 -import jdk.nashorn.internal.parser.Token; 4.48 -import jdk.nashorn.internal.parser.TokenType; 4.49 -import jdk.nashorn.internal.runtime.Debug; 4.50 -import jdk.nashorn.internal.runtime.DebugLogger; 4.51 - 4.52 -/** 4.53 - * This is a post pass for Lower, that removes casts for accessors to objects in 4.54 - * Scope, and replaces the accessor type with a narrower one with possible. 4.55 - * 4.56 - * Any node that implements TypeOverride will be subject to access specialization. 4.57 - * 4.58 - * TypeOverride basically means "type inference has determined that the field x 4.59 - * is an object, but if you do x & 17, it can be read as an int, which we hope 4.60 - * coincides with its internal representation. In that case there is no boxing 4.61 - * that may or may not be removed by the JVM and less data bandwidth. 4.62 - * 4.63 - * Ideally this should not be a post pass, but it requires slot AND scope info, and has 4.64 - * to be run where it is, which is called from {@link CodeGenerator}. 4.65 - * 4.66 - * @see TypeOverride 4.67 - */ 4.68 - 4.69 -final class AccessSpecializer extends NodeOperatorVisitor { 4.70 - /** Debug logger for access specialization. Enable it with --log=access:level 4.71 - or -Dnashorn.callsiteaccess.debug */ 4.72 - private static final DebugLogger LOG = new DebugLogger("access", "nashorn.callsiteaccess.debug"); 4.73 - private static final boolean DEBUG = LOG.isEnabled(); 4.74 - 4.75 - @Override 4.76 - public Node leave(final VarNode varNode) { 4.77 - if (varNode.isAssignment()) { 4.78 - return leaveAssign(varNode); 4.79 - } 4.80 - 4.81 - return varNode; 4.82 - } 4.83 - 4.84 - @Override 4.85 - public Node leave(final CallNode callNode) { 4.86 - final Node function = callNode.getFunction(); 4.87 - if (function instanceof ReferenceNode) { 4.88 - changeType(callNode, ((ReferenceNode)function).getReference().getType()); 4.89 - } 4.90 - return callNode; 4.91 - } 4.92 - 4.93 - @Override 4.94 - public Node leaveASSIGN(final BinaryNode binaryNode) { 4.95 - return leaveAssign(binaryNode); 4.96 - } 4.97 - 4.98 - @Override 4.99 - public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { 4.100 - return leaveAssign(binaryNode); 4.101 - } 4.102 - 4.103 - @Override 4.104 - public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { 4.105 - return leaveAssign(binaryNode); 4.106 - } 4.107 - 4.108 - @Override 4.109 - public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { 4.110 - return leaveAssign(binaryNode); 4.111 - } 4.112 - 4.113 - @Override 4.114 - public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { 4.115 - return leaveAssign(binaryNode); 4.116 - } 4.117 - 4.118 - @Override 4.119 - public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { 4.120 - return leaveAssign(binaryNode); 4.121 - } 4.122 - 4.123 - @Override 4.124 - public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { 4.125 - return leaveAssign(binaryNode); 4.126 - } 4.127 - 4.128 - @Override 4.129 - public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { 4.130 - return leaveAssign(binaryNode); 4.131 - } 4.132 - 4.133 - @Override 4.134 - public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { 4.135 - return leaveAssign(binaryNode); 4.136 - } 4.137 - 4.138 - @Override 4.139 - public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { 4.140 - return leaveAssign(binaryNode); 4.141 - } 4.142 - 4.143 - @Override 4.144 - public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { 4.145 - return leaveAssign(binaryNode); 4.146 - } 4.147 - 4.148 - @Override 4.149 - public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { 4.150 - return leaveAssign(binaryNode); 4.151 - } 4.152 - 4.153 - @Override 4.154 - public Node leaveCOMMALEFT(final BinaryNode binaryNode) { 4.155 - return propagateResultType(binaryNode, binaryNode.lhs().getType()); 4.156 - } 4.157 - 4.158 - @Override 4.159 - public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { 4.160 - return propagateResultType(binaryNode, binaryNode.rhs().getType()); 4.161 - } 4.162 - 4.163 - @Override 4.164 - public Node leaveCONVERT(final UnaryNode unaryNode) { 4.165 - final Type castTo = unaryNode.getType(); 4.166 - 4.167 - Node rhs = unaryNode.rhs(); 4.168 - 4.169 - // Go through all conversions until we find the first non-convert node. 4.170 - while (rhs.tokenType() == TokenType.CONVERT) { 4.171 - rhs = ((UnaryNode)rhs).rhs(); 4.172 - } 4.173 - 4.174 - // If this node can be type changed 4.175 - if (canHaveCallSiteType(rhs) && isSupportedCallSiteType(castTo)) { 4.176 - /* 4.177 - * Just add a callsite type and throw away the cast, appropriate 4.178 - * getter/setter is selected, which will do the conversion for us 4.179 - */ 4.180 - changeType(rhs, castTo); 4.181 - LOG.fine("*** cast: converting " + debugNode(unaryNode) + " to " + debugNode(rhs)); 4.182 - 4.183 - return rhs; 4.184 - } 4.185 - 4.186 - // Micro optimization for node hygiene and pattern detection - remove unnecessary same type cast 4.187 - if (unaryNode.getType().isEquivalentTo(rhs.getType())) { 4.188 - return rhs; 4.189 - } 4.190 - 4.191 - return unaryNode; 4.192 - } 4.193 - 4.194 - @Override 4.195 - public Node leaveDECINC(final UnaryNode unaryNode) { 4.196 - assert unaryNode.isAssignment(); 4.197 - 4.198 - final Node dest = unaryNode.getAssignmentDest(); 4.199 - if (canHaveCallSiteType(dest) && isSupportedCallSiteType(unaryNode.getType())) { 4.200 - changeTypeInAssignment(dest, unaryNode.getType()); 4.201 - } 4.202 - 4.203 - return unaryNode; 4.204 - } 4.205 - 4.206 - /** 4.207 - * Is this a node that can have its type overridden. This is true for 4.208 - * AccessNodes, IndexNodes and IdentNodes 4.209 - * 4.210 - * @param node the node to check 4.211 - * @return true if node can have a callsite type 4.212 - */ 4.213 - private static boolean canHaveCallSiteType(final Node node) { 4.214 - return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType(); 4.215 - } 4.216 - 4.217 - /** 4.218 - * Is the specialization type supported. Currently we treat booleans as objects 4.219 - * and have no special boolean type accessor, thus booleans are ignored. 4.220 - * TODO - support booleans? NASHORN-590 4.221 - * 4.222 - * @param castTo the type to check 4.223 - * @return true if call site type is supported 4.224 - */ 4.225 - private static boolean isSupportedCallSiteType(final Type castTo) { 4.226 - return castTo.isNumeric(); // don't specializable for boolean 4.227 - } 4.228 - 4.229 - /** 4.230 - * Turn a node into a covert node 4.231 - * 4.232 - * @param node the node 4.233 - * @return the node as a convert node 4.234 - */ 4.235 - private static Node convert(final Node node) { 4.236 - return new UnaryNode(node.getSource(), 4.237 - Token.recast(node.getToken(), TokenType.CONVERT), 4.238 - node); 4.239 - } 4.240 - 4.241 - private static Node leaveAssign(final Node node) { 4.242 - assert node.isAssignment() : node + " is not an assignment"; 4.243 - 4.244 - 4.245 - final Node lhs = ((Assignment<?>)node).getAssignmentDest(); 4.246 - Node rhs = ((Assignment<?>)node).getAssignmentSource(); 4.247 - 4.248 - /** 4.249 - * Nodes with local variable slots are assumed to be of their optimal type 4.250 - * already and aren't affected here. This is not strictly true, for instance 4.251 - * with doubles instead of in a bounded loop. TODO - range check: NASHORN-363 4.252 - * 4.253 - * This is also not strictly true for var y = x = 55; where y has no other uses 4.254 - * Then y can be an int, but lower conservatively does an object of the assign to 4.255 - * scope 4.256 - */ 4.257 - final Symbol lhsSymbol = lhs.getSymbol(); 4.258 - 4.259 - if (lhsSymbol.hasSlot() && !lhsSymbol.isScope()) { 4.260 - LOG.finest(lhs.getSymbol() + " has slot!"); 4.261 - if (!lhs.getType().isEquivalentTo(rhs.getType())) { 4.262 - LOG.finest("\tslot assignment: " +lhs.getType()+ " " +rhs.getType() + " " + debugNode(node)); 4.263 - 4.264 - final Node c = convert(rhs); 4.265 - c.setSymbol(lhsSymbol); 4.266 - ((Assignment<?>)node).setAssignmentSource(c); 4.267 - 4.268 - LOG.fine("*** slot assignment turned to : " + debugNode(node)); 4.269 - } else { 4.270 - LOG.finest("aborted - type equivalence between lhs and rhs"); 4.271 - } 4.272 - 4.273 - return node; 4.274 - } 4.275 - 4.276 - // e.g. __DIR__, __LINE__, __FILE__ - don't try to change these 4.277 - if (lhs instanceof IdentNode && ((IdentNode)lhs).isSpecialIdentity()) { 4.278 - return node; 4.279 - } 4.280 - 4.281 - /** 4.282 - * Try to cast to the type of the right hand side, now pruned. E.g. an (object)17 should 4.283 - * now be just 17, an int. 4.284 - */ 4.285 - Type castTo = rhs.getType(); 4.286 - 4.287 - // If LHS can't get a new type, neither can rhs - retain the convert 4.288 - if (!canHaveCallSiteType(lhs)) { 4.289 - return node; 4.290 - } 4.291 - 4.292 - // Take the narrowest type of the entire cast sequence 4.293 - while (rhs.tokenType() == TokenType.CONVERT) { 4.294 - rhs = ((UnaryNode)rhs).rhs(); 4.295 - castTo = Type.narrowest(rhs.getType(), castTo); //e.g. (object)(int) -> int even though object is outermost 4.296 - } 4.297 - 4.298 - // If castTo is wider than widestOperationType, castTo can be further slowed down 4.299 - final Type widestOperationType = node.getWidestOperationType(); 4.300 - LOG.finest("node wants to be " + castTo + " and its widest operation is " + widestOperationType); 4.301 - 4.302 - if (widestOperationType != castTo && Type.widest(castTo, widestOperationType) == castTo) { 4.303 - LOG.info("###" + node + " castTo was " + castTo + " but could be downgraded to " + node.getWidestOperationType()); 4.304 - castTo = node.getWidestOperationType(); 4.305 - if (rhs instanceof TypeOverride) { 4.306 - changeType(rhs, castTo); 4.307 - } 4.308 - } 4.309 - 4.310 - /* 4.311 - * If this is a self modifying op, we can't be narrower than the widest optype 4.312 - * or e.g. x = x + 12 and x += 12 will turn into different things 4.313 - */ 4.314 - if (node.isSelfModifying()) { 4.315 - castTo = Type.widest(widestOperationType, castTo); 4.316 - } 4.317 - 4.318 - // We only specialize for numerics, not for booleans. 4.319 - if (isSupportedCallSiteType(castTo)) { 4.320 - if (rhs.getType() != castTo) { 4.321 - LOG.finest("cast was necessary, abort: " + node + " " + rhs.getType() + " != " + castTo); 4.322 - return node; 4.323 - } 4.324 - 4.325 - LOG.finest("assign: " + debugNode(node)); 4.326 - 4.327 - changeTypeInAssignment(lhs, castTo); 4.328 - ((Assignment<?>)node).setAssignmentSource(rhs); 4.329 - 4.330 - LOG.info("### modified to " + debugNode(node) + " (given type override " + castTo + ")"); 4.331 - 4.332 - propagateResultType(node, castTo); 4.333 - } 4.334 - 4.335 - return node; 4.336 - } 4.337 - 4.338 - private static Node propagateResultType(final Node node, final Type type) { 4.339 - //warning! this CANNOT be done for non temporaries as they are used in other computations 4.340 - if (isSupportedCallSiteType(type)) { 4.341 - if (node.getSymbol().isTemp()) { 4.342 - LOG.finest("changing temporary type: " + debugNode(node) + " to " + type); 4.343 - node.getSymbol().setTypeOverride(type); 4.344 - LOG.info("### node modified to " + debugNode(node) + " (given type override " + type + ")"); 4.345 - } 4.346 - } 4.347 - return node; 4.348 - } 4.349 - 4.350 - private static void changeTypeInAssignment(final Node dest, final Type newType) { 4.351 - if (changeType(dest, newType)) { 4.352 - LOG.finest("changed assignment " + dest + " " + dest.getSymbol()); 4.353 - assert !newType.isObject(); 4.354 - 4.355 - final HashSet<Node> exclude = new HashSet<>(); 4.356 - 4.357 - dest.accept(new NodeVisitor() { 4.358 - 4.359 - private void setCanBePrimitive(final Symbol symbol) { 4.360 - LOG.fine("*** can be primitive symbol " + symbol + " " + Debug.id(symbol)); 4.361 - symbol.setCanBePrimitive(newType); 4.362 - } 4.363 - 4.364 - @Override 4.365 - public Node enter(final IdentNode identNode) { 4.366 - if (!exclude.contains(identNode)) { 4.367 - setCanBePrimitive(identNode.getSymbol()); 4.368 - } 4.369 - return null; 4.370 - } 4.371 - 4.372 - @Override 4.373 - public Node enter(final AccessNode accessNode) { 4.374 - setCanBePrimitive(accessNode.getProperty().getSymbol()); 4.375 - return null; 4.376 - } 4.377 - 4.378 - @Override 4.379 - public Node enter(final IndexNode indexNode) { 4.380 - exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine 4.381 - return indexNode; 4.382 - } 4.383 - 4.384 - }); 4.385 - } 4.386 - } 4.387 - 4.388 - private static boolean changeType(final Node node, final Type newType) { 4.389 - if (!node.getType().equals(newType)) { 4.390 - ((TypeOverride)node).setType(newType); 4.391 - return true; 4.392 - } 4.393 - return false; 4.394 - } 4.395 - 4.396 - private static String debugNode(final Node node) { 4.397 - if (DEBUG) { 4.398 - return node.toString(); 4.399 - } 4.400 - return ""; 4.401 - } 4.402 -}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/jdk/nashorn/internal/codegen/Attr.java Wed Jan 30 12:26:45 2013 +0100 5.3 @@ -0,0 +1,1584 @@ 5.4 +/* 5.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. Oracle designates this 5.11 + * particular file as subject to the "Classpath" exception as provided 5.12 + * by Oracle in the LICENSE file that accompanied this code. 5.13 + * 5.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.17 + * version 2 for more details (a copy is included in the LICENSE file that 5.18 + * accompanied this code). 5.19 + * 5.20 + * You should have received a copy of the GNU General Public License version 5.21 + * 2 along with this work; if not, write to the Free Software Foundation, 5.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.23 + * 5.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.25 + * or visit www.oracle.com if you need additional information or have any 5.26 + * questions. 5.27 + */ 5.28 + 5.29 +package jdk.nashorn.internal.codegen; 5.30 + 5.31 +import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 5.32 +import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; 5.33 +import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; 5.34 +import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 5.35 +import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN; 5.36 +import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX; 5.37 +import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 5.38 +import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 5.39 +import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; 5.40 +import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 5.41 +import static jdk.nashorn.internal.ir.Symbol.IS_LET; 5.42 +import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; 5.43 +import static jdk.nashorn.internal.ir.Symbol.IS_THIS; 5.44 +import static jdk.nashorn.internal.ir.Symbol.IS_VAR; 5.45 + 5.46 +import java.util.ArrayList; 5.47 +import java.util.HashSet; 5.48 +import java.util.LinkedList; 5.49 +import java.util.List; 5.50 +import java.util.Set; 5.51 + 5.52 +import jdk.nashorn.internal.codegen.types.Type; 5.53 +import jdk.nashorn.internal.ir.AccessNode; 5.54 +import jdk.nashorn.internal.ir.BinaryNode; 5.55 +import jdk.nashorn.internal.ir.Block; 5.56 +import jdk.nashorn.internal.ir.CallNode; 5.57 +import jdk.nashorn.internal.ir.CallNode.EvalArgs; 5.58 +import jdk.nashorn.internal.ir.CaseNode; 5.59 +import jdk.nashorn.internal.ir.CatchNode; 5.60 +import jdk.nashorn.internal.ir.ForNode; 5.61 +import jdk.nashorn.internal.ir.FunctionNode; 5.62 +import jdk.nashorn.internal.ir.IdentNode; 5.63 +import jdk.nashorn.internal.ir.IndexNode; 5.64 +import jdk.nashorn.internal.ir.LiteralNode; 5.65 +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 5.66 +import jdk.nashorn.internal.ir.Node; 5.67 +import jdk.nashorn.internal.ir.ObjectNode; 5.68 +import jdk.nashorn.internal.ir.PropertyNode; 5.69 +import jdk.nashorn.internal.ir.ReferenceNode; 5.70 +import jdk.nashorn.internal.ir.ReturnNode; 5.71 +import jdk.nashorn.internal.ir.RuntimeNode; 5.72 +import jdk.nashorn.internal.ir.RuntimeNode.Request; 5.73 +import jdk.nashorn.internal.ir.SwitchNode; 5.74 +import jdk.nashorn.internal.ir.Symbol; 5.75 +import jdk.nashorn.internal.ir.TernaryNode; 5.76 +import jdk.nashorn.internal.ir.TryNode; 5.77 +import jdk.nashorn.internal.ir.UnaryNode; 5.78 +import jdk.nashorn.internal.ir.VarNode; 5.79 +import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 5.80 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 5.81 +import jdk.nashorn.internal.parser.TokenType; 5.82 +import jdk.nashorn.internal.runtime.Context; 5.83 +import jdk.nashorn.internal.runtime.Debug; 5.84 +import jdk.nashorn.internal.runtime.DebugLogger; 5.85 +import jdk.nashorn.internal.runtime.ECMAException; 5.86 +import jdk.nashorn.internal.runtime.JSType; 5.87 +import jdk.nashorn.internal.runtime.Property; 5.88 +import jdk.nashorn.internal.runtime.PropertyMap; 5.89 +import jdk.nashorn.internal.runtime.ScriptFunction; 5.90 +import jdk.nashorn.internal.runtime.ScriptObject; 5.91 +import jdk.nashorn.internal.runtime.Source; 5.92 + 5.93 +/** 5.94 + * This is the attribution pass of the code generator. Attr takes Lowered IR, 5.95 + * that is, IR where control flow has been computed and high level to low level 5.96 + * substitions for operations have been performed. 5.97 + * 5.98 + * After Attr, every symbol will have a conservative correct type. 5.99 + * 5.100 + * Any expression that requires temporary storage as part of computation will 5.101 + * also be detected here and give a temporary symbol 5.102 + * 5.103 + * Types can be narrowed after Attr by Access Specialization in FinalizeTypes, 5.104 + * but in general, this is where the main symbol type information is 5.105 + * computed. 5.106 + */ 5.107 + 5.108 +final class Attr extends NodeOperatorVisitor { 5.109 + /** Current compiler. */ 5.110 + private final Compiler compiler; 5.111 + 5.112 + /** Current source. */ 5.113 + private final Source source; 5.114 + 5.115 + /** 5.116 + * Local definitions in current block (to discriminate from function 5.117 + * declarations always defined in the function scope. This is for 5.118 + * "can be undefined" analysis. 5.119 + */ 5.120 + private Set<String> localDefs; 5.121 + 5.122 + /** 5.123 + * Local definitions in current block to guard against cases like 5.124 + * NASHORN-467 when things can be undefined as they are used before 5.125 + * their local var definition. *sigh* JavaScript... 5.126 + */ 5.127 + private Set<String> localUses; 5.128 + 5.129 + private static final DebugLogger LOG = new DebugLogger("attr"); 5.130 + 5.131 + /** 5.132 + * Constructor. 5.133 + * 5.134 + * @param compiler the compiler 5.135 + */ 5.136 + Attr(final Compiler compiler) { 5.137 + this.compiler = compiler; 5.138 + this.source = compiler.getSource(); 5.139 + } 5.140 + 5.141 + @Override 5.142 + protected Node enterDefault(final Node node) { 5.143 + return start(node); 5.144 + } 5.145 + 5.146 + @Override 5.147 + protected Node leaveDefault(final Node node) { 5.148 + return end(node); 5.149 + } 5.150 + 5.151 + @Override 5.152 + public Node leave(final AccessNode accessNode) { 5.153 + newTemporary(Type.OBJECT, accessNode); //While Object type is assigned here, Access Specialization in FinalizeTypes may narrow this 5.154 + end(accessNode); 5.155 + return accessNode; 5.156 + } 5.157 + 5.158 + @Override 5.159 + public Node enter(final Block block) { 5.160 + start(block); 5.161 + 5.162 + final Set<String> savedLocalDefs = localDefs; 5.163 + final Set<String> savedLocalUses = localUses; 5.164 + 5.165 + block.setFrame(getCurrentFunctionNode().pushFrame()); 5.166 + 5.167 + try { 5.168 + // a block starts out by copying the local defs and local uses 5.169 + // from the outer level. But we need the copies, as when we 5.170 + // leave the block the def and use sets given upon entry must 5.171 + // be restored 5.172 + localDefs = new HashSet<>(savedLocalDefs); 5.173 + localUses = new HashSet<>(savedLocalUses); 5.174 + 5.175 + for (final Node statement : block.getStatements()) { 5.176 + statement.accept(this); 5.177 + } 5.178 + } finally { 5.179 + localDefs = savedLocalDefs; 5.180 + localUses = savedLocalUses; 5.181 + 5.182 + getCurrentFunctionNode().popFrame(); 5.183 + } 5.184 + 5.185 + end(block); 5.186 + 5.187 + return null; 5.188 + } 5.189 + 5.190 + @Override 5.191 + public Node enter(final CallNode callNode) { 5.192 + start(callNode); 5.193 + 5.194 + callNode.getFunction().accept(this); 5.195 + 5.196 + final List<Node> acceptedArgs = new ArrayList<>(callNode.getArgs().size()); 5.197 + for (final Node arg : callNode.getArgs()) { 5.198 + LOG.info("Doing call arg " + arg); 5.199 + acceptedArgs.add(arg.accept(this)); 5.200 + } 5.201 + callNode.setArgs(acceptedArgs); 5.202 + 5.203 + final EvalArgs evalArgs = callNode.getEvalArgs(); 5.204 + if (evalArgs != null) { 5.205 + evalArgs.setCode(evalArgs.getCode().accept(this)); 5.206 + 5.207 + final IdentNode thisNode = new IdentNode(getCurrentFunctionNode().getThisNode()); 5.208 + assert thisNode.getSymbol() != null; //should copy attributed symbol and that's it 5.209 + evalArgs.setThis(thisNode); 5.210 + } 5.211 + 5.212 + newTemporary(Type.OBJECT, callNode); // object type here, access specialization in FinalizeTypes may narrow it later 5.213 + newType(callNode.getFunction().getSymbol(), Type.OBJECT); 5.214 + 5.215 + end(callNode); 5.216 + 5.217 + return null; 5.218 + } 5.219 + 5.220 + @Override 5.221 + public Node enter(final CatchNode catchNode) { 5.222 + final IdentNode exception = catchNode.getException(); 5.223 + final Block block = getCurrentBlock(); 5.224 + 5.225 + start(catchNode); 5.226 + 5.227 + // define block-local exception variable 5.228 + final Symbol def = block.defineSymbol(exception.getName(), IS_VAR | IS_LET, exception); 5.229 + newType(def, Type.OBJECT); 5.230 + addLocalDef(exception.getName()); 5.231 + 5.232 + return catchNode; 5.233 + } 5.234 + 5.235 + @Override 5.236 + public Node enter(final FunctionNode functionNode) { 5.237 + start(functionNode, false); 5.238 + 5.239 + clearLocalDefs(); 5.240 + clearLocalUses(); 5.241 + 5.242 + functionNode.setFrame(functionNode.pushFrame()); 5.243 + 5.244 + initThis(functionNode); 5.245 + initCallee(functionNode); 5.246 + if (functionNode.isVarArg()) { 5.247 + initVarArg(functionNode); 5.248 + } 5.249 + 5.250 + initParameters(functionNode); 5.251 + initScope(functionNode); 5.252 + initReturn(functionNode); 5.253 + 5.254 + // Add all nested functions as symbols in this function 5.255 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 5.256 + final IdentNode ident = nestedFunction.getIdent(); 5.257 + if (ident != null && nestedFunction.isStatement()) { 5.258 + final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction); 5.259 + newType(functionSymbol, Type.typeFor(ScriptFunction.class)); 5.260 + } 5.261 + } 5.262 + 5.263 + if (functionNode.isScript()) { 5.264 + initFromPropertyMap(compiler.getContext(), functionNode); 5.265 + } 5.266 + 5.267 + // Add function name as local symbol 5.268 + if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) { 5.269 + final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode); 5.270 + newType(selfSymbol, Type.OBJECT); 5.271 + selfSymbol.setNode(functionNode); 5.272 + } 5.273 + 5.274 + /* 5.275 + * This pushes all declarations (except for non-statements, i.e. for 5.276 + * node temporaries) to the top of the function scope. This way we can 5.277 + * get around problems like 5.278 + * 5.279 + * while (true) { 5.280 + * break; 5.281 + * if (true) { 5.282 + * var s; 5.283 + * } 5.284 + * } 5.285 + * 5.286 + * to an arbitrary nesting depth. 5.287 + * 5.288 + * @see NASHORN-73 5.289 + */ 5.290 + 5.291 + final List<Symbol> declaredSymbols = new ArrayList<>(); 5.292 + for (final VarNode decl : functionNode.getDeclarations()) { 5.293 + final IdentNode ident = decl.getName(); 5.294 + // any declared symbols that aren't visited need to be typed as well, hence the list 5.295 + declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident))); 5.296 + } 5.297 + 5.298 + // Every nested function needs a definition in the outer function with its name. Add these. 5.299 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 5.300 + final VarNode varNode = nestedFunction.getFunctionVarNode(); 5.301 + if (varNode != null) { 5.302 + varNode.accept(this); 5.303 + assert varNode.isFunctionVarNode() : varNode + " should be function var node"; 5.304 + } 5.305 + } 5.306 + 5.307 + for (final Node statement : functionNode.getStatements()) { 5.308 + if (statement instanceof VarNode && ((VarNode)statement).isFunctionVarNode()) { 5.309 + continue; //var nodes have already been processed, skip or they will generate additional defs/uses and false "can be undefined" 5.310 + } 5.311 + statement.accept(this); 5.312 + } 5.313 + 5.314 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 5.315 + LOG.info("Going into nested function " + functionNode.getName() + " -> " + nestedFunction.getName()); 5.316 + nestedFunction.accept(this); 5.317 + } 5.318 + 5.319 + //unknown parameters are promoted to object type. 5.320 + finalizeParameters(functionNode); 5.321 + finalizeTypes(functionNode); 5.322 + for (final Symbol symbol : declaredSymbols) { 5.323 + if (symbol.getSymbolType().isUnknown()) { 5.324 + symbol.setType(Type.OBJECT); 5.325 + symbol.setCanBeUndefined(); 5.326 + } 5.327 + } 5.328 + 5.329 + if (functionNode.getReturnType().isUnknown()) { 5.330 + LOG.info("Unknown return type promoted to object"); 5.331 + functionNode.setReturnType(Type.OBJECT); 5.332 + } 5.333 + 5.334 + if (functionNode.getSelfSymbolInit() != null) { 5.335 + LOG.info("Accepting self symbol init " + functionNode.getSelfSymbolInit() + " for " + functionNode.getName()); 5.336 + final Node init = functionNode.getSelfSymbolInit(); 5.337 + final List<Node> newStatements = new ArrayList<>(); 5.338 + newStatements.add(init); 5.339 + newStatements.addAll(functionNode.getStatements()); 5.340 + functionNode.setStatements(newStatements); 5.341 + functionNode.setNeedsSelfSymbol(functionNode.getSelfSymbolInit().accept(this)); 5.342 + } 5.343 + 5.344 + functionNode.popFrame(); 5.345 + 5.346 + end(functionNode, false); 5.347 + 5.348 + return null; 5.349 + } 5.350 + 5.351 + @Override 5.352 + public Node leaveCONVERT(final UnaryNode unaryNode) { 5.353 + assert false : "There should be no convert operators in IR during Attribution"; 5.354 + end(unaryNode); 5.355 + return unaryNode; 5.356 + } 5.357 + 5.358 + @Override 5.359 + public Node enter(final IdentNode identNode) { 5.360 + final String name = identNode.getName(); 5.361 + 5.362 + start(identNode); 5.363 + 5.364 + if (identNode.isPropertyName()) { 5.365 + // assign a pseudo symbol to property name 5.366 + final Symbol pseudoSymbol = pseudoSymbol(name); 5.367 + LOG.info("IdentNode is property name -> assigning pseudo symbol " + pseudoSymbol); 5.368 + LOG.unindent(); 5.369 + identNode.setSymbol(pseudoSymbol); 5.370 + return null; 5.371 + } 5.372 + 5.373 + final Block block = getCurrentBlock(); 5.374 + final Symbol oldSymbol = identNode.getSymbol(); 5.375 + 5.376 + Symbol symbol = block.findSymbol(name); 5.377 + 5.378 + //If an existing symbol with the name is found, use that otherwise, declare a new one 5.379 + if (symbol != null) { 5.380 + LOG.info("Existing symbol = " + symbol); 5.381 + if (isFunctionExpressionSelfReference(symbol)) { 5.382 + final FunctionNode functionNode = (FunctionNode)symbol.getNode(); 5.383 + assert functionNode.getCalleeNode() != null; 5.384 + 5.385 + final VarNode var = new VarNode(source, functionNode.getToken(), functionNode.getFinish(), functionNode.getIdent(), functionNode.getCalleeNode()); 5.386 + //newTemporary(Type.OBJECT, var); //ScriptFunction? TODO 5.387 + 5.388 + functionNode.setNeedsSelfSymbol(var); 5.389 + } 5.390 + 5.391 + if (!identNode.isInitializedHere()) { // NASHORN-448 5.392 + // here is a use outside the local def scope 5.393 + if (!isLocalDef(name)) { 5.394 + newType(symbol, Type.OBJECT); 5.395 + symbol.setCanBeUndefined(); 5.396 + } 5.397 + } 5.398 + 5.399 + identNode.setSymbol(symbol); 5.400 + if (!getCurrentFunctionNode().isLocal(symbol)) { 5.401 + // non-local: we need to put symbol in scope (if it isn't already) 5.402 + if (!symbol.isScope()) { 5.403 + final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction()); 5.404 + for (final Block lookupBlock : lookupBlocks) { 5.405 + final Symbol refSymbol = lookupBlock.findSymbol(name); 5.406 + if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f() 5.407 + LOG.finest("Found a ref symbol that must be scope " + refSymbol); 5.408 + refSymbol.setIsScope(); 5.409 + } 5.410 + } 5.411 + } 5.412 + } 5.413 + } else { 5.414 + LOG.info("No symbol exists. Declare undefined: " + symbol); 5.415 + symbol = block.useSymbol(name, identNode); 5.416 + // we have never seen this before, it can be undefined 5.417 + newType(symbol, Type.OBJECT); // TODO unknown -we have explicit casts anyway? 5.418 + symbol.setCanBeUndefined(); 5.419 + } 5.420 + 5.421 + if (symbol != oldSymbol && !identNode.isInitializedHere()) { 5.422 + symbol.increaseUseCount(); 5.423 + } 5.424 + addLocalUse(identNode.getName()); 5.425 + 5.426 + end(identNode); 5.427 + 5.428 + return null; 5.429 + } 5.430 + 5.431 + @Override 5.432 + public Node leave(final IndexNode indexNode) { 5.433 + newTemporary(Type.OBJECT, indexNode); //TORO 5.434 + return indexNode; 5.435 + } 5.436 + 5.437 + @SuppressWarnings("rawtypes") 5.438 + @Override 5.439 + public Node enter(final LiteralNode literalNode) { 5.440 + try { 5.441 + start(literalNode); 5.442 + assert !literalNode.isTokenType(TokenType.THIS) : "tokentype for " + literalNode + " is this"; //guard against old dead code case. literal nodes should never inherit tokens 5.443 + 5.444 + if (literalNode instanceof ArrayLiteralNode) { 5.445 + final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; 5.446 + final Node[] array = arrayLiteralNode.getValue(); 5.447 + 5.448 + for (int i = 0; i < array.length; i++) { 5.449 + final Node element = array[i]; 5.450 + if (element != null) { 5.451 + array[i] = element.accept(this); 5.452 + } 5.453 + } 5.454 + arrayLiteralNode.analyze(); 5.455 + //array literal node now has an element type and all elements are attributed 5.456 + } else { 5.457 + assert !(literalNode.getValue() instanceof Node) : "literals with Node values not supported"; 5.458 + } 5.459 + 5.460 + getCurrentFunctionNode().newLiteral(literalNode); 5.461 + } finally { 5.462 + end(literalNode); 5.463 + } 5.464 + return null; 5.465 + } 5.466 + 5.467 + @Override 5.468 + public Node leave(final ObjectNode objectNode) { 5.469 + newTemporary(Type.OBJECT, objectNode); 5.470 + end(objectNode); 5.471 + return objectNode; 5.472 + } 5.473 + 5.474 + @Override 5.475 + public Node enter(final PropertyNode propertyNode) { 5.476 + // assign a pseudo symbol to property name, see NASHORN-710 5.477 + propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); 5.478 + end(propertyNode); 5.479 + return propertyNode; 5.480 + } 5.481 + 5.482 + @Override 5.483 + public Node enter(final ReferenceNode referenceNode) { 5.484 + final FunctionNode functionNode = referenceNode.getReference(); 5.485 + if (functionNode != null) { 5.486 + functionNode.addReferencingParentBlock(getCurrentBlock()); 5.487 + } 5.488 + end(referenceNode); 5.489 + 5.490 + return referenceNode; 5.491 + } 5.492 + 5.493 + @Override 5.494 + public Node leave(final ReferenceNode referenceNode) { 5.495 + newTemporary(Type.OBJECT, referenceNode); //reference node type is always an object, i.e. the scriptFunction. the function return type varies though 5.496 + end(referenceNode); 5.497 + return referenceNode; 5.498 + } 5.499 + 5.500 + @Override 5.501 + public Node leave(final ReturnNode returnNode) { 5.502 + final Node expr = returnNode.getExpression(); 5.503 + 5.504 + if (expr != null) { 5.505 + //we can't do parameter specialization if we return something that hasn't been typed yet 5.506 + final Symbol symbol = expr.getSymbol(); 5.507 + if (expr.getType().isUnknown() && symbol.isParam()) { 5.508 + symbol.setType(Type.OBJECT); 5.509 + } 5.510 + getCurrentFunctionNode().setReturnType(Type.widest(getCurrentFunctionNode().getReturnType(), symbol.getSymbolType())); 5.511 + LOG.info("Returntype is now " + getCurrentFunctionNode().getReturnType()); 5.512 + } 5.513 + 5.514 + end(returnNode); 5.515 + 5.516 + return returnNode; 5.517 + } 5.518 + 5.519 + @Override 5.520 + public Node leave(final SwitchNode switchNode) { 5.521 + Type type = Type.UNKNOWN; 5.522 + 5.523 + for (final CaseNode caseNode : switchNode.getCases()) { 5.524 + final Node test = caseNode.getTest(); 5.525 + if (test != null) { 5.526 + if (test instanceof LiteralNode) { 5.527 + //go down to integers if we can 5.528 + final LiteralNode<?> lit = (LiteralNode<?>)test; 5.529 + if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) { 5.530 + if (JSType.isRepresentableAsInt(lit.getNumber())) { 5.531 + caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); 5.532 + } 5.533 + } 5.534 + } 5.535 + 5.536 + type = Type.widest(type, caseNode.getTest().getType()); 5.537 + } 5.538 + } 5.539 + 5.540 + //only optimize for all integers 5.541 + if (!type.isInteger()) { 5.542 + type = Type.OBJECT; 5.543 + } 5.544 + 5.545 + switchNode.setTag(newInternal(compiler.uniqueName(SWITCH_TAG_PREFIX.tag()), type)); 5.546 + 5.547 + end(switchNode); 5.548 + 5.549 + return switchNode; 5.550 + } 5.551 + 5.552 + @Override 5.553 + public Node leave(final TryNode tryNode) { 5.554 + tryNode.setException(exceptionSymbol()); 5.555 + 5.556 + if (tryNode.getFinallyBody() != null) { 5.557 + tryNode.setFinallyCatchAll(exceptionSymbol()); 5.558 + } 5.559 + 5.560 + end(tryNode); 5.561 + 5.562 + return tryNode; 5.563 + } 5.564 + 5.565 + @Override 5.566 + public Node enter(final VarNode varNode) { 5.567 + start(varNode); 5.568 + 5.569 + final IdentNode ident = varNode.getName(); 5.570 + final String name = ident.getName(); 5.571 + 5.572 + final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident); 5.573 + assert symbol != null; 5.574 + 5.575 + LOG.info("VarNode " + varNode + " set symbol " + symbol); 5.576 + varNode.setSymbol(symbol); 5.577 + 5.578 + // NASHORN-467 - use before definition of vars - conservative 5.579 + if (localUses.contains(ident.getName())) { 5.580 + newType(symbol, Type.OBJECT); 5.581 + symbol.setCanBeUndefined(); 5.582 + } 5.583 + 5.584 + if (varNode.getInit() != null) { 5.585 + varNode.getInit().accept(this); 5.586 + } 5.587 + 5.588 + return varNode; 5.589 + } 5.590 + 5.591 + @Override 5.592 + public Node leave(final VarNode varNode) { 5.593 + final Node init = varNode.getInit(); 5.594 + final IdentNode ident = varNode.getName(); 5.595 + final String name = ident.getName(); 5.596 + 5.597 + if (init != null) { 5.598 + addLocalDef(name); 5.599 + } 5.600 + 5.601 + if (init == null) { 5.602 + // var x; with no init will be treated like a use of x by 5.603 + // visit(IdentNode) unless we remove the name 5.604 + // from the localdef list. 5.605 + removeLocalDef(name); 5.606 + return varNode; 5.607 + } 5.608 + 5.609 + final Symbol symbol = varNode.getSymbol(); 5.610 + final boolean isScript = symbol.getBlock().getFunction().isScript(); //see NASHORN-56 5.611 + if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) { 5.612 + // Forbid integers as local vars for now as we have no way to treat them as undefined 5.613 + newType(symbol, init.getType()); 5.614 + } else { 5.615 + newType(symbol, Type.OBJECT); 5.616 + } 5.617 + 5.618 + assert varNode.hasType() : varNode; 5.619 + 5.620 + end(varNode); 5.621 + 5.622 + return varNode; 5.623 + } 5.624 + 5.625 + @Override 5.626 + public Node leaveADD(final UnaryNode unaryNode) { 5.627 + newTemporary(arithType(), unaryNode); 5.628 + end(unaryNode); 5.629 + return unaryNode; 5.630 + } 5.631 + 5.632 + @Override 5.633 + public Node leaveBIT_NOT(final UnaryNode unaryNode) { 5.634 + newTemporary(Type.INT, unaryNode); 5.635 + end(unaryNode); 5.636 + return unaryNode; 5.637 + } 5.638 + 5.639 + @Override 5.640 + public Node leaveDECINC(final UnaryNode unaryNode) { 5.641 + // @see assignOffset 5.642 + ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs()); 5.643 + final Type type = arithType(); 5.644 + newType(unaryNode.rhs().getSymbol(), type); 5.645 + newTemporary(type, unaryNode); 5.646 + end(unaryNode); 5.647 + return unaryNode; 5.648 + } 5.649 + 5.650 + @Override 5.651 + public Node leaveDELETE(final UnaryNode unaryNode) { 5.652 + final FunctionNode currentFunctionNode = getCurrentFunctionNode(); 5.653 + final boolean strictMode = currentFunctionNode.isStrictMode(); 5.654 + final Node rhs = unaryNode.rhs(); 5.655 + final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this); 5.656 + 5.657 + Request request = Request.DELETE; 5.658 + final RuntimeNode runtimeNode; 5.659 + final List<Node> args = new ArrayList<>(); 5.660 + 5.661 + if (rhs instanceof IdentNode) { 5.662 + // If this is a declared variable or a function parameter, delete always fails (except for globals). 5.663 + final String name = ((IdentNode)rhs).getName(); 5.664 + 5.665 + final boolean failDelete = strictMode || rhs.getSymbol().isParam() || (rhs.getSymbol().isVar() && !rhs.getSymbol().isTopLevel()); 5.666 + 5.667 + if (failDelete && rhs.getSymbol().isThis()) { 5.668 + return LiteralNode.newInstance(unaryNode, true).accept(this); 5.669 + } 5.670 + final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this); 5.671 + 5.672 + args.add(currentFunctionNode.getScopeNode()); 5.673 + args.add(literalNode); 5.674 + args.add(strictFlagNode); 5.675 + 5.676 + if (failDelete) { 5.677 + request = Request.FAIL_DELETE; 5.678 + } 5.679 + } else if (rhs instanceof AccessNode) { 5.680 + final Node base = ((AccessNode)rhs).getBase(); 5.681 + final IdentNode property = ((AccessNode)rhs).getProperty(); 5.682 + 5.683 + args.add(base); 5.684 + args.add(LiteralNode.newInstance(unaryNode, property.getName()).accept(this)); 5.685 + args.add(strictFlagNode); 5.686 + 5.687 + } else if (rhs instanceof IndexNode) { 5.688 + final Node base = ((IndexNode)rhs).getBase(); 5.689 + final Node index = ((IndexNode)rhs).getIndex(); 5.690 + 5.691 + args.add(base); 5.692 + args.add(index); 5.693 + args.add(strictFlagNode); 5.694 + 5.695 + } else { 5.696 + return LiteralNode.newInstance(unaryNode, true).accept(this); 5.697 + } 5.698 + 5.699 + runtimeNode = new RuntimeNode(unaryNode, request, args); 5.700 + assert runtimeNode.getSymbol() == unaryNode.getSymbol(); //clone constructor should do this 5.701 + 5.702 + runtimeNode.accept(this); 5.703 + return runtimeNode; 5.704 + } 5.705 + 5.706 + 5.707 + @Override 5.708 + public Node leaveNEW(final UnaryNode unaryNode) { 5.709 + newTemporary(Type.OBJECT, unaryNode); 5.710 + end(unaryNode); 5.711 + return unaryNode; 5.712 + } 5.713 + 5.714 + @Override 5.715 + public Node leaveNOT(final UnaryNode unaryNode) { 5.716 + newTemporary(Type.BOOLEAN, unaryNode); 5.717 + end(unaryNode); 5.718 + return unaryNode; 5.719 + } 5.720 + 5.721 + @Override 5.722 + public Node leaveTYPEOF(final UnaryNode unaryNode) { 5.723 + final Node rhs = unaryNode.rhs(); 5.724 + 5.725 + RuntimeNode runtimeNode; 5.726 + 5.727 + List<Node> args = new ArrayList<>(); 5.728 + if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) { 5.729 + args.add(getCurrentFunctionNode().getScopeNode()); 5.730 + args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null 5.731 + } else { 5.732 + args.add(rhs); 5.733 + args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' 5.734 + } 5.735 + 5.736 + runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); 5.737 + assert runtimeNode.getSymbol() == unaryNode.getSymbol(); 5.738 + 5.739 + runtimeNode.accept(this); 5.740 + 5.741 + end(unaryNode); 5.742 + 5.743 + return runtimeNode; 5.744 + } 5.745 + 5.746 + @Override 5.747 + public Node leave(final RuntimeNode runtimeNode) { 5.748 + newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode); 5.749 + return runtimeNode; 5.750 + } 5.751 + 5.752 + @Override 5.753 + public Node leaveSUB(final UnaryNode unaryNode) { 5.754 + newTemporary(arithType(), unaryNode); 5.755 + end(unaryNode); 5.756 + return unaryNode; 5.757 + } 5.758 + 5.759 + @Override 5.760 + public Node leaveVOID(final UnaryNode unaryNode) { 5.761 + final RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.VOID); 5.762 + runtimeNode.accept(this); 5.763 + assert runtimeNode.getSymbol().getSymbolType().isObject(); 5.764 + end(unaryNode); 5.765 + return runtimeNode; 5.766 + } 5.767 + 5.768 + /** 5.769 + * Add is a special binary, as it works not only on arithmetic, but for 5.770 + * strings etc as well. 5.771 + */ 5.772 + @Override 5.773 + public Node leaveADD(final BinaryNode binaryNode) { 5.774 + final Node lhs = binaryNode.lhs(); 5.775 + final Node rhs = binaryNode.rhs(); 5.776 + 5.777 + ensureTypeNotUnknown(lhs); 5.778 + ensureTypeNotUnknown(rhs); 5.779 + newTemporary(Type.widest(lhs.getType(), rhs.getType()), binaryNode); 5.780 + 5.781 + end(binaryNode); 5.782 + 5.783 + return binaryNode; 5.784 + } 5.785 + 5.786 + @Override 5.787 + public Node leaveAND(final BinaryNode binaryNode) { 5.788 + newTemporary(Type.OBJECT, binaryNode); 5.789 + end(binaryNode); 5.790 + return binaryNode; 5.791 + } 5.792 + 5.793 + /** 5.794 + * This is a helper called before an assignment. 5.795 + * @param binaryNode assignment node 5.796 + */ 5.797 + private Node enterAssignmentNode(final BinaryNode binaryNode) { 5.798 + start(binaryNode); 5.799 + 5.800 + final Node lhs = binaryNode.lhs(); 5.801 + 5.802 + if (lhs instanceof IdentNode) { 5.803 + final Block block = getCurrentBlock(); 5.804 + final IdentNode ident = (IdentNode)lhs; 5.805 + final String name = ident.getName(); 5.806 + 5.807 + Symbol symbol = getCurrentBlock().findSymbol(name); 5.808 + 5.809 + if (symbol == null) { 5.810 + symbol = block.defineSymbol(name, IS_GLOBAL, ident); 5.811 + binaryNode.setSymbol(symbol); 5.812 + } else if (!getCurrentFunctionNode().isLocal(symbol)) { 5.813 + symbol.setIsScope(); 5.814 + } 5.815 + 5.816 + addLocalDef(name); 5.817 + } 5.818 + 5.819 + return binaryNode; 5.820 + } 5.821 + 5.822 + @Override 5.823 + public Node enterASSIGN(final BinaryNode binaryNode) { 5.824 + return enterAssignmentNode(binaryNode); 5.825 + } 5.826 + 5.827 + @Override 5.828 + public Node leaveASSIGN(final BinaryNode binaryNode) { 5.829 + return leaveAssignmentNode(binaryNode); 5.830 + } 5.831 + 5.832 + @Override 5.833 + public Node enterASSIGN_ADD(final BinaryNode binaryNode) { 5.834 + return enterAssignmentNode(binaryNode); 5.835 + } 5.836 + 5.837 + @Override 5.838 + public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { 5.839 + final Node lhs = binaryNode.lhs(); 5.840 + final Node rhs = binaryNode.rhs(); 5.841 + 5.842 + final Type widest = Type.widest(lhs.getType(), rhs.getType()); 5.843 + //Type.NUMBER if we can't prove that the add doesn't overflow. todo 5.844 + return leaveSelfModifyingAssignmentNode(binaryNode, widest.isNumeric() ? Type.NUMBER : Type.OBJECT); 5.845 + } 5.846 + 5.847 + @Override 5.848 + public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) { 5.849 + return enterAssignmentNode(binaryNode); 5.850 + } 5.851 + 5.852 + @Override 5.853 + public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { 5.854 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.855 + } 5.856 + 5.857 + @Override 5.858 + public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) { 5.859 + return enterAssignmentNode(binaryNode); 5.860 + } 5.861 + 5.862 + @Override 5.863 + public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { 5.864 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.865 + } 5.866 + 5.867 + @Override 5.868 + public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { 5.869 + return enterAssignmentNode(binaryNode); 5.870 + } 5.871 + 5.872 + @Override 5.873 + public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { 5.874 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.875 + } 5.876 + 5.877 + @Override 5.878 + public Node enterASSIGN_DIV(final BinaryNode binaryNode) { 5.879 + return enterAssignmentNode(binaryNode); 5.880 + } 5.881 + 5.882 + @Override 5.883 + public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { 5.884 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.885 + } 5.886 + 5.887 + @Override 5.888 + public Node enterASSIGN_MOD(final BinaryNode binaryNode) { 5.889 + return enterAssignmentNode(binaryNode); 5.890 + } 5.891 + 5.892 + @Override 5.893 + public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { 5.894 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.895 + } 5.896 + 5.897 + @Override 5.898 + public Node enterASSIGN_MUL(final BinaryNode binaryNode) { 5.899 + return enterAssignmentNode(binaryNode); 5.900 + } 5.901 + 5.902 + @Override 5.903 + public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { 5.904 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.905 + } 5.906 + 5.907 + @Override 5.908 + public Node enterASSIGN_SAR(final BinaryNode binaryNode) { 5.909 + return enterAssignmentNode(binaryNode); 5.910 + } 5.911 + 5.912 + @Override 5.913 + public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { 5.914 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.915 + } 5.916 + 5.917 + @Override 5.918 + public Node enterASSIGN_SHL(final BinaryNode binaryNode) { 5.919 + return enterAssignmentNode(binaryNode); 5.920 + } 5.921 + 5.922 + @Override 5.923 + public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { 5.924 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.925 + } 5.926 + 5.927 + @Override 5.928 + public Node enterASSIGN_SHR(final BinaryNode binaryNode) { 5.929 + return enterAssignmentNode(binaryNode); 5.930 + } 5.931 + 5.932 + @Override 5.933 + public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { 5.934 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.935 + } 5.936 + 5.937 + @Override 5.938 + public Node enterASSIGN_SUB(final BinaryNode binaryNode) { 5.939 + return enterAssignmentNode(binaryNode); 5.940 + } 5.941 + 5.942 + @Override 5.943 + public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { 5.944 + return leaveSelfModifyingAssignmentNode(binaryNode); 5.945 + } 5.946 + 5.947 + @Override 5.948 + public Node leaveBIT_AND(final BinaryNode binaryNode) { 5.949 + newTemporary(Type.INT, binaryNode); 5.950 + return binaryNode; 5.951 + } 5.952 + 5.953 + @Override 5.954 + public Node leaveBIT_OR(final BinaryNode binaryNode) { 5.955 + newTemporary(Type.INT, binaryNode); 5.956 + return binaryNode; 5.957 + } 5.958 + 5.959 + @Override 5.960 + public Node leaveBIT_XOR(final BinaryNode binaryNode) { 5.961 + newTemporary(Type.INT, binaryNode); 5.962 + return binaryNode; 5.963 + } 5.964 + 5.965 + @Override 5.966 + public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { 5.967 + newTemporary(binaryNode.rhs().getType(), binaryNode); 5.968 + return binaryNode; 5.969 + } 5.970 + 5.971 + @Override 5.972 + public Node leaveCOMMALEFT(final BinaryNode binaryNode) { 5.973 + newTemporary(binaryNode.lhs().getType(), binaryNode); 5.974 + return binaryNode; 5.975 + } 5.976 + 5.977 + @Override 5.978 + public Node leaveDIV(final BinaryNode binaryNode) { 5.979 + return leaveBinaryArithmetic(binaryNode); 5.980 + } 5.981 + 5.982 + private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { 5.983 + final Node lhs = binaryNode.lhs(); 5.984 + final Node rhs = binaryNode.rhs(); 5.985 + 5.986 + newTemporary(Type.BOOLEAN, binaryNode); 5.987 + ensureTypeNotUnknown(lhs); 5.988 + ensureTypeNotUnknown(rhs); 5.989 + 5.990 + end(binaryNode); 5.991 + return binaryNode; 5.992 + } 5.993 + 5.994 + //leave a binary node and inherit the widest type of lhs , rhs 5.995 + private Node leaveBinaryArithmetic(final BinaryNode binaryNode) { 5.996 + if (!Compiler.shouldUseIntegerArithmetic()) { 5.997 + newTemporary(Type.NUMBER, binaryNode); 5.998 + return binaryNode; 5.999 + } 5.1000 + newTemporary(Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType(), Type.NUMBER), binaryNode); 5.1001 + return binaryNode; 5.1002 + } 5.1003 + 5.1004 + @Override 5.1005 + public Node leaveEQ(final BinaryNode binaryNode) { 5.1006 + return leaveCmp(binaryNode, Request.EQ); 5.1007 + } 5.1008 + 5.1009 + @Override 5.1010 + public Node leaveEQ_STRICT(final BinaryNode binaryNode) { 5.1011 + return leaveCmp(binaryNode, Request.EQ_STRICT); 5.1012 + } 5.1013 + 5.1014 + @Override 5.1015 + public Node leaveGE(final BinaryNode binaryNode) { 5.1016 + return leaveCmp(binaryNode, Request.GE); 5.1017 + } 5.1018 + 5.1019 + @Override 5.1020 + public Node leaveGT(final BinaryNode binaryNode) { 5.1021 + return leaveCmp(binaryNode, Request.GT); 5.1022 + } 5.1023 + 5.1024 + @Override 5.1025 + public Node leaveIN(final BinaryNode binaryNode) { 5.1026 + try { 5.1027 + return new RuntimeNode(binaryNode, Request.IN).accept(this); 5.1028 + } finally { 5.1029 + end(binaryNode); 5.1030 + } 5.1031 + } 5.1032 + 5.1033 + @Override 5.1034 + public Node leaveINSTANCEOF(final BinaryNode binaryNode) { 5.1035 + try { 5.1036 + return new RuntimeNode(binaryNode, Request.INSTANCEOF).accept(this); 5.1037 + } finally { 5.1038 + end(binaryNode); 5.1039 + } 5.1040 + } 5.1041 + 5.1042 + @Override 5.1043 + public Node leaveLE(final BinaryNode binaryNode) { 5.1044 + return leaveCmp(binaryNode, Request.LE); 5.1045 + } 5.1046 + 5.1047 + @Override 5.1048 + public Node leaveLT(final BinaryNode binaryNode) { 5.1049 + return leaveCmp(binaryNode, Request.LT); 5.1050 + } 5.1051 + 5.1052 + @Override 5.1053 + public Node leaveMOD(final BinaryNode binaryNode) { 5.1054 + return leaveBinaryArithmetic(binaryNode); 5.1055 + } 5.1056 + 5.1057 + @Override 5.1058 + public Node leaveMUL(final BinaryNode binaryNode) { 5.1059 + return leaveBinaryArithmetic(binaryNode); 5.1060 + } 5.1061 + 5.1062 + @Override 5.1063 + public Node leaveNE(final BinaryNode binaryNode) { 5.1064 + return leaveCmp(binaryNode, Request.NE); 5.1065 + } 5.1066 + 5.1067 + @Override 5.1068 + public Node leaveNE_STRICT(final BinaryNode binaryNode) { 5.1069 + return leaveCmp(binaryNode, Request.NE_STRICT); 5.1070 + } 5.1071 + 5.1072 + @Override 5.1073 + public Node leaveOR(final BinaryNode binaryNode) { 5.1074 + newTemporary(Type.OBJECT, binaryNode); 5.1075 + end(binaryNode); 5.1076 + return binaryNode; 5.1077 + } 5.1078 + 5.1079 + @Override 5.1080 + public Node leaveSAR(final BinaryNode binaryNode) { 5.1081 + newTemporary(Type.INT, binaryNode); 5.1082 + end(binaryNode); 5.1083 + return binaryNode; 5.1084 + } 5.1085 + 5.1086 + @Override 5.1087 + public Node leaveSHL(final BinaryNode binaryNode) { 5.1088 + newTemporary(Type.INT, binaryNode); 5.1089 + end(binaryNode); 5.1090 + return binaryNode; 5.1091 + } 5.1092 + 5.1093 + @Override 5.1094 + public Node leaveSHR(final BinaryNode binaryNode) { 5.1095 + newTemporary(Type.LONG, binaryNode); 5.1096 + end(binaryNode); 5.1097 + return binaryNode; 5.1098 + } 5.1099 + 5.1100 + @Override 5.1101 + public Node leaveSUB(final BinaryNode binaryNode) { 5.1102 + return leaveBinaryArithmetic(binaryNode); 5.1103 + } 5.1104 + 5.1105 + @Override 5.1106 + public Node leave(final ForNode forNode) { 5.1107 + if (forNode.isForIn()) { 5.1108 + forNode.setIterator(newInternal(getCurrentFunctionNode(), compiler.uniqueName(ITERATOR_PREFIX.tag()), Type.OBJECT)); //NASHORN-73 5.1109 + /* 5.1110 + * Iterators return objects, so we need to widen the scope of the 5.1111 + * init variable if it, for example, has been assigned double type 5.1112 + * see NASHORN-50 5.1113 + */ 5.1114 + newType(forNode.getInit().getSymbol(), Type.OBJECT); 5.1115 + } 5.1116 + 5.1117 + end(forNode); 5.1118 + 5.1119 + return forNode; 5.1120 + } 5.1121 + 5.1122 + @Override 5.1123 + public Node leave(final TernaryNode ternaryNode) { 5.1124 + final Node lhs = ternaryNode.rhs(); 5.1125 + final Node rhs = ternaryNode.third(); 5.1126 + 5.1127 + ensureTypeNotUnknown(lhs); 5.1128 + ensureTypeNotUnknown(rhs); 5.1129 + 5.1130 + final Type type = Type.widest(lhs.getType(), rhs.getType()); 5.1131 + newTemporary(type, ternaryNode); 5.1132 + 5.1133 + end(ternaryNode); 5.1134 + 5.1135 + return ternaryNode; 5.1136 + } 5.1137 + 5.1138 + private static void initThis(final FunctionNode functionNode) { 5.1139 + final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null); 5.1140 + newType(thisSymbol, Type.OBJECT); 5.1141 + thisSymbol.setNeedsSlot(true); 5.1142 + functionNode.getThisNode().setSymbol(thisSymbol); 5.1143 + LOG.info("Initialized scope symbol: " + thisSymbol); 5.1144 + } 5.1145 + 5.1146 + private static void initScope(final FunctionNode functionNode) { 5.1147 + final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null); 5.1148 + newType(scopeSymbol, Type.typeFor(ScriptObject.class)); 5.1149 + scopeSymbol.setNeedsSlot(true); 5.1150 + functionNode.getScopeNode().setSymbol(scopeSymbol); 5.1151 + LOG.info("Initialized scope symbol: " + scopeSymbol); 5.1152 + } 5.1153 + 5.1154 + private static void initReturn(final FunctionNode functionNode) { 5.1155 + final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); 5.1156 + newType(returnSymbol, Type.OBJECT); 5.1157 + returnSymbol.setNeedsSlot(true); 5.1158 + functionNode.getResultNode().setSymbol(returnSymbol); 5.1159 + LOG.info("Initialized return symbol: " + returnSymbol); 5.1160 + //return symbol is always object as it's the __return__ thing. What returnType is is another matter though 5.1161 + } 5.1162 + 5.1163 + private static void initVarArg(final FunctionNode functionNode) { 5.1164 + assert functionNode.getCalleeNode() != null; 5.1165 + 5.1166 + final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); 5.1167 + newType(varArgsSymbol, Type.OBJECT_ARRAY); 5.1168 + varArgsSymbol.setNeedsSlot(true); 5.1169 + functionNode.getVarArgsNode().setSymbol(varArgsSymbol); 5.1170 + LOG.info("Initialized varargs symbol: " + varArgsSymbol); 5.1171 + 5.1172 + final String argumentsName = functionNode.getArgumentsNode().getName(); 5.1173 + final Symbol argumentsSymbol = functionNode.defineSymbol(argumentsName, IS_PARAM | IS_INTERNAL, null); 5.1174 + newType(argumentsSymbol, Type.OBJECT); 5.1175 + argumentsSymbol.setNeedsSlot(true); 5.1176 + functionNode.getArgumentsNode().setSymbol(argumentsSymbol); 5.1177 + LOG.info("Initialized vararg varArgsSymbol=" + varArgsSymbol + " argumentsSymbol=" + argumentsSymbol); 5.1178 + } 5.1179 + 5.1180 + private static void initCallee(final FunctionNode functionNode) { 5.1181 + assert functionNode.getCalleeNode() != null : functionNode + " has no callee"; 5.1182 + final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); 5.1183 + newType(calleeSymbol, Type.typeFor(ScriptFunction.class)); 5.1184 + calleeSymbol.setNeedsSlot(true); 5.1185 + functionNode.getCalleeNode().setSymbol(calleeSymbol); 5.1186 + LOG.info("Initialized callee symbol " + calleeSymbol); 5.1187 + } 5.1188 + 5.1189 + /** 5.1190 + * Initialize parameters for function node. This may require specializing 5.1191 + * types if a specialization profile is known 5.1192 + * 5.1193 + * @param functionNode the function node 5.1194 + */ 5.1195 + private void initParameters(final FunctionNode functionNode) { 5.1196 + //If a function is specialized, we don't need to tag either it return 5.1197 + // type or its parameters with the widest (OBJECT) type for safety. 5.1198 + functionNode.setReturnType(Type.UNKNOWN); 5.1199 + 5.1200 + for (final IdentNode ident : functionNode.getParameters()) { 5.1201 + addLocalDef(ident.getName()); 5.1202 + final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident); 5.1203 + if (paramSymbol != null) { 5.1204 + newType(paramSymbol, Type.UNKNOWN); 5.1205 + } 5.1206 + 5.1207 + LOG.info("Initialized param " + paramSymbol); 5.1208 + } 5.1209 + } 5.1210 + 5.1211 + /** 5.1212 + * This has to run before fix assignment types, store any type specializations for 5.1213 + * paramters, then turn then to objects for the generic version of this method 5.1214 + * 5.1215 + * @param functionNode functionNode 5.1216 + */ 5.1217 + private static void finalizeParameters(final FunctionNode functionNode) { 5.1218 + boolean nonObjectParams = false; 5.1219 + List<Type> paramSpecializations = new ArrayList<>(); 5.1220 + 5.1221 + for (final IdentNode ident : functionNode.getParameters()) { 5.1222 + final Symbol paramSymbol = ident.getSymbol(); 5.1223 + if (paramSymbol != null) { 5.1224 + Type type = paramSymbol.getSymbolType(); 5.1225 + if (type.isUnknown()) { 5.1226 + type = Type.OBJECT; 5.1227 + } 5.1228 + paramSpecializations.add(type); 5.1229 + if (!type.isObject()) { 5.1230 + nonObjectParams = true; 5.1231 + } 5.1232 + newType(paramSymbol, Type.OBJECT); 5.1233 + } 5.1234 + } 5.1235 + 5.1236 + if (!nonObjectParams) { 5.1237 + paramSpecializations = null; 5.1238 + // Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters 5.1239 + // here. If the callee has parameter specializations, we can regenerate it with those particular types for speed. 5.1240 + } else { 5.1241 + LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations); 5.1242 + } 5.1243 + 5.1244 + // parameters should not be slots for a vararg function, make sure this is the case 5.1245 + if (functionNode.isVarArg()) { 5.1246 + for (final IdentNode param : functionNode.getParameters()) { 5.1247 + param.getSymbol().setNeedsSlot(false); 5.1248 + } 5.1249 + } 5.1250 + } 5.1251 + 5.1252 + /** 5.1253 + * Move any properties from a global map into the scope of this method 5.1254 + * @param context context 5.1255 + * @param functionNode the function node for which to init scope vars 5.1256 + */ 5.1257 + private static void initFromPropertyMap(final Context context, final FunctionNode functionNode) { 5.1258 + // For a script, add scope symbols as defined in the property map 5.1259 + assert functionNode.isScript(); 5.1260 + 5.1261 + final PropertyMap map = Context.getGlobalMap(); 5.1262 + 5.1263 + for (final Property property : map.getProperties()) { 5.1264 + final String key = property.getKey(); 5.1265 + final Symbol symbol = functionNode.defineSymbol(key, IS_GLOBAL, null); 5.1266 + newType(symbol, Type.OBJECT); 5.1267 + LOG.info("Added global symbol from property map " + symbol); 5.1268 + } 5.1269 + } 5.1270 + 5.1271 + private static void ensureTypeNotUnknown(final Node node) { 5.1272 + 5.1273 + final Symbol symbol = node.getSymbol(); 5.1274 + 5.1275 + LOG.info("Ensure type not unknown for: " + symbol); 5.1276 + 5.1277 + /* 5.1278 + * Note that not just unknowns, but params need to be blown 5.1279 + * up to objects, because we can have something like 5.1280 + * 5.1281 + * function f(a) { 5.1282 + * var b = ~a; //b and a are inferred to be int 5.1283 + * return b; 5.1284 + * } 5.1285 + * 5.1286 + * In this case, it would be correct to say that "if you have 5.1287 + * an int at the callsite, just pass it". 5.1288 + * 5.1289 + * However 5.1290 + * 5.1291 + * function f(a) { 5.1292 + * var b = ~a; //b and a are inferred to be int 5.1293 + * return b == 17; //b is still inferred to be int. 5.1294 + * } 5.1295 + * 5.1296 + * can be called with f("17") and if we assume that b is an 5.1297 + * int and don't blow it up to an object in the comparison, we 5.1298 + * are screwed. I hate JavaScript. 5.1299 + * 5.1300 + * This check has to be done for any operation that might take 5.1301 + * objects as parameters, for example +, but not *, which is known 5.1302 + * to coerce types into doubles 5.1303 + */ 5.1304 + if (node.getType().isUnknown() || symbol.isParam()) { 5.1305 + newType(symbol, Type.OBJECT); 5.1306 + symbol.setCanBeUndefined(); 5.1307 + } 5.1308 + } 5.1309 + 5.1310 + private static Symbol pseudoSymbol(final String name) { 5.1311 + return new Symbol(name, 0, Type.OBJECT); 5.1312 + } 5.1313 + 5.1314 + private Symbol exceptionSymbol() { 5.1315 + return newInternal(compiler.uniqueName(EXCEPTION_PREFIX.tag()), Type.typeFor(ECMAException.class)); 5.1316 + } 5.1317 + 5.1318 + /** 5.1319 + * In an assignment, recursively make sure that there are slots for 5.1320 + * everything that has to be laid out as temporary storage, which is the 5.1321 + * case if we are assign-op:ing a BaseNode subclass. This has to be 5.1322 + * recursive to handle things like multi dimensional arrays as lhs 5.1323 + * 5.1324 + * see NASHORN-258 5.1325 + * 5.1326 + * @param functionNode the current function node (has to be passed as it changes in the visitor below) 5.1327 + * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes 5.1328 + */ 5.1329 + private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) { 5.1330 + assignmentDest.accept(new NodeVisitor() { 5.1331 + @Override 5.1332 + public Node leave(final IndexNode indexNode) { 5.1333 + final Node index = indexNode.getIndex(); 5.1334 + index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant()); 5.1335 + return indexNode; 5.1336 + } 5.1337 + }); 5.1338 + } 5.1339 + 5.1340 + /** 5.1341 + * Return the type that arithmetic ops should use. Until we have implemented better type 5.1342 + * analysis (range based) or overflow checks that are fast enough for int arithmetic, 5.1343 + * this is the number type 5.1344 + * @return the arithetic type 5.1345 + */ 5.1346 + private static Type arithType() { 5.1347 + return Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER; 5.1348 + } 5.1349 + 5.1350 + /** 5.1351 + * If types have changed, we can have failed to update vars. For example 5.1352 + * 5.1353 + * var x = 17; //x is int 5.1354 + * x = "apa"; //x is object. This will be converted fine 5.1355 + * 5.1356 + * @param functionNode 5.1357 + */ 5.1358 + private static void finalizeTypes(final FunctionNode functionNode) { 5.1359 + final Set<Node> changed = new HashSet<>(); 5.1360 + do { 5.1361 + changed.clear(); 5.1362 + functionNode.accept(new NodeVisitor() { 5.1363 + 5.1364 + private void widen(final Node node, final Type to) { 5.1365 + if (node instanceof LiteralNode) { 5.1366 + return; 5.1367 + } 5.1368 + Type from = node.getType(); 5.1369 + if (!Type.areEquivalent(from, to) && Type.widest(from, to) == to) { 5.1370 + LOG.fine("Had to post pass widen '" + node + "' " + Debug.id(node) + " from " + node.getType() + " to " + to); 5.1371 + newType(node.getSymbol(), to); 5.1372 + changed.add(node); 5.1373 + } 5.1374 + } 5.1375 + 5.1376 + /** 5.1377 + * Eg. 5.1378 + * 5.1379 + * var d = 17; 5.1380 + * var e; 5.1381 + * e = d; //initially typed as int for node type, should retype as double 5.1382 + * e = object; 5.1383 + * 5.1384 + * var d = 17; 5.1385 + * var e; 5.1386 + * 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 5.1387 + * e = object; 5.1388 + * 5.1389 + */ 5.1390 + @SuppressWarnings("fallthrough") 5.1391 + @Override 5.1392 + public Node leave(final BinaryNode binaryNode) { 5.1393 + final Type widest = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); 5.1394 + switch (binaryNode.tokenType()) { 5.1395 + default: 5.1396 + if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) { 5.1397 + break; 5.1398 + } 5.1399 + widen(binaryNode.lhs(), widest); 5.1400 + case ADD: 5.1401 + widen(binaryNode, widest); 5.1402 + break; 5.1403 + } 5.1404 + return binaryNode; 5.1405 + } 5.1406 + }); 5.1407 + } while (!changed.isEmpty()); 5.1408 + } 5.1409 + 5.1410 + /** 5.1411 + * This assign helper is called after an assignment, when all children of 5.1412 + * the assign has been processed. It fixes the types and recursively makes 5.1413 + * sure that everyhing has slots that should have them in the chain. 5.1414 + * 5.1415 + * @param binaryNode assignment node 5.1416 + */ 5.1417 + private Node leaveAssignmentNode(final BinaryNode binaryNode) { 5.1418 + final Node lhs = binaryNode.lhs(); 5.1419 + final Node rhs = binaryNode.rhs(); 5.1420 + 5.1421 + final Type type; 5.1422 + if (rhs.getType().isNumeric()) { 5.1423 + type = Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()); 5.1424 + } else { 5.1425 + type = Type.OBJECT; //force lhs to be an object if not numeric assignment, e.g. strings too. 5.1426 + } 5.1427 + newTemporary(type, binaryNode); 5.1428 + newType(lhs.getSymbol(), type); 5.1429 + end(binaryNode); 5.1430 + return binaryNode; 5.1431 + } 5.1432 + 5.1433 + private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode) { 5.1434 + return leaveSelfModifyingAssignmentNode(binaryNode, binaryNode.getWidestOperationType()); 5.1435 + } 5.1436 + 5.1437 + private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) { 5.1438 + //e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType()) is the coerce type 5.1439 + final Node lhs = binaryNode.lhs(); 5.1440 + 5.1441 + newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType 5.1442 + newTemporary(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine 5.1443 + 5.1444 + ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode); 5.1445 + 5.1446 + end(binaryNode); 5.1447 + return binaryNode; 5.1448 + } 5.1449 + 5.1450 + private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) { 5.1451 + if (currentFunction.findParentFunction() == topFunction) { 5.1452 + final List<Block> blocks = new LinkedList<>(); 5.1453 + 5.1454 + blocks.add(currentFunction.getParent()); 5.1455 + blocks.addAll(currentFunction.getReferencingParentBlocks()); 5.1456 + return blocks; 5.1457 + } 5.1458 + /* 5.1459 + * assumption: all parent blocks of an inner function will always be in the same outer function; 5.1460 + * therefore we can simply skip through intermediate functions. 5.1461 + * @see FunctionNode#addReferencingParentBlock(Block) 5.1462 + */ 5.1463 + return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction); 5.1464 + } 5.1465 + 5.1466 + private static boolean isFunctionExpressionSelfReference(final Symbol symbol) { 5.1467 + if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) { 5.1468 + return ((FunctionNode)symbol.getNode()).getIdent().getName().equals(symbol.getName()); 5.1469 + } 5.1470 + return false; 5.1471 + } 5.1472 + 5.1473 + private static Symbol newTemporary(final FunctionNode functionNode, final Type type, final Node node) { 5.1474 + LOG.info("New TEMPORARY added to " + functionNode.getName() + " type=" + type); 5.1475 + return functionNode.newTemporary(type, node); 5.1476 + } 5.1477 + 5.1478 + private Symbol newTemporary(final Type type, final Node node) { 5.1479 + return newTemporary(getCurrentFunctionNode(), type, node); 5.1480 + } 5.1481 + 5.1482 + private Symbol newInternal(final FunctionNode functionNode, final String name, final Type type) { 5.1483 + final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); 5.1484 + iter.setType(type); // NASHORN-73 5.1485 + return iter; 5.1486 + } 5.1487 + 5.1488 + private Symbol newInternal(final String name, final Type type) { 5.1489 + return newInternal(getCurrentFunctionNode(), name, type); 5.1490 + } 5.1491 + 5.1492 + private static void newType(final Symbol symbol, final Type type) { 5.1493 + final Type oldType = symbol.getSymbolType(); 5.1494 + symbol.setType(type); 5.1495 + 5.1496 + if (symbol.getSymbolType() != oldType) { 5.1497 + LOG.info("New TYPE " + type + " for " + symbol + " (was " + oldType + ")"); 5.1498 + } 5.1499 + 5.1500 + if (symbol.isParam()) { 5.1501 + symbol.setType(type); 5.1502 + LOG.info("Param type change " + symbol); 5.1503 + } 5.1504 + } 5.1505 + 5.1506 + private void clearLocalDefs() { 5.1507 + localDefs = new HashSet<>(); 5.1508 + } 5.1509 + 5.1510 + private boolean isLocalDef(final String name) { 5.1511 + return localDefs.contains(name); 5.1512 + } 5.1513 + 5.1514 + private void addLocalDef(final String name) { 5.1515 + LOG.info("Adding local def of symbol: '" + name + "'"); 5.1516 + localDefs.add(name); 5.1517 + } 5.1518 + 5.1519 + private void removeLocalDef(final String name) { 5.1520 + LOG.info("Removing local def of symbol: '" + name + "'"); 5.1521 + localDefs.remove(name); 5.1522 + } 5.1523 + 5.1524 + private void clearLocalUses() { 5.1525 + localUses = new HashSet<>(); 5.1526 + } 5.1527 + 5.1528 + private void addLocalUse(final String name) { 5.1529 + LOG.info("Adding local use of symbol: '" + name + "'"); 5.1530 + localUses.add(name); 5.1531 + } 5.1532 + 5.1533 + private static String name(final Node node) { 5.1534 + final String cn = node.getClass().getName(); 5.1535 + int lastDot = cn.lastIndexOf('.'); 5.1536 + if (lastDot == -1) { 5.1537 + return cn; 5.1538 + } 5.1539 + return cn.substring(lastDot + 1); 5.1540 + } 5.1541 + 5.1542 + private Node start(final Node node) { 5.1543 + return start(node, true); 5.1544 + } 5.1545 + 5.1546 + private Node start(final Node node, final boolean printNode) { 5.1547 + final StringBuilder sb = new StringBuilder(); 5.1548 + 5.1549 + sb.append("[ENTER "). 5.1550 + append(name(node)). 5.1551 + append("] "). 5.1552 + append(printNode ? node.toString() : ""). 5.1553 + append(" in '"). 5.1554 + append(getCurrentFunctionNode().getName()). 5.1555 + append("'"); 5.1556 + LOG.info(sb.toString()); 5.1557 + LOG.indent(); 5.1558 + 5.1559 + return node; 5.1560 + } 5.1561 + 5.1562 + private Node end(final Node node) { 5.1563 + return end(node, true); 5.1564 + } 5.1565 + 5.1566 + private Node end(final Node node, final boolean printNode) { 5.1567 + final StringBuilder sb = new StringBuilder(); 5.1568 + 5.1569 + sb.append("[LEAVE "). 5.1570 + append(name(node)). 5.1571 + append("] "). 5.1572 + append(printNode ? node.toString() : ""). 5.1573 + append(" in '"). 5.1574 + append(getCurrentFunctionNode().getName()); 5.1575 + 5.1576 + if (node.getSymbol() == null) { 5.1577 + sb.append(" <NO SYMBOL>"); 5.1578 + } else { 5.1579 + sb.append(" <symbol=").append(node.getSymbol()).append('>'); 5.1580 + } 5.1581 + 5.1582 + LOG.unindent(); 5.1583 + LOG.info(sb.toString()); 5.1584 + 5.1585 + return node; 5.1586 + } 5.1587 +}
6.1 --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Jan 29 14:25:39 2013 -0400 6.2 +++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Jan 30 12:26:45 2013 +0100 6.3 @@ -56,7 +56,6 @@ 6.4 import java.util.LinkedList; 6.5 import java.util.List; 6.6 import java.util.Map; 6.7 -import java.util.Map.Entry; 6.8 import java.util.TreeMap; 6.9 import jdk.nashorn.internal.codegen.ClassEmitter.Flag; 6.10 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 6.11 @@ -524,28 +523,6 @@ 6.12 return method; 6.13 } 6.14 6.15 - /** 6.16 - * Create a new function object, including generating its stub code. 6.17 - * 6.18 - * @param functionNode FunctionNode to utilize. 6.19 - */ 6.20 - private void newFunctionObject(final FunctionNode functionNode) { 6.21 - // Turn thisProperties into keys and symbols for the FunctionAnalyzer 6.22 - final Map<String, Node> thisProperties = functionNode.getThisProperties(); 6.23 - 6.24 - final List<String> keys = new ArrayList<>(); 6.25 - final List<Symbol> symbols = new ArrayList<>(); 6.26 - 6.27 - /* TODO - Predefine known properties. 6.28 - for (final Entry<String, Node> entry : thisProperties.entrySet()) { 6.29 - keys.add(entry.getKey()); 6.30 - symbols.add(entry.getValue().getSymbol()); 6.31 - } 6.32 - */ 6.33 - 6.34 - new FunctionObjectCreator(this, functionNode, keys, symbols).makeObject(method); 6.35 - } 6.36 - 6.37 @Override 6.38 public Node enter(final CallNode callNode) { 6.39 if (callNode.testResolved()) { 6.40 @@ -603,12 +580,12 @@ 6.41 6.42 final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); 6.43 // load evaluated code 6.44 - load(evalArgs.code); 6.45 + load(evalArgs.getCode()); 6.46 method.convert(Type.OBJECT); 6.47 // special/extra 'eval' arguments 6.48 - load(evalArgs.evalThis); 6.49 - method.load(evalArgs.location); 6.50 - method.load(evalArgs.strictMode); 6.51 + load(evalArgs.getThis()); 6.52 + method.load(evalArgs.getLocation()); 6.53 + method.load(evalArgs.getStrictMode()); 6.54 method.convert(Type.OBJECT); 6.55 6.56 // direct call to Global.directEval 6.57 @@ -683,7 +660,7 @@ 6.58 } 6.59 6.60 if (callee.needsCallee()) { // TODO: always true 6.61 - newFunctionObject(callee); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead. 6.62 + new FunctionObjectCreator(CodeGenerator.this, callee).makeObject(method); // TODO: if callee not needed, function object is used only to pass scope (could be optimized). if neither the scope nor the function object is needed by the callee, we can pass null instead. 6.63 } 6.64 6.65 loadArgs(args, signature, isVarArg, argCount); 6.66 @@ -1300,6 +1277,7 @@ 6.67 @SuppressWarnings("rawtypes") 6.68 @Override 6.69 public Node enter(final LiteralNode literalNode) { 6.70 + assert literalNode.getSymbol() != null : literalNode + " has no symbol"; 6.71 load(literalNode).store(literalNode.getSymbol()); 6.72 return null; 6.73 } 6.74 @@ -1410,7 +1388,7 @@ 6.75 return null; 6.76 } 6.77 6.78 - newFunctionObject(referenceNode.getReference()); 6.79 + new FunctionObjectCreator(this, referenceNode.getReference()).makeObject(method); 6.80 6.81 return null; 6.82 } 6.83 @@ -1561,15 +1539,12 @@ 6.84 /* 6.85 * First check if this should be something other than a runtime node 6.86 * AccessSpecializer might have changed the type 6.87 + * 6.88 + * TODO - remove this - Access Specializer will always know after Attr/Lower 6.89 */ 6.90 - if (runtimeNode.isPrimitive()) { 6.91 - 6.92 + if (runtimeNode.isPrimitive() && !runtimeNode.isFinal()) { 6.93 final Node lhs = runtimeNode.getArgs().get(0); 6.94 - Node rhs = null; 6.95 - 6.96 - if (runtimeNode.getArgs().size() > 1) { 6.97 - rhs = runtimeNode.getArgs().get(1); 6.98 - } 6.99 + final Node rhs = runtimeNode.getArgs().size() > 1 ? runtimeNode.getArgs().get(1) : null; 6.100 6.101 final Type type = runtimeNode.getType(); 6.102 final Symbol symbol = runtimeNode.getSymbol(); 6.103 @@ -1590,7 +1565,15 @@ 6.104 case GT: 6.105 return enterCmp(lhs, rhs, Condition.GT, type, symbol); 6.106 case ADD: 6.107 - return enterNumericAdd(lhs, rhs, type, symbol); 6.108 + Type widest = Type.widest(lhs.getType(), rhs.getType()); 6.109 + load(lhs); 6.110 + method.convert(widest); 6.111 + load(rhs); 6.112 + method.convert(widest); 6.113 + method.add(); 6.114 + method.convert(type); 6.115 + method.store(symbol); 6.116 + return null; 6.117 default: 6.118 // it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar 6.119 // assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state"; 6.120 @@ -1605,7 +1588,7 @@ 6.121 return null; 6.122 } 6.123 6.124 - if (specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) { 6.125 + if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) { 6.126 return null; 6.127 } 6.128 6.129 @@ -2029,7 +2012,6 @@ 6.130 if (needsScope) { 6.131 method.loadScope(); 6.132 } 6.133 - 6.134 load(init); 6.135 6.136 if (needsScope) { 6.137 @@ -2324,12 +2306,11 @@ 6.138 } 6.139 6.140 private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) { 6.141 - assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type); 6.142 + assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs); 6.143 load(lhs); 6.144 load(rhs); 6.145 method.add(); 6.146 method.store(symbol); 6.147 - 6.148 return null; 6.149 } 6.150 6.151 @@ -3152,17 +3133,19 @@ 6.152 /** 6.153 * Take the original target args from the stack and use them 6.154 * together with the value to be stored to emit the store code 6.155 + * 6.156 + * The case that targetSymbol is in scope (!hasSlot) and we actually 6.157 + * need to do a conversion on non-equivalent types exists, but is 6.158 + * very rare. See for example test/script/basic/access-specializer.js 6.159 */ 6.160 - if (targetSymbol.hasSlot()) { 6.161 - method.convert(target.getType()); 6.162 - } 6.163 + method.convert(target.getType()); 6.164 6.165 target.accept(new NodeVisitor(compileUnit, method) { 6.166 @Override 6.167 public Node enter(final IdentNode node) { 6.168 final Symbol symbol = target.getSymbol(); 6.169 if (symbol.isScope()) { 6.170 - if(symbol.isFastScope(currentFunction)) { 6.171 + if (symbol.isFastScope(currentFunction)) { 6.172 storeFastScopeVar(target.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags()); 6.173 } else { 6.174 method.dynamicSet(target.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
7.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java Tue Jan 29 14:25:39 2013 -0400 7.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java Wed Jan 30 12:26:45 2013 +0100 7.3 @@ -70,8 +70,16 @@ 7.4 INITIALIZED, 7.5 /** method has been parsed */ 7.6 PARSED, 7.7 + /** constant folding pass */ 7.8 + CONSTANT_FOLDED, 7.9 /** method has been lowered */ 7.10 LOWERED, 7.11 + /** method hass been attributed */ 7.12 + ATTR, 7.13 + /** method has been split */ 7.14 + SPLIT, 7.15 + /** method has had its types finalized */ 7.16 + FINALIZED, 7.17 /** method has been emitted to bytecode */ 7.18 EMITTED 7.19 } 7.20 @@ -136,25 +144,6 @@ 7.21 private static final boolean LAZY_JIT = false; 7.22 7.23 /** 7.24 - * Should we use integers for literals and all operations 7.25 - * that are based in constant and parameter assignment as 7.26 - * long as they can be proven not to overflow? With this enabled 7.27 - * var x = 17 would tag x as an integer, rather than a double, 7.28 - * but as soon as it is used in an operation that may potentially 7.29 - * overflow, such as an add, we conservatively widen it to double 7.30 - * (number type). This is because overflow checks in the code 7.31 - * are likely much more expensive that method specialization 7.32 - * 7.33 - * @return true if numbers should start as ints, false if they should 7.34 - * start as doubles 7.35 - */ 7.36 - static boolean shouldUseIntegers() { 7.37 - return USE_INTS; 7.38 - } 7.39 - 7.40 - private static final boolean USE_INTS; 7.41 - 7.42 - /** 7.43 * Should we use integers for arithmetic operations as well? 7.44 * TODO: We currently generate no overflow checks so this is 7.45 * disabled 7.46 @@ -165,13 +154,12 @@ 7.47 * operands by default. 7.48 */ 7.49 static boolean shouldUseIntegerArithmetic() { 7.50 - return Compiler.shouldUseIntegers() && USE_INT_ARITH; 7.51 + return USE_INT_ARITH; 7.52 } 7.53 7.54 private static final boolean USE_INT_ARITH; 7.55 7.56 static { 7.57 - USE_INTS = !Options.getBooleanProperty("nashorn.compiler.ints.disable"); 7.58 USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic"); 7.59 7.60 assert !USE_INT_ARITH : "Integer arithmetic is not enabled"; 7.61 @@ -338,46 +326,93 @@ 7.62 try { 7.63 strict |= functionNode.isStrictMode(); 7.64 7.65 - if (!state.contains(State.LOWERED)) { 7.66 - debugPrintAST(); 7.67 + /* 7.68 + * These are the compile phases: 7.69 + * 7.70 + * Constant folding pass 7.71 + * Simple constant folding that will make elementary constructs go away 7.72 + * 7.73 + * Lower (Control flow pass) 7.74 + * Finalizes the control flow. Clones blocks for finally constructs and 7.75 + * similar things. Establishes termination criteria for nodes 7.76 + * Guarantee return instructions to method making sure control flow 7.77 + * cannot fall off the end. Replacing high level nodes with lower such 7.78 + * as runtime nodes where applicable. 7.79 + * 7.80 + * Attr 7.81 + * Assign symbols and types to all nodes. 7.82 + * 7.83 + * Splitter 7.84 + * Split the AST into several compile units based on a size heuristic 7.85 + * Splitter needs attributed AST for weight calculations (e.g. is 7.86 + * a + b a ScriptRuntime.ADD with call overhead or a dadd with much 7.87 + * less). Split IR can lead to scope information being changed. 7.88 + * 7.89 + * Contract: all variables must have slot assignments and scope assignments 7.90 + * before lowering. 7.91 + * 7.92 + * FinalizeTypes 7.93 + * This pass finalizes the types for nodes. If Attr created wider types than 7.94 + * known during the first pass, convert nodes are inserted or access nodes 7.95 + * are specialized where scope accesses. 7.96 + * 7.97 + * Runtime nodes may be removed and primitivized or reintroduced depending 7.98 + * on information that was established in Attr. 7.99 + * 7.100 + * CodeGeneration 7.101 + * Emit bytecode 7.102 + * 7.103 + */ 7.104 + 7.105 + debugPrintAST(); 7.106 + 7.107 + if (!state.contains(State.FINALIZED)) { 7.108 + LOG.info("Folding constants in '" + functionNode.getName() + "'"); 7.109 + functionNode.accept(new FoldConstants()); 7.110 + state.add(State.CONSTANT_FOLDED); 7.111 + 7.112 LOG.info("Lowering '" + functionNode.getName() + "'"); 7.113 functionNode.accept(new Lower(this)); 7.114 state.add(State.LOWERED); 7.115 7.116 + LOG.info("Attributing types '" + functionNode.getName() + "'"); 7.117 + functionNode.accept(new Attr(this)); 7.118 + state.add(State.ATTR); 7.119 + 7.120 + this.scriptName = computeNames(); 7.121 + 7.122 + // Main script code always goes to this compile unit. Note that since we start this with zero weight 7.123 + // and add script code last this class may end up slightly larger than others, but reserving one class 7.124 + // just for the main script seems wasteful. 7.125 + final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0L); 7.126 + LOG.info("Splitting '" + functionNode.getName() + "'"); 7.127 + new Splitter(this, functionNode, scriptCompileUnit).split(); 7.128 + state.add(State.SPLIT); 7.129 + assert functionNode.getCompileUnit() == scriptCompileUnit; 7.130 + 7.131 + assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode(); 7.132 + if (functionNode.isStrictMode()) { 7.133 + strict = true; 7.134 + } 7.135 + 7.136 + LOG.info("Finalizing types for '" + functionNode.getName() + "'"); 7.137 + functionNode.accept(new FinalizeTypes(this)); 7.138 + state.add(State.FINALIZED); 7.139 + 7.140 + // print ast and parse if --print-lower-ast and/or --print-lower-parse are selected 7.141 + debugPrintAST(); 7.142 + debugPrintParse(); 7.143 + 7.144 if (errors.hasErrors()) { 7.145 return false; 7.146 } 7.147 } 7.148 7.149 - scriptName = computeNames(); 7.150 - 7.151 - // Main script code always goes to this compile unit. Note that since we start this with zero weight 7.152 - // and add script code last this class may end up slightly larger than others, but reserving one class 7.153 - // just for the main script seems wasteful. 7.154 - final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0l); 7.155 - LOG.info("Splitting '" + functionNode.getName() + "'"); 7.156 - new Splitter(this, functionNode, scriptCompileUnit).split(); 7.157 - assert functionNode.getCompileUnit() == scriptCompileUnit; 7.158 - 7.159 - /** Compute compile units */ 7.160 - assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode(); 7.161 - if (functionNode.isStrictMode()) { 7.162 - strict = true; 7.163 - } 7.164 - 7.165 - LOG.info("Adjusting slot and scope information for symbols for '" + functionNode.getName() + "'"); 7.166 - functionNode.accept(new Lower.FinalizeSymbols()); 7.167 - 7.168 - LOG.info("Specializing callsite types for '" + functionNode.getName() + "'"); 7.169 - functionNode.accept(new AccessSpecializer()); 7.170 - 7.171 try { 7.172 LOG.info("Emitting bytecode for '" + functionNode.getName() + "'"); 7.173 final CodeGenerator codegen = new CodeGenerator(this); 7.174 functionNode.accept(codegen); 7.175 codegen.generateScopeCalls(); 7.176 - debugPrintAST(); 7.177 - debugPrintParse(); 7.178 } catch (final VerifyError e) { 7.179 if (context._verify_code || context._print_code) { 7.180 context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java Wed Jan 30 12:26:45 2013 +0100 8.3 @@ -0,0 +1,919 @@ 8.4 +/* 8.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 8.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 8.7 + * 8.8 + * This code is free software; you can redistribute it and/or modify it 8.9 + * under the terms of the GNU General Public License version 2 only, as 8.10 + * published by the Free Software Foundation. Oracle designates this 8.11 + * particular file as subject to the "Classpath" exception as provided 8.12 + * by Oracle in the LICENSE file that accompanied this code. 8.13 + * 8.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 8.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 8.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 8.17 + * version 2 for more details (a copy is included in the LICENSE file that 8.18 + * accompanied this code). 8.19 + * 8.20 + * You should have received a copy of the GNU General Public License version 8.21 + * 2 along with this work; if not, write to the Free Software Foundation, 8.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 8.23 + * 8.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 8.25 + * or visit www.oracle.com if you need additional information or have any 8.26 + * questions. 8.27 + */ 8.28 + 8.29 +package jdk.nashorn.internal.codegen; 8.30 + 8.31 +import java.util.HashSet; 8.32 +import java.util.List; 8.33 + 8.34 +import jdk.nashorn.internal.codegen.types.Type; 8.35 +import jdk.nashorn.internal.ir.AccessNode; 8.36 +import jdk.nashorn.internal.ir.Assignment; 8.37 +import jdk.nashorn.internal.ir.BinaryNode; 8.38 +import jdk.nashorn.internal.ir.Block; 8.39 +import jdk.nashorn.internal.ir.CallNode; 8.40 +import jdk.nashorn.internal.ir.CallNode.EvalArgs; 8.41 +import jdk.nashorn.internal.ir.CaseNode; 8.42 +import jdk.nashorn.internal.ir.CatchNode; 8.43 +import jdk.nashorn.internal.ir.DoWhileNode; 8.44 +import jdk.nashorn.internal.ir.ExecuteNode; 8.45 +import jdk.nashorn.internal.ir.ForNode; 8.46 +import jdk.nashorn.internal.ir.FunctionNode; 8.47 +import jdk.nashorn.internal.ir.IdentNode; 8.48 +import jdk.nashorn.internal.ir.IfNode; 8.49 +import jdk.nashorn.internal.ir.IndexNode; 8.50 +import jdk.nashorn.internal.ir.LiteralNode; 8.51 +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 8.52 +import jdk.nashorn.internal.ir.Node; 8.53 +import jdk.nashorn.internal.ir.ReferenceNode; 8.54 +import jdk.nashorn.internal.ir.ReturnNode; 8.55 +import jdk.nashorn.internal.ir.RuntimeNode; 8.56 +import jdk.nashorn.internal.ir.RuntimeNode.Request; 8.57 +import jdk.nashorn.internal.ir.SwitchNode; 8.58 +import jdk.nashorn.internal.ir.Symbol; 8.59 +import jdk.nashorn.internal.ir.TernaryNode; 8.60 +import jdk.nashorn.internal.ir.ThrowNode; 8.61 +import jdk.nashorn.internal.ir.TypeOverride; 8.62 +import jdk.nashorn.internal.ir.UnaryNode; 8.63 +import jdk.nashorn.internal.ir.VarNode; 8.64 +import jdk.nashorn.internal.ir.WhileNode; 8.65 +import jdk.nashorn.internal.ir.WithNode; 8.66 +import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 8.67 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 8.68 +import jdk.nashorn.internal.parser.Token; 8.69 +import jdk.nashorn.internal.parser.TokenType; 8.70 +import jdk.nashorn.internal.runtime.Debug; 8.71 +import jdk.nashorn.internal.runtime.DebugLogger; 8.72 +import jdk.nashorn.internal.runtime.JSType; 8.73 +import jdk.nashorn.internal.runtime.Source; 8.74 + 8.75 +/** 8.76 + * Lower to more primitive operations. After lowering, an AST has symbols and 8.77 + * types. Lowering may also add specialized versions of methods to the script if 8.78 + * the optimizer is turned on. 8.79 + * 8.80 + * Any expression that requires temporary storage as part of computation will 8.81 + * also be detected here and give a temporary symbol 8.82 + * 8.83 + * For any op that we process in FinalizeTypes it is an absolute guarantee 8.84 + * that scope and slot information is correct. This enables e.g. AccessSpecialization 8.85 + * and frame optimizations 8.86 + */ 8.87 + 8.88 +final class FinalizeTypes extends NodeOperatorVisitor { 8.89 + 8.90 + /** Current source. */ 8.91 + private final Source source; 8.92 + 8.93 + private static final DebugLogger LOG = new DebugLogger("finalize"); 8.94 + 8.95 + /** 8.96 + * Constructor. 8.97 + * 8.98 + * @param compiler the compiler 8.99 + */ 8.100 + FinalizeTypes(final Compiler compiler) { 8.101 + this.source = compiler.getSource(); 8.102 + } 8.103 + 8.104 + @Override 8.105 + public Node leave(final CallNode callNode) { 8.106 + final EvalArgs evalArgs = callNode.getEvalArgs(); 8.107 + if (evalArgs != null) { 8.108 + evalArgs.setCode(evalArgs.getCode().accept(this)); 8.109 + } 8.110 + 8.111 + // AccessSpecializer - call return type may change the access for this location 8.112 + final Node function = callNode.getFunction(); 8.113 + if (function instanceof ReferenceNode) { 8.114 + setTypeOverride(callNode, ((ReferenceNode)function).getReference().getType()); 8.115 + } 8.116 + return callNode; 8.117 + } 8.118 + 8.119 + private Node leaveUnary(final UnaryNode unaryNode) { 8.120 + unaryNode.setRHS(convert(unaryNode.rhs(), unaryNode.getType())); 8.121 + return unaryNode; 8.122 + } 8.123 + 8.124 + @Override 8.125 + public Node leaveADD(final UnaryNode unaryNode) { 8.126 + return leaveUnary(unaryNode); 8.127 + } 8.128 + 8.129 + @Override 8.130 + public Node leaveBIT_NOT(final UnaryNode unaryNode) { 8.131 + return leaveUnary(unaryNode); 8.132 + } 8.133 + 8.134 + @Override 8.135 + public Node leaveCONVERT(final UnaryNode unaryNode) { 8.136 + assert unaryNode.rhs().tokenType() != TokenType.CONVERT : "convert(convert encountered. check its origin and remove it"; 8.137 + return unaryNode; 8.138 + } 8.139 + 8.140 + @Override 8.141 + public Node leaveDECINC(final UnaryNode unaryNode) { 8.142 + specialize(unaryNode); 8.143 + return unaryNode; 8.144 + } 8.145 + 8.146 + @Override 8.147 + public Node leaveNEW(final UnaryNode unaryNode) { 8.148 + assert unaryNode.getSymbol() != null && unaryNode.getSymbol().getSymbolType().isObject(); 8.149 + ((CallNode)unaryNode.rhs()).setIsNew(); 8.150 + return unaryNode; 8.151 + } 8.152 + 8.153 + @Override 8.154 + public Node leaveSUB(final UnaryNode unaryNode) { 8.155 + return leaveUnary(unaryNode); 8.156 + } 8.157 + 8.158 + /** 8.159 + * Add is a special binary, as it works not only on arithmetic, but for 8.160 + * strings etc as well. 8.161 + */ 8.162 + @Override 8.163 + public Node leaveADD(final BinaryNode binaryNode) { 8.164 + final Node lhs = binaryNode.lhs(); 8.165 + final Node rhs = binaryNode.rhs(); 8.166 + 8.167 + final Type type = binaryNode.getType(); 8.168 + 8.169 + if (type.isObject()) { 8.170 + if (!isAddString(binaryNode)) { 8.171 + return new RuntimeNode(binaryNode, Request.ADD); 8.172 + } 8.173 + } 8.174 + 8.175 + binaryNode.setLHS(convert(lhs, type)); 8.176 + binaryNode.setRHS(convert(rhs, type)); 8.177 + return binaryNode; 8.178 + } 8.179 + 8.180 + @Override 8.181 + public Node leaveAND(final BinaryNode binaryNode) { 8.182 + return binaryNode; 8.183 + } 8.184 + 8.185 + @Override 8.186 + public Node leaveASSIGN(final BinaryNode binaryNode) { 8.187 + Type destType = specialize(binaryNode); 8.188 + if (destType == null) { 8.189 + destType = binaryNode.getType(); 8.190 + } 8.191 + binaryNode.setRHS(convert(binaryNode.rhs(), destType)); 8.192 + return binaryNode; 8.193 + } 8.194 + 8.195 + @Override 8.196 + public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { 8.197 + return leaveASSIGN(binaryNode); 8.198 + } 8.199 + 8.200 + @Override 8.201 + public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { 8.202 + return leaveASSIGN(binaryNode); 8.203 + } 8.204 + 8.205 + @Override 8.206 + public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { 8.207 + return leaveASSIGN(binaryNode); 8.208 + } 8.209 + 8.210 + @Override 8.211 + public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { 8.212 + return leaveASSIGN(binaryNode); 8.213 + } 8.214 + 8.215 + @Override 8.216 + public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { 8.217 + return leaveASSIGN(binaryNode); 8.218 + } 8.219 + 8.220 + @Override 8.221 + public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { 8.222 + return leaveASSIGN(binaryNode); 8.223 + } 8.224 + 8.225 + @Override 8.226 + public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { 8.227 + return leaveASSIGN(binaryNode); 8.228 + } 8.229 + 8.230 + @Override 8.231 + public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { 8.232 + return leaveASSIGN(binaryNode); 8.233 + } 8.234 + 8.235 + @Override 8.236 + public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { 8.237 + return leaveASSIGN(binaryNode); 8.238 + } 8.239 + 8.240 + @Override 8.241 + public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { 8.242 + return leaveASSIGN(binaryNode); 8.243 + } 8.244 + 8.245 + @Override 8.246 + public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { 8.247 + return leaveASSIGN(binaryNode); 8.248 + } 8.249 + 8.250 + @Override 8.251 + public Node leaveBIT_AND(BinaryNode binaryNode) { 8.252 + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger() : binaryNode.getSymbol(); 8.253 + return leaveBinary(binaryNode, Type.INT, Type.INT); 8.254 + } 8.255 + 8.256 + @Override 8.257 + public Node leaveBIT_OR(BinaryNode binaryNode) { 8.258 + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger(); 8.259 + return leaveBinary(binaryNode, Type.INT, Type.INT); 8.260 + } 8.261 + 8.262 + @Override 8.263 + public Node leaveBIT_XOR(BinaryNode binaryNode) { 8.264 + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isInteger(); 8.265 + return leaveBinary(binaryNode, Type.INT, Type.INT); 8.266 + } 8.267 + 8.268 + @Override 8.269 + public Node leaveCOMMALEFT(final BinaryNode binaryNode) { 8.270 + assert binaryNode.getSymbol() != null; 8.271 + binaryNode.setRHS(discard(binaryNode.rhs())); 8.272 + // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed 8.273 + // in that case, update the node type as well 8.274 + propagateType(binaryNode, binaryNode.lhs().getType()); 8.275 + return binaryNode; 8.276 + } 8.277 + 8.278 + @Override 8.279 + public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { 8.280 + assert binaryNode.getSymbol() != null; 8.281 + binaryNode.setLHS(discard(binaryNode.lhs())); 8.282 + // AccessSpecializer - the type of rhs, which is the remaining value of this node may have changed 8.283 + // in that case, update the node type as well 8.284 + propagateType(binaryNode, binaryNode.rhs().getType()); 8.285 + return binaryNode; 8.286 + } 8.287 + 8.288 + @Override 8.289 + public Node leaveDIV(final BinaryNode binaryNode) { 8.290 + return leaveBinaryArith(binaryNode); 8.291 + } 8.292 + 8.293 + 8.294 + @Override 8.295 + public Node leaveEQ(final BinaryNode binaryNode) { 8.296 + return leaveCmp(binaryNode, Request.EQ); 8.297 + } 8.298 + 8.299 + @Override 8.300 + public Node leaveEQ_STRICT(final BinaryNode binaryNode) { 8.301 + return leaveCmp(binaryNode, Request.EQ_STRICT); 8.302 + } 8.303 + 8.304 + @Override 8.305 + public Node leaveGE(final BinaryNode binaryNode) { 8.306 + return leaveCmp(binaryNode, Request.GE); 8.307 + } 8.308 + 8.309 + @Override 8.310 + public Node leaveGT(final BinaryNode binaryNode) { 8.311 + return leaveCmp(binaryNode, Request.GT); 8.312 + } 8.313 + 8.314 + @Override 8.315 + public Node leaveLE(final BinaryNode binaryNode) { 8.316 + return leaveCmp(binaryNode, Request.LE); 8.317 + } 8.318 + 8.319 + @Override 8.320 + public Node leaveLT(final BinaryNode binaryNode) { 8.321 + return leaveCmp(binaryNode, Request.LT); 8.322 + } 8.323 + 8.324 + @Override 8.325 + public Node leaveMOD(final BinaryNode binaryNode) { 8.326 + return leaveBinaryArith(binaryNode); 8.327 + } 8.328 + 8.329 + @Override 8.330 + public Node leaveMUL(final BinaryNode binaryNode) { 8.331 + return leaveBinaryArith(binaryNode); 8.332 + } 8.333 + 8.334 + @Override 8.335 + public Node leaveNE(final BinaryNode binaryNode) { 8.336 + return leaveCmp(binaryNode, Request.NE); 8.337 + } 8.338 + 8.339 + @Override 8.340 + public Node leaveNE_STRICT(final BinaryNode binaryNode) { 8.341 + return leaveCmp(binaryNode, Request.NE_STRICT); 8.342 + } 8.343 + 8.344 + @Override 8.345 + public Node leaveOR(final BinaryNode binaryNode) { 8.346 + return binaryNode; 8.347 + } 8.348 + 8.349 + @Override 8.350 + public Node leaveSAR(final BinaryNode binaryNode) { 8.351 + return leaveBinary(binaryNode, Type.INT, Type.INT); 8.352 + } 8.353 + 8.354 + @Override 8.355 + public Node leaveSHL(final BinaryNode binaryNode) { 8.356 + return leaveBinary(binaryNode, Type.INT, Type.INT); 8.357 + } 8.358 + 8.359 + @Override 8.360 + public Node leaveSHR(final BinaryNode binaryNode) { 8.361 + assert binaryNode.getSymbol() != null && binaryNode.getSymbol().getSymbolType().isLong(); 8.362 + return leaveBinary(binaryNode, Type.INT, Type.INT); 8.363 + } 8.364 + 8.365 + @Override 8.366 + public Node leaveSUB(final BinaryNode binaryNode) { 8.367 + return leaveBinaryArith(binaryNode); 8.368 + } 8.369 + 8.370 + @Override 8.371 + public Node enter(final Block block) { 8.372 + updateSymbols(block); 8.373 + return block; 8.374 + } 8.375 + 8.376 + @Override 8.377 + public Node leave(final CatchNode catchNode) { 8.378 + final Node exceptionCondition = catchNode.getExceptionCondition(); 8.379 + if (exceptionCondition != null) { 8.380 + catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); 8.381 + } 8.382 + return catchNode; 8.383 + } 8.384 + 8.385 + @Override 8.386 + public Node enter(final DoWhileNode doWhileNode) { 8.387 + return enter((WhileNode)doWhileNode); 8.388 + } 8.389 + 8.390 + @Override 8.391 + public Node leave(final DoWhileNode doWhileNode) { 8.392 + return leave((WhileNode)doWhileNode); 8.393 + } 8.394 + 8.395 + @Override 8.396 + public Node leave(final ExecuteNode executeNode) { 8.397 + executeNode.setExpression(discard(executeNode.getExpression())); 8.398 + return executeNode; 8.399 + } 8.400 + 8.401 + @Override 8.402 + public Node leave(final ForNode forNode) { 8.403 + final Node init = forNode.getInit(); 8.404 + final Node test = forNode.getTest(); 8.405 + final Node modify = forNode.getModify(); 8.406 + 8.407 + if (forNode.isForIn()) { 8.408 + forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 8.409 + return forNode; 8.410 + } 8.411 + 8.412 + if (init != null) { 8.413 + forNode.setInit(discard(init)); 8.414 + } 8.415 + 8.416 + if (test != null) { 8.417 + forNode.setTest(convert(test, Type.BOOLEAN)); 8.418 + } else { 8.419 + assert forNode.hasGoto() : "forNode " + forNode + " needs goto and is missing it in " + getCurrentFunctionNode(); 8.420 + } 8.421 + 8.422 + if (modify != null) { 8.423 + forNode.setModify(discard(modify)); 8.424 + } 8.425 + 8.426 + return forNode; 8.427 + } 8.428 + 8.429 + @Override 8.430 + public Node enter(final FunctionNode functionNode) { 8.431 + updateSymbols(functionNode); 8.432 + return functionNode; 8.433 + } 8.434 + 8.435 + @Override 8.436 + public Node leave(final IfNode ifNode) { 8.437 + final Node test = convert(ifNode.getTest(), Type.BOOLEAN); 8.438 + ifNode.setTest(test); 8.439 + return ifNode; 8.440 + } 8.441 + 8.442 + @SuppressWarnings("rawtypes") 8.443 + @Override 8.444 + public Node enter(final LiteralNode literalNode) { 8.445 + if (literalNode instanceof ArrayLiteralNode) { 8.446 + final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; 8.447 + final Node[] array = arrayLiteralNode.getValue(); 8.448 + final Type elementType = arrayLiteralNode.getElementType(); 8.449 + 8.450 + for (int i = 0; i < array.length; i++) { 8.451 + final Node element = array[i]; 8.452 + if (element != null) { 8.453 + array[i] = convert(element.accept(this), elementType); 8.454 + } 8.455 + } 8.456 + } 8.457 + 8.458 + return null; 8.459 + } 8.460 + 8.461 + @Override 8.462 + public Node leave(final ReturnNode returnNode) { 8.463 + final Node expr = returnNode.getExpression(); 8.464 + if (expr != null) { 8.465 + returnNode.setExpression(convert(expr, getCurrentFunctionNode().getReturnType())); 8.466 + } 8.467 + return returnNode; 8.468 + } 8.469 + 8.470 + @Override 8.471 + public Node leave(final RuntimeNode runtimeNode) { 8.472 + final List<Node> args = runtimeNode.getArgs(); 8.473 + for (final Node arg : args) { 8.474 + assert !arg.getType().isUnknown(); 8.475 + } 8.476 + return runtimeNode; 8.477 + } 8.478 + 8.479 + @Override 8.480 + public Node leave(final SwitchNode switchNode) { 8.481 + final Node expression = switchNode.getExpression(); 8.482 + final List<CaseNode> cases = switchNode.getCases(); 8.483 + final boolean allInteger = switchNode.getTag().getSymbolType().isInteger(); 8.484 + 8.485 + if (!allInteger) { 8.486 + switchNode.setExpression(convert(expression, Type.OBJECT)); 8.487 + for (final CaseNode caseNode : cases) { 8.488 + final Node test = caseNode.getTest(); 8.489 + if (test != null) { 8.490 + caseNode.setTest(convert(test, Type.OBJECT)); 8.491 + } 8.492 + } 8.493 + } 8.494 + 8.495 + return switchNode; 8.496 + } 8.497 + 8.498 + @Override 8.499 + public Node leave(final TernaryNode ternaryNode) { 8.500 + ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN)); 8.501 + return ternaryNode; 8.502 + } 8.503 + 8.504 + @Override 8.505 + public Node leave(final ThrowNode throwNode) { 8.506 + throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT)); 8.507 + return throwNode; 8.508 + } 8.509 + 8.510 + @Override 8.511 + public Node leave(final VarNode varNode) { 8.512 + final Node rhs = varNode.getInit(); 8.513 + if (rhs != null) { 8.514 + Type destType = specialize(varNode); 8.515 + if (destType == null) { 8.516 + destType = varNode.getType(); 8.517 + } 8.518 + assert varNode.hasType() : varNode + " doesn't have a type"; 8.519 + varNode.setInit(convert(rhs, destType)); 8.520 + } 8.521 + return varNode; 8.522 + } 8.523 + 8.524 + @Override 8.525 + public Node leave(final WhileNode whileNode) { 8.526 + final Node test = whileNode.getTest(); 8.527 + if (test != null) { 8.528 + whileNode.setTest(convert(test, Type.BOOLEAN)); 8.529 + } 8.530 + return whileNode; 8.531 + } 8.532 + 8.533 + @Override 8.534 + public Node leave(final WithNode withNode) { 8.535 + withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT)); 8.536 + return withNode; 8.537 + } 8.538 + 8.539 + private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { 8.540 + if (!symbol.isScope()) { 8.541 + LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope"); 8.542 + } 8.543 + if (loseSlot && symbol.hasSlot()) { 8.544 + LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope"); 8.545 + } 8.546 + } 8.547 + 8.548 + /** 8.549 + * Called after a block or function node (subclass of block) is finished. Guarantees 8.550 + * that scope and slot information is correct for every symbol 8.551 + * @param block block for which to to finalize type info. 8.552 + */ 8.553 + private static void updateSymbols(final Block block) { 8.554 + 8.555 + if (!block.needsScope()) { 8.556 + return; // nothing to do 8.557 + } 8.558 + 8.559 + assert !(block instanceof FunctionNode) || block.getFunction() == block; 8.560 + 8.561 + final FunctionNode functionNode = block.getFunction(); 8.562 + final List<Symbol> symbols = block.getFrame().getSymbols(); 8.563 + final boolean allVarsInScope = functionNode.varsInScope(); 8.564 + final boolean isVarArg = functionNode.isVarArg(); 8.565 + 8.566 + for (final Symbol symbol : symbols) { 8.567 + if (symbol.isInternal() || symbol.isThis()) { 8.568 + continue; 8.569 + } 8.570 + 8.571 + if (symbol.isVar()) { 8.572 + if (allVarsInScope || symbol.isScope()) { 8.573 + updateSymbolsLog(functionNode, symbol, true); 8.574 + symbol.setIsScope(); 8.575 + symbol.setNeedsSlot(false); 8.576 + } else { 8.577 + assert symbol.hasSlot() : symbol + " should have a slot only, no scope"; 8.578 + } 8.579 + } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) { 8.580 + updateSymbolsLog(functionNode, symbol, isVarArg); 8.581 + symbol.setIsScope(); 8.582 + symbol.setNeedsSlot(!isVarArg); 8.583 + } 8.584 + } 8.585 + } 8.586 + 8.587 + /** 8.588 + * Exit a comparison node and do the appropriate replacements. We need to introduce runtime 8.589 + * nodes late for comparisons as types aren't known until the last minute 8.590 + * 8.591 + * Both compares and adds may turn into runtimes node at this level as when we first bump 8.592 + * into the op in Attr, we may type it according to what we know there, which may be wrong later 8.593 + * 8.594 + * e.g. i (int) < 5 -> normal compare 8.595 + * i = object 8.596 + * then the post pass that would add the conversion to the 5 needs to 8.597 + * 8.598 + * @param binaryNode binary node to leave 8.599 + * @param request runtime request 8.600 + * @return lowered cmp node 8.601 + */ 8.602 + @SuppressWarnings("fallthrough") 8.603 + private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { 8.604 + final Node lhs = binaryNode.lhs(); 8.605 + final Node rhs = binaryNode.rhs(); 8.606 + 8.607 + Type widest = Type.widest(lhs.getType(), rhs.getType()); 8.608 + 8.609 + boolean newRuntimeNode = false, finalized = false; 8.610 + switch (request) { 8.611 + case EQ_STRICT: 8.612 + case NE_STRICT: 8.613 + if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { 8.614 + newRuntimeNode = true; 8.615 + widest = Type.OBJECT; 8.616 + finalized = true; 8.617 + } 8.618 + //fallthru 8.619 + default: 8.620 + if (newRuntimeNode || widest.isObject()) { 8.621 + final RuntimeNode runtimeNode = new RuntimeNode(binaryNode, request); 8.622 + if (finalized) { 8.623 + runtimeNode.setIsFinal(); 8.624 + } 8.625 + return runtimeNode; 8.626 + } 8.627 + break; 8.628 + } 8.629 + 8.630 + binaryNode.setLHS(convert(lhs, widest)); 8.631 + binaryNode.setRHS(convert(rhs, widest)); 8.632 + 8.633 + return binaryNode; 8.634 + } 8.635 + 8.636 + /** 8.637 + * Compute the binary arithmetic type given the lhs and an rhs of a binary expression 8.638 + * @param lhsType the lhs type 8.639 + * @param rhsType the rhs type 8.640 + * @return the correct binary type 8.641 + */ 8.642 + private static Type binaryArithType(final Type lhsType, final Type rhsType) { 8.643 + if (!Compiler.shouldUseIntegerArithmetic()) { 8.644 + return Type.NUMBER; 8.645 + } 8.646 + return Type.widest(lhsType, rhsType, Type.NUMBER); 8.647 + } 8.648 + 8.649 + private Node leaveBinaryArith(final BinaryNode binaryNode) { 8.650 + final Type type = binaryArithType(binaryNode.lhs().getType(), binaryNode.rhs().getType()); 8.651 + return leaveBinary(binaryNode, type, type); 8.652 + } 8.653 + 8.654 + private Node leaveBinary(final BinaryNode binaryNode, final Type lhsType, final Type rhsType) { 8.655 + binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)); 8.656 + binaryNode.setRHS(convert(binaryNode.rhs(), rhsType)); 8.657 + return binaryNode; 8.658 + } 8.659 + 8.660 + /** 8.661 + * A symbol (and {@link Property}) can be tagged as "may be primitive". This is 8.662 + * used a hint for dual fields that it is even worth it to try representing this 8.663 + * field as something other than java.lang.Object. 8.664 + * 8.665 + * @param node node in which to tag symbols as primitive 8.666 + * @param to which primitive type to use for tagging 8.667 + */ 8.668 + private static void setCanBePrimitive(final Node node, final Type to) { 8.669 + final HashSet<Node> exclude = new HashSet<>(); 8.670 + 8.671 + node.accept(new NodeVisitor() { 8.672 + private void setCanBePrimitive(final Symbol symbol) { 8.673 + LOG.info("*** can be primitive symbol " + symbol + " " + Debug.id(symbol)); 8.674 + symbol.setCanBePrimitive(to); 8.675 + } 8.676 + 8.677 + @Override 8.678 + public Node enter(final IdentNode identNode) { 8.679 + if (!exclude.contains(identNode)) { 8.680 + setCanBePrimitive(identNode.getSymbol()); 8.681 + } 8.682 + return null; 8.683 + } 8.684 + 8.685 + @Override 8.686 + public Node enter(final AccessNode accessNode) { 8.687 + setCanBePrimitive(accessNode.getProperty().getSymbol()); 8.688 + return null; 8.689 + } 8.690 + 8.691 + @Override 8.692 + public Node enter(final IndexNode indexNode) { 8.693 + exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine 8.694 + return indexNode; 8.695 + } 8.696 + }); 8.697 + } 8.698 + 8.699 + private static Type specialize(final Assignment<?> assignment) { 8.700 + final Node node = ((Node)assignment); 8.701 + final Node lhs = assignment.getAssignmentDest(); 8.702 + final Node rhs = assignment.getAssignmentSource(); 8.703 + 8.704 + if (!canHaveCallSiteType(lhs)) { 8.705 + return null; 8.706 + } 8.707 + 8.708 + final Type to; 8.709 + if (node.isSelfModifying()) { 8.710 + to = node.getWidestOperationType(); 8.711 + } else { 8.712 + to = rhs.getType(); 8.713 + } 8.714 + 8.715 + if (!isSupportedCallSiteType(to)) { 8.716 + //meaningless to specialize to boolean or object 8.717 + return null; 8.718 + } 8.719 + 8.720 + setTypeOverride(lhs, to); 8.721 + propagateType(node, to); 8.722 + 8.723 + return to; 8.724 + } 8.725 + 8.726 + 8.727 + /** 8.728 + * Is this a node that can have its type overridden. This is true for 8.729 + * AccessNodes, IndexNodes and IdentNodes 8.730 + * 8.731 + * @param node the node to check 8.732 + * @return true if node can have a callsite type 8.733 + */ 8.734 + private static boolean canHaveCallSiteType(final Node node) { 8.735 + return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType(); 8.736 + } 8.737 + 8.738 + /** 8.739 + * Is the specialization type supported. Currently we treat booleans as objects 8.740 + * and have no special boolean type accessor, thus booleans are ignored. 8.741 + * TODO - support booleans? NASHORN-590 8.742 + * 8.743 + * @param castTo the type to check 8.744 + * @return true if call site type is supported 8.745 + */ 8.746 + private static boolean isSupportedCallSiteType(final Type castTo) { 8.747 + return castTo.isNumeric(); // don't specializable for boolean 8.748 + } 8.749 + 8.750 + /** 8.751 + * Override the type of a node for e.g. access specialization of scope 8.752 + * objects. Normally a variable can only get a wider type and narrower type 8.753 + * sets are ignored. Not that a variable can still be on object type as 8.754 + * per the type analysis, but a specific access may be narrower, e.g. if it 8.755 + * is used in an arithmetic op. This overrides a type, regardless of 8.756 + * type environment and is used primarily by the access specializer 8.757 + * 8.758 + * @param node node for which to change type 8.759 + * @param to new type 8.760 + */ 8.761 + private static void setTypeOverride(final Node node, final Type to) { 8.762 + final Type from = node.getType(); 8.763 + if (!node.getType().equals(to)) { 8.764 + LOG.info("Changing call override type for '" + node + "' from " + node.getType() + " to " + to); 8.765 + if (!to.isObject() && from.isObject()) { 8.766 + setCanBePrimitive(node, to); 8.767 + } 8.768 + } 8.769 + LOG.info("Type override for lhs in '" + node + "' => " + to); 8.770 + ((TypeOverride)node).setType(to); 8.771 + } 8.772 + 8.773 + /** 8.774 + * Add an explicit conversion. This is needed when attribution has created types 8.775 + * that do not mesh into an op type, e.g. a = b, where b is object and a is double 8.776 + * at the end of Attr, needs explicit conversion logic. 8.777 + * 8.778 + * An explicit conversion can be one of the following: 8.779 + * + Convert a literal - just replace it with another literal 8.780 + * + Convert a scope object - just replace the type of the access, e.g. get()D->get()I 8.781 + * + Explicit convert placement, e.g. a = (double)b - all other cases 8.782 + * 8.783 + * No other part of the world after {@link Attr} may introduce new symbols. This 8.784 + * is the only place. 8.785 + * 8.786 + * @param node node to convert 8.787 + * @param to destination type 8.788 + * @return conversion node 8.789 + */ 8.790 + private Node convert(final Node node, final Type to) { 8.791 + assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass(); 8.792 + assert node != null : "node is null"; 8.793 + assert node.getSymbol() != null : "node " + node + " has no symbol!"; 8.794 + assert node.tokenType() != TokenType.CONVERT : "assert convert in convert " + node + " in " + getCurrentFunctionNode(); 8.795 + 8.796 + final Type from = node.getType(); 8.797 + 8.798 + if (Type.areEquivalent(from, to)) { 8.799 + return node; 8.800 + } 8.801 + 8.802 + if (from.isObject() && to.isObject()) { 8.803 + return node; 8.804 + } 8.805 + 8.806 + Node resultNode = node; 8.807 + 8.808 + if (node instanceof LiteralNode && !to.isObject()) { 8.809 + final LiteralNode<?> newNode = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, to).eval(); 8.810 + if (newNode != null) { 8.811 + resultNode = newNode; 8.812 + } 8.813 + } else { 8.814 + if (canHaveCallSiteType(node) && isSupportedCallSiteType(to)) { 8.815 + setTypeOverride(node, to); 8.816 + return resultNode; 8.817 + } 8.818 + resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node); 8.819 + } 8.820 + 8.821 + LOG.info("CONVERT('" + node + "', " + to + ") => '" + resultNode + "'"); 8.822 + 8.823 + //This is the only place in this file that can create new temporaries 8.824 + //FinalizeTypes may not introduce ANY node that is not a conversion. 8.825 + getCurrentFunctionNode().newTemporary(getCurrentBlock().getFrame(), to, resultNode); 8.826 + resultNode.copyTerminalFlags(node); 8.827 + 8.828 + return resultNode; 8.829 + } 8.830 + 8.831 + private Node discard(final Node node) { 8.832 + node.setDiscard(true); 8.833 + 8.834 + if (node.getSymbol() != null) { 8.835 + final Node discard = new UnaryNode(source, Token.recast(node.getToken(), TokenType.DISCARD), node); 8.836 + //discard never has a symbol in the discard node - then it would be a nop 8.837 + discard.copyTerminalFlags(node); 8.838 + return discard; 8.839 + } 8.840 + 8.841 + // node has no result (symbol) so we can keep it the way it is 8.842 + return node; 8.843 + } 8.844 + 8.845 + /** 8.846 + * Whenever an expression like an addition or an assignment changes type, it 8.847 + * may be that case that {@link Attr} created a symbol for an intermediate 8.848 + * result of the expression, say for an addition. This also has to be updated 8.849 + * if the expression type changes. 8.850 + * 8.851 + * Assignments use their lhs as node symbol, and in this case we can't modify 8.852 + * it. Then {@link CodeGenerator#Store} needs to do an explicit conversion. 8.853 + * This is happens very rarely. 8.854 + * 8.855 + * @param node 8.856 + * @param to 8.857 + */ 8.858 + private static void propagateType(final Node node, final Type to) { 8.859 + final Symbol symbol = node.getSymbol(); 8.860 + if (symbol.isTemp()) { 8.861 + symbol.setTypeOverride(to); 8.862 + LOG.info("Type override for temporary in '" + node + "' => " + to); 8.863 + } 8.864 + } 8.865 + 8.866 + /** 8.867 + * Determine if the outcome of + operator is a string. 8.868 + * 8.869 + * @param node Node to test. 8.870 + * @return true if a string result. 8.871 + */ 8.872 + private boolean isAddString(final Node node) { 8.873 + if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) { 8.874 + final BinaryNode binaryNode = (BinaryNode)node; 8.875 + final Node lhs = binaryNode.lhs(); 8.876 + final Node rhs = binaryNode.rhs(); 8.877 + 8.878 + return isAddString(lhs) || isAddString(rhs); 8.879 + } 8.880 + 8.881 + return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString(); 8.882 + } 8.883 + 8.884 + /** 8.885 + * Whenever an explicit conversion is needed and the convertee is a literal, we can 8.886 + * just change the literal 8.887 + */ 8.888 + static class LiteralNodeConstantEvaluator extends FoldConstants.ConstantEvaluator<LiteralNode<?>> { 8.889 + private final Type type; 8.890 + 8.891 + LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) { 8.892 + super(parent); 8.893 + this.type = type; 8.894 + } 8.895 + 8.896 + @Override 8.897 + protected LiteralNode<?> eval() { 8.898 + final Object value = ((LiteralNode<?>)parent).getValue(); 8.899 + 8.900 + LiteralNode<?> literalNode = null; 8.901 + 8.902 + if (type.isString()) { 8.903 + literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value)); 8.904 + } else if (type.isBoolean()) { 8.905 + literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value)); 8.906 + } else if (type.isInteger()) { 8.907 + literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); 8.908 + } else if (type.isLong()) { 8.909 + literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); 8.910 + } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) { 8.911 + literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value)); 8.912 + } 8.913 + 8.914 + if (literalNode != null) { 8.915 + //inherit literal symbol for attr. 8.916 + literalNode.setSymbol(parent.getSymbol()); 8.917 + } 8.918 + 8.919 + return literalNode; 8.920 + } 8.921 + } 8.922 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/jdk/nashorn/internal/codegen/FoldConstants.java Wed Jan 30 12:26:45 2013 +0100 9.3 @@ -0,0 +1,278 @@ 9.4 +/* 9.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 9.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 9.7 + * 9.8 + * This code is free software; you can redistribute it and/or modify it 9.9 + * under the terms of the GNU General Public License version 2 only, as 9.10 + * published by the Free Software Foundation. Oracle designates this 9.11 + * particular file as subject to the "Classpath" exception as provided 9.12 + * by Oracle in the LICENSE file that accompanied this code. 9.13 + * 9.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 9.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 9.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 9.17 + * version 2 for more details (a copy is included in the LICENSE file that 9.18 + * accompanied this code). 9.19 + * 9.20 + * You should have received a copy of the GNU General Public License version 9.21 + * 2 along with this work; if not, write to the Free Software Foundation, 9.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 9.23 + * 9.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 9.25 + * or visit www.oracle.com if you need additional information or have any 9.26 + * questions. 9.27 + */ 9.28 + 9.29 +package jdk.nashorn.internal.codegen; 9.30 + 9.31 +import jdk.nashorn.internal.codegen.types.Type; 9.32 +import jdk.nashorn.internal.ir.BinaryNode; 9.33 +import jdk.nashorn.internal.ir.Block; 9.34 +import jdk.nashorn.internal.ir.EmptyNode; 9.35 +import jdk.nashorn.internal.ir.ExecuteNode; 9.36 +import jdk.nashorn.internal.ir.IfNode; 9.37 +import jdk.nashorn.internal.ir.LiteralNode; 9.38 +import jdk.nashorn.internal.ir.Node; 9.39 +import jdk.nashorn.internal.ir.TernaryNode; 9.40 +import jdk.nashorn.internal.ir.UnaryNode; 9.41 +import jdk.nashorn.internal.ir.visitor.NodeVisitor; 9.42 +import jdk.nashorn.internal.runtime.DebugLogger; 9.43 +import jdk.nashorn.internal.runtime.JSType; 9.44 +import jdk.nashorn.internal.runtime.ScriptRuntime; 9.45 +import jdk.nashorn.internal.runtime.Source; 9.46 + 9.47 +/** 9.48 + * Simple constant folding pass, executed before IR is starting to be lowered. 9.49 + */ 9.50 +public class FoldConstants extends NodeVisitor { 9.51 + 9.52 + private static final DebugLogger LOG = new DebugLogger("fold"); 9.53 + 9.54 + @Override 9.55 + public Node leave(final UnaryNode unaryNode) { 9.56 + final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval(); 9.57 + if (literalNode != null) { 9.58 + LOG.info("Unary constant folded " + unaryNode + " to " + literalNode); 9.59 + return literalNode; 9.60 + } 9.61 + return unaryNode; 9.62 + } 9.63 + 9.64 + @Override 9.65 + public Node leave(final BinaryNode binaryNode) { 9.66 + final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); 9.67 + if (literalNode != null) { 9.68 + LOG.info("Binary constant folded " + binaryNode + " to " + literalNode); 9.69 + return literalNode; 9.70 + } 9.71 + return binaryNode; 9.72 + } 9.73 + 9.74 + @Override 9.75 + public Node leave(final IfNode ifNode) { 9.76 + final Node test = ifNode.getTest(); 9.77 + if (test instanceof LiteralNode) { 9.78 + final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); 9.79 + if (shortCut != null) { 9.80 + return new ExecuteNode(shortCut); 9.81 + } 9.82 + return new EmptyNode(ifNode); 9.83 + } 9.84 + return ifNode; 9.85 + } 9.86 + 9.87 + @Override 9.88 + public Node leave(final TernaryNode ternaryNode) { 9.89 + final Node test = ternaryNode.lhs(); 9.90 + if (test instanceof LiteralNode) { 9.91 + return ((LiteralNode<?>)test).isTrue() ? ternaryNode.rhs() : ternaryNode.third(); 9.92 + } 9.93 + return ternaryNode; 9.94 + } 9.95 + 9.96 + /** 9.97 + * Helper class to evaluate constant expressions at compile time This is 9.98 + * also a simplifier used by BinaryNode visits, UnaryNode visits and 9.99 + * conversions. 9.100 + */ 9.101 + abstract static class ConstantEvaluator<T extends Node> { 9.102 + protected T parent; 9.103 + protected final Source source; 9.104 + protected final long token; 9.105 + protected final int finish; 9.106 + 9.107 + protected ConstantEvaluator(final T parent) { 9.108 + this.parent = parent; 9.109 + this.source = parent.getSource(); 9.110 + this.token = parent.getToken(); 9.111 + this.finish = parent.getFinish(); 9.112 + } 9.113 + 9.114 + /** 9.115 + * Returns a literal node that replaces the given parent node, or null if replacement 9.116 + * is impossible 9.117 + * @return the literal node 9.118 + */ 9.119 + protected abstract LiteralNode<?> eval(); 9.120 + } 9.121 + 9.122 + private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> { 9.123 + UnaryNodeConstantEvaluator(final UnaryNode parent) { 9.124 + super(parent); 9.125 + } 9.126 + 9.127 + @Override 9.128 + protected LiteralNode<?> eval() { 9.129 + final Node rhsNode = parent.rhs(); 9.130 + 9.131 + if (!(rhsNode instanceof LiteralNode)) { 9.132 + return null; 9.133 + } 9.134 + 9.135 + final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode; 9.136 + final boolean rhsInteger = rhs.getType().isInteger(); 9.137 + 9.138 + LiteralNode<?> literalNode; 9.139 + 9.140 + switch (parent.tokenType()) { 9.141 + case ADD: 9.142 + if (rhsInteger) { 9.143 + literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32()); 9.144 + } else { 9.145 + literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber()); 9.146 + } 9.147 + break; 9.148 + case SUB: 9.149 + if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js 9.150 + literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32()); 9.151 + } else { 9.152 + literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber()); 9.153 + } 9.154 + break; 9.155 + case NOT: 9.156 + literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean()); 9.157 + break; 9.158 + case BIT_NOT: 9.159 + literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32()); 9.160 + break; 9.161 + default: 9.162 + return null; 9.163 + } 9.164 + 9.165 + return literalNode; 9.166 + } 9.167 + } 9.168 + 9.169 + //TODO add AND and OR with one constant parameter (bitwise) 9.170 + private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> { 9.171 + BinaryNodeConstantEvaluator(final BinaryNode parent) { 9.172 + super(parent); 9.173 + } 9.174 + 9.175 + @Override 9.176 + protected LiteralNode<?> eval() { 9.177 + LiteralNode<?> result; 9.178 + 9.179 + result = reduceTwoLiterals(); 9.180 + if (result != null) { 9.181 + return result; 9.182 + } 9.183 + 9.184 + result = reduceOneLiteral(); 9.185 + if (result != null) { 9.186 + return result; 9.187 + } 9.188 + 9.189 + return null; 9.190 + } 9.191 + 9.192 + @SuppressWarnings("static-method") 9.193 + private LiteralNode<?> reduceOneLiteral() { 9.194 + //TODO handle patterns like AND, OR, numeric ops that can be strength reduced but not replaced by a single literal node etc 9.195 + return null; 9.196 + } 9.197 + 9.198 + private LiteralNode<?> reduceTwoLiterals() { 9.199 + if (!(parent.lhs() instanceof LiteralNode && parent.rhs() instanceof LiteralNode)) { 9.200 + return null; 9.201 + } 9.202 + 9.203 + final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs(); 9.204 + final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs(); 9.205 + 9.206 + final Type widest = Type.widest(lhs.getType(), rhs.getType()); 9.207 + 9.208 + boolean isInteger = widest.isInteger(); 9.209 + boolean isLong = widest.isLong(); 9.210 + 9.211 + double value; 9.212 + 9.213 + switch (parent.tokenType()) { 9.214 + case DIV: 9.215 + value = lhs.getNumber() / rhs.getNumber(); 9.216 + break; 9.217 + case ADD: 9.218 + if ((lhs.isString() || rhs.isNumeric()) && (rhs.isString() || rhs.isNumeric())) { 9.219 + Object res = ScriptRuntime.ADD(lhs.getObject(), rhs.getObject()); 9.220 + if (res instanceof Number) { 9.221 + value = ((Number)res).doubleValue(); 9.222 + break; 9.223 + } 9.224 + assert res instanceof CharSequence : res + " was not a CharSequence, it was a " + res.getClass(); 9.225 + return LiteralNode.newInstance(source, token, finish, res.toString()); 9.226 + } 9.227 + return null; 9.228 + case MUL: 9.229 + value = lhs.getNumber() * rhs.getNumber(); 9.230 + break; 9.231 + case MOD: 9.232 + value = lhs.getNumber() % rhs.getNumber(); 9.233 + break; 9.234 + case SUB: 9.235 + value = lhs.getNumber() - rhs.getNumber(); 9.236 + break; 9.237 + case SHR: 9.238 + return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL); 9.239 + case SAR: 9.240 + return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32()); 9.241 + case SHL: 9.242 + return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32()); 9.243 + case BIT_XOR: 9.244 + return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32()); 9.245 + case BIT_AND: 9.246 + return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32()); 9.247 + case BIT_OR: 9.248 + return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32()); 9.249 + case GE: 9.250 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject())); 9.251 + case LE: 9.252 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject())); 9.253 + case GT: 9.254 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject())); 9.255 + case LT: 9.256 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject())); 9.257 + case NE: 9.258 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject())); 9.259 + case NE_STRICT: 9.260 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject())); 9.261 + case EQ: 9.262 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject())); 9.263 + case EQ_STRICT: 9.264 + return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject())); 9.265 + default: 9.266 + return null; 9.267 + } 9.268 + 9.269 + isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value); 9.270 + isLong &= value != 0.0 && JSType.isRepresentableAsLong(value); 9.271 + 9.272 + if (isInteger) { 9.273 + return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); 9.274 + } else if (isLong) { 9.275 + return LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); 9.276 + } 9.277 + 9.278 + return LiteralNode.newInstance(source, token, finish, value); 9.279 + } 9.280 + } 9.281 +}
10.1 --- a/src/jdk/nashorn/internal/codegen/Lower.java Tue Jan 29 14:25:39 2013 -0400 10.2 +++ b/src/jdk/nashorn/internal/codegen/Lower.java Wed Jan 30 12:26:45 2013 +0100 10.3 @@ -28,47 +28,19 @@ 10.4 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; 10.5 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE; 10.6 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL; 10.7 -import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX; 10.8 -import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX; 10.9 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; 10.10 import static jdk.nashorn.internal.codegen.CompilerConstants.SCRIPT_RETURN; 10.11 -import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX; 10.12 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; 10.13 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 10.14 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.ADD; 10.15 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.DELETE; 10.16 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ; 10.17 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.EQ_STRICT; 10.18 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.FAIL_DELETE; 10.19 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.GE; 10.20 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.GT; 10.21 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.IN; 10.22 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.INSTANCEOF; 10.23 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.LE; 10.24 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.LT; 10.25 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE; 10.26 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.NE_STRICT; 10.27 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.TYPEOF; 10.28 -import static jdk.nashorn.internal.ir.RuntimeNode.Request.VOID; 10.29 -import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL; 10.30 -import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 10.31 -import static jdk.nashorn.internal.ir.Symbol.IS_LET; 10.32 -import static jdk.nashorn.internal.ir.Symbol.IS_PARAM; 10.33 -import static jdk.nashorn.internal.ir.Symbol.IS_THIS; 10.34 -import static jdk.nashorn.internal.ir.Symbol.IS_VAR; 10.35 -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 10.36 10.37 import java.util.ArrayDeque; 10.38 import java.util.ArrayList; 10.39 import java.util.Arrays; 10.40 import java.util.Deque; 10.41 -import java.util.HashSet; 10.42 -import java.util.LinkedList; 10.43 import java.util.List; 10.44 -import java.util.Set; 10.45 -import jdk.nashorn.internal.codegen.types.Type; 10.46 + 10.47 import jdk.nashorn.internal.ir.AccessNode; 10.48 -import jdk.nashorn.internal.ir.Assignment; 10.49 +import jdk.nashorn.internal.ir.BaseNode; 10.50 import jdk.nashorn.internal.ir.BinaryNode; 10.51 import jdk.nashorn.internal.ir.Block; 10.52 import jdk.nashorn.internal.ir.BreakNode; 10.53 @@ -85,19 +57,13 @@ 10.54 import jdk.nashorn.internal.ir.IfNode; 10.55 import jdk.nashorn.internal.ir.IndexNode; 10.56 import jdk.nashorn.internal.ir.LabelNode; 10.57 +import jdk.nashorn.internal.ir.LabeledNode; 10.58 import jdk.nashorn.internal.ir.LineNumberNode; 10.59 import jdk.nashorn.internal.ir.LiteralNode; 10.60 -import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; 10.61 import jdk.nashorn.internal.ir.Node; 10.62 -import jdk.nashorn.internal.ir.ObjectNode; 10.63 -import jdk.nashorn.internal.ir.PropertyNode; 10.64 -import jdk.nashorn.internal.ir.ReferenceNode; 10.65 import jdk.nashorn.internal.ir.ReturnNode; 10.66 -import jdk.nashorn.internal.ir.RuntimeNode; 10.67 -import jdk.nashorn.internal.ir.RuntimeNode.Request; 10.68 import jdk.nashorn.internal.ir.SwitchNode; 10.69 import jdk.nashorn.internal.ir.Symbol; 10.70 -import jdk.nashorn.internal.ir.TernaryNode; 10.71 import jdk.nashorn.internal.ir.ThrowNode; 10.72 import jdk.nashorn.internal.ir.TryNode; 10.73 import jdk.nashorn.internal.ir.UnaryNode; 10.74 @@ -108,54 +74,26 @@ 10.75 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 10.76 import jdk.nashorn.internal.parser.Token; 10.77 import jdk.nashorn.internal.parser.TokenType; 10.78 -import jdk.nashorn.internal.runtime.Context; 10.79 import jdk.nashorn.internal.runtime.DebugLogger; 10.80 -import jdk.nashorn.internal.runtime.ECMAException; 10.81 -import jdk.nashorn.internal.runtime.JSType; 10.82 -import jdk.nashorn.internal.runtime.Property; 10.83 -import jdk.nashorn.internal.runtime.PropertyMap; 10.84 -import jdk.nashorn.internal.runtime.ScriptFunction; 10.85 -import jdk.nashorn.internal.runtime.ScriptObject; 10.86 import jdk.nashorn.internal.runtime.ScriptRuntime; 10.87 import jdk.nashorn.internal.runtime.Source; 10.88 -import jdk.nashorn.internal.runtime.Undefined; 10.89 10.90 /** 10.91 - * Lower to more primitive operations. After lowering, an AST has symbols and 10.92 - * types. Lowering may also add specialized versions of methods to the script if 10.93 - * the optimizer is turned on. 10.94 + * Lower to more primitive operations. After lowering, an AST still has no symbols 10.95 + * and types, but several nodes have been turned into more low level constructs 10.96 + * and control flow termination criteria have been computed. 10.97 * 10.98 - * Any expression that requires temporary storage as part of computation will 10.99 - * also be detected here and give a temporary symbol 10.100 + * We do things like code copying/inlining of finallies here, as it is much 10.101 + * harder and context dependent to do any code copying after symbols have been 10.102 + * finalized. 10.103 */ 10.104 10.105 final class Lower extends NodeOperatorVisitor { 10.106 - /** Current compiler. */ 10.107 + 10.108 private final Compiler compiler; 10.109 10.110 - /** Current source. */ 10.111 private final Source source; 10.112 10.113 - /** List of lowered statements */ 10.114 - private List<Node> statements; 10.115 - 10.116 - /** All symbols that are declared locally in a function node */ 10.117 - private List<Symbol> declaredSymbolsLocal; 10.118 - 10.119 - /** 10.120 - * Local definitions in current block (to discriminate from function 10.121 - * declarations always defined in the function scope. This is for 10.122 - * "can be undefined" analysis. 10.123 - */ 10.124 - private Set<String> localDefs; 10.125 - 10.126 - /** 10.127 - * Local definitions in current block to guard against cases like 10.128 - * NASHORN-467 when things can be undefined as they are used before 10.129 - * their local var definition. *sigh* JavaScript... 10.130 - */ 10.131 - private Set<String> localUses; 10.132 - 10.133 /** 10.134 * Nesting level stack. Currently just used for loops to avoid the problem 10.135 * with terminal bodies that end with throw/return but still do continues to 10.136 @@ -163,8 +101,11 @@ 10.137 */ 10.138 private final Deque<Node> nesting; 10.139 10.140 - private static final DebugLogger LOG = new DebugLogger("lower"); 10.141 - private static final boolean DEBUG = LOG.isEnabled(); 10.142 + private static final DebugLogger LOG = new DebugLogger("lower"); 10.143 + 10.144 + private Node lastStatement; 10.145 + 10.146 + private List<Node> statements; 10.147 10.148 /** 10.149 * Constructor. 10.150 @@ -174,52 +115,17 @@ 10.151 Lower(final Compiler compiler) { 10.152 this.compiler = compiler; 10.153 this.source = compiler.getSource(); 10.154 + this.nesting = new ArrayDeque<>(); 10.155 this.statements = new ArrayList<>(); 10.156 - this.nesting = new ArrayDeque<>(); 10.157 - } 10.158 - 10.159 - private void nest(final Node node) { 10.160 - nesting.push(node); 10.161 - } 10.162 - 10.163 - private void unnest() { 10.164 - nesting.pop(); 10.165 - } 10.166 - 10.167 - static void debug(final String str) { 10.168 - if (DEBUG) { 10.169 - LOG.info(str); 10.170 - } 10.171 - } 10.172 - 10.173 - @Override 10.174 - public Node leave(final AccessNode accessNode) { 10.175 - //accessNode.setBase(convert(accessNode.getBase(), Type.OBJECT)); 10.176 - getCurrentFunctionNode().newTemporary(Type.OBJECT, accessNode); //This is not always an object per se, but currently the narrowing logic resides in AccessSpecializer. @see AccessSpecializer! 10.177 - 10.178 - return accessNode; 10.179 } 10.180 10.181 @Override 10.182 public Node enter(final Block block) { 10.183 - /* 10.184 - * Save the statement list from the outer construct we are currently 10.185 - * generating and push frame 10.186 - */ 10.187 - final List<Node> savedStatements = statements; 10.188 - final Set<String> savedDefs = localDefs; 10.189 - final Set<String> savedUses = localUses; 10.190 - 10.191 - block.setFrame(getCurrentFunctionNode().pushFrame()); 10.192 + final Node savedLastStatement = lastStatement; 10.193 + final List<Node> savedStatements = statements; 10.194 10.195 try { 10.196 - /* 10.197 - * Reset the statement instance var, new block 10.198 - */ 10.199 - statements = new ArrayList<>(); 10.200 - localDefs = new HashSet<>(savedDefs); 10.201 - localUses = new HashSet<>(savedUses); 10.202 - 10.203 + this.statements = new ArrayList<>(); 10.204 for (final Node statement : block.getStatements()) { 10.205 statement.accept(this); 10.206 /* 10.207 @@ -231,28 +137,715 @@ 10.208 * 10.209 * @see NASHORN-285 10.210 */ 10.211 - final Node lastStatement = Node.lastStatement(statements); 10.212 if (lastStatement != null && lastStatement.isTerminal()) { 10.213 - block.copyTerminalFlags(lastStatement); 10.214 + copyTerminal(block, lastStatement); 10.215 + break; 10.216 + } 10.217 + } 10.218 + block.setStatements(statements); 10.219 + 10.220 + } finally { 10.221 + this.statements = savedStatements; 10.222 + this.lastStatement = savedLastStatement; 10.223 + } 10.224 + 10.225 + return null; 10.226 + } 10.227 + 10.228 + @Override 10.229 + public Node enter(final BreakNode breakNode) { 10.230 + return enterBreakOrContinue(breakNode); 10.231 + } 10.232 + 10.233 + @Override 10.234 + public Node enter(final CallNode callNode) { 10.235 + final Node function = markerFunction(callNode.getFunction()); 10.236 + callNode.setFunction(function); 10.237 + checkEval(callNode); //check if this is an eval call and store the information 10.238 + return callNode; 10.239 + } 10.240 + 10.241 + @Override 10.242 + public Node leave(final CaseNode caseNode) { 10.243 + caseNode.copyTerminalFlags(caseNode.getBody()); 10.244 + return caseNode; 10.245 + } 10.246 + 10.247 + @Override 10.248 + public Node leave(final CatchNode catchNode) { 10.249 + catchNode.copyTerminalFlags(catchNode.getBody()); 10.250 + addStatement(catchNode); 10.251 + return catchNode; 10.252 + } 10.253 + 10.254 + @Override 10.255 + public Node enter(final ContinueNode continueNode) { 10.256 + return enterBreakOrContinue(continueNode); 10.257 + } 10.258 + 10.259 + @Override 10.260 + public Node enter(final DoWhileNode doWhileNode) { 10.261 + return enter((WhileNode)doWhileNode); 10.262 + } 10.263 + 10.264 + @Override 10.265 + public Node leave(final DoWhileNode doWhileNode) { 10.266 + return leave((WhileNode)doWhileNode); 10.267 + } 10.268 + 10.269 + @Override 10.270 + public Node enter(final EmptyNode emptyNode) { 10.271 + return null; 10.272 + } 10.273 + 10.274 + @Override 10.275 + public Node leave(final ExecuteNode executeNode) { 10.276 + final Node expr = executeNode.getExpression(); 10.277 + 10.278 + if (getCurrentFunctionNode().isScript()) { 10.279 + if (!(expr instanceof Block)) { 10.280 + if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) { 10.281 + executeNode.setExpression(new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN), 10.282 + getCurrentFunctionNode().getResultNode(), 10.283 + expr)); 10.284 + } 10.285 + } 10.286 + } 10.287 + 10.288 + copyTerminal(executeNode, executeNode.getExpression()); 10.289 + addStatement(executeNode); 10.290 + 10.291 + return executeNode; 10.292 + } 10.293 + 10.294 + @Override 10.295 + public Node enter(final ForNode forNode) { 10.296 + nest(forNode); 10.297 + return forNode; 10.298 + } 10.299 + 10.300 + @Override 10.301 + public Node leave(final ForNode forNode) { 10.302 + final Node test = forNode.getTest(); 10.303 + final Block body = forNode.getBody(); 10.304 + 10.305 + if (!forNode.isForIn() && test == null) { 10.306 + setHasGoto(forNode); 10.307 + } 10.308 + 10.309 + final boolean escapes = controlFlowEscapes(body); 10.310 + if (escapes) { 10.311 + setTerminal(body, false); 10.312 + } 10.313 + 10.314 + // pop the loop from the loop context 10.315 + unnest(forNode); 10.316 + 10.317 + if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { 10.318 + forNode.setTest(null); 10.319 + setTerminal(forNode, !escapes); 10.320 + } 10.321 + 10.322 + addStatement(forNode); 10.323 + 10.324 + return forNode; 10.325 + } 10.326 + 10.327 + @Override 10.328 + public Node enter(final FunctionNode functionNode) { 10.329 + LOG.info("START FunctionNode: " + functionNode.getName()); 10.330 + 10.331 + initFunctionNode(functionNode); 10.332 + 10.333 + Node initialEvalResult = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED); 10.334 + 10.335 + nest(functionNode); 10.336 + 10.337 + /* 10.338 + * As we are evaluating a nested structure, we need to store the 10.339 + * statement list for the surrounding block and restore it when the 10.340 + * function is done 10.341 + */ 10.342 + final List<Node> savedStatements = statements; 10.343 + final Node savedLastStatement = lastStatement; 10.344 + 10.345 + statements = new ArrayList<>(); 10.346 + lastStatement = null; 10.347 + 10.348 + // for initial eval result is the last declared function 10.349 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 10.350 + final IdentNode ident = nestedFunction.getIdent(); 10.351 + if (ident != null && nestedFunction.isStatement()) { 10.352 + initialEvalResult = new IdentNode(ident); 10.353 + } 10.354 + } 10.355 + 10.356 + if (functionNode.needsSelfSymbol()) { 10.357 + //function needs to start with var funcIdent = __callee_; 10.358 + statements.add(functionNode.getSelfSymbolInit().accept(this)); 10.359 + } 10.360 + 10.361 + try { 10.362 + // Every nested function needs a definition in the outer function with its name. Add these. 10.363 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 10.364 + final VarNode varNode = nestedFunction.getFunctionVarNode(); 10.365 + if (varNode != null) { 10.366 + final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode(); 10.367 + if (lineNumberNode != null) { 10.368 + lineNumberNode.accept(this); 10.369 + } 10.370 + varNode.accept(this); 10.371 + varNode.setIsFunctionVarNode(); 10.372 + } 10.373 + } 10.374 + 10.375 + if (functionNode.isScript()) { 10.376 + new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this); 10.377 + } 10.378 + 10.379 + //do the statements - this fills the block with code 10.380 + for (final Node statement : functionNode.getStatements()) { 10.381 + statement.accept(this); 10.382 + //If there are unused terminated endpoints in the function, we need 10.383 + // to add a "return undefined" in those places for correct semantics 10.384 + LOG.info("Checking lastStatement="+lastStatement+" for terminal flags"); 10.385 + if (lastStatement != null && lastStatement.hasTerminalFlags()) { 10.386 + copyTerminal(functionNode, lastStatement); 10.387 break; 10.388 } 10.389 } 10.390 10.391 - block.setStatements(statements); 10.392 + functionNode.setStatements(statements); 10.393 + 10.394 + if (!functionNode.isTerminal()) { 10.395 + guaranteeReturn(functionNode); 10.396 + } 10.397 + 10.398 + //lower all nested functions 10.399 + for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 10.400 + nestedFunction.accept(this); 10.401 + } 10.402 + 10.403 } finally { 10.404 - /* 10.405 - * Restore the saved statements after block ends and pop frame 10.406 - */ 10.407 - statements = savedStatements; 10.408 - localDefs = savedDefs; 10.409 - localUses = savedUses; 10.410 - 10.411 - getCurrentFunctionNode().popFrame(); 10.412 + statements = savedStatements; 10.413 + lastStatement = savedLastStatement; 10.414 } 10.415 10.416 + LOG.info("END FunctionNode: " + functionNode.getName()); 10.417 + unnest(functionNode); 10.418 + 10.419 return null; 10.420 } 10.421 10.422 + @Override 10.423 + public Node enter(final IfNode ifNode) { 10.424 + return nest(ifNode); 10.425 + } 10.426 + 10.427 + @Override 10.428 + public Node leave(final IfNode ifNode) { 10.429 + final Node pass = ifNode.getPass(); 10.430 + final Node fail = ifNode.getFail(); 10.431 + 10.432 + if (pass.isTerminal() && fail != null && fail.isTerminal()) { 10.433 + setTerminal(ifNode, true); 10.434 + } 10.435 + 10.436 + addStatement(ifNode); 10.437 + unnest(ifNode); 10.438 + 10.439 + return ifNode; 10.440 + } 10.441 + 10.442 + @Override 10.443 + public Node enter(LabelNode labelNode) { 10.444 + final Block body = labelNode.getBody(); 10.445 + body.accept(this); 10.446 + copyTerminal(labelNode, body); 10.447 + addStatement(labelNode); 10.448 + return null; 10.449 + } 10.450 + 10.451 + @Override 10.452 + public Node enter(final LineNumberNode lineNumberNode) { 10.453 + addStatement(lineNumberNode, false); // don't put it in lastStatement cache 10.454 + return null; 10.455 + } 10.456 + 10.457 + @Override 10.458 + public Node enter(final ReturnNode returnNode) { 10.459 + final TryNode tryNode = returnNode.getTryChain(); 10.460 + final Node expr = returnNode.getExpression(); 10.461 + 10.462 + if (tryNode != null) { 10.463 + //we are inside a try block - we don't necessarily have a result node yet. attr will do that. 10.464 + if (expr != null) { 10.465 + //we need to evaluate the result of the return in case it is complex while 10.466 + //still in the try block, store it in a result value and return it afterwards 10.467 + final long token = returnNode.getToken(); 10.468 + final Node resultNode = new IdentNode(getCurrentFunctionNode().getResultNode()); 10.469 + final Node assignResult = new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expr); 10.470 + 10.471 + //add return_in_try = expr; to try block 10.472 + new ExecuteNode(source, token, Token.descPosition(token), assignResult).accept(this); 10.473 + 10.474 + //splice in the finally code, inlining it here 10.475 + if (copyFinally(tryNode, null)) { 10.476 + return null; 10.477 + } 10.478 + 10.479 + //make sure that the return node now returns 'return_in_try' 10.480 + returnNode.setExpression(resultNode); 10.481 + } else if (copyFinally(tryNode, null)) { 10.482 + return null; 10.483 + } 10.484 + } else if (expr != null) { 10.485 + returnNode.setExpression(expr.accept(this)); 10.486 + } 10.487 + 10.488 + addStatement(returnNode); 10.489 + 10.490 + return null; 10.491 + } 10.492 + 10.493 + @Override 10.494 + public Node leave(final ReturnNode returnNode) { 10.495 + addStatement(returnNode); //ReturnNodes are always terminal, marked as such in constructor 10.496 + return returnNode; 10.497 + } 10.498 + 10.499 + @Override 10.500 + public Node enter(final SwitchNode switchNode) { 10.501 + nest(switchNode); 10.502 + return switchNode; 10.503 + } 10.504 + 10.505 + @Override 10.506 + public Node leave(final SwitchNode switchNode) { 10.507 + unnest(switchNode); 10.508 + 10.509 + final List<CaseNode> cases = switchNode.getCases(); 10.510 + final CaseNode defaultCase = switchNode.getDefaultCase(); 10.511 + 10.512 + boolean allTerminal = !cases.isEmpty(); 10.513 + for (final CaseNode caseNode : switchNode.getCases()) { 10.514 + allTerminal &= caseNode.isTerminal(); 10.515 + } 10.516 + 10.517 + if (allTerminal && defaultCase != null && defaultCase.isTerminal()) { 10.518 + setTerminal(switchNode, true); 10.519 + } 10.520 + 10.521 + addStatement(switchNode); 10.522 + 10.523 + return switchNode; 10.524 + } 10.525 + 10.526 + @Override 10.527 + public Node leave(final ThrowNode throwNode) { 10.528 + addStatement(throwNode); //ThrowNodes are always terminal, marked as such in constructor 10.529 + return throwNode; 10.530 + } 10.531 + 10.532 + @Override 10.533 + public Node enter(final TryNode tryNode) { 10.534 + final Block finallyBody = tryNode.getFinallyBody(); 10.535 + final long token = tryNode.getToken(); 10.536 + final int finish = tryNode.getFinish(); 10.537 + 10.538 + nest(tryNode); 10.539 + 10.540 + if (finallyBody == null) { 10.541 + //do nothing if no finally exists 10.542 + return tryNode; 10.543 + } 10.544 + 10.545 + /* 10.546 + * We have a finally clause. 10.547 + * 10.548 + * Transform to do finally tail duplication as follows: 10.549 + * 10.550 + * <pre> 10.551 + * try { 10.552 + * try_body 10.553 + * } catch e1 { 10.554 + * catchbody_1 10.555 + * } 10.556 + * ... 10.557 + * } catch en { 10.558 + * catchbody_n 10.559 + * } finally { 10.560 + * finally_body 10.561 + * } 10.562 + * 10.563 + * (where e1 ... en are optional) 10.564 + * 10.565 + * turns into 10.566 + * 10.567 + * try { 10.568 + * try { 10.569 + * try_body 10.570 + * } catch e1 { 10.571 + * catchbody1 10.572 + * //nothing inlined explicitly here, return, break other 10.573 + * //terminals may inline the finally body 10.574 + * ... 10.575 + * } catch en { 10.576 + * catchbody2 10.577 + * //nothing inlined explicitly here, return, break other 10.578 + * //terminals may inline the finally body 10.579 + * } 10.580 + * } catch all ex { 10.581 + * finally_body_inlined 10.582 + * rethrow ex 10.583 + * } 10.584 + * finally_body_inlined 10.585 + * </pre> 10.586 + * 10.587 + * If tries are catches are terminal, visitors for return, break & 10.588 + * continue will handle the tail duplications. Throw needs to be 10.589 + * treated specially with the catchall as described in the above 10.590 + * ASCII art. 10.591 + * 10.592 + * If the try isn't terminal we do the finally_body_inlined at the 10.593 + * end. If the try is terminated with continue/break/return the 10.594 + * existing visitor logic will inline the finally before that 10.595 + * operation. if the try is terminated with a throw, the catches e1 10.596 + * ... en will have a chance to process the exception. If the 10.597 + * appropriate catch e1..en is non terminal we fall through to the 10.598 + * last finally_body_inlined. if the catch e1...en IS terminal with 10.599 + * continue/break/return existing visitor logic will fix it. If they 10.600 + * are terminal with another throw it goes to the catchall and the 10.601 + * finally_body_inlined marked (*) will fix it before rethrowing 10.602 + * whatever problem there was for identical semantic. 10.603 + */ 10.604 + 10.605 + // if try node does not contain a catch we can skip creation of a new 10.606 + // try node and just append our synthetic catch to the existing try node. 10.607 + if (!tryNode.getCatchBlocks().isEmpty()) { 10.608 + // insert an intermediate try-catch* node, where we move the body and all catch blocks. 10.609 + // the original try node become a try-finally container for the new try-catch* node. 10.610 + // because we don't clone (to avoid deep copy), we have to fix the block chain in the end. 10.611 + final TryNode innerTryNode; 10.612 + innerTryNode = new TryNode(source, token, finish, tryNode.getNext()); 10.613 + innerTryNode.setBody(tryNode.getBody()); 10.614 + innerTryNode.setCatchBlocks(tryNode.getCatchBlocks()); 10.615 + 10.616 + // set outer tryNode's body to innerTryNode 10.617 + final Block outerBody; 10.618 + outerBody = new Block(source, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode()); 10.619 + outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode))); 10.620 + tryNode.setBody(outerBody); 10.621 + tryNode.setCatchBlocks(null); 10.622 + 10.623 + // now before we go on, we have to fix the block parents 10.624 + // (we repair the block tree after the insertion so that all references are intact) 10.625 + innerTryNode.getBody().setParent(tryNode.getBody()); 10.626 + for (final Block block : innerTryNode.getCatchBlocks()) { 10.627 + block.setParent(tryNode.getBody()); 10.628 + } 10.629 + } 10.630 + 10.631 + // create a catch-all that inlines finally and rethrows 10.632 + 10.633 + final Block catchBlock = new Block(source, token, finish, getCurrentBlock(), getCurrentFunctionNode()); 10.634 + //this catch block should get define symbol 10.635 + 10.636 + final Block catchBody = new Block(source, token, finish, catchBlock, getCurrentFunctionNode()); 10.637 + final Node catchAllFinally = finallyBody.clone(); 10.638 + 10.639 + catchBody.addStatement(new ExecuteNode(source, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally)); 10.640 + setTerminal(catchBody, true); 10.641 + 10.642 + final CatchNode catchAllNode; 10.643 + final IdentNode exception; 10.644 + 10.645 + exception = new IdentNode(source, token, finish, compiler.uniqueName("catch_all")); 10.646 + catchAllNode = new CatchNode(source, token, finish, new IdentNode(exception), null, catchBody); 10.647 + catchAllNode.setIsSyntheticRethrow(); 10.648 + 10.649 + catchBlock.addStatement(catchAllNode); 10.650 + 10.651 + // replace all catches of outer tryNode with the catch-all 10.652 + tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock))); 10.653 + 10.654 + /* 10.655 + * We leave the finally block for the original try in place for now 10.656 + * so that children visitations will work. It is removed and placed 10.657 + * afterwards in the else case below, after all children are visited 10.658 + */ 10.659 + 10.660 + return tryNode; 10.661 + } 10.662 + 10.663 + @Override 10.664 + public Node leave(final TryNode tryNode) { 10.665 + final Block finallyBody = tryNode.getFinallyBody(); 10.666 + 10.667 + boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal()); 10.668 + 10.669 + for (final Block catchBlock : tryNode.getCatchBlocks()) { 10.670 + allTerminal &= catchBlock.isTerminal(); 10.671 + } 10.672 + 10.673 + tryNode.setIsTerminal(allTerminal); 10.674 + 10.675 + addStatement(tryNode); 10.676 + unnest(tryNode); 10.677 + 10.678 + // if finally body is present, place it after the tryNode 10.679 + if (finallyBody != null) { 10.680 + tryNode.setFinallyBody(null); 10.681 + addStatement(finallyBody); 10.682 + } 10.683 + 10.684 + return tryNode; 10.685 + } 10.686 + 10.687 + @Override 10.688 + public Node leave(final VarNode varNode) { 10.689 + addStatement(varNode); 10.690 + return varNode; 10.691 + } 10.692 + 10.693 + @Override 10.694 + public Node enter(final WhileNode whileNode) { 10.695 + return nest(whileNode); 10.696 + } 10.697 + 10.698 + @Override 10.699 + public Node leave(final WhileNode whileNode) { 10.700 + final Node test = whileNode.getTest(); 10.701 + 10.702 + if (test == null) { 10.703 + setHasGoto(whileNode); 10.704 + } 10.705 + 10.706 + final Block body = whileNode.getBody(); 10.707 + final boolean escapes = controlFlowEscapes(body); 10.708 + if (escapes) { 10.709 + setTerminal(body, false); 10.710 + } 10.711 + 10.712 + Node node = whileNode; 10.713 + 10.714 + if (body.isTerminal()) { 10.715 + if (whileNode instanceof DoWhileNode) { 10.716 + setTerminal(whileNode, true); 10.717 + } else if (conservativeAlwaysTrue(test)) { 10.718 + node = new ForNode(source, whileNode.getToken(), whileNode.getFinish()); 10.719 + ((ForNode)node).setBody(body); 10.720 + ((ForNode)node).accept(this); 10.721 + setTerminal(node, !escapes); 10.722 + } 10.723 + } 10.724 + 10.725 + // pop the loop from the loop context 10.726 + unnest(whileNode); 10.727 + addStatement(node); 10.728 + 10.729 + return node; 10.730 + } 10.731 + 10.732 + @Override 10.733 + public Node leave(final WithNode withNode) { 10.734 + if (withNode.getBody().isTerminal()) { 10.735 + setTerminal(withNode, true); 10.736 + } 10.737 + addStatement(withNode); 10.738 + 10.739 + return withNode; 10.740 + } 10.741 + 10.742 + @Override 10.743 + public Node leaveDELETE(final UnaryNode unaryNode) { 10.744 + final Node rhs = unaryNode.rhs(); 10.745 + if (rhs instanceof IdentNode || rhs instanceof BaseNode) { 10.746 + return unaryNode; 10.747 + } 10.748 + addStatement(new ExecuteNode(rhs)); 10.749 + return LiteralNode.newInstance(unaryNode, true); 10.750 + } 10.751 + 10.752 + /** 10.753 + * Given a function node that is a callee in a CallNode, replace it with 10.754 + * the appropriate marker function. This is used by {@link CodeGenerator} 10.755 + * for fast scope calls 10.756 + * 10.757 + * @param function function called by a CallNode 10.758 + * @return transformed node to marker function or identity if not ident/access/indexnode 10.759 + */ 10.760 + private static Node markerFunction(final Node function) { 10.761 + if (function instanceof IdentNode) { 10.762 + return new IdentNode((IdentNode)function) { 10.763 + @Override 10.764 + public boolean isFunction() { 10.765 + return true; 10.766 + } 10.767 + }; 10.768 + } else if (function instanceof AccessNode) { 10.769 + return new AccessNode((AccessNode)function) { 10.770 + @Override 10.771 + public boolean isFunction() { 10.772 + return true; 10.773 + } 10.774 + }; 10.775 + } else if (function instanceof IndexNode) { 10.776 + return new IndexNode((IndexNode)function) { 10.777 + @Override 10.778 + public boolean isFunction() { 10.779 + return true; 10.780 + } 10.781 + }; 10.782 + } 10.783 + 10.784 + return function; 10.785 + } 10.786 + 10.787 + /** 10.788 + * Calculate a synthetic eval location for a node for the stacktrace, for example src#17<eval> 10.789 + * @param node a node 10.790 + * @return eval location 10.791 + */ 10.792 + private static String evalLocation(final IdentNode node) { 10.793 + //final StringBuilder sb = new StringBuilder(node.getSource().getName()); 10.794 + return new StringBuilder(). 10.795 + append(node.getSource().getName()). 10.796 + append('#'). 10.797 + append(node.getSource().getLine(node.position())). 10.798 + append("<eval>"). 10.799 + toString(); 10.800 + } 10.801 + 10.802 + /** 10.803 + * Check whether a call node may be a call to eval. In that case we 10.804 + * clone the args in order to create the following construct in 10.805 + * {@link CodeGenerator} 10.806 + * 10.807 + * <pre> 10.808 + * if (calledFuntion == buildInEval) { 10.809 + * eval(cloned arg); 10.810 + * } else { 10.811 + * cloned arg; 10.812 + * } 10.813 + * </pre> 10.814 + * 10.815 + * @param callNode call node to check if it's an eval 10.816 + */ 10.817 + private void checkEval(final CallNode callNode) { 10.818 + if (callNode.getFunction() instanceof IdentNode) { 10.819 + 10.820 + final List<Node> args = callNode.getArgs(); 10.821 + final IdentNode callee = (IdentNode)callNode.getFunction(); 10.822 + 10.823 + // 'eval' call with at least one argument 10.824 + if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) { 10.825 + final CallNode.EvalArgs evalArgs = 10.826 + new CallNode.EvalArgs( 10.827 + args.get(0).clone().accept(this), //clone as we use this for the "is eval case". original evaluated separately for "is not eval case" 10.828 + getCurrentFunctionNode().getThisNode(), 10.829 + evalLocation(callee), 10.830 + getCurrentFunctionNode().isStrictMode()); 10.831 + callNode.setEvalArgs(evalArgs); 10.832 + } 10.833 + } 10.834 + } 10.835 + 10.836 + private static boolean conservativeAlwaysTrue(final Node node) { 10.837 + return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue())); 10.838 + } 10.839 + 10.840 + /** 10.841 + * Helper that given a loop body makes sure that it is not terminal if it 10.842 + * has a continue that leads to the loop header or to outer loops' loop 10.843 + * headers. This means that, even if the body ends with a terminal 10.844 + * statement, we cannot tag it as terminal 10.845 + * 10.846 + * @param loopBody the loop body to check 10.847 + * @return true if control flow may escape the loop 10.848 + */ 10.849 + private boolean controlFlowEscapes(final Node loopBody) { 10.850 + final List<Node> escapes = new ArrayList<>(); 10.851 + 10.852 + loopBody.accept(new NodeVisitor() { 10.853 + @Override 10.854 + public Node leave(final BreakNode node) { 10.855 + escapes.add(node); 10.856 + return node; 10.857 + } 10.858 + 10.859 + @Override 10.860 + public Node leave(final ContinueNode node) { 10.861 + // all inner loops have been popped. 10.862 + if (nesting.contains(node.getTargetNode())) { 10.863 + escapes.add(node); 10.864 + } 10.865 + return node; 10.866 + } 10.867 + }); 10.868 + 10.869 + return !escapes.isEmpty(); 10.870 + } 10.871 + 10.872 + private void guaranteeReturn(final FunctionNode functionNode) { 10.873 + Node resultNode; 10.874 + 10.875 + if (functionNode.isScript()) { 10.876 + resultNode = functionNode.getResultNode(); // the eval result, symbol assigned in Attr 10.877 + } else { 10.878 + if (lastStatement != null && lastStatement.isTerminal() || lastStatement instanceof ReturnNode) { 10.879 + return; //already in place or not needed, as it should be for a non-undefined returning function 10.880 + } 10.881 + resultNode = LiteralNode.newInstance(functionNode, ScriptRuntime.UNDEFINED); 10.882 + } 10.883 + 10.884 + //create a return statement 10.885 + final Node returnNode = new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null); 10.886 + returnNode.accept(this); 10.887 + } 10.888 + 10.889 + 10.890 + private Node nest(final Node node) { 10.891 + LOG.info("Nesting: " + node); 10.892 + LOG.indent(); 10.893 + nesting.push(node); 10.894 + return node; 10.895 + } 10.896 + 10.897 + private void unnest(final Node node) { 10.898 + LOG.unindent(); 10.899 + assert nesting.getFirst() == node : "inconsistent nesting order : " + nesting.getFirst() + " != " + node; 10.900 + LOG.info("Unnesting: " + nesting); 10.901 + nesting.pop(); 10.902 + } 10.903 + 10.904 + private static void setTerminal(final Node node, final boolean isTerminal) { 10.905 + LOG.info("terminal = " + isTerminal + " for " + node); 10.906 + node.setIsTerminal(isTerminal); 10.907 + } 10.908 + 10.909 + private static void setHasGoto(final Node node) { //, final boolean hasGoto) { 10.910 + LOG.info("hasGoto = true for " + node); 10.911 + node.setHasGoto(); 10.912 + } 10.913 + 10.914 + private static void copyTerminal(final Node node, final Node sourceNode) { 10.915 + LOG.info("copy terminal flags " + sourceNode + " -> " + node); 10.916 + node.copyTerminalFlags(sourceNode); 10.917 + } 10.918 + 10.919 + private void addStatement(final Node statement, final boolean storeInLastStatement) { 10.920 + LOG.info("add statement = " + statement + " (lastStatement = " + lastStatement + ")"); 10.921 + statements.add(statement); 10.922 + if (storeInLastStatement) { 10.923 + lastStatement = statement; 10.924 + } 10.925 + } 10.926 + 10.927 + private void addStatement(final Node statement) { 10.928 + addStatement(statement, true); 10.929 + } 10.930 + 10.931 /** 10.932 * Determine if Try block is inside target block. 10.933 * 10.934 @@ -316,271 +909,15 @@ 10.935 return false; 10.936 } 10.937 10.938 - @Override 10.939 - public Node enter(final BreakNode breakNode) { 10.940 - final TryNode tryNode = breakNode.getTryChain(); 10.941 - 10.942 - if (tryNode != null && copyFinally(tryNode, breakNode.getTargetNode())) { 10.943 + private Node enterBreakOrContinue(final LabeledNode labeledNode) { 10.944 + final TryNode tryNode = labeledNode.getTryChain(); 10.945 + if (tryNode != null && copyFinally(tryNode, labeledNode.getTargetNode())) { 10.946 return null; 10.947 } 10.948 - 10.949 - statements.add(breakNode); 10.950 - 10.951 + addStatement(labeledNode); 10.952 return null; 10.953 } 10.954 10.955 - /** 10.956 - * Blesses nodes with an expectant type, converting if necessary. 10.957 - * 10.958 - * @param node Node to be blest. 10.959 - * @param type Type class expected. 10.960 - * 10.961 - * @return Converted node or original node if no conversion is needed. 10.962 - */ 10.963 - private Node convert(final Node node, final Type type) { 10.964 - 10.965 - final Symbol symbol = node.getSymbol(); 10.966 - final FunctionNode functionNode = getCurrentFunctionNode(); 10.967 - 10.968 - assert !type.isUnknown() : "unknown"; 10.969 - 10.970 - /* 10.971 - * Conversions are now mandatory, have to be placed at code time and 10.972 - * cannot be removed as there might be cases like: 10.973 - * 10.974 - * var x = 17; if (x + 1) { ... } 10.975 - * 10.976 - * x = Number(4711); if (x + 1) { ... } 10.977 - * 10.978 - * In the old world this behaved as follows: 10.979 - * 10.980 - * Here, x is originally inferred type to a number, then the if does a 10.981 - * double add without a cast. However, the next statement turns x into 10.982 - * an object, and this messes up the original. If we would now suddenly 10.983 - * need an explicit cast for the first addition, given that we don't do 10.984 - * fancy stuff like splitting live range. 10.985 - * 10.986 - * Even worse is the case where we have an eval that modifies local 10.987 - * variables in the middle of a function and may widen a type suspected 10.988 - * to be at most e.g. NUMBER to e.g. OBJECT. 10.989 - * 10.990 - * There are a few local optimizations we can do. If we want to do an 10.991 - * OBJECT to OBJECT cast, for example, we can skip it as already is 10.992 - * maximally wide, except if we are in a method with an eval where 10.993 - * everything is possible... 10.994 - * 10.995 - * @see test/test262/test/suite/ch07/7.2/S7.2_A1.4_T2.js for an example 10.996 - * of this. 10.997 - */ 10.998 - 10.999 - assert symbol != null : "no symbol for " + node; 10.1000 - 10.1001 - /* check object to object cast */ 10.1002 - if (!functionNode.hasEval() && node.getType().isEquivalentTo(Type.OBJECT) && type.isEquivalentTo(Type.OBJECT)) { 10.1003 - return node; 10.1004 - } 10.1005 - 10.1006 - Node resultNode = node; 10.1007 - 10.1008 - // Literal nodes may be converted directly 10.1009 - 10.1010 - if (node instanceof LiteralNode) { 10.1011 - final LiteralNode<?> convertedLiteral = new LiteralNodeConstantEvaluator((LiteralNode<?>)node, type).eval(); 10.1012 - if (convertedLiteral != null) { 10.1013 - resultNode = newLiteral(convertedLiteral); 10.1014 - } 10.1015 - // object literals still need the cast 10.1016 - if (type.isObject()) { 10.1017 - resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), node); 10.1018 - } 10.1019 - } else { 10.1020 - if (resultNode.getSymbol().isParam()) { 10.1021 - resultNode.getSymbol().setType(type); 10.1022 - } 10.1023 - resultNode = new UnaryNode(source, Token.recast(node.getToken(), TokenType.CONVERT), resultNode); 10.1024 - } 10.1025 - 10.1026 - functionNode.newTemporary(type, resultNode); 10.1027 - resultNode.copyTerminalFlags(node); 10.1028 - 10.1029 - return resultNode; 10.1030 - } 10.1031 - 10.1032 - /** 10.1033 - * Accept and convert all arguments to type Object. If we have a 10.1034 - * specialization profile for this function, we instead try to specialize 10.1035 - * the arguments before the casts based on their current types and values. 10.1036 - * 10.1037 - * @param callNode function call 10.1038 - * @return return type for call 10.1039 - */ 10.1040 - private Type acceptArgs(final CallNode callNode) { 10.1041 - final List<Node> oldArgs = callNode.getArgs(); 10.1042 - final List<Node> acceptedArgs = new ArrayList<>(oldArgs.size()); 10.1043 - 10.1044 - for (final Node arg : oldArgs) { 10.1045 - //acceptedArgs.add(convert(arg.accept(this), OBJECT)); 10.1046 - acceptedArgs.add(arg.accept(this)); 10.1047 - } 10.1048 - callNode.setArgs(acceptedArgs); 10.1049 - 10.1050 - return Type.OBJECT; 10.1051 - } 10.1052 - 10.1053 - private static String evalLocation(final IdentNode node) { 10.1054 - final StringBuilder sb = new StringBuilder(node.getSource().getName()); 10.1055 - 10.1056 - sb.append('#'); 10.1057 - sb.append(node.getSource().getLine(node.position())); 10.1058 - sb.append("<eval>"); 10.1059 - 10.1060 - return sb.toString(); 10.1061 - } 10.1062 - 10.1063 - private void checkEval(final CallNode callNode) { 10.1064 - if (callNode.getFunction() instanceof IdentNode) { 10.1065 - 10.1066 - final List<Node> args = callNode.getArgs(); 10.1067 - final IdentNode callee = (IdentNode)callNode.getFunction(); 10.1068 - 10.1069 - // 'eval' call with atleast one argument 10.1070 - if (args.size() >= 1 && EVAL.tag().equals(callee.getName())) { 10.1071 - final CallNode.EvalArgs evalArgs = new CallNode.EvalArgs(); 10.1072 - // code that is evaluated 10.1073 - evalArgs.code = args.get(0).clone(); 10.1074 - evalArgs.code.accept(this); 10.1075 - // 'this' to be passed to evaluated code 10.1076 - evalArgs.evalThis = new IdentNode(getCurrentFunctionNode().getThisNode()); 10.1077 - // location string of the eval call 10.1078 - evalArgs.location = evalLocation(callee); 10.1079 - // strict mode context or not? 10.1080 - evalArgs.strictMode = getCurrentFunctionNode().isStrictMode(); 10.1081 - callNode.setEvalArgs(evalArgs); 10.1082 - } 10.1083 - } 10.1084 - } 10.1085 - 10.1086 - private static Node markerFunction(final Node function) { 10.1087 - if (function instanceof IdentNode) { 10.1088 - return new IdentNode((IdentNode)function) { 10.1089 - @Override 10.1090 - public boolean isFunction() { 10.1091 - return true; 10.1092 - } 10.1093 - }; 10.1094 - } else if (function instanceof AccessNode) { 10.1095 - return new AccessNode((AccessNode)function) { 10.1096 - @Override 10.1097 - public boolean isFunction() { 10.1098 - return true; 10.1099 - } 10.1100 - }; 10.1101 - } else if (function instanceof IndexNode) { 10.1102 - return new IndexNode((IndexNode)function) { 10.1103 - @Override 10.1104 - public boolean isFunction() { 10.1105 - return true; 10.1106 - } 10.1107 - }; 10.1108 - } 10.1109 - 10.1110 - return function; 10.1111 - } 10.1112 - 10.1113 - @Override 10.1114 - public Node enter(final CallNode callNode) { 10.1115 - final Node function = callNode.getFunction(); 10.1116 - final Node markedFunction = markerFunction(function); 10.1117 - 10.1118 - callNode.setFunction(markedFunction.accept(this)); 10.1119 - 10.1120 - checkEval(callNode); 10.1121 - 10.1122 - final Type returnType = acceptArgs(callNode); 10.1123 - getCurrentFunctionNode().newTemporary(returnType, callNode); 10.1124 - callNode.getFunction().getSymbol().setType(returnType); 10.1125 - 10.1126 - return null; 10.1127 - } 10.1128 - 10.1129 - @Override 10.1130 - public Node leave(final CaseNode caseNode) { 10.1131 - caseNode.copyTerminalFlags(caseNode.getBody()); 10.1132 - 10.1133 - return caseNode; 10.1134 - } 10.1135 - 10.1136 - @Override 10.1137 - public Node enter(final CatchNode catchNode) { 10.1138 - final IdentNode ident = catchNode.getException(); 10.1139 - final Block block = getCurrentBlock(); 10.1140 - 10.1141 - // define block-local exception variable 10.1142 - block.defineSymbol(ident.getName(), IS_VAR | IS_LET, ident).setType(Type.OBJECT); 10.1143 - localDefs.add(ident.getName()); 10.1144 - 10.1145 - return catchNode; 10.1146 - } 10.1147 - 10.1148 - @Override 10.1149 - public Node leave(final CatchNode catchNode) { 10.1150 - final Node exceptionCondition = catchNode.getExceptionCondition(); 10.1151 - if (exceptionCondition != null) { 10.1152 - catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); 10.1153 - } 10.1154 - 10.1155 - catchNode.copyTerminalFlags(catchNode.getBody()); 10.1156 - 10.1157 - statements.add(catchNode); 10.1158 - 10.1159 - return catchNode; 10.1160 - } 10.1161 - 10.1162 - @Override 10.1163 - public Node enter(final ContinueNode continueNode) { 10.1164 - final TryNode tryNode = continueNode.getTryChain(); 10.1165 - final Node target = continueNode.getTargetNode(); 10.1166 - 10.1167 - if (tryNode != null && copyFinally(tryNode, target)) { 10.1168 - return null; 10.1169 - } 10.1170 - 10.1171 - statements.add(continueNode); 10.1172 - 10.1173 - return null; 10.1174 - } 10.1175 - 10.1176 - @Override 10.1177 - public Node enter(final DoWhileNode whileNode) { 10.1178 - return enter((WhileNode)whileNode); 10.1179 - } 10.1180 - 10.1181 - @Override 10.1182 - public Node leave(final DoWhileNode whileNode) { 10.1183 - return leave((WhileNode)whileNode); 10.1184 - } 10.1185 - 10.1186 - @Override 10.1187 - public Node enter(final EmptyNode emptyNode) { 10.1188 - return null; 10.1189 - } 10.1190 - 10.1191 - /** 10.1192 - * Is this an assignment to the special variable that hosts scripting eval 10.1193 - * results? 10.1194 - * 10.1195 - * @param expression expression to check whether it is $evalresult = X 10.1196 - * 10.1197 - * @return true if an assignment to eval result, false otherwise 10.1198 - */ 10.1199 - private boolean isEvalResultAssignment(final Node expression) { 10.1200 - Node e = expression; 10.1201 - if (e.tokenType() == TokenType.DISCARD) { 10.1202 - e = ((UnaryNode)expression).rhs(); 10.1203 - } 10.1204 - final Node resultNode = getCurrentFunctionNode().getResultNode(); 10.1205 - return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode); 10.1206 - } 10.1207 10.1208 /** 10.1209 * An internal expression has a symbol that is tagged internal. Check if 10.1210 @@ -595,2543 +932,41 @@ 10.1211 } 10.1212 10.1213 /** 10.1214 - * Discard the result of the expression. 10.1215 + * Is this an assignment to the special variable that hosts scripting eval 10.1216 + * results? 10.1217 * 10.1218 - * @param expression Expression to discard. 10.1219 - * 10.1220 - * @return Discard node. 10.1221 + * @param expression expression to check whether it is $evalresult = X 10.1222 + * @return true if an assignment to eval result, false otherwise 10.1223 */ 10.1224 - private Node discard(final Node expression) { 10.1225 - expression.setDiscard(true); 10.1226 - 10.1227 - if (expression.getSymbol() != null) { 10.1228 - final Node discard = new UnaryNode(source, Token.recast(expression.getToken(), TokenType.DISCARD), expression); 10.1229 - discard.copyTerminalFlags(expression); 10.1230 - 10.1231 - return discard; 10.1232 + private boolean isEvalResultAssignment(final Node expression) { 10.1233 + Node e = expression; 10.1234 + if (e.tokenType() == TokenType.DISCARD) { 10.1235 + e = ((UnaryNode)expression).rhs(); 10.1236 } 10.1237 - 10.1238 - return expression; 10.1239 + final Node resultNode = getCurrentFunctionNode().getResultNode(); 10.1240 + return e instanceof BinaryNode && ((BinaryNode)e).lhs().equals(resultNode); 10.1241 } 10.1242 10.1243 /** 10.1244 - * ExecuteNodes are needed to actually generate code, with a few exceptions 10.1245 - * such as ReturnNodes and ThrowNodes and various control flow that can 10.1246 - * standalone. Every other kind of statement needs to be wrapped in an 10.1247 - * ExecuteNode in order to become code 10.1248 - * 10.1249 - * @param executeNode the execute node to visit 10.1250 + * Prepare special function nodes. 10.1251 + * TODO : only create those that are needed. 10.1252 + * TODO : make sure slot numbering is not hardcoded in {@link CompilerConstants} - now creation order is significant 10.1253 */ 10.1254 - @Override 10.1255 - public Node leave(final ExecuteNode executeNode) { 10.1256 - 10.1257 - Node expression = executeNode.getExpression(); 10.1258 - 10.1259 - /* 10.1260 - * Handle the eval result for scripts. Every statement has to write its 10.1261 - * return value to a special variable that is the result of the script. 10.1262 - */ 10.1263 - if (getCurrentFunctionNode().isScript() && !(expression instanceof Block) && !isEvalResultAssignment(expression) && !isInternalExpression(expression)) { 10.1264 - final Node resultNode = getCurrentFunctionNode().getResultNode(); 10.1265 - expression = new BinaryNode(source, Token.recast(executeNode.getToken(), TokenType.ASSIGN), resultNode, convert(expression, resultNode.getType())); 10.1266 - 10.1267 - getCurrentFunctionNode().newTemporary(Type.OBJECT, expression); 10.1268 - } 10.1269 - 10.1270 - expression = discard(expression); 10.1271 - executeNode.setExpression(expression); 10.1272 - executeNode.copyTerminalFlags(expression); 10.1273 - 10.1274 - statements.add(executeNode); 10.1275 - 10.1276 - return executeNode; 10.1277 - } 10.1278 - 10.1279 - /** 10.1280 - * Helper that given a loop body makes sure that it is not terminal if it 10.1281 - * has a continue that leads to the loop header or to outer loops' loop 10.1282 - * headers. This means that, even if the body ends with a terminal 10.1283 - * statement, we cannot tag it as terminal 10.1284 - * 10.1285 - * @param loopBody the loop body to check 10.1286 - */ 10.1287 - private boolean controlFlowEscapes(final Node loopBody) { 10.1288 - final List<Node> escapes = new ArrayList<>(); 10.1289 - 10.1290 - loopBody.accept(new NodeVisitor() { 10.1291 - @Override 10.1292 - public Node leave(final BreakNode node) { 10.1293 - escapes.add(node); 10.1294 - return node; 10.1295 - } 10.1296 - 10.1297 - @Override 10.1298 - public Node leave(final ContinueNode node) { 10.1299 - // all inner loops have been popped. 10.1300 - if (nesting.contains(node.getTargetNode())) { 10.1301 - escapes.add(node); 10.1302 - } 10.1303 - return node; 10.1304 - } 10.1305 - }); 10.1306 - 10.1307 - return !escapes.isEmpty(); 10.1308 - } 10.1309 - 10.1310 - @Override 10.1311 - public Node enter(final ForNode forNode) { 10.1312 - // push the loop to the loop context 10.1313 - nest(forNode); 10.1314 - return forNode; 10.1315 - } 10.1316 - 10.1317 - private static boolean conservativeAlwaysTrue(final Node node) { 10.1318 - return node == null || ((node instanceof LiteralNode) && Boolean.TRUE.equals(((LiteralNode<?>)node).getValue())); 10.1319 - } 10.1320 - 10.1321 - @Override 10.1322 - public Node leave(final ForNode forNode) { 10.1323 - final Node init = forNode.getInit(); 10.1324 - final Node test = forNode.getTest(); 10.1325 - final Node modify = forNode.getModify(); 10.1326 - 10.1327 - if (forNode.isForIn()) { 10.1328 - final String name = compiler.uniqueName(ITERATOR_PREFIX.tag()); 10.1329 - // DEFINE SYMBOL: may be any type, not local var 10.1330 - final Symbol iter = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); 10.1331 - iter.setType(Type.OBJECT); // NASHORN-73 10.1332 - forNode.setIterator(iter); 10.1333 - forNode.setModify(convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 10.1334 - 10.1335 - /* 10.1336 - * Iterators return objects, so we need to widen the scope of the 10.1337 - * init variable if it, for example, has been assigned double type 10.1338 - * see NASHORN-50 10.1339 - */ 10.1340 - forNode.getInit().getSymbol().setType(Type.OBJECT); 10.1341 - } else { 10.1342 - /* Normal for node, not for in */ 10.1343 - 10.1344 - if (init != null) { 10.1345 - forNode.setInit(discard(init)); 10.1346 - } 10.1347 - 10.1348 - if (test != null) { 10.1349 - forNode.setTest(convert(test, Type.BOOLEAN)); 10.1350 - } else { 10.1351 - forNode.setHasGoto(); 10.1352 - } 10.1353 - 10.1354 - if (modify != null) { 10.1355 - forNode.setModify(discard(modify)); 10.1356 - } 10.1357 - } 10.1358 - 10.1359 - final Block body = forNode.getBody(); 10.1360 - 10.1361 - final boolean escapes = controlFlowEscapes(body); 10.1362 - if (escapes) { 10.1363 - body.setIsTerminal(false); 10.1364 - } 10.1365 - 10.1366 - // pop the loop from the loop context 10.1367 - unnest(); 10.1368 - 10.1369 - if (!forNode.isForIn() && conservativeAlwaysTrue(test)) { 10.1370 - forNode.setTest(null); 10.1371 - forNode.setIsTerminal(!escapes); 10.1372 - } 10.1373 - 10.1374 - statements.add(forNode); 10.1375 - 10.1376 - return forNode; 10.1377 - } 10.1378 - 10.1379 - /** 10.1380 - * Initialize this symbol and variable for function node 10.1381 - * 10.1382 - * @param functionNode the function node 10.1383 - */ 10.1384 - private void initThis(final FunctionNode functionNode) { 10.1385 - final long token = functionNode.getToken(); 10.1386 - final int finish = functionNode.getFinish(); 10.1387 - 10.1388 - final Symbol thisSymbol = functionNode.defineSymbol(THIS.tag(), IS_PARAM | IS_THIS, null); 10.1389 - final IdentNode thisNode = new IdentNode(source, token, finish, thisSymbol.getName()); 10.1390 - 10.1391 - thisSymbol.setType(Type.OBJECT); 10.1392 - thisSymbol.setNeedsSlot(true); 10.1393 - 10.1394 - thisNode.setSymbol(thisSymbol); 10.1395 - 10.1396 - functionNode.setThisNode(thisNode); 10.1397 - } 10.1398 - 10.1399 - /** 10.1400 - * Initialize scope symbol and variable for function node 10.1401 - * 10.1402 - * @param functionNode the function node 10.1403 - */ 10.1404 - private void initScope(final FunctionNode functionNode) { 10.1405 - final long token = functionNode.getToken(); 10.1406 - final int finish = functionNode.getFinish(); 10.1407 - 10.1408 - final Symbol scopeSymbol = functionNode.defineSymbol(SCOPE.tag(), IS_VAR | IS_INTERNAL, null); 10.1409 - final IdentNode scopeNode = new IdentNode(source, token, finish, scopeSymbol.getName()); 10.1410 - 10.1411 - scopeSymbol.setType(ScriptObject.class); 10.1412 - scopeSymbol.setNeedsSlot(true); 10.1413 - 10.1414 - scopeNode.setSymbol(scopeSymbol); 10.1415 - 10.1416 - functionNode.setScopeNode(scopeNode); 10.1417 - } 10.1418 - 10.1419 - /** 10.1420 - * Initialize return symbol and variable for function node 10.1421 - * 10.1422 - * @param functionNode the function node 10.1423 - */ 10.1424 - private void initReturn(final FunctionNode functionNode) { 10.1425 - final long token = functionNode.getToken(); 10.1426 - final int finish = functionNode.getFinish(); 10.1427 - 10.1428 - final Symbol returnSymbol = functionNode.defineSymbol(SCRIPT_RETURN.tag(), IS_VAR | IS_INTERNAL, null); 10.1429 - final IdentNode returnNode = new IdentNode(source, token, finish, returnSymbol.getName()); 10.1430 - 10.1431 - returnSymbol.setType(Object.class); 10.1432 - returnSymbol.setNeedsSlot(true); 10.1433 - 10.1434 - returnNode.setSymbol(returnSymbol); 10.1435 - 10.1436 - functionNode.setResultNode(returnNode); 10.1437 - } 10.1438 - 10.1439 - /** 10.1440 - * Initialize varargs for function node, if applicable 10.1441 - * 10.1442 - * @param functionNode 10.1443 - */ 10.1444 - private void initVarArg(final FunctionNode functionNode) { 10.1445 + private void initFunctionNode(final FunctionNode functionNode) { 10.1446 final long token = functionNode.getToken(); 10.1447 final int finish = functionNode.getFinish(); 10.1448 10.1449 - assert functionNode.getCalleeNode() != null; 10.1450 + LOG.info("Init this, scope, result, callee, varargs and argument for " + functionNode.getName()); 10.1451 10.1452 - final Symbol varArgsSymbol = functionNode.defineSymbol(VARARGS.tag(), IS_PARAM | IS_INTERNAL, null); 10.1453 - final IdentNode varArgsNode = new IdentNode(source, token, finish, varArgsSymbol.getName()); 10.1454 - 10.1455 - varArgsSymbol.setType(Type.OBJECT_ARRAY); 10.1456 - varArgsSymbol.setNeedsSlot(true); 10.1457 - 10.1458 - varArgsNode.setSymbol(varArgsSymbol); 10.1459 - 10.1460 - functionNode.setVarArgsNode(varArgsNode); 10.1461 - 10.1462 - final String argumentsName = ARGUMENTS.tag(); 10.1463 - final String name = functionNode.hideArguments() ? functionNode.uniqueName("$" + argumentsName) : argumentsName; 10.1464 - final Symbol argumentsSymbol = functionNode.defineSymbol(name, IS_PARAM | IS_INTERNAL, null); 10.1465 - final IdentNode argumentsNode = new IdentNode(source, token, finish, argumentsSymbol.getName()); 10.1466 - 10.1467 - argumentsSymbol.setType(Type.OBJECT); 10.1468 - argumentsSymbol.setNeedsSlot(true); 10.1469 - 10.1470 - argumentsNode.setSymbol(argumentsSymbol); 10.1471 - 10.1472 - functionNode.setArgumentsNode(argumentsNode); 10.1473 + functionNode.setThisNode(new IdentNode(source, token, finish, THIS.tag())); 10.1474 + functionNode.setScopeNode(new IdentNode(source, token, finish, SCOPE.tag())); 10.1475 + functionNode.setResultNode(new IdentNode(source, token, finish, SCRIPT_RETURN.tag())); 10.1476 + functionNode.setCalleeNode(new IdentNode(source, token, finish, CALLEE.tag())); 10.1477 + functionNode.setVarArgsNode(new IdentNode(source, token, finish, VARARGS.tag())); 10.1478 + functionNode.setArgumentsNode(new IdentNode(source, token, finish, functionNode.hideArguments() ? compiler.uniqueName('$' + ARGUMENTS.tag()) : ARGUMENTS.tag())); 10.1479 } 10.1480 10.1481 - private void initCallee(final FunctionNode functionNode) { 10.1482 - if (functionNode.getCalleeNode() != null) { 10.1483 - return; 10.1484 - } 10.1485 +} 10.1486 10.1487 - final long token = functionNode.getToken(); 10.1488 - final int finish = functionNode.getFinish(); 10.1489 10.1490 - final Symbol calleeSymbol = functionNode.defineSymbol(CALLEE.tag(), IS_PARAM | IS_INTERNAL, null); 10.1491 - final IdentNode calleeNode = new IdentNode(source, token, finish, calleeSymbol.getName()); 10.1492 10.1493 - calleeSymbol.setType(ScriptFunction.class); 10.1494 - calleeSymbol.setNeedsSlot(true); 10.1495 - 10.1496 - calleeNode.setSymbol(calleeSymbol); 10.1497 - 10.1498 - functionNode.setCalleeNode(calleeNode); 10.1499 - } 10.1500 - 10.1501 - /** 10.1502 - * Initialize parameters for function node. This may require specializing 10.1503 - * types if a specialization profile is known 10.1504 - * 10.1505 - * @param functionNode the function node 10.1506 - */ 10.1507 - private void initParameters(final FunctionNode functionNode) { 10.1508 - /* 10.1509 - * If a function is specialized, we don't need to tag either it return 10.1510 - * type or its parameters with the widest (OBJECT) type for safety. 10.1511 - */ 10.1512 - functionNode.setReturnType(Type.UNKNOWN); 10.1513 - 10.1514 - for (final IdentNode ident : functionNode.getParameters()) { 10.1515 - localDefs.add(ident.getName()); 10.1516 - final Symbol paramSymbol = functionNode.defineSymbol(ident.getName(), IS_PARAM, ident); 10.1517 - if (paramSymbol != null) { 10.1518 - paramSymbol.setType(Type.UNKNOWN); 10.1519 - } 10.1520 - } 10.1521 - } 10.1522 - 10.1523 - /** 10.1524 - * This has to run before fix assignment types, store any type specializations for 10.1525 - * paramters, then turn then to objects for the generic version of this method 10.1526 - * 10.1527 - * @param functionNode functionNode 10.1528 - */ 10.1529 - private static void fixParameters(final FunctionNode functionNode) { 10.1530 - boolean nonObjectParams = false; 10.1531 - List<Type> paramSpecializations = new ArrayList<>(); 10.1532 - 10.1533 - for (final IdentNode ident : functionNode.getParameters()) { 10.1534 - final Symbol paramSymbol = ident.getSymbol(); 10.1535 - if (paramSymbol != null) { 10.1536 - Type type = paramSymbol.getSymbolType(); 10.1537 - if (type.isUnknown()) { 10.1538 - type = Type.OBJECT; 10.1539 - } 10.1540 - paramSpecializations.add(type); 10.1541 - if (!type.isObject()) { 10.1542 - nonObjectParams = true; 10.1543 - } 10.1544 - paramSymbol.setType(Type.OBJECT); 10.1545 - } 10.1546 - } 10.1547 - 10.1548 - if (!nonObjectParams) { 10.1549 - paramSpecializations = null; 10.1550 - /* 10.1551 - * Later, when resolving a call to this method, the linker can say "I have a double, an int and an object" as parameters 10.1552 - * here. If the callee has parameter specializations, we can regenerate it with those particular types for speed. 10.1553 - */ 10.1554 - } else { 10.1555 - LOG.info("parameter specialization possible: " + functionNode.getName() + " " + paramSpecializations); 10.1556 - } 10.1557 - 10.1558 - // parameters should not be slots for a vararg function, make sure this is the case 10.1559 - if (functionNode.isVarArg()) { 10.1560 - for (final IdentNode param : functionNode.getParameters()) { 10.1561 - param.getSymbol().setNeedsSlot(false); 10.1562 - } 10.1563 - } 10.1564 - } 10.1565 - 10.1566 - private LiteralNode<Undefined> undefined() { 10.1567 - return LiteralNode.newInstance(source, 0, 0, UNDEFINED); 10.1568 - } 10.1569 - 10.1570 - private void guaranteeReturn(final FunctionNode functionNode) { 10.1571 - Node resultNode; 10.1572 - 10.1573 - if (functionNode.isScript()) { 10.1574 - resultNode = functionNode.getResultNode(); 10.1575 - } else { 10.1576 - final Node lastStatement = Node.lastStatement(functionNode.getStatements()); 10.1577 - if (lastStatement != null && (lastStatement.isTerminal() || lastStatement instanceof ReturnNode)) { 10.1578 - return; // return already in place, as it should be for a non-undefined returning function 10.1579 - } 10.1580 - resultNode = undefined().accept(this); 10.1581 - } 10.1582 - 10.1583 - new ReturnNode(source, functionNode.getLastToken(), functionNode.getFinish(), resultNode, null).accept(this); 10.1584 - 10.1585 - functionNode.setReturnType(Type.OBJECT); 10.1586 - } 10.1587 - 10.1588 - /** 10.1589 - * Fix return types for a node. The return type is the widest of all known 10.1590 - * return expressions in the function, and has to be the same for all return 10.1591 - * nodes, thus we need to traverse all of them in case some of them must be 10.1592 - * upcast 10.1593 - * 10.1594 - * @param node function node to scan return types for 10.1595 - */ 10.1596 - private void fixReturnTypes(final FunctionNode node) { 10.1597 - 10.1598 - if (node.getReturnType().isUnknown()) { 10.1599 - node.setReturnType(Type.OBJECT); // for example, infinite loops 10.1600 - } 10.1601 - 10.1602 - node.accept(new NodeVisitor() { 10.1603 - @Override 10.1604 - public Node enter(final FunctionNode subFunction) { 10.1605 - //leave subfunctions alone. their return values are already specialized and should not 10.1606 - //be touched. 10.1607 - return null; 10.1608 - } 10.1609 - @Override 10.1610 - public Node leave(final ReturnNode returnNode) { 10.1611 - if (returnNode.hasExpression()) { 10.1612 - returnNode.setExpression(convert(returnNode.getExpression(), node.getReturnType())); 10.1613 - } 10.1614 - return returnNode; 10.1615 - } 10.1616 - }); 10.1617 - } 10.1618 - 10.1619 - /** 10.1620 - * Augment assignments with casts after traversing a function node. 10.1621 - * Assignment nodes are special, as they require entire scope for type 10.1622 - * inference. 10.1623 - * 10.1624 - * Consider e.g. 10.1625 - * 10.1626 - * var x = 18.5; //double 10.1627 - * for (...) { 10.1628 - * var y = x; //initially y is inferred to be double y = func(x) //y changes to object, but above assignment is unaware that it needs to be var y = (object)x instaed 10.1629 - */ 10.1630 - 10.1631 - private void fixAssignmentTypes(final FunctionNode node, final List<Symbol> declaredSymbols) { 10.1632 - 10.1633 - // Make sure all unknown symbols are OBJECT types now. No UNKNOWN may survive lower. 10.1634 - for (final Symbol symbol : declaredSymbols) { 10.1635 - if (symbol.getSymbolType().isUnknown()) { 10.1636 - LOG.finest("fixAssignmentTypes: widening " + symbol + " to object " + symbol + " in " + getCurrentFunctionNode()); 10.1637 - symbol.setType(Type.OBJECT); 10.1638 - symbol.setCanBeUndefined(); 10.1639 - } 10.1640 - 10.1641 - if (symbol.canBeUndefined()) { 10.1642 - /* 10.1643 - * Ideally we'd like to only widen to the narrowest 10.1644 - * possible type that supports local variables, i.e. doubles for 10.1645 - * numerics, but 10.1646 - * 10.1647 - * var x; 10.1648 - * 10.1649 - * if (x !== undefined) do something 10.1650 - * 10.1651 - * x++; 10.1652 - * 10.1653 - * Screws this up, as x is determined to be a double even though 10.1654 - * the use is undefined This can be fixed with better use 10.1655 - * analysis in the IdentNode visitor. 10.1656 - * 10.1657 - * This actually seems to be superseded with calls to ensureTypeNotUnknown 10.1658 - */ 10.1659 - if (!Type.areEquivalent(symbol.getSymbolType(), Type.OBJECT)) { 10.1660 - debug(symbol + " in " + getCurrentFunctionNode() + " can be undefined. Widening to object"); 10.1661 - symbol.setType(Type.OBJECT); 10.1662 - } 10.1663 - } 10.1664 - } 10.1665 - 10.1666 - node.accept(new NodeVisitor() { 10.1667 - 10.1668 - private void fix(final Assignment<? extends Node> assignment) { 10.1669 - final Node src = assignment.getAssignmentSource(); 10.1670 - final Node dest = assignment.getAssignmentDest(); 10.1671 - 10.1672 - if (src == null) { 10.1673 - return; 10.1674 - } 10.1675 - 10.1676 - //we don't know about scope yet so this is too conservative. AccessSpecialized will remove casts that are not required 10.1677 - 10.1678 - final Type srcType = src.getType(); 10.1679 - Type destType = dest.getType(); 10.1680 - 10.1681 - //we can only narrow an operation type if the variable doesn't have a slot 10.1682 - if (!dest.getSymbol().hasSlot() && !node.hasDeepWithOrEval()) { 10.1683 - destType = Type.narrowest(((Node)assignment).getWidestOperationType(), destType); 10.1684 - } 10.1685 - 10.1686 - if (!Type.areEquivalent(destType, srcType)) { 10.1687 - LOG.finest("fixAssignment " + assignment + " type = " + src.getType() + "=>" + dest.getType()); 10.1688 - assignment.setAssignmentSource(convert(src, destType)); 10.1689 - } 10.1690 - } 10.1691 - 10.1692 - private Node checkAssignment(final Node assignNode) { 10.1693 - if (!assignNode.isAssignment()) { 10.1694 - return assignNode; 10.1695 - } 10.1696 - fix((Assignment<?>)assignNode); 10.1697 - return assignNode; 10.1698 - } 10.1699 - 10.1700 - 10.1701 - @Override 10.1702 - public Node leave(final UnaryNode unaryNode) { 10.1703 - return checkAssignment(unaryNode); 10.1704 - } 10.1705 - 10.1706 - @Override 10.1707 - public Node leave(final BinaryNode binaryNode) { 10.1708 - return checkAssignment(binaryNode); 10.1709 - } 10.1710 - 10.1711 - @Override 10.1712 - public Node leave(final VarNode varNode) { 10.1713 - return checkAssignment(varNode); 10.1714 - } 10.1715 - }); 10.1716 - } 10.1717 - 10.1718 - /* 10.1719 - * For a script, add scope symbols as defined in the property map 10.1720 - */ 10.1721 - private static void initFromPropertyMap(final Context context, final FunctionNode functionNode) { 10.1722 - assert functionNode.isScript(); 10.1723 - 10.1724 - final PropertyMap map = context.getGlobalMap(); 10.1725 - 10.1726 - for (final Property property : map.getProperties()) { 10.1727 - final String key = property.getKey(); 10.1728 - functionNode.defineSymbol(key, IS_GLOBAL, null).setType(Type.OBJECT); 10.1729 - } 10.1730 - } 10.1731 - 10.1732 - @Override 10.1733 - public Node enter(final FunctionNode functionNode) { 10.1734 - LOG.info("START FunctionNode: " + functionNode.getName()); 10.1735 - 10.1736 - Node initialEvalResult = undefined(); 10.1737 - 10.1738 - nest(functionNode); 10.1739 - 10.1740 - localDefs = new HashSet<>(); 10.1741 - localUses = new HashSet<>(); 10.1742 - 10.1743 - functionNode.setFrame(functionNode.pushFrame()); 10.1744 - 10.1745 - initThis(functionNode); 10.1746 - initCallee(functionNode); 10.1747 - if (functionNode.isVarArg()) { 10.1748 - initVarArg(functionNode); 10.1749 - } 10.1750 - 10.1751 - initParameters(functionNode); 10.1752 - initScope(functionNode); 10.1753 - initReturn(functionNode); 10.1754 - 10.1755 - /* 10.1756 - * Add all nested functions as symbols in this function 10.1757 - */ 10.1758 - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 10.1759 - final IdentNode ident = nestedFunction.getIdent(); 10.1760 - 10.1761 - // for initial eval result is the last declared function 10.1762 - if (ident != null && nestedFunction.isStatement()) { 10.1763 - final Symbol functionSymbol = functionNode.defineSymbol(ident.getName(), IS_VAR, nestedFunction); 10.1764 - 10.1765 - functionSymbol.setType(ScriptFunction.class); 10.1766 - initialEvalResult = new IdentNode(ident); 10.1767 - } 10.1768 - } 10.1769 - 10.1770 - if (functionNode.isScript()) { 10.1771 - initFromPropertyMap(compiler.getContext(), functionNode); 10.1772 - } 10.1773 - 10.1774 - // Add function name as local symbol 10.1775 - if (!functionNode.isStatement() && !functionNode.isAnonymous() && !functionNode.isScript()) { 10.1776 - final Symbol selfSymbol = functionNode.defineSymbol(functionNode.getIdent().getName(), IS_VAR, functionNode); 10.1777 - selfSymbol.setType(Type.OBJECT); 10.1778 - selfSymbol.setNode(functionNode); 10.1779 - } 10.1780 - 10.1781 - /* 10.1782 - * As we are evaluating a nested structure, we need to store the 10.1783 - * statement list for the surrounding block and restore it when the 10.1784 - * function is done 10.1785 - */ 10.1786 - final List<Node> savedStatements = statements; 10.1787 - statements = new ArrayList<>(); 10.1788 - 10.1789 - /* 10.1790 - * This pushes all declarations (except for non-statements, i.e. for 10.1791 - * node temporaries) to the top of the function scope. This way we can 10.1792 - * get around problems like 10.1793 - * 10.1794 - * while (true) { 10.1795 - * break; 10.1796 - * if (true) { 10.1797 - * var s; 10.1798 - * } 10.1799 - * } 10.1800 - * 10.1801 - * to an arbitrary nesting depth. 10.1802 - * 10.1803 - * @see NASHORN-73 10.1804 - */ 10.1805 - 10.1806 - final List<Symbol> declaredSymbols = new ArrayList<>(); 10.1807 - for (final VarNode decl : functionNode.getDeclarations()) { 10.1808 - final IdentNode ident = decl.getName(); 10.1809 - // any declared symbols that aren't visited need to be typed as 10.1810 - // well, hence the list 10.1811 - declaredSymbols.add(functionNode.defineSymbol(ident.getName(), IS_VAR, new IdentNode(ident))); 10.1812 - } 10.1813 - 10.1814 - declaredSymbolsLocal = new ArrayList<>(); 10.1815 - 10.1816 - /* 10.1817 - * Main function code lowering 10.1818 - */ 10.1819 - try { 10.1820 - /* 10.1821 - * Every nested function needs a definition in the outer function 10.1822 - * with its name. Add these. 10.1823 - */ 10.1824 - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 10.1825 - final VarNode varNode = nestedFunction.getFunctionVarNode(); 10.1826 - if (varNode != null) { 10.1827 - final LineNumberNode lineNumberNode = nestedFunction.getFunctionVarLineNumberNode(); 10.1828 - if (lineNumberNode != null) { 10.1829 - lineNumberNode.accept(this); 10.1830 - } 10.1831 - varNode.accept(this); 10.1832 - varNode.setIsFunctionVarNode(); 10.1833 - } 10.1834 - } 10.1835 - 10.1836 - if (functionNode.isScript()) { 10.1837 - new ExecuteNode(source, functionNode.getFirstToken(), functionNode.getFinish(), initialEvalResult).accept(this); 10.1838 - } 10.1839 - 10.1840 - /* 10.1841 - * Now we do the statements. This fills the block with code 10.1842 - */ 10.1843 - for (final Node statement : functionNode.getStatements()) { 10.1844 - statement.accept(this); 10.1845 - 10.1846 - final Node lastStatement = Node.lastStatement(statements); 10.1847 - if (lastStatement != null && lastStatement.hasTerminalFlags()) { 10.1848 - functionNode.copyTerminalFlags(lastStatement); 10.1849 - break; 10.1850 - } 10.1851 - } 10.1852 - 10.1853 - functionNode.setStatements(statements); 10.1854 - 10.1855 - /* 10.1856 - * If there are unusedterminated enpoints in the function, we need 10.1857 - * to add a "return undefined" in those places for correct semantics 10.1858 - */ 10.1859 - if (!functionNode.isTerminal()) { 10.1860 - guaranteeReturn(functionNode); 10.1861 - } 10.1862 - 10.1863 - /* 10.1864 - * Emit all nested functions 10.1865 - */ 10.1866 - for (final FunctionNode nestedFunction : functionNode.getFunctions()) { 10.1867 - nestedFunction.accept(this); 10.1868 - } 10.1869 - 10.1870 - fixReturnTypes(functionNode); 10.1871 - 10.1872 - if (functionNode.needsSelfSymbol()) { 10.1873 - final IdentNode selfSlotNode = new IdentNode(functionNode.getIdent()); 10.1874 - selfSlotNode.setSymbol(functionNode.findSymbol(functionNode.getIdent().getName())); 10.1875 - final Node functionRef = new IdentNode(functionNode.getCalleeNode()); 10.1876 - statements.add(0, new VarNode(source, functionNode.getToken(), functionNode.getFinish(), selfSlotNode, functionRef, false).accept(this)); 10.1877 - } 10.1878 - } finally { 10.1879 - /* 10.1880 - * Restore statement for outer block that we were working on 10.1881 - */ 10.1882 - statements = savedStatements; 10.1883 - } 10.1884 - 10.1885 - unnest(); 10.1886 - 10.1887 - fixParameters(functionNode); 10.1888 - 10.1889 - /* 10.1890 - * Fix assignment issues. assignments are troublesome as they can be on 10.1891 - * the form "dest = dest op source" where the type is different for the 10.1892 - * two dest:s: right now this is a post pass that handles putting casts 10.1893 - * in the correct places, but it can be implemented as a bottom up AST 10.1894 - * traversal on the fly. TODO. 10.1895 - */ 10.1896 - fixAssignmentTypes(functionNode, declaredSymbols); 10.1897 - 10.1898 - /* 10.1899 - * Set state of function to lowered and pop the frame 10.1900 - */ 10.1901 - functionNode.setIsLowered(true); 10.1902 - functionNode.popFrame(); 10.1903 - 10.1904 - LOG.info("END FunctionNode: " + functionNode.getName()); 10.1905 - 10.1906 - return null; 10.1907 - } 10.1908 - 10.1909 - @Override 10.1910 - public Node enter(final IdentNode identNode) { 10.1911 - final String name = identNode.getName(); 10.1912 - 10.1913 - if (identNode.isPropertyName()) { 10.1914 - // assign a pseudo symbol to property name 10.1915 - identNode.setSymbol(new Symbol(name, 0, Type.OBJECT)); 10.1916 - return null; 10.1917 - } 10.1918 - 10.1919 - final Block block = getCurrentBlock(); 10.1920 - final Symbol oldSymbol = identNode.getSymbol(); 10.1921 - Symbol symbol = block.findSymbol(name); 10.1922 - 10.1923 - /* 10.1924 - * If an existing symbol with the name is found, use that otherwise, 10.1925 - * declare a new one 10.1926 - */ 10.1927 - 10.1928 - if (symbol != null) { 10.1929 - if (isFunctionExpressionSelfReference(symbol)) { 10.1930 - ((FunctionNode) symbol.getNode()).setNeedsSelfSymbol(); 10.1931 - } 10.1932 - 10.1933 - if (!identNode.isInitializedHere()) { // NASHORN-448 10.1934 - // here is a use outside the local def scope 10.1935 - if (!localDefs.contains(name)) { 10.1936 - symbol.setType(Type.OBJECT); 10.1937 - symbol.setCanBeUndefined(); 10.1938 - } 10.1939 - } 10.1940 - 10.1941 - identNode.setSymbol(symbol); 10.1942 - if (!getCurrentFunctionNode().isLocal(symbol)) { 10.1943 - // non-local: we need to put symbol in scope (if it isn't already) 10.1944 - if (!symbol.isScope()) { 10.1945 - final List<Block> lookupBlocks = findLookupBlocksHelper(getCurrentFunctionNode(), symbol.findFunction()); 10.1946 - for (final Block lookupBlock : lookupBlocks) { 10.1947 - final Symbol refSymbol = lookupBlock.findSymbol(name); 10.1948 - if (refSymbol != null) { // See NASHORN-837, function declaration in lexical scope: try {} catch (x){ function f() { use(x) } } f() 10.1949 - LOG.finest("Found a ref symbol that must be scope " + refSymbol); 10.1950 - refSymbol.setIsScope(); 10.1951 - } 10.1952 - } 10.1953 - } 10.1954 - } 10.1955 - } else { 10.1956 - symbol = block.useSymbol(name, identNode); 10.1957 - // we have never seen this before, it can be undefined 10.1958 - symbol.setType(Type.OBJECT); // TODO unknown -we have explicit casts anyway? 10.1959 - symbol.setCanBeUndefined(); 10.1960 - } 10.1961 - 10.1962 - if (symbol != oldSymbol && !identNode.isInitializedHere()) { 10.1963 - symbol.increaseUseCount(); 10.1964 - } 10.1965 - localUses.add(identNode.getName()); 10.1966 - 10.1967 - return null; 10.1968 - } 10.1969 - 10.1970 - private static List<Block> findLookupBlocksHelper(final FunctionNode currentFunction, final FunctionNode topFunction) { 10.1971 - if (currentFunction.findParentFunction() == topFunction) { 10.1972 - final List<Block> blocks = new LinkedList<>(); 10.1973 - 10.1974 - blocks.add(currentFunction.getParent()); 10.1975 - blocks.addAll(currentFunction.getReferencingParentBlocks()); 10.1976 - return blocks; 10.1977 - } 10.1978 - /** 10.1979 - * assumption: all parent blocks of an inner function will always be in the same outer function; 10.1980 - * therefore we can simply skip through intermediate functions. 10.1981 - * @see FunctionNode#addReferencingParentBlock(Block) 10.1982 - */ 10.1983 - return findLookupBlocksHelper(currentFunction.findParentFunction(), topFunction); 10.1984 - } 10.1985 - 10.1986 - private static boolean isFunctionExpressionSelfReference(final Symbol symbol) { 10.1987 - if (symbol.isVar() && symbol.getNode() == symbol.getBlock() && symbol.getNode() instanceof FunctionNode) { 10.1988 - return ((FunctionNode) symbol.getNode()).getIdent().getName().equals(symbol.getName()); 10.1989 - } 10.1990 - return false; 10.1991 - } 10.1992 - 10.1993 - @Override 10.1994 - public Node enter(final IfNode ifNode) { 10.1995 - nest(ifNode); 10.1996 - return ifNode; 10.1997 - } 10.1998 - 10.1999 - @Override 10.2000 - public Node leave(final IfNode ifNode) { 10.2001 - 10.2002 - final Node test = convert(ifNode.getTest(), Type.BOOLEAN); 10.2003 - 10.2004 - /* 10.2005 - * constant if checks should short circuit into just one of the 10.2006 - * pass/fail blocks 10.2007 - */ 10.2008 - if (test.getSymbol().isConstant()) { 10.2009 - assert test instanceof LiteralNode<?>; 10.2010 - final Block shortCut = ((LiteralNode<?>)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); 10.2011 - if (shortCut != null) { 10.2012 - for (final Node statement : shortCut.getStatements()) { 10.2013 - statements.add(statement); 10.2014 - } 10.2015 - } 10.2016 - return null; 10.2017 - } 10.2018 - 10.2019 - final Node pass = ifNode.getPass(); 10.2020 - final Node fail = ifNode.getFail(); 10.2021 - 10.2022 - if (pass.isTerminal() && fail != null && fail.isTerminal()) { 10.2023 - ifNode.setIsTerminal(true); 10.2024 - } 10.2025 - 10.2026 - ifNode.setTest(test); 10.2027 - statements.add(ifNode); 10.2028 - 10.2029 - unnest(); 10.2030 - 10.2031 - return ifNode; 10.2032 - } 10.2033 - 10.2034 - @Override 10.2035 - public Node leave(final IndexNode indexNode) { 10.2036 - getCurrentFunctionNode().newTemporary(Type.OBJECT, indexNode); 10.2037 - 10.2038 - return indexNode; 10.2039 - } 10.2040 - 10.2041 - @Override 10.2042 - public Node enter(final LabelNode labeledNode) { 10.2043 - // Don't want a symbol for label. 10.2044 - final Block body = labeledNode.getBody(); 10.2045 - body.accept(this); 10.2046 - labeledNode.copyTerminalFlags(body); 10.2047 - statements.add(labeledNode); 10.2048 - 10.2049 - return null; 10.2050 - } 10.2051 - 10.2052 - @Override 10.2053 - public Node enter(final LineNumberNode lineNumberNode) { 10.2054 - statements.add(lineNumberNode); 10.2055 - return null; 10.2056 - } 10.2057 - 10.2058 - /** 10.2059 - * Generate a new literal symbol. 10.2060 - * 10.2061 - * @param literalNode LiteralNode needing symbol. 10.2062 - * @return The literal node augmented with the symbol 10.2063 - */ 10.2064 - private LiteralNode<?> newLiteral(final LiteralNode<?> literalNode) { 10.2065 - return newLiteral(getCurrentFunctionNode(), literalNode); 10.2066 - } 10.2067 - 10.2068 - private static LiteralNode<?> newLiteral(final FunctionNode functionNode, final LiteralNode<?> literalNode) { 10.2069 - functionNode.newLiteral(literalNode); 10.2070 - return literalNode; 10.2071 - } 10.2072 - 10.2073 - @SuppressWarnings("rawtypes") 10.2074 - @Override 10.2075 - public Node enter(final LiteralNode literalNode) { 10.2076 - if (literalNode.isTokenType(TokenType.THIS)) { 10.2077 - literalNode.setSymbol(getCurrentFunctionNode().getThisNode().getSymbol()); 10.2078 - return null; 10.2079 - } 10.2080 - 10.2081 - if (literalNode instanceof ArrayLiteralNode) { 10.2082 - final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; 10.2083 - final Node[] array = arrayLiteralNode.getValue(); 10.2084 - 10.2085 - for (int i = 0; i < array.length; i++) { 10.2086 - final Node element = array[i]; 10.2087 - if (element != null) { 10.2088 - array[i] = array[i].accept(this); 10.2089 - } 10.2090 - } 10.2091 - 10.2092 - arrayLiteralNode.analyze(); 10.2093 - final Type elementType = arrayLiteralNode.getElementType(); 10.2094 - 10.2095 - // we only have types after all elements are accepted 10.2096 - for (int i = 0; i < array.length; i++) { 10.2097 - final Node element = array[i]; 10.2098 - if (element != null) { 10.2099 - array[i] = convert(element, elementType); 10.2100 - } 10.2101 - } 10.2102 - } else { 10.2103 - final Object value = literalNode.getValue(); 10.2104 - 10.2105 - if (value instanceof Node) { 10.2106 - final Node node = ((Node)value).accept(this); 10.2107 - if (node != null) { 10.2108 - return newLiteral(LiteralNode.newInstance(source, node.getToken(), node.getFinish(), node)); 10.2109 - } 10.2110 - } 10.2111 - } 10.2112 - 10.2113 - newLiteral(literalNode); 10.2114 - 10.2115 - return null; 10.2116 - } 10.2117 - 10.2118 - @Override 10.2119 - public Node leave(final ObjectNode objectNode) { 10.2120 - getCurrentFunctionNode().newTemporary(Type.OBJECT, objectNode); 10.2121 - return objectNode; 10.2122 - } 10.2123 - 10.2124 - @Override 10.2125 - public Node enter(final PropertyNode propertyNode) { 10.2126 - // assign a pseudo symbol to property name, see NASHORN-710 10.2127 - propertyNode.setSymbol(new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); 10.2128 - return propertyNode; 10.2129 - } 10.2130 - 10.2131 - @Override 10.2132 - public Node enter(final ReferenceNode referenceNode) { 10.2133 - final FunctionNode functionNode = referenceNode.getReference(); 10.2134 - if (functionNode != null) { 10.2135 - functionNode.addReferencingParentBlock(getCurrentBlock()); 10.2136 - } 10.2137 - return referenceNode; 10.2138 - } 10.2139 - 10.2140 - @Override 10.2141 - public Node leave(final ReferenceNode referenceNode) { 10.2142 - getCurrentFunctionNode().newTemporary(Type.OBJECT, referenceNode); 10.2143 - return referenceNode; 10.2144 - } 10.2145 - 10.2146 - /** 10.2147 - * For each return node we need to set a return expression of the correct 10.2148 - * type. In the non-specializing world, we can always upcast to object and 10.2149 - * ignore the rest, but in the specializing world we have to keep track of 10.2150 - * the widest return type so far, which is the common one for this method. 10.2151 - * 10.2152 - * @param returnNode the return node we are generating 10.2153 - * @param expression the expression to return from this code 10.2154 - */ 10.2155 - private void setReturnExpression(final ReturnNode returnNode, final Node expression) { 10.2156 - final FunctionNode functionNode = getCurrentFunctionNode(); 10.2157 - 10.2158 - returnNode.setExpression(expression); 10.2159 - 10.2160 - final Symbol symbol = expression.getSymbol(); 10.2161 - if (expression.getType().isUnknown() && symbol.isParam()) { 10.2162 - symbol.setType(Type.OBJECT); 10.2163 - } 10.2164 - functionNode.setReturnType(Type.widest(expression.getType(), functionNode.getReturnType())); 10.2165 - } 10.2166 - 10.2167 - @Override 10.2168 - public Node enter(final ReturnNode returnNode) { 10.2169 - final TryNode tryNode = returnNode.getTryChain(); 10.2170 - final Node expression = returnNode.getExpression(); 10.2171 - 10.2172 - if (tryNode != null) { 10.2173 - if (expression != null) { 10.2174 - final long token = returnNode.getToken(); 10.2175 - final Node resultNode = getCurrentFunctionNode().getResultNode(); 10.2176 - new ExecuteNode(source, token, Token.descPosition(token), new BinaryNode(source, Token.recast(token, TokenType.ASSIGN), resultNode, expression)).accept(this); 10.2177 - 10.2178 - if (copyFinally(tryNode, null)) { 10.2179 - return null; 10.2180 - } 10.2181 - 10.2182 - setReturnExpression(returnNode, resultNode); 10.2183 - } else if (copyFinally(tryNode, null)) { 10.2184 - return null; 10.2185 - } 10.2186 - } else if (expression != null) { 10.2187 - setReturnExpression(returnNode, expression.accept(this)); 10.2188 - } 10.2189 - 10.2190 - statements.add(returnNode); 10.2191 - 10.2192 - return null; 10.2193 - } 10.2194 - 10.2195 - @Override 10.2196 - public Node leave(final RuntimeNode runtimeNode) { 10.2197 - for (final Node arg : runtimeNode.getArgs()) { 10.2198 - ensureTypeNotUnknown(arg); 10.2199 - } 10.2200 - 10.2201 - getCurrentFunctionNode().newTemporary(runtimeNode.getRequest().getReturnType(), runtimeNode); 10.2202 - 10.2203 - return runtimeNode; 10.2204 - } 10.2205 - 10.2206 - @Override 10.2207 - public Node enter(final SwitchNode switchNode) { 10.2208 - nest(switchNode); 10.2209 - return switchNode; 10.2210 - } 10.2211 - 10.2212 - @Override 10.2213 - public Node leave(final SwitchNode switchNode) { 10.2214 - unnest(); 10.2215 - 10.2216 - final Node expression = switchNode.getExpression(); 10.2217 - final List<CaseNode> cases = switchNode.getCases(); 10.2218 - final CaseNode defaultCase = switchNode.getDefaultCase(); 10.2219 - final boolean hasDefault = defaultCase != null; 10.2220 - final int n = cases.size() + (hasDefault ? -1 : 0); 10.2221 - 10.2222 - boolean allTerminal = !cases.isEmpty(); 10.2223 - boolean allInteger = n > 1; 10.2224 - 10.2225 - for (final CaseNode caseNode : cases) { 10.2226 - allTerminal &= caseNode.isTerminal(); 10.2227 - final Node test = caseNode.getTest(); 10.2228 - 10.2229 - // Skip default. 10.2230 - if (test == null) { 10.2231 - continue; 10.2232 - } 10.2233 - 10.2234 - // If not a number. 10.2235 - if (!(test instanceof LiteralNode) || !test.getType().isNumeric()) { 10.2236 - allInteger = false; 10.2237 - continue; 10.2238 - } 10.2239 - 10.2240 - final LiteralNode<?> testLiteral = (LiteralNode<?>)test; 10.2241 - 10.2242 - // If a potential integer. 10.2243 - if (!(testLiteral.getValue() instanceof Integer)) { 10.2244 - // If not an integer value. 10.2245 - if (!JSType.isRepresentableAsInt(testLiteral.getNumber())) { 10.2246 - allInteger = false; 10.2247 - continue; 10.2248 - } 10.2249 - 10.2250 - // Guarantee all case literals are Integers (simplifies sorting in code gen.) 10.2251 - caseNode.setTest(newLiteral(LiteralNode.newInstance(source, testLiteral.getToken(), testLiteral.getFinish(), testLiteral.getInt32()))); 10.2252 - } 10.2253 - } 10.2254 - 10.2255 - if (allTerminal && defaultCase != null && defaultCase.isTerminal()) { 10.2256 - switchNode.setIsTerminal(true); 10.2257 - } 10.2258 - 10.2259 - if (!allInteger) { 10.2260 - switchNode.setExpression(convert(expression, Type.OBJECT)); 10.2261 - 10.2262 - for (final CaseNode caseNode : cases) { 10.2263 - final Node test = caseNode.getTest(); 10.2264 - 10.2265 - if (test != null) { 10.2266 - caseNode.setTest(convert(test, Type.OBJECT)); 10.2267 - } 10.2268 - } 10.2269 - } 10.2270 - 10.2271 - final String name = compiler.uniqueName(SWITCH_TAG_PREFIX.tag()); 10.2272 - final Symbol tag = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); 10.2273 - 10.2274 - tag.setType(allInteger ? Type.INT : Type.OBJECT); 10.2275 - switchNode.setTag(tag); 10.2276 - 10.2277 - statements.add(switchNode); 10.2278 - 10.2279 - return switchNode; 10.2280 - } 10.2281 - 10.2282 - @Override 10.2283 - public Node leave(final ThrowNode throwNode) { 10.2284 - throwNode.setExpression(convert(throwNode.getExpression(), Type.OBJECT)); 10.2285 - statements.add(throwNode); 10.2286 - 10.2287 - return throwNode; 10.2288 - } 10.2289 - 10.2290 - @Override 10.2291 - public Node enter(final TryNode tryNode) { 10.2292 - final Block finallyBody = tryNode.getFinallyBody(); 10.2293 - final Source tryNodeSource = tryNode.getSource(); 10.2294 - final long token = tryNode.getToken(); 10.2295 - final int finish = tryNode.getFinish(); 10.2296 - 10.2297 - nest(tryNode); 10.2298 - 10.2299 - if (finallyBody == null) { 10.2300 - return tryNode; 10.2301 - } 10.2302 - 10.2303 - /** 10.2304 - * We have a finally clause. 10.2305 - * 10.2306 - * Transform to do finally tail duplication as follows: 10.2307 - * 10.2308 - * <pre> 10.2309 - * try { 10.2310 - * try_body 10.2311 - * } catch e1 { 10.2312 - * catchbody_1 10.2313 - * } 10.2314 - * ... 10.2315 - * } catch en { 10.2316 - * catchbody_n 10.2317 - * } finally { 10.2318 - * finally_body 10.2319 - * } 10.2320 - * 10.2321 - * (where e1 ... en are optional) 10.2322 - * 10.2323 - * turns into 10.2324 - * 10.2325 - * try { 10.2326 - * try { 10.2327 - * try_body 10.2328 - * } catch e1 { 10.2329 - * catchbody1 10.2330 - * //nothing inlined explicitly here, return, break other 10.2331 - * //terminals may inline the finally body 10.2332 - * ... 10.2333 - * } catch en { 10.2334 - * catchbody2 10.2335 - * //nothing inlined explicitly here, return, break other 10.2336 - * //terminals may inline the finally body 10.2337 - * } 10.2338 - * } catch all ex { 10.2339 - * finally_body_inlined 10.2340 - * rethrow ex 10.2341 - * } 10.2342 - * finally_body_inlined 10.2343 - * </pre> 10.2344 - * 10.2345 - * If tries are catches are terminal, visitors for return, break & 10.2346 - * continue will handle the tail duplications. Throw needs to be 10.2347 - * treated specially with the catchall as described in the above 10.2348 - * ASCII art. 10.2349 - * 10.2350 - * If the try isn't terminal we do the finally_body_inlined at the 10.2351 - * end. If the try is terminated with continue/break/return the 10.2352 - * existing visitor logic will inline the finally before that 10.2353 - * operation. if the try is terminated with a throw, the catches e1 10.2354 - * ... en will have a chance to process the exception. If the 10.2355 - * appropriate catch e1..en is non terminal we fall through to the 10.2356 - * last finally_body_inlined. if the catch e1...en IS terminal with 10.2357 - * continue/break/return existing visitor logic will fix it. If they 10.2358 - * are terminal with another throw it goes to the catchall and the 10.2359 - * finally_body_inlined marked (*) will fix it before rethrowing 10.2360 - * whatever problem there was for identical semantic. 10.2361 - */ 10.2362 - 10.2363 - // if try node does not contain a catch we can skip creation of a new 10.2364 - // try node and just append our synthetic catch to the existing try node. 10.2365 - if (!tryNode.getCatchBlocks().isEmpty()) { 10.2366 - // insert an intermediate try-catch* node, where we move the body and all catch blocks. 10.2367 - // the original try node become a try-finally container for the new try-catch* node. 10.2368 - // because we don't clone (to avoid deep copy), we have to fix the block chain in the end. 10.2369 - final TryNode innerTryNode = new TryNode(tryNodeSource, token, finish, tryNode.getNext()); 10.2370 - innerTryNode.setBody(tryNode.getBody()); 10.2371 - innerTryNode.setCatchBlocks(tryNode.getCatchBlocks()); 10.2372 - 10.2373 - // set outer tryNode's body to innerTryNode 10.2374 - final Block outerBody = new Block(tryNodeSource, token, finish, tryNode.getBody().getParent(), getCurrentFunctionNode()); 10.2375 - outerBody.setStatements(new ArrayList<Node>(Arrays.asList(innerTryNode))); 10.2376 - tryNode.setBody(outerBody); 10.2377 - tryNode.setCatchBlocks(null); 10.2378 - 10.2379 - // now before we go on, we have to fix the block parents 10.2380 - // (we repair the block tree after the insertion so that all references are intact) 10.2381 - innerTryNode.getBody().setParent(tryNode.getBody()); 10.2382 - for (final Block block : innerTryNode.getCatchBlocks()) { 10.2383 - block.setParent(tryNode.getBody()); 10.2384 - } 10.2385 - } 10.2386 - 10.2387 - // create a catch-all that inlines finally and rethrows 10.2388 - final String name = compiler.uniqueName(EXCEPTION_PREFIX.tag()); 10.2389 - 10.2390 - final IdentNode exception = new IdentNode(tryNodeSource, token, finish, name); 10.2391 - exception.accept(this); 10.2392 - 10.2393 - final Block catchBlock = new Block(tryNodeSource, token, finish, getCurrentBlock(), getCurrentFunctionNode()); 10.2394 - final Block catchBody = new Block(tryNodeSource, token, finish, catchBlock, getCurrentFunctionNode()); 10.2395 - final Node catchAllFinally = finallyBody.clone(); 10.2396 - 10.2397 - catchBody.addStatement(new ExecuteNode(tryNodeSource, finallyBody.getToken(), finallyBody.getFinish(), catchAllFinally)); 10.2398 - catchBody.setIsTerminal(true); 10.2399 - 10.2400 - final CatchNode catchNode = new CatchNode(tryNodeSource, token, finish, new IdentNode(exception), null, catchBody); 10.2401 - catchNode.setIsSyntheticRethrow(); 10.2402 - catchBlock.addStatement(catchNode); 10.2403 - 10.2404 - // replace all catches of outer tryNode with the catch-all 10.2405 - tryNode.setCatchBlocks(new ArrayList<>(Arrays.asList(catchBlock))); 10.2406 - 10.2407 - /* 10.2408 - * We leave the finally block for the original try in place for now 10.2409 - * so that children visitations will work. It is removed and placed 10.2410 - * afterwards in the else case below, after all children are visited 10.2411 - */ 10.2412 - 10.2413 - return tryNode; 10.2414 - } 10.2415 - 10.2416 - @Override 10.2417 - public Node leave(final TryNode tryNode) { 10.2418 - final Block finallyBody = tryNode.getFinallyBody(); 10.2419 - 10.2420 - boolean allTerminal = tryNode.getBody().isTerminal() && (finallyBody == null || finallyBody.isTerminal()); 10.2421 - 10.2422 - for (final Block catchBlock : tryNode.getCatchBlocks()) { 10.2423 - allTerminal &= catchBlock.isTerminal(); 10.2424 - } 10.2425 - 10.2426 - tryNode.setIsTerminal(allTerminal); 10.2427 - 10.2428 - final String name = compiler.uniqueName(EXCEPTION_PREFIX.tag()); 10.2429 - final Symbol exception = getCurrentFunctionNode().defineSymbol(name, IS_VAR | IS_INTERNAL, null); 10.2430 - 10.2431 - exception.setType(ECMAException.class); 10.2432 - tryNode.setException(exception); 10.2433 - 10.2434 - statements.add(tryNode); 10.2435 - 10.2436 - unnest(); 10.2437 - 10.2438 - // if finally body is present, place it after the tryNode 10.2439 - if (finallyBody != null) { 10.2440 - tryNode.setFinallyBody(null); 10.2441 - statements.add(finallyBody); 10.2442 - } 10.2443 - 10.2444 - return tryNode; 10.2445 - } 10.2446 - 10.2447 - @Override 10.2448 - public Node enter(final VarNode varNode) { 10.2449 - final IdentNode ident = varNode.getName(); 10.2450 - final String name = ident.getName(); 10.2451 - 10.2452 - final Symbol symbol = getCurrentBlock().defineSymbol(name, IS_VAR, ident); 10.2453 - assert symbol != null; 10.2454 - 10.2455 - varNode.setSymbol(symbol); 10.2456 - declaredSymbolsLocal.add(symbol); 10.2457 - 10.2458 - // NASHORN-467 - use before definition of vars - conservative 10.2459 - if (localUses.contains(ident.getName())) { 10.2460 - symbol.setType(Type.OBJECT); 10.2461 - symbol.setCanBeUndefined(); 10.2462 - } 10.2463 - 10.2464 - return varNode; 10.2465 - } 10.2466 - 10.2467 - private static boolean isScopeOrGlobal(final Symbol symbol) { 10.2468 - return symbol.getBlock().getFunction().isScript(); //we can't do explicit isScope checks as early as lower, which is why AccessSpecializer is afterwards as well. 10.2469 - } 10.2470 - 10.2471 - @Override 10.2472 - public Node leave(final VarNode varNode) { 10.2473 - final Node init = varNode.getInit(); 10.2474 - final IdentNode ident = varNode.getName(); 10.2475 - final String name = ident.getName(); 10.2476 - 10.2477 - if (init != null) { 10.2478 - localDefs.add(name); 10.2479 - } 10.2480 - 10.2481 - if (varNode.shouldAppend()) { 10.2482 - statements.add(varNode); 10.2483 - } 10.2484 - 10.2485 - if (init == null) { 10.2486 - // var x; with no init will be treated like a use of x by 10.2487 - // visit(IdentNode) unless we remove the name 10.2488 - // from the localdef list. 10.2489 - localDefs.remove(name); 10.2490 - return varNode; 10.2491 - } 10.2492 - 10.2493 - final Symbol symbol = varNode.getSymbol(); 10.2494 - if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScopeOrGlobal(symbol)) { //see NASHORN-56 10.2495 - // Forbid integers as local vars for now as we have no way to treat them as undefined 10.2496 - final Type type = Compiler.shouldUseIntegers() ? init.getType() : Type.NUMBER; 10.2497 - varNode.setInit(convert(init, type)); 10.2498 - symbol.setType(type); 10.2499 - } else { 10.2500 - symbol.setType(Type.OBJECT); // var x = obj;, x is an "object" //TODO too conservative now? 10.2501 - } 10.2502 - 10.2503 - return varNode; 10.2504 - } 10.2505 - 10.2506 - @Override 10.2507 - public Node enter(final WhileNode whileNode) { 10.2508 - // push the loop to the loop context 10.2509 - nest(whileNode); 10.2510 - 10.2511 - return whileNode; 10.2512 - } 10.2513 - 10.2514 - @Override 10.2515 - public Node leave(final WhileNode whileNode) { 10.2516 - final Node test = whileNode.getTest(); 10.2517 - 10.2518 - if (test != null) { 10.2519 - whileNode.setTest(convert(test, Type.BOOLEAN)); 10.2520 - } else { 10.2521 - whileNode.setHasGoto(); 10.2522 - } 10.2523 - 10.2524 - Node returnNode = whileNode; 10.2525 - 10.2526 - final Block body = whileNode.getBody(); 10.2527 - final boolean escapes = controlFlowEscapes(body); 10.2528 - if (escapes) { 10.2529 - body.setIsTerminal(false); 10.2530 - } 10.2531 - 10.2532 - if (body.isTerminal()) { 10.2533 - if (whileNode instanceof DoWhileNode) { 10.2534 - whileNode.setIsTerminal(true); 10.2535 - } else if (conservativeAlwaysTrue(test)) { 10.2536 - returnNode = new ForNode(source, whileNode.getToken(), whileNode.getFinish()); 10.2537 - ((ForNode)returnNode).setBody(body); 10.2538 - returnNode.setIsTerminal(!escapes); 10.2539 - } 10.2540 - } 10.2541 - 10.2542 - // pop the loop from the loop context 10.2543 - unnest(); 10.2544 - statements.add(returnNode); 10.2545 - 10.2546 - return returnNode; 10.2547 - } 10.2548 - 10.2549 - @Override 10.2550 - public Node leave(final WithNode withNode) { 10.2551 - withNode.setExpression(convert(withNode.getExpression(), Type.OBJECT)); 10.2552 - 10.2553 - if (withNode.getBody().isTerminal()) { 10.2554 - withNode.setIsTerminal(true); 10.2555 - } 10.2556 - 10.2557 - statements.add(withNode); 10.2558 - 10.2559 - return withNode; 10.2560 - } 10.2561 - 10.2562 - /* 10.2563 - * Unary visits. 10.2564 - */ 10.2565 - 10.2566 - /** 10.2567 - * Visit unary node and set symbols and inferred type 10.2568 - * 10.2569 - * @param unaryNode unary node to visit 10.2570 - * @param type The narrowest allowed type for this node 10.2571 - * 10.2572 - * @return the final node, or replacement thereof 10.2573 - */ 10.2574 - public Node leaveUnary(final UnaryNode unaryNode, final Type type) { 10.2575 - /* Try to eliminate the unary node if the expression is constant */ 10.2576 - final LiteralNode<?> literalNode = new UnaryNodeConstantEvaluator(unaryNode).eval(); 10.2577 - if (literalNode != null) { 10.2578 - return newLiteral(literalNode); 10.2579 - } 10.2580 - 10.2581 - /* Add explicit conversion */ 10.2582 - unaryNode.setRHS(convert(unaryNode.rhs(), type)); 10.2583 - getCurrentFunctionNode().newTemporary(type, unaryNode); 10.2584 - 10.2585 - return unaryNode; 10.2586 - } 10.2587 - 10.2588 - @Override 10.2589 - public Node leaveADD(final UnaryNode unaryNode) { 10.2590 - return leaveUnary(unaryNode, Type.NUMBER); 10.2591 - } 10.2592 - 10.2593 - @Override 10.2594 - public Node leaveBIT_NOT(final UnaryNode unaryNode) { 10.2595 - return leaveUnary(unaryNode, Type.INT); 10.2596 - } 10.2597 - 10.2598 - @Override 10.2599 - public Node leaveCONVERT(final UnaryNode unaryNode) { 10.2600 - final Node rhs = unaryNode.rhs(); 10.2601 - 10.2602 - if (rhs.isTokenType(TokenType.CONVERT)) { 10.2603 - rhs.setSymbol(unaryNode.getSymbol()); 10.2604 - return rhs; 10.2605 - } 10.2606 - 10.2607 - return unaryNode; 10.2608 - } 10.2609 - 10.2610 - /** 10.2611 - * In an assignment, recursively make sure that there are slots for 10.2612 - * everything that has to be laid out as temporary storage, which is the 10.2613 - * case if we are assign-op:ing a BaseNode subclass. This has to be 10.2614 - * recursive to handle things like multi dimensional arrays as lhs 10.2615 - * 10.2616 - * see NASHORN-258 10.2617 - * 10.2618 - * @param functionNode the current function node (has to be passed as it changes in the visitor below) 10.2619 - * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes 10.2620 - */ 10.2621 - private static void ensureAssignmentSlots(final FunctionNode functionNode, final Node assignmentDest) { 10.2622 - 10.2623 - assignmentDest.accept(new NodeVisitor() { 10.2624 - 10.2625 - @Override 10.2626 - public Node leave(final AccessNode accessNode) { 10.2627 - final Node base = accessNode.getBase(); 10.2628 - if (base.getSymbol().isThis()) { 10.2629 - functionNode.addThisProperty(accessNode.getProperty().getName(), accessNode); 10.2630 - } 10.2631 - 10.2632 - return accessNode; 10.2633 - } 10.2634 - 10.2635 - @Override 10.2636 - public Node leave(final IndexNode indexNode) { 10.2637 - final Node index = indexNode.getIndex(); 10.2638 - index.getSymbol().setNeedsSlot(!index.getSymbol().isConstant()); 10.2639 - 10.2640 - return indexNode; 10.2641 - } 10.2642 - 10.2643 - }); 10.2644 - } 10.2645 - 10.2646 - @Override 10.2647 - public Node leaveDECINC(final UnaryNode unaryNode) { 10.2648 - // @see assignOffset 10.2649 - ensureAssignmentSlots(getCurrentFunctionNode(), unaryNode.rhs()); 10.2650 - final Type type = Compiler.shouldUseIntegerArithmetic() ? Type.INT : Type.NUMBER; 10.2651 - 10.2652 - unaryNode.rhs().getSymbol().setType(type); 10.2653 - getCurrentFunctionNode().newTemporary(type, unaryNode); 10.2654 - 10.2655 - return unaryNode; 10.2656 - } 10.2657 - 10.2658 - /** 10.2659 - * Helper class for wrapping a result 10.2660 - */ 10.2661 - private abstract static class ResultNodeVisitor extends NodeVisitor { 10.2662 - 10.2663 - private Node resultNode; 10.2664 - 10.2665 - ResultNodeVisitor(final Node resultNode) { 10.2666 - this.resultNode = resultNode; 10.2667 - } 10.2668 - 10.2669 - Node getResultNode() { 10.2670 - return resultNode; 10.2671 - } 10.2672 - 10.2673 - void setResultNode(final Node resultNode) { 10.2674 - this.resultNode = resultNode; 10.2675 - } 10.2676 - } 10.2677 - 10.2678 - @Override 10.2679 - public Node leaveDELETE(final UnaryNode unaryNode) { 10.2680 - final boolean strictMode = getCurrentFunctionNode().isStrictMode(); 10.2681 - final Node rhs = unaryNode.rhs(); 10.2682 - final long token = unaryNode.getToken(); 10.2683 - final int finish = unaryNode.getFinish(); 10.2684 - final LiteralNode<Boolean> trueNode = LiteralNode.newInstance(source, token, finish, true); 10.2685 - final LiteralNode<Boolean> strictFlagNode = LiteralNode.newInstance(source, token, finish, strictMode); 10.2686 - 10.2687 - newLiteral(trueNode); 10.2688 - newLiteral(strictFlagNode); 10.2689 - 10.2690 - final NodeVisitor lower = this; 10.2691 - final FunctionNode currentFunctionNode = getCurrentFunctionNode(); 10.2692 - 10.2693 - final ResultNodeVisitor visitor = new ResultNodeVisitor(trueNode) { 10.2694 - 10.2695 - private void initRuntimeNode(final RuntimeNode node) { 10.2696 - node.accept(lower); 10.2697 - currentFunctionNode.newTemporary(Type.BOOLEAN, unaryNode); 10.2698 - node.setSymbol(unaryNode.getSymbol()); 10.2699 - 10.2700 - setResultNode(node); 10.2701 - } 10.2702 - 10.2703 - @Override 10.2704 - public Node enter(final IdentNode node) { 10.2705 - // If this is a declared variable or a function parameter, delete always fails (except for globals). 10.2706 - final boolean failDelete = 10.2707 - strictMode || 10.2708 - node.getSymbol().isParam() || 10.2709 - (node.getSymbol().isVar() && !node.getSymbol().isTopLevel()); 10.2710 - 10.2711 - final Node literalNode = 10.2712 - newLiteral( 10.2713 - currentFunctionNode, 10.2714 - LiteralNode.newInstance( 10.2715 - source, 10.2716 - node.getToken(), 10.2717 - node.getFinish(), 10.2718 - node.getName())); 10.2719 - 10.2720 - if (failDelete) { 10.2721 - if (THIS.tag().equals(node.getName())) { 10.2722 - statements.add(new ExecuteNode(source, token, finish, discard(node))); 10.2723 - currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode); 10.2724 - return null; //trueNode it is 10.2725 - } 10.2726 - } 10.2727 - 10.2728 - final List<Node> args = new ArrayList<>(); 10.2729 - args.add(convert(currentFunctionNode.getScopeNode(), Type.OBJECT)); 10.2730 - args.add(convert(literalNode, Type.OBJECT)); 10.2731 - args.add(strictFlagNode); 10.2732 - 10.2733 - initRuntimeNode( 10.2734 - new RuntimeNode( 10.2735 - source, 10.2736 - token, 10.2737 - finish, 10.2738 - failDelete ? FAIL_DELETE : DELETE, 10.2739 - args)); 10.2740 - 10.2741 - return null; 10.2742 - } 10.2743 - 10.2744 - @Override 10.2745 - public Node enter(final AccessNode node) { 10.2746 - final IdentNode property = node.getProperty(); 10.2747 - 10.2748 - initRuntimeNode( 10.2749 - new RuntimeNode( 10.2750 - source, 10.2751 - token, 10.2752 - finish, 10.2753 - DELETE, 10.2754 - convert(node.getBase(), Type.OBJECT), 10.2755 - convert( 10.2756 - newLiteral( 10.2757 - currentFunctionNode, 10.2758 - LiteralNode.newInstance( 10.2759 - source, 10.2760 - property.getToken(), 10.2761 - property.getFinish(), 10.2762 - property.getName())), 10.2763 - Type.OBJECT), 10.2764 - strictFlagNode)); 10.2765 - 10.2766 - return null; 10.2767 - } 10.2768 - 10.2769 - @Override 10.2770 - public Node enter(final IndexNode node) { 10.2771 - initRuntimeNode( 10.2772 - new RuntimeNode( 10.2773 - source, 10.2774 - token, 10.2775 - finish, 10.2776 - DELETE, 10.2777 - convert(node.getBase(), Type.OBJECT), 10.2778 - convert(node.getIndex(), Type.OBJECT), 10.2779 - strictFlagNode)); 10.2780 - 10.2781 - return null; 10.2782 - } 10.2783 - 10.2784 - @Override 10.2785 - public Node enterDefault(final Node node) { 10.2786 - statements.add(new ExecuteNode(source, token, finish, discard(node))); 10.2787 - currentFunctionNode.newTemporary(Type.BOOLEAN, trueNode); 10.2788 - //just return trueNode which is the default 10.2789 - return null; 10.2790 - } 10.2791 - }; 10.2792 - 10.2793 - rhs.accept(visitor); 10.2794 - 10.2795 - return visitor.getResultNode(); 10.2796 - } 10.2797 - 10.2798 - @Override 10.2799 - public Node enterNEW(final UnaryNode unaryNode) { 10.2800 - final CallNode callNode = (CallNode)unaryNode.rhs(); 10.2801 - 10.2802 - callNode.setIsNew(); 10.2803 - 10.2804 - final Node function = callNode.getFunction(); 10.2805 - final Node markedFunction = markerFunction(function); 10.2806 - 10.2807 - callNode.setFunction(markedFunction.accept(this)); 10.2808 - 10.2809 - acceptArgs(callNode); 10.2810 - 10.2811 - getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode); 10.2812 - 10.2813 - return null; 10.2814 - } 10.2815 - 10.2816 - @Override 10.2817 - public Node leaveNOT(final UnaryNode unaryNode) { 10.2818 - return leaveUnary(unaryNode, Type.BOOLEAN); 10.2819 - } 10.2820 - 10.2821 - /** 10.2822 - * Create a null literal 10.2823 - * 10.2824 - * @param parent node to inherit token from. 10.2825 - * @return the new null literal 10.2826 - */ 10.2827 - private LiteralNode<?> newNullLiteral(final Node parent) { 10.2828 - return newLiteral(LiteralNode.newInstance(parent.getSource(), parent.getToken(), parent.getFinish())); 10.2829 - } 10.2830 - 10.2831 - @Override 10.2832 - public Node leaveTYPEOF(final UnaryNode unaryNode) { 10.2833 - final Node rhs = unaryNode.rhs(); 10.2834 - final long token = unaryNode.getToken(); 10.2835 - final int finish = unaryNode.getFinish(); 10.2836 - 10.2837 - RuntimeNode runtimeNode; 10.2838 - 10.2839 - if (rhs instanceof IdentNode) { 10.2840 - final IdentNode ident = (IdentNode)rhs; 10.2841 - 10.2842 - if (ident.getSymbol().isParam() || ident.getSymbol().isVar()) { 10.2843 - runtimeNode = new RuntimeNode( 10.2844 - source, 10.2845 - token, 10.2846 - finish, 10.2847 - TYPEOF, 10.2848 - convert( 10.2849 - rhs, 10.2850 - Type.OBJECT), 10.2851 - newNullLiteral(unaryNode)); 10.2852 - } else { 10.2853 - runtimeNode = new RuntimeNode( 10.2854 - source, 10.2855 - token, 10.2856 - finish, 10.2857 - TYPEOF, 10.2858 - convert( 10.2859 - getCurrentFunctionNode().getScopeNode(), 10.2860 - Type.OBJECT), 10.2861 - convert( 10.2862 - newLiteral( 10.2863 - LiteralNode.newInstance( 10.2864 - source, 10.2865 - ident.getToken(), 10.2866 - ident.getFinish(), 10.2867 - ident.getName())), 10.2868 - Type.OBJECT)); 10.2869 - } 10.2870 - } else { 10.2871 - runtimeNode = new RuntimeNode( 10.2872 - source, 10.2873 - token, 10.2874 - finish, 10.2875 - TYPEOF, 10.2876 - convert( 10.2877 - rhs, 10.2878 - Type.OBJECT), 10.2879 - newNullLiteral(unaryNode)); 10.2880 - } 10.2881 - 10.2882 - runtimeNode.accept(this); 10.2883 - 10.2884 - getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode); 10.2885 - runtimeNode.setSymbol(unaryNode.getSymbol()); 10.2886 - 10.2887 - return runtimeNode; 10.2888 - } 10.2889 - 10.2890 - @Override 10.2891 - public Node leaveSUB(final UnaryNode unaryNode) { 10.2892 - return leaveUnary(unaryNode, Type.NUMBER); 10.2893 - } 10.2894 - 10.2895 - @Override 10.2896 - public Node leaveVOID(final UnaryNode unaryNode) { 10.2897 - final Node rhs = unaryNode.rhs(); 10.2898 - getCurrentFunctionNode().newTemporary(Type.OBJECT, unaryNode); 10.2899 - 10.2900 - // Set up request. 10.2901 - final RuntimeNode runtimeNode = new RuntimeNode(source, unaryNode.getToken(), unaryNode.getFinish(), VOID, convert(rhs, Type.OBJECT)); 10.2902 - 10.2903 - // Use same symbol as unary node. 10.2904 - runtimeNode.setSymbol(unaryNode.getSymbol()); 10.2905 - 10.2906 - return runtimeNode; 10.2907 - } 10.2908 - 10.2909 - /** 10.2910 - * Compute the narrowest possible type for a binaryNode, based on its 10.2911 - * contents. 10.2912 - * 10.2913 - * @param binaryNode the binary node 10.2914 - * @return the narrowest possible type 10.2915 - */ 10.2916 - private static Type binaryType(final BinaryNode binaryNode) { 10.2917 - final Node lhs = binaryNode.lhs(); 10.2918 - assert lhs.hasType(); 10.2919 - 10.2920 - final Node rhs = binaryNode.rhs(); 10.2921 - assert rhs.hasType(); 10.2922 - 10.2923 - // actually bitwise assignments are ok for ints. TODO 10.2924 - switch (binaryNode.tokenType()) { 10.2925 - case ASSIGN_BIT_AND: 10.2926 - case ASSIGN_BIT_OR: 10.2927 - case ASSIGN_BIT_XOR: 10.2928 - case ASSIGN_SHL: 10.2929 - case ASSIGN_SAR: 10.2930 - return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.INT) : Type.INT; 10.2931 - case ASSIGN_SHR: 10.2932 - return Type.LONG; 10.2933 - case ASSIGN: 10.2934 - return Compiler.shouldUseIntegers() ? Type.widest(lhs.getType(), rhs.getType(), Type.NUMBER) : Type.NUMBER; 10.2935 - default: 10.2936 - return binaryArithType(lhs.getType(), rhs.getType()); 10.2937 - } 10.2938 - } 10.2939 - 10.2940 - private static Type binaryArithType(final Type lhsType, final Type rhsType) { 10.2941 - if (!Compiler.shouldUseIntegerArithmetic()) { 10.2942 - return Type.NUMBER; 10.2943 - } 10.2944 - return Type.widest(lhsType, rhsType, Type.NUMBER); 10.2945 - } 10.2946 - 10.2947 - /** 10.2948 - * Visit binary node and set symbols and inferred type 10.2949 - * 10.2950 - * @param binaryNode unary node to visit 10.2951 - * @param type The narrowest allowed type for this node 10.2952 - * 10.2953 - * @return the final node, or replacement thereof 10.2954 - */ 10.2955 - private Node leaveBinary(final BinaryNode binaryNode, final Type type) { 10.2956 - return leaveBinary(binaryNode, type, type, type); 10.2957 - } 10.2958 - 10.2959 - /** 10.2960 - * Visit a binary node, specifying types for rhs, rhs and destType. 10.2961 - * 10.2962 - * @param binaryNode the binary node 10.2963 - * @param destType destination type 10.2964 - * @param lhsType type for left hand side 10.2965 - * @param rhsType type for right hand side 10.2966 - * 10.2967 - * @return resulting binary node 10.2968 - */ 10.2969 - private Node leaveBinary(final BinaryNode binaryNode, final Type destType, final Type lhsType, final Type rhsType) { 10.2970 - /* Attempt to turn this binary expression into a constant */ 10.2971 - final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); 10.2972 - if (literalNode != null) { 10.2973 - return newLiteral(literalNode); 10.2974 - } 10.2975 - 10.2976 - if (lhsType != null) { 10.2977 - binaryNode.setLHS(convert(binaryNode.lhs(), lhsType)); 10.2978 - } 10.2979 - 10.2980 - if (rhsType != null) { 10.2981 - binaryNode.setRHS(convert(binaryNode.rhs(), rhsType)); 10.2982 - } 10.2983 - 10.2984 - getCurrentFunctionNode().newTemporary(destType, binaryNode); 10.2985 - 10.2986 - return binaryNode; 10.2987 - } 10.2988 - 10.2989 - /** 10.2990 - * Determine if the outcome of + operator is a string. 10.2991 - * 10.2992 - * @param node 10.2993 - * Node to test. 10.2994 - * @return true if a string result. 10.2995 - */ 10.2996 - private boolean isAddString(final Node node) { 10.2997 - if (node instanceof BinaryNode && node.isTokenType(TokenType.ADD)) { 10.2998 - final BinaryNode binaryNode = (BinaryNode)node; 10.2999 - final Node lhs = binaryNode.lhs(); 10.3000 - final Node rhs = binaryNode.rhs(); 10.3001 - 10.3002 - return isAddString(lhs) || isAddString(rhs); 10.3003 - } 10.3004 - 10.3005 - return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).getObject() instanceof String; 10.3006 - } 10.3007 - 10.3008 - /** 10.3009 - * Helper for creating a new runtime node from a parent node, inheriting its 10.3010 - * types and tokens 10.3011 - * 10.3012 - * @param parent Parent node. 10.3013 - * @param args Runtime request arguments. 10.3014 - * @param request Runtime request type. 10.3015 - * @return New {@link RuntimeNode}. 10.3016 - */ 10.3017 - 10.3018 - private RuntimeNode newRuntime(final Node parent, final List<Node> args, final Request request) { 10.3019 - final RuntimeNode runtimeNode = new RuntimeNode(source, parent.getToken(), parent.getFinish(), request, args); 10.3020 - runtimeNode.setStart(parent.getStart()); 10.3021 - runtimeNode.setFinish(parent.getFinish()); 10.3022 - 10.3023 - // Use same symbol as parent node. 10.3024 - runtimeNode.accept(this); 10.3025 - runtimeNode.setSymbol(parent.getSymbol()); 10.3026 - 10.3027 - return runtimeNode; 10.3028 - } 10.3029 - 10.3030 - /** 10.3031 - * Helper for creating a new runtime node from a binary node 10.3032 - * 10.3033 - * @param binaryNode {@link RuntimeNode} expression. 10.3034 - * @param request Runtime request type. 10.3035 - * @return New {@link RuntimeNode}. 10.3036 - */ 10.3037 - private RuntimeNode newRuntime(final BinaryNode binaryNode, final Request request) { 10.3038 - return newRuntime(binaryNode, Arrays.asList(new Node[] { binaryNode.lhs(), binaryNode.rhs() }), request); 10.3039 - } 10.3040 - 10.3041 - /** 10.3042 - * Add is a special binary, as it works not only on arithmetic, but for 10.3043 - * strings etc as well. 10.3044 - */ 10.3045 - @Override 10.3046 - public Node leaveADD(final BinaryNode binaryNode) { 10.3047 - final Node lhs = binaryNode.lhs(); 10.3048 - final Node rhs = binaryNode.rhs(); 10.3049 - 10.3050 - //parameters must be blown up to objects 10.3051 - ensureTypeNotUnknown(lhs); 10.3052 - ensureTypeNotUnknown(rhs); 10.3053 - 10.3054 - if ((lhs.getType().isNumeric() || lhs.getType().isBoolean()) && (rhs.getType().isNumeric() || rhs.getType().isBoolean())) { 10.3055 - return leaveBinary(binaryNode, binaryType(binaryNode)); 10.3056 - } else if (isAddString(binaryNode)) { 10.3057 - binaryNode.setLHS(convert(lhs, Type.OBJECT)); 10.3058 - binaryNode.setRHS(convert(rhs, Type.OBJECT)); 10.3059 - getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode); 10.3060 - } else { 10.3061 - getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode); 10.3062 - return newRuntime(binaryNode, ADD); 10.3063 - } 10.3064 - 10.3065 - return binaryNode; 10.3066 - } 10.3067 - 10.3068 - @Override 10.3069 - public Node leaveAND(final BinaryNode binaryNode) { 10.3070 - return leaveBinary(binaryNode, Type.OBJECT, null, null); 10.3071 - } 10.3072 - 10.3073 - /** 10.3074 - * This is a helper called before an assignment. 10.3075 - * @param binaryNode assignment node 10.3076 - */ 10.3077 - private Node enterAssign(final BinaryNode binaryNode) { 10.3078 - final Node lhs = binaryNode.lhs(); 10.3079 - 10.3080 - if (!(lhs instanceof IdentNode)) { 10.3081 - return binaryNode; 10.3082 - } 10.3083 - 10.3084 - final Block block = getCurrentBlock(); 10.3085 - final IdentNode ident = (IdentNode)lhs; 10.3086 - final String name = ident.getName(); 10.3087 - 10.3088 - Symbol symbol = getCurrentBlock().findSymbol(name); 10.3089 - 10.3090 - if (symbol == null) { 10.3091 - symbol = block.defineSymbol(name, IS_GLOBAL, ident); 10.3092 - binaryNode.setSymbol(symbol); 10.3093 - } else if (!getCurrentFunctionNode().isLocal(symbol)) { 10.3094 - symbol.setIsScope(); 10.3095 - } 10.3096 - 10.3097 - localDefs.add(name); 10.3098 - 10.3099 - return binaryNode; 10.3100 - } 10.3101 - 10.3102 - /** 10.3103 - * This assign helper is called after an assignment, when all children of 10.3104 - * the assign has been processed. It fixes the types and recursively makes 10.3105 - * sure that everyhing has slots that should have them in the chain. 10.3106 - * 10.3107 - * @param binaryNode assignment node 10.3108 - * @param destType destination type of assignment 10.3109 - */ 10.3110 - private Node leaveAssign(final BinaryNode binaryNode, final Type destType) { 10.3111 - binaryNode.lhs().getSymbol().setType(destType); // lhs inherits dest type 10.3112 - getCurrentFunctionNode().newTemporary(destType, binaryNode); // as does destination 10.3113 - 10.3114 - ensureAssignmentSlots(getCurrentFunctionNode(), binaryNode.lhs()); 10.3115 - return binaryNode; 10.3116 - } 10.3117 - 10.3118 - @Override 10.3119 - public Node enterASSIGN(final BinaryNode binaryNode) { 10.3120 - return enterAssign(binaryNode); 10.3121 - } 10.3122 - 10.3123 - @Override 10.3124 - public Node leaveASSIGN(final BinaryNode binaryNode) { 10.3125 - final Node lhs = binaryNode.lhs(); 10.3126 - final Node rhs = binaryNode.rhs(); 10.3127 - 10.3128 - if (rhs.getType().isNumeric()) { 10.3129 - final Symbol lhsSymbol = lhs.getSymbol(); 10.3130 - final Type lhsType = Type.widest(lhs.getType(), binaryType(binaryNode)); 10.3131 - 10.3132 - // for index nodes, we can set anything 10.3133 - lhsSymbol.setType(lhsType); 10.3134 - getCurrentFunctionNode().newTemporary(lhs.getType(), binaryNode); 10.3135 - if (!(lhs instanceof IndexNode)) { 10.3136 - binaryNode.setRHS(convert(rhs, lhsType)); 10.3137 - } 10.3138 - } else { 10.3139 - // Force symbol to be object if not numeric assignment. 10.3140 - binaryNode.setRHS(convert(rhs, Type.OBJECT)); 10.3141 - getCurrentFunctionNode().newTemporary(Type.OBJECT, binaryNode); 10.3142 - 10.3143 - if (lhs instanceof IdentNode) { 10.3144 - lhs.getSymbol().setType(Type.OBJECT); 10.3145 - } 10.3146 - } 10.3147 - 10.3148 - if (lhs instanceof AccessNode) { 10.3149 - final Node baseNode = ((AccessNode)lhs).getBase(); 10.3150 - 10.3151 - if (baseNode.getSymbol().isThis()) { 10.3152 - final IdentNode property = ((AccessNode)lhs).getProperty(); 10.3153 - getCurrentFunctionNode().addThisProperty(property.getName(), property); 10.3154 - } 10.3155 - } 10.3156 - 10.3157 - return binaryNode; 10.3158 - } 10.3159 - 10.3160 - @Override 10.3161 - public Node enterASSIGN_ADD(final BinaryNode binaryNode) { 10.3162 - return enterAssign(binaryNode); 10.3163 - } 10.3164 - 10.3165 - @Override 10.3166 - public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { 10.3167 - final Node lhs = binaryNode.lhs(); 10.3168 - final Node rhs = binaryNode.rhs(); 10.3169 - final boolean bothNumeric = lhs.getType().isNumeric() && rhs.getType().isNumeric(); 10.3170 - 10.3171 - /* 10.3172 - * In the case of bothNumeric, 10.3173 - * compute type for lhs += rhs. Assign type to lhs. Assign type to 10.3174 - * temporary for this node. Convert rhs from whatever it was to this 10.3175 - * type. Legacy wise dest has always been narrowed. It should 10.3176 - * actually only be the lhs that is the double, or other narrower 10.3177 - * type than OBJECT 10.3178 - */ 10.3179 - return leaveAssign(binaryNode, bothNumeric ? binaryType(binaryNode) : Type.OBJECT); 10.3180 - } 10.3181 - 10.3182 - @Override 10.3183 - public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) { 10.3184 - return enterAssign(binaryNode); 10.3185 - } 10.3186 - 10.3187 - @Override 10.3188 - public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { 10.3189 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3190 - } 10.3191 - 10.3192 - @Override 10.3193 - public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) { 10.3194 - return enterAssign(binaryNode); 10.3195 - } 10.3196 - 10.3197 - @Override 10.3198 - public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { 10.3199 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3200 - } 10.3201 - 10.3202 - @Override 10.3203 - public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) { 10.3204 - return enterAssign(binaryNode); 10.3205 - } 10.3206 - 10.3207 - @Override 10.3208 - public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { 10.3209 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3210 - } 10.3211 - 10.3212 - @Override 10.3213 - public Node enterASSIGN_DIV(final BinaryNode binaryNode) { 10.3214 - return enterAssign(binaryNode); 10.3215 - } 10.3216 - 10.3217 - @Override 10.3218 - public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { 10.3219 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3220 - } 10.3221 - 10.3222 - @Override 10.3223 - public Node enterASSIGN_MOD(final BinaryNode binaryNode) { 10.3224 - return enterAssign(binaryNode); 10.3225 - } 10.3226 - 10.3227 - @Override 10.3228 - public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { 10.3229 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3230 - } 10.3231 - 10.3232 - @Override 10.3233 - public Node enterASSIGN_MUL(final BinaryNode binaryNode) { 10.3234 - return enterAssign(binaryNode); 10.3235 - } 10.3236 - 10.3237 - @Override 10.3238 - public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { 10.3239 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3240 - } 10.3241 - 10.3242 - @Override 10.3243 - public Node enterASSIGN_SAR(final BinaryNode binaryNode) { 10.3244 - return enterAssign(binaryNode); 10.3245 - } 10.3246 - 10.3247 - @Override 10.3248 - public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { 10.3249 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3250 - } 10.3251 - 10.3252 - @Override 10.3253 - public Node enterASSIGN_SHL(final BinaryNode binaryNode) { 10.3254 - return enterAssign(binaryNode); 10.3255 - } 10.3256 - 10.3257 - @Override 10.3258 - public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { 10.3259 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3260 - } 10.3261 - 10.3262 - @Override 10.3263 - public Node enterASSIGN_SHR(final BinaryNode binaryNode) { 10.3264 - return enterAssign(binaryNode); 10.3265 - } 10.3266 - 10.3267 - @Override 10.3268 - public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { 10.3269 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3270 - } 10.3271 - 10.3272 - @Override 10.3273 - public Node enterASSIGN_SUB(final BinaryNode binaryNode) { 10.3274 - return enterAssign(binaryNode); 10.3275 - } 10.3276 - 10.3277 - @Override 10.3278 - public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { 10.3279 - return leaveAssign(binaryNode, binaryType(binaryNode)); 10.3280 - } 10.3281 - 10.3282 - @Override 10.3283 - public Node leaveBIT_AND(final BinaryNode binaryNode) { 10.3284 - return leaveBinary(binaryNode, Type.INT); 10.3285 - } 10.3286 - 10.3287 - @Override 10.3288 - public Node leaveBIT_OR(final BinaryNode binaryNode) { 10.3289 - return leaveBinary(binaryNode, Type.INT); 10.3290 - } 10.3291 - 10.3292 - @Override 10.3293 - public Node leaveBIT_XOR(final BinaryNode binaryNode) { 10.3294 - return leaveBinary(binaryNode, Type.INT); 10.3295 - } 10.3296 - 10.3297 - @Override 10.3298 - public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { 10.3299 - binaryNode.setLHS(discard(binaryNode.lhs())); 10.3300 - getCurrentFunctionNode().newTemporary(binaryNode.rhs().getType(), binaryNode); 10.3301 - 10.3302 - return binaryNode; 10.3303 - } 10.3304 - 10.3305 - @Override 10.3306 - public Node leaveCOMMALEFT(final BinaryNode binaryNode) { 10.3307 - binaryNode.setRHS(discard(binaryNode.rhs())); 10.3308 - getCurrentFunctionNode().newTemporary(binaryNode.lhs().getType(), binaryNode); 10.3309 - 10.3310 - return binaryNode; 10.3311 - } 10.3312 - 10.3313 - @Override 10.3314 - public Node leaveDIV(final BinaryNode binaryNode) { 10.3315 - return leaveBinary(binaryNode, binaryType(binaryNode)); 10.3316 - } 10.3317 - 10.3318 - @Override 10.3319 - public Node leaveEQ(final BinaryNode binaryNode) { 10.3320 - return leaveCmp(binaryNode, EQ); 10.3321 - } 10.3322 - 10.3323 - @Override 10.3324 - public Node leaveEQ_STRICT(final BinaryNode binaryNode) { 10.3325 - return leaveCmp(binaryNode, EQ_STRICT); 10.3326 - } 10.3327 - 10.3328 - private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { 10.3329 - final Node lhs = binaryNode.lhs(); 10.3330 - final Node rhs = binaryNode.rhs(); 10.3331 - 10.3332 - /* Attempt to turn this comparison into a constant and collapse it */ 10.3333 - final LiteralNode<?> literalNode = new BinaryNodeConstantEvaluator(binaryNode).eval(); 10.3334 - if (literalNode != null) { 10.3335 - return newLiteral(literalNode); 10.3336 - } 10.3337 - 10.3338 - // another case where dest != source operand types always a boolean 10.3339 - getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode); 10.3340 - 10.3341 - ensureTypeNotUnknown(lhs); 10.3342 - ensureTypeNotUnknown(rhs); 10.3343 - final Type type = Type.widest(lhs.getType(), rhs.getType()); 10.3344 - 10.3345 - if (type.isObject()) { 10.3346 - return newRuntime(binaryNode, request); 10.3347 - } 10.3348 - 10.3349 - if ((request.equals(EQ_STRICT) || request.equals(NE_STRICT)) && lhs.getType().isBoolean() != rhs.getType().isBoolean()) { 10.3350 - // special case: number compared against boolean => never equal. must not convert! 10.3351 - final boolean result = request.equals(NE_STRICT); 10.3352 - final LiteralNode<Boolean> resultNode = LiteralNode.newInstance(source, 0, 0, result); 10.3353 - final boolean canSkipLHS = (lhs instanceof LiteralNode); 10.3354 - final boolean canSkipRHS = (rhs instanceof LiteralNode); 10.3355 - 10.3356 - if (canSkipLHS && canSkipRHS) { 10.3357 - return resultNode.accept(this); 10.3358 - } 10.3359 - 10.3360 - final Node argNode; 10.3361 - 10.3362 - if (!canSkipLHS && !canSkipRHS) { 10.3363 - argNode = new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), lhs, rhs); 10.3364 - } else { 10.3365 - argNode = !canSkipLHS ? lhs : rhs; 10.3366 - } 10.3367 - 10.3368 - return new BinaryNode(source, Token.recast(binaryNode.getToken(), TokenType.COMMARIGHT), argNode, resultNode).accept(this); 10.3369 - } 10.3370 - 10.3371 - binaryNode.setLHS(convert(lhs, type)); 10.3372 - binaryNode.setRHS(convert(rhs, type)); 10.3373 - 10.3374 - return binaryNode; 10.3375 - } 10.3376 - 10.3377 - @Override 10.3378 - public Node leaveGE(final BinaryNode binaryNode) { 10.3379 - return leaveCmp(binaryNode, GE); 10.3380 - } 10.3381 - 10.3382 - @Override 10.3383 - public Node leaveGT(final BinaryNode binaryNode) { 10.3384 - return leaveCmp(binaryNode, GT); 10.3385 - } 10.3386 - 10.3387 - private Node exitIN_INSTANCEOF(final BinaryNode binaryNode, final Request request) { 10.3388 - getCurrentFunctionNode().newTemporary(request.getReturnType(), binaryNode); 10.3389 - return newRuntime(binaryNode, request); 10.3390 - } 10.3391 - 10.3392 - @Override 10.3393 - public Node leaveIN(final BinaryNode binaryNode) { 10.3394 - return exitIN_INSTANCEOF(binaryNode, IN); 10.3395 - } 10.3396 - 10.3397 - @Override 10.3398 - public Node leaveINSTANCEOF(final BinaryNode binaryNode) { 10.3399 - return exitIN_INSTANCEOF(binaryNode, INSTANCEOF); 10.3400 - } 10.3401 - 10.3402 - @Override 10.3403 - public Node leaveLE(final BinaryNode binaryNode) { 10.3404 - return leaveCmp(binaryNode, LE); 10.3405 - } 10.3406 - 10.3407 - @Override 10.3408 - public Node leaveLT(final BinaryNode binaryNode) { 10.3409 - return leaveCmp(binaryNode, LT); 10.3410 - } 10.3411 - 10.3412 - @Override 10.3413 - public Node leaveMOD(final BinaryNode binaryNode) { 10.3414 - return leaveBinary(binaryNode, binaryType(binaryNode)); 10.3415 - } 10.3416 - 10.3417 - @Override 10.3418 - public Node leaveMUL(final BinaryNode binaryNode) { 10.3419 - return leaveBinary(binaryNode, binaryType(binaryNode)); 10.3420 - } 10.3421 - 10.3422 - @Override 10.3423 - public Node leaveNE(final BinaryNode binaryNode) { 10.3424 - return leaveCmp(binaryNode, NE); 10.3425 - } 10.3426 - 10.3427 - @Override 10.3428 - public Node leaveNE_STRICT(final BinaryNode binaryNode) { 10.3429 - return leaveCmp(binaryNode, NE_STRICT); 10.3430 - } 10.3431 - 10.3432 - @Override 10.3433 - public Node leaveOR(final BinaryNode binaryNode) { 10.3434 - return leaveBinary(binaryNode, Type.OBJECT, null, null); 10.3435 - } 10.3436 - 10.3437 - @Override 10.3438 - public Node leaveSAR(final BinaryNode binaryNode) { 10.3439 - return leaveBinary(binaryNode, Type.INT); 10.3440 - } 10.3441 - 10.3442 - @Override 10.3443 - public Node leaveSHL(final BinaryNode binaryNode) { 10.3444 - return leaveBinary(binaryNode, Type.INT); 10.3445 - } 10.3446 - 10.3447 - @Override 10.3448 - public Node leaveSHR(final BinaryNode binaryNode) { 10.3449 - return leaveBinary(binaryNode, Type.LONG, Type.INT, Type.INT); 10.3450 - } 10.3451 - 10.3452 - @Override 10.3453 - public Node leaveSUB(final BinaryNode binaryNode) { 10.3454 - return leaveBinary(binaryNode, binaryType(binaryNode)); 10.3455 - } 10.3456 - 10.3457 - @Override 10.3458 - public Node leave(final TernaryNode ternaryNode) { 10.3459 - final Node test = ternaryNode.lhs(); 10.3460 - final Node lhs = ternaryNode.rhs(); 10.3461 - final Node rhs = ternaryNode.third(); 10.3462 - 10.3463 - ensureTypeNotUnknown(lhs); 10.3464 - ensureTypeNotUnknown(rhs); 10.3465 - final Type type = Type.widest(lhs.getType(), rhs.getType()); 10.3466 - 10.3467 - ternaryNode.setRHS(convert(lhs, type)); 10.3468 - ternaryNode.setThird(convert(rhs, type)); 10.3469 - 10.3470 - // optimize away the ternary if the test is constant. 10.3471 - if (test.getSymbol().isConstant()) { 10.3472 - return ((LiteralNode<?>)test).isTrue() ? lhs : rhs; 10.3473 - } 10.3474 - 10.3475 - ternaryNode.setLHS(convert(test, Type.BOOLEAN)); 10.3476 - 10.3477 - getCurrentFunctionNode().newTemporary(type, ternaryNode); 10.3478 - 10.3479 - return ternaryNode; 10.3480 - } 10.3481 - 10.3482 - private static void ensureTypeNotUnknown(final Node node) { 10.3483 - final Symbol symbol = node.getSymbol(); 10.3484 - 10.3485 - /* 10.3486 - * Note that not just unknowns, but params need to be blown 10.3487 - * up to objects, because we can have something like 10.3488 - * 10.3489 - * function f(a) { 10.3490 - * var b = ~a; //b and a are inferred to be int 10.3491 - * return b; 10.3492 - * } 10.3493 - * 10.3494 - * In this case, it would be correct to say that "if you have 10.3495 - * an int at the callsite, just pass it". 10.3496 - * 10.3497 - * However 10.3498 - * 10.3499 - * function f(a) { 10.3500 - * var b = ~a; //b and a are inferred to be int 10.3501 - * return b == 17; //b is still inferred to be int. 10.3502 - * } 10.3503 - * 10.3504 - * can be called with f("17") and if we assume that b is an 10.3505 - * int and don't blow it up to an object in the comparison, we 10.3506 - * are screwed. I hate JavaScript. 10.3507 - * 10.3508 - * This check has to be done for any operation that might take 10.3509 - * objects as parameters, for example +, but not *, which is known 10.3510 - * to coerce types into doubles 10.3511 - */ 10.3512 - if (node.getType().isUnknown() || symbol.isParam()) { 10.3513 - symbol.setType(Type.OBJECT); 10.3514 - symbol.setCanBeUndefined(); 10.3515 - } 10.3516 - } 10.3517 - 10.3518 - /** 10.3519 - * A simple node visitor that ensure that scope and slot information is correct. 10.3520 - * This is run as a post pass after we know all scope changing information about 10.3521 - * the Lowering. This is also called after potential mutations like splitting 10.3522 - * have taken place, as splitting forces scope. 10.3523 - * 10.3524 - * This was previously done on a per functionNode basis in {@link CodeGenerator}, 10.3525 - * but this is too late for type information to be used in {@link AccessSpecializer} 10.3526 - */ 10.3527 - static class FinalizeSymbols extends NodeVisitor { 10.3528 - @Override 10.3529 - public Node leave(final Block block) { 10.3530 - return updateSymbols(block); 10.3531 - } 10.3532 - 10.3533 - @Override 10.3534 - public Node leave(final FunctionNode function) { 10.3535 - return updateSymbols(function); 10.3536 - } 10.3537 - 10.3538 - private static void updateSymbolsLog(final FunctionNode functionNode, final Symbol symbol, final boolean loseSlot) { 10.3539 - if (!symbol.isScope()) { 10.3540 - LOG.finest("updateSymbols: " + symbol + " => scope, because all vars in " + functionNode.getName() + " are in scope"); 10.3541 - } 10.3542 - if (loseSlot && symbol.hasSlot()) { 10.3543 - LOG.finest("updateSymbols: " + symbol + " => no slot, because all vars in " + functionNode.getName() + " are in scope"); 10.3544 - } 10.3545 - } 10.3546 - 10.3547 - // called after a block or function node (subclass of block) is finished 10.3548 - // to correct scope and slot assignment for variables 10.3549 - private static Block updateSymbols(final Block block) { 10.3550 - 10.3551 - if (!block.needsScope()) { 10.3552 - return block; // nothing to do 10.3553 - } 10.3554 - 10.3555 - assert !(block instanceof FunctionNode) || block.getFunction() == block; 10.3556 - 10.3557 - final FunctionNode functionNode = block.getFunction(); 10.3558 - final List<Symbol> symbols = block.getFrame().getSymbols(); 10.3559 - final boolean allVarsInScope = functionNode.varsInScope(); 10.3560 - final boolean isVarArg = functionNode.isVarArg(); 10.3561 - 10.3562 - for (final Symbol symbol : symbols) { 10.3563 - if (symbol.isInternal() || symbol.isThis()) { 10.3564 - continue; 10.3565 - } 10.3566 - 10.3567 - if (symbol.isVar()) { 10.3568 - if (allVarsInScope || symbol.isScope()) { 10.3569 - updateSymbolsLog(functionNode, symbol, true); 10.3570 - symbol.setIsScope(); 10.3571 - symbol.setNeedsSlot(false); 10.3572 - } else { 10.3573 - assert symbol.hasSlot() : symbol + " should have a slot only, no scope"; 10.3574 - } 10.3575 - } else if (symbol.isParam() && (allVarsInScope || isVarArg || symbol.isScope())) { 10.3576 - updateSymbolsLog(functionNode, symbol, isVarArg); 10.3577 - symbol.setIsScope(); 10.3578 - symbol.setNeedsSlot(!isVarArg); 10.3579 - } 10.3580 - } 10.3581 - 10.3582 - return block; 10.3583 - } 10.3584 - } 10.3585 - 10.3586 - /** 10.3587 - * Helper class to evaluate constant expressions at compile time This is 10.3588 - * also a simplifier used by BinaryNode visits, UnaryNode visits and 10.3589 - * conversions. 10.3590 - */ 10.3591 - private abstract static class ConstantEvaluator<T extends Node> { 10.3592 - protected T parent; 10.3593 - protected final Source source; 10.3594 - protected final long token; 10.3595 - protected final int finish; 10.3596 - 10.3597 - protected ConstantEvaluator(final T parent) { 10.3598 - this.parent = parent; 10.3599 - this.source = parent.getSource(); 10.3600 - this.token = parent.getToken(); 10.3601 - this.finish = parent.getFinish(); 10.3602 - } 10.3603 - 10.3604 - /** 10.3605 - * Returns a literal node that replaces the given parent node, or null if replacement 10.3606 - * is impossible 10.3607 - * @return the literal node 10.3608 - */ 10.3609 - protected abstract LiteralNode<?> eval(); 10.3610 - } 10.3611 - 10.3612 - static class LiteralNodeConstantEvaluator extends ConstantEvaluator<LiteralNode<?>> { 10.3613 - private final Type type; 10.3614 - 10.3615 - LiteralNodeConstantEvaluator(final LiteralNode<?> parent, final Type type) { 10.3616 - super(parent); 10.3617 - this.type = type; 10.3618 - } 10.3619 - 10.3620 - @Override 10.3621 - protected LiteralNode<?> eval() { 10.3622 - final Object value = ((LiteralNode<?>)parent).getValue(); 10.3623 - 10.3624 - LiteralNode<?> literalNode = null; 10.3625 - 10.3626 - if (type.isString()) { 10.3627 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toString(value)); 10.3628 - } else if (type.isBoolean()) { 10.3629 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toBoolean(value)); 10.3630 - } else if (type.isInteger()) { 10.3631 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); 10.3632 - } else if (type.isLong()) { 10.3633 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); 10.3634 - } else if (type.isNumber() || parent.getType().isNumeric() && !parent.getType().isNumber()) { 10.3635 - literalNode = LiteralNode.newInstance(source, token, finish, JSType.toNumber(value)); 10.3636 - } 10.3637 - 10.3638 - return literalNode; 10.3639 - } 10.3640 - } 10.3641 - 10.3642 - private static class UnaryNodeConstantEvaluator extends ConstantEvaluator<UnaryNode> { 10.3643 - UnaryNodeConstantEvaluator(final UnaryNode parent) { 10.3644 - super(parent); 10.3645 - } 10.3646 - 10.3647 - @Override 10.3648 - protected LiteralNode<?> eval() { 10.3649 - final Node rhsNode = parent.rhs(); 10.3650 - 10.3651 - if (!rhsNode.getSymbol().isConstant()) { 10.3652 - return null; 10.3653 - } 10.3654 - 10.3655 - final LiteralNode<?> rhs = (LiteralNode<?>)rhsNode; 10.3656 - final boolean rhsInteger = rhs.getType().isInteger(); 10.3657 - 10.3658 - LiteralNode<?> literalNode; 10.3659 - 10.3660 - switch (parent.tokenType()) { 10.3661 - case ADD: 10.3662 - if (rhsInteger) { 10.3663 - literalNode = LiteralNode.newInstance(source, token, finish, rhs.getInt32()); 10.3664 - } else { 10.3665 - literalNode = LiteralNode.newInstance(source, token, finish, rhs.getNumber()); 10.3666 - } 10.3667 - break; 10.3668 - case SUB: 10.3669 - if (rhsInteger && rhs.getInt32() != 0) { // @see test/script/basic/minuszero.js 10.3670 - literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getInt32()); 10.3671 - } else { 10.3672 - literalNode = LiteralNode.newInstance(source, token, finish, -rhs.getNumber()); 10.3673 - } 10.3674 - break; 10.3675 - case NOT: 10.3676 - literalNode = LiteralNode.newInstance(source, token, finish, !rhs.getBoolean()); 10.3677 - break; 10.3678 - case BIT_NOT: 10.3679 - literalNode = LiteralNode.newInstance(source, token, finish, ~rhs.getInt32()); 10.3680 - break; 10.3681 - default: 10.3682 - return null; 10.3683 - } 10.3684 - 10.3685 - return literalNode; 10.3686 - } 10.3687 - } 10.3688 - 10.3689 - private static class BinaryNodeConstantEvaluator extends ConstantEvaluator<BinaryNode> { 10.3690 - BinaryNodeConstantEvaluator(final BinaryNode parent) { 10.3691 - super(parent); 10.3692 - } 10.3693 - 10.3694 - @Override 10.3695 - protected LiteralNode<?> eval() { 10.3696 - 10.3697 - if (!parent.lhs().getSymbol().isConstant() || !parent.rhs().getSymbol().isConstant()) { 10.3698 - return null; 10.3699 - } 10.3700 - 10.3701 - final LiteralNode<?> lhs = (LiteralNode<?>)parent.lhs(); 10.3702 - final LiteralNode<?> rhs = (LiteralNode<?>)parent.rhs(); 10.3703 - 10.3704 - final Type widest = Type.widest(lhs.getType(), rhs.getType()); 10.3705 - 10.3706 - boolean isInteger = widest.isInteger(); 10.3707 - boolean isLong = widest.isLong(); 10.3708 - 10.3709 - double value; 10.3710 - 10.3711 - switch (parent.tokenType()) { 10.3712 - case AND: 10.3713 - return JSType.toBoolean(lhs.getObject()) ? rhs : lhs; 10.3714 - case OR: 10.3715 - return JSType.toBoolean(lhs.getObject()) ? lhs : rhs; 10.3716 - case DIV: 10.3717 - value = lhs.getNumber() / rhs.getNumber(); 10.3718 - break; 10.3719 - case ADD: 10.3720 - value = lhs.getNumber() + rhs.getNumber(); 10.3721 - break; 10.3722 - case MUL: 10.3723 - value = lhs.getNumber() * rhs.getNumber(); 10.3724 - break; 10.3725 - case MOD: 10.3726 - value = lhs.getNumber() % rhs.getNumber(); 10.3727 - break; 10.3728 - case SUB: 10.3729 - value = lhs.getNumber() - rhs.getNumber(); 10.3730 - break; 10.3731 - case SHR: 10.3732 - return LiteralNode.newInstance(source, token, finish, (lhs.getInt32() >>> rhs.getInt32()) & 0xffff_ffffL); 10.3733 - case SAR: 10.3734 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() >> rhs.getInt32()); 10.3735 - case SHL: 10.3736 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() << rhs.getInt32()); 10.3737 - case BIT_XOR: 10.3738 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() ^ rhs.getInt32()); 10.3739 - case BIT_AND: 10.3740 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() & rhs.getInt32()); 10.3741 - case BIT_OR: 10.3742 - return LiteralNode.newInstance(source, token, finish, lhs.getInt32() | rhs.getInt32()); 10.3743 - case GE: 10.3744 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GE(lhs.getObject(), rhs.getObject())); 10.3745 - case LE: 10.3746 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LE(lhs.getObject(), rhs.getObject())); 10.3747 - case GT: 10.3748 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.GT(lhs.getObject(), rhs.getObject())); 10.3749 - case LT: 10.3750 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.LT(lhs.getObject(), rhs.getObject())); 10.3751 - case NE: 10.3752 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE(lhs.getObject(), rhs.getObject())); 10.3753 - case NE_STRICT: 10.3754 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.NE_STRICT(lhs.getObject(), rhs.getObject())); 10.3755 - case EQ: 10.3756 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ(lhs.getObject(), rhs.getObject())); 10.3757 - case EQ_STRICT: 10.3758 - return LiteralNode.newInstance(source, token, finish, ScriptRuntime.EQ_STRICT(lhs.getObject(), rhs.getObject())); 10.3759 - default: 10.3760 - return null; 10.3761 - } 10.3762 - 10.3763 - isInteger &= value != 0.0 && JSType.isRepresentableAsInt(value); 10.3764 - isLong &= value != 0.0 && JSType.isRepresentableAsLong(value); 10.3765 - 10.3766 - if (isInteger) { 10.3767 - return LiteralNode.newInstance(source, token, finish, JSType.toInt32(value)); 10.3768 - } else if (isLong) { 10.3769 - return LiteralNode.newInstance(source, token, finish, JSType.toLong(value)); 10.3770 - } 10.3771 - 10.3772 - return LiteralNode.newInstance(source, token, finish, value); 10.3773 - } 10.3774 - } 10.3775 -}
11.1 --- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java Tue Jan 29 14:25:39 2013 -0400 11.2 +++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java Wed Jan 30 12:26:45 2013 +0100 11.3 @@ -929,7 +929,7 @@ 11.4 */ 11.5 public MethodEmitter loadCallee() { 11.6 debug("load callee " + functionNode.getCalleeNode().getSymbol()); 11.7 - assert functionNode.getCalleeNode().getSymbol().getSlot() != 0; 11.8 + assert functionNode.getCalleeNode().getSymbol().getSlot() != 0 : "callee has wrong slot " + functionNode.getCalleeNode().getSymbol().getSlot() + " in " + functionNode.getName(); 11.9 11.10 return load(functionNode.getCalleeNode().getSymbol()); 11.11 } 11.12 @@ -992,7 +992,7 @@ 11.13 * @param symbol symbol to store stack to 11.14 */ 11.15 public void store(final Symbol symbol) { 11.16 - assert symbol != null; 11.17 + assert symbol != null : "No symbol to store"; 11.18 if (symbol.hasSlot()) { 11.19 debug("store", symbol); 11.20 popType(symbol.getSymbolType()).store(method, symbol.getSlot()); 11.21 @@ -1558,7 +1558,7 @@ 11.22 label.setStack(stack.clone()); 11.23 return; 11.24 } 11.25 - assert stacksEquivalent(stack, labelStack); 11.26 + assert stacksEquivalent(stack, labelStack) : "stacks " + stack + " is not equivalent with " + labelStack + " at join point"; 11.27 } 11.28 11.29 /**
12.1 --- a/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Tue Jan 29 14:25:39 2013 -0400 12.2 +++ b/src/jdk/nashorn/internal/codegen/SharedScopeCall.java Wed Jan 30 12:26:45 2013 +0100 12.3 @@ -159,7 +159,9 @@ 12.4 int slot = 2; 12.5 for (final Type type : paramTypes) { 12.6 method.load(type, slot++); 12.7 - if (type == Type.NUMBER || type == Type.LONG) slot++; 12.8 + if (type == Type.NUMBER || type == Type.LONG) { 12.9 + slot++; 12.10 + } 12.11 } 12.12 method.dynamicCall(returnType, paramTypes.length, flags); 12.13 }
13.1 --- a/src/jdk/nashorn/internal/codegen/WeighNodes.java Tue Jan 29 14:25:39 2013 -0400 13.2 +++ b/src/jdk/nashorn/internal/codegen/WeighNodes.java Wed Jan 30 12:26:45 2013 +0100 13.3 @@ -27,6 +27,8 @@ 13.4 13.5 import java.util.List; 13.6 import java.util.Map; 13.7 + 13.8 +import jdk.nashorn.internal.codegen.types.Type; 13.9 import jdk.nashorn.internal.ir.AccessNode; 13.10 import jdk.nashorn.internal.ir.BinaryNode; 13.11 import jdk.nashorn.internal.ir.Block; 13.12 @@ -53,17 +55,18 @@ 13.13 import jdk.nashorn.internal.ir.SwitchNode; 13.14 import jdk.nashorn.internal.ir.ThrowNode; 13.15 import jdk.nashorn.internal.ir.TryNode; 13.16 +import jdk.nashorn.internal.ir.UnaryNode; 13.17 import jdk.nashorn.internal.ir.VarNode; 13.18 import jdk.nashorn.internal.ir.WhileNode; 13.19 import jdk.nashorn.internal.ir.WithNode; 13.20 -import jdk.nashorn.internal.ir.visitor.NodeVisitor; 13.21 -import jdk.nashorn.internal.parser.TokenType; 13.22 +import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 13.23 + 13.24 13.25 /** 13.26 * Computes the "byte code" weight of an AST segment. This is used 13.27 * for Splitting too large class files 13.28 */ 13.29 -public class WeighNodes extends NodeVisitor { 13.30 +public class WeighNodes extends NodeOperatorVisitor { 13.31 /* 13.32 * Weight constants. 13.33 */ 13.34 @@ -77,6 +80,7 @@ 13.35 private static final long IF_WEIGHT = 2; 13.36 private static final long LITERAL_WEIGHT = 10; 13.37 private static final long LOOP_WEIGHT = 4; 13.38 + private static final long NEW_WEIGHT = 6; 13.39 private static final long REFERENCE_WEIGHT = 20; 13.40 private static final long RETURN_WEIGHT = 2; 13.41 private static final long SPLIT_WEIGHT = 40; 13.42 @@ -120,21 +124,7 @@ 13.43 } 13.44 13.45 @Override 13.46 - public Node leave(final BinaryNode binaryNode) { 13.47 - final TokenType tokenType = binaryNode.tokenType(); 13.48 - 13.49 - if (tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) { 13.50 - weight += ADD_WEIGHT; 13.51 - } else { 13.52 - weight += 1; 13.53 - } 13.54 - 13.55 - return binaryNode; 13.56 - } 13.57 - 13.58 - @Override 13.59 public Node enter(final Block block) { 13.60 - 13.61 if (weightCache != null && weightCache.containsKey(block)) { 13.62 weight += weightCache.get(block); 13.63 return null; 13.64 @@ -305,4 +295,274 @@ 13.65 weight += WITH_WEIGHT; 13.66 return withNode; 13.67 } 13.68 + 13.69 + @Override 13.70 + public Node leaveADD(final UnaryNode unaryNode) { 13.71 + return unaryNodeWeight(unaryNode); 13.72 + } 13.73 + 13.74 + @Override 13.75 + public Node leaveBIT_NOT(final UnaryNode unaryNode) { 13.76 + return unaryNodeWeight(unaryNode); 13.77 + } 13.78 + 13.79 + @Override 13.80 + public Node leaveCONVERT(final UnaryNode unaryNode) { 13.81 + return unaryNodeWeight(unaryNode); 13.82 + } 13.83 + 13.84 + @Override 13.85 + public Node leaveDECINC(final UnaryNode unaryNode) { 13.86 + return unaryNodeWeight(unaryNode); 13.87 + } 13.88 + 13.89 + @Override 13.90 + public Node leaveDELETE(final UnaryNode unaryNode) { 13.91 + return runtimeNodeWeight(unaryNode); 13.92 + } 13.93 + 13.94 + @Override 13.95 + public Node leaveDISCARD(final UnaryNode unaryNode) { 13.96 + return unaryNodeWeight(unaryNode); 13.97 + } 13.98 + 13.99 + @Override 13.100 + public Node leaveNEW(final UnaryNode unaryNode) { 13.101 + weight += NEW_WEIGHT; 13.102 + return unaryNode; 13.103 + } 13.104 + 13.105 + @Override 13.106 + public Node leaveNOT(final UnaryNode unaryNode) { 13.107 + return unaryNodeWeight(unaryNode); 13.108 + } 13.109 + 13.110 + @Override 13.111 + public Node leaveSUB(final UnaryNode unaryNode) { 13.112 + return unaryNodeWeight(unaryNode); 13.113 + } 13.114 + 13.115 + @Override 13.116 + public Node leaveTYPEOF(final UnaryNode unaryNode) { 13.117 + return runtimeNodeWeight(unaryNode); 13.118 + } 13.119 + 13.120 + @Override 13.121 + public Node leaveVOID(final UnaryNode unaryNode) { 13.122 + return unaryNodeWeight(unaryNode); 13.123 + } 13.124 + 13.125 + @Override 13.126 + public Node leaveADD(final BinaryNode binaryNode) { 13.127 + weight += ADD_WEIGHT; 13.128 + return binaryNode; 13.129 + } 13.130 + 13.131 + @Override 13.132 + public Node leaveAND(final BinaryNode binaryNode) { 13.133 + return binaryNodeWeight(binaryNode); 13.134 + } 13.135 + 13.136 + @Override 13.137 + public Node leaveASSIGN(final BinaryNode binaryNode) { 13.138 + return binaryNodeWeight(binaryNode); 13.139 + } 13.140 + 13.141 + @Override 13.142 + public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { 13.143 + weight += ADD_WEIGHT; 13.144 + return binaryNode; 13.145 + } 13.146 + 13.147 + @Override 13.148 + public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) { 13.149 + return binaryNodeWeight(binaryNode); 13.150 + } 13.151 + 13.152 + @Override 13.153 + public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) { 13.154 + return binaryNodeWeight(binaryNode); 13.155 + } 13.156 + 13.157 + @Override 13.158 + public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) { 13.159 + return binaryNodeWeight(binaryNode); 13.160 + } 13.161 + 13.162 + @Override 13.163 + public Node leaveASSIGN_DIV(final BinaryNode binaryNode) { 13.164 + return binaryNodeWeight(binaryNode); 13.165 + } 13.166 + 13.167 + @Override 13.168 + public Node leaveASSIGN_MOD(final BinaryNode binaryNode) { 13.169 + return binaryNodeWeight(binaryNode); 13.170 + } 13.171 + 13.172 + @Override 13.173 + public Node leaveASSIGN_MUL(final BinaryNode binaryNode) { 13.174 + return binaryNodeWeight(binaryNode); 13.175 + } 13.176 + 13.177 + @Override 13.178 + public Node leaveASSIGN_SAR(final BinaryNode binaryNode) { 13.179 + return binaryNodeWeight(binaryNode); 13.180 + } 13.181 + 13.182 + @Override 13.183 + public Node leaveASSIGN_SHL(final BinaryNode binaryNode) { 13.184 + return binaryNodeWeight(binaryNode); 13.185 + } 13.186 + 13.187 + @Override 13.188 + public Node leaveASSIGN_SHR(final BinaryNode binaryNode) { 13.189 + return binaryNodeWeight(binaryNode); 13.190 + } 13.191 + 13.192 + @Override 13.193 + public Node leaveASSIGN_SUB(final BinaryNode binaryNode) { 13.194 + return binaryNodeWeight(binaryNode); 13.195 + } 13.196 + 13.197 + @Override 13.198 + public Node leaveBIND(final BinaryNode binaryNode) { 13.199 + return binaryNodeWeight(binaryNode); 13.200 + } 13.201 + 13.202 + @Override 13.203 + public Node leaveBIT_AND(final BinaryNode binaryNode) { 13.204 + return binaryNodeWeight(binaryNode); 13.205 + } 13.206 + 13.207 + @Override 13.208 + public Node leaveBIT_OR(final BinaryNode binaryNode) { 13.209 + return binaryNodeWeight(binaryNode); 13.210 + } 13.211 + 13.212 + @Override 13.213 + public Node leaveBIT_XOR(final BinaryNode binaryNode) { 13.214 + return binaryNodeWeight(binaryNode); 13.215 + } 13.216 + 13.217 + @Override 13.218 + public Node leaveCOMMALEFT(final BinaryNode binaryNode) { 13.219 + return binaryNodeWeight(binaryNode); 13.220 + } 13.221 + 13.222 + @Override 13.223 + public Node leaveCOMMARIGHT(final BinaryNode binaryNode) { 13.224 + return binaryNodeWeight(binaryNode); 13.225 + } 13.226 + 13.227 + @Override 13.228 + public Node leaveDIV(final BinaryNode binaryNode) { 13.229 + return binaryNodeWeight(binaryNode); 13.230 + } 13.231 + 13.232 + @Override 13.233 + public Node leaveEQ(final BinaryNode binaryNode) { 13.234 + return runtimeNodeWeight(binaryNode); 13.235 + } 13.236 + 13.237 + @Override 13.238 + public Node leaveEQ_STRICT(final BinaryNode binaryNode) { 13.239 + return runtimeNodeWeight(binaryNode); 13.240 + } 13.241 + 13.242 + @Override 13.243 + public Node leaveGE(final BinaryNode binaryNode) { 13.244 + return runtimeNodeWeight(binaryNode); 13.245 + } 13.246 + 13.247 + @Override 13.248 + public Node leaveGT(final BinaryNode binaryNode) { 13.249 + return runtimeNodeWeight(binaryNode); 13.250 + } 13.251 + 13.252 + @Override 13.253 + public Node leaveIN(final BinaryNode binaryNode) { 13.254 + weight += CALL_WEIGHT; 13.255 + return binaryNode; 13.256 + } 13.257 + 13.258 + @Override 13.259 + public Node leaveINSTANCEOF(final BinaryNode binaryNode) { 13.260 + weight += CALL_WEIGHT; 13.261 + return binaryNode; 13.262 + } 13.263 + 13.264 + @Override 13.265 + public Node leaveLE(final BinaryNode binaryNode) { 13.266 + return runtimeNodeWeight(binaryNode); 13.267 + } 13.268 + 13.269 + @Override 13.270 + public Node leaveLT(final BinaryNode binaryNode) { 13.271 + return runtimeNodeWeight(binaryNode); 13.272 + } 13.273 + 13.274 + @Override 13.275 + public Node leaveMOD(final BinaryNode binaryNode) { 13.276 + return binaryNodeWeight(binaryNode); 13.277 + } 13.278 + 13.279 + @Override 13.280 + public Node leaveMUL(final BinaryNode binaryNode) { 13.281 + return binaryNodeWeight(binaryNode); 13.282 + } 13.283 + 13.284 + @Override 13.285 + public Node leaveNE(final BinaryNode binaryNode) { 13.286 + return runtimeNodeWeight(binaryNode); 13.287 + } 13.288 + 13.289 + @Override 13.290 + public Node leaveNE_STRICT(final BinaryNode binaryNode) { 13.291 + return runtimeNodeWeight(binaryNode); 13.292 + } 13.293 + 13.294 + @Override 13.295 + public Node leaveOR(final BinaryNode binaryNode) { 13.296 + return binaryNodeWeight(binaryNode); 13.297 + } 13.298 + 13.299 + @Override 13.300 + public Node leaveSAR(final BinaryNode binaryNode) { 13.301 + return binaryNodeWeight(binaryNode); 13.302 + } 13.303 + 13.304 + @Override 13.305 + public Node leaveSHL(final BinaryNode binaryNode) { 13.306 + return binaryNodeWeight(binaryNode); 13.307 + } 13.308 + 13.309 + @Override 13.310 + public Node leaveSHR(final BinaryNode binaryNode) { 13.311 + return binaryNodeWeight(binaryNode); 13.312 + } 13.313 + 13.314 + @Override 13.315 + public Node leaveSUB(final BinaryNode binaryNode) { 13.316 + return binaryNodeWeight(binaryNode); 13.317 + } 13.318 + 13.319 + private Node unaryNodeWeight(final UnaryNode unaryNode) { 13.320 + weight += 1; 13.321 + return unaryNode; 13.322 + } 13.323 + 13.324 + private Node binaryNodeWeight(final BinaryNode binaryNode) { 13.325 + weight += 1; 13.326 + return binaryNode; 13.327 + } 13.328 + 13.329 + private Node runtimeNodeWeight(final UnaryNode unaryNode) { 13.330 + weight += CALL_WEIGHT; 13.331 + return unaryNode; 13.332 + } 13.333 + 13.334 + private Node runtimeNodeWeight(final BinaryNode binaryNode) { 13.335 + weight += Type.widest(binaryNode.lhs().getType(), binaryNode.rhs().getType()).isObject() ? CALL_WEIGHT : 1; 13.336 + return binaryNode; 13.337 + } 13.338 }
14.1 --- a/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java Tue Jan 29 14:25:39 2013 -0400 14.2 +++ b/src/jdk/nashorn/internal/codegen/objects/FunctionObjectCreator.java Wed Jan 30 12:26:45 2013 +0100 14.3 @@ -33,8 +33,9 @@ 14.4 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; 14.5 14.6 import java.lang.invoke.MethodHandle; 14.7 +import java.util.ArrayList; 14.8 import java.util.EnumSet; 14.9 -import java.util.List; 14.10 + 14.11 import jdk.nashorn.internal.codegen.CodeGenerator; 14.12 import jdk.nashorn.internal.codegen.FunctionSignature; 14.13 import jdk.nashorn.internal.codegen.MethodEmitter; 14.14 @@ -61,11 +62,9 @@ 14.15 * 14.16 * @param codegen the code generator 14.17 * @param functionNode the function node to turn into a ScriptFunction implementation 14.18 - * @param keys initial keys for the object map 14.19 - * @param symbols corresponding initial symbols for object map 14.20 */ 14.21 - public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode, final List<String> keys, final List<Symbol> symbols) { 14.22 - super(codegen, keys, symbols, false, false); 14.23 + public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode) { 14.24 + super(codegen, new ArrayList<String>(), new ArrayList<Symbol>(), false, false); 14.25 this.functionNode = functionNode; 14.26 } 14.27
15.1 --- a/src/jdk/nashorn/internal/ir/Block.java Tue Jan 29 14:25:39 2013 -0400 15.2 +++ b/src/jdk/nashorn/internal/ir/Block.java Wed Jan 30 12:26:45 2013 +0100 15.3 @@ -35,11 +35,11 @@ 15.4 15.5 import java.io.PrintWriter; 15.6 import java.util.ArrayList; 15.7 -import java.util.Collection; 15.8 import java.util.Collections; 15.9 +import java.util.Comparator; 15.10 import java.util.HashMap; 15.11 import java.util.List; 15.12 -import java.util.Map; 15.13 + 15.14 import jdk.nashorn.internal.codegen.Frame; 15.15 import jdk.nashorn.internal.codegen.MethodEmitter.Label; 15.16 import jdk.nashorn.internal.ir.annotations.Ignore; 15.17 @@ -287,7 +287,7 @@ 15.18 15.19 for (Block block = this; block != null; block = block.getParent()) { 15.20 // Find name. 15.21 - final Symbol symbol = block.getSymbols().get(name); 15.22 + final Symbol symbol = block.symbols.get(name); 15.23 // If found then we are good. 15.24 if (symbol != null) { 15.25 return symbol; 15.26 @@ -307,7 +307,7 @@ 15.27 // Search up block chain to locate symbol. 15.28 for (Block block = this; block != null; block = block.getParent()) { 15.29 // Find name. 15.30 - final Symbol symbol = block.getSymbols().get(name); 15.31 + final Symbol symbol = block.symbols.get(name); 15.32 // If found then we are good. 15.33 if (symbol != null) { 15.34 return symbol; 15.35 @@ -458,14 +458,22 @@ 15.36 } 15.37 15.38 /** 15.39 - * Print symbols in block (debugging.) 15.40 + * Print symbols in block in alphabetical order, sorted on name 15.41 + * Used for debugging, see the --print-symbols flag 15.42 * 15.43 * @param stream print writer to output symbols to 15.44 * 15.45 * @return true if symbols were found 15.46 */ 15.47 public boolean printSymbols(final PrintWriter stream) { 15.48 - final Collection<Symbol> values = symbols.values(); 15.49 + final List<Symbol> values = new ArrayList<>(symbols.values()); 15.50 + 15.51 + Collections.sort(values, new Comparator<Symbol>() { 15.52 + @Override 15.53 + public int compare(final Symbol s0, final Symbol s1) { 15.54 + return s0.getName().compareTo(s1.getName()); 15.55 + } 15.56 + }); 15.57 15.58 for (final Symbol symbol : values) { 15.59 symbol.print(stream); 15.60 @@ -564,15 +572,6 @@ 15.61 } 15.62 15.63 /** 15.64 - * Get the symbol table for this block 15.65 - * 15.66 - * @return a symbol table, which is a map from symbol name to symbol. 15.67 - */ 15.68 - private Map<String, Symbol> getSymbols() { 15.69 - return symbols; 15.70 - } 15.71 - 15.72 - /** 15.73 * Check whether scope is necessary for this Block 15.74 * 15.75 * @return true if this function needs a scope
16.1 --- a/src/jdk/nashorn/internal/ir/CallNode.java Tue Jan 29 14:25:39 2013 -0400 16.2 +++ b/src/jdk/nashorn/internal/ir/CallNode.java Wed Jan 30 12:26:45 2013 +0100 16.3 @@ -28,7 +28,9 @@ 16.4 import java.util.ArrayList; 16.5 import java.util.Collections; 16.6 import java.util.List; 16.7 + 16.8 import jdk.nashorn.internal.codegen.types.Type; 16.9 +import jdk.nashorn.internal.ir.annotations.Ignore; 16.10 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 16.11 import jdk.nashorn.internal.runtime.Source; 16.12 16.13 @@ -57,16 +59,83 @@ 16.14 */ 16.15 public static class EvalArgs { 16.16 /** evaluated code */ 16.17 - public Node code; 16.18 + private Node code; 16.19 + 16.20 /** 'this' passed to evaluated code */ 16.21 - public Node evalThis; 16.22 + private IdentNode evalThis; 16.23 + 16.24 /** location string for the eval call */ 16.25 - public String location; 16.26 + final private String location; 16.27 + 16.28 /** is this call from a strict context? */ 16.29 - public boolean strictMode; 16.30 + final private boolean strictMode; 16.31 + 16.32 + /** 16.33 + * Constructor 16.34 + * 16.35 + * @param code code to evaluate 16.36 + * @param evalThis this node 16.37 + * @param location location for the eval call 16.38 + * @param strictMode is this a call from a strict context? 16.39 + */ 16.40 + public EvalArgs(final Node code, final IdentNode evalThis, final String location, final boolean strictMode) { 16.41 + this.code = code; 16.42 + this.evalThis = evalThis; 16.43 + this.location = location; 16.44 + this.strictMode = strictMode; 16.45 + } 16.46 + 16.47 + /** 16.48 + * Return the code that is to be eval:ed by this eval function 16.49 + * @return code as an AST node 16.50 + */ 16.51 + public Node getCode() { 16.52 + return code; 16.53 + } 16.54 + 16.55 + /** 16.56 + * Set the code that is to be eval.ed by this eval function 16.57 + * @param code the code as an AST node 16.58 + */ 16.59 + public void setCode(final Node code) { 16.60 + this.code = code; 16.61 + } 16.62 + 16.63 + /** 16.64 + * Get the {@code this} symbol used to invoke this eval call 16.65 + * @return the {@code this} symbol 16.66 + */ 16.67 + public IdentNode getThis() { 16.68 + return this.evalThis; 16.69 + } 16.70 + 16.71 + /** 16.72 + * Set the {@code this} symbol used to invoke this eval call 16.73 + * @param evalThis the {@code this} symbol 16.74 + */ 16.75 + public void setThis(final IdentNode evalThis) { 16.76 + this.evalThis = evalThis; 16.77 + } 16.78 + 16.79 + /** 16.80 + * Get the human readable location for this eval call 16.81 + * @return the location 16.82 + */ 16.83 + public String getLocation() { 16.84 + return this.location; 16.85 + } 16.86 + 16.87 + /** 16.88 + * Check whether this eval call is executed in strict mode 16.89 + * @return true if executed in strict mode, false otherwise 16.90 + */ 16.91 + public boolean getStrictMode() { 16.92 + return this.strictMode; 16.93 + } 16.94 } 16.95 16.96 /** arguments for 'eval' call. Non-null only if this call node is 'eval' */ 16.97 + @Ignore 16.98 private EvalArgs evalArgs; 16.99 16.100 /**
17.1 --- a/src/jdk/nashorn/internal/ir/CatchNode.java Tue Jan 29 14:25:39 2013 -0400 17.2 +++ b/src/jdk/nashorn/internal/ir/CatchNode.java Wed Jan 30 12:26:45 2013 +0100 17.3 @@ -57,7 +57,7 @@ 17.4 * @param body catch body 17.5 */ 17.6 public CatchNode(final Source source, final long token, final int finish, final IdentNode exception, final Node exceptionCondition, final Block body) { 17.7 - super (source, token, finish); 17.8 + super(source, token, finish); 17.9 17.10 this.exception = exception; 17.11 this.exceptionCondition = exceptionCondition;
18.1 --- a/src/jdk/nashorn/internal/ir/ExecuteNode.java Tue Jan 29 14:25:39 2013 -0400 18.2 +++ b/src/jdk/nashorn/internal/ir/ExecuteNode.java Wed Jan 30 12:26:45 2013 +0100 18.3 @@ -47,14 +47,22 @@ 18.4 */ 18.5 public ExecuteNode(final Source source, final long token, final int finish, final Node expression) { 18.6 super(source, token, finish); 18.7 + this.expression = expression; 18.8 + } 18.9 18.10 + /** 18.11 + * Constructor 18.12 + * 18.13 + * @param expression an expression to wrap, from which source, tokens and finish are also inherited 18.14 + */ 18.15 + public ExecuteNode(final Node expression) { 18.16 + super(expression.getSource(), expression.getToken(), expression.getFinish()); 18.17 this.expression = expression; 18.18 } 18.19 18.20 private ExecuteNode(final ExecuteNode executeNode, final CopyState cs) { 18.21 super(executeNode); 18.22 - 18.23 - expression = cs.existingOrCopy(executeNode.expression); 18.24 + this.expression = cs.existingOrCopy(executeNode.expression); 18.25 } 18.26 18.27 @Override
19.1 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java Tue Jan 29 14:25:39 2013 -0400 19.2 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java Wed Jan 30 12:26:45 2013 +0100 19.3 @@ -32,10 +32,8 @@ 19.4 19.5 import java.util.ArrayList; 19.6 import java.util.Collections; 19.7 -import java.util.LinkedHashMap; 19.8 import java.util.LinkedList; 19.9 import java.util.List; 19.10 -import java.util.Map; 19.11 import java.util.Stack; 19.12 import jdk.nashorn.internal.codegen.CompileUnit; 19.13 import jdk.nashorn.internal.codegen.Compiler; 19.14 @@ -126,9 +124,6 @@ 19.15 @Ignore 19.16 private IdentNode varArgsNode; 19.17 19.18 - /** this access properties. */ 19.19 - private final LinkedHashMap<String, Node> thisProperties; 19.20 - 19.21 /** Pending label list. */ 19.22 private final Stack<LabelNode> labelStack; 19.23 19.24 @@ -147,6 +142,10 @@ 19.25 @Ignore 19.26 private LineNumberNode funcVarLineNumberNode; 19.27 19.28 + /** Initializer var func = __callee__, where applicable */ 19.29 + @Ignore 19.30 + private Node selfSymbolInit; 19.31 + 19.32 /** Function flags. */ 19.33 private int flags; 19.34 19.35 @@ -183,7 +182,7 @@ 19.36 private static final int NEEDS_SCOPE = HAS_ALL_VARS_IN_SCOPE | IS_VAR_ARG; 19.37 19.38 /** What is the return type of this function? */ 19.39 - private Type returnType = Type.OBJECT; 19.40 + private Type returnType = Type.UNKNOWN; 19.41 19.42 /** 19.43 * Used to keep track of a function's parent blocks. 19.44 @@ -216,7 +215,6 @@ 19.45 this.firstToken = token; 19.46 this.lastToken = token; 19.47 this.namespace = new Namespace(compiler.getNamespace().getParent()); 19.48 - this.thisProperties = new LinkedHashMap<>(); 19.49 this.labelStack = new Stack<>(); 19.50 this.controlStack = new Stack<>(); 19.51 this.declarations = new ArrayList<>(); 19.52 @@ -249,7 +247,6 @@ 19.53 this.argumentsNode = (IdentNode)cs.existingOrCopy(functionNode.argumentsNode); 19.54 this.varArgsNode = (IdentNode)cs.existingOrCopy(functionNode.varArgsNode); 19.55 this.calleeNode = (IdentNode)cs.existingOrCopy(functionNode.calleeNode); 19.56 - this.thisProperties = new LinkedHashMap<>(); 19.57 this.labelStack = new Stack<>(); 19.58 this.controlStack = new Stack<>(); 19.59 this.declarations = new ArrayList<>(); 19.60 @@ -372,30 +369,44 @@ 19.61 /** 19.62 * Create a temporary variable to the current frame. 19.63 * 19.64 + * @param currentFrame Frame to add to - defaults to current function frame 19.65 * @param type Strong type of symbol. 19.66 * @param node Primary node to use symbol. 19.67 * 19.68 * @return Symbol used. 19.69 */ 19.70 - public Symbol newTemporary(final Type type, final Node node) { 19.71 - Symbol sym = node.getSymbol(); 19.72 + public Symbol newTemporary(final Frame currentFrame, final Type type, final Node node) { 19.73 + assert currentFrame != null; 19.74 + Symbol symbol = node.getSymbol(); 19.75 19.76 // If no symbol already present. 19.77 - if (sym == null) { 19.78 + if (symbol == null) { 19.79 final String uname = uniqueName(TEMP_PREFIX.tag()); 19.80 - sym = new Symbol(uname, IS_TEMP, type); 19.81 - sym.setNode(node); 19.82 + symbol = new Symbol(uname, IS_TEMP, type); 19.83 + symbol.setNode(node); 19.84 } 19.85 19.86 // Assign a slot if it doesn't have one. 19.87 - if (!sym.hasSlot()) { 19.88 - frames.addSymbol(sym); 19.89 + if (!symbol.hasSlot()) { 19.90 + currentFrame.addSymbol(symbol); 19.91 } 19.92 19.93 // Set symbol to node. 19.94 - node.setSymbol(sym); 19.95 + node.setSymbol(symbol); 19.96 19.97 - return sym; 19.98 + return symbol; 19.99 + } 19.100 + 19.101 + /** 19.102 + * Add a new temporary variable to the current frame 19.103 + * 19.104 + * @param type Strong type of symbol 19.105 + * @param node Primary node to use symbol 19.106 + * 19.107 + * @return symbol used 19.108 + */ 19.109 + public Symbol newTemporary(final Type type, final Node node) { 19.110 + return newTemporary(frames, type, node); 19.111 } 19.112 19.113 /** 19.114 @@ -414,22 +425,13 @@ 19.115 return sym; 19.116 } 19.117 19.118 - /** 19.119 - * Add a property to the constructor (function) based on this.x usage. 19.120 - * 19.121 - * @param key Name of property. 19.122 - * @param node Value node (has type.) 19.123 - */ 19.124 - public void addThisProperty(final String key, final Node node) { 19.125 - if (node == null) { 19.126 - return; 19.127 - } 19.128 - 19.129 - thisProperties.put(key, node); 19.130 - } 19.131 - 19.132 @Override 19.133 public void toString(final StringBuilder sb) { 19.134 + sb.append('['); 19.135 + sb.append(returnType); 19.136 + sb.append(']'); 19.137 + sb.append(' '); 19.138 + 19.139 sb.append("function"); 19.140 19.141 if (ident != null) { 19.142 @@ -872,11 +874,22 @@ 19.143 } 19.144 19.145 /** 19.146 + * Get the initializer statement for the __callee__ variable, where applicable 19.147 + * for self references 19.148 + * @return initialization 19.149 + */ 19.150 + public Node getSelfSymbolInit() { 19.151 + return this.selfSymbolInit; 19.152 + } 19.153 + 19.154 + /** 19.155 * Flag the function as needing a self symbol. This is needed only for 19.156 * self referring functions 19.157 + * @param selfSymbolInit initialization expression for self symbol 19.158 */ 19.159 - public void setNeedsSelfSymbol() { 19.160 + public void setNeedsSelfSymbol(final Node selfSymbolInit) { 19.161 this.flags |= NEEDS_SELF_SYMBOL; 19.162 + this.selfSymbolInit = selfSymbolInit; 19.163 } 19.164 19.165 /** 19.166 @@ -942,16 +955,6 @@ 19.167 } 19.168 19.169 /** 19.170 - * Get a all properties accessed with {@code this} used as a base in this 19.171 - * function - the map is ordered upon assignment order in the control flow 19.172 - * 19.173 - * @return map a map of property name to node mapping for this accesses 19.174 - */ 19.175 - public Map<String, Node> getThisProperties() { 19.176 - return Collections.unmodifiableMap(thisProperties); 19.177 - } 19.178 - 19.179 - /** 19.180 * Get the namespace this function uses for its symbols 19.181 * @return the namespace 19.182 */ 19.183 @@ -984,11 +987,7 @@ 19.184 //we never bother with object types narrower than objects, that will lead to byte code verification errors 19.185 //as for instance even if we know we are returning a string from a method, the code generator will always 19.186 //treat it as an object, at least for now 19.187 - this.returnType = returnType.isObject() ? Type.OBJECT : returnType; 19.188 - // Adjust type of result node symbol 19.189 - if (returnType != Type.UNKNOWN) { 19.190 - resultNode.getSymbol().setTypeOverride(this.returnType); 19.191 - } 19.192 + this.returnType = Type.widest(this.returnType, returnType.isObject() ? Type.OBJECT : returnType); 19.193 } 19.194 19.195 /** 19.196 @@ -1010,15 +1009,13 @@ 19.197 19.198 /** 19.199 * Set the lowered state 19.200 - * 19.201 - * @param isLowered lowered state 19.202 */ 19.203 - public void setIsLowered(final boolean isLowered) { 19.204 - flags = isLowered ? flags | IS_LOWERED : flags & ~IS_LOWERED; 19.205 + public void setIsLowered() { 19.206 + flags |= IS_LOWERED; 19.207 } 19.208 19.209 /** 19.210 - * Get the lowered 19.211 + * Get the lowered state 19.212 * 19.213 * @return true if function is lowered 19.214 */ 19.215 @@ -1077,22 +1074,6 @@ 19.216 } 19.217 19.218 /** 19.219 - * @return the unit index 19.220 - */ 19.221 -// public int getUnit() { 19.222 - // return unit; 19.223 - // } 19.224 - 19.225 - /** 19.226 - * Set the index of this function's compile unit. Used by splitter. 19.227 - * @see Splitter 19.228 - * @param unit the unit index 19.229 - */ 19.230 -// public void setUnit(final int unit) { 19.231 -// this.unit = unit; 19.232 -// } 19.233 - 19.234 - /** 19.235 * Get the compile unit used to compile this function 19.236 * @see Compiler 19.237 * @see Splitter
20.1 --- a/src/jdk/nashorn/internal/ir/LiteralNode.java Tue Jan 29 14:25:39 2013 -0400 20.2 +++ b/src/jdk/nashorn/internal/ir/LiteralNode.java Wed Jan 30 12:26:45 2013 +0100 20.3 @@ -32,8 +32,10 @@ 20.4 import jdk.nashorn.internal.codegen.types.Type; 20.5 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 20.6 import jdk.nashorn.internal.parser.Lexer.LexerToken; 20.7 +import jdk.nashorn.internal.parser.Token; 20.8 import jdk.nashorn.internal.parser.TokenType; 20.9 import jdk.nashorn.internal.runtime.JSType; 20.10 +import jdk.nashorn.internal.runtime.ScriptRuntime; 20.11 import jdk.nashorn.internal.runtime.Source; 20.12 import jdk.nashorn.internal.runtime.Undefined; 20.13 20.14 @@ -200,6 +202,15 @@ 20.15 } 20.16 20.17 /** 20.18 + * Test if tha value is a number 20.19 + * 20.20 + * @return True if value is a number 20.21 + */ 20.22 + public boolean isNumeric() { 20.23 + return value instanceof Number; 20.24 + } 20.25 + 20.26 + /** 20.27 * Assist in IR navigation. 20.28 * 20.29 * @param visitor IR navigating visitor. 20.30 @@ -240,16 +251,49 @@ 20.31 * @return the new literal node 20.32 */ 20.33 public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish) { 20.34 - return new LiteralNode<Node>(source, token, finish, null) { 20.35 - @Override 20.36 - protected Node copy(final CopyState cs) { 20.37 - return LiteralNode.newInstance(getSource(), getToken(), getFinish()); 20.38 - } 20.39 - @Override 20.40 - public Type getType() { 20.41 - return Type.OBJECT; 20.42 - } 20.43 - }; 20.44 + return new NodeLiteralNode(source, token, finish); 20.45 + } 20.46 + 20.47 + /** 20.48 + * Create a new null literal based on a parent node (source, token, finish) 20.49 + * 20.50 + * @param parent parent node 20.51 + * 20.52 + * @return the new literal node 20.53 + */ 20.54 + public static LiteralNode<?> newInstance(final Node parent) { 20.55 + return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish()); 20.56 + } 20.57 + 20.58 + private static class BooleanLiteralNode extends LiteralNode<Boolean> { 20.59 + 20.60 + private BooleanLiteralNode(final Source source, final long token, final int finish, final boolean value) { 20.61 + super(source, Token.recast(token, value ? TokenType.TRUE : TokenType.FALSE), finish, value); 20.62 + } 20.63 + 20.64 + private BooleanLiteralNode(final BooleanLiteralNode literalNode) { 20.65 + super(literalNode); 20.66 + } 20.67 + 20.68 + @Override 20.69 + protected Node copy(final CopyState cs) { 20.70 + return new BooleanLiteralNode(this); 20.71 + } 20.72 + 20.73 + @Override 20.74 + public boolean isTrue() { 20.75 + return value; 20.76 + } 20.77 + 20.78 + @Override 20.79 + public Type getType() { 20.80 + return Type.BOOLEAN; 20.81 + } 20.82 + 20.83 + @Override 20.84 + public Type getWidestOperationType() { 20.85 + return Type.BOOLEAN; 20.86 + } 20.87 } 20.88 20.89 /** 20.90 @@ -263,29 +307,63 @@ 20.91 * @return the new literal node 20.92 */ 20.93 public static LiteralNode<Boolean> newInstance(final Source source, final long token, final int finish, final boolean value) { 20.94 - return new LiteralNode<Boolean>(source, token, finish, value) { 20.95 - @Override 20.96 - protected Node copy(final CopyState cs) { 20.97 - return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue()); 20.98 + return new BooleanLiteralNode(source, token, finish, value); 20.99 + } 20.100 + 20.101 + /** 20.102 + * Create a new boolean literal based on a parent node (source, token, finish) 20.103 + * 20.104 + * @param parent parent node 20.105 + * @param value true or false 20.106 + * 20.107 + * @return the new literal node 20.108 + */ 20.109 + public static LiteralNode<?> newInstance(final Node parent, final boolean value) { 20.110 + return new BooleanLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value); 20.111 + } 20.112 + 20.113 + private static class NumberLiteralNode extends LiteralNode<Number> { 20.114 + 20.115 + private final Type type = numberGetType(value); 20.116 + 20.117 + private NumberLiteralNode(final Source source, final long token, final int finish, final Number value) { 20.118 + super(source, Token.recast(token, TokenType.DECIMAL), finish, value); 20.119 + } 20.120 + 20.121 + private NumberLiteralNode(final NumberLiteralNode literalNode) { 20.122 + super(literalNode); 20.123 + } 20.124 + 20.125 + private static Type numberGetType(final Number number) { 20.126 + if (number instanceof Integer) { 20.127 + return Type.INT; 20.128 + } else if (number instanceof Long) { 20.129 + return Type.LONG; 20.130 + } else if (number instanceof Double) { 20.131 + return Type.NUMBER; 20.132 + } else { 20.133 + assert false; 20.134 } 20.135 20.136 - @Override 20.137 - public boolean isTrue() { 20.138 - return value; 20.139 - } 20.140 + return null; 20.141 + } 20.142 20.143 - @Override 20.144 - public Type getType() { 20.145 - return Type.BOOLEAN; 20.146 - } 20.147 + @Override 20.148 + protected Node copy(final CopyState cs) { 20.149 + return new NumberLiteralNode(this); 20.150 + } 20.151 20.152 - @Override 20.153 - public Type getWidestOperationType() { 20.154 - return Type.BOOLEAN; 20.155 - } 20.156 - }; 20.157 + @Override 20.158 + public Type getType() { 20.159 + return type; 20.160 + } 20.161 + 20.162 + @Override 20.163 + public Type getWidestOperationType() { 20.164 + return getType(); 20.165 + } 20.166 + 20.167 } 20.168 - 20.169 /** 20.170 * Create a new number literal 20.171 * 20.172 @@ -297,40 +375,34 @@ 20.173 * @return the new literal node 20.174 */ 20.175 public static LiteralNode<Number> newInstance(final Source source, final long token, final int finish, final Number value) { 20.176 - return new LiteralNode<Number>(source, token, finish, value) { 20.177 + return new NumberLiteralNode(source, token, finish, value); 20.178 + } 20.179 20.180 - private Type numberGetType(final Number number) { 20.181 - if (number instanceof Integer) { 20.182 - return Type.INT; 20.183 - } else if (number instanceof Long) { 20.184 - return Type.LONG; 20.185 - } else if (number instanceof Double) { 20.186 - return Type.NUMBER; 20.187 - } else { 20.188 - assert false; 20.189 - } 20.190 + /** 20.191 + * Create a new number literal based on a parent node (source, token, finish) 20.192 + * 20.193 + * @param parent parent node 20.194 + * @param value literal value 20.195 + * 20.196 + * @return the new literal node 20.197 + */ 20.198 + public static LiteralNode<?> newInstance(final Node parent, final Number value) { 20.199 + return new NumberLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value); 20.200 + } 20.201 20.202 - return null; 20.203 - } 20.204 + private static class UndefinedLiteralNode extends LiteralNode<Undefined> { 20.205 + private UndefinedLiteralNode(final Source source, final long token, final int finish) { 20.206 + super(source, Token.recast(token, TokenType.OBJECT), finish, ScriptRuntime.UNDEFINED); 20.207 + } 20.208 20.209 - private final Type type = numberGetType(value); 20.210 + private UndefinedLiteralNode(final UndefinedLiteralNode literalNode) { 20.211 + super(literalNode); 20.212 + } 20.213 20.214 - @Override 20.215 - protected Node copy(final CopyState cs) { 20.216 - return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue()); 20.217 - } 20.218 - 20.219 - @Override 20.220 - public Type getType() { 20.221 - return type; 20.222 - } 20.223 - 20.224 - @Override 20.225 - public Type getWidestOperationType() { 20.226 - return getType(); 20.227 - } 20.228 - 20.229 - }; 20.230 + @Override 20.231 + protected Node copy(final CopyState cs) { 20.232 + return new UndefinedLiteralNode(this); 20.233 + } 20.234 } 20.235 20.236 /** 20.237 @@ -344,12 +416,41 @@ 20.238 * @return the new literal node 20.239 */ 20.240 public static LiteralNode<Undefined> newInstance(final Source source, final long token, final int finish, final Undefined value) { 20.241 - return new LiteralNode<Undefined>(source, token, finish, value) { 20.242 - @Override 20.243 - protected Node copy(final CopyState cs) { 20.244 - return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue()); 20.245 - } 20.246 - }; 20.247 + return new UndefinedLiteralNode(source, token, finish); 20.248 + } 20.249 + 20.250 + /** 20.251 + * Create a new null literal based on a parent node (source, token, finish) 20.252 + * 20.253 + * @param parent parent node 20.254 + * @param value undefined value 20.255 + * 20.256 + * @return the new literal node 20.257 + */ 20.258 + public static LiteralNode<?> newInstance(final Node parent, final Undefined value) { 20.259 + return new UndefinedLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish()); 20.260 + } 20.261 + 20.262 + private static class StringLiteralNode extends LiteralNode<String> { 20.263 + private StringLiteralNode(final Source source, final long token, final int finish, final String value) { 20.264 + super(source, Token.recast(token, TokenType.STRING), finish, value); 20.265 + } 20.266 + 20.267 + private StringLiteralNode(final StringLiteralNode literalNode) { 20.268 + super(literalNode); 20.269 + } 20.270 + 20.271 + @Override 20.272 + protected Node copy(final CopyState cs) { 20.273 + return new StringLiteralNode(this); 20.274 + } 20.275 + 20.276 + @Override 20.277 + public void toString(final StringBuilder sb) { 20.278 + sb.append('\"'); 20.279 + sb.append(value); 20.280 + sb.append('\"'); 20.281 + } 20.282 } 20.283 20.284 /** 20.285 @@ -363,19 +464,39 @@ 20.286 * @return the new literal node 20.287 */ 20.288 public static LiteralNode<String> newInstance(final Source source, final long token, final int finish, final String value) { 20.289 - return new LiteralNode<String>(source, token, finish, value) { 20.290 - @Override 20.291 - protected Node copy(final CopyState cs) { 20.292 - return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue()); 20.293 - } 20.294 + return new StringLiteralNode(source, token, finish, value); 20.295 + } 20.296 20.297 - @Override 20.298 - public void toString(final StringBuilder sb) { 20.299 - sb.append('\"'); 20.300 - sb.append(value); 20.301 - sb.append('\"'); 20.302 - } 20.303 - }; 20.304 + /** 20.305 + * Create a new String literal based on a parent node (source, token, finish) 20.306 + * 20.307 + * @param parent parent node 20.308 + * @param value string value 20.309 + * 20.310 + * @return the new literal node 20.311 + */ 20.312 + public static LiteralNode<?> newInstance(final Node parent, final String value) { 20.313 + return new StringLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value); 20.314 + } 20.315 + 20.316 + private static class LexerTokenLiteralNode extends LiteralNode<LexerToken> { 20.317 + private LexerTokenLiteralNode(final Source source, final long token, final int finish, final LexerToken value) { 20.318 + super(source, Token.recast(token, TokenType.STRING), finish, value); //TODO is string the correct token type here? 20.319 + } 20.320 + 20.321 + private LexerTokenLiteralNode(final LexerTokenLiteralNode literalNode) { 20.322 + super(literalNode); 20.323 + } 20.324 + 20.325 + @Override 20.326 + protected Node copy(final CopyState cs) { 20.327 + return new LexerTokenLiteralNode(this); 20.328 + } 20.329 + 20.330 + @Override 20.331 + public void toString(final StringBuilder sb) { 20.332 + sb.append(value.toString()); 20.333 + } 20.334 } 20.335 20.336 /** 20.337 @@ -389,22 +510,65 @@ 20.338 * @return the new literal node 20.339 */ 20.340 public static LiteralNode<LexerToken> newInstance(final Source source, final long token, final int finish, final LexerToken value) { 20.341 - return new LiteralNode<LexerToken>(source, token, finish, value) { 20.342 - @Override 20.343 - protected Node copy(final CopyState cs) { 20.344 - return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue()); 20.345 - } 20.346 - 20.347 - 20.348 - @Override 20.349 - public void toString(final StringBuilder sb) { 20.350 - sb.append(value.toString()); 20.351 - } 20.352 - }; 20.353 + return new LexerTokenLiteralNode(source, token, finish, value); 20.354 } 20.355 20.356 /** 20.357 - * Create a new array for an arbitrary node 20.358 + * Create a new lexer token literal based on a parent node (source, token, finish) 20.359 + * 20.360 + * @param parent parent node 20.361 + * @param value lexer token 20.362 + * 20.363 + * @return the new literal node 20.364 + */ 20.365 + public static LiteralNode<?> newInstance(final Node parent, final LexerToken value) { 20.366 + return new LexerTokenLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value); 20.367 + } 20.368 + 20.369 + private static class NodeLiteralNode extends LiteralNode<Node> { 20.370 + 20.371 + private NodeLiteralNode(final Source source, final long token, final int finish) { 20.372 + this(source, token, finish, null); 20.373 + } 20.374 + 20.375 + private NodeLiteralNode(final Source source, final long token, final int finish, final Node value) { 20.376 + super(source, Token.recast(token, TokenType.OBJECT), finish, value); 20.377 + } 20.378 + 20.379 + private NodeLiteralNode(final LiteralNode<Node> literalNode) { 20.380 + super(literalNode); 20.381 + } 20.382 + 20.383 + @Override 20.384 + protected Node copy(final CopyState cs) { 20.385 + return new NodeLiteralNode(this); 20.386 + } 20.387 + 20.388 + @Override 20.389 + public Node accept(final NodeVisitor visitor) { 20.390 + if (visitor.enter(this) != null) { 20.391 + if (value != null) { 20.392 + value = value.accept(visitor); 20.393 + } 20.394 + return visitor.leave(this); 20.395 + } 20.396 + 20.397 + return this; 20.398 + } 20.399 + 20.400 + @Override 20.401 + public Type getType() { 20.402 + return value == null ? Type.OBJECT : super.getType(); 20.403 + } 20.404 + 20.405 + @Override 20.406 + public Type getWidestOperationType() { 20.407 + return value == null ? Type.OBJECT : value.getWidestOperationType(); 20.408 + } 20.409 + 20.410 + } 20.411 + /** 20.412 + * Create a new node literal for an arbitrary node 20.413 * 20.414 * @param source the source 20.415 * @param token token 20.416 @@ -414,33 +578,19 @@ 20.417 * @return the new literal node 20.418 */ 20.419 public static LiteralNode<Node> newInstance(final Source source, final long token, final int finish, final Node value) { 20.420 - return new LiteralNode<Node>(source, token, finish, value) { 20.421 - @Override 20.422 - protected Node copy(final CopyState cs) { 20.423 - return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue()); 20.424 - } 20.425 + return new NodeLiteralNode(source, token, finish, value); 20.426 + } 20.427 20.428 - @Override 20.429 - public Node accept(final NodeVisitor visitor) { 20.430 - if (visitor.enter(this) != null) { 20.431 - value = value.accept(visitor); 20.432 - return visitor.leave(this); 20.433 - } 20.434 - 20.435 - return this; 20.436 - } 20.437 - 20.438 - @Override 20.439 - public void toString(final StringBuilder sb) { 20.440 - value.toString(sb); 20.441 - } 20.442 - 20.443 - @Override 20.444 - public Type getWidestOperationType() { 20.445 - return value.getWidestOperationType(); 20.446 - } 20.447 - 20.448 - }; 20.449 + /** 20.450 + * Create a new node literal based on a parent node (source, token, finish) 20.451 + * 20.452 + * @param parent parent node 20.453 + * @param value node value 20.454 + * 20.455 + * @return the new literal node 20.456 + */ 20.457 + public static LiteralNode<?> newInstance(final Node parent, final Node value) { 20.458 + return new NodeLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value); 20.459 } 20.460 20.461 /** 20.462 @@ -521,8 +671,7 @@ 20.463 * @param value array literal value, a Node array 20.464 */ 20.465 protected ArrayLiteralNode(final Source source, final long token, final int finish, final Node[] value) { 20.466 - super(source, token, finish, value); 20.467 - 20.468 + super(source, Token.recast(token, TokenType.ARRAY), finish, value); 20.469 this.elementType = Type.UNKNOWN; 20.470 } 20.471 20.472 @@ -530,13 +679,14 @@ 20.473 * Copy constructor 20.474 * @param node source array literal node 20.475 */ 20.476 - protected ArrayLiteralNode(final LiteralNode<Node[]> node) { 20.477 + protected ArrayLiteralNode(final ArrayLiteralNode node) { 20.478 super(node); 20.479 + this.elementType = node.elementType; 20.480 } 20.481 20.482 @Override 20.483 protected Node copy(final CopyState cs) { 20.484 - return LiteralNode.newInstance(getSource(), getToken(), getFinish(), getValue()); 20.485 + return new ArrayLiteralNode(this); 20.486 } 20.487 20.488 /** 20.489 @@ -769,6 +919,19 @@ 20.490 return new ArrayLiteralNode(source, token, finish, value.toArray(new Node[value.size()])); 20.491 } 20.492 20.493 + 20.494 + /** 20.495 + * Create a new array literal based on a parent node (source, token, finish) 20.496 + * 20.497 + * @param parent parent node 20.498 + * @param value literal value list 20.499 + * 20.500 + * @return the new literal node 20.501 + */ 20.502 + public static LiteralNode<?> newInstance(final Node parent, final List<Node> value) { 20.503 + return new ArrayLiteralNode(parent.getSource(), parent.getToken(), parent.getFinish(), value.toArray(new Node[value.size()])); 20.504 + } 20.505 + 20.506 /** 20.507 * Create a new array literal of Nodes 20.508 *
21.1 --- a/src/jdk/nashorn/internal/ir/Node.java Tue Jan 29 14:25:39 2013 -0400 21.2 +++ b/src/jdk/nashorn/internal/ir/Node.java Wed Jan 30 12:26:45 2013 +0100 21.3 @@ -26,7 +26,7 @@ 21.4 package jdk.nashorn.internal.ir; 21.5 21.6 import java.util.IdentityHashMap; 21.7 -import java.util.List; 21.8 + 21.9 import jdk.nashorn.internal.codegen.types.Type; 21.10 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 21.11 import jdk.nashorn.internal.parser.Token; 21.12 @@ -38,7 +38,7 @@ 21.13 */ 21.14 public abstract class Node extends Location { 21.15 /** Node symbol. */ 21.16 - private Symbol nodeSymbol; 21.17 + private Symbol symbol; 21.18 21.19 /** Start of source range. */ 21.20 protected int start; 21.21 @@ -68,7 +68,7 @@ 21.22 public Node(final Source source, final long token, final int finish) { 21.23 super(source, token); 21.24 21.25 - start = Token.descPosition(token); 21.26 + this.start = Token.descPosition(token); 21.27 this.finish = finish; 21.28 } 21.29 21.30 @@ -80,7 +80,7 @@ 21.31 protected Node(final Node node) { 21.32 super(node); 21.33 21.34 - this.nodeSymbol = node.nodeSymbol; 21.35 + this.symbol = node.symbol; 21.36 this.isResolved = node.isResolved; 21.37 this.isTerminal = node.isTerminal; 21.38 this.hasGoto = node.hasGoto; 21.39 @@ -107,8 +107,8 @@ 21.40 * @return the type of the node. 21.41 */ 21.42 public Type getType() { 21.43 - assert hasType(); 21.44 - return nodeSymbol.getSymbolType(); 21.45 + assert hasType() : this + " has no type"; 21.46 + return symbol.getSymbolType(); 21.47 } 21.48 21.49 /** 21.50 @@ -379,7 +379,7 @@ 21.51 * @return the symbol 21.52 */ 21.53 public Symbol getSymbol() { 21.54 - return nodeSymbol; 21.55 + return symbol; 21.56 } 21.57 21.58 /** 21.59 @@ -389,7 +389,7 @@ 21.60 * @param symbol the symbol 21.61 */ 21.62 public void setSymbol(final Symbol symbol) { 21.63 - nodeSymbol = symbol; 21.64 + this.symbol = symbol; 21.65 } 21.66 21.67 /** 21.68 @@ -412,21 +412,4 @@ 21.69 this.isTerminal = isTerminal; 21.70 } 21.71 21.72 - /** 21.73 - * Return last node in a statement list. 21.74 - * 21.75 - * @param statements Statement list. 21.76 - * 21.77 - * @return Last (non-debug) statement or null if empty block. 21.78 - */ 21.79 - public static Node lastStatement(final List<Node> statements) { 21.80 - for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) { 21.81 - final Node node = statements.get(lastIndex); 21.82 - if (!node.isDebug()) { 21.83 - return node; 21.84 - } 21.85 - } 21.86 - 21.87 - return null; 21.88 - } 21.89 }
22.1 --- a/src/jdk/nashorn/internal/ir/RuntimeNode.java Tue Jan 29 14:25:39 2013 -0400 22.2 +++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java Wed Jan 30 12:26:45 2013 +0100 22.3 @@ -257,6 +257,9 @@ 22.4 /** Call site override - e.g. we know that a ScriptRuntime.ADD will return an int */ 22.5 private Type callSiteType; 22.6 22.7 + /** is final - i.e. may not be removed again, lower in the code pipeline */ 22.8 + private boolean isFinal; 22.9 + 22.10 /** 22.11 * Constructor 22.12 * 22.13 @@ -286,6 +289,51 @@ 22.14 this(source, token, finish, request, Arrays.asList(args)); 22.15 } 22.16 22.17 + /** 22.18 + * Constructor 22.19 + * 22.20 + * @param parent parent node from which to inherit source, token, finish 22.21 + * @param request the request 22.22 + * @param args arguments to request 22.23 + */ 22.24 + public RuntimeNode(final Node parent, final Request request, final Node... args) { 22.25 + this(parent, request, Arrays.asList(args)); 22.26 + } 22.27 + 22.28 + /** 22.29 + * Constructor 22.30 + * 22.31 + * @param parent parent node from which to inherit source, token, finish 22.32 + * @param request the request 22.33 + * @param args arguments to request 22.34 + */ 22.35 + public RuntimeNode(final Node parent, final Request request, final List<Node> args) { 22.36 + super(parent); 22.37 + 22.38 + this.request = request; 22.39 + this.args = args; 22.40 + } 22.41 + 22.42 + /** 22.43 + * Constructor 22.44 + * 22.45 + * @param parent parent node from which to inherit source, token, finish and arguments 22.46 + * @param request the request 22.47 + */ 22.48 + public RuntimeNode(final UnaryNode parent, final Request request) { 22.49 + this(parent, request, parent.rhs()); 22.50 + } 22.51 + 22.52 + /** 22.53 + * Constructor 22.54 + * 22.55 + * @param parent parent node from which to inherit source, token, finish and arguments 22.56 + * @param request the request 22.57 + */ 22.58 + public RuntimeNode(final BinaryNode parent, final Request request) { 22.59 + this(parent, request, parent.lhs(), parent.rhs()); 22.60 + } 22.61 + 22.62 private RuntimeNode(final RuntimeNode runtimeNode, final CopyState cs) { 22.63 super(runtimeNode); 22.64 22.65 @@ -300,6 +348,21 @@ 22.66 this.callSiteType = runtimeNode.callSiteType; 22.67 } 22.68 22.69 + /** 22.70 + * Is this node final - i.e. it can never be replaced with other nodes again 22.71 + * @return true if final 22.72 + */ 22.73 + public boolean isFinal() { 22.74 + return isFinal; 22.75 + } 22.76 + 22.77 + /** 22.78 + * Flag this node as final - i.e it may never be replaced with other nodes again 22.79 + */ 22.80 + public void setIsFinal() { 22.81 + this.isFinal = true; 22.82 + } 22.83 + 22.84 @Override 22.85 protected Node copy(final CopyState cs) { 22.86 return new RuntimeNode(this, cs);
23.1 --- a/src/jdk/nashorn/internal/ir/Symbol.java Tue Jan 29 14:25:39 2013 -0400 23.2 +++ b/src/jdk/nashorn/internal/ir/Symbol.java Wed Jan 30 12:26:45 2013 +0100 23.3 @@ -26,6 +26,10 @@ 23.4 package jdk.nashorn.internal.ir; 23.5 23.6 import java.io.PrintWriter; 23.7 +import java.util.HashSet; 23.8 +import java.util.Set; 23.9 +import java.util.StringTokenizer; 23.10 + 23.11 import jdk.nashorn.internal.codegen.types.Type; 23.12 import jdk.nashorn.internal.runtime.Context; 23.13 import jdk.nashorn.internal.runtime.options.Options; 23.14 @@ -46,7 +50,7 @@ 23.15 /** Is this a constant */ 23.16 public static final int IS_CONSTANT = 0b0000_0101; 23.17 23.18 - static final int KINDMASK = 0b0000_1111; 23.19 + static final int KINDMASK = 0b0000_1111; 23.20 23.21 /** Is this scope */ 23.22 public static final int IS_SCOPE = 0b0000_0001_0000; 23.23 @@ -85,8 +89,33 @@ 23.24 /** Number of times this symbol is used in code */ 23.25 private int useCount; 23.26 23.27 - /** Debugging option - dump info and stack trace when a symbol with a given name is manipulated */ 23.28 - private static final String TRACE_SYMBOL = Options.getStringProperty("nashorn.compiler.symbol.trace", null); 23.29 + /** Debugging option - dump info and stack trace when symbols with given names are manipulated */ 23.30 + private static final Set<String> TRACE_SYMBOLS; 23.31 + private static final Set<String> TRACE_SYMBOLS_STACKTRACE; 23.32 + 23.33 + static { 23.34 + final String stacktrace = Options.getStringProperty("nashorn.compiler.symbol.stacktrace", null); 23.35 + final String trace; 23.36 + if (stacktrace != null) { 23.37 + trace = stacktrace; //stacktrace always implies trace as well 23.38 + TRACE_SYMBOLS_STACKTRACE = new HashSet<>(); 23.39 + for (StringTokenizer st = new StringTokenizer(stacktrace, ","); st.hasMoreTokens(); ) { 23.40 + TRACE_SYMBOLS_STACKTRACE.add(st.nextToken()); 23.41 + } 23.42 + } else { 23.43 + trace = Options.getStringProperty("nashorn.compiler.symbol.trace", null); 23.44 + TRACE_SYMBOLS_STACKTRACE = null; 23.45 + } 23.46 + 23.47 + if (trace != null) { 23.48 + TRACE_SYMBOLS = new HashSet<>(); 23.49 + for (StringTokenizer st = new StringTokenizer(trace, ","); st.hasMoreTokens(); ) { 23.50 + TRACE_SYMBOLS.add(st.nextToken()); 23.51 + } 23.52 + } else { 23.53 + TRACE_SYMBOLS = null; 23.54 + } 23.55 + } 23.56 23.57 /** 23.58 * Constructor 23.59 @@ -106,6 +135,7 @@ 23.60 this.type = type; 23.61 this.slot = slot; 23.62 this.fieldIndex = -1; 23.63 + trace("CREATE SYMBOL"); 23.64 } 23.65 23.66 /** 23.67 @@ -135,7 +165,7 @@ 23.68 final StringBuilder sb = new StringBuilder(); 23.69 sb.append(string.substring(0, Math.min(string.length(), max))); 23.70 23.71 - while (sb.length () < max) { 23.72 + while (sb.length() < max) { 23.73 sb.append(' '); 23.74 } 23.75 return sb.toString(); 23.76 @@ -263,6 +293,23 @@ 23.77 return name.hashCode() ^ block.hashCode(); 23.78 } 23.79 23.80 + private static String type(final String desc) { 23.81 + switch (desc.charAt(desc.length() - 1)) { 23.82 + case ';': 23.83 + return desc;//"obj"; 23.84 + case 'D': 23.85 + return "double"; 23.86 + case 'I': 23.87 + return "int"; 23.88 + case 'J': 23.89 + return "long"; 23.90 + case 'Z': 23.91 + return "boolean"; 23.92 + default: 23.93 + return "UNKNOWN"; 23.94 + } 23.95 + } 23.96 + 23.97 @Override 23.98 public String toString() { 23.99 final StringBuilder sb = new StringBuilder(); 23.100 @@ -270,8 +317,8 @@ 23.101 23.102 sb.append(name). 23.103 append(' '). 23.104 - append("(type="). 23.105 - append(desc.charAt(desc.length() - 1) == ';' ? 'O' : desc). 23.106 + append('('). 23.107 + append(type(desc)). 23.108 append(')'); 23.109 23.110 if (hasSlot()) { 23.111 @@ -602,10 +649,13 @@ 23.112 return block instanceof FunctionNode && ((FunctionNode) block).isScript(); 23.113 } 23.114 23.115 + 23.116 private void trace(final String desc) { 23.117 - if (TRACE_SYMBOL != null && TRACE_SYMBOL.equals(name)) { 23.118 + if (TRACE_SYMBOLS != null && (TRACE_SYMBOLS.isEmpty() || TRACE_SYMBOLS.contains(name))) { 23.119 Context.err("SYMBOL: '" + name + "' " + desc); 23.120 - new Throwable().printStackTrace(Context.getCurrentErr()); 23.121 + if (TRACE_SYMBOLS_STACKTRACE != null && (TRACE_SYMBOLS_STACKTRACE.isEmpty() || TRACE_SYMBOLS_STACKTRACE.contains(name))) { 23.122 + new Throwable().printStackTrace(Context.getContext().getErr()); 23.123 + } 23.124 } 23.125 } 23.126 }
24.1 --- a/src/jdk/nashorn/internal/ir/TryNode.java Tue Jan 29 14:25:39 2013 -0400 24.2 +++ b/src/jdk/nashorn/internal/ir/TryNode.java Wed Jan 30 12:26:45 2013 +0100 24.3 @@ -56,6 +56,9 @@ 24.4 /** Exception symbol. */ 24.5 private Symbol exception; 24.6 24.7 + /** Catchall exception for finally expansion, where applicable */ 24.8 + private Symbol finallyCatchAll; 24.9 + 24.10 /** 24.11 * Constructor 24.12 * 24.13 @@ -184,6 +187,23 @@ 24.14 } 24.15 24.16 /** 24.17 + * Get the catch all symbol for this try block 24.18 + * @return catch all symbol 24.19 + */ 24.20 + public Symbol getFinallyCatchAll() { 24.21 + return this.finallyCatchAll; 24.22 + } 24.23 + 24.24 + /** 24.25 + * If a finally block exists, the synthetic catchall needs another symbol to 24.26 + * store its throwable 24.27 + * @param finallyCatchAll a symbol for the finally catch all exception 24.28 + */ 24.29 + public void setFinallyCatchAll(final Symbol finallyCatchAll) { 24.30 + this.finallyCatchAll = finallyCatchAll; 24.31 + } 24.32 + 24.33 + /** 24.34 * Get the exit label for this try block 24.35 * @return exit label 24.36 */
25.1 --- a/src/jdk/nashorn/internal/ir/VarNode.java Tue Jan 29 14:25:39 2013 -0400 25.2 +++ b/src/jdk/nashorn/internal/ir/VarNode.java Wed Jan 30 12:26:45 2013 +0100 25.3 @@ -41,9 +41,6 @@ 25.4 /** Is this a function var node */ 25.5 private boolean isFunctionVarNode; 25.6 25.7 - /** Should append VarNode to statement list? */ 25.8 - private final boolean shouldAppend; 25.9 - 25.10 /** 25.11 * Constructor 25.12 * 25.13 @@ -54,25 +51,10 @@ 25.14 * @param init init node or null if just a declaration 25.15 */ 25.16 public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init) { 25.17 - this(source, token, finish, name, init, true); 25.18 - } 25.19 - 25.20 - /** 25.21 - * Constructor 25.22 - * 25.23 - * @param source the source 25.24 - * @param token token 25.25 - * @param finish finish 25.26 - * @param name name of variable 25.27 - * @param init init node or null if just a declaration 25.28 - * @param shouldAppend should this turn into explicit code, like if it were an ExecuteNode 25.29 - */ 25.30 - public VarNode(final Source source, final long token, final int finish, final IdentNode name, final Node init, final boolean shouldAppend) { 25.31 super(source, token, finish); 25.32 25.33 this.name = name; 25.34 this.init = init; 25.35 - this.shouldAppend = shouldAppend; 25.36 if (init != null) { 25.37 this.name.setIsInitializedHere(); 25.38 } 25.39 @@ -83,7 +65,6 @@ 25.40 25.41 this.name = (IdentNode)cs.existingOrCopy(varNode.name); 25.42 this.init = cs.existingOrCopy(varNode.init); 25.43 - this.shouldAppend = varNode.shouldAppend; 25.44 } 25.45 25.46 @Override 25.47 @@ -116,7 +97,6 @@ 25.48 setInit(source); 25.49 } 25.50 25.51 - 25.52 /** 25.53 * Does this variable declaration have an init value 25.54 * @return true if an init exists, false otherwise 25.55 @@ -235,15 +215,4 @@ 25.56 this.isFunctionVarNode = true; 25.57 } 25.58 25.59 - /** 25.60 - * Is this the var for a for-in node or other construct that means 25.61 - * manual or no appends of this varNode to the statement list in 25.62 - * Lower? The default is yes as most VarNodes are auto-append to 25.63 - * the end of the statement list when lowered 25.64 - * 25.65 - * @return should compiler append var node to statement list 25.66 - */ 25.67 - public boolean shouldAppend() { 25.68 - return shouldAppend; 25.69 - } 25.70 }
26.1 --- a/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Tue Jan 29 14:25:39 2013 -0400 26.2 +++ b/src/jdk/nashorn/internal/ir/debug/ASTWriter.java Wed Jan 30 12:26:45 2013 +0100 26.3 @@ -113,8 +113,8 @@ 26.4 status += " Goto "; 26.5 } 26.6 26.7 - if (node.getSymbol() != null && node.getSymbol().hasSlot()) { 26.8 - status += " Slot " + node.getSymbol(); 26.9 + if (node.getSymbol() != null) { 26.10 + status += node.getSymbol(); 26.11 } 26.12 26.13 status = status.trim();
27.1 --- a/src/jdk/nashorn/internal/objects/NativeJSON.java Tue Jan 29 14:25:39 2013 -0400 27.2 +++ b/src/jdk/nashorn/internal/objects/NativeJSON.java Wed Jan 30 12:26:45 2013 +0100 27.3 @@ -265,7 +265,7 @@ 27.4 27.5 if (node instanceof LiteralNode) { 27.6 // check for array literal 27.7 - if (node.tokenType() == TokenType.LBRACKET) { 27.8 + if (node.tokenType() == TokenType.ARRAY) { 27.9 assert node instanceof ArrayLiteralNode; 27.10 final Node[] elements = ((ArrayLiteralNode)node).getValue(); 27.11
28.1 --- a/src/jdk/nashorn/internal/objects/NativeString.java Tue Jan 29 14:25:39 2013 -0400 28.2 +++ b/src/jdk/nashorn/internal/objects/NativeString.java Wed Jan 30 12:26:45 2013 +0100 28.3 @@ -789,6 +789,7 @@ 28.4 * 28.5 * @param self self reference 28.6 * @param start start position for slice 28.7 + * @param end end position for slice 28.8 * @return sliced out substring 28.9 */ 28.10 @SpecializedFunction 28.11 @@ -808,6 +809,7 @@ 28.12 * 28.13 * @param self self reference 28.14 * @param start start position for slice 28.15 + * @param end end position for slice 28.16 * @return sliced out substring 28.17 */ 28.18 @SpecializedFunction 28.19 @@ -843,9 +845,8 @@ 28.20 } 28.21 28.22 private static Object splitString(String str, String separator, long limit) { 28.23 - 28.24 - if (separator.equals("")) { 28.25 - Object[] array = new Object[str.length()]; 28.26 + if (separator.isEmpty()) { 28.27 + final Object[] array = new Object[str.length()]; 28.28 for (int i = 0; i < array.length; i++) { 28.29 array[i] = String.valueOf(str.charAt(i)); 28.30 } 28.31 @@ -856,18 +857,18 @@ 28.32 final int strLength = str.length(); 28.33 final int sepLength = separator.length(); 28.34 int pos = 0; 28.35 - int count = 0; 28.36 + int n = 0; 28.37 28.38 - while (pos < strLength && count < limit) { 28.39 + while (pos < strLength && n < limit) { 28.40 int found = str.indexOf(separator, pos); 28.41 if (found == -1) { 28.42 break; 28.43 } 28.44 elements.add(str.substring(pos, found)); 28.45 - count++; 28.46 + n++; 28.47 pos = found + sepLength; 28.48 } 28.49 - if (pos <= strLength && count < limit) { 28.50 + if (pos <= strLength && n < limit) { 28.51 elements.add(str.substring(pos)); 28.52 } 28.53 28.54 @@ -963,9 +964,8 @@ 28.55 28.56 if (validStart < validEnd) { 28.57 return str.substring(validStart, validEnd); 28.58 - } else { 28.59 - return str.substring(validEnd, validStart); 28.60 } 28.61 + return str.substring(validEnd, validStart); 28.62 } 28.63 28.64 /**
29.1 --- a/src/jdk/nashorn/internal/parser/AbstractParser.java Tue Jan 29 14:25:39 2013 -0400 29.2 +++ b/src/jdk/nashorn/internal/parser/AbstractParser.java Wed Jan 30 12:26:45 2013 +0100 29.3 @@ -96,11 +96,6 @@ 29.4 this.token = Token.toDesc(EOL, 0, 1); 29.5 this.type = EOL; 29.6 this.last = EOL; 29.7 - this.start = 0; 29.8 - this.finish = 0; 29.9 - this.line = 0; 29.10 - this.linePosition = 0; 29.11 - this.lexer = null; 29.12 this.isStrictMode = strict; 29.13 } 29.14
30.1 --- a/src/jdk/nashorn/internal/parser/Parser.java Tue Jan 29 14:25:39 2013 -0400 30.2 +++ b/src/jdk/nashorn/internal/parser/Parser.java Wed Jan 30 12:26:45 2013 +0100 30.3 @@ -587,8 +587,6 @@ 30.4 script.setLastToken(token); 30.5 script.setFinish(source.getLength() - 1); 30.6 30.7 - block.addStatement(lineNumber()); 30.8 - 30.9 return script; 30.10 } 30.11 30.12 @@ -617,6 +615,24 @@ 30.13 } 30.14 30.15 /** 30.16 + * Return last node in a statement list. 30.17 + * 30.18 + * @param statements Statement list. 30.19 + * 30.20 + * @return Last (non-debug) statement or null if empty block. 30.21 + */ 30.22 + private static Node lastStatement(final List<Node> statements) { 30.23 + for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) { 30.24 + final Node node = statements.get(lastIndex); 30.25 + if (!node.isDebug()) { 30.26 + return node; 30.27 + } 30.28 + } 30.29 + 30.30 + return null; 30.31 + } 30.32 + 30.33 + /** 30.34 * SourceElements : 30.35 * SourceElement 30.36 * SourceElements SourceElement 30.37 @@ -645,7 +661,7 @@ 30.38 // check for directive prologues 30.39 if (checkDirective) { 30.40 // skip any debug statement like line number to get actual first line 30.41 - final Node lastStatement = Node.lastStatement(block.getStatements()); 30.42 + final Node lastStatement = lastStatement(block.getStatements()); 30.43 30.44 // get directive prologue, if any 30.45 final String directive = getDirective(lastStatement); 30.46 @@ -677,7 +693,7 @@ 30.47 } 30.48 30.49 // verify that function name as well as parameter names 30.50 - // satisfystrict mode restrictions. 30.51 + // satisfy strict mode restrictions. 30.52 verifyStrictIdent(function.getIdent(), "function name"); 30.53 for (final IdentNode param : function.getParameters()) { 30.54 verifyStrictIdent(param, "function parameter"); 30.55 @@ -2628,12 +2644,12 @@ 30.56 // just expression as function body 30.57 final Node expr = expression(); 30.58 30.59 - // create a return statement 30.60 - final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null); 30.61 - final ExecuteNode executeNode = new ExecuteNode(source, returnNode.getToken(), finish, returnNode); 30.62 + // create a return statement - this creates code in itself and does not need to be 30.63 + // wrapped into an ExecuteNode 30.64 + final ReturnNode returnNode = new ReturnNode(source, expr.getToken(), finish, expr, null); 30.65 30.66 // add the return statement 30.67 - functionNode.addStatement(executeNode); 30.68 + functionNode.addStatement(returnNode); 30.69 functionNode.setLastToken(token); 30.70 functionNode.setFinish(Token.descPosition(token) + Token.descLength(token)); 30.71 30.72 @@ -2648,8 +2664,6 @@ 30.73 functionNode.setFinish(finish); 30.74 30.75 } 30.76 - 30.77 - block.addStatement(lineNumber()); 30.78 } finally { 30.79 restoreBlock(); 30.80 }
31.1 --- a/src/jdk/nashorn/internal/parser/TokenType.java Tue Jan 29 14:25:39 2013 -0400 31.2 +++ b/src/jdk/nashorn/internal/parser/TokenType.java Wed Jan 30 12:26:45 2013 +0100 31.3 @@ -171,6 +171,8 @@ 31.4 IDENT (LITERAL, null), 31.5 REGEX (LITERAL, null), 31.6 XML (LITERAL, null), 31.7 + OBJECT (LITERAL, null), 31.8 + ARRAY (LITERAL, null), 31.9 31.10 COMMALEFT (IR, null), 31.11 CONVERT (IR, null),
32.1 --- a/src/jdk/nashorn/internal/runtime/Context.java Tue Jan 29 14:25:39 2013 -0400 32.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java Wed Jan 30 12:26:45 2013 +0100 32.3 @@ -478,11 +478,11 @@ 32.4 return _timezone; 32.5 } 32.6 32.7 - /* 32.8 + /** 32.9 * Get the PropertyMap of the current global scope 32.10 * @return the property map of the current global scope 32.11 */ 32.12 - public PropertyMap getGlobalMap() { 32.13 + public static PropertyMap getGlobalMap() { 32.14 return Context.getGlobalTrusted().getMap(); 32.15 } 32.16
33.1 --- a/src/jdk/nashorn/internal/runtime/DebugLogger.java Tue Jan 29 14:25:39 2013 -0400 33.2 +++ b/src/jdk/nashorn/internal/runtime/DebugLogger.java Wed Jan 30 12:26:45 2013 +0100 33.3 @@ -41,6 +41,8 @@ 33.4 33.5 private int indent; 33.6 33.7 + private static final int INDENT_SPACE = 4; 33.8 + 33.9 /** 33.10 * Constructor 33.11 * 33.12 @@ -93,7 +95,24 @@ 33.13 */ 33.14 public void indent(final int pos) { 33.15 if (isEnabled) { 33.16 - indent += pos * 4; 33.17 + indent += pos * INDENT_SPACE; 33.18 + } 33.19 + } 33.20 + 33.21 + /** 33.22 + * Add an indent position 33.23 + */ 33.24 + public void indent() { 33.25 + indent += INDENT_SPACE; 33.26 + } 33.27 + 33.28 + /** 33.29 + * Unindent a position 33.30 + */ 33.31 + public void unindent() { 33.32 + indent -= INDENT_SPACE; 33.33 + if (indent < 0) { 33.34 + indent = 0; 33.35 } 33.36 } 33.37
34.1 --- a/src/jdk/nashorn/internal/runtime/OptionsObject.java Tue Jan 29 14:25:39 2013 -0400 34.2 +++ b/src/jdk/nashorn/internal/runtime/OptionsObject.java Wed Jan 30 12:26:45 2013 +0100 34.3 @@ -106,6 +106,11 @@ 34.4 /** time zone for this context */ 34.5 public final TimeZone _timezone; 34.6 34.7 + /** 34.8 + * Constructor 34.9 + * 34.10 + * @param context a context 34.11 + */ 34.12 public OptionsObject(final Context context) { 34.13 this._anon_functions = context._anon_functions; 34.14 this._callsite_flags = context._callsite_flags;
35.1 --- a/src/jdk/nashorn/internal/runtime/PropertyMap.java Tue Jan 29 14:25:39 2013 -0400 35.2 +++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java Wed Jan 30 12:26:45 2013 +0100 35.3 @@ -635,7 +635,13 @@ 35.4 35.5 sb.append(ScriptRuntime.safeToString(property.getKey())); 35.6 final Class<?> ctype = property.getCurrentType(); 35.7 - sb.append(" <" + property.getClass().getSimpleName() + ":" + (ctype == null ? "undefined" : ctype.getSimpleName()) + ">"); 35.8 + sb.append(" <"). 35.9 + append(property.getClass().getSimpleName()). 35.10 + append(':'). 35.11 + append(ctype == null ? 35.12 + "undefined" : 35.13 + ctype.getSimpleName()). 35.14 + append('>'); 35.15 } 35.16 35.17 sb.append(']');
36.1 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Jan 29 14:25:39 2013 -0400 36.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Jan 30 12:26:45 2013 +0100 36.3 @@ -1044,6 +1044,10 @@ 36.4 set(key, value, getContext()._strict); 36.5 } 36.6 36.7 + /** 36.8 + * Return true if the script object context is strict 36.9 + * @return true if strict context 36.10 + */ 36.11 public final boolean isStrictContext() { 36.12 return getContext()._strict; 36.13 } 36.14 @@ -1419,7 +1423,10 @@ 36.15 return (flags & IS_SCOPE) != 0; 36.16 } 36.17 36.18 - // java.util.Map-like methods to help ScriptObjectMirror implementation 36.19 + /** 36.20 + * Clears the properties from a ScriptObject 36.21 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.22 + */ 36.23 public void clear() { 36.24 final boolean strict = getContext()._strict; 36.25 final Iterator<String> iter = propertyIterator(); 36.26 @@ -1428,10 +1435,24 @@ 36.27 } 36.28 } 36.29 36.30 + /** 36.31 + * Checks if a property with a given key is present in a ScriptObject 36.32 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.33 + * 36.34 + * @param key the key to check for 36.35 + * @return true if a property with the given key exists, false otherwise 36.36 + */ 36.37 public boolean containsKey(final Object key) { 36.38 return has(key); 36.39 } 36.40 36.41 + /** 36.42 + * Checks if a property with a given value is present in a ScriptObject 36.43 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.44 + * 36.45 + * @param value value to check for 36.46 + * @return true if a property with the given value exists, false otherwise 36.47 + */ 36.48 public boolean containsValue(final Object value) { 36.49 final Iterator<Object> iter = valueIterator(); 36.50 while (iter.hasNext()) { 36.51 @@ -1442,6 +1463,13 @@ 36.52 return false; 36.53 } 36.54 36.55 + /** 36.56 + * Returns the set of <property, value> entries that make up this 36.57 + * ScriptObject's properties 36.58 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.59 + * 36.60 + * @return an entry set of all the properties in this object 36.61 + */ 36.62 public Set<Map.Entry<Object, Object>> entrySet() { 36.63 final Iterator<String> iter = propertyIterator(); 36.64 final Set<Map.Entry<Object, Object>> entries = new HashSet<>(); 36.65 @@ -1452,10 +1480,23 @@ 36.66 return Collections.unmodifiableSet(entries); 36.67 } 36.68 36.69 + /** 36.70 + * Check whether a ScriptObject contains no properties 36.71 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.72 + * 36.73 + * @return true if object has no properties 36.74 + */ 36.75 public boolean isEmpty() { 36.76 return !propertyIterator().hasNext(); 36.77 } 36.78 36.79 + /** 36.80 + * Return the set of keys (property names) for all properties 36.81 + * in this ScriptObject 36.82 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.83 + * 36.84 + * @return keySet of this ScriptObject 36.85 + */ 36.86 public Set<Object> keySet() { 36.87 final Iterator<String> iter = propertyIterator(); 36.88 final Set<Object> keySet = new HashSet<>(); 36.89 @@ -1465,12 +1506,27 @@ 36.90 return Collections.unmodifiableSet(keySet); 36.91 } 36.92 36.93 + /** 36.94 + * Put a property in the ScriptObject 36.95 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.96 + * 36.97 + * @param key property key 36.98 + * @param value property value 36.99 + * @return oldValue if property with same key existed already 36.100 + */ 36.101 public Object put(final Object key, final Object value) { 36.102 final Object oldValue = get(key); 36.103 set(key, value, getContext()._strict); 36.104 return oldValue; 36.105 } 36.106 36.107 + /** 36.108 + * Put several properties in the ScriptObject given a mapping 36.109 + * of their keys to their values 36.110 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.111 + * 36.112 + * @param otherMap a <key,value> map of properties to add 36.113 + */ 36.114 public void putAll(final Map<?, ?> otherMap) { 36.115 final boolean strict = getContext()._strict; 36.116 for (final Map.Entry<?, ?> entry : otherMap.entrySet()) { 36.117 @@ -1478,12 +1534,26 @@ 36.118 } 36.119 } 36.120 36.121 + /** 36.122 + * Remove a property from the ScriptObject. 36.123 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.124 + * 36.125 + * @param key the key of the property 36.126 + * @return the oldValue of the removed property 36.127 + */ 36.128 public Object remove(final Object key) { 36.129 final Object oldValue = get(key); 36.130 delete(key, getContext()._strict); 36.131 return oldValue; 36.132 } 36.133 36.134 + /** 36.135 + * Return the size of the ScriptObject - i.e. the number of properties 36.136 + * it contains 36.137 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.138 + * 36.139 + * @return number of properties in ScriptObject 36.140 + */ 36.141 public int size() { 36.142 int n = 0; 36.143 for (final Iterator<String> iter = propertyIterator(); iter.hasNext(); iter.next()) { 36.144 @@ -1492,6 +1562,12 @@ 36.145 return n; 36.146 } 36.147 36.148 + /** 36.149 + * Return the values of the properties in the ScriptObject 36.150 + * (java.util.Map-like method to help ScriptObjectMirror implementation) 36.151 + * 36.152 + * @return collection of values for the properties in this ScriptObject 36.153 + */ 36.154 public Collection<Object> values() { 36.155 final List<Object> values = new ArrayList<>(size()); 36.156 final Iterator<Object> iter = valueIterator();
37.1 --- a/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Tue Jan 29 14:25:39 2013 -0400 37.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptingFunctions.java Wed Jan 30 12:26:45 2013 +0100 37.3 @@ -41,13 +41,13 @@ 37.4 */ 37.5 public class ScriptingFunctions { 37.6 37.7 - /** Handle to implementation of {@link ScriptingFunctions#read} - Nashorn extension */ 37.8 + /** Handle to implementation of {@link ScriptingFunctions#readLine} - Nashorn extension */ 37.9 public static final MethodHandle READLINE = findOwnMH("readLine", Object.class, Object.class); 37.10 37.11 /** Handle to implementation of {@link ScriptingFunctions#readFully} - Nashorn extension */ 37.12 public static final MethodHandle READFULLY = findOwnMH("readFully", Object.class, Object.class, Object.class); 37.13 37.14 - /** Handle to implementation of {@link ScriptingFunctions#read} - Nashorn extension */ 37.15 + /** Handle to implementation of {@link ScriptingFunctions#quit} - Nashorn extension */ 37.16 public static final MethodHandle QUIT = findOwnMH("quit", Object.class, Object.class, Object.class); 37.17 37.18 private ScriptingFunctions() {
38.1 --- a/src/jdk/nashorn/internal/runtime/arrays/ArrayIterator.java Tue Jan 29 14:25:39 2013 -0400 38.2 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayIterator.java Wed Jan 30 12:26:45 2013 +0100 38.3 @@ -25,7 +25,6 @@ 38.4 38.5 package jdk.nashorn.internal.runtime.arrays; 38.6 38.7 -import jdk.nashorn.internal.runtime.Context; 38.8 import jdk.nashorn.internal.runtime.ScriptObject; 38.9 38.10 /**
39.1 --- a/src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java Tue Jan 29 14:25:39 2013 -0400 39.2 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayLikeIterator.java Wed Jan 30 12:26:45 2013 +0100 39.3 @@ -26,7 +26,6 @@ 39.4 package jdk.nashorn.internal.runtime.arrays; 39.5 39.6 import java.util.Iterator; 39.7 -import jdk.nashorn.internal.runtime.Context; 39.8 import jdk.nashorn.internal.runtime.JSType; 39.9 import jdk.nashorn.internal.runtime.ScriptObject; 39.10
40.1 --- a/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java Tue Jan 29 14:25:39 2013 -0400 40.2 +++ b/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java Wed Jan 30 12:26:45 2013 +0100 40.3 @@ -27,7 +27,6 @@ 40.4 40.5 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 40.6 40.7 -import jdk.nashorn.internal.runtime.Context; 40.8 import jdk.nashorn.internal.runtime.GlobalObject; 40.9 import jdk.nashorn.internal.runtime.PropertyDescriptor; 40.10
41.1 --- a/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java Tue Jan 29 14:25:39 2013 -0400 41.2 +++ b/src/jdk/nashorn/internal/runtime/arrays/SealedArrayFilter.java Wed Jan 30 12:26:45 2013 +0100 41.3 @@ -27,7 +27,6 @@ 41.4 41.5 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 41.6 41.7 -import jdk.nashorn.internal.runtime.Context; 41.8 import jdk.nashorn.internal.runtime.GlobalObject; 41.9 import jdk.nashorn.internal.runtime.PropertyDescriptor; 41.10
42.1 --- a/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Tue Jan 29 14:25:39 2013 -0400 42.2 +++ b/src/jdk/nashorn/internal/runtime/linker/LinkerCallSite.java Wed Jan 30 12:26:45 2013 +0100 42.3 @@ -446,7 +446,7 @@ 42.4 * 42.5 * @throws Throwable if invocation fails or throws exception/error 42.6 */ 42.7 - @SuppressWarnings("unused") 42.8 + @SuppressWarnings({"unused", "resource"}) 42.9 public Object traceObject(final MethodHandle mh, final Object... args) throws Throwable { 42.10 final PrintWriter out = Context.getCurrentErr(); 42.11 tracePrint(out, "ENTER ", args, null); 42.12 @@ -464,7 +464,7 @@ 42.13 * 42.14 * @throws Throwable if invocation fails or throws exception/error 42.15 */ 42.16 - @SuppressWarnings("unused") 42.17 + @SuppressWarnings({"unused", "resource"}) 42.18 public void traceVoid(final MethodHandle mh, final Object... args) throws Throwable { 42.19 final PrintWriter out = Context.getCurrentErr(); 42.20 tracePrint(out, "ENTER ", args, null);
43.1 --- a/src/jdk/nashorn/internal/runtime/options/Options.java Tue Jan 29 14:25:39 2013 -0400 43.2 +++ b/src/jdk/nashorn/internal/runtime/options/Options.java Wed Jan 30 12:26:45 2013 +0100 43.3 @@ -416,7 +416,7 @@ 43.4 System.setProperty(value.substring(0, eq), value.substring(eq + 1)); 43.5 } else { 43.6 // -Dfoo is fine. Set System property "foo" with "" as it's value 43.7 - if (!value.equals("")) { 43.8 + if (!value.isEmpty()) { 43.9 System.setProperty(value, ""); 43.10 } else { 43.11 // do not allow empty property name
44.1 --- a/src/jdk/nashorn/tools/Shell.java Tue Jan 29 14:25:39 2013 -0400 44.2 +++ b/src/jdk/nashorn/tools/Shell.java Wed Jan 30 12:26:45 2013 +0100 44.3 @@ -178,7 +178,7 @@ 44.4 * @return null if there are problems with option parsing. 44.5 */ 44.6 @SuppressWarnings("resource") 44.7 - private Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) { 44.8 + private static Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) { 44.9 final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out); 44.10 final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : new PrintStream(err); 44.11 final PrintWriter wout = new PrintWriter(pout, true); 44.12 @@ -230,7 +230,7 @@ 44.13 * @return error code 44.14 * @throws IOException when any script file read results in I/O error 44.15 */ 44.16 - private int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException { 44.17 + private static int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException { 44.18 final ScriptObject oldGlobal = Context.getGlobal(); 44.19 final boolean globalChanged = (oldGlobal != global); 44.20 try { 44.21 @@ -330,7 +330,7 @@ 44.22 * @return return code 44.23 */ 44.24 @SuppressWarnings("resource") 44.25 - private int readEvalPrint(final Context context, final ScriptObject global) { 44.26 + private static int readEvalPrint(final Context context, final ScriptObject global) { 44.27 final String prompt = bundle.getString("shell.prompt"); 44.28 final BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 44.29 final PrintWriter err = context.getErr();
45.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 45.2 +++ b/test/script/basic/access-specializer.js Wed Jan 30 12:26:45 2013 +0100 45.3 @@ -0,0 +1,43 @@ 45.4 +/* 45.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 45.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 45.7 + * 45.8 + * This code is free software; you can redistribute it and/or modify it 45.9 + * under the terms of the GNU General Public License version 2 only, as 45.10 + * published by the Free Software Foundation. 45.11 + * 45.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 45.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 45.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 45.15 + * version 2 for more details (a copy is included in the LICENSE file that 45.16 + * accompanied this code). 45.17 + * 45.18 + * You should have received a copy of the GNU General Public License version 45.19 + * 2 along with this work; if not, write to the Free Software Foundation, 45.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 45.21 + * 45.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 45.23 + * or visit www.oracle.com if you need additional information or have any 45.24 + * questions. 45.25 + */ 45.26 + 45.27 +/** 45.28 + * This is a simple test that checks that access specialization in FinalizeTypes is consistent. 45.29 + * Here, a2 = 0 will be turned int {I}a2 = 0, and all would be fine and well, only we can't change 45.30 + * the symbol type for a2 from double, and we can't as it's not a temporary. Either we have to put 45.31 + * a temporary in at the late finalize stage and add another assignment, or we genericize the check 45.32 + * in CodeGenerator#Store so we detect whether a target is of the wrong type before storing. It 45.33 + * is hopefully very rare, and will only be a problem when assignment results that have been 45.34 + * specialized live on the stack 45.35 + * 45.36 + * @test 45.37 + * @run 45.38 + */ 45.39 + 45.40 +function f() { 45.41 + var a0 = a1 = a2 = 0; 45.42 + a0 = 16.1; 45.43 + a1 = 17.1; 45.44 + a2 = 18.1; 45.45 +} 45.46 +f();
46.1 --- a/test/script/basic/compile-octane.js.EXPECTED Tue Jan 29 14:25:39 2013 -0400 46.2 +++ b/test/script/basic/compile-octane.js.EXPECTED Wed Jan 30 12:26:45 2013 +0100 46.3 @@ -1,12 +1,36 @@ 46.4 +Compiling... box2d.js 46.5 Compiled OK: box2d.js 46.6 + 46.7 +Compiling... code-load.js 46.8 Compiled OK: code-load.js 46.9 + 46.10 +Compiling... crypto.js 46.11 Compiled OK: crypto.js 46.12 + 46.13 +Compiling... deltablue.js 46.14 Compiled OK: deltablue.js 46.15 + 46.16 +Compiling... earley-boyer.js 46.17 Compiled OK: earley-boyer.js 46.18 + 46.19 +Compiling... gbemu.js 46.20 Compiled OK: gbemu.js 46.21 + 46.22 +Compiling... navier-stokes.js 46.23 Compiled OK: navier-stokes.js 46.24 + 46.25 +Compiling... pdfjs.js 46.26 Compiled OK: pdfjs.js 46.27 + 46.28 +Compiling... raytrace.js 46.29 Compiled OK: raytrace.js 46.30 + 46.31 +Compiling... regexp.js 46.32 Compiled OK: regexp.js 46.33 + 46.34 +Compiling... richards.js 46.35 Compiled OK: richards.js 46.36 + 46.37 +Compiling... splay.js 46.38 Compiled OK: splay.js 46.39 +
47.1 --- a/test/script/basic/run-octane.js Tue Jan 29 14:25:39 2013 -0400 47.2 +++ b/test/script/basic/run-octane.js Wed Jan 30 12:26:45 2013 +0100 47.3 @@ -65,11 +65,7 @@ 47.4 47.5 function run_one_benchmark(arg, iters) { 47.6 47.7 - load(path + 'base.js'); 47.8 - load(arg); 47.9 - 47.10 var file_name; 47.11 - 47.12 var file = arg.split('/'); 47.13 if (file.length == 1) { 47.14 file = arg.split('\\'); 47.15 @@ -80,9 +76,17 @@ 47.16 file.pop(); 47.17 } 47.18 file_name = file[file.length - 1]; 47.19 + 47.20 + if (typeof compile_only !== 'undefined') { 47.21 + print("Compiling... " + file_name); 47.22 + } 47.23 + 47.24 + load(path + 'base.js'); 47.25 + load(arg); 47.26 47.27 if (typeof compile_only !== 'undefined') { 47.28 print("Compiled OK: " + file_name); 47.29 + print(""); 47.30 return; 47.31 } 47.32
48.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 48.2 +++ b/test/script/basic/typecoerce.js Wed Jan 30 12:26:45 2013 +0100 48.3 @@ -0,0 +1,53 @@ 48.4 +/* 48.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 48.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 48.7 + * 48.8 + * This code is free software; you can redistribute it and/or modify it 48.9 + * under the terms of the GNU General Public License version 2 only, as 48.10 + * published by the Free Software Foundation. 48.11 + * 48.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 48.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 48.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 48.15 + * version 2 for more details (a copy is included in the LICENSE file that 48.16 + * accompanied this code). 48.17 + * 48.18 + * You should have received a copy of the GNU General Public License version 48.19 + * 2 along with this work; if not, write to the Free Software Foundation, 48.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 48.21 + * 48.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 48.23 + * or visit www.oracle.com if you need additional information or have any 48.24 + * questions. 48.25 + */ 48.26 + 48.27 +/** 48.28 + * There was a bug in the old Lower that didn't fully propagate type information 48.29 + * by way of assignment. 'q' in the example below would have finished a double 48.30 + * even though it can get an object value through the assignment 'q = l' 48.31 + * 48.32 + * Furthermore, this caused type coercion to be done at q = l, and not a q = q * 2, 48.33 + * which is a bug. This test ensures it happens in the correct order 48.34 + * 48.35 + * @test 48.36 + * @run 48.37 + */ 48.38 + 48.39 +function createObject() { 48.40 + var obj = { valueOf: function() { print("toNumber coercion"); return 17; }} 48.41 + return obj; 48.42 +} 48.43 + 48.44 +function f() { 48.45 + var l = 1.2; //number 48.46 + var q = 2.3; //number 48.47 + for (var i = 0; i < 2; i++) { 48.48 + q = l; // q = toNumber(l), no coercion here 48.49 + print("assignment done"); 48.50 + q = q * 2; // q = q * 2, coercion here 48.51 + print("multiplication done"); 48.52 + l = createObject(); 48.53 + } 48.54 +} 48.55 + 48.56 +f();
49.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 49.2 +++ b/test/script/basic/typecoerce.js.EXPECTED Wed Jan 30 12:26:45 2013 +0100 49.3 @@ -0,0 +1,5 @@ 49.4 +assignment done 49.5 +multiplication done 49.6 +assignment done 49.7 +toNumber coercion 49.8 +multiplication done