/* * Copyright (c) 2013-2016 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.sandbox; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.S_ASTERISK; 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.VM_LANGUAGE_CLOJURE; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.VM_LANGUAGE_GROOVY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.VM_LANGUAGE_JAVA; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.VM_LANGUAGE_JAVASCRIPT; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.VM_LANGUAGE_JRUBY; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.VM_LANGUAGE_KOTLIN; import static org.adoptopenjdk.jitwatch.core.JITWatchConstants.VM_LANGUAGE_SCALA; import static org.adoptopenjdk.jitwatch.util.UserInterfaceUtil.FONT_MONOSPACE_FAMILY; import static org.adoptopenjdk.jitwatch.util.UserInterfaceUtil.FONT_MONOSPACE_SIZE; import java.io.File; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.adoptopenjdk.jitwatch.core.JITWatchConfig; import org.adoptopenjdk.jitwatch.core.JITWatchConstants; import org.adoptopenjdk.jitwatch.jvmlang.LanguageManager; import org.adoptopenjdk.jitwatch.logger.ILogListener; import org.adoptopenjdk.jitwatch.model.IMetaMember; import org.adoptopenjdk.jitwatch.parser.ILogParseErrorListener; import org.adoptopenjdk.jitwatch.parser.ILogParser; import org.adoptopenjdk.jitwatch.process.IExternalProcess; import org.adoptopenjdk.jitwatch.sandbox.Sandbox; import org.adoptopenjdk.jitwatch.ui.Dialogs; import org.adoptopenjdk.jitwatch.ui.Dialogs.Response; import org.adoptopenjdk.jitwatch.ui.main.IStageAccessProxy; import org.adoptopenjdk.jitwatch.ui.main.JITWatchUI; import org.adoptopenjdk.jitwatch.ui.stage.IStageClosedListener; import org.adoptopenjdk.jitwatch.ui.stage.StageManager; import org.adoptopenjdk.jitwatch.util.DisassemblyUtil; 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.Event; 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.ComboBox; import javafx.scene.control.SplitPane; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TextArea; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; import javafx.stage.WindowEvent; public class SandboxStage extends Stage implements ISandboxStage, IStageClosedListener, ILogListener, ILogParseErrorListener { private static final Logger logger = LoggerFactory.getLogger(SandboxStage.class); private TextArea taLog; private IStageAccessProxy accessProxy; private Sandbox sandbox; private TabPane tabPane; private Button btnSandboxConfig; private Button btnRun; private SandboxConfigStage sandboxConfigStage; private ObservableList<String> languageList = FXCollections.observableArrayList(); private ComboBox<String> comboBoxVMLanguage = new ComboBox<>(languageList); private JITWatchConfig config; public SandboxStage(final IStageClosedListener closeListener, IStageAccessProxy proxy, final ILogParser parser) { this.accessProxy = proxy; config = parser.getConfig(); config.switchToSandbox(); setupVMLanguages(); sandbox = new Sandbox(parser, this, this); setTitle("Sandbox - Code, Compile, Execute, and Analyse JIT logs"); tabPane = new TabPane(); tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() { @Override public void changed(ObservableValue<? extends Tab> ov, Tab t, Tab t1) { Tab selectedTab = tabPane.getSelectionModel().getSelectedItem(); if (selectedTab != null) { EditorPane pane = (EditorPane) selectedTab.getContent(); setVMLanguage(pane); } } }); SplitPane splitVertical = new SplitPane(); splitVertical.setOrientation(Orientation.VERTICAL); taLog = new TextArea(); String style = "-fx-font-family:" + FONT_MONOSPACE_FAMILY + "; -fx-font-size:" + FONT_MONOSPACE_SIZE + "px; -fx-background-color:white;"; taLog.setStyle(style); Button btnNewEditor = new Button("New Editor"); btnNewEditor.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { addEditor(null); } }); Button btnOpen = new Button("Open"); btnOpen.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { if (tabPane.getSelectionModel().getSelectedItem() == null) { addEditor(null); } Tab selectedTab = tabPane.getSelectionModel().getSelectedItem(); EditorPane pane = (EditorPane) selectedTab.getContent(); if (pane.isModified()) { pane.promptSave(); } FileChooser fc = new FileChooser(); fc.setTitle("Choose source file"); fc.setInitialDirectory(Sandbox.SANDBOX_SOURCE_DIR.toFile()); File result = fc.showOpenDialog(getStageForChooser()); if (result != null) { pane.loadSource(result); selectedTab.setText(pane.getName()); setVMLanguage(pane); saveEditorPaneConfig(); } } }); Button btnSave = new Button("Save"); btnSave.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { Tab selectedTab = tabPane.getSelectionModel().getSelectedItem(); if (selectedTab != null) { EditorPane pane = (EditorPane) selectedTab.getContent(); pane.saveFile(); selectedTab.setText(pane.getName()); setVMLanguage(pane); } } }); btnSandboxConfig = new Button("Configure Sandbox"); btnSandboxConfig.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { sandboxConfigStage = new SandboxConfigStage(SandboxStage.this, parser.getConfig()); StageManager.addAndShow(SandboxStage.this, sandboxConfigStage, SandboxStage.this); btnSandboxConfig.setDisable(true); } }); Button btnResetSandbox = new Button("Reset Sandbox"); btnResetSandbox.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { Response resp = Dialogs.showYesNoDialog(SandboxStage.this, "Reset Sandbox?", "Delete all modified Sandbox sources and classes?"); if (resp == Response.YES) { initialiseLog(); sandbox.reset(); loadDefaultEditors(); } } }); comboBoxVMLanguage.valueProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> ov, String oldVal, String newVal) { if (newVal != null) { log("Changed language to " + newVal); } } }); btnRun = new Button("Run"); btnRun.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { btnRun.setDisable(true); Tab selectedTab = tabPane.getSelectionModel().getSelectedItem(); if (selectedTab != null) { EditorPane pane = (EditorPane) selectedTab.getContent(); if (pane.isModified()) { pane.promptSave(); } setVMLanguage(pane); runFile(pane); } } }); Button btnOutput = new Button("View Output"); btnOutput.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent e) { IExternalProcess lastProcess = sandbox.getLastProcess(); String outputString; if (lastProcess != null) { outputString = lastProcess.getOutputStream(); } else { outputString = "No output"; } showOutput(outputString); } }); HBox hBoxTools = new HBox(); hBoxTools.setSpacing(10); hBoxTools.setPadding(new Insets(10)); hBoxTools.getChildren().add(btnNewEditor); hBoxTools.getChildren().add(btnOpen); hBoxTools.getChildren().add(btnSave); hBoxTools.getChildren().add(btnSandboxConfig); hBoxTools.getChildren().add(btnResetSandbox); hBoxTools.getChildren().add(comboBoxVMLanguage); hBoxTools.getChildren().add(btnRun); hBoxTools.getChildren().add(btnOutput); splitVertical.getItems().add(tabPane); splitVertical.getItems().add(taLog); splitVertical.setDividerPositions(0.75, 0.25); VBox vBoxMain = new VBox(); vBoxMain.getChildren().add(hBoxTools); vBoxMain.getChildren().add(splitVertical); BorderPane borderPane = new BorderPane(); borderPane.setTop(hBoxTools); borderPane.setCenter(splitVertical); initialiseLog(); Scene scene = UserInterfaceUtil.getScene(borderPane, JITWatchUI.WINDOW_WIDTH, JITWatchUI.WINDOW_HEIGHT); setScene(scene); setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent arg0) { saveEditorPaneConfig(); closeListener.handleStageClosed(SandboxStage.this); } }); loadLastEditorPanes(); } @Override public void setModified(EditorPane pane, boolean isModified) { for (Tab tab : tabPane.getTabs()) { EditorPane currentPane = (EditorPane) tab.getContent(); if (currentPane == pane) { String tabText = tab.getText(); if (isModified) { if (!tabText.endsWith(S_ASTERISK)) { tab.setText(tabText + S_ASTERISK); } } else { tab.setText(pane.getName()); } } } } private void setVMLanguage(EditorPane pane) { if (pane != null) { setVMLanguageFromFile(pane.getSourceFile()); } } @Override public void runFile(final EditorPane pane) { saveUnsavedEditors(); new Thread(new Runnable() { @Override public void run() { runSandbox(pane.getSourceFile()); btnRun.setDisable(false); } }).start(); } private void initialiseLog() { taLog.setText(S_EMPTY); log("Sandbox ready"); log("Disassembler available: " + DisassemblyUtil.isDisassemblerAvailable()); } private void loadLastEditorPanes() { List<String> panes = config.getLastEditorPaneList(); if (panes.size() == 0) { loadDefaultEditors(); } else { tabPane.getTabs().clear(); for (String panePath : panes) { addEditor(new File(panePath)); } } } private void loadDefaultEditors() { tabPane.getTabs().clear(); addEditor(new File(Sandbox.SANDBOX_SOURCE_DIR.toFile(), "SimpleInliningTest.java")); saveEditorPaneConfig(); } private void addEditor(File filename) { final EditorPane pane = new EditorPane(this); if (filename != null) { pane.loadSource(filename); } final Tab tab = new Tab(); tab.setContent(pane); tab.setText(pane.getName()); EventHandler<Event> closeHandler = new EventHandler<Event>() { @Override public void handle(Event e) { if (pane.isModified()) { pane.promptSave(); } tabPane.getTabs().remove(tab); } }; // JavaFX 2.2 (from Java 7) has no onCloseRequestProperty if (JITWatchUI.IS_JAVA_FX2) { tab.setOnClosed(closeHandler); } else { // Use reflection to call setOnCloseRequestProperty for Java 8 try { MethodType mt = MethodType.methodType(void.class, EventHandler.class); MethodHandle mh = MethodHandles.lookup().findVirtual(Tab.class, "setOnCloseRequest", mt); // fails with invokeExact due to generic type erasure? mh.invoke(tab, closeHandler); } catch (Throwable t) { logger.error("Exception: {}", t.getMessage(), t); } } tabPane.getTabs().add(tab); pane.requestFocus(); setVMLanguage(pane); saveEditorPaneConfig(); } private void saveEditorPaneConfig() { List<String> editorPanePaths = new ArrayList<>(); for (Tab tab : tabPane.getTabs()) { EditorPane pane = (EditorPane) tab.getContent(); if (pane != null && pane.getSourceFile() != null) { String editorPanePath = pane.getSourceFile().getAbsolutePath(); editorPanePaths.add(editorPanePath); } } config.setLastEditorPaneList(editorPanePaths); config.saveConfig(); } private void saveUnsavedEditors() { for (Tab tab : tabPane.getTabs()) { EditorPane pane = (EditorPane) tab.getContent(); pane.promptSave(); } } private void runSandbox(File fileToRun) { try { Platform.runLater(new Runnable() { @Override public void run() { taLog.setText(S_EMPTY); } }); String language = comboBoxVMLanguage.getValue(); if (language != null) { List<File> compileList = new ArrayList<>(); for (Tab tab : tabPane.getTabs()) { EditorPane pane = (EditorPane) tab.getContent(); File sourceFile = pane.getSourceFile(); if (sourceFile != null) { if (LanguageManager.isCompilable(language, sourceFile)) { compileList.add(sourceFile); } } } if (compileList.size() > 0) { sandbox.runSandbox(language, compileList, fileToRun); } else { log("Nothing to compile?"); } } } catch (Exception e) { logger.error("Sandbox failure", e); } } @Override public void addSourceFolder(File sourceFolder) { config.addSourceFolder(sourceFolder); } private void setVMLanguageFromFile(File sourceFile) { String language = LanguageManager.getLanguageFromFile(sourceFile); if (language != null) { comboBoxVMLanguage.getSelectionModel().select(language); } } @Override public void handleLogEntry(String text) { log(text); } @Override public void handleErrorEntry(String text) { log(text); } @Override public void log(final String text) { Platform.runLater(new Runnable() { @Override public void run() { taLog.appendText(text + S_NEWLINE); } }); } @Override public void openTriView(final IMetaMember member) { log("Launching TriView for " + member); Platform.runLater(new Runnable() { @Override public void run() { accessProxy.openTriView(member, true, 0); } }); } @Override public void showOutput(final String output) { Platform.runLater(new Runnable() { @Override public void run() { // TODO perhaps filter out classloading statements? accessProxy.openTextViewer("Sandbox Output", output, false, false); } }); } @Override public void showError(final String error) { Platform.runLater(new Runnable() { @Override public void run() { accessProxy.openTextViewer("Error", error, false, false); } }); } @Override public void handleStageClosed(Stage stage) { StageManager.closeStage(stage); if (stage instanceof SandboxConfigStage) { btnSandboxConfig.setDisable(false); } } private void setupVMLanguages() { List<String> vmLanguageList = config.getVMLanguageList(); addVMLanguages(config); vmLanguageList = config.getVMLanguageList(); Collections.sort(vmLanguageList); languageList.addAll(vmLanguageList); comboBoxVMLanguage.getSelectionModel().select(VM_LANGUAGE_JAVA); } private void addVMLanguages(JITWatchConfig config) { List<String> vmLanguageList = config.getVMLanguageList(); if (!vmLanguageList.contains(JITWatchConstants.VM_LANGUAGE_JAVA)) { config.addOrUpdateVMLanguage(VM_LANGUAGE_JAVA, System.getProperty("java.home")); } if (!vmLanguageList.contains(JITWatchConstants.VM_LANGUAGE_SCALA)) { config.addOrUpdateVMLanguage(VM_LANGUAGE_SCALA, S_EMPTY); } if (!vmLanguageList.contains(JITWatchConstants.VM_LANGUAGE_JRUBY)) { config.addOrUpdateVMLanguage(VM_LANGUAGE_JRUBY, S_EMPTY); } if (!vmLanguageList.contains(JITWatchConstants.VM_LANGUAGE_GROOVY)) { config.addOrUpdateVMLanguage(VM_LANGUAGE_GROOVY, S_EMPTY); } if (!vmLanguageList.contains(JITWatchConstants.VM_LANGUAGE_KOTLIN)) { config.addOrUpdateVMLanguage(VM_LANGUAGE_KOTLIN, S_EMPTY); } if (!vmLanguageList.contains(JITWatchConstants.VM_LANGUAGE_JAVASCRIPT)) { config.addOrUpdateVMLanguage(VM_LANGUAGE_JAVASCRIPT, System.getProperty("java.home")); } if (!vmLanguageList.contains(JITWatchConstants.VM_LANGUAGE_CLOJURE)) { config.addOrUpdateVMLanguage(VM_LANGUAGE_CLOJURE, S_EMPTY); } config.saveConfig(); } @Override public void handleError(final String title, final String body) { logger.error(title); Platform.runLater(new Runnable() { @Override public void run() { Dialogs.showOKDialog(SandboxStage.this, title, body); } }); } @Override public Stage getStageForChooser() { return this; } }