ParserContext.java revision 1088:7e62d98d4625
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        }
145        return getBreakable();
146    }
147
148    /**
149     * Returns the loop node of the current loop, or null if not inside a loop
150     * @return loop noder
151     */
152    public ParserContextLoopNode getCurrentLoop() {
153        final Iterator<ParserContextLoopNode> iter = new NodeIterator<>(ParserContextLoopNode.class, getCurrentFunction());
154        return iter.hasNext() ? iter.next() : null;
155    }
156
157    private ParserContextLoopNode getContinueTo() {
158        return getCurrentLoop();
159    }
160
161    /**
162     * Find the continue target node corresponding to this label.
163     * @param labelName label name to search for. If null the closest loop node will be returned unconditionally, e.g. a
164     * while loop with no label
165     * @return closest continue target node
166     */
167    public ParserContextLoopNode getContinueTo(final String labelName) {
168        if (labelName != null) {
169            final ParserContextLabelNode foundLabel = findLabel(labelName);
170            if (foundLabel != null) {
171                // iterate to the nearest loop to the foundLabel
172                ParserContextLoopNode loop = null;
173                for (final NodeIterator<ParserContextLoopNode> iter = new NodeIterator<>(ParserContextLoopNode.class, foundLabel); iter.hasNext(); ) {
174                    loop = iter.next();
175                }
176                return loop;
177            }
178            return null;
179        }
180        return getContinueTo();
181    }
182
183    /**
184     * Get the function body of a function node on the stack.
185     * This will trigger an assertion if node isn't present
186     * @param functionNode function node
187     * @return body of function node
188     */
189    public ParserContextBlockNode getFunctionBody(final ParserContextFunctionNode functionNode) {
190        for (int i = sp - 1; i >= 0 ; i--) {
191            if (stack[i] == functionNode) {
192                return (ParserContextBlockNode)stack[i + 1];
193            }
194        }
195        throw new AssertionError(functionNode.getName() + " not on context stack");
196    }
197
198    /**
199     * Check the stack for a given label node by name
200     * @param name name of the label
201     * @return LabelNode if found, null otherwise
202     */
203    public ParserContextLabelNode findLabel(final String name) {
204        for (final Iterator<ParserContextLabelNode> iter = new NodeIterator<>(ParserContextLabelNode.class, getCurrentFunction()); iter.hasNext(); ) {
205            final ParserContextLabelNode next = iter.next();
206            if (next.getLabelName().equals(name)) {
207                return next;
208            }
209        }
210        return null;
211    }
212
213    /**
214     * Prepends a statement to the current node.
215     * @param statement The statement to prepend
216     */
217    public void prependStatementToCurrentNode(final Statement statement) {
218        assert statement != null;
219        stack[sp - 1].prependStatement(statement);
220    }
221
222    /**
223     * Appends a statement to the current Node.
224     * @param statement The statement to append
225     */
226    public void appendStatementToCurrentNode(final Statement statement) {
227        assert statement != null;
228        stack[sp - 1].appendStatement(statement);
229    }
230
231    /**
232     * Returns the innermost function in the context.
233     * @return the innermost function in the context.
234     */
235    public ParserContextFunctionNode getCurrentFunction() {
236        for (int i = sp - 1; i >= 0; i--) {
237            if (stack[i] instanceof ParserContextFunctionNode) {
238                return (ParserContextFunctionNode) stack[i];
239            }
240        }
241        return null;
242    }
243
244    /**
245     * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first.
246     * @return an iterator over all blocks in the context.
247     */
248    public Iterator<ParserContextBlockNode> getBlocks() {
249        return new NodeIterator<>(ParserContextBlockNode.class);
250    }
251
252    /**
253     * Returns the innermost block in the context.
254     * @return the innermost block in the context.
255     */
256    public ParserContextBlockNode getCurrentBlock() {
257        return getBlocks().next();
258    }
259
260    /**
261     * The last statement added to the context
262     * @return The last statement added to the context
263     */
264    public Statement getLastStatement() {
265        if (sp == 0) {
266            return null;
267        }
268        final ParserContextNode top = stack[sp - 1];
269        final int s = top.getStatements().size();
270        return s == 0 ? null : top.getStatements().get(s - 1);
271    }
272
273    /**
274     * Returns an iterator over all functions in the context, with the top (innermost open) function first.
275     * @return an iterator over all functions in the context.
276     */
277    public Iterator<ParserContextFunctionNode> getFunctions() {
278        return new NodeIterator<>(ParserContextFunctionNode.class);
279    }
280
281    private class NodeIterator <T extends ParserContextNode> implements Iterator<T> {
282        private int index;
283        private T next;
284        private final Class<T> clazz;
285        private ParserContextNode until;
286
287        NodeIterator(final Class<T> clazz) {
288            this(clazz, null);
289        }
290
291        NodeIterator(final Class<T> clazz, final ParserContextNode until) {
292            this.index = sp - 1;
293            this.clazz = clazz;
294            this.until = until;
295            this.next  = findNext();
296        }
297
298        @Override
299        public boolean hasNext() {
300            return next != null;
301        }
302
303        @Override
304        public T next() {
305            if (next == null) {
306                throw new NoSuchElementException();
307            }
308            final T lnext = next;
309            next = findNext();
310            return lnext;
311        }
312
313        @SuppressWarnings("unchecked")
314        private T findNext() {
315            for (int i = index; i >= 0; i--) {
316                final Object node = stack[i];
317                if (node == until) {
318                    return null;
319                }
320                if (clazz.isAssignableFrom(node.getClass())) {
321                    index = i - 1;
322                    return (T)node;
323                }
324            }
325            return null;
326        }
327
328        @Override
329        public void remove() {
330            throw new UnsupportedOperationException();
331        }
332    }
333}
334