/* * StatementSequence.java - This file is part of the Jakstab project. * Copyright 2007-2015 Johannes Kinder <jk@jakstab.org> * * 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. * * 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, see <http://www.gnu.org/licenses/>. */ package org.jakstab.rtl.statements; import java.io.Serializable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.jakstab.rtl.Context; import org.jakstab.rtl.expressions.*; import org.jakstab.ssl.CanonizationVisitor; import org.jakstab.util.Logger; /** * A sequence of RTL statements, only used during SSL instantiation and in stub generation. * * @author Johannes Kinder */ public class StatementSequence implements Iterable<RTLStatement>, Serializable { @SuppressWarnings("unused") private final static Logger logger = Logger.getLogger(StatementSequence.class); private static final long serialVersionUID = -3750283603929931899L; private LinkedList<RTLStatement> sequence; /** * Creates a new empty statement sequence. */ public StatementSequence() { sequence = new LinkedList<RTLStatement>(); } /** * Inserts a statement before the start of this sequence when it is not null, * otherwise does nothing. * * @param statement the statement to be added. */ public void addFirst(RTLStatement statement) { if (statement != null) { sequence.addFirst(statement); } } /** * Inserts a sequence of statements before the start of this sequence when the * sequence object is not null, otherwise does nothing. * * @param statements the statement sequence to be added */ public void addFirst(StatementSequence statements) { if (statements != null) { sequence.addAll(0, statements.sequence); } } /** * Adds a statement to the end of this sequence when it is not null, * otherwise does nothing. * * @param statement the statement to be added. */ public void addLast(RTLStatement statement) { if (statement != null) { sequence.addLast(statement); } } /** * Adds a sequence of statements to the end of this sequence when the sequence * object is not null, otherwise does nothing. * * @param statements the statement sequence to be added */ public void addLast(StatementSequence statements) { if (statements != null) { sequence.addAll(statements.sequence); } } public Iterator<RTLStatement> iterator() { return sequence.iterator(); } public void removeLast() { sequence.removeLast(); } public RTLStatement getFirst() { return sequence.getFirst(); } public RTLStatement getLast() { return sequence.getLast(); } /** * Returns a canonical copy of this sequence. Canonization is required only * once, after parsing a SSL specification. Canonical statements do not contain * assignments of the special %RPT and %SKIP registers. * * NOTE: Unchanged elements are not copied but the originals are returned. * * @return this statement sequence converted to canonical form. */ public StatementSequence canonize() { CanonizationVisitor visitor = new CanonizationVisitor(); RTLExpression skipCondition = null; RTLExpression repeatCondition = null; LinkedList<RTLStatement> oldSequence = sequence; sequence = new LinkedList<RTLStatement>(); /* Evaluate substatements */ for (RTLStatement statement : oldSequence) { RTLStatement evaldStatement; if (statement instanceof AssignmentTemplate) { AssignmentTemplate a = (AssignmentTemplate)statement; RTLExpression genericEvaldLHS = a.getLeftHandSide().accept(visitor); RTLExpression evaldRHS = a.getRightHandSide().accept(visitor); if (!(genericEvaldLHS instanceof Writable)) throw new RuntimeException("Error: LHS of assignment no longer writable after canonization: " + a.getLeftHandSide().toString() + " = " + genericEvaldLHS.toString()); Writable evaldLHS = (Writable)genericEvaldLHS; /* Convert %SKIP assignments to conditionals */ if (evaldLHS.equals(ExpressionFactory.SKIP)) { skipCondition = evaldRHS; continue; } /* Convert %RPT assignments to conditionals */ if (evaldLHS.equals(ExpressionFactory.REPEAT)) { repeatCondition = evaldRHS; continue; } // if both sides are equal, remove this statement if (evaldLHS.equals(evaldRHS)) continue; evaldStatement = new AssignmentTemplate(a.getBitWidth(), evaldLHS, evaldRHS); } else /* non AssignmentTemplate */ { evaldStatement = statement.evaluate(new Context()); } addLast(evaldStatement); } if (sequence.size() < 1) return null; if (skipCondition != null) { RTLGoto skipGoto = new RTLGoto(ExpressionFactory.pc, skipCondition, RTLGoto.Type.STRING_LENGTH_CHECK); addFirst(skipGoto); } if (repeatCondition != null) { // Create a dummy goto statement, this will point to the instruction's address after instantiation if (!repeatCondition.equals(ExpressionFactory.FALSE)) { RTLStatement condGoto = new RTLGoto(null, repeatCondition, RTLGoto.Type.REPEAT); addLast(condGoto); } } return this; } /** * Move all bitrange expressions from the LHS of assignments to the RHS, * and convert all assignment templates to variable or memory assignments. * @return */ public StatementSequence normalizeAssignments() { LinkedList<RTLStatement> oldSequence = sequence; sequence = new LinkedList<RTLStatement>(); for (RTLStatement statement : oldSequence) { if (statement instanceof AssignmentTemplate) sequence.addLast(((AssignmentTemplate)statement).convertToSpecificAssignmentType()); else sequence.addLast(statement); } return this; } public StatementSequence evaluate(Context context) { LinkedList<RTLStatement> oldSequence = sequence; sequence = new LinkedList<RTLStatement>(); for (RTLStatement statement : oldSequence) { RTLStatement evaldStatement = statement.evaluate(context); if (evaldStatement != null) addLast(evaldStatement); } if (sequence.size() < 1) return null; return this; } public int getLength() { return sequence.size(); } /** * @return the statement sequence */ protected List<RTLStatement> getSequence() { return sequence; } public StatementSequence replace(RTLStatement statement, RTLStatement replacement) { LinkedList<RTLStatement> oldSequence = sequence; sequence = new LinkedList<RTLStatement>(); for (RTLStatement stmt : oldSequence) { if (stmt.equals(statement)) { if (replacement != null) { addLast(replacement); } } else addLast(stmt); } if (sequence.size() < 1) { return null; } return this; } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append("{ "); for (RTLStatement statement : sequence) { res.append(statement.toString()); res.append("; "); } res.append(" }"); return res.toString(); } public StatementSequence copy() { StatementSequence res = new StatementSequence(); for (RTLStatement statement : sequence) { res.addLast(statement.copy()); } return res; } public SetOfVariables getDefinedVariables() { SetOfVariables res = new SetOfVariables(); for (RTLStatement s : sequence) res.addAll(s.getDefinedVariables()); return res; } }