All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.java.decompiler.modules.decompiler.StatEdge Maven / Gradle / Ivy

Go to download

Modern Java & JVM language decompiler aiming to be as accurate as possible, with an emphasis on output quality.

The newest version!
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.java.decompiler.modules.decompiler;

import org.jetbrains.annotations.Nullable;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.EdgeDirection;

import java.util.ArrayList;
import java.util.List;

// A connection between 2 statements.
// Describes edges in graphs where statements are the vertices.
public class StatEdge {
  // Represents direct control flow between 2 statements
  public static final int TYPE_REGULAR = 1;
  // Represents implicit control flow between any statement in a try block and the catch blocks
  // This is because any statement in the try block can throw and execution can flow to the catch blocks from there
  public static final int TYPE_EXCEPTION = 2;
  // Represents control flow out of a statement and to the next statement
  // Also represents returns
  public static final int TYPE_BREAK = 4;
  // Represents control flow back up to a previous statement, marking a loop
  public static final int TYPE_CONTINUE = 8;
  // Represents exits from finally blocks
  public static final int TYPE_FINALLYEXIT = 32;

  public static final int[] TYPES = new int[]{
    TYPE_REGULAR,
    TYPE_EXCEPTION,
    TYPE_BREAK,
    TYPE_CONTINUE,
    TYPE_FINALLYEXIT
  };

  private int type;

  private Statement source;

  private Statement destination;

  private List exceptions;

  // The statement that this edge is enclosed in.
  // Take for example, this structure, where we are analyzing the break edge:
  //
  // label1: {
  //   if (...) {
  //     break label1;
  //   }
  // }
  // System.out.println("Test");
  //
  // The body of the if statement would be considered the source, and the println would be considered the destination.
  // The sequence statement enclosing the if would be considered the closure.
  // BREAK and CONTINUE edge types should always have a closure! (except for when break edges are return edges)
  public @Nullable Statement closure;

  // Whether this edge is labeled or not.
  public boolean labeled = true;

  // Whether this edge is explicitly defined or implicit.
  public boolean explicit = true;

  // Whether this edge can be inlined to simplify the decompiled output or not.
  public boolean canInline = true;

  // If this edge is a continue edge set as a break edge for readability
  public boolean phantomContinue = false;

  public StatEdge(int type, Statement source, Statement destination, @Nullable Statement closure) {
    this(type, source, destination);
    this.closure = closure;
  }

  public StatEdge(int type, Statement source, Statement destination) {
    this.type = type;
    this.source = source;
    this.destination = destination;

    ValidationHelper.notNull(source);
    ValidationHelper.notNull(destination);
  }

  public StatEdge(Statement source, Statement destination, List exceptions) {
    this(TYPE_EXCEPTION, source, destination);
    if (exceptions != null) {
      this.exceptions = new ArrayList<>(exceptions);
    }
  }

  public int getType() {
    return type;
  }

  public void setType(int type) {
    this.type = type;
  }

  public Statement getSource() {
    return source;
  }

  public void setSource(Statement source) {
    ValidationHelper.assertTrue(source != null, "Should not be null");
    this.source = source;
  }

  /**
   * Makes this edge point from a different source. This will
   * remove this edge as a successor of the old source,
   * add it as a successor of the new source,
   * and update the predecessor of the current destination.
   * @param newSource the new source of this edge
   */
  public void changeSource(Statement newSource) {
    ValidationHelper.assertTrue(newSource != null, "Should not be null");

    Statement oldSource = this.source;
    oldSource.removeEdgeInternal(EdgeDirection.FORWARD, this);
    newSource.addEdgeInternal(EdgeDirection.FORWARD, this);
    this.source = newSource;
  }

  public Statement getDestination() {
    return destination;
  }

  public void setDestination(Statement destination) {
    ValidationHelper.assertTrue(destination != null, "Should not be null");
    this.destination = destination;
  }

  /**
   * Makes this edge point from a different destination. This will
   * remove this edge as a predecessor of the old destination,
   * add it as a predecessor of the new destination,
   * and update the successor of the current source.
   * @param newDestination the new destination of this edge
   */
  public void changeDestination(Statement newDestination) {
    ValidationHelper.assertTrue(newDestination != null, "Should not be null");

    Statement oldDestination = this.destination;
    oldDestination.removeEdgeInternal(EdgeDirection.BACKWARD, this);
    newDestination.addEdgeInternal(EdgeDirection.BACKWARD, this);
    this.destination = newDestination;
  }


  /**
   * Updates the type of this edge.
   * It will notify both the source and the destination of the change.
   * @param type
   */
  public void changeType(int type) {
    this.source.changeEdgeType(EdgeDirection.FORWARD, this, type);
  }

  /**
   * Removes this edge from the graph.
   * The source will lose this edge as a successor,
   * the destination will lose this edge as a predecessor.
   * and if there is a labeled closure, it will be removed from there as well.
   */
  public void remove() {
    this.source.removeEdgeInternal(EdgeDirection.FORWARD, this);
    this.destination.removeEdgeInternal(EdgeDirection.BACKWARD, this);

    if (this.closure != null) {
      this.closure.getLabelEdges().remove(this);
    }
  }

  /**
   * Remove the closure of this edge. This edge will no
   * longer be labeled.
   */
  public void removeClosure() {
    ValidationHelper.validateTrue(closure != null, "closure shouldn't be null here");
    this.closure.getLabelEdges().remove(this);
    this.labeled = false;
    this.closure = null;
  }

  public List getExceptions() {
    return this.exceptions;
  }

  @Override
  public String toString() {
    return this.type + ": " + this.source.toString() + " -> " + this.destination.toString() + ((this.closure == null) ? "" : " (" + this.closure + ")") + ((this.exceptions == null) ? "" : " Exceptions: " + this.exceptions);
  }

  public void changeClosure(Statement stat) {
    if (this.closure != null) {
      this.closure.getLabelEdges().remove(this);
    }
    this.closure = stat;
    if (this.closure != null) {
      this.closure.getLabelEdges().add(this);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy