/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.nashorn.internal.ir; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.List; /** * This is a subclass of lexical context used for filling * blocks (and function nodes) with statements. When popping * a block from the lexical context, any statements that have * been generated in it are committed to the block. This saves * unnecessary object mutations and lexical context replacement */ public class BlockLexicalContext extends LexicalContext { /** statement stack, each block on the lexical context maintains one of these, which is * committed to the block on pop */ private final Deque<List<Statement>> sstack = new ArrayDeque<>(); /** Last non debug statement emitted in this context */ protected Statement lastStatement; @Override public <T extends LexicalContextNode> T push(final T node) { final T pushed = super.push(node); if (node instanceof Block) { sstack.push(new ArrayList<Statement>()); } return pushed; } /** * Get the statement list from the stack, possibly filtered * @return statement list */ protected List<Statement> popStatements() { return sstack.pop(); } /** * Override this method to perform some additional processing on the block after its statements have been set. By * default does nothing and returns the original block. * @param block the block to operate on * @return a modified block. */ protected Block afterSetStatements(final Block block) { return block; } @SuppressWarnings("unchecked") @Override public <T extends Node> T pop(final T node) { T expected = node; if (node instanceof Block) { final List<Statement> newStatements = popStatements(); expected = (T)((Block)node).setStatements(this, newStatements); expected = (T)afterSetStatements((Block)expected); if (!sstack.isEmpty()) { lastStatement = lastStatement(sstack.peek()); } } return super.pop(expected); } /** * Append a statement to the block being generated * @param statement statement to add */ public void appendStatement(final Statement statement) { assert statement != null; sstack.peek().add(statement); lastStatement = statement; } /** * Prepend a statement to the block being generated * @param statement statement to prepend * @return the prepended statement */ public Node prependStatement(final Statement statement) { assert statement != null; sstack.peek().add(0, statement); return statement; } /** * Prepend a list of statement to the block being generated * @param statements a list of statements to prepend */ public void prependStatements(final List<Statement> statements) { assert statements != null; sstack.peek().addAll(0, statements); } /** * Get the last statement that was emitted into a block * @return the last statement emitted */ public Statement getLastStatement() { return lastStatement; } private static Statement lastStatement(final List<Statement> statements) { final int s = statements.size(); return s == 0 ? null : statements.get(s - 1); } }