src/jdk/nashorn/internal/codegen/CodeGenerator.java

changeset 605
03a68e7ca1d5
parent 602
8d29733ad609
child 622
64e841576c68
equal deleted inserted replaced
604:ec3094d9d5d5 605:03a68e7ca1d5
41 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS; 41 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
42 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; 42 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
43 import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup; 43 import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
44 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; 44 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
45 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 45 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
46 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
47 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; 46 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
48 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 47 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
49 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL; 48 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
50 import static jdk.nashorn.internal.ir.Symbol.IS_TEMP; 49 import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
51 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE; 50 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
58 import java.util.EnumSet; 57 import java.util.EnumSet;
59 import java.util.HashSet; 58 import java.util.HashSet;
60 import java.util.Iterator; 59 import java.util.Iterator;
61 import java.util.LinkedList; 60 import java.util.LinkedList;
62 import java.util.List; 61 import java.util.List;
63 import java.util.Locale;
64 import java.util.Set; 62 import java.util.Set;
65 import java.util.TreeMap; 63 import java.util.TreeMap;
66 import jdk.nashorn.internal.codegen.ClassEmitter.Flag; 64 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
67 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 65 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
68 import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode; 66 import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode;
109 import jdk.nashorn.internal.ir.TryNode; 107 import jdk.nashorn.internal.ir.TryNode;
110 import jdk.nashorn.internal.ir.UnaryNode; 108 import jdk.nashorn.internal.ir.UnaryNode;
111 import jdk.nashorn.internal.ir.VarNode; 109 import jdk.nashorn.internal.ir.VarNode;
112 import jdk.nashorn.internal.ir.WhileNode; 110 import jdk.nashorn.internal.ir.WhileNode;
113 import jdk.nashorn.internal.ir.WithNode; 111 import jdk.nashorn.internal.ir.WithNode;
114 import jdk.nashorn.internal.ir.debug.ASTWriter;
115 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; 112 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
116 import jdk.nashorn.internal.ir.visitor.NodeVisitor; 113 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
117 import jdk.nashorn.internal.objects.Global; 114 import jdk.nashorn.internal.objects.Global;
118 import jdk.nashorn.internal.objects.ScriptFunctionImpl; 115 import jdk.nashorn.internal.objects.ScriptFunctionImpl;
119 import jdk.nashorn.internal.parser.Lexer.RegexToken; 116 import jdk.nashorn.internal.parser.Lexer.RegexToken;
215 * Load an identity node 212 * Load an identity node
216 * 213 *
217 * @param identNode an identity node to load 214 * @param identNode an identity node to load
218 * @return the method generator used 215 * @return the method generator used
219 */ 216 */
220 private MethodEmitter loadIdent(final IdentNode identNode) { 217 private MethodEmitter loadIdent(final IdentNode identNode, final Type type) {
221 final Symbol symbol = identNode.getSymbol(); 218 final Symbol symbol = identNode.getSymbol();
222 219
223 if (!symbol.isScope()) { 220 if (!symbol.isScope()) {
224 assert symbol.hasSlot() || symbol.isParam(); 221 assert symbol.hasSlot() || symbol.isParam();
225 return method.load(symbol); 222 return method.load(symbol).convert(type);
226 } 223 }
227 224
228 final String name = symbol.getName(); 225 final String name = symbol.getName();
229 final Source source = lc.getCurrentFunction().getSource(); 226 final Source source = lc.getCurrentFunction().getSource();
230 227
241 method.loadCompilerConstant(SCOPE); 238 method.loadCompilerConstant(SCOPE);
242 239
243 if (isFastScope(symbol)) { 240 if (isFastScope(symbol)) {
244 // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope. 241 // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
245 if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) { 242 if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) {
246 return loadSharedScopeVar(identNode.getType(), symbol, flags); 243 return loadSharedScopeVar(type, symbol, flags);
247 } 244 }
248 return loadFastScopeVar(identNode.getType(), symbol, flags, identNode.isFunction()); 245 return loadFastScopeVar(type, symbol, flags, identNode.isFunction());
249 } 246 }
250 return method.dynamicGet(identNode.getType(), identNode.getName(), flags, identNode.isFunction()); 247 return method.dynamicGet(type, identNode.getName(), flags, identNode.isFunction());
251 } 248 }
252 } 249 }
253 250
254 /** 251 /**
255 * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load 252 * Check if this symbol can be accessed directly with a putfield or getfield or dynamic load
311 private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) { 308 private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) {
312 loadFastScopeProto(symbol, false); 309 loadFastScopeProto(symbol, false);
313 return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod); 310 return method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
314 } 311 }
315 312
316 private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) { 313 private MethodEmitter storeFastScopeVar(final Symbol symbol, final int flags) {
317 loadFastScopeProto(symbol, true); 314 loadFastScopeProto(symbol, true);
318 method.dynamicSet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE); 315 method.dynamicSet(symbol.getName(), flags | CALLSITE_FAST_SCOPE);
319 return method; 316 return method;
320 } 317 }
321 318
322 private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) { 319 private int getScopeProtoDepth(final Block startingBlock, final Symbol symbol) {
323 int depth = 0; 320 int depth = 0;
357 * @param node node to load 354 * @param node node to load
358 * 355 *
359 * @return the method emitter used 356 * @return the method emitter used
360 */ 357 */
361 MethodEmitter load(final Expression node) { 358 MethodEmitter load(final Expression node) {
362 return load(node, false); 359 return load(node, node.hasType() ? node.getType() : null, false);
363 } 360 }
364 361
365 private MethodEmitter load(final Expression node, final boolean baseAlreadyOnStack) { 362 private static boolean safeLiteral(final Expression rhs) {
363 return rhs instanceof LiteralNode && !(rhs instanceof ArrayLiteralNode);
364 }
365
366 MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type) {
367 return loadBinaryOperands(lhs, rhs, type, false);
368 }
369
370 private MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type, final boolean baseAlreadyOnStack) {
371 // ECMAScript 5.1 specification (sections 11.5-11.11 and 11.13) prescribes that when evaluating a binary
372 // expression "LEFT op RIGHT", the order of operations must be: LOAD LEFT, LOAD RIGHT, CONVERT LEFT, CONVERT
373 // RIGHT, EXECUTE OP. Unfortunately, doing it in this order defeats potential optimizations that arise when we
374 // can combine a LOAD with a CONVERT operation (e.g. use a dynamic getter with the conversion target type as its
375 // return value). What we do here is reorder LOAD RIGHT and CONVERT LEFT when possible; it is possible only when
376 // we can prove that executing CONVERT LEFT can't have a side effect that changes the value of LOAD RIGHT.
377 // Basically, if we know that either LEFT is not an object, or RIGHT is a constant literal, then we can do the
378 // reordering and collapse LOAD/CONVERT into a single operation; otherwise we need to do the more costly
379 // separate operations to preserve specification semantics.
380 final Type lhsType = lhs.getType();
381 if (lhsType.isObject() && !safeLiteral(rhs)) {
382 // Can't reorder. Load and convert separately.
383 load(lhs, lhsType, baseAlreadyOnStack);
384 load(rhs, rhs.getType(), false);
385 // Avoid empty SWAP, SWAP bytecode sequence if CONVERT LEFT is a no-op
386 if (!lhsType.isEquivalentTo(type)) {
387 method.swap();
388 method.convert(type);
389 method.swap();
390 }
391 method.convert(type);
392 } else {
393 // Can reorder. Combine load and convert into single operations.
394 load(lhs, type, baseAlreadyOnStack);
395 load(rhs, type, false);
396 }
397
398 return method;
399 }
400
401 MethodEmitter loadBinaryOperands(final BinaryNode node) {
402 return loadBinaryOperands(node.lhs(), node.rhs(), node.getType(), false);
403 }
404
405 private MethodEmitter load(final Expression node, final Type type) {
406 return load(node, type, false);
407 }
408
409 private MethodEmitter load(final Expression node, final Type type, final boolean baseAlreadyOnStack) {
366 final Symbol symbol = node.getSymbol(); 410 final Symbol symbol = node.getSymbol();
367 411
368 // If we lack symbols, we just generate what we see. 412 // If we lack symbols, we just generate what we see.
369 if (symbol == null) { 413 if (symbol == null || type == null) {
370 node.accept(this); 414 node.accept(this);
371 return method; 415 return method;
372 } 416 }
373 417
374 /* 418 /*
376 * or IndexNode e.g. "x[y]". Both AccessNodes and IndexNodes are 420 * or IndexNode e.g. "x[y]". Both AccessNodes and IndexNodes are
377 * BaseNodes and the logic for loading the base object is reused 421 * BaseNodes and the logic for loading the base object is reused
378 */ 422 */
379 final CodeGenerator codegen = this; 423 final CodeGenerator codegen = this;
380 424
381 node.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 425 node.accept(new NodeVisitor<LexicalContext>(lc) {
382 @Override 426 @Override
383 public boolean enterIdentNode(final IdentNode identNode) { 427 public boolean enterIdentNode(final IdentNode identNode) {
384 loadIdent(identNode); 428 loadIdent(identNode, type);
385 return false; 429 return false;
386 } 430 }
387 431
388 @Override 432 @Override
389 public boolean enterAccessNode(final AccessNode accessNode) { 433 public boolean enterAccessNode(final AccessNode accessNode) {
390 if (!baseAlreadyOnStack) { 434 if (!baseAlreadyOnStack) {
391 load(accessNode.getBase()).convert(Type.OBJECT); 435 load(accessNode.getBase()).convert(Type.OBJECT);
392 } 436 }
393 assert method.peekType().isObject(); 437 assert method.peekType().isObject();
394 method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction()); 438 method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
395 return false; 439 return false;
396 } 440 }
397 441
398 @Override 442 @Override
399 public boolean enterIndexNode(final IndexNode indexNode) { 443 public boolean enterIndexNode(final IndexNode indexNode) {
400 if (!baseAlreadyOnStack) { 444 if (!baseAlreadyOnStack) {
401 load(indexNode.getBase()).convert(Type.OBJECT); 445 load(indexNode.getBase()).convert(Type.OBJECT);
402 load(indexNode.getIndex()); 446 load(indexNode.getIndex());
403 } 447 }
404 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction()); 448 method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction());
405 return false; 449 return false;
406 } 450 }
407 451
408 @Override 452 @Override
409 public boolean enterFunctionNode(FunctionNode functionNode) { 453 public boolean enterFunctionNode(FunctionNode functionNode) {
410 // function nodes will always leave a constructed function object on stack, no need to load the symbol 454 // function nodes will always leave a constructed function object on stack, no need to load the symbol
411 // separately as in enterDefault() 455 // separately as in enterDefault()
412 functionNode.accept(codegen); 456 functionNode.accept(codegen);
457 method.convert(type);
413 return false; 458 return false;
414 } 459 }
415 460
416 @Override 461 @Override
462 public boolean enterCallNode(CallNode callNode) {
463 return codegen.enterCallNode(callNode, type);
464 }
465
466 @Override
467 public boolean enterLiteralNode(LiteralNode<?> literalNode) {
468 return codegen.enterLiteralNode(literalNode, type);
469 }
470
471 @Override
417 public boolean enterDefault(final Node otherNode) { 472 public boolean enterDefault(final Node otherNode) {
473 final Node currentDiscard = codegen.lc.getCurrentDiscard();
418 otherNode.accept(codegen); // generate code for whatever we are looking at. 474 otherNode.accept(codegen); // generate code for whatever we are looking at.
419 method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there) 475 if(currentDiscard != otherNode) {
476 method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
477 assert method.peekType() != null;
478 method.convert(type);
479 }
420 return false; 480 return false;
421 } 481 }
422 }); 482 });
423 483
424 return method; 484 return method;
581 } 641 }
582 642
583 return argCount; 643 return argCount;
584 } 644 }
585 645
646
586 @Override 647 @Override
587 public boolean enterCallNode(final CallNode callNode) { 648 public boolean enterCallNode(final CallNode callNode) {
649 return enterCallNode(callNode, callNode.getType());
650 }
651
652 private boolean enterCallNode(final CallNode callNode, final Type callNodeType) {
588 lineNumber(callNode.getLineNumber()); 653 lineNumber(callNode.getLineNumber());
589 654
590 final List<Expression> args = callNode.getArgs(); 655 final List<Expression> args = callNode.getArgs();
591 final Expression function = callNode.getFunction(); 656 final Expression function = callNode.getFunction();
592 final Block currentBlock = lc.getCurrentBlock(); 657 final Block currentBlock = lc.getCurrentBlock();
593 final CodeGeneratorLexicalContext codegenLexicalContext = lc; 658 final CodeGeneratorLexicalContext codegenLexicalContext = lc;
594 final Type callNodeType = callNode.getType();
595 659
596 function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 660 function.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
597 661
598 private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) { 662 private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) {
599 final Symbol symbol = identNode.getSymbol(); 663 final Symbol symbol = identNode.getSymbol();
610 final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, identNode.getType(), callNodeType, paramTypes, scopeCallFlags); 674 final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, identNode.getType(), callNodeType, paramTypes, scopeCallFlags);
611 return scopeCall.generateInvoke(method); 675 return scopeCall.generateInvoke(method);
612 } 676 }
613 677
614 private void scopeCall(final IdentNode node, final int flags) { 678 private void scopeCall(final IdentNode node, final int flags) {
615 load(node); 679 load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
616 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
617 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. 680 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
618 method.loadNull(); //the 'this' 681 method.loadNull(); //the 'this'
619 method.dynamicCall(callNodeType, 2 + loadArgs(args), flags); 682 method.dynamicCall(callNodeType, 2 + loadArgs(args), flags);
620 } 683 }
621 684
622 private void evalCall(final IdentNode node, final int flags) { 685 private void evalCall(final IdentNode node, final int flags) {
623 load(node); 686 load(node, Type.OBJECT); // Type.OBJECT as foo() makes no sense if foo == 3
624 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
625 687
626 final Label not_eval = new Label("not_eval"); 688 final Label not_eval = new Label("not_eval");
627 final Label eval_done = new Label("eval_done"); 689 final Label eval_done = new Label("eval_done");
628 690
629 // check if this is the real built-in eval 691 // check if this is the real built-in eval
636 698
637 method.loadCompilerConstant(SCOPE); // Load up self (scope). 699 method.loadCompilerConstant(SCOPE); // Load up self (scope).
638 700
639 final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); 701 final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
640 // load evaluated code 702 // load evaluated code
641 load(evalArgs.getCode()); 703 load(evalArgs.getCode(), Type.OBJECT);
642 method.convert(Type.OBJECT);
643 // special/extra 'eval' arguments 704 // special/extra 'eval' arguments
644 load(evalArgs.getThis()); 705 load(evalArgs.getThis());
645 method.load(evalArgs.getLocation()); 706 method.load(evalArgs.getLocation());
646 method.load(evalArgs.getStrictMode()); 707 method.load(evalArgs.getStrictMode());
647 method.convert(Type.OBJECT); 708 method.convert(Type.OBJECT);
688 return false; 749 return false;
689 } 750 }
690 751
691 @Override 752 @Override
692 public boolean enterAccessNode(final AccessNode node) { 753 public boolean enterAccessNode(final AccessNode node) {
693 load(node.getBase()); 754 load(node.getBase(), Type.OBJECT);
694 method.convert(Type.OBJECT);
695 method.dup(); 755 method.dup();
696 method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true); 756 method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
697 method.swap(); 757 method.swap();
698 method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags()); 758 method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
699 assert method.peekType().equals(callNodeType);
700 759
701 return false; 760 return false;
702 } 761 }
703 762
704 @Override 763 @Override
725 return false; 784 return false;
726 } 785 }
727 786
728 @Override 787 @Override
729 public boolean enterIndexNode(final IndexNode node) { 788 public boolean enterIndexNode(final IndexNode node) {
730 load(node.getBase()); 789 load(node.getBase(), Type.OBJECT);
731 method.convert(Type.OBJECT);
732 method.dup(); 790 method.dup();
733 load(node.getIndex());
734 final Type indexType = node.getIndex().getType(); 791 final Type indexType = node.getIndex().getType();
735 if (indexType.isObject() || indexType.isBoolean()) { 792 if (indexType.isObject() || indexType.isBoolean()) {
736 method.convert(Type.OBJECT); //TODO 793 load(node.getIndex(), Type.OBJECT); //TODO
794 } else {
795 load(node.getIndex());
737 } 796 }
738 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true); 797 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
739 method.swap(); 798 method.swap();
740 method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags()); 799 method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags());
741 assert method.peekType().equals(callNode.getType());
742 800
743 return false; 801 return false;
744 } 802 }
745 803
746 @Override 804 @Override
747 protected boolean enterDefault(final Node node) { 805 protected boolean enterDefault(final Node node) {
748 // Load up function. 806 // Load up function.
749 load(function); 807 load(function, Type.OBJECT); //TODO, e.g. booleans can be used as functions
750 method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
751 method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE 808 method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
752 method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE); 809 method.dynamicCall(callNodeType, 2 + loadArgs(args), getCallSiteFlags() | CALLSITE_SCOPE);
753 assert method.peekType().equals(callNode.getType());
754 810
755 return false; 811 return false;
756 } 812 }
757 }); 813 });
758 814
851 final Symbol iter = forNode.getIterator(); 907 final Symbol iter = forNode.getIterator();
852 final Label loopLabel = new Label("loop"); 908 final Label loopLabel = new Label("loop");
853 909
854 final Expression init = forNode.getInit(); 910 final Expression init = forNode.getInit();
855 911
856 load(modify); 912 load(modify, Type.OBJECT);
857 assert modify.getType().isObject();
858 method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR); 913 method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
859 method.store(iter); 914 method.store(iter);
860 method._goto(forNode.getContinueLabel()); 915 method._goto(forNode.getContinueLabel());
861 method.label(loopLabel); 916 method.label(loopLabel);
862 917
1201 final Expression element = nodes[index]; 1256 final Expression element = nodes[index];
1202 1257
1203 if (element == null) { 1258 if (element == null) {
1204 method.loadEmpty(elementType); 1259 method.loadEmpty(elementType);
1205 } else { 1260 } else {
1206 assert elementType.isEquivalentTo(element.getType()) : "array element type doesn't match array type"; 1261 load(element, elementType);
1207 load(element);
1208 } 1262 }
1209 1263
1210 method.arraystore(); 1264 method.arraystore();
1211 } 1265 }
1212 1266
1272 } 1326 }
1273 } 1327 }
1274 } 1328 }
1275 1329
1276 // literal values 1330 // literal values
1277 private MethodEmitter load(final LiteralNode<?> node) { 1331 private MethodEmitter loadLiteral(final LiteralNode<?> node, final Type type) {
1278 final Object value = node.getValue(); 1332 final Object value = node.getValue();
1279 1333
1280 if (value == null) { 1334 if (value == null) {
1281 method.loadNull(); 1335 method.loadNull();
1282 } else if (value instanceof Undefined) { 1336 } else if (value instanceof Undefined) {
1292 } else if (value instanceof RegexToken) { 1346 } else if (value instanceof RegexToken) {
1293 loadRegex((RegexToken)value); 1347 loadRegex((RegexToken)value);
1294 } else if (value instanceof Boolean) { 1348 } else if (value instanceof Boolean) {
1295 method.load((Boolean)value); 1349 method.load((Boolean)value);
1296 } else if (value instanceof Integer) { 1350 } else if (value instanceof Integer) {
1297 method.load((Integer)value); 1351 if(type.isEquivalentTo(Type.NUMBER)) {
1352 method.load(((Integer)value).doubleValue());
1353 } else if(type.isEquivalentTo(Type.LONG)) {
1354 method.load(((Integer)value).longValue());
1355 } else {
1356 method.load((Integer)value);
1357 }
1298 } else if (value instanceof Long) { 1358 } else if (value instanceof Long) {
1299 method.load((Long)value); 1359 if(type.isEquivalentTo(Type.NUMBER)) {
1360 method.load(((Long)value).doubleValue());
1361 } else {
1362 method.load((Long)value);
1363 }
1300 } else if (value instanceof Double) { 1364 } else if (value instanceof Double) {
1301 method.load((Double)value); 1365 method.load((Double)value);
1302 } else if (node instanceof ArrayLiteralNode) { 1366 } else if (node instanceof ArrayLiteralNode) {
1303 final ArrayType type = (ArrayType)node.getType(); 1367 final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node;
1304 loadArray((ArrayLiteralNode)node, type); 1368 final ArrayType atype = arrayLiteral.getArrayType();
1305 globalAllocateArray(type); 1369 loadArray(arrayLiteral, atype);
1370 globalAllocateArray(atype);
1306 } else { 1371 } else {
1307 assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value; 1372 assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value;
1308 } 1373 }
1309 1374
1310 return method; 1375 return method;
1344 return method; 1409 return method;
1345 } 1410 }
1346 1411
1347 @Override 1412 @Override
1348 public boolean enterLiteralNode(final LiteralNode<?> literalNode) { 1413 public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
1414 return enterLiteralNode(literalNode, literalNode.getType());
1415 }
1416
1417 private boolean enterLiteralNode(final LiteralNode<?> literalNode, final Type type) {
1349 assert literalNode.getSymbol() != null : literalNode + " has no symbol"; 1418 assert literalNode.getSymbol() != null : literalNode + " has no symbol";
1350 load(literalNode).store(literalNode.getSymbol()); 1419 loadLiteral(literalNode, type).convert(type).store(literalNode.getSymbol());
1351 return false; 1420 return false;
1352 } 1421 }
1353 1422
1354 @Override 1423 @Override
1355 public boolean enterObjectNode(final ObjectNode objectNode) { 1424 public boolean enterObjectNode(final ObjectNode objectNode) {
1620 return enterCmp(lhs, rhs, Condition.GE, type, symbol); 1689 return enterCmp(lhs, rhs, Condition.GE, type, symbol);
1621 case GT: 1690 case GT:
1622 return enterCmp(lhs, rhs, Condition.GT, type, symbol); 1691 return enterCmp(lhs, rhs, Condition.GT, type, symbol);
1623 case ADD: 1692 case ADD:
1624 Type widest = Type.widest(lhs.getType(), rhs.getType()); 1693 Type widest = Type.widest(lhs.getType(), rhs.getType());
1625 load(lhs); 1694 load(lhs, widest);
1626 method.convert(widest); 1695 load(rhs, widest);
1627 load(rhs);
1628 method.convert(widest);
1629 method.add(); 1696 method.add();
1630 method.convert(type); 1697 method.convert(type);
1631 method.store(symbol); 1698 method.store(symbol);
1632 return false; 1699 return false;
1633 default: 1700 default:
1636 break; 1703 break;
1637 } 1704 }
1638 } 1705 }
1639 1706
1640 if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) { 1707 if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) {
1641 return false; 1708 return false;
1642 } 1709 }
1643 1710
1644 if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) { 1711 if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
1645 return false; 1712 return false;
1646 } 1713 }
1647 1714
1648 for (final Expression arg : args) { 1715 for (final Expression arg : args) {
1649 load(arg).convert(Type.OBJECT); //TODO this should not be necessary below Lower 1716 load(arg).convert(Type.OBJECT);
1650 } 1717 }
1651 1718
1652 method.invokestatic( 1719 method.invokestatic(
1653 CompilerConstants.className(ScriptRuntime.class), 1720 CompilerConstants.className(ScriptRuntime.class),
1654 runtimeNode.getRequest().toString(), 1721 runtimeNode.getRequest().toString(),
1901 } 1968 }
1902 1969
1903 method.lookupswitch(defaultLabel, ints, labels); 1970 method.lookupswitch(defaultLabel, ints, labels);
1904 } 1971 }
1905 } else { 1972 } else {
1906 load(expression); 1973 load(expression, Type.OBJECT);
1907 1974 method.store(tag);
1908 if (expression.getType().isInteger()) {
1909 method.convert(Type.NUMBER).dup();
1910 method.store(tag);
1911 method.conditionalJump(Condition.NE, true, defaultLabel);
1912 } else {
1913 assert tag.getSymbolType().isObject();
1914 method.convert(Type.OBJECT); //e.g. 1 literal pushed and tag is object
1915 method.store(tag);
1916 }
1917 1975
1918 for (final CaseNode caseNode : cases) { 1976 for (final CaseNode caseNode : cases) {
1919 final Expression test = caseNode.getTest(); 1977 final Expression test = caseNode.getTest();
1920 1978
1921 if (test != null) { 1979 if (test != null) {
1922 method.load(tag); 1980 method.load(tag);
1923 load(test); 1981 load(test, Type.OBJECT);
1924 method.invoke(ScriptRuntime.EQ_STRICT); 1982 method.invoke(ScriptRuntime.EQ_STRICT);
1925 method.ifne(caseNode.getEntry()); 1983 method.ifne(caseNode.getEntry());
1926 } 1984 }
1927 } 1985 }
1928 1986
1959 final Expression expression = throwNode.getExpression(); 2017 final Expression expression = throwNode.getExpression();
1960 final int position = throwNode.position(); 2018 final int position = throwNode.position();
1961 final int line = throwNode.getLineNumber(); 2019 final int line = throwNode.getLineNumber();
1962 final int column = source.getColumn(position); 2020 final int column = source.getColumn(position);
1963 2021
1964 load(expression); 2022 load(expression, Type.OBJECT);
1965 assert expression.getType().isObject();
1966 2023
1967 method.load(source.getName()); 2024 method.load(source.getName());
1968 method.load(line); 2025 method.load(line);
1969 method.load(column); 2026 method.load(column);
1970 method.invoke(ECMAException.THROW_INIT); 2027 method.invoke(ECMAException.THROW_INIT);
2085 return false; 2142 return false;
2086 } 2143 }
2087 2144
2088 lineNumber(varNode); 2145 lineNumber(varNode);
2089 2146
2090 final Symbol varSymbol = varNode.getName().getSymbol(); 2147 final IdentNode identNode = varNode.getName();
2091 assert varSymbol != null : "variable node " + varNode + " requires a name with a symbol"; 2148 final Symbol identSymbol = identNode.getSymbol();
2149 assert identSymbol != null : "variable node " + varNode + " requires a name with a symbol";
2092 2150
2093 assert method != null; 2151 assert method != null;
2094 2152
2095 final boolean needsScope = varSymbol.isScope(); 2153 final boolean needsScope = identSymbol.isScope();
2096 if (needsScope) { 2154 if (needsScope) {
2097 method.loadCompilerConstant(SCOPE); 2155 method.loadCompilerConstant(SCOPE);
2098 } 2156 }
2099 load(init);
2100 2157
2101 if (needsScope) { 2158 if (needsScope) {
2159 load(init);
2102 int flags = CALLSITE_SCOPE | getCallSiteFlags(); 2160 int flags = CALLSITE_SCOPE | getCallSiteFlags();
2103 final IdentNode identNode = varNode.getName(); 2161 if (isFastScope(identSymbol)) {
2104 final Type type = identNode.getType(); 2162 storeFastScopeVar(identSymbol, flags);
2105 if (isFastScope(varSymbol)) {
2106 storeFastScopeVar(type, varSymbol, flags);
2107 } else { 2163 } else {
2108 method.dynamicSet(type, identNode.getName(), flags); 2164 method.dynamicSet(identNode.getName(), flags);
2109 } 2165 }
2110 } else { 2166 } else {
2111 method.convert(varNode.getName().getType()); // aw: convert moved here 2167 load(init, identNode.getType());
2112 method.store(varSymbol); 2168 method.store(identSymbol);
2113 } 2169 }
2114 2170
2115 return false; 2171 return false;
2116 } 2172 }
2117 2173
2166 method.loadCompilerConstant(SCOPE); 2222 method.loadCompilerConstant(SCOPE);
2167 } else { 2223 } else {
2168 tryLabel = null; 2224 tryLabel = null;
2169 } 2225 }
2170 2226
2171 load(expression); 2227 load(expression, Type.OBJECT);
2172 assert expression.getType().isObject() : "with expression needs to be object: " + expression;
2173 2228
2174 if (hasScope) { 2229 if (hasScope) {
2175 // Construct a WithObject if we have a scope 2230 // Construct a WithObject if we have a scope
2176 method.invoke(ScriptRuntime.OPEN_WITH); 2231 method.invoke(ScriptRuntime.OPEN_WITH);
2177 method.storeCompilerConstant(SCOPE); 2232 method.storeCompilerConstant(SCOPE);
2209 return false; 2264 return false;
2210 } 2265 }
2211 2266
2212 @Override 2267 @Override
2213 public boolean enterADD(final UnaryNode unaryNode) { 2268 public boolean enterADD(final UnaryNode unaryNode) {
2214 load(unaryNode.rhs()); 2269 load(unaryNode.rhs(), unaryNode.getType());
2215 assert unaryNode.rhs().getType().isNumber() : unaryNode.rhs().getType() + " "+ unaryNode.getSymbol(); 2270 assert unaryNode.getType().isNumeric();
2216 method.store(unaryNode.getSymbol()); 2271 method.store(unaryNode.getSymbol());
2217
2218 return false; 2272 return false;
2219 } 2273 }
2220 2274
2221 @Override 2275 @Override
2222 public boolean enterBIT_NOT(final UnaryNode unaryNode) { 2276 public boolean enterBIT_NOT(final UnaryNode unaryNode) {
2223 load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol()); 2277 load(unaryNode.rhs(), Type.INT).load(-1).xor().store(unaryNode.getSymbol());
2224 return false;
2225 }
2226
2227 // do this better with convert calls to method. TODO
2228 @Override
2229 public boolean enterCONVERT(final UnaryNode unaryNode) {
2230 final Expression rhs = unaryNode.rhs();
2231 final Type to = unaryNode.getType();
2232
2233 if (to.isObject() && rhs instanceof LiteralNode) {
2234 final LiteralNode<?> literalNode = (LiteralNode<?>)rhs;
2235 final Object value = literalNode.getValue();
2236
2237 if (value instanceof Number) {
2238 assert !to.isArray() : "type hygiene - cannot convert number to array: (" + to.getTypeClass().getSimpleName() + ')' + value;
2239 if (value instanceof Integer) {
2240 method.load((Integer)value);
2241 } else if (value instanceof Long) {
2242 method.load((Long)value);
2243 } else if (value instanceof Double) {
2244 method.load((Double)value);
2245 } else {
2246 assert false;
2247 }
2248 method.convert(Type.OBJECT);
2249 } else if (value instanceof Boolean) {
2250 method.getField(staticField(Boolean.class, value.toString().toUpperCase(Locale.ENGLISH), Boolean.class));
2251 } else {
2252 load(rhs);
2253 method.convert(unaryNode.getType());
2254 }
2255 } else {
2256 load(rhs);
2257 method.convert(unaryNode.getType());
2258 }
2259
2260 method.store(unaryNode.getSymbol());
2261
2262 return false; 2278 return false;
2263 } 2279 }
2264 2280
2265 @Override 2281 @Override
2266 public boolean enterDECINC(final UnaryNode unaryNode) { 2282 public boolean enterDECINC(final UnaryNode unaryNode) {
2274 2290
2275 new SelfModifyingStore<UnaryNode>(unaryNode, rhs) { 2291 new SelfModifyingStore<UnaryNode>(unaryNode, rhs) {
2276 2292
2277 @Override 2293 @Override
2278 protected void evaluate() { 2294 protected void evaluate() {
2279 load(rhs, true); 2295 load(rhs, type, true);
2280
2281 method.convert(type);
2282 if (!isPostfix) { 2296 if (!isPostfix) {
2283 if (type.isInteger()) { 2297 if (type.isInteger()) {
2284 method.load(isIncrement ? 1 : -1); 2298 method.load(isIncrement ? 1 : -1);
2285 } else if (type.isLong()) { 2299 } else if (type.isLong()) {
2286 method.load(isIncrement ? 1L : -1L); 2300 method.load(isIncrement ? 1L : -1L);
2342 2356
2343 @Override 2357 @Override
2344 public boolean enterNOT(final UnaryNode unaryNode) { 2358 public boolean enterNOT(final UnaryNode unaryNode) {
2345 final Expression rhs = unaryNode.rhs(); 2359 final Expression rhs = unaryNode.rhs();
2346 2360
2347 load(rhs); 2361 load(rhs, Type.BOOLEAN);
2348 2362
2349 final Label trueLabel = new Label("true"); 2363 final Label trueLabel = new Label("true");
2350 final Label afterLabel = new Label("after"); 2364 final Label afterLabel = new Label("after");
2351 2365
2352 method.convert(Type.BOOLEAN);
2353 method.ifne(trueLabel); 2366 method.ifne(trueLabel);
2354 method.load(true); 2367 method.load(true);
2355 method._goto(afterLabel); 2368 method._goto(afterLabel);
2356 method.label(trueLabel); 2369 method.label(trueLabel);
2357 method.load(false); 2370 method.load(false);
2361 return false; 2374 return false;
2362 } 2375 }
2363 2376
2364 @Override 2377 @Override
2365 public boolean enterSUB(final UnaryNode unaryNode) { 2378 public boolean enterSUB(final UnaryNode unaryNode) {
2366 load(unaryNode.rhs()).neg().store(unaryNode.getSymbol()); 2379 assert unaryNode.getType().isNumeric();
2367 2380 load(unaryNode.rhs()).convert(unaryNode.getType()).neg().store(unaryNode.getSymbol());
2368 return false; 2381 return false;
2369 } 2382 }
2370 2383
2371 @Override 2384 @Override
2372 public boolean enterVOID(final UnaryNode unaryNode) { 2385 public boolean enterVOID(final UnaryNode unaryNode) {
2375 2388
2376 return false; 2389 return false;
2377 } 2390 }
2378 2391
2379 private void enterNumericAdd(final Expression lhs, final Expression rhs, final Type type, final Symbol symbol) { 2392 private void enterNumericAdd(final Expression lhs, final Expression rhs, final Type type, final Symbol symbol) {
2380 assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type) : lhs.getType() + " != " + rhs.getType() + " != " + type + " " + new ASTWriter(lhs) + " " + new ASTWriter(rhs); 2393 loadBinaryOperands(lhs, rhs, type);
2381 load(lhs);
2382 load(rhs);
2383 method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack? 2394 method.add(); //if the symbol is optimistic, it always needs to be written, not on the stack?
2384 method.store(symbol); 2395 method.store(symbol);
2385 } 2396 }
2386 2397
2387 @Override 2398 @Override
2391 2402
2392 final Type type = binaryNode.getType(); 2403 final Type type = binaryNode.getType();
2393 if (type.isNumeric()) { 2404 if (type.isNumeric()) {
2394 enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol()); 2405 enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol());
2395 } else { 2406 } else {
2396 load(lhs).convert(Type.OBJECT); 2407 loadBinaryOperands(binaryNode);
2397 load(rhs).convert(Type.OBJECT);
2398 method.add(); 2408 method.add();
2399 method.store(binaryNode.getSymbol()); 2409 method.store(binaryNode.getSymbol());
2400 } 2410 }
2401 2411
2402 return false; 2412 return false;
2437 final Type lhsType = lhs.getType(); 2447 final Type lhsType = lhs.getType();
2438 final Type rhsType = rhs.getType(); 2448 final Type rhsType = rhs.getType();
2439 2449
2440 if (!lhsType.isEquivalentTo(rhsType)) { 2450 if (!lhsType.isEquivalentTo(rhsType)) {
2441 //this is OK if scoped, only locals are wrong 2451 //this is OK if scoped, only locals are wrong
2442 assert !(lhs instanceof IdentNode) || lhs.getSymbol().isScope() : new ASTWriter(binaryNode);
2443 } 2452 }
2444 2453
2445 new Store<BinaryNode>(binaryNode, lhs) { 2454 new Store<BinaryNode>(binaryNode, lhs) {
2446 @Override 2455 @Override
2447 protected void evaluate() { 2456 protected void evaluate() {
2448 load(rhs); 2457 if ((lhs instanceof IdentNode) && !lhs.getSymbol().isScope()) {
2458 load(rhs, lhsType);
2459 } else {
2460 load(rhs);
2461 }
2449 } 2462 }
2450 }.store(); 2463 }.store();
2451 2464
2452 return false; 2465 return false;
2453 } 2466 }
2482 2495
2483 protected abstract void op(); 2496 protected abstract void op();
2484 2497
2485 @Override 2498 @Override
2486 protected void evaluate() { 2499 protected void evaluate() {
2487 load(assignNode.lhs(), true).convert(opType); 2500 loadBinaryOperands(assignNode.lhs(), assignNode.rhs(), opType, true);
2488 load(assignNode.rhs()).convert(opType);
2489 op(); 2501 op();
2490 method.convert(assignNode.getType()); 2502 method.convert(assignNode.getType());
2491 } 2503 }
2492 } 2504 }
2493 2505
2654 private abstract class BinaryArith { 2666 private abstract class BinaryArith {
2655 2667
2656 protected abstract void op(); 2668 protected abstract void op();
2657 2669
2658 protected void evaluate(final BinaryNode node) { 2670 protected void evaluate(final BinaryNode node) {
2659 load(node.lhs()); 2671 loadBinaryOperands(node);
2660 load(node.rhs());
2661 op(); 2672 op();
2662 method.store(node.getSymbol()); 2673 method.store(node.getSymbol());
2663 } 2674 }
2664 } 2675 }
2665 2676
2737 final Type rhsType = rhs.getType(); 2748 final Type rhsType = rhs.getType();
2738 2749
2739 final Type widest = Type.widest(lhsType, rhsType); 2750 final Type widest = Type.widest(lhsType, rhsType);
2740 assert widest.isNumeric() || widest.isBoolean() : widest; 2751 assert widest.isNumeric() || widest.isBoolean() : widest;
2741 2752
2742 load(lhs); 2753 loadBinaryOperands(lhs, rhs, widest);
2743 method.convert(widest);
2744 load(rhs);
2745 method.convert(widest);
2746
2747 final Label trueLabel = new Label("trueLabel"); 2754 final Label trueLabel = new Label("trueLabel");
2748 final Label afterLabel = new Label("skip"); 2755 final Label afterLabel = new Label("skip");
2749 2756
2750 method.conditionalJump(cond, trueLabel); 2757 method.conditionalJump(cond, trueLabel);
2751 2758
2860 2867
2861 @Override 2868 @Override
2862 public boolean enterSHR(final BinaryNode binaryNode) { 2869 public boolean enterSHR(final BinaryNode binaryNode) {
2863 new BinaryArith() { 2870 new BinaryArith() {
2864 @Override 2871 @Override
2872 protected void evaluate(final BinaryNode node) {
2873 loadBinaryOperands(node.lhs(), node.rhs(), Type.INT);
2874 op();
2875 method.store(node.getSymbol());
2876 }
2877 @Override
2865 protected void op() { 2878 protected void op() {
2866 method.shr(); 2879 method.shr();
2867 method.convert(Type.LONG).load(JSType.MAX_UINT).and(); 2880 method.convert(Type.LONG).load(JSType.MAX_UINT).and();
2868 } 2881 }
2869 }.evaluate(binaryNode); 2882 }.evaluate(binaryNode);
2896 Type widest = Type.widest(ternaryNode.getType(), Type.widest(trueExpr.getType(), falseExpr.getType())); 2909 Type widest = Type.widest(ternaryNode.getType(), Type.widest(trueExpr.getType(), falseExpr.getType()));
2897 if (trueExpr.getType().isArray() || falseExpr.getType().isArray()) { //loadArray creates a Java array type on the stack, calls global allocate, which creates a native array type 2910 if (trueExpr.getType().isArray() || falseExpr.getType().isArray()) { //loadArray creates a Java array type on the stack, calls global allocate, which creates a native array type
2898 widest = Type.OBJECT; 2911 widest = Type.OBJECT;
2899 } 2912 }
2900 2913
2901 load(test); 2914 load(test, Type.BOOLEAN);
2902 assert test.getType().isBoolean() : "lhs in ternary must be boolean";
2903
2904 // we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17 2915 // we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17
2905 // will left as (Object)x=55 : (Object)17 by Lower. Then the first term can be {I}x=55 of type int, which breaks the 2916 // will left as (Object)x=55 : (Object)17 by Lower. Then the first term can be {I}x=55 of type int, which breaks the
2906 // symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions 2917 // symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions
2907 // too early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to 2918 // too early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to
2908 // do this property. Then we never need any conversions in CodeGenerator 2919 // do this property. Then we never need any conversions in CodeGenerator
2909 method.ifeq(falseLabel); 2920 method.ifeq(falseLabel);
2910 load(trueExpr); 2921 load(trueExpr, widest);
2911 method.convert(widest);
2912 method._goto(exitLabel); 2922 method._goto(exitLabel);
2913 method.label(falseLabel); 2923 method.label(falseLabel);
2914 load(falseExpr); 2924 load(falseExpr, widest);
2915 method.convert(widest);
2916 method.label(exitLabel); 2925 method.label(exitLabel);
2917 method.store(symbol); 2926 method.store(symbol);
2918 2927
2919 return false; 2928 return false;
2920 } 2929 }
3042 private void enterBaseNode() { 3051 private void enterBaseNode() {
3043 assert target instanceof BaseNode : "error - base node " + target + " must be instanceof BaseNode"; 3052 assert target instanceof BaseNode : "error - base node " + target + " must be instanceof BaseNode";
3044 final BaseNode baseNode = (BaseNode)target; 3053 final BaseNode baseNode = (BaseNode)target;
3045 final Expression base = baseNode.getBase(); 3054 final Expression base = baseNode.getBase();
3046 3055
3047 load(base); 3056 load(base, Type.OBJECT);
3048 method.convert(Type.OBJECT);
3049 depth += Type.OBJECT.getSlots(); 3057 depth += Type.OBJECT.getSlots();
3050 3058
3051 if (isSelfModifying()) { 3059 if (isSelfModifying()) {
3052 method.dup(); 3060 method.dup();
3053 } 3061 }
3062 @Override 3070 @Override
3063 public boolean enterIndexNode(final IndexNode node) { 3071 public boolean enterIndexNode(final IndexNode node) {
3064 enterBaseNode(); 3072 enterBaseNode();
3065 3073
3066 final Expression index = node.getIndex(); 3074 final Expression index = node.getIndex();
3067 // could be boolean here as well
3068 load(index);
3069 if (!index.getType().isNumeric()) { 3075 if (!index.getType().isNumeric()) {
3070 method.convert(Type.OBJECT); 3076 // could be boolean here as well
3077 load(index, Type.OBJECT);
3078 } else {
3079 load(index);
3071 } 3080 }
3072 depth += index.getType().getSlots(); 3081 depth += index.getType().getSlots();
3073 3082
3074 if (isSelfModifying()) { 3083 if (isSelfModifying()) {
3075 //convert "base base index" to "base index base index" 3084 //convert "base base index" to "base index base index"
3134 * 3143 *
3135 * The case that targetSymbol is in scope (!hasSlot) and we actually 3144 * The case that targetSymbol is in scope (!hasSlot) and we actually
3136 * need to do a conversion on non-equivalent types exists, but is 3145 * need to do a conversion on non-equivalent types exists, but is
3137 * very rare. See for example test/script/basic/access-specializer.js 3146 * very rare. See for example test/script/basic/access-specializer.js
3138 */ 3147 */
3139 method.convert(target.getType());
3140
3141 target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) { 3148 target.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
3142 @Override 3149 @Override
3143 protected boolean enterDefault(Node node) { 3150 protected boolean enterDefault(Node node) {
3144 throw new AssertionError("Unexpected node " + node + " in store epilogue"); 3151 throw new AssertionError("Unexpected node " + node + " in store epilogue");
3145 }
3146
3147 @Override
3148 public boolean enterUnaryNode(final UnaryNode node) {
3149 if (node.tokenType() == TokenType.CONVERT && node.getSymbol() != null) {
3150 method.convert(node.rhs().getType());
3151 }
3152 return true;
3153 } 3152 }
3154 3153
3155 @Override 3154 @Override
3156 public boolean enterIdentNode(final IdentNode node) { 3155 public boolean enterIdentNode(final IdentNode node) {
3157 final Symbol symbol = node.getSymbol(); 3156 final Symbol symbol = node.getSymbol();
3158 assert symbol != null; 3157 assert symbol != null;
3159 if (symbol.isScope()) { 3158 if (symbol.isScope()) {
3160 if (isFastScope(symbol)) { 3159 if (isFastScope(symbol)) {
3161 storeFastScopeVar(node.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags()); 3160 storeFastScopeVar(symbol, CALLSITE_SCOPE | getCallSiteFlags());
3162 } else { 3161 } else {
3163 method.dynamicSet(node.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags()); 3162 method.dynamicSet(node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
3164 } 3163 }
3165 } else { 3164 } else {
3165 method.convert(node.getType());
3166 method.store(symbol); 3166 method.store(symbol);
3167 } 3167 }
3168 return false; 3168 return false;
3169 3169
3170 } 3170 }
3171 3171
3172 @Override 3172 @Override
3173 public boolean enterAccessNode(final AccessNode node) { 3173 public boolean enterAccessNode(final AccessNode node) {
3174 method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags()); 3174 method.dynamicSet(node.getProperty().getName(), getCallSiteFlags());
3175 return false; 3175 return false;
3176 } 3176 }
3177 3177
3178 @Override 3178 @Override
3179 public boolean enterIndexNode(final IndexNode node) { 3179 public boolean enterIndexNode(final IndexNode node) {

mercurial