001/*
002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon)
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 */
009
010package org.eclipse.golo.compiler.ir;
011
012import java.util.LinkedList;
013import java.util.List;
014import java.util.Optional;
015
016import org.eclipse.golo.compiler.parser.GoloASTNode;
017
018import static java.util.Collections.unmodifiableList;
019import static org.eclipse.golo.compiler.ir.Builders.*;
020import static java.util.Objects.requireNonNull;
021
022public final class Block extends ExpressionStatement implements Scope {
023  private final List<GoloStatement> statements = new LinkedList<>();
024  private ReferenceTable referenceTable;
025  private boolean hasReturn = false;
026
027  Block(ReferenceTable referenceTable) {
028    super();
029    this.referenceTable = referenceTable;
030  }
031
032  public static Block emptyBlock() {
033    return new Block(new ReferenceTable());
034  }
035
036  public static Block of(Object block) {
037    if (block == null) {
038      return emptyBlock();
039    }
040    if (block instanceof Block) {
041      return (Block) block;
042    }
043    throw cantConvert("Block", block);
044  }
045
046  @Override
047  public Block ofAST(GoloASTNode n) {
048    super.ofAST(n);
049    return this;
050  }
051
052  public void merge(Block other) {
053    for (GoloStatement innerStatement : other.getStatements()) {
054      this.addStatement(innerStatement);
055    }
056  }
057
058  public ReferenceTable getReferenceTable() {
059    return referenceTable;
060  }
061
062  @Override
063  public Optional<ReferenceTable> getLocalReferenceTable() {
064    return Optional.of(referenceTable);
065  }
066
067  public Block ref(Object referenceTable) {
068    if (referenceTable instanceof ReferenceTable) {
069      setReferenceTable((ReferenceTable) referenceTable);
070      return this;
071    }
072    throw new IllegalArgumentException("not a reference table");
073  }
074
075  public void setReferenceTable(ReferenceTable referenceTable) {
076    this.referenceTable = requireNonNull(referenceTable);
077  }
078
079  public void internReferenceTable() {
080    this.referenceTable = referenceTable.flatDeepCopy(true);
081  }
082
083  public List<GoloStatement> getStatements() {
084    return unmodifiableList(statements);
085  }
086
087  public Block add(Object statement) {
088    this.addStatement(toGoloStatement(statement));
089    return this;
090  }
091
092  private void updateStateWith(GoloStatement statement) {
093    referenceTable.updateFrom(statement);
094    makeParentOf(statement);
095    checkForReturns(statement);
096  }
097
098  public void addStatement(GoloStatement statement) {
099    statements.add(statement);
100    updateStateWith(statement);
101  }
102
103  public void prependStatement(GoloStatement statement) {
104    statements.add(0, statement);
105    updateStateWith(statement);
106  }
107
108  private void setStatement(int idx, GoloStatement statement) {
109    statements.set(idx, statement);
110    updateStateWith(statement);
111  }
112
113  private void checkForReturns(GoloStatement statement) {
114    if (statement instanceof ReturnStatement || statement instanceof ThrowStatement) {
115      hasReturn = true;
116    } else if (statement instanceof ConditionalBranching) {
117      hasReturn = hasReturn || ((ConditionalBranching) statement).returnsFromBothBranches();
118    }
119  }
120
121  public boolean hasReturn() {
122    return hasReturn;
123  }
124
125  public int size() {
126    return statements.size();
127  }
128
129  public boolean hasOnlyReturn() {
130    return statements.size() == 1
131           && statements.get(0) instanceof ReturnStatement
132           && !((ReturnStatement) statements.get(0)).isReturningVoid();
133  }
134
135  @Override
136  public String toString() {
137    return "{" + statements.toString() + "}";
138  }
139
140  public boolean isEmpty() {
141    return statements.isEmpty();
142  }
143
144  @Override
145  public void relink(ReferenceTable table) {
146    this.referenceTable.relink(table);
147  }
148
149  @Override
150  public void relinkTopLevel(ReferenceTable table) {
151    this.referenceTable.relinkTopLevel(table);
152  }
153
154  @Override
155  public void accept(GoloIrVisitor visitor) {
156    visitor.visitBlock(this);
157  }
158
159  @Override
160  public void walk(GoloIrVisitor visitor) {
161    for (LocalReference ref : referenceTable.ownedReferences()) {
162      ref.accept(visitor);
163    }
164    for (GoloStatement statement : statements) {
165      statement.accept(visitor);
166    }
167  }
168
169  @Override
170  protected void replaceElement(GoloElement original, GoloElement newElement) {
171    if (statements.contains(original) && newElement instanceof GoloStatement) {
172      setStatement(statements.indexOf(original), (GoloStatement) newElement);
173    } else {
174      throw cantReplace(original, newElement);
175    }
176  }
177}