From 8e13d9afd39c28d2738a4a6ce3a77289e79d13f3 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Mon, 8 Feb 2021 22:34:50 +0100 Subject: [PATCH] A plugin to show desugared and decompiled code produced by javac. --- extra/java.debugjavac/.gitignore | 1 + extra/java.debugjavac/pom.xml | 217 ++++++++ .../modules/java/debugjavac/Bundle.properties | 26 + .../java/debugjavac/CompilerDescription.java | 229 +++++++++ .../java/debugjavac/DJavaDataObject.java | 57 +++ .../java/debugjavac/DecompileToolbar.form | 123 +++++ .../java/debugjavac/DecompileToolbar.java | 334 ++++++++++++ .../java/debugjavac/DecompiledTab.java | 474 ++++++++++++++++++ .../modules/java/debugjavac/Decompiler.java | 128 +++++ .../debugjavac/DecompilerDescription.java | 56 +++ .../java/debugjavac/TopLevelLexer.java | 88 ++++ .../java/debugjavac/TopLevelTokenId.java | 75 +++ .../modules/java/debugjavac/Utilities.java | 110 ++++ .../impl/DesugarDecompilerImpl.java | 120 +++++ .../debugjavac/impl/JavapDecompilerImpl.java | 129 +++++ .../modules/java/debugjavac/impl/Main.java | 65 +++ .../java/debugjavac/impl/Utilities.java | 72 +++ .../java.debugjavac/src/main/nbm/manifest.mf | 3 + .../modules/java/debugjavac/Bundle.properties | 23 + 19 files changed, 2330 insertions(+) create mode 100644 extra/java.debugjavac/.gitignore create mode 100644 extra/java.debugjavac/pom.xml create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Bundle.properties create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/CompilerDescription.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DJavaDataObject.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.form create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompiledTab.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Decompiler.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompilerDescription.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelLexer.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelTokenId.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Utilities.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/DesugarDecompilerImpl.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/JavapDecompilerImpl.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Main.java create mode 100644 extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Utilities.java create mode 100644 extra/java.debugjavac/src/main/nbm/manifest.mf create mode 100644 extra/java.debugjavac/src/main/resources/org/netbeans/modules/java/debugjavac/Bundle.properties diff --git a/extra/java.debugjavac/.gitignore b/extra/java.debugjavac/.gitignore new file mode 100644 index 0000000..a6f89c2 --- /dev/null +++ b/extra/java.debugjavac/.gitignore @@ -0,0 +1 @@ +/target/ \ No newline at end of file diff --git a/extra/java.debugjavac/pom.xml b/extra/java.debugjavac/pom.xml new file mode 100644 index 0000000..4a31783 --- /dev/null +++ b/extra/java.debugjavac/pom.xml @@ -0,0 +1,217 @@ + + + + + + 4.0.0 + org.netbeans.modules + java.debugjavac + 1.0 + nbm + Show javac output + + + org.apache.netbeans + netbeans-parent + 2 + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + 4.3 + true + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.2 + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-source-plugin + 2.1.2 + + + + jar-no-fork + + + + + + org.apache.rat + apache-rat-plugin + + + src/main/nbm/manifest.mf + + + + + + + + org.netbeans.api + org-netbeans-api-annotations-common + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-text + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-filesystems + ${netbeans.version} + + + org.netbeans.api + org-openide-modules + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + org.netbeans.api + org-netbeans-api-java + ${netbeans.version} + + + org.netbeans.api + org-netbeans-api-java-classpath + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-platform + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-lexer + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib2 + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-errorstripe-api + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-parsing-api + ${netbeans.version} + + + org.netbeans.api + org-netbeans-core-multiview + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-source-base + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-java-source + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-nbjunit + ${netbeans.version} + test + + + + + UTF-8 + RELEASE112 + + \ No newline at end of file diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Bundle.properties b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Bundle.properties new file mode 100644 index 0000000..92d3045 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Bundle.properties @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +OpenIDE-Module-Display-Category=Java +OpenIDE-Module-Long-Description=\ + Find out how javac desugars and compiles Java source code +OpenIDE-Module-Name=Debugging for javac +CTL_DecompiledTabCaption=Decompiled +DecompileToolbar.jLabel1.text=Compiler: +DecompileToolbar.jLabel2.text=Decompiler: +OpenIDE-Module-Short-Description=Find out how javac desugars and compiles Java source code +DecompileToolbar.jLabel3.text=Extra javac options: diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/CompilerDescription.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/CompilerDescription.java new file mode 100644 index 0000000..0353d10 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/CompilerDescription.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.XMLDecoder; +import java.beans.XMLEncoder; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.netbeans.api.java.platform.JavaPlatform; +import org.netbeans.api.java.platform.JavaPlatformManager; +import org.netbeans.api.java.platform.Specification; +import org.netbeans.modules.java.debugjavac.Decompiler.Input; +import org.netbeans.modules.java.debugjavac.Decompiler.Result; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.modules.InstalledFileLocator; +import org.openide.modules.SpecificationVersion; +import org.openide.util.Exceptions; + +/** + * + * @author lahvac + */ +public interface CompilerDescription { + public String getName(); + public boolean isValid(); + public Result decompile(DecompilerDescription decompiler, Input input); + + public static class Factory { + private static Collection descriptions; + public static synchronized Collection descriptions() { + if (descriptions != null) return descriptions; + + File moduleJar = InstalledFileLocator.getDefault().locate("modules/org-netbeans-modules-java-debugjavac.jar", null, false); + + List result = new ArrayList<>(); + + for (JavaPlatform platform : JavaPlatformManager.getDefault().getInstalledPlatforms()) { + if (!"j2se".equals(platform.getSpecification().getName())) continue; + + for (FileObject installDir : platform.getInstallFolders()) { + FileObject toolsJar = installDir.getFileObject("lib/tools.jar"); + FileObject jrtfs = installDir.getFileObject("lib/jrt-fs.jar"); + + if (toolsJar != null || jrtfs != null) { + result.add(new ExecCompilerDescription(platform, moduleJar, toolsJar != null ? FileUtil.toFile(toolsJar) : null)); + } + } + } + + return descriptions = Collections.unmodifiableList(result); + } + + static { + JavaPlatformManager.getDefault().addPropertyChangeListener(new PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent evt) { + synchronized (CompilerDescription.class) { + descriptions = null; + //TODO: should refresh the existing combos + } + } + }); + } + + private static class LoaderBased implements CompilerDescription { + public final String displayName; + public final URL[] jars; + + private LoaderBased(String displayName, URL[] jars) { + this.displayName = displayName; + this.jars = jars; + } + + @Override + public String getName() { + return displayName; + } + + private final AtomicReference valid = new AtomicReference<>(); + + public boolean isValid() { + Boolean val = valid.get(); + + if (val == null) { + valid.set(val = isValidImpl()); + } + + return val; + } + + private boolean isValidImpl() { + ClassLoader loader = getClassLoader(); + + if (loader == null) return false; + + try { + Class.forName("javax.tools.ToolProvider", true, loader); + return true; + } catch (Throwable ex) { + return false; + } + } + + private ClassLoader classLoader; + + private synchronized ClassLoader getClassLoader() { + if (classLoader == null) { + try { + List urls = new ArrayList<>(); + + urls.addAll(Arrays.asList(jars)); + urls.add(InstalledFileLocator.getDefault().locate("modules/ext/decompile.jar", null, false).toURI().toURL()); + + classLoader = new URLClassLoader(urls.toArray(new URL[0]), DecompiledTab.class.getClassLoader()); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + } + } + + return classLoader; + } + + @Override + public Result decompile(DecompilerDescription decompiler, Input input) { + ClassLoader loader = getClassLoader(); + + if (loader == null) return new Result("Internal error - cannot find ClassLoader.", null, null); + + return decompiler.createDecompiler(loader).decompile(input); + } + + } + + private static final class ExecCompilerDescription implements CompilerDescription { + + private static final SpecificationVersion HAS_ADD_EXPORTS = new SpecificationVersion("9"); + + private final JavaPlatform platform; + private final File moduleJar; + private final File toolsJar; + + public ExecCompilerDescription(JavaPlatform platform, File moduleJar, File toolsJar) { + this.platform = platform; + this.moduleJar = moduleJar; + this.toolsJar = toolsJar; + } + + @Override + public String getName() { + return platform.getDisplayName(); + } + + @Override + public boolean isValid() { + return true; //TODO + } + + @Override + public Result decompile(DecompilerDescription decompiler, Input input) { + try { + List args = new ArrayList<>(); + args.add(FileUtil.toFile(platform.findTool("java")).getAbsolutePath()); + if (toolsJar != null) { + args.add("-Xbootclasspath/p:" + toolsJar.getAbsolutePath()); + } + if (platform.getSpecification().getVersion().compareTo(HAS_ADD_EXPORTS) >= 0) { + args.add("--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"); + args.add("--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED"); + args.add("--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED"); + args.add("--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"); + args.add("--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"); + args.add("--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED"); + args.add("--add-exports=jdk.jdeps/com.sun.tools.javap=ALL-UNNAMED"); + } + args.add("-classpath"); + args.add(moduleJar.getAbsolutePath()); + args.add("org.netbeans.modules.java.debugjavac.impl.Main"); + args.add(decompiler.id); + Process process = Runtime.getRuntime().exec(args.toArray(new String[0])); + try (XMLEncoder enc = new XMLEncoder(process.getOutputStream())) { + enc.writeObject(input); + } + try (XMLDecoder decl = new XMLDecoder(process.getInputStream())) { + return (Result) decl.readObject(); + } + } catch (IOException ex) { + StringWriter exception = new StringWriter(); + try (PrintWriter exceptionPW = new PrintWriter(exception)) { + ex.printStackTrace(exceptionPW); + } + return new Result(exception.toString(), null, null); + } + + } + + } + + } + +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DJavaDataObject.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DJavaDataObject.java new file mode 100644 index 0000000..8d89340 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DJavaDataObject.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.io.IOException; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.MIMEResolver; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectExistsException; +import org.openide.loaders.MultiDataObject; +import org.openide.loaders.MultiFileLoader; +import org.openide.util.NbBundle.Messages; + +@Messages({ + "LBL_DJava_LOADER=Files of DJava" +}) +@MIMEResolver.ExtensionRegistration( + displayName = "#LBL_DJava_LOADER", + mimeType = "text/x-java-decompiled", + extension = {"djava"}, + position = 999207 + ) +@DataObject.Registration( + mimeType = "text/x-java-decompiled", +// iconBase = "SET/PATH/TO/ICON/HERE", + displayName = "#LBL_DJava_LOADER", + position = 300 + ) +public class DJavaDataObject extends MultiDataObject { + + public DJavaDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException { + super(pf, loader); + registerEditor("text/x-java-decompiled", false); + } + + @Override + protected int associateLookup() { + return 1; + } + +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.form b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.form new file mode 100644 index 0000000..14a9135 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.form @@ -0,0 +1,123 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.java new file mode 100644 index 0000000..3b29341 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompileToolbar.java @@ -0,0 +1,334 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JList; +import javax.swing.MutableComboBoxModel; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; +import org.openide.filesystems.FileObject; +import org.openide.util.Exceptions; +import org.openide.util.NbPreferences; +import org.openide.util.WeakListeners; + +/** + * + * @author lahvac + */ +public class DecompileToolbar extends javax.swing.JPanel { + + private static final int HISTORY_LIMIT = 2; + + public DecompileToolbar(final FileObject decompiled, final FileObject originalSource) { + initComponents(); + + DefaultComboBoxModel compilerModel = new DefaultComboBoxModel<>(); + Collection compilerDescriptions = CompilerDescription.Factory.descriptions(); + + for (CompilerDescription cd : compilerDescriptions) { + compilerModel.addElement(cd); + } + + compiler.setModel(compilerModel); + compiler.setRenderer(new DefaultListCellRenderer() { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + if (value instanceof CompilerDescription) { + CompilerDescription compilerDescription = (CompilerDescription) value; + + value = compilerDescription.getName() + (compilerDescription.isValid() ? "" : " - unusable"); + } + return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + } + }); + compiler.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + try { + decompiled.setAttribute(CompilerDescription.class.getName(), compiler.getSelectedItem()); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + + DefaultComboBoxModel decompilerModel = new DefaultComboBoxModel<>(); + List decompilers = new ArrayList<>(); + + if (compilerDescriptions.size() > 0) { + compiler.setSelectedIndex(0); + + for (DecompilerDescription decompiler : DecompilerDescription.getDecompilers()) { + decompilerModel.addElement(decompiler); + decompilers.add(decompiler); + } + } + + decompiler.setModel(decompilerModel); + decompiler.setRenderer(new DefaultListCellRenderer() { + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + if (value instanceof DecompilerDescription) value = ((DecompilerDescription) value).displayName; + return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + } + }); + decompiler.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { + try { + decompiled.setAttribute(DecompilerDescription.class.getName(), ((DecompilerDescription) decompiler.getSelectedItem()).id); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + decompiler.setSelectedIndex(0); + extraOptions.setModel(createModel()); + Object extraParams = originalSource.getAttribute(DecompiledTab.PROP_EXTRA_PARAMS); + if (extraParams instanceof String) { + try { + extraOptions.setSelectedItem(extraParams); + decompiled.setAttribute(DecompiledTab.PROP_EXTRA_PARAMS, extraParams); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + extraOptions.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + String selected = (String) extraOptions.getSelectedItem(); + + masterModel.removeElement(selected); + masterModel.insertElementAt(selected, 0); + + while (masterModel.getSize() > HISTORY_LIMIT) { + masterModel.removeElementAt(masterModel.getSize() - 1); + } + + storeMasterData(); + + extraOptions.getModel().setSelectedItem(selected); //the above changes the selection + + decompiled.setAttribute(DecompiledTab.PROP_EXTRA_PARAMS, selected); + originalSource.setAttribute(DecompiledTab.PROP_EXTRA_PARAMS, selected); + + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + } + + private static MutableComboBoxModel masterModel; + + private static final String KEY_EXTRA_PARAMS_HISTORY = "extraParamsHistory"; + + private static Preferences getHistoryNode() { + Preferences prefs = NbPreferences.forModule(DecompileToolbar.class); + + return prefs.node(KEY_EXTRA_PARAMS_HISTORY); + } + + private ComboBoxModel createModel() { + if (masterModel == null) { + masterModel = new DefaultComboBoxModel<>(); + + try { + Preferences prefs = getHistoryNode(); + List keys = new ArrayList<>(Arrays.asList(prefs.keys())); + + Collections.sort(keys); + + for (String key : keys) { + masterModel.addElement(prefs.get(key, "")); + } + } catch (BackingStoreException ex) { + Exceptions.printStackTrace(ex); + } + } + + return new DelegatingModel(masterModel); + } + + private static void storeMasterData() { + try { + Preferences prefs = getHistoryNode(); + + prefs.clear(); + + for (int i = 0; i < masterModel.getSize(); i++) { + prefs.put(Integer.toString(i), masterModel.getElementAt(i)); + } + } catch (BackingStoreException ex) { + Exceptions.printStackTrace(ex); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jLabel1 = new javax.swing.JLabel(); + compiler = new javax.swing.JComboBox(); + jLabel2 = new javax.swing.JLabel(); + decompiler = new javax.swing.JComboBox(); + jLabel3 = new javax.swing.JLabel(); + extraOptions = new javax.swing.JComboBox(); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DecompileToolbar.class, "DecompileToolbar.jLabel1.text")); // NOI18N + + compiler.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(DecompileToolbar.class, "DecompileToolbar.jLabel2.text")); // NOI18N + + decompiler.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(DecompileToolbar.class, "DecompileToolbar.jLabel3.text")); // NOI18N + + extraOptions.setEditable(true); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(compiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(decompiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(extraOptions, 0, 338, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(compiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel2) + .addComponent(decompiler, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel3) + .addComponent(extraOptions, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JComboBox compiler; + private javax.swing.JComboBox decompiler; + private javax.swing.JComboBox extraOptions; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + // End of variables declaration//GEN-END:variables + + private static class DelegatingModel implements ComboBoxModel, ListDataListener { + private final ComboBoxModel master; + private final List listeners = new ArrayList<>(); + + private Object selected; + + public DelegatingModel(ComboBoxModel master) { + this.master = master; + this.master.addListDataListener(WeakListeners.create(ListDataListener.class, this, master)); + } + + @Override + public void setSelectedItem(Object anItem) { + this.selected = anItem; + ListDataEvent del = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, -1, -1); + + for (ListDataListener l : listeners) { + l.contentsChanged(del); + } + } + + @Override + public Object getSelectedItem() { + return selected; + } + + @Override + public int getSize() { + return master.getSize(); + } + + @Override + public String getElementAt(int index) { + return master.getElementAt(index); + } + + @Override + public void addListDataListener(ListDataListener l) { + listeners.add(l); + } + + @Override + public void removeListDataListener(ListDataListener l) { + listeners.remove(l); + } + + @Override + public void intervalAdded(ListDataEvent e) { + ListDataEvent del = new ListDataEvent(this, e.getType(), e.getIndex0(), e.getIndex1()); + + for (ListDataListener l : listeners) { + l.intervalAdded(del); + } + } + + @Override + public void intervalRemoved(ListDataEvent e) { + ListDataEvent del = new ListDataEvent(this, e.getType(), e.getIndex0(), e.getIndex1()); + + for (ListDataListener l : listeners) { + l.intervalRemoved(del); + } + } + + @Override + public void contentsChanged(ListDataEvent e) { + ListDataEvent del = new ListDataEvent(this, e.getType(), e.getIndex0(), e.getIndex1()); + + for (ListDataListener l : listeners) { + l.contentsChanged(del); + } + } + } +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompiledTab.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompiledTab.java new file mode 100644 index 0000000..e147644 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompiledTab.java @@ -0,0 +1,474 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Map; +import java.util.WeakHashMap; +import javax.swing.Action; +import javax.swing.JComponent; +import javax.swing.JEditorPane; +import javax.swing.JScrollPane; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.StyledDocument; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.editor.EditorRegistry; +import org.netbeans.api.editor.mimelookup.MimeRegistration; +import org.netbeans.api.java.source.CancellableTask; +import org.netbeans.api.java.source.CompilationInfo; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.api.java.source.JavaSource.Priority; +import org.netbeans.api.java.source.JavaSourceTaskFactory; +import org.netbeans.api.java.source.support.EditorAwareJavaSourceTaskFactory; +import org.netbeans.core.spi.multiview.CloseOperationState; +import org.netbeans.core.spi.multiview.MultiViewElement; +import org.netbeans.core.spi.multiview.MultiViewElementCallback; +import org.netbeans.editor.GuardedDocument; +import org.netbeans.modules.editor.NbEditorDocument; +import org.netbeans.modules.editor.NbEditorKit; +import org.netbeans.modules.editor.NbEditorUtilities; +import org.netbeans.modules.java.debugjavac.Decompiler.Input; +import org.netbeans.modules.java.debugjavac.Decompiler.Result; +import org.netbeans.modules.parsing.api.Source; +import org.netbeans.modules.parsing.spi.TaskIndexingMode; +import org.netbeans.spi.editor.errorstripe.UpToDateStatus; +import org.netbeans.spi.editor.errorstripe.UpToDateStatusProvider; +import org.netbeans.spi.editor.errorstripe.UpToDateStatusProviderFactory; +import org.openide.awt.UndoRedo; +import org.openide.cookies.EditorCookie; +import org.openide.cookies.SaveCookie; +import org.openide.filesystems.FileAttributeEvent; +import org.openide.filesystems.FileChangeAdapter; +import org.openide.filesystems.FileChangeListener; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.loaders.DataObject; +import org.openide.text.NbDocument; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.util.lookup.ServiceProvider; +import org.openide.windows.TopComponent; + +/** + * + * @author lahvac + */ +public class DecompiledTab { + + public static final String PROP_EXTRA_PARAMS = DecompiledTab.class.getName() + ".extraParams"; + + private static final String ATTR_DECOMPILED = "decompiled-temporary"; + private static final Map source2Decompiled = new WeakHashMap<>(); + private static final RequestProcessor DECOMPILE_RUNNER = new RequestProcessor(DecompiledTab.class.getName(), 1, false, false); + + public static synchronized FileObject findDecompiled(FileObject source) { + return findDecompiled(source, true); + } + + private static synchronized FileObject findDecompiled(FileObject source, boolean create) { + FileObject result = source2Decompiled.get(source); + + if (result == null && create) { + try { + FileObject decompiledFO = FileUtil.createMemoryFileSystem().getRoot().createData(source.getName(), "djava"); + + decompiledFO.setAttribute(ATTR_DECOMPILED, Boolean.TRUE); + + source2Decompiled.put(source, result = decompiledFO); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + } + + return result; + } + + private static @CheckForNull Document decompiledCodeDocument(FileObject file) { + return decompiledCodeDocument(file, true); + } + + private static @CheckForNull Document decompiledCodeDocument(FileObject file, boolean create) { + try { + FileObject decompiled = findDecompiled(file, create); + + if (decompiled == null) return null; + + DataObject decompiledDO = DataObject.find(decompiled); + EditorCookie ec = decompiledDO.getLookup().lookup(EditorCookie.class); + return ec.openDocument(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + return null; + } + } + + private static DecompilerDescription findDecompiler(String id) throws MalformedURLException { + for (DecompilerDescription decompiler : DecompilerDescription.getDecompilers()) { + if (id.equals(decompiler.id)) return decompiler; + } + + return null; + } + + private static void decompileIntoDocumentLater(final FileObject source) { + DECOMPILE_RUNNER.post(new Runnable() { + @Override public void run() { + doDecompileIntoDocument(source); + } + }); + + } + + private static void doDecompileIntoDocument(FileObject source) { + FileObject decompiled = findDecompiled(source, true); + final Document doc = decompiledCodeDocument(source); + + if (doc == null || doc.getProperty(DECOMPILE_TAB_ACTIVE) != Boolean.TRUE) return ; + + try { + Object compilerDescription = decompiled.getAttribute(CompilerDescription.class.getName()); + Object decompilerId = decompiled.getAttribute(DecompilerDescription.class.getName()); + Object extraParams = decompiled.getAttribute(PROP_EXTRA_PARAMS); + + if (!(compilerDescription instanceof CompilerDescription) || !(decompilerId instanceof String)) { + return ; + } + + if (!(extraParams instanceof String) || extraParams == null) { + extraParams = ""; + } + + final String decompiledCode; + + if (((CompilerDescription) compilerDescription).isValid()) { + final String code = Source.create(source).createSnapshot().getText().toString(); + UpToDateStatusProviderImpl.get(doc).update(UpToDateStatus.UP_TO_DATE_PROCESSING); + DecompilerDescription decompiler = findDecompiler((String) decompilerId); + Result decompileResult = ((CompilerDescription) compilerDescription).decompile(decompiler, new Input(code, Utilities.commandLineParameters(source, (String) extraParams))); + if (decompileResult.exception != null) { + decompiledCode = "#Section(text/plain) Ooops, an exception occurred while decompiling:\n" + decompileResult.exception; + } else { + decompiledCode = (decompileResult.decompiledOutput != null ? "#Section(" + decompileResult.decompiledMimeType + ") Output:\n" + decompileResult.decompiledOutput + "\n" : "") + + (decompileResult.compileErrors != null ? "#Section(text/plain) Processing Errors:\n" + decompileResult.compileErrors + "\n" : ""); + } + } else { + decompiledCode = "Unusable compiler"; + } + + NbDocument.runAtomic((StyledDocument) doc, new Runnable() { + @Override public void run() { + try { + doc.remove(0, doc.getLength()); + if (doc instanceof GuardedDocument) { + ((GuardedDocument) doc).getGuardedBlockChain().removeEmptyBlocks(); + } + doc.insertString(0, decompiledCode, null); + if (doc instanceof GuardedDocument) { + ((GuardedDocument) doc).getGuardedBlockChain().addBlock(0, doc.getLength(), true); + } + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + + SaveCookie sc = DataObject.find(decompiled).getLookup().lookup(SaveCookie.class); + + if (sc != null) sc.save(); + + UpToDateStatusProviderImpl.get(doc).update(UpToDateStatus.UP_TO_DATE_OK); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + private static final String DECOMPILE_TAB_ACTIVE = "decompile-tab-active"; + + @MultiViewElement.Registration( + displayName="Decompile", +// iconBase="org/netbeans/modules/java/resources/class.gif", + persistenceType=TopComponent.PERSISTENCE_ONLY_OPENED, + preferredID="java.decompile", + mimeType="text/x-java", + position=5000 + ) + public static MultiViewElement createMultiViewEditorElement(Lookup context) { + final DataObject d = context.lookup(DataObject.class); + final FileObject decompiled = findDecompiled(d.getPrimaryFile(), true); + return new MultiViewElement() { + private JEditorPane pane; + private JComponent scrollPane; + private final FileChangeListener fileListener = new FileChangeAdapter() { + @Override public void fileAttributeChanged(FileAttributeEvent fe) { + decompileIntoDocumentLater(d.getPrimaryFile()); + } + }; + @Override + public JComponent getVisualRepresentation() { + if (pane == null) { + pane = new JEditorPane(); + pane.setContentType("text/x-java-decompiled"); + pane.setEditorKit(new NbEditorKit() { + @Override public String getContentType() { + return "text/x-java-decompiled"; + } + }); + Document doc = decompiledCodeDocument(d.getPrimaryFile()); + if (doc != null) + pane.setDocument(doc); + scrollPane = doc instanceof NbEditorDocument ? (JComponent) ((NbEditorDocument) doc).createEditor(pane) : new JScrollPane(pane); + } + return scrollPane; + } + + private DecompileToolbar toolbar; + @Override + public JComponent getToolbarRepresentation() { + if (toolbar == null) { + FileObject decompiled = findDecompiled(d.getPrimaryFile(), true); + toolbar = new DecompileToolbar(decompiled, d.getPrimaryFile()); + } + return toolbar; + } + + @Override + public Action[] getActions() { + return new Action[0]; + } + + @Override + public Lookup getLookup() { + return Lookup.EMPTY; + } + + @Override + public void componentOpened() { + } + + @Override + public void componentClosed() { + } + + @Override + public void componentShowing() { + Document doc = decompiledCodeDocument(d.getPrimaryFile()); + if (doc != null) doc.putProperty(DECOMPILE_TAB_ACTIVE, true); + decompiled.addFileChangeListener(fileListener); + decompileIntoDocumentLater(d.getPrimaryFile()); + } + + @Override + public void componentHidden() { + Document doc = decompiledCodeDocument(d.getPrimaryFile()); + if (doc != null) doc.putProperty(DECOMPILE_TAB_ACTIVE, null); + decompiled.removeFileChangeListener(fileListener); + } + + @Override + public void componentActivated() { + } + + @Override + public void componentDeactivated() { + } + + @Override + public UndoRedo getUndoRedo() { + return null; + } + + @Override + public void setMultiViewCallback(MultiViewElementCallback callback) { + } + + @Override + public CloseOperationState canCloseElement() { + return CloseOperationState.STATE_OK; + } + + }; + } + + static { + EditorRegistry.addPropertyChangeListener(new DocL()); + } + + private static final class DocL implements PropertyChangeListener, DocumentListener { + + private Document lastFocused; + + @Override public void propertyChange(PropertyChangeEvent evt) { + JTextComponent fc = EditorRegistry.focusedComponent(); + Document doc = fc != null ? fc.getDocument() : null; + + if (doc == lastFocused) return ; + + if (lastFocused != null) { + lastFocused.removeDocumentListener(this); + } + + if (doc != null) { + doc.addDocumentListener(this); + } + + lastFocused = doc; + } + + @Override + public void insertUpdate(DocumentEvent e) { + update(e); + } + + @Override + public void removeUpdate(DocumentEvent e) { + update(e); + } + + private void update(DocumentEvent e) { + FileObject file = NbEditorUtilities.getFileObject(e.getDocument()); + + if (file == null) return ; + + Document doc = decompiledCodeDocument(file, false); + + if (doc == null) return ; + + UpToDateStatusProviderImpl.get(doc).update(UpToDateStatus.UP_TO_DATE_DIRTY); + } + + @Override + public void changedUpdate(DocumentEvent e) { + } + + } + + private static final class Updater implements CancellableTask { + @Override public void run(CompilationInfo parameter) throws Exception { + doDecompileIntoDocument(parameter.getFileObject()); +// FileObject sourceFile = parameter.getFileObject(); +//// if (sourceFile.getAttribute(ATTR_DECOMPILED) == Boolean.TRUE) return; +// final FileObject decompiled = findDecompiled(sourceFile, false); +// +// if (decompiled == null) return ; +// +// final ElementHandle handle = ElementHandle.create(parameter.getTopLevelElements().get(0)); +// +// JavaSource.create(parameter.getClasspathInfo(), decompiled).runModificationTask(new Task() { +// @Override public void run(WorkingCopy copy) throws Exception { +// copy.toPhase(Phase.RESOLVED); +// +// copy.rewrite(copy.getCompilationUnit(), CodeGenerator.generateCode(copy, (TypeElement) handle.resolve(copy))); +// } +// }).commit(); +// +// SaveCookie sc = DataObject.find(decompiled).getLookup().lookup(SaveCookie.class); +// +// if (sc != null) sc.save(); + } + + @Override public void cancel() { + } + + } + + @ServiceProvider(service=JavaSourceTaskFactory.class) + public static final class UpdaterFactory extends EditorAwareJavaSourceTaskFactory { + + public UpdaterFactory() { + super(Phase.RESOLVED, Priority.LOW, TaskIndexingMode.ALLOWED_DURING_SCAN); + } + + @Override + protected CancellableTask createTask(FileObject file) { + return new Updater(); + } + + } + + @MimeRegistration(mimeType="text/x-java-decompiled", service=UpToDateStatusProviderFactory.class) + public static final class UpToDateStatusProviderFactoryImpl implements UpToDateStatusProviderFactory { + @Override public UpToDateStatusProvider createUpToDateStatusProvider(Document document) { + return UpToDateStatusProviderImpl.get(document); + } + } + + private static final class UpToDateStatusProviderImpl extends UpToDateStatusProvider { + + public static UpToDateStatusProviderImpl get(Document doc) { + UpToDateStatusProviderImpl result = (UpToDateStatusProviderImpl) doc.getProperty(UpToDateStatusProviderImpl.class); + + if (result == null) { + result = new UpToDateStatusProviderImpl(doc); + } + + return result; + } + + private final Document doc; + + private UpToDateStatusProviderImpl(Document doc) { + this.doc = doc; + } + + @Override + public UpToDateStatus getUpToDate() { + UpToDateStatus status = (UpToDateStatus) doc.getProperty(UpToDateStatusProviderImpl.class.getName() + "-status-value"); + + if (status == null) status = UpToDateStatus.UP_TO_DATE_DIRTY; + + return status; + } + + private void update(UpToDateStatus newValue) { + UpToDateStatus oldValue = getUpToDate(); + + doc.putProperty(UpToDateStatusProviderImpl.class.getName() + "-status-value", newValue); + firePropertyChange(PROP_UP_TO_DATE, oldValue, newValue); + + //TODO: the event above does not always repaint the error stripe, workarounding: + NbDocument.runAtomic((StyledDocument) doc, new Runnable() { + @Override public void run() { + try { + doc.insertString(0, " ", null); + doc.remove(0, 1); + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + + try { + FileObject decompiledFO = NbEditorUtilities.getFileObject(doc); + SaveCookie sc = decompiledFO != null ? DataObject.find(decompiledFO).getLookup().lookup(SaveCookie.class) : null; + + if (sc != null) sc.save(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + } +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Decompiler.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Decompiler.java new file mode 100644 index 0000000..c02a0da --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Decompiler.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.util.List; + +/** + * + * @author lahvac + */ +public interface Decompiler { + public Result decompile(Input input); + + public final class Input { + public String source; + public List params; + + public Input() { + } + + public Input(String source, List params) { + this.source = source; + this.params = params; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public List getParams() { + return params; + } + + public void setParams(List params) { + this.params = params; + } + + } +// public final class Input { +// public final String source; +// public final List params; +// public Input(String source, List params) { +// this.source = source; +// this.params = params; +// } +// } + public final class Result { + public String compileErrors; + public String decompiledOutput; + public String decompiledMimeType; + public String exception; + + public Result() { + } + + public Result(String compileErrors, String decompiledOutput, String decompiledMimeType) { + this.compileErrors = compileErrors.trim().isEmpty() ? null : compileErrors; + this.decompiledOutput = decompiledOutput.trim().isEmpty() ? null : decompiledOutput; + this.decompiledMimeType = decompiledMimeType; + } + + public Result(String exception) { + this.exception = exception; + } + + public String getCompileErrors() { + return compileErrors; + } + + public void setCompileErrors(String compileErrors) { + this.compileErrors = compileErrors; + } + + public String getDecompiledOutput() { + return decompiledOutput; + } + + public void setDecompiledOutput(String decompiledOutput) { + this.decompiledOutput = decompiledOutput; + } + + public String getDecompiledMimeType() { + return decompiledMimeType; + } + + public void setDecompiledMimeType(String decompiledMimeType) { + this.decompiledMimeType = decompiledMimeType; + } + + public String getException() { + return exception; + } + + public void setException(String exception) { + this.exception = exception; + } + } +// public final class Result { +// public final String compileErrors; +// public final String decompiledOutput; +// public final String decompiledMimeType; +// public Result(String compileErrors, String decompiledOutput, String decompiledMimeType) { +// this.compileErrors = compileErrors.trim().isEmpty() ? null : compileErrors; +// this.decompiledOutput = decompiledOutput.trim().isEmpty() ? null : decompiledOutput; +// this.decompiledMimeType = decompiledMimeType; +// } +// } +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompilerDescription.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompilerDescription.java new file mode 100644 index 0000000..0f4bfe8 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/DecompilerDescription.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.util.Arrays; + +/** + * + * @author lahvac + */ +public final class DecompilerDescription { + public final String id; + public final String displayName; + public final String className; + + private DecompilerDescription(String id, String displayName, String className) { + this.id = id; + this.displayName = displayName; + this.className = className; + } + + public Decompiler createDecompiler(ClassLoader from) { + try { + Class loadClass = from.loadClass(className); + + return Decompiler.class.cast(loadClass.newInstance()); + } catch (ReflectiveOperationException ex) { + throw new IllegalStateException(ex); + } + } + + private static final Iterable DECOMPILERS = Arrays.asList( + new DecompilerDescription("javap", "javap", "org.netbeans.modules.java.debugjavac.impl.JavapDecompilerImpl"), + new DecompilerDescription("lower", "Desugared source", "org.netbeans.modules.java.debugjavac.impl.DesugarDecompilerImpl") + ); + + public static Iterable getDecompilers() { + return DECOMPILERS; + } +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelLexer.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelLexer.java new file mode 100644 index 0000000..548bfe8 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelLexer.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.netbeans.api.lexer.Token; +import org.netbeans.spi.lexer.Lexer; +import org.netbeans.spi.lexer.LexerInput; +import org.netbeans.spi.lexer.LexerRestartInfo; +import org.netbeans.spi.lexer.TokenFactory; + +/** + * + * @author lahvac + */ +public class TopLevelLexer implements Lexer { + + private static final Pattern SECTION_PATTERN = Pattern.compile("#Section\\(([^)]*)\\)[^\n]*\n"); + private final TokenFactory factory; + private final LexerInput input; + private TopLevelTokenId futureToken; + + public TopLevelLexer(LexerRestartInfo restart) { + this.factory = restart.tokenFactory(); + this.input = restart.input(); + this.futureToken = restart.state() != null ? (TopLevelTokenId) restart.state() : TopLevelTokenId.OTHER; + } + + @Override + public Token nextToken() { + StringBuilder text = new StringBuilder(); + int read; + + while ((read = input.read()) != LexerInput.EOF) { + text.append((char) read); + + Matcher m = SECTION_PATTERN.matcher(text); + + if (m.find()) { + if (m.start() == 0) { + String mimeType = m.group(1); + + switch (mimeType) { + case "text/x-java": futureToken = TopLevelTokenId.JAVA; break; + case "text/x-java-bytecode": futureToken = TopLevelTokenId.ASM; break; + default: futureToken = TopLevelTokenId.OTHER; break; + } + + return factory.createToken(TopLevelTokenId.SECTION_HEADER); + } else { + input.backup(input.readLength() - m.start()); + break; + } + } + } + + if (input.readLength() > 0) + return factory.createToken(futureToken); + + return null; + } + + @Override + public Object state() { + return futureToken; + } + + @Override + public void release() {} + +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelTokenId.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelTokenId.java new file mode 100644 index 0000000..5c1d401 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/TopLevelTokenId.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.util.Arrays; +import java.util.Collection; +import org.netbeans.api.editor.mimelookup.MimeRegistration; +import org.netbeans.api.lexer.InputAttributes; +import org.netbeans.api.lexer.Language; +import org.netbeans.api.lexer.LanguagePath; +import org.netbeans.api.lexer.Token; +import org.netbeans.api.lexer.TokenId; +import org.netbeans.spi.lexer.EmbeddingPresence; +import org.netbeans.spi.lexer.LanguageEmbedding; +import org.netbeans.spi.lexer.LanguageHierarchy; +import org.netbeans.spi.lexer.Lexer; +import org.netbeans.spi.lexer.LexerRestartInfo; + +/** + * + * @author lahvac + */ +public enum TopLevelTokenId implements TokenId { + SECTION_HEADER, + JAVA, + ASM, + OTHER; + + @Override + public String primaryCategory() { + return this == SECTION_HEADER ? "comment" : "code"; + } + + private static final Language LANGUAGE = new LanguageHierarchy() { + @Override protected Collection createTokenIds() { + return Arrays.asList(TopLevelTokenId.values()); + } + @Override protected Lexer createLexer(LexerRestartInfo info) { + return new TopLevelLexer(info); + } + @Override protected String mimeType() { + return "text/x-java-decompiled"; + } + @Override protected LanguageEmbedding embedding(Token token, LanguagePath languagePath, InputAttributes inputAttributes) { + switch (token.id()) { + case JAVA: + return LanguageEmbedding.create(Language.find("text/x-java"), 0, 0); + default: + return null; + } + } + + }.language(); + + @MimeRegistration(mimeType="text/x-java-decompiled", service=Language.class) + public static final Language language() { + return LANGUAGE; + } +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Utilities.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Utilities.java new file mode 100644 index 0000000..dfd635c --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/Utilities.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.classpath.ClassPath.PathConversionMode; +import org.netbeans.api.java.platform.JavaPlatform; +import org.netbeans.api.java.queries.SourceLevelQuery; +import org.openide.filesystems.FileObject; + +/** + * + * @author lahvac + */ +public class Utilities { + public static List commandLineParameters(FileObject source, String extraParameters) throws IOException { + List extraParams = new ArrayList<>(); + Set extraParamsSet = new HashSet<>(); + StringBuilder param = new StringBuilder(); + char currentQuote = '\0'; + for (char c : extraParameters.toCharArray()) { + switch (c) { + case '\r': continue; + case ' ': case '\n': case '\t': + if (currentQuote == '\0') { + String p = param.toString(); + param.delete(0, param.length()); + extraParams.add(p); + extraParamsSet.add(p); + continue; + } + break; + case '"': + if (currentQuote == '"') { + currentQuote = '\0'; + continue; + } else if (currentQuote == '\0') { + currentQuote = '"'; + continue; + } + break; + case '\'': + if (currentQuote == '\'') { + currentQuote = '\0'; + continue; + } else if (currentQuote == '\0') { + currentQuote = '\''; + continue; + } + break; + } + + param.append(c); + } + if (param.length() > 0) { + String p = param.toString(); + extraParams.add(p); + extraParamsSet.add(p); + } + List result = new ArrayList<>(); +// if (!extraParamsSet.contains("-bootclasspath")) { +// ClassPath boot = ClassPath.getClassPath(source, ClassPath.BOOT); +// if (boot == null) boot = JavaPlatform.getDefault().getBootstrapLibraries(); +// result.add("-bootclasspath"); +// result.add(boot.toString(PathConversionMode.PRINT)); +// } + if (!extraParamsSet.contains("-classpath")) { + ClassPath compile = ClassPath.getClassPath(source, ClassPath.COMPILE); + if (compile == null) compile = ClassPath.EMPTY; + result.add("-classpath"); + result.add(compile.toString(PathConversionMode.PRINT)); + } + String sourceLevel = SourceLevelQuery.getSourceLevel(source); + sourceLevel = sourceLevel != null ? sourceLevel : "1.8"; + if (!extraParamsSet.contains("-source")) { + result.add("-source"); + result.add(sourceLevel); + } + if (!extraParamsSet.contains("-target")) { + result.add("-target"); + result.add(sourceLevel); + } + + result.addAll(extraParams); + + return result; + } + +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/DesugarDecompilerImpl.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/DesugarDecompilerImpl.java new file mode 100644 index 0000000..016ad4b --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/DesugarDecompilerImpl.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac.impl; + +import com.sun.source.tree.ImportTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.api.JavacTool; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Env; +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.util.Context.Factory; +import com.sun.tools.javac.util.Pair; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Queue; +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileObject; +import org.netbeans.modules.java.debugjavac.Decompiler; + +/** + * + * @author lahvac + */ +public class DesugarDecompilerImpl implements Decompiler { + + @Override + public Result decompile(Input input) { + StringWriter errors = new StringWriter(); + StringWriter decompiled = new StringWriter(); + + try { + DiagnosticListener errorsListener = Utilities.errorReportingDiagnosticListener(errors); + JavaFileObject file = Utilities.sourceFileObject(input.source); + JavacTask task = JavacTool.create().getTask(null, + null, + errorsListener, Utilities.augmentCommandLineParameters(input), null, Arrays.asList(file)); + + JavaCompilerOverride.preRegister(((JavacTaskImpl) task).getContext(), decompiled); + task.generate(); + } catch (IOException ex) { + ex.printStackTrace(new PrintWriter(errors)); + } + + return new Result(errors.toString(), decompiled.toString().trim(), "text/x-java"); + } + + static class JavaCompilerOverride extends JavaCompiler { + public static void preRegister(com.sun.tools.javac.util.Context context, final StringWriter out) { + context.put(compilerKey, new Factory() { + @Override public JavaCompiler make(com.sun.tools.javac.util.Context c) { + return new JavaCompilerOverride(out, c); + } + }); + } + private final StringWriter out; + + public JavaCompilerOverride(StringWriter out, com.sun.tools.javac.util.Context context) { + super(context); + this.out = out; + } + + @Override public void generate(Queue, JCClassDecl>> queue, Queue results) { + Pair, JCClassDecl> first = queue.peek(); + + if (first != null) { + if (first.fst.toplevel.getPackageName() != null) { + out.write("package "); + out.write(first.fst.toplevel.getPackageName().toString()); + out.write(";\n\n"); + } + + boolean hasImports = false; + + for (Tree importCandidate : first.fst.toplevel.defs) { + if (importCandidate != null && importCandidate.getKind() == Kind.IMPORT) { + out.write("import "); + ImportTree importTree = (ImportTree) importCandidate; + if (importTree.isStatic()) { + out.write("static "); + } + out.write(importTree.getQualifiedIdentifier().toString()); + out.write(";\n"); + hasImports = true; + } + } + + if (hasImports) { + out.write("\n"); + } + + for (Pair, JCClassDecl> q : queue) { + out.write(q.snd.toString()); + out.write("\n"); + } + } + } + } +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/JavapDecompilerImpl.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/JavapDecompilerImpl.java new file mode 100644 index 0000000..a6afba7 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/JavapDecompilerImpl.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac.impl; + +import com.sun.tools.classfile.ConstantPoolException; +import com.sun.tools.javac.api.JavacTool; +import com.sun.tools.javap.Context; +import com.sun.tools.javap.JavapTask; +import com.sun.tools.javap.JavapTask.BadArgs; +import com.sun.tools.javap.JavapTask.ClassFileInfo; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import javax.tools.DiagnosticListener; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileManager.Location; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import org.netbeans.modules.java.debugjavac.Decompiler; +import org.openide.util.Exceptions; + +/** + * + * @author lahvac + */ +public class JavapDecompilerImpl implements Decompiler { + + @Override + public Result decompile(Input input) { + StringWriter errors = new StringWriter(); + StringWriter decompiled = new StringWriter(); + try { + final Map bytecode = compile(input, errors); + + if (!bytecode.isEmpty()) { + for (final Entry e : bytecode.entrySet()) { + class JavapTaskImpl extends JavapTask { + public Context getContext() { + return context; + } + } + JavapTaskImpl t = new JavapTaskImpl(); + List options = new ArrayList(); + options.add("-private"); + options.add("-verbose"); + options.add(e.getKey()); + t.handleOptions(options.toArray(new String[0])); + t.getContext().put(PrintWriter.class, new PrintWriter(decompiled)); + ClassFileInfo cfi = t.read(new SimpleJavaFileObject(URI.create("mem://mem"), Kind.CLASS) { + @Override public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(e.getValue()); + } + }); + + t.write(cfi); + } + } + } catch (IOException | ConstantPoolException ex) { + ex.printStackTrace(new PrintWriter(errors)); + } catch (BadArgs ex) { + Exceptions.printStackTrace(ex); + } + + return new Result(errors.toString(), decompiled.toString(), "text/x-java-bytecode"); + } + + private static Map compile(Input input, final StringWriter errors) throws IOException { + DiagnosticListener errorsListener = Utilities.errorReportingDiagnosticListener(errors); + StandardJavaFileManager sjfm = JavacTool.create().getStandardFileManager(errorsListener, null, null); + final Map class2BAOS = new HashMap(); + + JavaFileManager jfm = new ForwardingJavaFileManager(sjfm) { + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, javax.tools.FileObject sibling) throws IOException { + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + class2BAOS.put(className, buffer); + return new SimpleJavaFileObject(sibling.toUri(), kind) { + @Override + public OutputStream openOutputStream() throws IOException { + return buffer; + } + }; + } + }; + + JavaFileObject file = Utilities.sourceFileObject(input.source); + JavacTool.create().getTask(null, jfm, errorsListener, /*XXX:*/Utilities.augmentCommandLineParameters(input), null, Arrays.asList(file)).call(); + + Map result = new HashMap(); + + for (Map.Entry e : class2BAOS.entrySet()) { + result.put(e.getKey(), e.getValue().toByteArray()); + } + + return result; + } + +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Main.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Main.java new file mode 100644 index 0000000..932ec28 --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Main.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac.impl; + +import java.beans.XMLDecoder; +import java.beans.XMLEncoder; +import java.io.PrintWriter; +import java.io.StringWriter; +import org.netbeans.modules.java.debugjavac.Decompiler.Input; +import org.netbeans.modules.java.debugjavac.Decompiler.Result; +import org.netbeans.modules.java.debugjavac.DecompilerDescription; + +/** + * + * @author lahvac + */ +public class Main { + public static void main(String... args) { + try { + Input input; + + try (XMLDecoder decoder = new XMLDecoder(System.in)) { + input = (Input) decoder.readObject(); + } + + String id = args[0]; + + for (DecompilerDescription desc : DecompilerDescription.getDecompilers()) { + if (id.equals(desc.id)) { + Result result = desc.createDecompiler(Main.class.getClassLoader()).decompile(input); + try (XMLEncoder enc = new XMLEncoder(System.out)) { + enc.writeObject(result); + } + return ; + } + } + + throw new IllegalStateException("Cannot find: " + id); + } catch (Throwable t) { + try (XMLEncoder enc = new XMLEncoder(System.out)) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + pw.close(); + enc.writeObject(new Result(sw.toString())); + } + } + } +} diff --git a/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Utilities.java b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Utilities.java new file mode 100644 index 0000000..768f01d --- /dev/null +++ b/extra/java.debugjavac/src/main/java/org/netbeans/modules/java/debugjavac/impl/Utilities.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.java.debugjavac.impl; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import org.netbeans.modules.java.debugjavac.Decompiler.Input; + +/** + * + * @author lahvac + */ +public class Utilities { + public static List augmentCommandLineParameters(Input input) throws IOException { + try { + Class.forName("com.sun.tools.javac.comp.Repair"); + List augmentedParams = new ArrayList<>(input.params); + augmentedParams.add("-XDshouldStopPolicy=GENERATE"); + return augmentedParams; + } catch (ClassNotFoundException ex) { + //OK + return input.params; + } + } + + public static JavaFileObject sourceFileObject(final String code) { + return new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) { + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return code; + } + @Override public boolean isNameCompatible(String simpleName, Kind kind) { + return true; + } + }; + } + + public static DiagnosticListener errorReportingDiagnosticListener(final StringWriter out) { + return new DiagnosticListener() { + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + out.write(diagnostic.getMessage(null)); + out.write("\n"); + } + } + }; + } +} diff --git a/extra/java.debugjavac/src/main/nbm/manifest.mf b/extra/java.debugjavac/src/main/nbm/manifest.mf new file mode 100644 index 0000000..f8a1cfd --- /dev/null +++ b/extra/java.debugjavac/src/main/nbm/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/debugjavac/Bundle.properties + diff --git a/extra/java.debugjavac/src/main/resources/org/netbeans/modules/java/debugjavac/Bundle.properties b/extra/java.debugjavac/src/main/resources/org/netbeans/modules/java/debugjavac/Bundle.properties new file mode 100644 index 0000000..fbf3218 --- /dev/null +++ b/extra/java.debugjavac/src/main/resources/org/netbeans/modules/java/debugjavac/Bundle.properties @@ -0,0 +1,23 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +#Localized module labels. Defaults taken from POM (, , ) if unset. +#OpenIDE-Module-Name= +#OpenIDE-Module-Short-Description= +#OpenIDE-Module-Long-Description= +#OpenIDE-Module-Display-Category= +#Fri Jan 24 07:23:58 CET 2020