/*
 * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.js.parser;

import java.util.Iterator;
import java.util.NoSuchElementException;

import com.oracle.js.parser.ir.Scope;
import com.oracle.js.parser.ir.Statement;

A class that tracks the current lexical context of node visitation as a stack of ParserContextNode nodes. Has special methods to retrieve useful subsets of the context. This is implemented with a primitive array and a stack pointer, because it really makes a difference performance wise. None of the collection classes were optimal
/** * A class that tracks the current lexical context of node visitation as a stack of * {@code ParserContextNode} nodes. Has special methods to retrieve useful subsets of the context. * * This is implemented with a primitive array and a stack pointer, because it really makes a * difference performance wise. None of the collection classes were optimal */
class ParserContext { private ParserContextNode[] stack; private int sp; private static final int INITIAL_DEPTH = 16;
Constructs a ParserContext, initializes the stack
/** * Constructs a ParserContext, initializes the stack */
ParserContext() { this.sp = 0; this.stack = new ParserContextNode[INITIAL_DEPTH]; }
Pushes a new block on top of the context, making it the innermost open block.
Params:
  • node – the new node
Returns:The node that was pushed
/** * Pushes a new block on top of the context, making it the innermost open block. * * @param node the new node * @return The node that was pushed */
public <T extends ParserContextNode> T push(final T node) { assert !contains(node); if (sp == stack.length) { final ParserContextNode[] newStack = new ParserContextNode[sp * 2]; System.arraycopy(stack, 0, newStack, 0, sp); stack = newStack; } stack[sp] = node; sp++; return node; }
The topmost node on the stack
Returns:The topmost node on the stack
/** * The topmost node on the stack * * @return The topmost node on the stack */
public ParserContextNode peek() { return stack[sp - 1]; }
Removes and returns the topmost Node from the stack.
Params:
  • node – The node expected to be popped, used for sanity check
Returns:The removed node
/** * Removes and returns the topmost Node from the stack. * * @param node The node expected to be popped, used for sanity check * @return The removed node */
public <T extends ParserContextNode> T pop(final T node) { --sp; @SuppressWarnings("unchecked") final T popped = (T) stack[sp]; stack[sp] = null; assert node == popped; return popped; }
Tests if a node is on the stack.
Params:
  • node – The node to test
Returns:true if stack contains node, false otherwise
/** * Tests if a node is on the stack. * * @param node The node to test * @return true if stack contains node, false otherwise */
public boolean contains(final ParserContextNode node) { for (int i = 0; i < sp; i++) { if (stack[i] == node) { return true; } } return false; }
Returns the topmost ParserContextBreakableNode on the stack, null if none on stack
Returns:Returns the topmost ParserContextBreakableNode on the stack, null if none on stack
/** * Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on stack * * @return Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on * stack */
private ParserContextBreakableNode getBreakable() { for (final NodeIterator<ParserContextBreakableNode> iter = new NodeIterator<>(ParserContextBreakableNode.class, getCurrentFunction()); iter.hasNext();) { final ParserContextBreakableNode next = iter.next(); if (next.isBreakableWithoutLabel()) { return next; } } return null; }
Find the breakable node corresponding to this label.
Params:
  • labelName – name of the label to search for. If null, the closest breakable node will be returned unconditionally, e.g. a while loop with no label
Returns:closest breakable node
/** * Find the breakable node corresponding to this label. * * @param labelName name of the label to search for. If null, the closest breakable node will be * returned unconditionally, e.g. a while loop with no label * @return closest breakable node */
public ParserContextBreakableNode getBreakable(final String labelName) { if (labelName != null) { final ParserContextLabelNode foundLabel = findLabel(labelName); if (foundLabel != null) { // iterate to the nearest breakable to the foundLabel ParserContextBreakableNode breakable = null; for (final NodeIterator<ParserContextBreakableNode> iter = new NodeIterator<>(ParserContextBreakableNode.class, foundLabel); iter.hasNext();) { breakable = iter.next(); } return breakable; } return null; } else { return getBreakable(); } }
Returns the loop node of the current loop, or null if not inside a loop
Returns:loop noder
/** * Returns the loop node of the current loop, or null if not inside a loop * * @return loop noder */
public ParserContextLoopNode getCurrentLoop() { final Iterator<ParserContextLoopNode> iter = new NodeIterator<>(ParserContextLoopNode.class, getCurrentFunction()); return iter.hasNext() ? iter.next() : null; } private ParserContextLoopNode getContinueTo() { return getCurrentLoop(); }
Find the continue target node corresponding to this label.
Params:
  • labelName – label name to search for. If null the closest loop node will be returned unconditionally, e.g. a while loop with no label
Returns:closest continue target node
/** * Find the continue target node corresponding to this label. * * @param labelName label name to search for. If null the closest loop node will be returned * unconditionally, e.g. a while loop with no label * @return closest continue target node */
public ParserContextLoopNode getContinueTo(final String labelName) { if (labelName != null) { final ParserContextLabelNode foundLabel = findLabel(labelName); if (foundLabel != null) { // iterate to the nearest loop to the foundLabel ParserContextLoopNode loop = null; for (final NodeIterator<ParserContextLoopNode> iter = new NodeIterator<>(ParserContextLoopNode.class, foundLabel); iter.hasNext();) { loop = iter.next(); } return loop; } return null; } return getContinueTo(); }
Check the stack for a given label node by name
Params:
  • name – name of the label
Returns:LabelNode if found, null otherwise
/** * Check the stack for a given label node by name * * @param name name of the label * @return LabelNode if found, null otherwise */
public ParserContextLabelNode findLabel(final String name) { for (final Iterator<ParserContextLabelNode> iter = new NodeIterator<>(ParserContextLabelNode.class, getCurrentFunction()); iter.hasNext();) { final ParserContextLabelNode next = iter.next(); if (next.getLabelName().equals(name)) { return next; } } return null; }
Prepends a statement to the current node.
Params:
  • statement – The statement to prepend
/** * Prepends a statement to the current node. * * @param statement The statement to prepend */
public void prependStatementToCurrentNode(final Statement statement) { assert statement != null; stack[sp - 1].prependStatement(statement); }
Appends a statement to the current Node.
Params:
  • statement – The statement to append
/** * Appends a statement to the current Node. * * @param statement The statement to append */
public void appendStatementToCurrentNode(final Statement statement) { assert statement != null; stack[sp - 1].appendStatement(statement); }
Returns the innermost function in the context.
Returns:the innermost function in the context.
/** * Returns the innermost function in the context. * * @return the innermost function in the context. */
public ParserContextFunctionNode getCurrentFunction() { for (int i = sp - 1; i >= 0; i--) { if (stack[i] instanceof ParserContextFunctionNode) { return (ParserContextFunctionNode) stack[i]; } } return null; }
Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first.
Returns:an iterator over all blocks in the context.
/** * Returns an iterator over all blocks in the context, with the top block (innermost lexical * context) first. * * @return an iterator over all blocks in the context. */
public Iterator<ParserContextBlockNode> getBlocks() { return new NodeIterator<>(ParserContextBlockNode.class); }
Returns the innermost block in the context.
Returns:the innermost block in the context.
/** * Returns the innermost block in the context. * * @return the innermost block in the context. */
public ParserContextBlockNode getCurrentBlock() { return getBlocks().next(); }
The last statement added to the context
Returns:The last statement added to the context
/** * The last statement added to the context * * @return The last statement added to the context */
public Statement getLastStatement() { if (sp == 0) { return null; } final ParserContextNode top = stack[sp - 1]; final int s = top.getStatements().size(); return s == 0 ? null : top.getStatements().get(s - 1); }
Returns an iterator over all functions in the context, with the top (innermost open) function first.
Returns:an iterator over all functions in the context.
/** * Returns an iterator over all functions in the context, with the top (innermost open) function * first. * * @return an iterator over all functions in the context. */
public Iterator<ParserContextFunctionNode> getFunctions() { return new NodeIterator<>(ParserContextFunctionNode.class); } public ParserContextFunctionNode getCurrentNonArrowFunction() { final Iterator<ParserContextFunctionNode> iter = getFunctions(); while (iter.hasNext()) { final ParserContextFunctionNode fn = iter.next(); if (!fn.isArrow()) { return fn; } } return null; }
Returns the innermost scope in the context.
/** * Returns the innermost scope in the context. */
public Scope getCurrentScope() { NodeIterator<ParserContextScopableNode> iterator = new NodeIterator<>(ParserContextScopableNode.class); return iterator.hasNext() ? iterator.next().getScope() : null; }
Returns the current class node or null if not inside a class.
/** * Returns the current class node or null if not inside a class. */
public ParserContextClassNode getCurrentClass() { final Iterator<ParserContextClassNode> iter = new NodeIterator<>(ParserContextClassNode.class); return iter.hasNext() ? iter.next() : null; }
Returns an iterator over all classes in the context, with the innermost class first.
Returns:an iterator over all classes in the context.
/** * Returns an iterator over all classes in the context, with the innermost class first. * * @return an iterator over all classes in the context. */
public Iterator<ParserContextClassNode> getClasses() { return new NodeIterator<>(ParserContextClassNode.class); } private class NodeIterator<T extends ParserContextNode> implements Iterator<T> { private int index; private T next; private final Class<T> clazz; private ParserContextNode until; NodeIterator(final Class<T> clazz) { this(clazz, null); } NodeIterator(final Class<T> clazz, final ParserContextNode until) { this.index = sp - 1; this.clazz = clazz; this.until = until; this.next = findNext(); } @Override public boolean hasNext() { return next != null; } @Override public T next() { if (next == null) { throw new NoSuchElementException(); } final T lnext = next; next = findNext(); return lnext; } @SuppressWarnings("unchecked") private T findNext() { for (int i = index; i >= 0; i--) { final Object node = stack[i]; if (node == until) { return null; } if (clazz.isAssignableFrom(node.getClass())) { index = i - 1; return (T) node; } } return null; } } }