1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.codegen;
27
28 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.PRIVATE;
29 import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.STATIC;
30 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
31 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
32 import static jdk.nashorn.internal.codegen.CompilerConstants.LEAF;
33 import static jdk.nashorn.internal.codegen.CompilerConstants.QUICK_PREFIX;
34 import static jdk.nashorn.internal.codegen.CompilerConstants.REGEX_PREFIX;
35 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
36 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_ARRAY_ARG;
37 import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
38 import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
39 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
40 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
41 import static jdk.nashorn.internal.codegen.CompilerConstants.staticField;
42 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
43 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
44 import static jdk.nashorn.internal.ir.Symbol.IS_TEMP;
45 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FAST_SCOPE;
46 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_FUNCTION_DECLARATION;
47 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_SCOPE;
48 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
49
50 import java.io.PrintWriter;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.EnumSet;
54 import java.util.HashMap;
55 import java.util.Iterator;
56 import java.util.LinkedList;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Map.Entry;
60 import java.util.TreeMap;
61 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
62 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
63 import jdk.nashorn.internal.codegen.MethodEmitter.Condition;
64 import jdk.nashorn.internal.codegen.MethodEmitter.Label;
65 import jdk.nashorn.internal.codegen.RuntimeCallSite.SpecializedRuntimeNode;
66 import jdk.nashorn.internal.codegen.objects.FieldObjectCreator;
67 import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator;
68 import jdk.nashorn.internal.codegen.objects.MapCreator;
69 import jdk.nashorn.internal.codegen.objects.ObjectMapCreator;
70 import jdk.nashorn.internal.codegen.types.ArrayType;
71 import jdk.nashorn.internal.codegen.types.Type;
72 import jdk.nashorn.internal.ir.AccessNode;
73 import jdk.nashorn.internal.ir.BaseNode;
74 import jdk.nashorn.internal.ir.BinaryNode;
75 import jdk.nashorn.internal.ir.Block;
76 import jdk.nashorn.internal.ir.BreakNode;
77 import jdk.nashorn.internal.ir.CallNode;
78 import jdk.nashorn.internal.ir.CaseNode;
79 import jdk.nashorn.internal.ir.CatchNode;
80 import jdk.nashorn.internal.ir.ContinueNode;
81 import jdk.nashorn.internal.ir.DoWhileNode;
82 import jdk.nashorn.internal.ir.EmptyNode;
83 import jdk.nashorn.internal.ir.ExecuteNode;
84 import jdk.nashorn.internal.ir.ForNode;
85 import jdk.nashorn.internal.ir.FunctionNode;
86 import jdk.nashorn.internal.ir.IdentNode;
87 import jdk.nashorn.internal.ir.IfNode;
88 import jdk.nashorn.internal.ir.IndexNode;
89 import jdk.nashorn.internal.ir.LineNumberNode;
90 import jdk.nashorn.internal.ir.LiteralNode;
91 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
92 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
93 import jdk.nashorn.internal.ir.Node;
94 import jdk.nashorn.internal.ir.ObjectNode;
95 import jdk.nashorn.internal.ir.PropertyNode;
96 import jdk.nashorn.internal.ir.ReferenceNode;
97 import jdk.nashorn.internal.ir.ReturnNode;
98 import jdk.nashorn.internal.ir.RuntimeNode;
99 import jdk.nashorn.internal.ir.RuntimeNode.Request;
100 import jdk.nashorn.internal.ir.SplitNode;
101 import jdk.nashorn.internal.ir.SwitchNode;
102 import jdk.nashorn.internal.ir.Symbol;
103 import jdk.nashorn.internal.ir.TernaryNode;
104 import jdk.nashorn.internal.ir.ThrowNode;
105 import jdk.nashorn.internal.ir.TryNode;
106 import jdk.nashorn.internal.ir.UnaryNode;
107 import jdk.nashorn.internal.ir.VarNode;
108 import jdk.nashorn.internal.ir.WhileNode;
109 import jdk.nashorn.internal.ir.WithNode;
110 import jdk.nashorn.internal.ir.debug.ASTWriter;
111 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
112 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
113 import jdk.nashorn.internal.parser.Lexer.RegexToken;
114 import jdk.nashorn.internal.parser.TokenType;
115 import jdk.nashorn.internal.runtime.Context;
116 import jdk.nashorn.internal.runtime.ECMAException;
117 import jdk.nashorn.internal.runtime.PropertyMap;
118 import jdk.nashorn.internal.runtime.Scope;
119 import jdk.nashorn.internal.runtime.ScriptFunction;
120 import jdk.nashorn.internal.runtime.ScriptObject;
121 import jdk.nashorn.internal.runtime.ScriptRuntime;
122 import jdk.nashorn.internal.runtime.Source;
123 import jdk.nashorn.internal.runtime.Undefined;
124 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
125
126 /**
127 * This is the lowest tier of the code generator. It takes lowered ASTs emitted
128 * from Lower and emits Java byte code. The byte code emission logic is broken
129 * out into MethodEmitter. MethodEmitter works internally with a type stack, and
130 * keeps track of the contents of the byte code stack. This way we avoid a large
131 * number of special cases on the form
132 * <pre>
133 * if (type == INT) {
134 * visitInsn(ILOAD, slot);
135 * } else if (type == DOUBLE) {
136 * visitInsn(DOUBLE, slot);
137 * }
138 * </pre>
139 * This quickly became apparent when the code generator was generalized to work
140 * with all types, and not just numbers or objects.
141 * <p>
142 * The CodeGenerator visits nodes only once, tags them as resolved and emits
143 * bytecode for them.
144 */
145 public final class CodeGenerator extends NodeOperatorVisitor {
146
147 /** Current compiler */
148 private final Compiler compiler;
149
150 /** Compiler context */
151 private final Context context;
152
153 /** Call site flags given to the code generator to be used for all generated call sites */
154 private final int callSiteFlags;
155
156 /** How many regexp fields have been emitted */
157 private int regexFieldCount;
158
159 /** Map of shared scope call sites */
160 private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
161
162 /** When should we stop caching regexp expressions in fields to limit bytecode size? */
163 private static final int MAX_REGEX_FIELDS = 2 * 1024;
164
165 /**
166 * Constructor.
167 *
168 * @param compiler
169 */
170 CodeGenerator(final Compiler compiler) {
171 this.compiler = compiler;
172 this.context = compiler.getContext();
173 this.callSiteFlags = context._callsite_flags;
174 }
175
176 /**
177 * Get the compiler
178 *
179 * @return the compiler used
180 */
181 public Compiler getCompiler() {
182 return compiler;
183 }
184
185 /**
186 * Gets the call site flags, adding the strict flag if the current function
187 * being generated is in strict mode
188 *
189 * @return the correct flags for a call site in the current function
190 */
191 public int getCallSiteFlags() {
192 return getCurrentFunctionNode().isStrictMode() ? callSiteFlags | CALLSITE_STRICT : callSiteFlags;
193 }
194
195 /**
196 * Load an identity node
197 *
198 * @param identNode an identity node to load
199 * @return the method generator used
200 */
201 private MethodEmitter loadIdent(final IdentNode identNode) {
202 final Symbol symbol = identNode.getSymbol();
203
204 if (!symbol.isScope()) {
205 assert symbol.hasSlot() && symbol.getSlot() != 0 || symbol.isThis();
206 return method.load(symbol);
207 }
208
209 final String name = symbol.getName();
210
211 if (CompilerConstants.__FILE__.name().equals(name)) {
212 return method.load(identNode.getSource().getName());
213 } else if (CompilerConstants.__DIR__.name().equals(name)) {
214 return method.load(identNode.getSource().getBase());
215 } else if (CompilerConstants.__LINE__.name().equals(name)) {
216 return method.load(identNode.getSource().getLine(identNode.position())).convert(Type.OBJECT);
217 } else {
218 assert identNode.getSymbol().isScope() : identNode + " is not in scope!";
219
220 final int flags = CALLSITE_SCOPE | getCallSiteFlags();
221 method.loadScope();
222
223 if (symbol.isFastScope(getCurrentFunctionNode())) {
224 // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope.
225 if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD) {
226 return loadSharedScopeVar(identNode.getType(), symbol, flags);
227 }
228 return loadFastScopeVar(identNode.getType(), symbol, flags, identNode.isFunction());
229 }
230 return method.dynamicGet(identNode.getType(), identNode.getName(), flags, identNode.isFunction());
231 }
232 }
233
234 private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) {
235 method.load(symbol.isFastScope(getCurrentFunctionNode()) ? getScopeProtoDepth(getCurrentBlock(), symbol) : -1);
236 final SharedScopeCall scopeCall = getScopeGet(valueType, symbol, flags | CALLSITE_FAST_SCOPE);
237 scopeCall.generateInvoke(method);
238 return method;
239 }
240
241 private MethodEmitter loadFastScopeVar(final Type valueType, final Symbol symbol, final int flags, final boolean isMethod) {
242 loadFastScopeProto(symbol, false);
243 method.dynamicGet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE, isMethod);
244 return method;
245 }
246
247 private MethodEmitter storeFastScopeVar(final Type valueType, final Symbol symbol, final int flags) {
248 loadFastScopeProto(symbol, true);
249 method.dynamicSet(valueType, symbol.getName(), flags | CALLSITE_FAST_SCOPE);
250 return method;
251 }
252
253 private static int getScopeProtoDepth(final Block currentBlock, final Symbol symbol) {
254 if (currentBlock == symbol.getBlock()) {
255 return 0;
256 }
257
258 final int delta = currentBlock.needsScope() ? 1 : 0;
259 final Block parentBlock = currentBlock.getParent();
260
261 if (parentBlock != null) {
262 final int result = getScopeProtoDepth(parentBlock, symbol);
263 if (result != -1) {
264 return delta + result;
265 }
266 }
267
268 if (currentBlock instanceof FunctionNode) {
269 for (final Block lookupBlock : ((FunctionNode)currentBlock).getReferencingParentBlocks()) {
270 final int result = getScopeProtoDepth(lookupBlock, symbol);
271 if (result != -1) {
272 return delta + result;
273 }
274 }
275 }
276
277 return -1;
278 }
279
280 private void loadFastScopeProto(final Symbol symbol, final boolean swap) {
281 final int depth = getScopeProtoDepth(getCurrentBlock(), symbol);
282 assert depth != -1;
283 if(depth > 0) {
284 if (swap) {
285 method.swap();
286 }
287 for (int i = 0; i < depth; i++) {
288 method.invoke(ScriptObject.GET_PROTO);
289 }
290 if (swap) {
291 method.swap();
292 }
293 }
294 }
295
296 /**
297 * Generate code that loads this node to the stack. This method is only
298 * public to be accessible from the maps sub package. Do not call externally
299 *
300 * @param node node to load
301 *
302 * @return the method emitter used
303 */
304 public MethodEmitter load(final Node node) {
305 return load(node, false);
306 }
307
308 private MethodEmitter load(final Node node, final boolean baseAlreadyOnStack) {
309 final Symbol symbol = node.getSymbol();
310
311 // If we lack symbols, we just generate what we see.
312 if (symbol == null) {
313 node.accept(this);
314 return method;
315 }
316
317 /*
318 * The load may be of type IdentNode, e.g. "x", AccessNode, e.g. "x.y"
319 * or IndexNode e.g. "x[y]". Both AccessNodes and IndexNodes are
320 * BaseNodes and the logic for loading the base object is reused
321 */
322 final CodeGenerator codegen = this;
323
324 node.accept(new NodeVisitor(compileUnit, method) {
325 @Override
326 public Node enter(final IdentNode identNode) {
327 loadIdent(identNode);
328 return null;
329 }
330
331 @Override
332 public Node enter(final AccessNode accessNode) {
333 if (!baseAlreadyOnStack) {
334 load(accessNode.getBase()).convert(Type.OBJECT);
335 }
336 assert method.peekType().isObject();
337 method.dynamicGet(node.getType(), accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
338 return null;
339 }
340
341 @Override
342 public Node enter(final IndexNode indexNode) {
343 if (!baseAlreadyOnStack) {
344 load(indexNode.getBase());
345 load(indexNode.getIndex());
346 }
347 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), indexNode.isFunction());
348 return null;
349 }
350
351 @Override
352 public Node enterDefault(final Node otherNode) {
353 otherNode.accept(codegen); // generate code for whatever we are looking at.
354 method.load(symbol); // load the final symbol to the stack (or nop if no slot, then result is already there)
355 return null;
356 }
357 });
358
359 return method;
360 }
361
362 @Override
363 public Node enter(final AccessNode accessNode) {
364 if (accessNode.testResolved()) {
365 return null;
366 }
367
368 load(accessNode);
369
370 return null;
371 }
372
373 /**
374 * Initialize a specific set of vars to undefined. This has to be done at
375 * the start of each method for local variables that aren't passed as
376 * parameters.
377 *
378 * @param symbols list of symbols.
379 */
380 private void initSymbols(final Iterable<Symbol> symbols) {
381 final LinkedList<Symbol> numbers = new LinkedList<>();
382 final LinkedList<Symbol> objects = new LinkedList<>();
383
384 for (final Symbol symbol : symbols) {
385 /*
386 * The following symbols are guaranteed to be defined and thus safe
387 * from having unsigned written to them: parameters internals this
388 *
389 * Otherwise we must, unless we perform control/escape analysis,
390 * assign them undefined.
391 */
392 final boolean isInternal = symbol.isParam() || symbol.isInternal() || symbol.isThis() || !symbol.canBeUndefined();
393
394 if (symbol.hasSlot() && !isInternal) {
395 assert symbol.getSymbolType().isNumber() || symbol.getSymbolType().isObject() : "no potentially undefined narrower local vars than doubles are allowed: " + symbol + " in " + getCurrentFunctionNode();
396 if (symbol.getSymbolType().isNumber()) {
397 numbers.add(symbol);
398 } else if (symbol.getSymbolType().isObject()) {
399 objects.add(symbol);
400 }
401 }
402 }
403
404 initSymbols(numbers, Type.NUMBER);
405 initSymbols(objects, Type.OBJECT);
406 }
407
408 private void initSymbols(final LinkedList<Symbol> symbols, final Type type) {
409 if (symbols.isEmpty()) {
410 return;
411 }
412
413 method.loadUndefined(type);
414 while (!symbols.isEmpty()) {
415 final Symbol symbol = symbols.removeFirst();
416 if (!symbols.isEmpty()) {
417 method.dup();
418 }
419 method.store(symbol);
420 }
421 }
422
423 /**
424 * Create symbol debug information.
425 *
426 * @param block block containing symbols.
427 */
428 private void symbolInfo(final Block block) {
429 for (final Symbol symbol : block.getFrame().getSymbols()) {
430 method.localVariable(symbol, block.getEntryLabel(), block.getBreakLabel());
431 }
432 }
433
434 @Override
435 public Node enter(final Block block) {
436 if (block.testResolved()) {
437 return null;
438 }
439
440 method.label(block.getEntryLabel());
441 initLocals(block);
442
443 return block;
444 }
445
446 @Override
447 public Node leave(final Block block) {
448 method.label(block.getBreakLabel());
449 symbolInfo(block);
450
451 if (block.needsScope()) {
452 popBlockScope(block);
453 }
454
455 return block;
456 }
457
458 private void popBlockScope(final Block block) {
459 final Label exitLabel = new Label("block_exit");
460 final Label recoveryLabel = new Label("block_catch");
461 final Label skipLabel = new Label("skip_catch");
462
463 /* pop scope a la try-finally */
464 method.loadScope();
465 method.invoke(ScriptObject.GET_PROTO);
466 method.storeScope();
467 method._goto(skipLabel);
468 method.label(exitLabel);
469
470 method._catch(recoveryLabel);
471 method.loadScope();
472 method.invoke(ScriptObject.GET_PROTO);
473 method.storeScope();
474 method.athrow();
475 method.label(skipLabel);
476 method._try(block.getEntryLabel(), exitLabel, recoveryLabel, Throwable.class);
477 }
478
479 @Override
480 public Node enter(final BreakNode breakNode) {
481 if (breakNode.testResolved()) {
482 return null;
483 }
484
485 for (int i = 0; i < breakNode.getScopeNestingLevel(); i++) {
486 closeWith();
487 }
488
489 method.splitAwareGoto(breakNode.getTargetLabel());
490
491 return null;
492 }
493
494 private MethodEmitter loadArgs(final List<Node> args) {
495 return loadArgs(args, null, false, args.size());
496 }
497
498 private MethodEmitter loadArgs(final List<Node> args, final String signature, final boolean isVarArg, final int argCount) {
499 // arg have already been converted to objects here.
500 if (isVarArg || argCount > LinkerCallSite.ARGLIMIT) {
501 loadArgsArray(args);
502 return method;
503 }
504
505 // pad with undefined if size is too short. argCount is the real number of args
506 int n = 0;
507 final Type[] params = signature == null ? null : Type.getMethodArguments(signature);
508 for (final Node arg : args) {
509 assert arg != null;
510 load(arg);
511 if (n >= argCount) {
512 method.pop(); // we had to load the arg for its side effects
513 } else if (params != null) {
514 method.convert(params[n]);
515 }
516 n++;
517 }
518
519 while (n < argCount) {
520 method.loadUndefined(Type.OBJECT);
521 n++;
522 }
523
524 return method;
525 }
526
527 /**
528 * Create a new function object, including generating its stub code.
529 *
530 * @param functionNode FunctionNode to utilize.
531 */
532 private void newFunctionObject(final FunctionNode functionNode) {
533 // Turn thisProperties into keys and symbols for the FunctionAnalyzer
534 final Map<String, Node> thisProperties = functionNode.getThisProperties();
535
536 final List<String> keys = new ArrayList<>();
537 final List<Symbol> symbols = new ArrayList<>();
538
539 /* TODO - Predefine known properties.
540 for (final Entry<String, Node> entry : thisProperties.entrySet()) {
541 keys.add(entry.getKey());
542 symbols.add(entry.getValue().getSymbol());
543 }
544 */
545
546 new FunctionObjectCreator(this, functionNode, keys, symbols).makeObject(method);
547 }
548
549 @Override
550 public Node enter(final CallNode callNode) {
551 if (callNode.testResolved()) {
552 return null;
553 }
554
555 final List<Node> args = callNode.getArgs();
556 final Node function = callNode.getFunction();
557 final FunctionNode currentFunction = getCurrentFunctionNode();
558 final Block currentBlock = getCurrentBlock();
559
560 function.accept(new NodeVisitor(compileUnit, method) {
561
562 private void sharedScopeCall(final IdentNode identNode, final int flags) {
563 final Symbol symbol = identNode.getSymbol();
564 int scopeCallFlags = flags;
565 method.loadScope();
566 if (symbol.isFastScope(currentFunction)) {
567 method.load(getScopeProtoDepth(currentBlock, symbol));
568 scopeCallFlags |= CALLSITE_FAST_SCOPE;
569 } else {
570 method.load(-1); // Bypass fast-scope code in shared callsite
571 }
572 loadArgs(args);
573 final Type[] paramTypes = method.getTypesFromStack(args.size());
574 final SharedScopeCall scopeCall = getScopeCall(symbol, identNode.getType(), callNode.getType(), paramTypes, scopeCallFlags);
575 scopeCall.generateInvoke(method);
576 }
577
578 private void scopeCall(final IdentNode node, final int flags) {
579 load(node);
580 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
581 // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
582 method.loadNull();
583 loadArgs(args);
584 method.dynamicCall(callNode.getType(), args.size(), flags);
585 }
586
587 private void evalCall(final IdentNode node, final int flags) {
588 load(node);
589 method.convert(Type.OBJECT); // foo() makes no sense if foo == 3
590
591 final Label not_eval = new Label("not_eval");
592 final Label eval_done = new Label("eval_done");
593
594 // check if this is the real built-in eval
595 method.dup();
596 globalIsEval();
597
598 method.ifeq(not_eval);
599 // We don't need ScriptFunction object for 'eval'
600 method.pop();
601
602 method.loadScope(); // Load up self (scope).
603
604 final CallNode.EvalArgs evalArgs = callNode.getEvalArgs();
605 // load evaluated code
606 load(evalArgs.code);
607 method.convert(Type.OBJECT);
608 // special/extra 'eval' arguments
609 load(evalArgs.evalThis);
610 method.load(evalArgs.location);
611 method.load(evalArgs.strictMode);
612 method.convert(Type.OBJECT);
613
614 // direct call to Global.directEval
615 globalDirectEval();
616 method.convert(callNode.getType());
617 method._goto(eval_done);
618
619 method.label(not_eval);
620 // This is some scope 'eval' or global eval replaced by user
621 // but not the built-in ECMAScript 'eval' function call
622 method.loadNull();
623 loadArgs(args);
624 method.dynamicCall(callNode.getType(), args.size(), flags);
625
626 method.label(eval_done);
627 }
628
629 @Override
630 public Node enter(final IdentNode node) {
631 final Symbol symbol = node.getSymbol();
632
633 if (symbol.isScope()) {
634 final int flags = getCallSiteFlags() | CALLSITE_SCOPE;
635 final int useCount = symbol.getUseCount();
636
637 // Threshold for generating shared scope callsite is lower for fast scope symbols because we know
638 // we can dial in the correct scope. However, we als need to enable it for non-fast scopes to
639 // support huge scripts like mandreel.js.
640 if (callNode.isEval()) {
641 evalCall(node, flags);
642 } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
643 || (!symbol.isFastScope(currentFunction) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
644 || callNode.inWithBlock()) {
645 scopeCall(node, flags);
646 } else {
647 sharedScopeCall(node, flags);
648 }
649 assert method.peekType().equals(callNode.getType());
650 } else {
651 enterDefault(node);
652 }
653
654 return null;
655 }
656
657 @Override
658 public Node enter(final AccessNode node) {
659 load(node.getBase());
660 method.convert(Type.OBJECT);
661 method.dup();
662 method.dynamicGet(node.getType(), node.getProperty().getName(), getCallSiteFlags(), true);
663 method.swap();
664 loadArgs(args);
665 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags());
666 assert method.peekType().equals(callNode.getType());
667
668 return null;
669 }
670
671 @Override
672 public Node enter(final ReferenceNode node) {
673 final FunctionNode callee = node.getReference();
674 final boolean isVarArg = callee.isVarArg();
675 final int argCount = isVarArg ? -1 : callee.getParameters().size();
676
677 final String signature = new FunctionSignature(true, callee.needsCallee(), callee.getReturnType(), isVarArg ? null : callee.getParameters()).toString();
678
679 if (callee.isStrictMode()) { // self is undefined
680 method.loadUndefined(Type.OBJECT);
681 } else { // get global from scope (which is the self)
682 globalInstance();
683 }
684
685 if (callee.needsCallee()) { // TODO: always true
686 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.
687 }
688
689 loadArgs(args, signature, isVarArg, argCount);
690 method.invokeStatic(callee.getCompileUnit().getUnitClassName(), callee.getName(), signature);
691 assert method.peekType().equals(callee.getReturnType()) : method.peekType() + " != " + callee.getReturnType();
692
693 return null;
694 }
695
696 @Override
697 public Node enter(final IndexNode node) {
698 load(node.getBase());
699 method.convert(Type.OBJECT);
700 method.dup();
701 load(node.getIndex());
702 final Type indexType = node.getIndex().getType();
703 if (indexType.isObject() || indexType.isBoolean()) {
704 method.convert(Type.OBJECT); //TODO
705 }
706 method.dynamicGetIndex(node.getType(), getCallSiteFlags(), true);
707 method.swap();
708 loadArgs(args);
709 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags());
710 assert method.peekType().equals(callNode.getType());
711
712 return null;
713 }
714
715 @Override
716 protected Node enterDefault(final Node node) {
717 // Load up function.
718 load(function);
719 method.convert(Type.OBJECT); //TODO, e.g. booleans can be used as functions
720 method.loadNull(); // ScriptFunction will figure out the correct this when it sees CALLSITE_SCOPE
721
722 loadArgs(args);
723 method.dynamicCall(callNode.getType(), args.size(), getCallSiteFlags() | CALLSITE_SCOPE);
724 assert method.peekType().equals(callNode.getType());
725
726 return null;
727 }
728 });
729
730 method.store(callNode.getSymbol());
731
732 return null;
733 }
734
735 @Override
736 public Node enter(final ContinueNode continueNode) {
737 if (continueNode.testResolved()) {
738 return null;
739 }
740
741 for (int i = 0; i < continueNode.getScopeNestingLevel(); i++) {
742 closeWith();
743 }
744
745 method.splitAwareGoto(continueNode.getTargetLabel());
746
747 return null;
748 }
749
750 @Override
751 public Node enter(final DoWhileNode doWhileNode) {
752 return enter((WhileNode)doWhileNode);
753 }
754
755 @Override
756 public Node enter(final EmptyNode emptyNode) {
757 return null;
758 }
759
760 @Override
761 public Node enter(final ExecuteNode executeNode) {
762 if (executeNode.testResolved()) {
763 return null;
764 }
765
766 final Node expression = executeNode.getExpression();
767 expression.accept(this);
768
769 return null;
770 }
771
772 @Override
773 public Node enter(final ForNode forNode) {
774 if (forNode.testResolved()) {
775 return null;
776 }
777
778 final Node test = forNode.getTest();
779 final Block body = forNode.getBody();
780 final Node modify = forNode.getModify();
781
782 final Label breakLabel = forNode.getBreakLabel();
783 final Label continueLabel = forNode.getContinueLabel();
784 final Label loopLabel = new Label("loop");
785
786 Node init = forNode.getInit();
787
788 if (forNode.isForIn()) {
789 final Symbol iter = forNode.getIterator();
790
791 // We have to evaluate the optional initializer expression
792 // of the iterator variable of the for-in statement.
793 if (init instanceof VarNode) {
794 init.accept(this);
795 init = ((VarNode)init).getName();
796 }
797
798 load(modify);
799 assert modify.getType().isObject();
800 method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
801 method.store(iter);
802 method._goto(continueLabel);
803 method.label(loopLabel);
804
805 new Store<Node>(init) {
806 @Override
807 protected void evaluate() {
808 method.load(iter);
809 method.invoke(interfaceCallNoLookup(Iterator.class, "next", Object.class));
810 }
811 }.store();
812
813 body.accept(this);
814
815 method.label(continueLabel);
816 method.load(iter);
817 method.invoke(interfaceCallNoLookup(Iterator.class, "hasNext", boolean.class));
818 method.ifne(loopLabel);
819 method.label(breakLabel);
820 } else {
821 if (init != null) {
822 init.accept(this);
823 }
824
825 final Label testLabel = new Label("test");
826
827 method._goto(testLabel);
828 method.label(loopLabel);
829 body.accept(this);
830 method.label(continueLabel);
831
832 if (!body.isTerminal() && modify != null) {
833 load(modify);
834 }
835
836 method.label(testLabel);
837 if (test != null) {
838 new BranchOptimizer(this, method).execute(test, loopLabel, true);
839 } else {
840 method._goto(loopLabel);
841 }
842
843 method.label(breakLabel);
844 }
845
846 return null;
847 }
848
849 /**
850 * Initialize the slots in a frame to undefined.
851 *
852 * @param block block with local vars.
853 */
854 private void initLocals(final Block block) {
855 final FunctionNode function = block.getFunction();
856 final boolean isFunctionNode = block == function;
857
858 /*
859 * Get the symbols from the frame and realign the frame so that all
860 * slots get correct numbers. The slot numbering is not fixed until
861 * after initLocals has been run
862 */
863 final Frame frame = block.getFrame();
864 final List<Symbol> symbols = frame.getSymbols();
865
866 /* Fix the predefined slots so they have numbers >= 0, like varargs. */
867 frame.realign();
868
869 /*
870 * Determine if block needs scope, if not, just do initSymbols for this block.
871 */
872 if (block.needsScope()) {
873 /*
874 * Determine if function is varargs and consequently variables have to
875 * be in the scope.
876 */
877 final boolean isVarArg = function.isVarArg();
878 final boolean varsInScope = function.varsInScope();
879
880 // TODO for LET we can do better: if *block* does not contain any eval/with, we don't need its vars in scope.
881
882 final List<String> nameList = new ArrayList<>();
883 final List<Symbol> locals = new ArrayList<>();
884
885 // If there are variable arguments, we need to load them (functions only).
886 if (isFunctionNode && isVarArg) {
887 method.loadVarArgs();
888 method.loadCallee();
889 method.load(function.getParameters().size());
890 globalAllocateArguments();
891 method.storeArguments();
892 }
893
894 // Initalize symbols and values
895 final List<Symbol> newSymbols = new ArrayList<>();
896 final List<Symbol> values = new ArrayList<>();
897
898 for (final Symbol symbol : symbols) {
899 if (symbol.isInternal() || symbol.isThis()) {
900 continue;
901 }
902
903 if (symbol.isVar() && (varsInScope || symbol.isScope())) {
904 nameList.add(symbol.getName());
905 newSymbols.add(symbol);
906 values.add(null);
907 assert symbol.isScope() : "scope for " + symbol + " should have been set in Lower already " + function.getName();
908 assert !symbol.hasSlot() : "slot for " + symbol + " should have been removed in Lower already" + function.getName();
909 } else if (symbol.isVar()) {
910 assert symbol.hasSlot() : symbol + " should have a slot only, no scope";
911 locals.add(symbol);
912 } else if (symbol.isParam() && (varsInScope || isVarArg || symbol.isScope())) {
913 nameList.add(symbol.getName());
914 newSymbols.add(symbol);
915 values.add(isVarArg ? null : symbol);
916 assert symbol.isScope() : "scope for " + symbol + " should have been set in Lower already " + function.getName() + " varsInScope="+varsInScope+" isVarArg="+isVarArg+" symbol.isScope()=" + symbol.isScope();
917 assert !(isVarArg && symbol.hasSlot()) : "slot for " + symbol + " should have been removed in Lower already " + function.getName();
918 }
919 }
920
921 /* Correct slot numbering again */
922 frame.realign();
923
924 // we may have locals that need to be initialized
925 initSymbols(locals);
926
927 if (isFunctionNode) {
928 initScope();
929 }
930
931 /*
932 * Create a new object based on the symbols and values, generate
933 * bootstrap code for object
934 */
935 final FieldObjectCreator<Symbol> foc = new FieldObjectCreator<Symbol>(this, nameList, newSymbols, values, true, isVarArg) {
936 @Override
937 protected Type getValueType(final Symbol value) {
938 return value.getSymbolType();
939 }
940
941 @Override
942 protected void loadValue(final Symbol value) {
943 method.load(value);
944 }
945 };
946 foc.makeObject(method);
947
948 // runScript(): merge scope into global
949 if (isFunctionNode && function.isScript()) {
950 method.invoke(ScriptRuntime.MERGE_SCOPE);
951 }
952
953 method.storeScope();
954 } else {
955 initSymbols(symbols);
956
957 if (isFunctionNode) {
958 initScope();
959 }
960 }
961
962 // Debugging: print symbols? @see --print-symbols flag
963 printSymbols(block, (isFunctionNode ? "Function " : "Block in ") + (function.getIdent() == null ? "<anonymous>" : function.getIdent().getName()));
964 }
965
966 private void initScope() {
967 method.loadCallee();
968 method.invoke(ScriptFunction.GET_SCOPE);
969 method.storeScope();
970 }
971
972 @Override
973 public Node enter(final FunctionNode functionNode) {
974 if (functionNode.testResolved()) {
975 return null;
976 }
977
978 compileUnit = functionNode.getCompileUnit();
979 assert compileUnit != null;
980
981 method = compileUnit.getClassEmitter().method(functionNode);
982 functionNode.setMethodEmitter(method);
983 // Mark end for variable tables.
984 method.begin();
985 method.label(functionNode.getEntryLabel());
986
987 initLocals(functionNode);
988
989 return functionNode;
990 }
991
992 @Override
993 public Node leave(final FunctionNode functionNode) {
994 // Mark end for variable tables.
995 method.label(functionNode.getBreakLabel());
996
997 if (!functionNode.needsScope()) {
998 method.markerVariable(LEAF.tag(), functionNode.getEntryLabel(), functionNode.getBreakLabel());
999 }
1000
1001 symbolInfo(functionNode);
1002 try {
1003 method.end(); // wrap up this method
1004 } catch (final Throwable t) {
1005 Context.printStackTrace(t);
1006 final VerifyError e = new VerifyError("Code generation bug in \"" + functionNode.getName() + "\": likely stack misaligned: " + t + " " + functionNode.getSource().getName());
1007 e.initCause(t);
1008 throw e;
1009 }
1010
1011 return functionNode;
1012 }
1013
1014 @Override
1015 public Node enter(final IdentNode identNode) {
1016 return null;
1017 }
1018
1019 @Override
1020 public Node enter(final IfNode ifNode) {
1021 if (ifNode.testResolved()) {
1022 return null;
1023 }
1024
1025 final Node test = ifNode.getTest();
1026 final Block pass = ifNode.getPass();
1027 final Block fail = ifNode.getFail();
1028
1029 final Label failLabel = new Label("if_fail");
1030 final Label afterLabel = fail == null ? failLabel : new Label("if_done");
1031
1032 new BranchOptimizer(this, method).execute(test, failLabel, false);
1033
1034 boolean passTerminal = false;
1035 boolean failTerminal = false;
1036
1037 pass.accept(this);
1038 if (!pass.hasTerminalFlags()) {
1039 method._goto(afterLabel); //don't fallthru to fail block
1040 } else {
1041 passTerminal = pass.isTerminal();
1042 }
1043
1044 if (fail != null) {
1045 method.label(failLabel);
1046 fail.accept(this);
1047 failTerminal = fail.isTerminal();
1048 }
1049
1050 //if if terminates, put the after label there
1051 if (!passTerminal || !failTerminal) {
1052 method.label(afterLabel);
1053 }
1054
1055 return null;
1056 }
1057
1058 @Override
1059 public Node enter(final IndexNode indexNode) {
1060 if (indexNode.testResolved()) {
1061 return null;
1062 }
1063
1064 load(indexNode);
1065
1066 return null;
1067 }
1068
1069 @Override
1070 public Node enter(final LineNumberNode lineNumberNode) {
1071 if (lineNumberNode.testResolved()) {
1072 return null;
1073 }
1074
1075 final Label label = new Label("line:" + lineNumberNode.getLineNumber() + " (" + getCurrentFunctionNode().getName() + ")");
1076 method.label(label);
1077 method.lineNumber(lineNumberNode.getLineNumber(), label);
1078
1079 return null;
1080 }
1081
1082 /**
1083 * Load a list of nodes as an array of a specific type
1084 * The array will contain the visited nodes.
1085 *
1086 * @param arrayLiteralNode the array of contents
1087 * @param arrayType the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT
1088 *
1089 * @return the method generator that was used
1090 */
1091 private MethodEmitter loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) {
1092 assert arrayType == Type.INT_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY;
1093
1094 final Node[] nodes = arrayLiteralNode.getValue();
1095 final Object presets = arrayLiteralNode.getPresets();
1096 final int[] postsets = arrayLiteralNode.getPostsets();
1097 final Class<?> type = arrayType.getTypeClass();
1098 final List<ArrayUnit> units = arrayLiteralNode.getUnits();
1099
1100 loadConstant(presets);
1101
1102 final Type elementType = arrayType.getElementType();
1103
1104 if (units != null) {
1105 final CompileUnit savedCompileUnit = compileUnit;
1106 final MethodEmitter savedMethod = method;
1107
1108 try {
1109 for (final ArrayUnit unit : units) {
1110 compileUnit = unit.getCompileUnit();
1111
1112 final String className = compileUnit.getUnitClassName();
1113 final String name = compiler.uniqueName(SPLIT_PREFIX.tag());
1114 final String signature = methodDescriptor(type, Object.class, ScriptFunction.class, ScriptObject.class, type);
1115
1116 method = compileUnit.getClassEmitter().method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), name, signature);
1117 method.setFunctionNode(getCurrentFunctionNode());
1118 method.begin();
1119
1120 fixScopeSlot();
1121
1122 method.load(arrayType, SPLIT_ARRAY_ARG.slot());
1123
1124 for (int i = unit.getLo(); i < unit.getHi(); i++) {
1125 storeElement(nodes, elementType, postsets[i]);
1126 }
1127
1128 method._return();
1129 method.end();
1130
1131 savedMethod.loadThis();
1132 savedMethod.swap();
1133 savedMethod.loadCallee();
1134 savedMethod.swap();
1135 savedMethod.loadScope();
1136 savedMethod.swap();
1137 savedMethod.invokeStatic(className, name, signature);
1138 }
1139 } finally {
1140 compileUnit = savedCompileUnit;
1141 method = savedMethod;
1142 }
1143
1144 return method;
1145 }
1146
1147 for (final int postset : postsets) {
1148 storeElement(nodes, elementType, postset);
1149 }
1150
1151 return method;
1152 }
1153
1154 private void storeElement(final Node[] nodes, final Type elementType, final int index) {
1155 method.dup();
1156 method.load(index);
1157
1158 final Node element = nodes[index];
1159
1160 if (element == null) {
1161 method.loadEmpty(elementType);
1162 } else {
1163 assert elementType.isEquivalentTo(element.getType()) : "array element type doesn't match array type";
1164 load(element);
1165 }
1166
1167 method.arraystore();
1168 }
1169
1170 private MethodEmitter loadArgsArray(final List<Node> args) {
1171 final Object[] array = new Object[args.size()];
1172 loadConstant(array);
1173
1174 for (int i = 0; i < args.size(); i++) {
1175 method.dup();
1176 method.load(i);
1177 load(args.get(i)).convert(Type.OBJECT); //has to be upcast to object or we fail
1178 method.arraystore();
1179 }
1180
1181 return method;
1182 }
1183
1184 /**
1185 * Load a constant from the constant array. This is only public to be callable from the objects
1186 * subpackage. Do not call directly.
1187 *
1188 * @param string string to load
1189 */
1190 public void loadConstant(final String string) {
1191 final String unitClassName = compileUnit.getUnitClassName();
1192 final ClassEmitter classEmitter = compileUnit.getClassEmitter();
1193 final int index = compiler.getConstantData().add(string);
1194
1195 method.load(index);
1196 method.invokeStatic(unitClassName, GET_STRING.tag(), methodDescriptor(String.class, int.class));
1197 classEmitter.needGetConstantMethod(String.class);
1198 }
1199
1200 /**
1201 * Load a constant from the constant array. This is only public to be callable from the objects
1202 * subpackage. Do not call directly.
1203 *
1204 * @param object object to load
1205 */
1206 public void loadConstant(final Object object) {
1207 final String unitClassName = compileUnit.getUnitClassName();
1208 final ClassEmitter classEmitter = compileUnit.getClassEmitter();
1209 final int index = compiler.getConstantData().add(object);
1210 final Class<?> cls = object.getClass();
1211
1212 if (cls == PropertyMap.class) {
1213 method.load(index);
1214 method.invokeStatic(unitClassName, GET_MAP.tag(), methodDescriptor(PropertyMap.class, int.class));
1215 classEmitter.needGetConstantMethod(PropertyMap.class);
1216 } else if (cls.isArray()) {
1217 method.load(index);
1218 final String methodName = ClassEmitter.getArrayMethodName(cls);
1219 method.invokeStatic(unitClassName, methodName, methodDescriptor(cls, int.class));
1220 classEmitter.needGetConstantMethod(cls);
1221 } else {
1222 method.loadConstants(unitClassName).load(index).arrayload();
1223 if (cls != Object.class) {
1224 method.checkcast(cls);
1225 }
1226 }
1227 }
1228
1229 // literal values
1230 private MethodEmitter load(final LiteralNode<?> node) {
1231 final Object value = node.getValue();
1232
1233 if (value == null) {
1234 method.loadNull();
1235 } else if (value instanceof Undefined) {
1236 method.loadUndefined(Type.OBJECT);
1237 } else if (value instanceof String) {
1238 final String string = (String)value;
1239
1240 if (string.length() > (MethodEmitter.LARGE_STRING_THRESHOLD / 3)) { // 3 == max bytes per encoded char
1241 loadConstant(string);
1242 } else {
1243 method.load(string);
1244 }
1245 } else if (value instanceof RegexToken) {
1246 loadRegex((RegexToken)value);
1247 } else if (value instanceof Boolean) {
1248 method.load((Boolean)value);
1249 } else if (value instanceof Integer) {
1250 method.load((Integer)value);
1251 } else if (value instanceof Long) {
1252 method.load((Long)value);
1253 } else if (value instanceof Double) {
1254 method.load((Double)value);
1255 } else if (node instanceof ArrayLiteralNode) {
1256 final ArrayType type = (ArrayType)node.getType();
1257 loadArray((ArrayLiteralNode)node, type);
1258 globalAllocateArray(type);
1259 } else {
1260 assert false : "Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value;
1261 }
1262
1263 return method;
1264 }
1265
1266 private MethodEmitter loadRegexToken(final RegexToken value) {
1267 method.load(value.getExpression());
1268 method.load(value.getOptions());
1269 return globalNewRegExp();
1270 }
1271
1272 private MethodEmitter loadRegex(final RegexToken regexToken) {
1273 if (regexFieldCount > MAX_REGEX_FIELDS) {
1274 return loadRegexToken(regexToken);
1275 }
1276 // emit field
1277 final String regexName = compiler.uniqueName(REGEX_PREFIX.tag());
1278 final ClassEmitter classEmitter = compileUnit.getClassEmitter();
1279
1280 classEmitter.field(EnumSet.of(PRIVATE, STATIC), regexName, Object.class);
1281 regexFieldCount++;
1282
1283 // get field, if null create new regex, finally clone regex object
1284 method.getStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
1285 method.dup();
1286 final Label cachedLabel = new Label("cached");
1287 method.ifnonnull(cachedLabel);
1288
1289 method.pop();
1290 loadRegexToken(regexToken);
1291 method.dup();
1292 method.putStatic(compileUnit.getUnitClassName(), regexName, typeDescriptor(Object.class));
1293
1294 method.label(cachedLabel);
1295 globalRegExpCopy();
1296
1297 return method;
1298 }
1299
1300 @SuppressWarnings("rawtypes")
1301 @Override
1302 public Node enter(final LiteralNode literalNode) {
1303 load(literalNode).store(literalNode.getSymbol());
1304 return null;
1305 }
1306
1307 @Override
1308 public Node enter(final ObjectNode objectNode) {
1309 if (objectNode.testResolved()) {
1310 return null;
1311 }
1312
1313 final List<Node> elements = objectNode.getElements();
1314 final int size = elements.size();
1315
1316 final List<String> keys = new ArrayList<>();
1317 final List<Symbol> symbols = new ArrayList<>();
1318 final List<Node> values = new ArrayList<>();
1319
1320 boolean hasGettersSetters = false;
1321
1322 for (int i = 0; i < size; i++) {
1323 final PropertyNode propertyNode = (PropertyNode)elements.get(i);
1324 final Node value = propertyNode.getValue();
1325 final String key = propertyNode.getKeyName();
1326 final Symbol symbol = value == null ? null : propertyNode.getSymbol();
1327
1328 if (value == null) {
1329 hasGettersSetters = true;
1330 }
1331
1332 keys.add(key);
1333 symbols.add(symbol);
1334 values.add(value);
1335 }
1336
1337 new FieldObjectCreator<Node>(this, keys, symbols, values) {
1338 @Override
1339 protected Type getValueType(final Node node) {
1340 return node.getType();
1341 }
1342
1343 @Override
1344 protected void loadValue(final Node node) {
1345 load(node);
1346 }
1347
1348 /**
1349 * Ensure that the properties start out as object types so that
1350 * we can do putfield initializations instead of dynamicSetIndex
1351 * which would be the case to determine initial property type
1352 * otherwise.
1353 *
1354 * Use case, it's very expensive to do a million var x = {a:obj, b:obj}
1355 * just to have to invalidate them immediately on initialization
1356 *
1357 * see NASHORN-594
1358 */
1359 @Override
1360 protected MapCreator newMapCreator(final Class<?> fieldObjectClass) {
1361 return new ObjectMapCreator(fieldObjectClass, keys, symbols);
1362 }
1363
1364 }.makeObject(method);
1365
1366 method.dup();
1367 globalObjectPrototype();
1368 method.invoke(ScriptObject.SET_PROTO);
1369
1370 if (!hasGettersSetters) {
1371 method.store(objectNode.getSymbol());
1372 return null;
1373 }
1374
1375 for (final Node element : elements) {
1376 final PropertyNode propertyNode = (PropertyNode)element;
1377 final Object key = propertyNode.getKey();
1378 final ReferenceNode getter = (ReferenceNode)propertyNode.getGetter();
1379 final ReferenceNode setter = (ReferenceNode)propertyNode.getSetter();
1380
1381 if (getter == null && setter == null) {
1382 continue;
1383 }
1384
1385 method.dup().loadKey(key);
1386
1387 if (getter == null) {
1388 method.loadNull();
1389 } else {
1390 getter.accept(this);
1391 }
1392
1393 if (setter == null) {
1394 method.loadNull();
1395 } else {
1396 setter.accept(this);
1397 }
1398
1399 method.invoke(ScriptObject.SET_USER_ACCESSORS);
1400 }
1401
1402 method.store(objectNode.getSymbol());
1403
1404 return null;
1405 }
1406
1407 @Override
1408 public Node enter(final ReferenceNode referenceNode) {
1409 if (referenceNode.testResolved()) {
1410 return null;
1411 }
1412
1413 newFunctionObject(referenceNode.getReference());
1414
1415 return null;
1416 }
1417
1418 @Override
1419 public Node enter(final ReturnNode returnNode) {
1420 if (returnNode.testResolved()) {
1421 return null;
1422 }
1423
1424 // Set the split return flag in the scope if this is a split method fragment.
1425 if (method.getSplitNode() != null) {
1426 assert method.getSplitNode().hasReturn() : "unexpected return in split node";
1427
1428 method.loadScope();
1429 method.checkcast(Scope.class);
1430 method.load(0);
1431 method.invoke(Scope.SET_SPLIT_STATE);
1432 }
1433
1434 final Node expression = returnNode.getExpression();
1435 if (expression != null) {
1436 load(expression);
1437 } else {
1438 method.loadUndefined(getCurrentFunctionNode().getReturnType());
1439 }
1440
1441 method._return(getCurrentFunctionNode().getReturnType());
1442
1443 return null;
1444 }
1445
1446 private static boolean isNullLiteral(final Node node) {
1447 return node instanceof LiteralNode<?> && ((LiteralNode<?>) node).isNull();
1448 }
1449
1450 private boolean nullCheck(final RuntimeNode runtimeNode, final List<Node> args, final String signature) {
1451 final Request request = runtimeNode.getRequest();
1452
1453 if (!Request.isEQ(request) && !Request.isNE(request)) {
1454 return false;
1455 }
1456
1457 assert args.size() == 2 : "EQ or NE or TYPEOF need two args";
1458
1459 Node lhs = args.get(0);
1460 Node rhs = args.get(1);
1461
1462 if (isNullLiteral(lhs)) {
1463 final Node tmp = lhs;
1464 lhs = rhs;
1465 rhs = tmp;
1466 }
1467
1468 if (isNullLiteral(rhs)) {
1469 final Label trueLabel = new Label("trueLabel");
1470 final Label falseLabel = new Label("falseLabel");
1471 final Label endLabel = new Label("end");
1472
1473 load(lhs);
1474 method.dup();
1475 if (Request.isEQ(request)) {
1476 method.ifnull(trueLabel);
1477 } else if (Request.isNE(request)) {
1478 method.ifnonnull(trueLabel);
1479 } else {
1480 assert false : "Invalid request " + request;
1481 }
1482
1483 method.label(falseLabel);
1484 load(rhs);
1485 method.invokeStatic(CompilerConstants.className(ScriptRuntime.class), request.toString(), signature);
1486 method._goto(endLabel);
1487
1488 method.label(trueLabel);
1489 // if NE (not strict) this can be "undefined != null" which is supposed to be false
1490 if (request == Request.NE) {
1491 method.loadUndefined(Type.OBJECT);
1492 final Label isUndefined = new Label("isUndefined");
1493 final Label afterUndefinedCheck = new Label("afterUndefinedCheck");
1494 method.if_acmpeq(isUndefined);
1495 // not undefined
1496 method.load(true);
1497 method._goto(afterUndefinedCheck);
1498 method.label(isUndefined);
1499 method.load(false);
1500 method.label(afterUndefinedCheck);
1501 } else {
1502 method.pop();
1503 method.load(true);
1504 }
1505 method.label(endLabel);
1506 method.convert(runtimeNode.getType());
1507 method.store(runtimeNode.getSymbol());
1508
1509 return true;
1510 }
1511
1512 return false;
1513 }
1514
1515 private boolean specializationCheck(final RuntimeNode.Request request, final Node node, final List<Node> args) {
1516 if (!request.canSpecialize()) {
1517 return false;
1518 }
1519
1520 assert args.size() == 2;
1521 final Node lhs = args.get(0);
1522 final Node rhs = args.get(1);
1523
1524 final Type returnType = node.getType();
1525 load(lhs);
1526 load(rhs);
1527
1528 Request finalRequest = request;
1529
1530 final Request reverse = Request.reverse(request);
1531 if (method.peekType().isObject() && reverse != null) {
1532 if (!method.peekType(1).isObject()) {
1533 method.swap();
1534 finalRequest = reverse;
1535 }
1536 }
1537
1538 method.dynamicRuntimeCall(
1539 new SpecializedRuntimeNode(
1540 finalRequest,
1541 new Type[] {
1542 method.peekType(1),
1543 method.peekType()
1544 },
1545 returnType).getInitialName(),
1546 returnType,
1547 finalRequest);
1548
1549 method.convert(node.getType());
1550 method.store(node.getSymbol());
1551
1552 return true;
1553 }
1554
1555 @Override
1556 public Node enter(final RuntimeNode runtimeNode) {
1557 if (runtimeNode.testResolved()) {
1558 return null;
1559 }
1560
1561 /*
1562 * First check if this should be something other than a runtime node
1563 * AccessSpecializer might have changed the type
1564 */
1565 if (runtimeNode.isPrimitive()) {
1566
1567 final Node lhs = runtimeNode.getArgs().get(0);
1568 Node rhs = null;
1569
1570 if (runtimeNode.getArgs().size() > 1) {
1571 rhs = runtimeNode.getArgs().get(1);
1572 }
1573
1574 final Type type = runtimeNode.getType();
1575 final Symbol symbol = runtimeNode.getSymbol();
1576
1577 switch (runtimeNode.getRequest()) {
1578 case EQ:
1579 case EQ_STRICT:
1580 return enterCmp(lhs, rhs, Condition.EQ, type, symbol);
1581 case NE:
1582 case NE_STRICT:
1583 return enterCmp(lhs, rhs, Condition.NE, type, symbol);
1584 case LE:
1585 return enterCmp(lhs, rhs, Condition.LE, type, symbol);
1586 case LT:
1587 return enterCmp(lhs, rhs, Condition.LT, type, symbol);
1588 case GE:
1589 return enterCmp(lhs, rhs, Condition.GE, type, symbol);
1590 case GT:
1591 return enterCmp(lhs, rhs, Condition.GT, type, symbol);
1592 case ADD:
1593 return enterNumericAdd(lhs, rhs, type, symbol);
1594 default:
1595 // it's ok to send this one on with only primitive arguments, maybe INSTANCEOF(true, true) or similar
1596 // assert false : runtimeNode + " has all primitive arguments. This is an inconsistent state";
1597 break;
1598 }
1599 }
1600
1601 // Get the request arguments.
1602 final List<Node> args = runtimeNode.getArgs();
1603
1604 if (nullCheck(runtimeNode, args, new FunctionSignature(false, runtimeNode.getType(), args).toString())) {
1605 return null;
1606 }
1607
1608 if (specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
1609 return null;
1610 }
1611
1612 for (final Node arg : runtimeNode.getArgs()) {
1613 load(arg).convert(Type.OBJECT); //TODO this should not be necessary below Lower
1614 }
1615
1616 method.invokeStatic(
1617 CompilerConstants.className(ScriptRuntime.class),
1618 runtimeNode.getRequest().toString(),
1619 new FunctionSignature(
1620 false,
1621 runtimeNode.getType(),
1622 runtimeNode.getArgs().size()).toString());
1623 method.convert(runtimeNode.getType());
1624 method.store(runtimeNode.getSymbol());
1625
1626 return null;
1627 }
1628
1629 @Override
1630 public Node enter(final SplitNode splitNode) {
1631 if (splitNode.testResolved()) {
1632 return null;
1633 }
1634
1635 final CompileUnit splitCompileUnit = splitNode.getCompileUnit();
1636
1637 final FunctionNode fn = getCurrentFunctionNode();
1638 final String className = splitCompileUnit.getUnitClassName();
1639 final String name = splitNode.getName();
1640
1641 final Class<?> rtype = fn.getReturnType().getTypeClass();
1642 final Class<?>[] ptypes = fn.isVarArg() ?
1643 new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class, Object.class} :
1644 new Class<?>[] {Object.class, ScriptFunction.class, ScriptObject.class};
1645
1646 setCurrentCompileUnit(splitCompileUnit);
1647 splitNode.setCompileUnit(splitCompileUnit);
1648
1649 final Call splitCall = staticCallNoLookup(
1650 className,
1651 name,
1652 methodDescriptor(rtype, ptypes));
1653
1654 setCurrentMethodEmitter(
1655 splitCompileUnit.getClassEmitter().method(
1656 EnumSet.of(Flag.PUBLIC, Flag.STATIC),
1657 name,
1658 rtype,
1659 ptypes));
1660
1661 method.setFunctionNode(fn);
1662 method.setSplitNode(splitNode);
1663 splitNode.setMethodEmitter(method);
1664
1665 final MethodEmitter caller = splitNode.getCaller();
1666 caller.loadThis();
1667 caller.loadCallee();
1668 caller.loadScope();
1669 if (fn.isVarArg()) {
1670 caller.loadArguments();
1671 }
1672 caller.invoke(splitCall);
1673 caller.storeResult();
1674
1675 method.begin();
1676
1677 method.loadUndefined(fn.getReturnType());
1678 method.storeResult();
1679
1680 fixScopeSlot();
1681
1682 return splitNode;
1683 }
1684
1685 private void fixScopeSlot() {
1686 if (getCurrentFunctionNode().getScopeNode().getSymbol().getSlot() != SCOPE.slot()) {
1687 // TODO hack to move the scope to the expected slot (that's needed because split methods reuse the same slots as the root method)
1688 method.load(Type.typeFor(ScriptObject.class), SCOPE.slot());
1689 method.storeScope();
1690 }
1691 }
1692
1693 @Override
1694 public Node leave(final SplitNode splitNode) {
1695 try {
1696 // Wrap up this method.
1697 method.loadResult();
1698 method._return(getCurrentFunctionNode().getReturnType());
1699 method.end();
1700 } catch (final Throwable t) {
1701 Context.printStackTrace(t);
1702 final VerifyError e = new VerifyError("Code generation bug in \"" + splitNode.getName() + "\": likely stack misaligned: " + t + " " + compiler.getSource().getName());
1703 e.initCause(t);
1704 throw e;
1705 }
1706
1707 // Handle return from split method if there was one.
1708 final MethodEmitter caller = splitNode.getCaller();
1709 final List<Label> targets = splitNode.getExternalTargets();
1710 final int targetCount = targets.size();
1711
1712 if (splitNode.hasReturn() || targetCount > 0) {
1713
1714 caller.loadScope();
1715 caller.checkcast(Scope.class);
1716 caller.invoke(Scope.GET_SPLIT_STATE);
1717
1718 // Split state is -1 for no split state, 0 for return, 1..n+1 for break/continue
1719 final Label breakLabel = new Label("no_split_state");
1720 final int low = splitNode.hasReturn() ? 0 : 1;
1721 final int labelCount = targetCount + 1 - low;
1722 final Label[] labels = new Label[labelCount];
1723
1724 for (int i = 0; i < labelCount; i++) {
1725 labels[i] = new Label("split_state_" + i);
1726 }
1727
1728 caller.tableSwitch(low, targetCount, breakLabel, labels);
1729 for (int i = low; i <= targetCount; i++) {
1730 caller.label(labels[i - low]);
1731 if (i == 0) {
1732 caller.loadResult();
1733 caller._return(getCurrentFunctionNode().getReturnType());
1734 } else {
1735 // Clear split state.
1736 caller.loadScope();
1737 caller.checkcast(Scope.class);
1738 caller.load(-1);
1739 caller.invoke(Scope.SET_SPLIT_STATE);
1740 caller.splitAwareGoto(targets.get(i - 1));
1741 }
1742 }
1743
1744 caller.label(breakLabel);
1745 }
1746
1747 return splitNode;
1748 }
1749
1750 @Override
1751 public Node enter(final SwitchNode switchNode) {
1752 if (switchNode.testResolved()) {
1753 return null;
1754 }
1755
1756 final Node expression = switchNode.getExpression();
1757 final Symbol tag = switchNode.getTag();
1758 final boolean allInteger = tag.getSymbolType().isInteger();
1759 final List<CaseNode> cases = switchNode.getCases();
1760 final CaseNode defaultCase = switchNode.getDefaultCase();
1761 final Label breakLabel = switchNode.getBreakLabel();
1762
1763 Label defaultLabel = breakLabel;
1764 boolean hasDefault = false;
1765
1766 if (defaultCase != null) {
1767 defaultLabel = defaultCase.getEntry();
1768 hasDefault = true;
1769 }
1770
1771 if (cases.isEmpty()) {
1772 method.label(breakLabel);
1773 return null;
1774 }
1775
1776 if (allInteger) {
1777 // Tree for sorting values.
1778 final TreeMap<Integer, Label> tree = new TreeMap<>();
1779
1780 // Build up sorted tree.
1781 for (final CaseNode caseNode : cases) {
1782 final Node test = caseNode.getTest();
1783
1784 if (test != null) {
1785 final Integer value = (Integer)((LiteralNode<?>) test).getValue();
1786 final Label entry = caseNode.getEntry();
1787
1788 // Take first duplicate.
1789 if (!(tree.containsKey(value))) {
1790 tree.put(value, entry);
1791 }
1792 }
1793 }
1794
1795 // Copy values and labels to arrays.
1796 final int size = tree.size();
1797 final Integer[] values = tree.keySet().toArray(new Integer[size]);
1798 final Label[] labels = tree.values().toArray(new Label[size]);
1799
1800 // Discern low, high and range.
1801 final int lo = values[0];
1802 final int hi = values[size - 1];
1803 final int range = hi - lo + 1;
1804
1805 // Find an unused value for default.
1806 int deflt = Integer.MIN_VALUE;
1807 for (final int value : values) {
1808 if (deflt == value) {
1809 deflt++;
1810 } else if (deflt < value) {
1811 break;
1812 }
1813 }
1814
1815 // Load switch expression.
1816 load(expression);
1817 final Type type = expression.getType();
1818
1819 // If expression not int see if we can convert, if not use deflt to trigger default.
1820 if (!type.isInteger()) {
1821 method.load(deflt);
1822 method.invoke(staticCallNoLookup(ScriptRuntime.class, "switchTagAsInt", int.class, type.getTypeClass(), int.class));
1823 }
1824
1825 // If reasonable size and not too sparse (80%), use table otherwise use lookup.
1826 if (range > 0 && range < 4096 && range < (size * 5 / 4)) {
1827 final Label[] table = new Label[range];
1828 Arrays.fill(table, defaultLabel);
1829
1830 for (int i = 0; i < size; i++) {
1831 final int value = values[i];
1832 table[value - lo] = labels[i];
1833 }
1834
1835 method.tableSwitch(lo, hi, defaultLabel, table);
1836 } else {
1837 final int[] ints = new int[size];
1838
1839 for (int i = 0; i < size; i++) {
1840 ints[i] = values[i];
1841 }
1842
1843 method.lookupSwitch(defaultLabel, ints, labels);
1844 }
1845 } else {
1846 load(expression);
1847
1848 if (expression.getType().isInteger()) {
1849 method.convert(Type.NUMBER).dup();
1850 method.store(tag);
1851 method.conditionalJump(MethodEmitter.Condition.NE, true, defaultLabel);
1852 } else {
1853 method.store(tag);
1854 }
1855
1856 for (final CaseNode caseNode : cases) {
1857 final Node test = caseNode.getTest();
1858
1859 if (test != null) {
1860 method.load(tag);
1861 load(test);
1862 method.invoke(ScriptRuntime.EQ_STRICT);
1863 method.ifne(caseNode.getEntry());
1864 }
1865 }
1866
1867 method._goto(hasDefault ? defaultLabel : breakLabel);
1868 }
1869
1870 for (final CaseNode caseNode : cases) {
1871 method.label(caseNode.getEntry());
1872 caseNode.getBody().accept(this);
1873 }
1874
1875 if (!switchNode.isTerminal()) {
1876 method.label(breakLabel);
1877 }
1878
1879 return null;
1880 }
1881
1882 @Override
1883 public Node enter(final ThrowNode throwNode) {
1884 if (throwNode.testResolved()) {
1885 return null;
1886 }
1887
1888 method._new(ECMAException.class).dup();
1889
1890 final Node expression = throwNode.getExpression();
1891 final Source source = compiler.getSource();
1892 final int position = throwNode.position();
1893 final int line = source.getLine(position);
1894 final int column = source.getColumn(position);
1895
1896 load(expression);
1897 assert expression.getType().isObject();
1898
1899 method.load(source.getName());
1900 method.load(line);
1901 method.load(column);
1902 method.invoke(ECMAException.THROW_INIT);
1903
1904 method.athrow();
1905
1906 return null;
1907 }
1908
1909 @Override
1910 public Node enter(final TryNode tryNode) {
1911 if (tryNode.testResolved()) {
1912 return null;
1913 }
1914
1915 final Block body = tryNode.getBody();
1916 final List<Block> catchBlocks = tryNode.getCatchBlocks();
1917 final Symbol symbol = tryNode.getException();
1918 final Label entry = new Label("try");
1919 final Label recovery = new Label("catch");
1920 final Label exit = tryNode.getExit();
1921 final Label skip = new Label("skip");
1922
1923 method.label(entry);
1924
1925 body.accept(this);
1926
1927 if (!body.hasTerminalFlags()) {
1928 method._goto(skip);
1929 }
1930
1931 method.label(exit);
1932
1933 method._catch(recovery);
1934 method.store(symbol);
1935
1936 for (int i = 0; i < catchBlocks.size(); i++) {
1937 final Block saveBlock = getCurrentBlock();
1938 final Block catchBlock = catchBlocks.get(i);
1939
1940 setCurrentBlock(catchBlock);
1941
1942 try {
1943 enter(catchBlock);
1944
1945 final CatchNode catchNode = (CatchNode)catchBlocks.get(i).getStatements().get(0);
1946 final IdentNode exception = catchNode.getException();
1947 final Node exceptionCondition = catchNode.getExceptionCondition();
1948 final Block catchBody = catchNode.getBody();
1949
1950 if (catchNode.isSyntheticRethrow()) {
1951 // Generate catch body (inlined finally) and rethrow exception
1952 catchBody.accept(this);
1953 method.load(symbol).athrow();
1954 continue;
1955 }
1956
1957 new Store<IdentNode>(exception) {
1958 @Override
1959 protected void evaluate() {
1960 /*
1961 * If caught object is an instance of ECMAException, then
1962 * bind obj.thrown to the script catch var. Or else bind the
1963 * caught object itself to the script catch var.
1964 */
1965 final Label notEcmaException = new Label("no_ecma_exception");
1966
1967 method.load(symbol).dup()._instanceof(ECMAException.class).ifeq(notEcmaException);
1968 method.checkcast(ECMAException.class); //TODO is this necessary?
1969 method.getField(ECMAException.THROWN);
1970 method.label(notEcmaException);
1971 }
1972 }.store();
1973
1974 final Label next;
1975
1976 if (exceptionCondition != null) {
1977 next = new Label("next");
1978 load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
1979 } else {
1980 next = null;
1981 }
1982
1983 catchBody.accept(this);
1984
1985 if (i + 1 != catchBlocks.size() && !catchBody.hasTerminalFlags()) {
1986 method._goto(skip);
1987 }
1988
1989 if (next != null) {
1990 if (i + 1 == catchBlocks.size()) {
1991 // no next catch block - rethrow if condition failed
1992 method._goto(skip);
1993 method.label(next);
1994 method.load(symbol).athrow();
1995 } else {
1996 method.label(next);
1997 }
1998 }
1999
2000 leave(catchBlock);
2001 } finally {
2002 setCurrentBlock(saveBlock);
2003 }
2004 }
2005
2006 method.label(skip);
2007 method._try(entry, exit, recovery, Throwable.class);
2008
2009 // Finally body is always inlined elsewhere so it doesn't need to be emitted
2010
2011 return null;
2012 }
2013
2014 @Override
2015 public Node enter(final VarNode varNode) {
2016 final Node init = varNode.getInit();
2017
2018 if (varNode.testResolved() || init == null) {
2019 return null;
2020 }
2021
2022 final Symbol varSymbol = varNode.getSymbol();
2023
2024 assert varSymbol != null : "variable node " + varNode + " requires a symbol";
2025
2026 assert method != null;
2027
2028 final boolean needsScope = varSymbol.isScope();
2029 if (needsScope) {
2030 method.loadScope();
2031 }
2032
2033 load(init);
2034
2035 if (needsScope) {
2036 int flags = CALLSITE_SCOPE | getCallSiteFlags();
2037 if (varNode.isFunctionVarNode()) {
2038 flags |= CALLSITE_FUNCTION_DECLARATION;
2039 }
2040 final IdentNode identNode = varNode.getName();
2041 final Type type = identNode.getType();
2042 if(varSymbol.isFastScope(getCurrentFunctionNode())) {
2043 storeFastScopeVar(type, varSymbol, flags);
2044 } else {
2045 method.dynamicSet(type, identNode.getName(), flags);
2046 }
2047 } else {
2048 assert varNode.getType() == varNode.getName().getType() : "varNode type=" + varNode.getType() + " nametype=" + varNode.getName().getType() + " inittype=" + init.getType();
2049
2050 method.convert(varNode.getType()); // aw: convert moved here
2051 method.store(varSymbol);
2052 }
2053
2054 return null;
2055 }
2056
2057 @Override
2058 public Node enter(final WhileNode whileNode) {
2059 if (whileNode.testResolved()) {
2060 return null;
2061 }
2062
2063 final Node test = whileNode.getTest();
2064 final Block body = whileNode.getBody();
2065 final Label breakLabel = whileNode.getBreakLabel();
2066 final Label continueLabel = whileNode.getContinueLabel();
2067 final Label loopLabel = new Label("loop");
2068
2069 if (!(whileNode instanceof DoWhileNode)) {
2070 method._goto(continueLabel);
2071 }
2072
2073 method.label(loopLabel);
2074 body.accept(this);
2075 if (!whileNode.isTerminal()) {
2076 method.label(continueLabel);
2077 new BranchOptimizer(this, method).execute(test, loopLabel, true);
2078 method.label(breakLabel);
2079 }
2080
2081 return null;
2082 }
2083
2084 private void closeWith() {
2085 method.loadScope();
2086 method.invoke(ScriptRuntime.CLOSE_WITH);
2087 method.storeScope();
2088 }
2089
2090 @Override
2091 public Node enter(final WithNode withNode) {
2092 if (withNode.testResolved()) {
2093 return null;
2094 }
2095
2096 final Node expression = withNode.getExpression();
2097 final Node body = withNode.getBody();
2098
2099 final Label tryLabel = new Label("with_try");
2100 final Label endLabel = new Label("with_end");
2101 final Label catchLabel = new Label("with_catch");
2102 final Label exitLabel = new Label("with_exit");
2103
2104 method.label(tryLabel);
2105
2106 method.loadScope();
2107 load(expression);
2108
2109 assert expression.getType().isObject() : "with expression needs to be object: " + expression;
2110
2111 method.invoke(ScriptRuntime.OPEN_WITH);
2112 method.storeScope();
2113
2114 body.accept(this);
2115
2116 if (!body.isTerminal()) {
2117 closeWith();
2118 method._goto(exitLabel);
2119 }
2120
2121 method.label(endLabel);
2122
2123 method._catch(catchLabel);
2124 closeWith();
2125 method.athrow();
2126
2127 method.label(exitLabel);
2128
2129 method._try(tryLabel, endLabel, catchLabel);
2130
2131 return null;
2132 }
2133
2134 @Override
2135 public Node enterADD(final UnaryNode unaryNode) {
2136 if (unaryNode.testResolved()) {
2137 return null;
2138 }
2139
2140 load(unaryNode.rhs());
2141 assert unaryNode.rhs().getType().isNumber();
2142 method.store(unaryNode.getSymbol());
2143
2144 return null;
2145 }
2146
2147 @Override
2148 public Node enterBIT_NOT(final UnaryNode unaryNode) {
2149 if (unaryNode.testResolved()) {
2150 return null;
2151 }
2152
2153 load(unaryNode.rhs()).convert(Type.INT).load(-1).xor().store(unaryNode.getSymbol());
2154
2155 return null;
2156 }
2157
2158 // do this better with convert calls to method. TODO
2159 @Override
2160 public Node enterCONVERT(final UnaryNode unaryNode) {
2161 if (unaryNode.testResolved()) {
2162 return null;
2163 }
2164
2165 final Node rhs = unaryNode.rhs();
2166 final Type to = unaryNode.getType();
2167
2168 if (to.isObject() && rhs instanceof LiteralNode) {
2169 final LiteralNode<?> literalNode = (LiteralNode<?>)rhs;
2170 final Object value = literalNode.getValue();
2171
2172 if (value instanceof Number) {
2173 assert !to.isArray() : "type hygiene - cannot convert number to array: (" + to.getTypeClass().getSimpleName() + ')' + value;
2174 if (value instanceof Integer) {
2175 method.load((Integer)value);
2176 } else if (value instanceof Long) {
2177 method.load((Long)value);
2178 } else if (value instanceof Double) {
2179 method.load((Double)value);
2180 } else {
2181 assert false;
2182 }
2183 method.convert(Type.OBJECT);
2184 } else if (value instanceof Boolean) {
2185 method.getField(staticField(Boolean.class, value.toString().toUpperCase(), Boolean.class));
2186 } else {
2187 load(rhs);
2188 method.convert(unaryNode.getType());
2189 }
2190 } else {
2191 load(rhs);
2192 method.convert(unaryNode.getType());
2193 }
2194
2195 method.store(unaryNode.getSymbol());
2196
2197 return null;
2198 }
2199
2200 @Override
2201 public Node enterDECINC(final UnaryNode unaryNode) {
2202 if (unaryNode.testResolved()) {
2203 return null;
2204 }
2205
2206 final Node rhs = unaryNode.rhs();
2207 final Type type = unaryNode.getType();
2208 final TokenType tokenType = unaryNode.tokenType();
2209 final boolean isPostfix = tokenType == TokenType.DECPOSTFIX || tokenType == TokenType.INCPOSTFIX;
2210 final boolean isIncrement = tokenType == TokenType.INCPREFIX || tokenType == TokenType.INCPOSTFIX;
2211
2212 assert !type.isObject();
2213
2214 new SelfModifyingStore<UnaryNode>(unaryNode, rhs) {
2215
2216 @Override
2217 protected void evaluate() {
2218 load(rhs, true);
2219
2220 method.convert(type);
2221 if (!isPostfix) {
2222 if (type.isInteger()) {
2223 method.load(isIncrement ? 1 : -1);
2224 } else if (type.isLong()) {
2225 method.load(isIncrement ? 1L : -1L);
2226 } else {
2227 method.load(isIncrement ? 1.0 : -1.0);
2228 }
2229 method.add();
2230 }
2231 }
2232
2233 @Override
2234 protected void storeNonDiscard() {
2235 super.storeNonDiscard();
2236 if (isPostfix) {
2237 if (type.isInteger()) {
2238 method.load(isIncrement ? 1 : -1);
2239 } else if (type.isLong()) {
2240 method.load(isIncrement ? 1L : 1L);
2241 } else {
2242 method.load(isIncrement ? 1.0 : -1.0);
2243 }
2244 method.add();
2245 }
2246 }
2247 }.store();
2248
2249 return null;
2250 }
2251
2252 @Override
2253 public Node enterDISCARD(final UnaryNode unaryNode) {
2254 if (unaryNode.testResolved()) {
2255 return null;
2256 }
2257
2258 final Node rhs = unaryNode.rhs();
2259
2260 load(rhs);
2261
2262 if (rhs.shouldDiscard()) {
2263 method.pop();
2264 }
2265
2266 return null;
2267 }
2268
2269 @Override
2270 public Node enterNEW(final UnaryNode unaryNode) {
2271 if (unaryNode.testResolved()) {
2272 return null;
2273 }
2274
2275 final CallNode callNode = (CallNode)unaryNode.rhs();
2276 final List<Node> args = callNode.getArgs();
2277
2278 // Load function reference.
2279 load(callNode.getFunction()).convert(Type.OBJECT); // must detect type error
2280
2281 // Load arguments.
2282 loadArgs(args);
2283
2284 method.dynamicNew(args.size(), getCallSiteFlags());
2285 method.store(unaryNode.getSymbol());
2286
2287 return null;
2288 }
2289
2290 @Override
2291 public Node enterNOT(final UnaryNode unaryNode) {
2292 if (unaryNode.testResolved()) {
2293 return null;
2294 }
2295
2296 final Node rhs = unaryNode.rhs();
2297
2298 load(rhs);
2299
2300 final Label trueLabel = new Label("true");
2301 final Label afterLabel = new Label("after");
2302
2303 method.convert(Type.BOOLEAN);
2304 method.ifne(trueLabel);
2305 method.load(true);
2306 method._goto(afterLabel);
2307 method.label(trueLabel);
2308 method.load(false);
2309 method.label(afterLabel);
2310 method.store(unaryNode.getSymbol());
2311
2312 return null;
2313 }
2314
2315 @Override
2316 public Node enterSUB(final UnaryNode unaryNode) {
2317 if (unaryNode.testResolved()) {
2318 return null;
2319 }
2320
2321 load(unaryNode.rhs()).neg().store(unaryNode.getSymbol());
2322
2323 return null;
2324 }
2325
2326 private Node enterNumericAdd(final Node lhs, final Node rhs, final Type type, final Symbol symbol) {
2327 assert lhs.getType().equals(rhs.getType()) && lhs.getType().equals(type);
2328 load(lhs);
2329 load(rhs);
2330 method.add();
2331 method.store(symbol);
2332
2333 return null;
2334 }
2335
2336 @Override
2337 public Node enterADD(final BinaryNode binaryNode) {
2338 if (binaryNode.testResolved()) {
2339 return null;
2340 }
2341
2342 final Node lhs = binaryNode.lhs();
2343 final Node rhs = binaryNode.rhs();
2344
2345 final Type type = binaryNode.getType();
2346 if (type.isNumeric()) {
2347 enterNumericAdd(lhs, rhs, type, binaryNode.getSymbol());
2348 } else {
2349 load(lhs).convert(Type.OBJECT);
2350 load(rhs).convert(Type.OBJECT);
2351 method.add();
2352 method.store(binaryNode.getSymbol());
2353 }
2354
2355 return null;
2356 }
2357
2358 private Node enterAND_OR(final BinaryNode binaryNode) {
2359 if (binaryNode.testResolved()) {
2360 return null;
2361 }
2362
2363 final Node lhs = binaryNode.lhs();
2364 final Node rhs = binaryNode.rhs();
2365
2366 final Label skip = new Label("skip");
2367
2368 load(lhs).convert(Type.OBJECT).dup().convert(Type.BOOLEAN);
2369
2370 if (binaryNode.tokenType() == TokenType.AND) {
2371 method.ifeq(skip);
2372 } else {
2373 method.ifne(skip);
2374 }
2375
2376 method.pop();
2377 load(rhs).convert(Type.OBJECT);
2378 method.label(skip);
2379 method.store(binaryNode.getSymbol());
2380
2381 return null;
2382 }
2383
2384 @Override
2385 public Node enterAND(final BinaryNode binaryNode) {
2386 return enterAND_OR(binaryNode);
2387 }
2388
2389 @Override
2390 public Node enterASSIGN(final BinaryNode binaryNode) {
2391 if (binaryNode.testResolved()) {
2392 return null;
2393 }
2394
2395 final Node lhs = binaryNode.lhs();
2396 final Node rhs = binaryNode.rhs();
2397
2398 final Type lhsType = lhs.getType();
2399 final Type rhsType = rhs.getType();
2400
2401 if (!lhsType.isEquivalentTo(rhsType)) {
2402 //this is OK if scoped, only locals are wrong
2403 assert !(lhs instanceof IdentNode) || lhs.getSymbol().isScope() : new ASTWriter(binaryNode);
2404 }
2405
2406 new Store<BinaryNode>(binaryNode, lhs) {
2407 @Override
2408 protected void evaluate() {
2409 load(rhs);
2410 }
2411 }.store();
2412
2413 return null;
2414 }
2415
2416 /**
2417 * Helper class for assignment ops, e.g. *=, += and so on..
2418 */
2419 private abstract class AssignOp extends SelfModifyingStore<BinaryNode> {
2420
2421 /** The type of the resulting operation */
2422 private final Type opType;
2423
2424 /**
2425 * Constructor
2426 *
2427 * @param node the assign op node
2428 */
2429 AssignOp(final BinaryNode node) {
2430 this(node.getType(), node);
2431 }
2432
2433 /**
2434 * Constructor
2435 *
2436 * @param opType type of the computation - overriding the type of the node
2437 * @param node the assign op node
2438 */
2439 AssignOp(final Type opType, final BinaryNode node) {
2440 super(node, node.lhs());
2441 this.opType = opType;
2442 }
2443
2444 @Override
2445 public void store() {
2446 if (assignNode.testResolved()) {
2447 return;
2448 }
2449 super.store();
2450 }
2451
2452 protected abstract void op();
2453
2454 @Override
2455 protected void evaluate() {
2456 load(assignNode.lhs(), true).convert(opType);
2457 load(assignNode.rhs()).convert(opType);
2458 op();
2459 method.convert(assignNode.getType());
2460 }
2461 }
2462
2463 @Override
2464 public Node enterASSIGN_ADD(final BinaryNode binaryNode) {
2465 assert RuntimeNode.Request.ADD.canSpecialize();
2466 final boolean specialize = binaryNode.getType() == Type.OBJECT;
2467
2468 new AssignOp(binaryNode) {
2469 @Override
2470 protected boolean isSelfModifying() {
2471 return !specialize;
2472 }
2473
2474 @Override
2475 protected void op() {
2476 method.add();
2477 }
2478
2479 @Override
2480 protected void evaluate() {
2481 if (specialize && specializationCheck(Request.ADD, assignNode, Arrays.asList(assignNode.lhs(), assignNode.rhs()))) {
2482 return;
2483 }
2484 super.evaluate();
2485 }
2486 }.store();
2487
2488 return null;
2489 }
2490
2491 @Override
2492 public Node enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
2493 new AssignOp(Type.INT, binaryNode) {
2494 @Override
2495 protected void op() {
2496 method.and();
2497 }
2498 }.store();
2499
2500 return null;
2501 }
2502
2503 @Override
2504 public Node enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
2505 new AssignOp(Type.INT, binaryNode) {
2506 @Override
2507 protected void op() {
2508 method.or();
2509 }
2510 }.store();
2511
2512 return null;
2513 }
2514
2515 @Override
2516 public Node enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
2517 new AssignOp(Type.INT, binaryNode) {
2518 @Override
2519 protected void op() {
2520 method.xor();
2521 }
2522 }.store();
2523
2524 return null;
2525 }
2526
2527 @Override
2528 public Node enterASSIGN_DIV(final BinaryNode binaryNode) {
2529 new AssignOp(binaryNode) {
2530 @Override
2531 protected void op() {
2532 method.div();
2533 }
2534 }.store();
2535
2536 return null;
2537 }
2538
2539 @Override
2540 public Node enterASSIGN_MOD(final BinaryNode binaryNode) {
2541 new AssignOp(binaryNode) {
2542 @Override
2543 protected void op() {
2544 method.rem();
2545 }
2546 }.store();
2547
2548 return null;
2549 }
2550
2551 @Override
2552 public Node enterASSIGN_MUL(final BinaryNode binaryNode) {
2553 new AssignOp(binaryNode) {
2554 @Override
2555 protected void op() {
2556 method.mul();
2557 }
2558 }.store();
2559
2560 return null;
2561 }
2562
2563 @Override
2564 public Node enterASSIGN_SAR(final BinaryNode binaryNode) {
2565 new AssignOp(Type.INT, binaryNode) {
2566 @Override
2567 protected void op() {
2568 method.sar();
2569 }
2570 }.store();
2571
2572 return null;
2573 }
2574
2575 @Override
2576 public Node enterASSIGN_SHL(final BinaryNode binaryNode) {
2577 new AssignOp(Type.INT, binaryNode) {
2578 @Override
2579 protected void op() {
2580 method.shl();
2581 }
2582 }.store();
2583
2584 return null;
2585 }
2586
2587 @Override
2588 public Node enterASSIGN_SHR(final BinaryNode binaryNode) {
2589 new AssignOp(Type.INT, binaryNode) {
2590 @Override
2591 protected void op() {
2592 method.shr();
2593 method.convert(Type.LONG).load(0xffff_ffffL).and();
2594 }
2595 }.store();
2596
2597 return null;
2598 }
2599
2600 @Override
2601 public Node enterASSIGN_SUB(final BinaryNode binaryNode) {
2602 new AssignOp(binaryNode) {
2603 @Override
2604 protected void op() {
2605 method.sub();
2606 }
2607 }.store();
2608
2609 return null;
2610 }
2611
2612 /**
2613 * Helper class for binary arithmetic ops
2614 */
2615 private abstract class BinaryArith {
2616
2617 protected abstract void op();
2618
2619 protected void evaluate(final BinaryNode node) {
2620 if (node.testResolved()) {
2621 return;
2622 }
2623 load(node.lhs());
2624 load(node.rhs());
2625 op();
2626 method.store(node.getSymbol());
2627 }
2628 }
2629
2630 @Override
2631 public Node enterBIT_AND(final BinaryNode binaryNode) {
2632 new BinaryArith() {
2633 @Override
2634 protected void op() {
2635 method.and();
2636 }
2637 }.evaluate(binaryNode);
2638
2639 return null;
2640 }
2641
2642 @Override
2643 public Node enterBIT_OR(final BinaryNode binaryNode) {
2644 new BinaryArith() {
2645 @Override
2646 protected void op() {
2647 method.or();
2648 }
2649 }.evaluate(binaryNode);
2650
2651 return null;
2652 }
2653
2654 @Override
2655 public Node enterBIT_XOR(final BinaryNode binaryNode) {
2656 new BinaryArith() {
2657 @Override
2658 protected void op() {
2659 method.xor();
2660 }
2661 }.evaluate(binaryNode);
2662
2663 return null;
2664 }
2665
2666 private Node enterComma(final BinaryNode binaryNode) {
2667 if (binaryNode.testResolved()) {
2668 return null;
2669 }
2670
2671 final Node lhs = binaryNode.lhs();
2672 final Node rhs = binaryNode.rhs();
2673
2674 load(lhs);
2675 load(rhs);
2676 method.store(binaryNode.getSymbol());
2677
2678 return null;
2679 }
2680
2681 @Override
2682 public Node enterCOMMARIGHT(final BinaryNode binaryNode) {
2683 return enterComma(binaryNode);
2684 }
2685
2686 @Override
2687 public Node enterCOMMALEFT(final BinaryNode binaryNode) {
2688 return enterComma(binaryNode);
2689 }
2690
2691 @Override
2692 public Node enterDIV(final BinaryNode binaryNode) {
2693 new BinaryArith() {
2694 @Override
2695 protected void op() {
2696 method.div();
2697 }
2698 }.evaluate(binaryNode);
2699
2700 return null;
2701 }
2702
2703 private Node enterCmp(final Node lhs, final Node rhs, final Condition cond, final Type type, final Symbol symbol) {
2704 final Type lhsType = lhs.getType();
2705 final Type rhsType = rhs.getType();
2706
2707 final Type widest = Type.widest(lhsType, rhsType);
2708 assert widest.isNumeric() || widest.isBoolean() : widest;
2709
2710 load(lhs);
2711 method.convert(widest);
2712 load(rhs);
2713 method.convert(widest);
2714
2715 final Label trueLabel = new Label("trueLabel");
2716 final Label afterLabel = new Label("skip");
2717
2718 method.conditionalJump(cond, trueLabel);
2719
2720 method.load(Boolean.FALSE);
2721 method._goto(afterLabel);
2722 method.label(trueLabel);
2723 method.load(Boolean.TRUE);
2724 method.label(afterLabel);
2725
2726 method.convert(type);
2727 method.store(symbol);
2728
2729 return null;
2730 }
2731
2732 private Node enterCmp(final BinaryNode binaryNode, final Condition cond) {
2733 if (binaryNode.testResolved()) {
2734 return null;
2735 }
2736 return enterCmp(binaryNode.lhs(), binaryNode.rhs(), cond, binaryNode.getType(), binaryNode.getSymbol());
2737 }
2738
2739 @Override
2740 public Node enterEQ(final BinaryNode binaryNode) {
2741 return enterCmp(binaryNode, Condition.EQ);
2742 }
2743
2744 @Override
2745 public Node enterEQ_STRICT(final BinaryNode binaryNode) {
2746 return enterCmp(binaryNode, Condition.EQ);
2747 }
2748
2749 @Override
2750 public Node enterGE(final BinaryNode binaryNode) {
2751 return enterCmp(binaryNode, Condition.GE);
2752 }
2753
2754 @Override
2755 public Node enterGT(final BinaryNode binaryNode) {
2756 return enterCmp(binaryNode, Condition.GT);
2757 }
2758
2759 @Override
2760 public Node enterLE(final BinaryNode binaryNode) {
2761 return enterCmp(binaryNode, Condition.LE);
2762 }
2763
2764 @Override
2765 public Node enterLT(final BinaryNode binaryNode) {
2766 return enterCmp(binaryNode, Condition.LT);
2767 }
2768
2769 @Override
2770 public Node enterMOD(final BinaryNode binaryNode) {
2771 new BinaryArith() {
2772 @Override
2773 protected void op() {
2774 method.rem();
2775 }
2776 }.evaluate(binaryNode);
2777
2778 return null;
2779 }
2780
2781 @Override
2782 public Node enterMUL(final BinaryNode binaryNode) {
2783 new BinaryArith() {
2784 @Override
2785 protected void op() {
2786 method.mul();
2787 }
2788 }.evaluate(binaryNode);
2789
2790 return null;
2791 }
2792
2793 @Override
2794 public Node enterNE(final BinaryNode binaryNode) {
2795 return enterCmp(binaryNode, Condition.NE);
2796 }
2797
2798 @Override
2799 public Node enterNE_STRICT(final BinaryNode binaryNode) {
2800 return enterCmp(binaryNode, Condition.NE);
2801 }
2802
2803 @Override
2804 public Node enterOR(final BinaryNode binaryNode) {
2805 return enterAND_OR(binaryNode);
2806 }
2807
2808 @Override
2809 public Node enterSAR(final BinaryNode binaryNode) {
2810 new BinaryArith() {
2811 @Override
2812 protected void op() {
2813 method.sar();
2814 }
2815 }.evaluate(binaryNode);
2816
2817 return null;
2818 }
2819
2820 @Override
2821 public Node enterSHL(final BinaryNode binaryNode) {
2822 new BinaryArith() {
2823 @Override
2824 protected void op() {
2825 method.shl();
2826 }
2827 }.evaluate(binaryNode);
2828
2829 return null;
2830 }
2831
2832 @Override
2833 public Node enterSHR(final BinaryNode binaryNode) {
2834 new BinaryArith() {
2835 @Override
2836 protected void op() {
2837 method.shr();
2838 method.convert(Type.LONG).load(0xffff_ffffL).and();
2839 }
2840 }.evaluate(binaryNode);
2841
2842 return null;
2843 }
2844
2845 @Override
2846 public Node enterSUB(final BinaryNode binaryNode) {
2847 new BinaryArith() {
2848 @Override
2849 protected void op() {
2850 method.sub();
2851 }
2852 }.evaluate(binaryNode);
2853
2854 return null;
2855 }
2856
2857 /*
2858 * Ternary visits.
2859 */
2860 @Override
2861 public Node enter(final TernaryNode ternaryNode) {
2862 if (ternaryNode.testResolved()) {
2863 return null;
2864 }
2865
2866 final Node lhs = ternaryNode.lhs();
2867 final Node rhs = ternaryNode.rhs();
2868 final Node third = ternaryNode.third();
2869
2870 final Symbol symbol = ternaryNode.getSymbol();
2871 final Label falseLabel = new Label("ternary_false");
2872 final Label exitLabel = new Label("ternary_exit");
2873
2874 final Type widest = Type.widest(rhs.getType(), third.getType());
2875
2876 load(lhs);
2877 assert lhs.getType().isBoolean() : "lhs in ternary must be boolean";
2878
2879 // we still keep the conversion here as the AccessSpecializer can have separated the types, e.g. var y = x ? x=55 : 17
2880 // 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
2881 // symmetry for the temporary slot for this TernaryNode. This is evidence that we assign types and explicit conversions
2882 // to early, or Apply the AccessSpecializer too late. We are mostly probably looking for a separate type pass to
2883 // do this property. Then we never need any conversions in CodeGenerator
2884 method.ifeq(falseLabel);
2885 load(rhs);
2886 method.convert(widest);
2887 method._goto(exitLabel);
2888 method.label(falseLabel);
2889 load(third);
2890 method.convert(widest);
2891 method.label(exitLabel);
2892 method.store(symbol);
2893
2894 return null;
2895 }
2896
2897 /**
2898 * Generate all shared scope calls generated during codegen.
2899 */
2900 protected void generateScopeCalls() {
2901 for (final SharedScopeCall scopeAccess : scopeCalls.values()) {
2902 scopeAccess.generateScopeCall();
2903 }
2904 }
2905
2906 /**
2907 * Get a shared static method representing a dynamic scope callsite.
2908 *
2909 * @param symbol the symbol
2910 * @param valueType the value type of the symbol
2911 * @param returnType the return type
2912 * @param paramTypes the parameter types
2913 * @param flags the callsite flags
2914 * @return an object representing a shared scope call
2915 */
2916 private SharedScopeCall getScopeCall(final Symbol symbol, final Type valueType, final Type returnType,
2917 final Type[] paramTypes, final int flags) {
2918
2919 final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags);
2920 if (scopeCalls.containsKey(scopeCall)) {
2921 return scopeCalls.get(scopeCall);
2922 }
2923 scopeCall.setClassAndName(compileUnit, compiler);
2924 scopeCalls.put(scopeCall, scopeCall);
2925 return scopeCall;
2926 }
2927
2928 /**
2929 * Get a shared static method representing a dynamic scope get access.
2930 *
2931 * @param type the type of the variable
2932 * @param symbol the symbol
2933 * @param flags the callsite flags
2934 * @return an object representing a shared scope call
2935 */
2936 private SharedScopeCall getScopeGet(final Type type, final Symbol symbol, final int flags) {
2937
2938 final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
2939 if (scopeCalls.containsKey(scopeCall)) {
2940 return scopeCalls.get(scopeCall);
2941 }
2942 scopeCall.setClassAndName(compileUnit, compiler);
2943 scopeCalls.put(scopeCall, scopeCall);
2944 return scopeCall;
2945 }
2946
2947 /**
2948 * Debug code used to print symbols
2949 *
2950 * @param block the block we are in
2951 * @param ident identifier for block or function where applicable
2952 */
2953 private void printSymbols(final Block block, final String ident) {
2954 if (!context._print_symbols) {
2955 return;
2956 }
2957
2958 @SuppressWarnings("resource")
2959 final PrintWriter out = context.getErr();
2960 out.println("[BLOCK in '" + ident + "']");
2961 if (!block.printSymbols(out)) {
2962 out.println("<no symbols>");
2963 }
2964 out.println();
2965 }
2966
2967
2968 /**
2969 * The difference between a store and a self modifying store is that
2970 * the latter may load part of the target on the stack, e.g. the base
2971 * of an AccessNode or the base and index of an IndexNode. These are used
2972 * both as target and as an extra source. Previously it was problematic
2973 * for self modifying stores if the target/lhs didn't belong to one
2974 * of three trivial categories: IdentNode, AcessNodes, IndexNodes. In that
2975 * case it was evaluated and tagged as "resolved", which meant at the second
2976 * time the lhs of this store was read (e.g. in a = a (second) + b for a += b,
2977 * it would be evaluated to a nop in the scope and cause stack underflow
2978 *
2979 * see NASHORN-703
2980 *
2981 * @param <T>
2982 */
2983 private abstract class SelfModifyingStore<T extends Node> extends Store<T> {
2984 protected SelfModifyingStore(final T assignNode, final Node target) {
2985 super(assignNode, target);
2986 }
2987
2988 @Override
2989 protected boolean isSelfModifying() {
2990 return true;
2991 }
2992 }
2993
2994 /**
2995 * Helper class to generate stores
2996 */
2997 private abstract class Store<T extends Node> {
2998
2999 /** An assignment node, e.g. x += y */
3000 protected final T assignNode;
3001
3002 /** The target node to store to, e.g. x */
3003 private final Node target;
3004
3005 /** Should the result always be discarded, no matter what? */
3006 private final boolean alwaysDiscard;
3007
3008 /** How deep on the stack do the arguments go if this generates an indy call */
3009 private int depth;
3010
3011 /** If we have too many arguments, we need temporary storage, this is stored in 'quick' */
3012 private Symbol quick;
3013
3014 /**
3015 * Constructor
3016 *
3017 * @param assignNode the node representing the whole assignment
3018 * @param target the target node of the assignment (destination)
3019 */
3020 protected Store(final T assignNode, final Node target) {
3021 this.assignNode = assignNode;
3022 this.target = target;
3023 this.alwaysDiscard = assignNode == target;
3024 }
3025
3026 /**
3027 * Constructor
3028 *
3029 * @param assignNode the node representing the whole assignment
3030 */
3031 protected Store(final T assignNode) {
3032 this(assignNode, assignNode);
3033 }
3034
3035 /**
3036 * Is this a self modifying store operation, e.g. *= or ++
3037 * @return true if self modifying store
3038 */
3039 protected boolean isSelfModifying() {
3040 return false;
3041 }
3042
3043 private void prologue() {
3044 final Symbol targetSymbol = target.getSymbol();
3045 final Symbol scopeSymbol = getCurrentFunctionNode().getScopeNode().getSymbol();
3046
3047 /**
3048 * This loads the parts of the target, e.g base and index. they are kept
3049 * on the stack throughout the store and used at the end to execute it
3050 */
3051
3052 target.accept(new NodeVisitor(compileUnit, method) {
3053 @Override
3054 public Node enter(final IdentNode node) {
3055 if (targetSymbol.isScope()) {
3056 method.load(scopeSymbol);
3057 depth++;
3058 }
3059 return null;
3060 }
3061
3062 private void enterBaseNode() {
3063 assert target instanceof BaseNode : "error - base node " + target + " must be instanceof BaseNode";
3064 final BaseNode baseNode = (BaseNode)target;
3065 final Node base = baseNode.getBase();
3066
3067 load(base);
3068 method.convert(Type.OBJECT);
3069 depth += Type.OBJECT.getSlots();
3070
3071 if (isSelfModifying()) {
3072 method.dup();
3073 }
3074 }
3075
3076 @Override
3077 public Node enter(final AccessNode node) {
3078 enterBaseNode();
3079 return null;
3080 }
3081
3082 @Override
3083 public Node enter(final IndexNode node) {
3084 enterBaseNode();
3085
3086 final Node index = node.getIndex();
3087 // could be boolean here as well
3088 load(index);
3089 if (!index.getType().isNumeric()) {
3090 method.convert(Type.OBJECT);
3091 }
3092 depth += index.getType().getSlots();
3093
3094 if (isSelfModifying()) {
3095 //convert "base base index" to "base index base index"
3096 method.dup(1);
3097 }
3098
3099 return null;
3100 }
3101
3102 });
3103 }
3104
3105 private Symbol quickSymbol(final Type type) {
3106 return quickSymbol(type, QUICK_PREFIX.tag());
3107 }
3108
3109 /**
3110 * Quick symbol generates an extra local variable, always using the same
3111 * slot, one that is available after the end of the frame.
3112 *
3113 * @param type the type of the symbol
3114 * @param prefix the prefix for the variable name for the symbol
3115 *
3116 * @return the quick symbol
3117 */
3118 private Symbol quickSymbol(final Type type, final String prefix) {
3119 final String name = compiler.uniqueName(prefix);
3120 final Symbol symbol = new Symbol(name, IS_TEMP | IS_INTERNAL, null, null);
3121
3122 symbol.setType(type);
3123 symbol.setSlot(getCurrentBlock().getFrame().getSlotCount());
3124
3125 return symbol;
3126 }
3127
3128 // store the result that "lives on" after the op, e.g. "i" in i++ postfix.
3129 protected void storeNonDiscard() {
3130 if (assignNode.shouldDiscard() || alwaysDiscard) {
3131 assignNode.setDiscard(false);
3132 return;
3133 }
3134
3135 final Symbol symbol = assignNode.getSymbol();
3136 if (symbol.hasSlot()) {
3137 method.dup().store(symbol);
3138 return;
3139 }
3140
3141 if (method.dup(depth) == null) {
3142 method.dup();
3143 this.quick = quickSymbol(method.peekType());
3144 method.store(quick);
3145 }
3146 }
3147
3148 private void epilogue() {
3149 final Symbol targetSymbol = target.getSymbol();
3150 final FunctionNode currentFunction = getCurrentFunctionNode();
3151
3152 /**
3153 * Take the original target args from the stack and use them
3154 * together with the value to be stored to emit the store code
3155 */
3156 if (targetSymbol.hasSlot()) {
3157 method.convert(target.getType());
3158 }
3159
3160 target.accept(new NodeVisitor(compileUnit, method) {
3161 @Override
3162 public Node enter(final IdentNode node) {
3163 final Symbol symbol = target.getSymbol();
3164 if (symbol.isScope()) {
3165 if(symbol.isFastScope(currentFunction)) {
3166 storeFastScopeVar(target.getType(), symbol, CALLSITE_SCOPE | getCallSiteFlags());
3167 } else {
3168 method.dynamicSet(target.getType(), node.getName(), CALLSITE_SCOPE | getCallSiteFlags());
3169 }
3170 } else {
3171 assert targetSymbol != null;
3172 method.store(targetSymbol);
3173 }
3174 return null;
3175
3176 }
3177
3178 @Override
3179 public Node enter(final AccessNode node) {
3180 method.dynamicSet(node.getProperty().getType(), node.getProperty().getName(), getCallSiteFlags());
3181 return null;
3182 }
3183
3184 @Override
3185 public Node enter(final IndexNode node) {
3186 method.dynamicSetIndex(getCallSiteFlags());
3187 return null;
3188 }
3189 });
3190
3191
3192 // whatever is on the stack now is the final answer
3193 }
3194
3195 protected abstract void evaluate();
3196
3197 void store() {
3198 prologue();
3199 evaluate(); // leaves an operation of whatever the operationType was on the stack
3200 storeNonDiscard();
3201 epilogue();
3202 if (quick != null) {
3203 method.load(quick);
3204 }
3205 }
3206
3207 }
3208
3209 /*
3210 * Globals are special. We cannot refer to any Global (or NativeObject) class by .class, as they are different
3211 * for different contexts. As far as I can tell, the only NativeObject that we need to deal with like this
3212 * is from the code pipeline is Global
3213 */
3214 private MethodEmitter globalInstance() {
3215 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "instance", "()L" + Compiler.GLOBAL_OBJECT + ';');
3216 }
3217
3218 private MethodEmitter globalObjectPrototype() {
3219 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "objectPrototype", methodDescriptor(ScriptObject.class));
3220 }
3221
3222 private MethodEmitter globalAllocateArguments() {
3223 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocateArguments", methodDescriptor(Object.class, Object[].class, Object.class, int.class));
3224 }
3225
3226 private MethodEmitter globalNewRegExp() {
3227 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "newRegExp", methodDescriptor(Object.class, String.class, String.class));
3228 }
3229
3230 private MethodEmitter globalRegExpCopy() {
3231 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "regExpCopy", methodDescriptor(Object.class, Object.class));
3232 }
3233
3234 private MethodEmitter globalAllocateArray(final ArrayType type) {
3235 //make sure the native array is treated as an array type
3236 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "allocate", methodDescriptor(Object.class, type.getTypeClass()), type);
3237 }
3238
3239 private MethodEmitter globalIsEval() {
3240 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "isEval", methodDescriptor(boolean.class, Object.class));
3241 }
3242
3243 private MethodEmitter globalDirectEval() {
3244 return method.invokeStatic(Compiler.GLOBAL_OBJECT, "directEval",
3245 methodDescriptor(Object.class, Object.class, Object.class, Object.class, Object.class, Object.class));
3246 }
3247 }
--- EOF ---