diff --git a/teavm-eclipse/teavm-eclipse-plugin/.settings/org.eclipse.jdt.ui.prefs b/teavm-eclipse/teavm-eclipse-plugin/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..6592633f4 --- /dev/null +++ b/teavm-eclipse/teavm-eclipse-plugin/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.ondemandthreshold=90 +org.eclipse.jdt.ui.staticondemandthreshold=3 diff --git a/teavm-eclipse/teavm-eclipse-plugin/plugin.xml b/teavm-eclipse/teavm-eclipse-plugin/plugin.xml index d09cc3412..c4f24e6c7 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/plugin.xml +++ b/teavm-eclipse/teavm-eclipse-plugin/plugin.xml @@ -70,9 +70,15 @@ - + - + + + + + + + diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java index 2540bb92d..bef957d2a 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java @@ -37,8 +37,9 @@ public class TeaVMEclipsePlugin extends AbstractUIPlugin { public static final String ID = "org.teavm.eclipse"; public static final String NATURE_ID = ID + ".nature"; public static final String BUILDER_ID = ID + ".builder"; - public static final String MAIN_METHOD_DIALOG_ID = ID + ".dialogs.mainMethod"; + public static final String CLASS_DIALOG_ID = ID + ".dialogs.classSelection"; public static final String DEPENDENCY_MARKER_ID = ID + ".dependencyMarker"; + public static final String CONFIG_MARKER_ID = ID + ".configMarker"; private static TeaVMEclipsePlugin defaultInstance; private ConcurrentMap settingsMap = new ConcurrentHashMap<>(); diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java index a9c5530db..d52caffe3 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java @@ -1,6 +1,8 @@ package org.teavm.eclipse; import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -13,6 +15,7 @@ import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.teavm.dependency.*; +import org.teavm.model.ClassHolderTransformer; import org.teavm.model.InstructionLocation; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -72,6 +75,7 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { if ((kind == AUTO_BUILD || kind == INCREMENTAL_BUILD) && !shouldBuild(profile)) { return; } + getProject().deleteMarkers(TeaVMEclipsePlugin.CONFIG_MARKER_ID, true, IResource.DEPTH_INFINITE); IStringVariableManager varManager = VariablesPlugin.getDefault().getStringVariableManager(); TeaVMTool tool = new TeaVMTool(); tool.setClassLoader(classLoader); @@ -87,6 +91,9 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { tool.setIncremental(profile.isIncremental()); String cacheDir = profile.getCacheDirectory(); tool.setCacheDirectory(!cacheDir.isEmpty() ? new File(varManager.performStringSubstitution(cacheDir)) : null); + for (ClassHolderTransformer transformer : instantiateTransformers(profile, classLoader)) { + tool.getTransformers().add(transformer); + } tool.setProgressListener(new TeaVMEclipseProgressListener(this, monitor, TICKS_PER_PROFILE)); try { monitor.beginTask("Running TeaVM", 10000); @@ -328,6 +335,48 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { return projects; } + private List instantiateTransformers(TeaVMProfile profile, ClassLoader classLoader) + throws CoreException{ + List transformerInstances = new ArrayList<>(); + for (String transformerName : profile.getTransformers()) { + Class transformerRawType; + try { + transformerRawType = Class.forName(transformerName, true, classLoader); + } catch (ClassNotFoundException e) { + putConfigMarker("Transformer not found: " + transformerName); + continue; + } + if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) { + putConfigMarker("Transformer " + transformerName + " is not a subtype of " + + ClassHolderTransformer.class.getName()); + continue; + } + Class transformerType = transformerRawType.asSubclass( + ClassHolderTransformer.class); + Constructor ctor; + try { + ctor = transformerType.getConstructor(); + } catch (NoSuchMethodException e) { + putConfigMarker("Transformer " + transformerName + " has no default constructor"); + continue; + } + try { + ClassHolderTransformer transformer = ctor.newInstance(); + transformerInstances.add(transformer); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + putConfigMarker("Error instantiating transformer " + transformerName); + continue; + } + } + return transformerInstances; + } + + private void putConfigMarker(String message) throws CoreException { + IMarker marker = getProject().createMarker(TeaVMEclipsePlugin.CONFIG_MARKER_ID); + marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); + marker.setAttribute(IMarker.MESSAGE, message); + } + private void prepareClassPath() throws CoreException { classPath = new URL[0]; sourceContainers = new IContainer[0]; diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/ClassSelectionDialog.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/ClassSelectionDialog.java new file mode 100644 index 000000000..0283d433c --- /dev/null +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/ClassSelectionDialog.java @@ -0,0 +1,161 @@ +package org.teavm.eclipse.ui; + +import static org.teavm.eclipse.TeaVMEclipsePlugin.CLASS_DIALOG_ID; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.core.search.SearchRequestor; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog; +import org.teavm.eclipse.TeaVMEclipsePlugin; +/** + * + * @author Alexey Andreev + */ +public abstract class ClassSelectionDialog extends FilteredItemsSelectionDialog { + private IJavaProject javaProject; + + public ClassSelectionDialog(Shell shell, IJavaProject javaProject) { + super(shell, false); + this.javaProject = javaProject; + LabelProvider labelProvider = new LabelProvider() { + @Override public String getText(Object element) { + return getElementName(element); + } + }; + setListLabelProvider(labelProvider); + setDetailsLabelProvider(labelProvider); + } + + @Override + protected String getInitialPattern() { + return ""; + } + + @Override + protected Control createExtendedContentArea(Composite parent) { + return null; + } + + @Override + protected ItemsFilter createFilter() { + return new ItemsFilter() { + @Override public boolean matchItem(Object item) { + IType type = (IType)item; + return type.getFullyQualifiedName().toLowerCase().contains(getPattern().toLowerCase()); + } + @Override public boolean isConsistentItem(Object item) { + return item instanceof IType; + } + }; + } + + @Override + protected void fillContentProvider(AbstractContentProvider contentProvider, ItemsFilter filter, + IProgressMonitor progressMonitor) throws CoreException { + IType[] mainTypes = findTypes(filter.getPattern(), progressMonitor); + for (IType type : mainTypes) { + contentProvider.add(type, filter); + } + } + + @Override + protected IDialogSettings getDialogSettings() { + IDialogSettings settings = TeaVMEclipsePlugin.getDefault().getDialogSettings(); + IDialogSettings section = settings.getSection(CLASS_DIALOG_ID); + if (section == null) { + section = settings.addNewSection(CLASS_DIALOG_ID); + } + return section; + } + + @Override + public String getElementName(Object element) { + IType type = (IType)element; + return getTypeName(type); + } + + private String getTypeName(IType type) { + if (type == null) { + return null; + } + StringBuilder sb = new StringBuilder(type.getTypeQualifiedName()); + if (type.getPackageFragment() != null) { + sb.append(" in ").append(type.getPackageFragment().getElementName()); + } + return sb.toString(); + } + + @Override + protected Comparator getItemsComparator() { + return new Comparator() { + @Override public int compare(IType o1, IType o2) { + return getTypeName(o1).compareTo(getTypeName(o2)); + } + }; + } + + @Override + protected IStatus validateItem(Object item) { + return Status.OK_STATUS; + } + + private IType[] findTypes(String patternText, IProgressMonitor progressMonitor) { + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { javaProject }, + IJavaSearchScope.SOURCES | IJavaSearchScope.REFERENCED_PROJECTS | + IJavaSearchScope.APPLICATION_LIBRARIES); + SearchPattern pattern = createSearchPattern(patternText); + SearchParticipant[] participants = new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }; + ClassCollector collector = new ClassCollector(); + try { + new SearchEngine().search(pattern, participants, scope, collector, progressMonitor); + } catch (CoreException e) { + logError(e); + return new IType[0]; + } + IType[] foundTypes = collector.getTypes().toArray(new IType[collector.getTypes().size()]); + return foundTypes; + } + + private void logError(Throwable e) { + IStatus status = TeaVMEclipsePlugin.makeError(e); + TeaVMEclipsePlugin.getDefault().getLog().log(status); + ErrorDialog.openError(getShell(), "Error", "Error", status); + } + + private class ClassCollector extends SearchRequestor { + private Set types = new HashSet<>(); + + public Set getTypes() { + return types; + } + + @Override + public void acceptSearchMatch(SearchMatch match) throws CoreException { + IType type = acceptMatch(match); + if (type != null) { + types.add(type); + } + } + } + + protected abstract SearchPattern createSearchPattern(String text); + + protected abstract IType acceptMatch(SearchMatch match) throws CoreException; +} diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/MainClassSelectionDialog.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/MainClassSelectionDialog.java index 4055b8411..5b323b4ac 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/MainClassSelectionDialog.java +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/MainClassSelectionDialog.java @@ -1,146 +1,37 @@ package org.teavm.eclipse.ui; -import static org.teavm.eclipse.TeaVMEclipsePlugin.MAIN_METHOD_DIALOG_ID; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Set; -import org.eclipse.core.runtime.*; -import org.eclipse.jdt.core.*; -import org.eclipse.jdt.core.search.*; -import org.eclipse.jface.dialogs.ErrorDialog; -import org.eclipse.jface.dialogs.IDialogSettings; -import org.eclipse.jface.viewers.LabelProvider; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog; -import org.teavm.eclipse.TeaVMEclipsePlugin; /** * * @author Alexey Andreev */ -public class MainClassSelectionDialog extends FilteredItemsSelectionDialog { - private IJavaProject javaProject; - +public class MainClassSelectionDialog extends ClassSelectionDialog { public MainClassSelectionDialog(Shell shell, IJavaProject javaProject) { - super(shell, false); - this.javaProject = javaProject; + super(shell, javaProject); setTitle("Selecting main class"); - LabelProvider labelProvider = new LabelProvider() { - @Override public String getText(Object element) { - return getElementName(element); - } - }; - setListLabelProvider(labelProvider); - setDetailsLabelProvider(labelProvider); } @Override - protected Control createExtendedContentArea(Composite parent) { + protected SearchPattern createSearchPattern(String text) { + return SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); + } + + @Override + protected IType acceptMatch(SearchMatch match) throws CoreException { + IMethod method = (IMethod)match.getElement(); + if ((method.getFlags() & Flags.AccStatic) != 0) { + return method.getDeclaringType(); + } return null; } - - @Override - protected ItemsFilter createFilter() { - return new ItemsFilter() { - @Override public boolean matchItem(Object item) { - IType type = (IType)item; - return type.getTypeQualifiedName().toLowerCase().contains(getPattern().toLowerCase()); - } - @Override public boolean isConsistentItem(Object item) { - return item instanceof IType; - } - }; - } - - @Override - protected void fillContentProvider(AbstractContentProvider contentProvider, ItemsFilter filter, - IProgressMonitor progressMonitor) throws CoreException { - IType[] mainTypes = findMainTypes(progressMonitor); - for (IType type : mainTypes) { - contentProvider.add(type, filter); - } - } - - @Override - protected IDialogSettings getDialogSettings() { - IDialogSettings settings = TeaVMEclipsePlugin.getDefault().getDialogSettings(); - IDialogSettings section = settings.getSection(MAIN_METHOD_DIALOG_ID); - if (section == null) { - section = settings.addNewSection(MAIN_METHOD_DIALOG_ID); - } - return section; - } - - @Override - public String getElementName(Object element) { - IType type = (IType)element; - return getTypeName(type); - } - - private String getTypeName(IType type) { - if (type == null) { - return null; - } - StringBuilder sb = new StringBuilder(type.getTypeQualifiedName()); - if (type.getPackageFragment() != null) { - sb.append(" in ").append(type.getPackageFragment().getElementName()); - } - return sb.toString(); - } - - @Override - protected Comparator getItemsComparator() { - return new Comparator() { - @Override public int compare(IType o1, IType o2) { - return getTypeName(o1).compareTo(getTypeName(o2)); - } - }; - } - - @Override - protected IStatus validateItem(Object item) { - return Status.OK_STATUS; - } - - private IType[] findMainTypes(IProgressMonitor progressMonitor) { - IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { javaProject }, - IJavaSearchScope.SOURCES | IJavaSearchScope.REFERENCED_PROJECTS | - IJavaSearchScope.APPLICATION_LIBRARIES); - SearchPattern pattern = SearchPattern.createPattern("main(String[]) void", IJavaSearchConstants.METHOD, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); - SearchParticipant[] participants = new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }; - MainClassCollector collector = new MainClassCollector(); - try { - new SearchEngine().search(pattern, participants, scope, collector, progressMonitor); - } catch (CoreException e) { - logError(e); - return new IType[0]; - } - IType[] foundTypes = collector.getTypes().toArray(new IType[collector.getTypes().size()]); - return foundTypes; - } - - private void logError(Throwable e) { - IStatus status = TeaVMEclipsePlugin.makeError(e); - TeaVMEclipsePlugin.getDefault().getLog().log(status); - ErrorDialog.openError(getShell(), "Error", "Error", status); - } - - private static class MainClassCollector extends SearchRequestor { - private Set types = new HashSet<>(); - - public Set getTypes() { - return types; - } - - @Override - public void acceptSearchMatch(SearchMatch match) throws CoreException { - IMethod method = (IMethod)match.getElement(); - if ((method.getFlags() & Flags.AccStatic) != 0) { - types.add(method.getDeclaringType()); - } - } - } } diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/TeaVMProfileDialog.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/TeaVMProfileDialog.java index 73d668a9c..2273ee7ea 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/TeaVMProfileDialog.java +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/TeaVMProfileDialog.java @@ -284,10 +284,20 @@ public class TeaVMProfileDialog extends Dialog { addTransformerButton = new Button(group, SWT.PUSH); addTransformerButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); addTransformerButton.setText("Add..."); + addTransformerButton.addSelectionListener(new SelectionAdapter() { + @Override public void widgetSelected(SelectionEvent e) { + addTransformer(); + } + }); removeTransformerButton = new Button(group, SWT.PUSH); removeTransformerButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); removeTransformerButton.setText("Remove"); + removeTransformerButton.addSelectionListener(new SelectionAdapter() { + @Override public void widgetSelected(SelectionEvent e) { + removeTransformer(); + } + }); } private Group createGroup(Composite parent, String title, int columns, boolean fillVert) { @@ -480,6 +490,33 @@ public class TeaVMProfileDialog extends Dialog { return filePath; } + private void addTransformer() { + TransformerClassSelectionDialog selectionDialog = new TransformerClassSelectionDialog(getParentShell(), + javaProject); + if (selectionDialog.open() == MainClassSelectionDialog.OK) { + Object[] result = selectionDialog.getResult(); + if (result.length > 0) { + IType type = (IType)result[0]; + List existingTypes = Arrays.asList(transormersList.getItems()); + if (!existingTypes.contains(type.getFullyQualifiedName())) { + transormersList.add(type.getFullyQualifiedName()); + } + } + } + } + + private void removeTransformer() { + if (transormersList.getSelectionCount() != 1) { + return; + } + boolean confirmed = MessageDialog.openConfirm(getShell(), "Removal confirmation", + "Are you sure to delete the " + transormersList.getSelection()[0] + " transformer?"); + if (!confirmed) { + return; + } + transormersList.remove(transormersList.getSelectionIndex()); + } + @Override protected void okPressed() { if (save()) { @@ -518,6 +555,7 @@ public class TeaVMProfileDialog extends Dialog { propertyList.add(property); } updateCacheFieldsEnabled(); + transormersList.setItems(profile.getTransformers()); } private boolean save() { @@ -543,6 +581,7 @@ public class TeaVMProfileDialog extends Dialog { properties.setProperty(property.key, property.value); } profile.setProperties(properties); + profile.setTransformers(transormersList.getItems()); return true; } } diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/TransformerClassSelectionDialog.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/TransformerClassSelectionDialog.java new file mode 100644 index 000000000..a52e963cf --- /dev/null +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/ui/TransformerClassSelectionDialog.java @@ -0,0 +1,32 @@ +package org.teavm.eclipse.ui; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.swt.widgets.Shell; +import org.teavm.model.ClassHolderTransformer; + +/** + * + * @author Alexey Andreev + */ +public class TransformerClassSelectionDialog extends ClassSelectionDialog { + public TransformerClassSelectionDialog(Shell shell, IJavaProject javaProject) { + super(shell, javaProject); + setTitle("Selecting TeaVM transformer"); + } + + @Override + protected SearchPattern createSearchPattern(String text) { + return SearchPattern.createPattern(ClassHolderTransformer.class.getName(), IJavaSearchConstants.CLASS, + IJavaSearchConstants.IMPLEMENTORS, SearchPattern.R_EXACT_MATCH); + } + + @Override + protected IType acceptMatch(SearchMatch match) throws CoreException { + return (IType)match.getElement(); + } +}