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 java.util.ArrayDeque;
  29 import java.util.Collection;
  30 import java.util.Collections;
  31 import java.util.Deque;
  32 import java.util.HashMap;
  33 import java.util.Map;
  34 import jdk.nashorn.internal.IntDeque;
  35 import jdk.nashorn.internal.codegen.types.Type;
  36 import jdk.nashorn.internal.ir.Block;
  37 import jdk.nashorn.internal.ir.Expression;
  38 import jdk.nashorn.internal.ir.FunctionNode;
  39 import jdk.nashorn.internal.ir.LexicalContext;
  40 import jdk.nashorn.internal.ir.LexicalContextNode;
  41 import jdk.nashorn.internal.ir.Node;
  42 import jdk.nashorn.internal.ir.Symbol;
  43 import jdk.nashorn.internal.ir.WithNode;
  44 
  45 /**
  46  * A lexical context that also tracks if we have any dynamic scopes in the context. Such scopes can have new
  47  * variables introduced into them at run time - a with block or a function directly containing an eval call.
  48  * Furthermore, this class keeps track of current discard state, which the current method emitter being used is,
  49  * the current compile unit, and local variable indexes
  50  */
  51 final class CodeGeneratorLexicalContext extends LexicalContext {
  52     private int dynamicScopeCount;
  53 
  54     /** Map of shared scope call sites */
  55     private final Map<SharedScopeCall, SharedScopeCall> scopeCalls = new HashMap<>();
  56 
  57     /** Compile unit stack - every time we start a sub method (e.g. a split) we push one */
  58     private final Deque<CompileUnit> compileUnits = new ArrayDeque<>();
  59 
  60     /** Method emitter stack - every time we start a sub method (e.g. a split) we push one */
  61     private final Deque<MethodEmitter> methodEmitters = new ArrayDeque<>();
  62 
  63     /** The discard stack - whenever we evaluate an expression that will be discarded, we push it on this stack. Various
  64      * implementations of expression code emitter can choose to emit code that'll discard the expression themselves, or
  65      * ignore it in which case CodeGenerator.loadAndDiscard() will explicitly emit a pop instruction. */
  66     private final Deque<Expression> discard = new ArrayDeque<>();
  67 
  68 
  69     private final Deque<Map<String, Collection<Label>>> unwarrantedOptimismHandlers = new ArrayDeque<>();
  70     private final Deque<StringBuilder> slotTypesDescriptors = new ArrayDeque<>();
  71     private final IntDeque splitLiterals = new IntDeque();
  72 
  73     /** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
  74      *  currently on the lexical context stack. */
  75     private int[] nextFreeSlots = new int[16];
  76 
  77     /** size of next free slot vector */
  78     private int nextFreeSlotsSize;
  79 
  80     private boolean isWithBoundary(final Object node) {
  81         return node instanceof Block && !isEmpty() && peek() instanceof WithNode;
  82     }
  83 
  84     @Override
  85     public <T extends LexicalContextNode> T push(final T node) {
  86         if (isWithBoundary(node)) {
  87             dynamicScopeCount++;
  88         } else if (node instanceof FunctionNode) {
  89             if (((FunctionNode)node).inDynamicContext()) {
  90                 dynamicScopeCount++;
  91             }
  92             splitLiterals.push(0);
  93         }
  94         return super.push(node);
  95     }
  96 
  97     void enterSplitLiteral() {
  98         splitLiterals.getAndIncrement();
  99         pushFreeSlots(methodEmitters.peek().getUsedSlotsWithLiveTemporaries());
 100     }
 101 
 102     void exitSplitLiteral() {
 103         final int count = splitLiterals.decrementAndGet();
 104         assert count >= 0;
 105     }
 106 
 107     @Override
 108     public <T extends Node> T pop(final T node) {
 109         final T popped = super.pop(node);
 110         if (isWithBoundary(node)) {
 111             dynamicScopeCount--;
 112             assert dynamicScopeCount >= 0;
 113         } else if (node instanceof FunctionNode) {
 114             if (((FunctionNode)node).inDynamicContext()) {
 115                 dynamicScopeCount--;
 116                 assert dynamicScopeCount >= 0;
 117             }
 118             assert splitLiterals.peek() == 0;
 119             splitLiterals.pop();
 120         }
 121         return popped;
 122     }
 123 
 124     boolean inDynamicScope() {
 125         return dynamicScopeCount > 0;
 126     }
 127 
 128     boolean inSplitLiteral() {
 129         return !splitLiterals.isEmpty() && splitLiterals.peek() > 0;
 130     }
 131 
 132     MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) {
 133         methodEmitters.push(newMethod);
 134         return newMethod;
 135     }
 136 
 137     MethodEmitter popMethodEmitter(final MethodEmitter oldMethod) {
 138         assert methodEmitters.peek() == oldMethod;
 139         methodEmitters.pop();
 140         return methodEmitters.isEmpty() ? null : methodEmitters.peek();
 141     }
 142 
 143     void pushUnwarrantedOptimismHandlers() {
 144         unwarrantedOptimismHandlers.push(new HashMap<String, Collection<Label>>());
 145         slotTypesDescriptors.push(new StringBuilder());
 146     }
 147 
 148     Map<String, Collection<Label>> getUnwarrantedOptimismHandlers() {
 149         return unwarrantedOptimismHandlers.peek();
 150     }
 151 
 152     Map<String, Collection<Label>> popUnwarrantedOptimismHandlers() {
 153         slotTypesDescriptors.pop();
 154         return unwarrantedOptimismHandlers.pop();
 155     }
 156 
 157     CompileUnit pushCompileUnit(final CompileUnit newUnit) {
 158         compileUnits.push(newUnit);
 159         return newUnit;
 160     }
 161 
 162     CompileUnit popCompileUnit(final CompileUnit oldUnit) {
 163         assert compileUnits.peek() == oldUnit;
 164         final CompileUnit unit = compileUnits.pop();
 165         assert unit.hasCode() : "compile unit popped without code";
 166         unit.setUsed();
 167         return compileUnits.isEmpty() ? null : compileUnits.peek();
 168     }
 169 
 170     boolean hasCompileUnits() {
 171         return !compileUnits.isEmpty();
 172     }
 173 
 174     Collection<SharedScopeCall> getScopeCalls() {
 175         return Collections.unmodifiableCollection(scopeCalls.values());
 176     }
 177 
 178     /**
 179      * Get a shared static method representing a dynamic scope callsite.
 180      *
 181      * @param unit current compile unit
 182      * @param symbol the symbol
 183      * @param valueType the value type of the symbol
 184      * @param returnType the return type
 185      * @param paramTypes the parameter types
 186      * @param flags the callsite flags
 187      * @param isOptimistic is this an optimistic call
 188      * @return an object representing a shared scope call
 189      */
 190     SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType,
 191                                  final Type returnType, final Type[] paramTypes, final int flags,
 192                                  final boolean isOptimistic) {
 193         final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags,
 194                 isOptimistic);
 195         if (scopeCalls.containsKey(scopeCall)) {
 196             return scopeCalls.get(scopeCall);
 197         }
 198         scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall"));
 199         scopeCalls.put(scopeCall, scopeCall);
 200         return scopeCall;
 201     }
 202 
 203     /**
 204      * Get a shared static method representing a dynamic scope get access.
 205      *
 206      * @param unit current compile unit
 207      * @param symbol the symbol
 208      * @param valueType the type of the variable
 209      * @param flags the callsite flags
 210      * @param isOptimistic is this an optimistic get
 211      * @return an object representing a shared scope get
 212      */
 213     SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags,
 214                                 final boolean isOptimistic) {
 215         return getScopeCall(unit, symbol, valueType, valueType, null, flags, isOptimistic);
 216     }
 217 
 218     void onEnterBlock(final Block block) {
 219         pushFreeSlots(assignSlots(block, isFunctionBody() ? 0 : getUsedSlotCount()));
 220     }
 221 
 222     private void pushFreeSlots(final int freeSlots) {
 223         if (nextFreeSlotsSize == nextFreeSlots.length) {
 224             final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
 225             System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
 226             nextFreeSlots = newNextFreeSlots;
 227         }
 228         nextFreeSlots[nextFreeSlotsSize++] = freeSlots;
 229     }
 230 
 231     int getUsedSlotCount() {
 232         return nextFreeSlots[nextFreeSlotsSize - 1];
 233     }
 234 
 235     void releaseSlots() {
 236         --nextFreeSlotsSize;
 237         final int undefinedFromSlot = nextFreeSlotsSize == 0 ? 0 : nextFreeSlots[nextFreeSlotsSize - 1];
 238         if(!slotTypesDescriptors.isEmpty()) {
 239             slotTypesDescriptors.peek().setLength(undefinedFromSlot);
 240         }
 241         methodEmitters.peek().undefineLocalVariables(undefinedFromSlot, false);
 242     }
 243 
 244     private int assignSlots(final Block block, final int firstSlot) {
 245         int fromSlot = firstSlot;
 246         final MethodEmitter method = methodEmitters.peek();
 247         for (final Symbol symbol : block.getSymbols()) {
 248             if (symbol.hasSlot()) {
 249                 symbol.setFirstSlot(fromSlot);
 250                 final int toSlot = fromSlot + symbol.slotCount();
 251                 method.defineBlockLocalVariable(fromSlot, toSlot);
 252                 fromSlot = toSlot;
 253             }
 254         }
 255         return fromSlot;
 256     }
 257 
 258     static Type getTypeForSlotDescriptor(final char typeDesc) {
 259         // Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
 260         // MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
 261         switch (typeDesc) {
 262             case 'I':
 263             case 'i':
 264                 return Type.INT;
 265             case 'J':
 266             case 'j':
 267                 return Type.LONG;
 268             case 'D':
 269             case 'd':
 270                 return Type.NUMBER;
 271             case 'A':
 272             case 'a':
 273                 return Type.OBJECT;
 274             case 'U':
 275             case 'u':
 276                 return Type.UNKNOWN;
 277             default:
 278                 throw new AssertionError();
 279         }
 280     }
 281 
 282     void pushDiscard(final Expression expr) {
 283         discard.push(expr);
 284     }
 285 
 286     boolean popDiscardIfCurrent(final Expression expr) {
 287         if (isCurrentDiscard(expr)) {
 288             discard.pop();
 289             return true;
 290         }
 291         return false;
 292     }
 293 
 294     boolean isCurrentDiscard(final Expression expr) {
 295         return discard.peek() == expr;
 296     }
 297 
 298     int quickSlot(final Type type) {
 299         return methodEmitters.peek().defineTemporaryLocalVariable(type.getSlots());
 300     }
 301 }
 302