ParserContext.java revision 1060:ca67ae7c46cb
1/* 2 * Copyright (c) 2014, 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 */ 25package jdk.nashorn.internal.parser; 26 27import java.util.Iterator; 28import java.util.NoSuchElementException; 29import jdk.nashorn.internal.ir.Statement; 30 31/** 32 * A class that tracks the current lexical context of node visitation as a stack of {@code ParserContextNode} nodes. Has special 33 * methods to retrieve useful subsets of the context. 34 * 35 * This is implemented with a primitive array and a stack pointer, because it really makes a difference 36 * performance wise. None of the collection classes were optimal 37 */ 38 39class ParserContext { 40 41 private ParserContextNode[] stack; 42 private int sp; 43 44 private static final int INITIAL_DEPTH = 16; 45 46 /** 47 * Constructs a ParserContext, 48 * initializes the stack 49 */ 50 public ParserContext(){ 51 this.sp = 0; 52 this.stack = new ParserContextNode[INITIAL_DEPTH]; 53 } 54 55 /** 56 * Pushes a new block on top of the context, making it the innermost open block. 57 * @param node the new node 58 * @return The node that was pushed 59 */ 60 public <T extends ParserContextNode> T push(final T node) { 61 assert !contains(node); 62 if (sp == stack.length) { 63 final ParserContextNode[] newStack = new ParserContextNode[sp * 2]; 64 System.arraycopy(stack, 0, newStack, 0, sp); 65 stack = newStack; 66 } 67 stack[sp] = node; 68 sp++; 69 70 return node; 71 } 72 73 /** 74 * The topmost node on the stack 75 * @return The topmost node on the stack 76 */ 77 public ParserContextNode peek() { 78 return stack[sp - 1]; 79 } 80 81 /** 82 * Removes and returns the topmost Node from the stack. 83 * @param node The node expected to be popped, used for sanity check 84 * @return The removed node 85 */ 86 public <T extends ParserContextNode> T pop(final T node) { 87 --sp; 88 @SuppressWarnings("unchecked") 89 final T popped = (T)stack[sp]; 90 stack[sp] = null; 91 assert node == popped; 92 93 return popped; 94 } 95 96 /** 97 * Tests if a node is on the stack. 98 * @param node The node to test 99 * @return true if stack contains node, false otherwise 100 */ 101 public boolean contains(final ParserContextNode node) { 102 for (int i = 0; i < sp; i++) { 103 if (stack[i] == node) { 104 return true; 105 } 106 } 107 return false; 108 } 109 110 /** 111 * Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on stack 112 * @return Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on stack 113 */ 114 private ParserContextBreakableNode getBreakable() { 115 for (final NodeIterator<ParserContextBreakableNode> iter = new NodeIterator<>(ParserContextBreakableNode.class, getCurrentFunction()); iter.hasNext(); ) { 116 final ParserContextBreakableNode next = iter.next(); 117 if (next.isBreakableWithoutLabel()) { 118 return next; 119 } 120 } 121 return null; 122 } 123 124 125 126 /** 127 * Find the breakable node corresponding to this label. 128 * @param labelName name of the label to search for. If null, the closest breakable node will be returned 129 * unconditionally, e.g. a while loop with no label 130 * @return closest breakable node 131 */ 132 public ParserContextBreakableNode getBreakable(final String labelName) { 133 if (labelName != null) { 134 final ParserContextLabelNode foundLabel = findLabel(labelName); 135 if (foundLabel != null) { 136 // iterate to the nearest breakable to the foundLabel 137 ParserContextBreakableNode breakable = null; 138 for (final NodeIterator<ParserContextBreakableNode> iter = new NodeIterator<>(ParserContextBreakableNode.class, foundLabel); iter.hasNext(); ) { 139 breakable = iter.next(); 140 } 141 return breakable; 142 } 143 return null; 144 } else { 145 return getBreakable(); 146 } 147 } 148 149 /** 150 * Returns the loop node of the current loop, or null if not inside a loop 151 * @return loop noder 152 */ 153 public ParserContextLoopNode getCurrentLoop() { 154 final Iterator<ParserContextLoopNode> iter = new NodeIterator<>(ParserContextLoopNode.class, getCurrentFunction()); 155 return iter.hasNext() ? iter.next() : null; 156 } 157 158 private ParserContextLoopNode getContinueTo() { 159 return getCurrentLoop(); 160 } 161 162 /** 163 * Find the continue target node corresponding to this label. 164 * @param labelName label name to search for. If null the closest loop node will be returned unconditionally, e.g. a 165 * while loop with no label 166 * @return closest continue target node 167 */ 168 public ParserContextLoopNode getContinueTo(final String labelName) { 169 if (labelName != null) { 170 final ParserContextLabelNode foundLabel = findLabel(labelName); 171 if (foundLabel != null) { 172 // iterate to the nearest loop to the foundLabel 173 ParserContextLoopNode loop = null; 174 for (final NodeIterator<ParserContextLoopNode> iter = new NodeIterator<>(ParserContextLoopNode.class, foundLabel); iter.hasNext(); ) { 175 loop = iter.next(); 176 } 177 return loop; 178 } 179 return null; 180 } 181 return getContinueTo(); 182 } 183 184 /** 185 * Get the function body of a function node on the stack. 186 * This will trigger an assertion if node isn't present 187 * @param functionNode function node 188 * @return body of function node 189 */ 190 public ParserContextBlockNode getFunctionBody(final ParserContextFunctionNode functionNode) { 191 for (int i = sp - 1; i >= 0 ; i--) { 192 if (stack[i] == functionNode) { 193 return (ParserContextBlockNode)stack[i + 1]; 194 } 195 } 196 throw new AssertionError(functionNode.getName() + " not on context stack"); 197 } 198 199 /** 200 * Check the stack for a given label node by name 201 * @param name name of the label 202 * @return LabelNode if found, null otherwise 203 */ 204 public ParserContextLabelNode findLabel(final String name) { 205 for (final Iterator<ParserContextLabelNode> iter = new NodeIterator<>(ParserContextLabelNode.class, getCurrentFunction()); iter.hasNext(); ) { 206 final ParserContextLabelNode next = iter.next(); 207 if (next.getLabelName().equals(name)) { 208 return next; 209 } 210 } 211 return null; 212 } 213 214 /** 215 * Prepends a statement to the current node. 216 * @param statement The statement to prepend 217 */ 218 public void prependStatementToCurrentNode(final Statement statement) { 219 assert statement != null; 220 stack[sp - 1].prependStatement(statement); 221 } 222 223 /** 224 * Appends a statement to the current Node. 225 * @param statement The statement to append 226 */ 227 public void appendStatementToCurrentNode(final Statement statement) { 228 assert statement != null; 229 stack[sp - 1].appendStatement(statement); 230 } 231 232 /** 233 * Returns the innermost function in the context. 234 * @return the innermost function in the context. 235 */ 236 public ParserContextFunctionNode getCurrentFunction() { 237 for (int i = sp - 1; i >= 0; i--) { 238 if (stack[i] instanceof ParserContextFunctionNode) { 239 return (ParserContextFunctionNode) stack[i]; 240 } 241 } 242 return null; 243 } 244 245 /** 246 * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first. 247 * @return an iterator over all blocks in the context. 248 */ 249 public Iterator<ParserContextBlockNode> getBlocks() { 250 return new NodeIterator<>(ParserContextBlockNode.class); 251 } 252 253 /** 254 * Returns the innermost block in the context. 255 * @return the innermost block in the context. 256 */ 257 public ParserContextBlockNode getCurrentBlock() { 258 return getBlocks().next(); 259 } 260 261 /** 262 * The last statement added to the context 263 * @return The last statement added to the context 264 */ 265 public Statement getLastStatement() { 266 if (sp == 0) { 267 return null; 268 } 269 final ParserContextNode top = stack[sp - 1]; 270 final int s = top.getStatements().size(); 271 return s == 0 ? null : top.getStatements().get(s - 1); 272 } 273 274 /** 275 * Returns an iterator over all functions in the context, with the top (innermost open) function first. 276 * @return an iterator over all functions in the context. 277 */ 278 public Iterator<ParserContextFunctionNode> getFunctions() { 279 return new NodeIterator<>(ParserContextFunctionNode.class); 280 } 281 282 private class NodeIterator <T extends ParserContextNode> implements Iterator<T> { 283 private int index; 284 private T next; 285 private final Class<T> clazz; 286 private ParserContextNode until; 287 288 NodeIterator(final Class<T> clazz) { 289 this(clazz, null); 290 } 291 292 NodeIterator(final Class<T> clazz, final ParserContextNode until) { 293 this.index = sp - 1; 294 this.clazz = clazz; 295 this.until = until; 296 this.next = findNext(); 297 } 298 299 @Override 300 public boolean hasNext() { 301 return next != null; 302 } 303 304 @Override 305 public T next() { 306 if (next == null) { 307 throw new NoSuchElementException(); 308 } 309 final T lnext = next; 310 next = findNext(); 311 return lnext; 312 } 313 314 @SuppressWarnings("unchecked") 315 private T findNext() { 316 for (int i = index; i >= 0; i--) { 317 final Object node = stack[i]; 318 if (node == until) { 319 return null; 320 } 321 if (clazz.isAssignableFrom(node.getClass())) { 322 index = i - 1; 323 return (T)node; 324 } 325 } 326 return null; 327 } 328 329 @Override 330 public void remove() { 331 throw new UnsupportedOperationException(); 332 } 333 } 334} 335