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

org.adoptopenjdk.jitwatch.ui.triview.TriView Maven / Gradle / Ivy

/*
 * Copyright (c) 2013-2017 Chris Newland.
 * Licensed under https://github.com/AdoptOpenJDK/jitwatch/blob/master/LICENSE-BSD
 * Instructions: https://github.com/AdoptOpenJDK/jitwatch/wiki
 */
package org.adoptopenjdk.jitwatch.ui.triview;

import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_CLOSE_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOLLAR;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_DOT;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_OPEN_PARENTHESES;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.C_SPACE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.DEBUG_LOGGING_TRIVIEW;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_EMPTY;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_NEWLINE;
import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_STATIC_INIT;

import java.util.List;

import org.adoptopenjdk.jitwatch.core.JITWatchConfig;
import org.adoptopenjdk.jitwatch.loader.ResourceLoader;
import org.adoptopenjdk.jitwatch.model.Compilation;
import org.adoptopenjdk.jitwatch.model.IMetaMember;
import org.adoptopenjdk.jitwatch.model.IReadOnlyJITDataModel;
import org.adoptopenjdk.jitwatch.model.MemberSignatureParts;
import org.adoptopenjdk.jitwatch.model.MetaClass;
import org.adoptopenjdk.jitwatch.model.assembly.AssemblyMethod;
import org.adoptopenjdk.jitwatch.model.bytecode.BytecodeInstruction;
import org.adoptopenjdk.jitwatch.model.bytecode.ClassBC;
import org.adoptopenjdk.jitwatch.model.bytecode.LineTable;
import org.adoptopenjdk.jitwatch.model.bytecode.LineTableEntry;
import org.adoptopenjdk.jitwatch.model.bytecode.MemberBytecode;
import org.adoptopenjdk.jitwatch.model.bytecode.SourceMapper;
import org.adoptopenjdk.jitwatch.ui.Dialogs;
import org.adoptopenjdk.jitwatch.ui.main.JITWatchUI;
import org.adoptopenjdk.jitwatch.ui.triview.assembly.AssemblyLabel;
import org.adoptopenjdk.jitwatch.ui.triview.assembly.ViewerAssembly;
import org.adoptopenjdk.jitwatch.ui.triview.bytecode.BytecodeLabel;
import org.adoptopenjdk.jitwatch.ui.triview.bytecode.ViewerBytecode;
import org.adoptopenjdk.jitwatch.ui.triview.source.ViewerSource;
import org.adoptopenjdk.jitwatch.util.BytecodeReceivingRunnable;
import org.adoptopenjdk.jitwatch.util.UserInterfaceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;

public class TriView extends Stage implements ILineListener
{
	private IMetaMember currentMember;
	private JITWatchConfig config;

	private ViewerSource viewerSource;
	private ViewerBytecode viewerBytecode;
	private ViewerAssembly viewerAssembly;

	private SplitPane splitViewer;

	private TriViewPane paneSource;
	private TriViewPane paneBytecode;
	private TriViewPane paneAssembly;

	private CheckBox checkSource;
	private CheckBox checkBytecode;
	private CheckBox checkAssembly;
	private CheckBox checkMouseover;
	private CheckBox checkLocalLabels;

	private Button btnCompileChain;
	private Button btnJITJournal;
	private Button btnLineTable;
	private Button btnInlinedInto;

	private ObservableList comboMemberList = FXCollections.observableArrayList();
	private ComboBox comboMember;

	private ObservableList comboCompilationList = FXCollections.observableArrayList();
	private ComboBox comboSelectedCompilation;

	private ClassSearch classSearch;

	private CompilationInfo compilationInfo;

	private Label lblMemberInfo;

	private boolean ignoreComboChanged = false;

	private boolean classBytecodeMismatch = false;

	private static final Logger logger = LoggerFactory.getLogger(TriView.class);

	private LineType focussedViewer = LineType.SOURCE;

	private TriViewNavigationStack navigationStack;

	private IReadOnlyJITDataModel model;

	public TriView(final JITWatchUI parent, final JITWatchConfig config)
	{
		this.config = config;
		this.model = parent.getJITDataModel();

		setTitle("TriView - Source, Bytecode, Assembly Viewer - JITWatch");

		VBox vBox = new VBox();

		HBox hBoxToolBarClass = new HBox();
		hBoxToolBarClass.setSpacing(10);
		hBoxToolBarClass.setPadding(new Insets(10));

		HBox hBoxToolBarButtons = new HBox();
		hBoxToolBarButtons.setSpacing(10);
		hBoxToolBarButtons.setPadding(new Insets(0, 10, 10, 10));

		setupCheckBoxes();

		btnCompileChain = new Button("Chain");
		btnCompileChain.setOnAction(new EventHandler()
		{
			@Override
			public void handle(ActionEvent e)
			{
				if (currentMember != null)
				{
					parent.openCompileChain(currentMember);
				}
			}
		});
		btnCompileChain.setTooltip(new Tooltip("Show chain of compiled and inlined children"));

		btnJITJournal = new Button("Journal");
		btnJITJournal.setOnAction(new EventHandler()
		{
			@Override
			public void handle(ActionEvent e)
			{
				if (currentMember != null)
				{
					String windowTitle = "JIT Journal for " + currentMember.toString();

					Compilation selectedCompilation = currentMember.getSelectedCompilation();

					if (selectedCompilation != null)
					{
						windowTitle += " - Compilation " + selectedCompilation.getSignature();
					}

					parent.openJournalViewer(windowTitle, currentMember);
				}
			}
		});
		btnJITJournal.setTooltip(new Tooltip("Show journal of JIT events for this member"));

		btnLineTable = new Button("LNT");
		btnLineTable.setOnAction(new EventHandler()
		{
			@Override
			public void handle(ActionEvent e)
			{
				if (currentMember != null)
				{
					String lineNumberTable = currentMember.getMemberBytecode().getLineTable().toString();

					parent.openTextViewer("LineNumberTable for " + currentMember.toString(), lineNumberTable, false, false);
				}
			}
		});
		btnLineTable.setTooltip(new Tooltip("Show LineNumberTable for current bytecode"));

		btnInlinedInto = new Button("Inlined into");
		btnInlinedInto.setOnAction(new EventHandler()
		{
			@Override
			public void handle(ActionEvent e)
			{
				if (currentMember != null)
				{
					parent.openInlinedIntoReport(currentMember);
				}
			}
		});
		btnInlinedInto.setTooltip(new Tooltip("Show where this method was inlined into"));

		compilationInfo = new CompilationInfo();

		Region spacerTop = new Region();
		HBox.setHgrow(spacerTop, Priority.ALWAYS);

		Region spacerBottom = new Region();
		HBox.setHgrow(spacerBottom, Priority.ALWAYS);

		hBoxToolBarButtons.getChildren().add(checkSource);
		hBoxToolBarButtons.getChildren().add(checkBytecode);
		hBoxToolBarButtons.getChildren().add(checkAssembly);
		hBoxToolBarButtons.getChildren().add(btnCompileChain);
		hBoxToolBarButtons.getChildren().add(btnJITJournal);
		hBoxToolBarButtons.getChildren().add(btnLineTable);
		hBoxToolBarButtons.getChildren().add(btnInlinedInto);
		hBoxToolBarButtons.getChildren().add(checkMouseover);
		hBoxToolBarButtons.getChildren().add(spacerBottom);
		hBoxToolBarButtons.getChildren().add(compilationInfo);

		Label lblClass = new Label("Class:");
		classSearch = new ClassSearch(this, parent.getPackageManager());
		classSearch.prefWidthProperty().bind(widthProperty().multiply(0.42));

		Label lblMember = new Label("Member:");

		// ================ Set up Member combo box ====================

		comboMember = new ComboBox<>(comboMemberList);
		comboMember.prefWidthProperty().bind(widthProperty().multiply(0.4));

		comboMember.valueProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue ov, IMetaMember oldVal, IMetaMember newVal)
			{
				if (!ignoreComboChanged)
				{
					if (newVal != null)
					{
						TriView.this.setMember(newVal, true, 0);
					}
				}
			}
		});

		comboMember.setCellFactory(getCallbackForMemberListCellFactory());

		comboMember.setConverter(new StringConverter()
		{
			@Override
			public String toString(IMetaMember mm)
			{
				return mm != null ? mm.toStringUnqualifiedMethodName(false, false) : "null";
			}

			@Override
			public IMetaMember fromString(String arg0)
			{
				return null;
			}
		});

		hBoxToolBarClass.getChildren().add(lblClass);
		hBoxToolBarClass.getChildren().add(classSearch);

		hBoxToolBarClass.getChildren().add(spacerTop);

		hBoxToolBarClass.getChildren().add(lblMember);
		hBoxToolBarClass.getChildren().add(comboMember);

		splitViewer = new SplitPane();
		splitViewer.setOrientation(Orientation.HORIZONTAL);

		Scene scene = UserInterfaceUtil.getScene(vBox, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT);
		navigationStack = new TriViewNavigationStack(this, scene);

		viewerSource = new ViewerSource(parent, this, LineType.SOURCE);
		viewerBytecode = new ViewerBytecode(parent, navigationStack, model, this, LineType.BYTECODE);
		viewerAssembly = new ViewerAssembly(parent, this, LineType.ASSEMBLY);

		paneSource = new TriViewPane(this, "Source", viewerSource);
		paneBytecode = new TriViewPane(this, "Bytecode (double click for JVM spec)", viewerBytecode);
		paneAssembly = new TriViewPane(this, "Assembly", viewerAssembly, getAssemblyTitleComponents());

		splitViewer.prefHeightProperty().bind(vBox.heightProperty());

		lblMemberInfo = new Label();

		vBox.getChildren().add(hBoxToolBarClass);
		vBox.getChildren().add(hBoxToolBarButtons);
		vBox.getChildren().add(splitViewer);
		vBox.getChildren().add(lblMemberInfo);

		setScene(scene);

		checkColumns();
		
		updateButtons();
	}

	private void setupCheckBoxes()
	{
		checkSource = new CheckBox("_Source");
		checkBytecode = new CheckBox("_Bytecode");
		checkAssembly = new CheckBox("_Assembly");

		createCheckBoxMouseFollow();

		checkSource.setSelected(true);
		checkBytecode.setSelected(true);
		checkAssembly.setSelected(true);

		addEventFilter(javafx.scene.input.KeyEvent.KEY_PRESSED, new EventHandler()
		{
			@Override
			public void handle(javafx.scene.input.KeyEvent event)
			{
				if (event.isAltDown())
				{
					switch (event.getCode())
					{
					case S:
						checkSource.setSelected(!checkSource.isSelected());
						break;
					case B:
						checkBytecode.setSelected(!checkBytecode.isSelected());
						break;
					case A:
						checkAssembly.setSelected(!checkAssembly.isSelected());
						break;
					case M:
						checkMouseover.setSelected(!checkMouseover.isSelected());
						break;
					case L:
						checkLocalLabels.setSelected(!checkLocalLabels.isSelected());
						break;
					default:
						break;
					}
				}
			}
		});

		ChangeListener checkListener = new ChangeListener()
		{
			@Override
			public void changed(ObservableValue ov, Boolean oldVal, Boolean newVal)
			{
				checkColumns();
			}
		};

		checkSource.selectedProperty().addListener(checkListener);
		checkBytecode.selectedProperty().addListener(checkListener);
		checkAssembly.selectedProperty().addListener(checkListener);
	}

	private HBox getAssemblyTitleComponents()
	{
		HBox hbox = new HBox();
		hbox.setSpacing(16);

		checkLocalLabels = new CheckBox("_Labels");
		checkLocalLabels.setTooltip(new Tooltip("Local labels"));

		checkLocalLabels.setSelected(config.isLocalAsmLabels());

		checkLocalLabels.selectedProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue ov, Boolean oldVal, Boolean newVal)
			{
				config.setLocalAsmLabels(newVal);
				config.saveConfig();

				if (currentMember != null)
				{
					updateBytecodeAndAssembly(false, 0);
				}
			}
		});

		hbox.getChildren().add(checkLocalLabels);

		comboSelectedCompilation = new ComboBox<>(comboCompilationList);
		comboSelectedCompilation.setStyle("-fx-font-size: 10px");

		comboSelectedCompilation.valueProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue ov, String oldVal, String newVal)
			{
				int index = comboSelectedCompilation.getSelectionModel().getSelectedIndex();

				if (index >= 0 && index < currentMember.getCompilations().size())
				{
					currentMember.setSelectedCompilation(index);

					updateBytecodeAndAssembly(false, 0);
				}
			}
		});

		hbox.getChildren().add(comboSelectedCompilation);

		return hbox;
	}

	private void createCheckBoxMouseFollow()
	{
		checkMouseover = new CheckBox("_Mouseover");

		checkMouseover.setSelected(config.isTriViewMouseFollow());

		checkMouseover.selectedProperty().addListener(new ChangeListener()
		{
			@Override
			public void changed(ObservableValue ov, Boolean oldVal, Boolean newVal)
			{
				config.setTriViewMouseFollow(newVal);
				config.saveConfig();
			}
		});
	}

	private Callback, ListCell> getCallbackForMemberListCellFactory()
	{
		return new Callback, ListCell>()
		{
			@Override
			public ListCell call(ListView arg0)
			{
				return new ListCell()
				{
					@Override
					protected void updateItem(IMetaMember item, boolean empty)
					{
						super.updateItem(item, empty);

						if (item == null || empty)
						{
							setText(S_EMPTY);
							setGraphic(null);
						}
						else
						{
							performUpdateOfItem(this, item);
						}
					}

					private void performUpdateOfItem(ListCell listCell, IMetaMember item)
					{
						listCell.setText(item.toStringUnqualifiedMethodName(false, false));

						if (item.isCompiled() && UserInterfaceUtil.IMAGE_TICK != null)
						{
							listCell.setGraphic(new ImageView(UserInterfaceUtil.IMAGE_TICK));
						}
						else
						{
							listCell.setGraphic(null);
						}
					}
				};
			}
		};
	}

	private void checkColumns()
	{
		splitViewer.getItems().clear();

		int colCount = 0;

		if (checkSource.isSelected())
		{
			splitViewer.getItems().add(paneSource);
			colCount++;
		}
		if (checkBytecode.isSelected())
		{
			splitViewer.getItems().add(paneBytecode);
			colCount++;
		}
		if (checkAssembly.isSelected())
		{
			splitViewer.getItems().add(paneAssembly);
			colCount++;
		}

		switch (colCount)
		{
		case 0:
			splitViewer.setDividerPositions(0);
			break;
		case 1:
			splitViewer.setDividerPositions(1);
			break;
		case 2:
			splitViewer.setDividerPositions(0.5);
			break;
		case 3:
			splitViewer.setDividerPositions(0.333, 0.666);
			break;
		default:
			break;
		}
	}

	public void setMetaClass(MetaClass metaClass)
	{
		String fqName = metaClass.getFullyQualifiedName();

		classSearch.setText(fqName);

		List members = metaClass.getMetaMembers();

		if (!members.isEmpty())
		{
			setMember(members.get(0), false, 0);
		}
		else
		{
			// unlikely but if no members then clear the combo
			comboMemberList.clear();
		}
	}

	public IMetaMember getMetaMember()
	{
		return currentMember;
	}

	public void setMember(IMetaMember member, boolean force)
	{
		boolean jumpToSource = true;

		setMember(member, force, jumpToSource, 0);
	}
	
	public void setMember(IMetaMember member, boolean force, int highlightBCI)
	{
		boolean jumpToSource = true;

		setMember(member, force, jumpToSource, highlightBCI);
	}

	public void setMember(final IMetaMember member, boolean force, final boolean jumpToSource, final int highlightBCI)
	{
		MetaClass previousClass = currentMember == null ? null : currentMember.getMetaClass();

		currentMember = member;

		final MetaClass memberClass = currentMember.getMetaClass();

		boolean sameClass = evaluateSameClass(force, previousClass, memberClass);

		processIfNotSameClass(sameClass, memberClass);

		ignoreComboChanged = true;
		comboMember.setValue(currentMember);
		ignoreComboChanged = false;

		comboSelectedCompilation.getSelectionModel().clearSelection();
		comboCompilationList.clear();

		List compilations = currentMember.getCompilations();

		for (Compilation compilation : compilations)
		{
			comboCompilationList.add(compilation.getSignature());
		}

		Compilation selectedCompilation = currentMember.getSelectedCompilation();

		if (selectedCompilation != null)
		{
			int selectedCompilationIndex = selectedCompilation.getIndex();
			comboSelectedCompilation.getSelectionModel().select(selectedCompilationIndex);
		}
				
		updateButtons();

		List allClassLocations = config.getAllClassLocations();

		final boolean finalSameClass = sameClass;

		UserInterfaceUtil.getBytecodeAndUpdateUI(currentMember, model, allClassLocations, new BytecodeReceivingRunnable()
		{
			public void run()
			{
				if (!finalSameClass)
				{					
					String source = null;

					String sourceFileName = getClassBC().getSourceFile();

					if (sourceFileName != null)
					{
						source = ResourceLoader.getSourceForFilename(sourceFileName, config.getSourceLocations());

						if (source == null)
						{
							source = ResourceLoader.getSourceForClassName(memberClass.getFullyQualifiedName(),
									config.getSourceLocations());
						}
					}

					boolean lineNumbers = true;
					boolean canHighlight = true;

					if (source == null)
					{
						source = "Source of " + member.getMetaClass().getName() + " not found\nin the configured source locations.";
						lineNumbers = false;
						canHighlight = false;
					}

					viewerSource.setContent(source, lineNumbers, canHighlight);
				}

				StringBuilder statusBarBuilder = new StringBuilder();
				
				updateStatusBarWithClassInformation(getClassBC(), statusBarBuilder);
				
				updateStatusBarIfCompiled(statusBarBuilder);

				applyActionsIfOffsetMismatchDetected(statusBarBuilder);
				
				lblMemberInfo.setText(statusBarBuilder.toString());

				updateBytecodeAndAssembly(jumpToSource, highlightBCI);
			}
		});
	}
	
	private void updateButtons()
	{
		boolean isCompiled = currentMember != null ? currentMember.isCompiled() : false;
		
		comboSelectedCompilation.setVisible(isCompiled);
		
		btnCompileChain.setDisable(!isCompiled);
		
		btnJITJournal.setDisable(!isCompiled);
		
		btnLineTable.setDisable(currentMember == null);
		
		btnInlinedInto.setDisable(currentMember == null);
	}

	private void applyActionsIfOffsetMismatchDetected(StringBuilder statusBarBuilder)
	{
		if (viewerBytecode.isOffsetMismatchDetected())
		{
			statusBarBuilder.append(C_SPACE).append("WARNING Class bytecode offsets do not match JIT log");

			if (!classBytecodeMismatch)
			{
				classBytecodeMismatch = true;

				Platform.runLater(new Runnable()
				{
					@Override
					public void run()
					{
						Dialogs.showOKDialog(TriView.this, "Wrong classes mounted for log file?",
								"Warning! The bytecode for this class does not match the bytecode offsets in your JIT log."
										+ S_NEWLINE
										+ "Are the mounted classes the same ones used at runtime when the log was created?");
					}
				});
			}
		}
	}

	private void updateBytecodeAndAssembly(boolean focusSource, int highlightBCI)
	{
		Compilation compilation = currentMember.getSelectedCompilation();

		compilationInfo.setCompilation(compilation);

		viewerBytecode.setContent(currentMember);

		MemberBytecode memberBytecode = currentMember.getMemberBytecode();

		if (focusSource)
		{
			if (memberBytecode != null)
			{
				viewerBytecode.highlightLine(highlightBCI, true);
				lineHighlighted(highlightBCI, LineType.BYTECODE_BCI);
			}
			else
			{
				viewerSource.jumpToMemberSource(currentMember);
				viewerSource.setScrollBar();
			}
		}

		if (currentMember.isCompiled())
		{
			AssemblyMethod asmMethod = null;

			if (compilation != null)
			{
				asmMethod = compilation.getAssembly();
			}

			if (asmMethod == null)
			{
				// TODO check if -XX:+PrintAssembly was used
				String msg = "Assembly not found. Was -XX:+PrintAssembly option used?";
				viewerAssembly.setContent(msg, false, false);
			}
			else
			{
				viewerAssembly.setAssemblyMethod(asmMethod, config.isLocalAsmLabels());
			}
		}
		else
		{
			String msg = "Not JIT-compiled";
			viewerAssembly.setContent(msg, false, false);

			if (compilation == null && memberBytecode != null)
			{
				compilationInfo.setBytecodeSize(Integer.toString(memberBytecode.size()));
			}

			lblMemberInfo.setText(S_EMPTY);
		}
	}

	private void updateStatusBarIfCompiled(StringBuilder statusBarBuilder)
	{
		if (currentMember.isCompiled())
		{
			statusBarBuilder.append(C_SPACE).append(currentMember.toStringUnqualifiedMethodName(true, true));

			Compilation compilation = currentMember.getSelectedCompilation();

			if (compilation != null)
			{
				statusBarBuilder.append(" compiled with ").append(compilation.getCompiler());
			}
		}
	}

	private void updateStatusBarWithClassInformation(ClassBC classBytecode, StringBuilder statusBarBuilder)
	{
		if (classBytecode != null)
		{
			int majorVersion = classBytecode.getMajorVersion();
			int minorVersion = classBytecode.getMinorVersion();
			String javaVersion = classBytecode.getJavaVersion();

			statusBarBuilder.append("Mounted class version: ");
			statusBarBuilder.append(majorVersion).append(C_DOT).append(minorVersion);
			statusBarBuilder.append(C_SPACE).append(C_OPEN_PARENTHESES);
			statusBarBuilder.append(javaVersion).append(C_CLOSE_PARENTHESES);
		}
	}

	private void processIfNotSameClass(boolean sameClass, MetaClass memberClass)
	{
		if (!sameClass)
		{
			classBytecodeMismatch = false;

			comboMember.getSelectionModel().clearSelection();
			comboMemberList.clear();
			comboMemberList.addAll(memberClass.getMetaMembers());
			
			String fqName = memberClass.getFullyQualifiedName();
			classSearch.setText(fqName);
		}
	}

	public void clear()
	{
		comboMember.getSelectionModel().clearSelection();
		comboMemberList.clear();
		
		comboCompilationList.clear();
		comboSelectedCompilation.getSelectionModel().clearSelection();
		
		paneSource.clear();
		paneBytecode.clear();
		paneAssembly.clear();
		
		compilationInfo.clear();
		
		currentMember = null;
		
		classSearch.clear();
		
		lblMemberInfo.setText(S_EMPTY);
		
		updateButtons();
	}

	private boolean evaluateSameClass(boolean force, MetaClass previousClass, MetaClass memberClass)
	{
		boolean result = false;

		if (!force)
		{
			if ((previousClass != null) && previousClass.equals(memberClass))
			{
				result = true;
			}
		}

		return result;
	}

	@Override
	public void lineHighlighted(int index, LineType lineType)
	{
		if (DEBUG_LOGGING_TRIVIEW)
		{
			logger.debug("lineHighlighted {} {}", index, lineType);
		}

		switch (lineType)
		{
		case SOURCE:
			highlightFromSource(index);
			break;
		case BYTECODE:
			highlightFromBytecode(index);
			break;
		case BYTECODE_BCI:
			int indexForBCI = viewerBytecode.getLineIndexForBytecodeOffset(index);
			viewerBytecode.highlightLine(indexForBCI, true);
			highlightFromBytecode(indexForBCI);
			break;
		case ASSEMBLY:
			highlightFromAssembly(index);
			break;
		default:
			break;
		}
	}

	private void highlightFromSource(int index)
	{
		int sourceLine = index + 1;
		int bytecodeHighlight = -1;

		MetaClass metaClass = null;

		if (currentMember != null)
		{
			metaClass = currentMember.getMetaClass();
		}

		if (DEBUG_LOGGING_TRIVIEW)
		{
			logger.debug("highlightFromSource: {}", sourceLine);
			logger.debug("metaClass: {}", metaClass.getFullyQualifiedName());
		}

		if (metaClass != null)
		{
			MemberBytecode memberBytecode = SourceMapper.getMemberBytecodeForSourceLine(
					metaClass.getClassBytecode(model, config.getConfiguredClassLocations()), sourceLine);

			if (memberBytecode != null)
			{
				if (DEBUG_LOGGING_TRIVIEW)
				{
					logger.debug("Found MemberBytecode for sourceLine: {}", sourceLine);
				}

				MemberSignatureParts msp = memberBytecode.getMemberSignatureParts();

				if (DEBUG_LOGGING_TRIVIEW)
				{
					logger.debug("MemberSignatureParts:\n{}", msp);
				}

				if (!msp.getFullyQualifiedClassName().equals(metaClass.getFullyQualifiedName()))
				{
					if (DEBUG_LOGGING_TRIVIEW)
					{
						logger.debug("Different class in this source file");
					}

					metaClass = model.getPackageManager().getMetaClass(msp.getFullyQualifiedClassName());
				}

				IMetaMember nextMember = metaClass.getMemberForSignature(msp);

				if (DEBUG_LOGGING_TRIVIEW)
				{
					logger.debug("nextMember: {}", nextMember);
				}

				if (nextMember != null)
				{
					if (!nextMember.equals(currentMember))
					{
						setMember(nextMember, false, false, 0);
					}

					LineTable lineTable = memberBytecode.getLineTable();

					LineTableEntry lineTableEntry = lineTable.getEntryForSourceLine(sourceLine);

					if (DEBUG_LOGGING_TRIVIEW)
					{
						logger.debug("getEntryForSourceLine({}) : {}", sourceLine, lineTableEntry);
					}

					if (lineTableEntry != null)
					{
						int bcOffset = lineTableEntry.getBytecodeOffset();

						bytecodeHighlight = viewerBytecode.getLineIndexForBytecodeOffset(bcOffset);
					}

				}
				else if (msp != null && !msp.getMemberName().equals(S_STATIC_INIT))
				{
					logger.warn("Could not find member for bytecode signature: {}", msp);
				}
			}

			int assemblyHighlight = -1;
			assemblyHighlight = viewerAssembly.getIndexForSourceLine(metaClass.getFullyQualifiedName(), sourceLine);
			viewerAssembly.highlightLine(assemblyHighlight, true);

		}

		viewerBytecode.highlightLine(bytecodeHighlight, true);
	}

	private void highlightFromBytecode(int index)
	{
		MetaClass metaClass = null;

		if (currentMember != null)
		{
			metaClass = currentMember.getMetaClass();
		}

		if (metaClass != null)
		{
			BytecodeLabel bcLabel = (BytecodeLabel) viewerBytecode.getLabelAtIndex(index);

			if (bcLabel != null)
			{
				BytecodeInstruction instruction = bcLabel.getInstruction();

				int bytecodeOffset = instruction.getOffset();

				int sourceHighlight = -1;
				int assemblyHighlight = viewerAssembly.getIndexForBytecodeOffset(metaClass.getFullyQualifiedName(), instruction);

				ClassBC classBytecode = metaClass.getClassBytecode(model, config.getConfiguredClassLocations());

				if (classBytecode != null)
				{
					MemberBytecode memberBytecode = classBytecode.getMemberBytecode(currentMember);

					if (memberBytecode != null)
					{
						sourceHighlight = SourceMapper.getSourceLineFromBytecode(memberBytecode, bytecodeOffset);
					}
					else
					{
						logger.warn("No MemberBytecode found for {}", currentMember);
					}

					if (sourceHighlight != -1)
					{
						// starts at 1
						sourceHighlight--;
					}
				}

				viewerSource.highlightLine(sourceHighlight, true);
				viewerAssembly.highlightLine(assemblyHighlight, true);
			}
		}
	}

	private void highlightFromAssembly(int index)
	{
		Label label = viewerAssembly.getLabelAtIndex(index);

		if (label != null && label instanceof AssemblyLabel)
		{
			String className = viewerAssembly.getClassNameFromLabel(label);

			if (isClassNameEqualsCurrentMemberClassName(className) || isClassNameAnInnerClassOfCurrentMember(className))
			{
				String sourceLine = viewerAssembly.getSourceLineFromLabel(label);
				String bci = viewerAssembly.getBytecodeOffsetFromLabel(label);

				if (sourceLine != null)
				{
					try
					{
						int sourceHighlight = Integer.parseInt(sourceLine) - 1;

						viewerSource.highlightLine(sourceHighlight, true);

						if (bci != null)
						{
							try
							{
								int bciIndex = Integer.parseInt(bci);
								viewerBytecode.highlightBytecodeOffset(bciIndex);
							}
							catch (NumberFormatException nfe)
							{
								logger.error("Could not parse bci: {}", bci, nfe);
							}
						}
					}
					catch (NumberFormatException nfe)
					{
						logger.error("Could not parse source line number: {}", sourceLine, nfe);
					}
				}
			}
		}
	}

	private boolean isClassNameEqualsCurrentMemberClassName(String className)
	{
		boolean result = false;

		if (currentMember != null && className != null)
		{
			String currentFQClassName = currentMember.getMetaClass().getFullyQualifiedName();

			result = className.equals(currentFQClassName);
		}

		return result;
	}

	private boolean isClassNameAnInnerClassOfCurrentMember(String className)
	{
		boolean result = false;

		if (currentMember != null && className != null)
		{
			int dollarPos = className.indexOf(C_DOLLAR);

			if (dollarPos != -1)
			{
				String currentFQClassName = currentMember.getMetaClass().getFullyQualifiedName();

				String baseClassName = className.substring(0, dollarPos);

				if (currentFQClassName.equals(baseClassName))
				{
					result = true;
				}
			}
		}

		return result;
	}

	@Override
	public void handleFocusNext()
	{
		switch (focussedViewer)
		{
		case SOURCE:
			if (checkBytecode.isSelected())
			{
				focusBytecode();
			}
			else if (checkAssembly.isSelected())
			{
				focusAssembly();
			}
			break;
		case BYTECODE:
			if (checkAssembly.isSelected())
			{
				focusAssembly();
			}
			break;
		case ASSEMBLY:
			break;
		default:
			break;
		}
	}

	@Override
	public void handleFocusPrev()
	{
		switch (focussedViewer)
		{
		case SOURCE:
			break;
		case BYTECODE:
			if (checkSource.isSelected())
			{
				focusSource();
			}
			break;
		case ASSEMBLY:
			if (checkBytecode.isSelected())
			{
				focusBytecode();
			}
			else if (checkSource.isSelected())
			{
				focusSource();
			}
			break;
		default:
			break;
		}
	}

	@Override
	public void handleFocusSelf(LineType lineType)
	{
		switch (lineType)
		{
		case SOURCE:
			focusSource();
			break;
		case BYTECODE:
			focusBytecode();
			break;
		case ASSEMBLY:
			focusAssembly();
			break;
		default:
			break;
		}
	}

	private void focusSource()
	{
		paneSource.focus();
		paneBytecode.unFocus();
		paneAssembly.unFocus();

		focussedViewer = LineType.SOURCE;
		viewerSource.requestFocus();
	}

	private void focusBytecode()
	{
		paneSource.unFocus();
		paneBytecode.focus();
		paneAssembly.unFocus();

		focussedViewer = LineType.BYTECODE;
		viewerBytecode.requestFocus();
	}

	private void focusAssembly()
	{
		paneSource.unFocus();
		paneBytecode.unFocus();
		paneAssembly.focus();

		focussedViewer = LineType.ASSEMBLY;
		viewerAssembly.requestFocus();
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy