From 915bb96ff9a96523dd0e9aaf8eca63af57c373e5 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Wed, 28 Mar 2012 21:20:39 +0200 Subject: [PATCH] get rid of PseudoClassLoader; InstrumentationClassFinder used instead. Also InstrumenterClassWriter using this finder is available as utility for all ASM-based instrumentation tasks --- .idea/modules.xml | 1 + build/scripts/layouts.gant | 4 +- community-main.iml | 2 +- .../forms-compiler/forms-compiler.iml | 1 + .../uiDesigner/compiler/AsmCodeGenerator.java | 152 +++-- .../compiler/FontPropertyCodeGenerator.java | 3 +- .../compiler/PropertyCodeGenerator.java | 9 +- .../compiler/StringPropertyCodeGenerator.java | 28 +- .../intellij/uiDesigner/compiler/Utils.java | 545 +++++++++--------- java/compiler/impl/compiler-impl.iml | 2 +- .../instrumentation-util.iml} | 0 .../InstrumentationClassFinder.java | 231 ++++++-- .../InstrumenterClassWriter.java | 43 ++ .../NotNullVerifyingInstrumenter.java | 0 java/compiler/javac2/javac2.iml | 2 +- .../src/com/intellij/ant/AntClassWriter.java | 54 -- .../com/intellij/ant/InstrumentationUtil.java | 32 +- .../javac2/src/com/intellij/ant/Javac2.java | 52 +- .../com/intellij/ant/PseudoClassLoader.java | 275 --------- java/java-tests/java-tests.iml | 2 +- jps/jps-builders/jps-builders.iml | 2 +- .../jps/incremental/java/JavaBuilder.java | 105 +--- jps/jps.iml | 1 + .../org/jetbrains/jps/ModuleBuildState.groovy | 4 +- .../jps/builders/StandardBuilders.groovy | 16 +- .../javacApi/Java16ApiCompiler.groovy | 23 +- .../javacApi/OptimizedFileManager.java | 26 +- .../uiDesigner/actions/PreviewFormAction.java | 195 ++++--- .../make/Form2ByteCodeCompiler.java | 149 ++--- .../make/PreviewNestedFormLoader.java | 9 +- .../uiDesigner/core/AsmCodeGeneratorTest.java | 132 ++++- 31 files changed, 1040 insertions(+), 1060 deletions(-) rename java/compiler/{notNull/notNull.iml => instrumentation-util/instrumentation-util.iml} (100%) rename {jps/jps-builders/src/org/jetbrains/jps/incremental/java => java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation}/InstrumentationClassFinder.java (71%) create mode 100644 java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumenterClassWriter.java rename java/compiler/{notNull => instrumentation-util}/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java (100%) delete mode 100644 java/compiler/javac2/src/com/intellij/ant/AntClassWriter.java delete mode 100644 java/compiler/javac2/src/com/intellij/ant/PseudoClassLoader.java diff --git a/.idea/modules.xml b/.idea/modules.xml index e6de17c97dfd..18bb3a5c3d05 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -51,6 +51,7 @@ + diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index e78e38697911..6381e2e9eab8 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -77,7 +77,7 @@ def layoutFull(String home, String targetDirectory, String patchedDescriptorDir //noinspection GroovyAssignabilityCheck List implementationModules = [platformImplementationModules(), - "notNull", + "instrumentation-util", "platform-main", "java-psi-impl", "java-impl", @@ -170,7 +170,7 @@ def layoutFull(String home, String targetDirectory, String patchedDescriptorDir module("javac2") module("forms-compiler") module("forms_rt") - module("notNull") + module("instrumentation-util") } jar("jps-server.jar") { diff --git a/community-main.iml b/community-main.iml index a52ba14d3c83..0dbcfb09ce10 100644 --- a/community-main.iml +++ b/community-main.iml @@ -31,7 +31,7 @@ - + diff --git a/java/compiler/forms-compiler/forms-compiler.iml b/java/compiler/forms-compiler/forms-compiler.iml index ce591cf3173e..39ba1446f119 100644 --- a/java/compiler/forms-compiler/forms-compiler.iml +++ b/java/compiler/forms-compiler/forms-compiler.iml @@ -13,6 +13,7 @@ + diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java index b7a344dcf74e..b838594d8f6f 100644 --- a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java +++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/AsmCodeGenerator.java @@ -15,6 +15,7 @@ */ package com.intellij.uiDesigner.compiler; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; import com.intellij.uiDesigner.UIFormXmlConstants; import com.intellij.uiDesigner.lw.*; import com.intellij.uiDesigner.shared.BorderType; @@ -28,7 +29,6 @@ import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import java.io.*; -import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; @@ -40,7 +40,7 @@ import java.util.Map; */ public class AsmCodeGenerator { private final LwRootContainer myRootContainer; - private final ClassLoader myLoader; + private final InstrumentationClassFinder myFinder; private final ArrayList myErrors; private final ArrayList myWarnings; @@ -96,20 +96,20 @@ public class AsmCodeGenerator { } public AsmCodeGenerator(LwRootContainer rootContainer, - ClassLoader loader, + InstrumentationClassFinder finder, NestedFormLoader formLoader, final boolean ignoreCustomCreation, final ClassWriter classWriter) { myFormLoader = formLoader; myIgnoreCustomCreation = ignoreCustomCreation; - if (loader == null){ + if (finder == null){ throw new IllegalArgumentException("loader cannot be null"); } if (rootContainer == null){ throw new IllegalArgumentException("rootContainer cannot be null"); } myRootContainer = rootContainer; - myLoader = loader; + myFinder = finder; myErrors = new ArrayList(); myWarnings = new ArrayList(); @@ -210,9 +210,9 @@ public class AsmCodeGenerator { codeGen.generatePushValue(generator, value); } - static Class getComponentClass(String className, final ClassLoader classLoader) throws CodeGenerationException { + static InstrumentationClassFinder.PseudoClass getComponentClass(String className, final InstrumentationClassFinder finder) throws CodeGenerationException { try { - return Class.forName(className, false, classLoader); + return finder.loadClass(className); } catch (ClassNotFoundException e) { throw new CodeGenerationException(null, "Class not found: " + className); @@ -220,6 +220,9 @@ public class AsmCodeGenerator { catch(UnsupportedClassVersionError e) { throw new CodeGenerationException(null, "Unsupported class version error: " + className); } + catch (IOException e) { + throw new CodeGenerationException(null, e.getMessage(), e); + } } public static Type typeFromClassName(final String className) { @@ -252,7 +255,7 @@ public class AsmCodeGenerator { for (Iterator iterator = myPropertyCodeGenerators.values().iterator(); iterator.hasNext();) { PropertyCodeGenerator propertyCodeGenerator = (PropertyCodeGenerator)iterator.next(); - propertyCodeGenerator.generateClassStart(this, name, myLoader); + propertyCodeGenerator.generateClassStart(this, name, myFinder); } } @@ -394,28 +397,30 @@ public class AsmCodeGenerator { myIdToLocalMap.put(lwComponent.getId(), new Integer(componentLocal)); - Class componentClass = getComponentClass(className, myLoader); + InstrumentationClassFinder.PseudoClass componentClass = getComponentClass(className, myFinder); validateFieldBinding(lwComponent, componentClass); if (myIgnoreCustomCreation) { - boolean creatable = true; - if ((componentClass.getModifiers() & (Modifier.PRIVATE | Modifier.ABSTRACT)) != 0) { - creatable = false; - } - else { - try { - final Constructor constructor = componentClass.getConstructor(new Class[0]); - if ((constructor.getModifiers() & Modifier.PUBLIC) == 0) { + try { + boolean creatable = true; + if ((componentClass.getModifiers() & (Modifier.PRIVATE | Modifier.ABSTRACT)) != 0) { + creatable = false; + } + else { + if (!componentClass.hasDefaultPublicConstructor()) { creatable = false; } } - catch(NoSuchMethodException ex) { - creatable = false; + if (!creatable) { + componentClass = Utils.suggestReplacementClass(componentClass); + componentType = Type.getType(componentClass.getDescriptor()); } } - if (!creatable) { - componentClass = Utils.suggestReplacementClass(componentClass); - componentType = Type.getType(componentClass); + catch (ClassNotFoundException e) { + throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e); + } + catch (IOException e) { + throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e); } } @@ -468,12 +473,11 @@ public class AsmCodeGenerator { } } - private int getNestedFormComponent(GeneratorAdapter generator, Class componentClass, int formLocal) throws CodeGenerationException { + private int getNestedFormComponent(GeneratorAdapter generator, InstrumentationClassFinder.PseudoClass componentClass, int formLocal) throws CodeGenerationException { final Type componentType = Type.getType(JComponent.class); int componentLocal = generator.newLocal(componentType); generator.loadLocal(formLocal); - generator.invokeVirtual(Type.getType(componentClass), - new Method(GET_ROOT_COMPONENT_METHOD_NAME, componentType, new Type[0])); + generator.invokeVirtual(Type.getType(componentClass.getDescriptor()), new Method(GET_ROOT_COMPONENT_METHOD_NAME, componentType, new Type[0])); generator.storeLocal(componentLocal); return componentLocal; } @@ -502,7 +506,7 @@ public class AsmCodeGenerator { } private void generateComponentProperties(final LwComponent lwComponent, - final Class componentClass, + final InstrumentationClassFinder.PseudoClass componentClass, final GeneratorAdapter generator, final int componentLocal) throws CodeGenerationException { // introspected properties @@ -543,7 +547,12 @@ public class AsmCodeGenerator { else { setterClass = Class.forName(propertyClass); } - componentClass.getMethod(property.getWriteMethodName(), new Class[] { setterClass } ); + //componentClass.getMethod(property.getWriteMethodName(), new Class[] { setterClass } ); + final String descriptor = "(L"+setterClass.getName().replace('.', '/') + ";)V"; + final InstrumentationClassFinder.PseudoMethod setter = componentClass.findMethod(property.getWriteMethodName(), descriptor); + if (setter == null) { + continue; + } } catch (Exception e) { continue; @@ -551,9 +560,16 @@ public class AsmCodeGenerator { } final PropertyCodeGenerator propGen = (PropertyCodeGenerator) myPropertyCodeGenerators.get(propertyClass); - if (propGen != null && propGen.generateCustomSetValue(lwComponent, componentClass, property, - generator, componentLocal, myClassName)) { - continue; + try { + if (propGen != null && propGen.generateCustomSetValue(lwComponent, componentClass, property, generator, componentLocal, myClassName)) { + continue; + } + } + catch (IOException e) { + throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e); + } + catch (ClassNotFoundException e) { + throw new CodeGenerationException(lwComponent.getId(), e.getMessage(), e); } generator.loadLocal(componentLocal); @@ -602,7 +618,7 @@ public class AsmCodeGenerator { Type declaringType = (property.getDeclaringClassName() != null) ? typeFromClassName(property.getDeclaringClassName()) - : Type.getType(componentClass); + : Type.getType(componentClass.getDescriptor()); generator.invokeVirtual(declaringType, new Method(property.getWriteMethodName(), Type.VOID_TYPE, new Type[] { setterArgType } )); } @@ -611,7 +627,7 @@ public class AsmCodeGenerator { } private void generateClientProperties(final LwComponent lwComponent, - final Class componentClass, + final InstrumentationClassFinder.PseudoClass componentClass, final GeneratorAdapter generator, final int componentLocal) throws CodeGenerationException { HashMap props = lwComponent.getDelegeeClientProperties(); @@ -652,7 +668,7 @@ public class AsmCodeGenerator { } } - Type componentType = Type.getType(componentClass); + Type componentType = Type.getType(componentClass.getDescriptor()); Type objectType = Type.getType(Object.class); generator.invokeVirtual(componentType, new Method("putClientProperty", Type.VOID_TYPE, new Type[] { objectType, objectType } )); @@ -664,7 +680,7 @@ public class AsmCodeGenerator { if (component instanceof LwNestedForm) return; int componentLocal = ((Integer) myIdToLocalMap.get(component.getId())).intValue(); final LayoutCodeGenerator layoutCodeGenerator = getComponentCodeGenerator(component.getParent()); - Class componentClass = getComponentClass(layoutCodeGenerator.mapComponentClass(component.getComponentClassName()), myLoader); + InstrumentationClassFinder.PseudoClass componentClass = getComponentClass(layoutCodeGenerator.mapComponentClass(component.getComponentClassName()), myFinder); final LwIntrospectedProperty[] introspectedProperties = component.getAssignedIntrospectedProperties(); for (int i = 0; i < introspectedProperties.length; i++) { @@ -681,7 +697,7 @@ public class AsmCodeGenerator { generator.loadLocal(targetLocal); Type declaringType = (property.getDeclaringClassName() != null) ? typeFromClassName(property.getDeclaringClassName()) - : Type.getType(componentClass); + : Type.getType(componentClass.getDescriptor()); generator.invokeVirtual(declaringType, new Method(property.getWriteMethodName(), Type.VOID_TYPE, new Type[] { typeFromClassName(property.getPropertyClassName()) } )); @@ -702,33 +718,45 @@ public class AsmCodeGenerator { private void generateButtonGroups(final LwRootContainer rootContainer, final GeneratorAdapter generator) throws CodeGenerationException { IButtonGroup[] groups = rootContainer.getButtonGroups(); if (groups.length > 0) { - int groupLocal = generator.newLocal(ourButtonGroupType); - for(int groupIndex=0; groupIndex 0) { - generator.newInstance(ourButtonGroupType); - generator.dup(); - generator.invokeConstructor(ourButtonGroupType, Method.getMethod("void ()")); - generator.storeLocal(groupLocal); + if (ids.length > 0) { + generator.newInstance(ourButtonGroupType); + generator.dup(); + generator.invokeConstructor(ourButtonGroupType, Method.getMethod("void ()")); + generator.storeLocal(groupLocal); - if (groups [groupIndex].isBound() && !myIgnoreCustomCreation) { - validateFieldClass(groups [groupIndex].getName(), ButtonGroup.class, null); - generator.loadThis(); - generator.loadLocal(groupLocal); - generator.putField(getMainClassType(), groups [groupIndex].getName(), ourButtonGroupType); - } - - for(int i = 0; i= 0) { generator.loadLocal(componentLocal); generator.push(textWithMnemonic.myText); - generator.invokeVirtual(Type.getType(componentClass), + generator.invokeVirtual(Type.getType(componentClass.getDescriptor()), new Method(property.getWriteMethodName(), Type.VOID_TYPE, new Type[] { Type.getType(String.class) } )); String setMnemonicMethodName; - if (AbstractButton.class.isAssignableFrom(componentClass)) { + if (abstractButtonClass.isAssignableFrom(componentClass)) { setMnemonicMethodName = "setMnemonic"; } else { @@ -89,14 +93,14 @@ public class StringPropertyCodeGenerator extends PropertyCodeGenerator implement generator.loadLocal(componentLocal); generator.push(textWithMnemonic.getMnemonicChar()); - generator.invokeVirtual(Type.getType(componentClass), + generator.invokeVirtual(Type.getType(componentClass.getDescriptor()), new Method(setMnemonicMethodName, Type.VOID_TYPE, new Type[] { Type.CHAR_TYPE } )); if (myHaveSetDisplayedMnemonicIndex) { generator.loadLocal(componentLocal); generator.push(textWithMnemonic.myMnemonicIndex); - generator.invokeVirtual(Type.getType(componentClass), + generator.invokeVirtual(Type.getType(componentClass.getDescriptor()), new Method("setDisplayedMnemonicIndex", Type.VOID_TYPE, new Type[] { Type.INT_TYPE } )); } @@ -105,7 +109,7 @@ public class StringPropertyCodeGenerator extends PropertyCodeGenerator implement } else { Method method; - if (AbstractButton.class.isAssignableFrom(componentClass)) { + if (abstractButtonClass.isAssignableFrom(componentClass)) { myClassesRequiringLoadButtonText.add(formClassName); method = myLoadButtonTextMethod; } diff --git a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/Utils.java b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/Utils.java index 4b28e2d04628..79fb947cacb5 100644 --- a/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/Utils.java +++ b/java/compiler/forms-compiler/src/com/intellij/uiDesigner/compiler/Utils.java @@ -15,6 +15,7 @@ */ package com.intellij.uiDesigner.compiler; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; import com.intellij.uiDesigner.core.GridConstraints; import com.intellij.uiDesigner.lw.*; import org.jdom.Document; @@ -28,6 +29,7 @@ import javax.swing.*; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.awt.*; +import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.lang.reflect.Constructor; @@ -43,306 +45,323 @@ import java.util.Set; * NOTE: the class must be compilable with JDK 1.3, so any methods and filds introduced in 1.4 or later must not be used */ public final class Utils { - public static final String FORM_NAMESPACE = "http://www.intellij.com/uidesigner/form/"; - private static final SAXParser SAX_PARSER = createParser(); + public static final String FORM_NAMESPACE = "http://www.intellij.com/uidesigner/form/"; + private static final SAXParser SAX_PARSER = createParser(); - private Utils() { + private Utils() { + } + + private static SAXParser createParser() { + try { + return SAXParserFactory.newInstance().newSAXParser(); + } + catch (Exception e) { + return null; + } + } + + /** + * @param provider if null, no classes loaded and no properties read + */ + public static LwRootContainer getRootContainer(final String formFileContent, final PropertiesProvider provider) throws Exception { + if (formFileContent.indexOf(FORM_NAMESPACE) == -1) { + throw new AlienFormFileException(); } - private static SAXParser createParser() { - try { - return SAXParserFactory.newInstance().newSAXParser(); - } - catch (Exception e) { - return null; + final Document document = new SAXBuilder().build(new StringReader(formFileContent), "UTF-8"); + + return getRootContainerFromDocument(document, provider); + } + + /** + * Get root from the url + * + * @param formFile the document URL + * @param provider the provider + * @return the root container + * @throws Exception if there is a problem with parsing DOM + */ + public static LwRootContainer getRootContainer(final URL formFile, final PropertiesProvider provider) throws Exception { + final Document document = new SAXBuilder().build(formFile); + return getRootContainerFromDocument(document, provider); + } + + + /** + * Get root from the document + * + * @param document the parsed document + * @param provider the provider + * @return the root container + * @throws Exception if there is a problem with parsing DOM + */ + private static LwRootContainer getRootContainerFromDocument(Document document, PropertiesProvider provider) throws Exception { + final LwRootContainer root = new LwRootContainer(); + root.read(document.getRootElement(), provider); + return root; + } + + public static LwRootContainer getRootContainer(final InputStream stream, final PropertiesProvider provider) throws Exception { + final Document document = new SAXBuilder().build(stream, "UTF-8"); + + return getRootContainerFromDocument(document, provider); + } + + public synchronized static String getBoundClassName(final String formFileContent) throws Exception { + if (formFileContent.indexOf(FORM_NAMESPACE) == -1) { + throw new AlienFormFileException(); + } + + final String[] className = new String[]{null}; + try { + SAX_PARSER.parse(new InputSource(new StringReader(formFileContent)), new DefaultHandler() { + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if ("form".equals(qName)) { + className[0] = attributes.getValue("", "bind-to-class"); + throw new SAXException("stop parsing"); + } } + }); + } + catch (Exception e) { + // Do nothing. } - /** - * @param provider if null, no classes loaded and no properties read - */ - public static LwRootContainer getRootContainer(final String formFileContent, final PropertiesProvider provider) throws Exception { - if (formFileContent.indexOf(FORM_NAMESPACE) == -1) { - throw new AlienFormFileException(); - } + return className[0]; + } - final Document document = new SAXBuilder().build(new StringReader(formFileContent), "UTF-8"); - - return getRootContainerFromDocument(document, provider); - } - - /** - * Get root from the url - * - * @param formFile the document URL - * @param provider the provider - * @return the root container - * @throws Exception if there is a problem with parsing DOM - */ - public static LwRootContainer getRootContainer(final URL formFile, final PropertiesProvider provider) throws Exception { - final Document document = new SAXBuilder().build(formFile); - return getRootContainerFromDocument(document, provider); - } - - - /** - * Get root from the document - * - * @param document the parsed document - * @param provider the provider - * @return the root container - * @throws Exception if there is a problem with parsing DOM - */ - private static LwRootContainer getRootContainerFromDocument(Document document, PropertiesProvider provider) throws Exception { - final LwRootContainer root = new LwRootContainer(); - root.read(document.getRootElement(), provider); - return root; - } - - public static LwRootContainer getRootContainer(final InputStream stream, final PropertiesProvider provider) throws Exception { - final Document document = new SAXBuilder().build(stream, "UTF-8"); - - return getRootContainerFromDocument(document, provider); - } - - public synchronized static String getBoundClassName(final String formFileContent) throws Exception { - if (formFileContent.indexOf(FORM_NAMESPACE) == -1) { - throw new AlienFormFileException(); - } - - final String[] className = new String[]{null}; - try { - SAX_PARSER.parse(new InputSource(new StringReader(formFileContent)), new DefaultHandler() { - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - if ("form".equals(qName)) { - className[0] = attributes.getValue("", "bind-to-class"); - throw new SAXException("stop parsing"); - } - } - }); - } - catch (Exception e) { - // Do nothing. - } - - return className[0]; - } - - /** - * Validates that specified class represents {@link javax.swing.JComponent} with - * empty constructor. - * - * @return descriptive human readable error message or null if - * no errors were detected. - */ + /** + * Validates that specified class represents {@link javax.swing.JComponent} with + * empty constructor. + * + * @return descriptive human readable error message or null if + * no errors were detected. + */ public static String validateJComponentClass(final ClassLoader loader, final String className, final boolean validateConstructor) { if (loader == null) { - throw new IllegalArgumentException("loader cannot be null"); - } - if (className == null) { - throw new IllegalArgumentException("className cannot be null"); - } + throw new IllegalArgumentException("loader cannot be null"); + } + if (className == null) { + throw new IllegalArgumentException("className cannot be null"); + } - // These classes are not visible for passed class loader! - if ("com.intellij.uiDesigner.HSpacer".equals(className) || "com.intellij.uiDesigner.VSpacer".equals(className)) { - return null; - } + // These classes are not visible for passed class loader! + if ("com.intellij.uiDesigner.HSpacer".equals(className) || "com.intellij.uiDesigner.VSpacer".equals(className)) { + return null; + } final Class aClass; - try { + try { aClass = Class.forName(className, false, loader); - } - catch (final ClassNotFoundException exc) { - return "Class \"" + className + "\"not found"; - } - catch (NoClassDefFoundError exc) { - return "Cannot load class " + className + ": " + exc.getMessage(); - } - catch (ExceptionInInitializerError exc) { - return "Cannot initialize class " + className + ": " + exc.getMessage(); - } - catch (UnsupportedClassVersionError exc) { - return "Unsupported class version error: " + className; - } + } + catch (final ClassNotFoundException exc) { + return "Class \"" + className + "\"not found"; + } + catch (NoClassDefFoundError exc) { + return "Cannot load class " + className + ": " + exc.getMessage(); + } + catch (ExceptionInInitializerError exc) { + return "Cannot initialize class " + className + ": " + exc.getMessage(); + } + catch (UnsupportedClassVersionError exc) { + return "Unsupported class version error: " + className; + } - if (validateConstructor) { + if (validateConstructor) { try { final Constructor constructor = aClass.getConstructor(new Class[0]); if ((constructor.getModifiers() & Modifier.PUBLIC) == 0) { - return "Class \"" + className + "\" does not have default public constructor"; - } - } + return "Class \"" + className + "\" does not have default public constructor"; + } + } catch (final Exception exc) { return "Class \"" + className + "\" does not have default constructor"; } } - // Check that JComponent is accessible via the loader + // Check that JComponent is accessible via the loader if (!JComponent.class.isAssignableFrom(aClass)) { - return "Class \"" + className + "\" is not an instance of javax.swing.JComponent"; - } + return "Class \"" + className + "\" is not an instance of javax.swing.JComponent"; + } - return null; + return null; + } + + public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader) + throws CodeGenerationException, RecursiveFormNestingException { + validateNestedFormLoop(formName, nestedFormLoader, null); + } + + public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader, final String targetForm) + throws CodeGenerationException, RecursiveFormNestingException { + HashSet usedFormNames = new HashSet(); + if (targetForm != null) { + usedFormNames.add(targetForm); } + validateNestedFormLoop(usedFormNames, formName, nestedFormLoader); + } - public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader) - throws CodeGenerationException, RecursiveFormNestingException { - validateNestedFormLoop(formName, nestedFormLoader, null); + private static void validateNestedFormLoop(final Set usedFormNames, final String formName, final NestedFormLoader nestedFormLoader) + throws CodeGenerationException, RecursiveFormNestingException { + if (usedFormNames.contains(formName)) { + throw new RecursiveFormNestingException(); } - - public static void validateNestedFormLoop(final String formName, final NestedFormLoader nestedFormLoader, final String targetForm) - throws CodeGenerationException, RecursiveFormNestingException { - HashSet usedFormNames = new HashSet(); - if (targetForm != null) { - usedFormNames.add(targetForm); - } - validateNestedFormLoop(usedFormNames, formName, nestedFormLoader); + usedFormNames.add(formName); + final LwRootContainer rootContainer; + try { + rootContainer = nestedFormLoader.loadForm(formName); } - - private static void validateNestedFormLoop(final Set usedFormNames, final String formName, final NestedFormLoader nestedFormLoader) - throws CodeGenerationException, RecursiveFormNestingException { - if (usedFormNames.contains(formName)) { - throw new RecursiveFormNestingException(); - } - usedFormNames.add(formName); - final LwRootContainer rootContainer; - try { - rootContainer = nestedFormLoader.loadForm(formName); - } - catch (Exception e) { - throw new CodeGenerationException(null, "Error loading nested form: " + e.getMessage(), e); - } - final Set thisFormNestedForms = new HashSet(); - final CodeGenerationException[] validateExceptions = new CodeGenerationException[1]; - final RecursiveFormNestingException[] recursiveNestingExceptions = new RecursiveFormNestingException[1]; - rootContainer.accept(new ComponentVisitor() { - public boolean visit(final IComponent component) { - if (component instanceof LwNestedForm) { - LwNestedForm nestedForm = (LwNestedForm)component; - if (!thisFormNestedForms.contains(nestedForm.getFormFileName())) { - thisFormNestedForms.add(nestedForm.getFormFileName()); - try { - validateNestedFormLoop(usedFormNames, nestedForm.getFormFileName(), nestedFormLoader); - } - catch (RecursiveFormNestingException e) { - recursiveNestingExceptions[0] = e; - return false; - } - catch (CodeGenerationException e) { - validateExceptions[0] = e; - return false; - } - } - } - return true; - } - }); - if (recursiveNestingExceptions[0] != null) { - throw recursiveNestingExceptions[0]; - } - if (validateExceptions[0] != null) { - throw validateExceptions[0]; - } + catch (Exception e) { + throw new CodeGenerationException(null, "Error loading nested form: " + e.getMessage(), e); } - - public static String findNotEmptyPanelWithXYLayout(final IComponent component) { - if (!(component instanceof IContainer)) { - return null; - } - final IContainer container = (IContainer)component; - if (container.getComponentCount() == 0) { - return null; - } - if (container.isXY()) { - return container.getId(); - } - for (int i = 0; i < container.getComponentCount(); i++) { - String id = findNotEmptyPanelWithXYLayout(container.getComponent(i)); - if (id != null) { - return id; - } - } - return null; - } - - public static int getHGap(LayoutManager layout) { - if (layout instanceof BorderLayout) { - return ((BorderLayout)layout).getHgap(); - } - if (layout instanceof CardLayout) { - return ((CardLayout)layout).getHgap(); - } - return 0; - } - - public static int getVGap(LayoutManager layout) { - if (layout instanceof BorderLayout) { - return ((BorderLayout)layout).getVgap(); - } - if (layout instanceof CardLayout) { - return ((CardLayout)layout).getVgap(); - } - return 0; - } - - public static int getCustomCreateComponentCount(final IContainer container) { - final int[] result = new int[1]; - result[0] = 0; - container.accept(new ComponentVisitor() { - public boolean visit(IComponent c) { - if (c.isCustomCreate()) { - result[0]++; - } - return true; - } - }); - return result[0]; - } - - public static Class suggestReplacementClass(Class componentClass) { - while (true) { - componentClass = componentClass.getSuperclass(); - if (componentClass.equals(JComponent.class)) { - return JPanel.class; - } - if ((componentClass.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE)) != 0) { - continue; - } + final Set thisFormNestedForms = new HashSet(); + final CodeGenerationException[] validateExceptions = new CodeGenerationException[1]; + final RecursiveFormNestingException[] recursiveNestingExceptions = new RecursiveFormNestingException[1]; + rootContainer.accept(new ComponentVisitor() { + public boolean visit(final IComponent component) { + if (component instanceof LwNestedForm) { + LwNestedForm nestedForm = (LwNestedForm)component; + if (!thisFormNestedForms.contains(nestedForm.getFormFileName())) { + thisFormNestedForms.add(nestedForm.getFormFileName()); try { - componentClass.getConstructor(new Class[]{}); + validateNestedFormLoop(usedFormNames, nestedForm.getFormFileName(), nestedFormLoader); } - catch (NoSuchMethodException ex) { - continue; + catch (RecursiveFormNestingException e) { + recursiveNestingExceptions[0] = e; + return false; } - return componentClass; + catch (CodeGenerationException e) { + validateExceptions[0] = e; + return false; + } + } } + return true; + } + }); + if (recursiveNestingExceptions[0] != null) { + throw recursiveNestingExceptions[0]; } + if (validateExceptions[0] != null) { + throw validateExceptions[0]; + } + } - public static int alignFromConstraints(final GridConstraints gc, final boolean horizontal) { - int anchor = gc.getAnchor(); - int fill = gc.getFill(); - int leftMask = horizontal ? GridConstraints.ANCHOR_WEST : GridConstraints.ANCHOR_NORTH; - int rightMask = horizontal ? GridConstraints.ANCHOR_EAST : GridConstraints.ANCHOR_SOUTH; - int fillMask = horizontal ? GridConstraints.FILL_HORIZONTAL : GridConstraints.FILL_VERTICAL; - if ((fill & fillMask) != 0) return GridConstraints.ALIGN_FILL; - if ((anchor & rightMask) != 0) return GridConstraints.ALIGN_RIGHT; - if ((anchor & leftMask) != 0) return GridConstraints.ALIGN_LEFT; - return GridConstraints.ALIGN_CENTER; + public static String findNotEmptyPanelWithXYLayout(final IComponent component) { + if (!(component instanceof IContainer)) { + return null; } + final IContainer container = (IContainer)component; + if (container.getComponentCount() == 0) { + return null; + } + if (container.isXY()) { + return container.getId(); + } + for (int i = 0; i < container.getComponentCount(); i++) { + String id = findNotEmptyPanelWithXYLayout(container.getComponent(i)); + if (id != null) { + return id; + } + } + return null; + } - public static boolean isBoundField(IComponent component, String fieldName) { - if (fieldName.equals(component.getBinding())) { - return true; - } - if (component instanceof IContainer) { - IContainer container = (IContainer)component; - for (int i = 0; i < container.getComponentCount(); i++) { - if (isBoundField(container.getComponent(i), fieldName)) { - return true; - } - } - } - return false; + public static int getHGap(LayoutManager layout) { + if (layout instanceof BorderLayout) { + return ((BorderLayout)layout).getHgap(); } + if (layout instanceof CardLayout) { + return ((CardLayout)layout).getHgap(); + } + return 0; + } + + public static int getVGap(LayoutManager layout) { + if (layout instanceof BorderLayout) { + return ((BorderLayout)layout).getVgap(); + } + if (layout instanceof CardLayout) { + return ((CardLayout)layout).getVgap(); + } + return 0; + } + + public static int getCustomCreateComponentCount(final IContainer container) { + final int[] result = new int[1]; + result[0] = 0; + container.accept(new ComponentVisitor() { + public boolean visit(IComponent c) { + if (c.isCustomCreate()) { + result[0]++; + } + return true; + } + }); + return result[0]; + } + + public static Class suggestReplacementClass(Class componentClass) { + while (true) { + componentClass = componentClass.getSuperclass(); + if (componentClass.equals(JComponent.class)) { + return JPanel.class; + } + if ((componentClass.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE)) != 0) { + continue; + } + try { + componentClass.getConstructor(new Class[]{}); + } + catch (NoSuchMethodException ex) { + continue; + } + return componentClass; + } + } + + public static InstrumentationClassFinder.PseudoClass suggestReplacementClass(InstrumentationClassFinder.PseudoClass componentClass) throws ClassNotFoundException, IOException { + final InstrumentationClassFinder.PseudoClass jComponentClass = componentClass.getFinder().loadClass(JComponent.class.getName()); + while (true) { + componentClass = componentClass.getSuperClass(); + if (componentClass.equals(jComponentClass)) { + return componentClass.getFinder().loadClass(JPanel.class.getName()); + } + if ((componentClass.getModifiers() & (Modifier.ABSTRACT | Modifier.PRIVATE)) != 0) { + continue; + } + if (!componentClass.hasDefaultPublicConstructor()) { + continue; + } + return componentClass; + } + } + + public static int alignFromConstraints(final GridConstraints gc, final boolean horizontal) { + int anchor = gc.getAnchor(); + int fill = gc.getFill(); + int leftMask = horizontal ? GridConstraints.ANCHOR_WEST : GridConstraints.ANCHOR_NORTH; + int rightMask = horizontal ? GridConstraints.ANCHOR_EAST : GridConstraints.ANCHOR_SOUTH; + int fillMask = horizontal ? GridConstraints.FILL_HORIZONTAL : GridConstraints.FILL_VERTICAL; + if ((fill & fillMask) != 0) return GridConstraints.ALIGN_FILL; + if ((anchor & rightMask) != 0) return GridConstraints.ALIGN_RIGHT; + if ((anchor & leftMask) != 0) return GridConstraints.ALIGN_LEFT; + return GridConstraints.ALIGN_CENTER; + } + + public static boolean isBoundField(IComponent component, String fieldName) { + if (fieldName.equals(component.getBinding())) { + return true; + } + if (component instanceof IContainer) { + IContainer container = (IContainer)component; + for (int i = 0; i < container.getComponentCount(); i++) { + if (isBoundField(container.getComponent(i), fieldName)) { + return true; + } + } + } + return false; + } } diff --git a/java/compiler/impl/compiler-impl.iml b/java/compiler/impl/compiler-impl.iml index f517eff750db..d9d39953f177 100644 --- a/java/compiler/impl/compiler-impl.iml +++ b/java/compiler/impl/compiler-impl.iml @@ -14,7 +14,7 @@ - + diff --git a/java/compiler/notNull/notNull.iml b/java/compiler/instrumentation-util/instrumentation-util.iml similarity index 100% rename from java/compiler/notNull/notNull.iml rename to java/compiler/instrumentation-util/instrumentation-util.iml diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/InstrumentationClassFinder.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java similarity index 71% rename from jps/jps-builders/src/org/jetbrains/jps/incremental/java/InstrumentationClassFinder.java rename to java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java index 8b2ee439af91..de009b37c9bf 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/InstrumentationClassFinder.java +++ b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumentationClassFinder.java @@ -1,10 +1,7 @@ -package org.jetbrains.jps.incremental.java; +package com.intellij.compiler.instrumentation; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; +import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.EmptyVisitor; import sun.misc.Resource; @@ -12,6 +9,7 @@ import sun.misc.Resource; import java.io.*; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLClassLoader; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -23,22 +21,81 @@ import java.util.zip.ZipFile; public class InstrumentationClassFinder { private static final PseudoClass[] EMPTY_PSEUDOCLASS_ARRAY = new PseudoClass[0]; private static final String CLASS_RESOURCE_EXTENSION = ".class"; + private static final URL[] URL_EMPTY_ARRAY = new URL[0]; private final Map myLoaded = new HashMap(); // className -> class object private final ClassFinderClasspath myPlatformClasspath; private final ClassFinderClasspath myClasspath; + private final URL[] myPlatformUrls; + private final URL[] myClasspathUrls; + private ClassLoader myLoader; + private byte[] myBuffer; - public InstrumentationClassFinder(final URL[] platformClasspath, final URL[] classpath) { - myPlatformClasspath = new ClassFinderClasspath(platformClasspath); - myClasspath = new ClassFinderClasspath(classpath); + public InstrumentationClassFinder(final URL[] cp) { + this(URL_EMPTY_ARRAY, cp); + } + + public InstrumentationClassFinder(final URL[] platformUrls, final URL[] classpathUrls) { + myPlatformUrls = platformUrls; + myClasspathUrls = classpathUrls; + myPlatformClasspath = new ClassFinderClasspath(platformUrls); + myClasspath = new ClassFinderClasspath(classpathUrls); + } + + // compatibility with legacy code requiring ClassLoader + public ClassLoader getLoader() { + ClassLoader loader = myLoader; + if (loader != null) { + return loader; + } + final URLClassLoader platformLoader = myPlatformUrls.length > 0 ? new URLClassLoader(myPlatformUrls, null) : null; + final ClassLoader cpLoader = new URLClassLoader(myClasspathUrls, platformLoader); + loader = new ClassLoader(cpLoader) { + + public InputStream getResourceAsStream(String name) { + InputStream is = null; + is = super.getResourceAsStream(name); + if (is == null) { + try { + is = InstrumentationClassFinder.this.getResourceAsStream(name); + } + catch (IOException ignored) { + } + } + return is; + } + + protected Class findClass(String name) throws ClassNotFoundException { + final InputStream is = lookupClassBeforeClasspath(name.replace('.', '/')); + if (is == null) { + throw new ClassNotFoundException(name); + } + try { + final byte[] bytes = loadBytes(is); + return defineClass(name, bytes, 0, bytes.length); + } + finally { + try { + is.close(); + } + catch (IOException ignored) { + } + } + } + }; + myLoader = loader; + return loader; } public void releaseResources() { myPlatformClasspath.releaseResources(); myClasspath.releaseResources(); myLoaded.clear(); + myBuffer = null; + myLoader = null; } - public PseudoClass loadClass(final String internalName) throws IOException, ClassNotFoundException{ + public PseudoClass loadClass(final String name) throws IOException, ClassNotFoundException{ + final String internalName = name.replace('.', '/'); // normalize final PseudoClass aClass = myLoaded.get(internalName); if (aClass != null) { return aClass; @@ -81,12 +138,28 @@ public class InstrumentationClassFinder { } } - @Nullable + public InputStream getResourceAsStream(String resourceName) throws IOException { + InputStream is = null; + + Resource resource = myPlatformClasspath.getResource(resourceName, false); + if (resource != null) { + is = resource.getInputStream(); + } + + if (is == null) { + resource = myClasspath.getResource(resourceName, false); + if (resource != null) { + is = resource.getInputStream(); + } + } + + return is; + } + protected InputStream lookupClassBeforeClasspath(final String internalClassName) { return null; } - @Nullable protected InputStream lookupClassAfterClasspath(final String internalClassName) { return null; } @@ -97,20 +170,61 @@ public class InstrumentationClassFinder { reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); - return new PseudoClass(visitor.myName, visitor.mySuperclassName, visitor.myInterfaces, visitor.myIsInterface); + return new PseudoClass(visitor.myName, visitor.mySuperclassName, visitor.myInterfaces, visitor.myModifiers, visitor.myMethods); } public final class PseudoClass { private final String myName; private final String mySuperClass; private final String[] myInterfaces; - private final boolean isInterface; + private final int myModifiers; + private final List myMethods; - private PseudoClass(final String name, final String superClass, final String[] interfaces, final boolean anInterface) { + private PseudoClass(final String name, final String superClass, final String[] interfaces, final int modifiers, List methods) { myName = name; mySuperClass = superClass; myInterfaces = interfaces; - isInterface = anInterface; + myModifiers = modifiers; + myMethods = methods; + } + + public int getModifiers() { + return myModifiers; + } + + public boolean isInterface() { + return (myModifiers & Opcodes.ACC_INTERFACE) > 0; + } + + public String getName() { + return myName; + } + + public List getMethods() { + return myMethods; + } + + public List findMethods(String name) { + final List result = new ArrayList(); + for (PseudoMethod method : myMethods) { + if (method.getName().equals(name)){ + result.add(method); + } + } + return result; + } + + public PseudoMethod findMethod(String name, String descriptor) { + for (PseudoMethod method : myMethods) { + if (method.getName().equals(name) && method.getSignature().equals(descriptor)){ + return method; + } + } + return null; + } + + public InstrumentationClassFinder getFinder() { + return InstrumentationClassFinder.this; } public PseudoClass getSuperClass() throws IOException, ClassNotFoundException { @@ -173,36 +287,69 @@ public class InstrumentationClassFinder { if (x.implementsInterface(this)) { return true; } - if (x.isInterface() && getName().equals("java/lang/Object")) { + if (x.isInterface() && "java/lang/Object".equals(getName())) { return true; } return false; } - public boolean isInterface() { - return isInterface; + public boolean hasDefaultPublicConstructor() { + for (PseudoMethod method : myMethods) { + if ("".equals(method.getName()) && "()V".equals(method.getSignature())) { + return true; + } + } + return false; + } + + public String getDescriptor() { + return "L" + myName + ";"; + } + } + + public static final class PseudoMethod { + private final int myAccess; + private final String myName; + private final String mySignature; + + public PseudoMethod(int access, String name, String signature) { + myAccess = access; + myName = name; + mySignature = signature; + } + + public int getModifiers() { + return myAccess; } public String getName() { return myName; } + + public String getSignature() { + return mySignature; + } } private static class V extends EmptyVisitor { public String mySuperclassName = null; public String[] myInterfaces = null; public String myName = null; - public boolean myIsInterface = false; + public int myModifiers; + private final List myMethods = new ArrayList(); - public void visitAttribute(Attribute attr) { - super.visitAttribute(attr); + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if ((access & Opcodes.ACC_PUBLIC) > 0) { + myMethods.add(new PseudoMethod(access, name, desc)); + } + return super.visitMethod(access, name, desc, signature, exceptions); } public void visit(int version, int access, String pName, String signature, String pSuperName, String[] pInterfaces) { mySuperclassName = pSuperName; myInterfaces = pInterfaces; myName = pName; - myIsInterface = (access & Opcodes.ACC_INTERFACE) > 0; + myModifiers = access; } } @@ -221,7 +368,6 @@ public class InstrumentationClassFinder { } } - @Nullable public Resource getResource(String s, boolean flag) { int i = 0; for (Loader loader; (loader = getLoader(i)) != null; i++) { @@ -242,7 +388,6 @@ public class InstrumentationClassFinder { myLoadersMap.clear(); } - @Nullable private synchronized Loader getLoader(int i) { while (myLoaders.size() < i + 1) { URL url; @@ -275,7 +420,6 @@ public class InstrumentationClassFinder { return myLoaders.get(i); } - @Nullable private Loader getLoader(final URL url, int index) throws IOException { String s; try { @@ -317,7 +461,6 @@ public class InstrumentationClassFinder { return myURL; } - @Nullable public abstract Resource getResource(final String name, boolean flag); public abstract void releaseResources(); @@ -345,7 +488,6 @@ public class InstrumentationClassFinder { public void releaseResources() { } - @Nullable public Resource getResource(final String name, boolean check) { URL url = null; File file = null; @@ -410,7 +552,6 @@ public class InstrumentationClassFinder { } } - @NonNls public String toString() { return "FileLoader [" + myRootDir + "]"; } @@ -438,7 +579,6 @@ public class InstrumentationClassFinder { } } - @Nullable private ZipFile acquireZipFile() throws IOException { ZipFile zipFile = myZipFile; if (zipFile == null) { @@ -448,7 +588,6 @@ public class InstrumentationClassFinder { return zipFile; } - @Nullable private ZipFile doGetZipFile() throws IOException { if (FILE_PROTOCOL.equals(myURL.getProtocol())) { String s = unescapePercentSequences(myURL.getFile().replace('/', File.separatorChar)); @@ -463,7 +602,6 @@ public class InstrumentationClassFinder { return null; } - @Nullable public Resource getResource(String name, boolean flag) { try { final ZipFile file = acquireZipFile(); @@ -501,11 +639,9 @@ public class InstrumentationClassFinder { return myURL; } - @Nullable public InputStream getInputStream() throws IOException { - ZipFile file = null; try { - file = acquireZipFile(); + final ZipFile file = acquireZipFile(); if (file == null) { return null; } @@ -527,7 +663,6 @@ public class InstrumentationClassFinder { } } - @NonNls public String toString() { return "JarLoader [" + myURL + "]"; } @@ -535,8 +670,7 @@ public class InstrumentationClassFinder { } - @NotNull - private static String unescapePercentSequences(@NotNull String s) { + private static String unescapePercentSequences(String s) { if (s.indexOf('%') == -1) { return s; } @@ -591,4 +725,27 @@ public class InstrumentationClassFinder { return -1; } + public byte[] loadBytes(InputStream stream) { + byte[] buf = myBuffer; + if (buf == null) { + buf = new byte[512]; + myBuffer = buf; + } + + final ByteArrayOutputStream result = new ByteArrayOutputStream(); + try { + while (true) { + int n = stream.read(buf, 0, buf.length); + if (n <= 0) { + break; + } + result.write(buf, 0, n); + } + result.close(); + } + catch (IOException ignored) { + } + return result.toByteArray(); + } + } diff --git a/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumenterClassWriter.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumenterClassWriter.java new file mode 100644 index 000000000000..ffdf058a2fbf --- /dev/null +++ b/java/compiler/instrumentation-util/src/com/intellij/compiler/instrumentation/InstrumenterClassWriter.java @@ -0,0 +1,43 @@ +package com.intellij.compiler.instrumentation; + +import org.objectweb.asm.ClassWriter; + +/** +* @author Eugene Zhuravlev +* Date: 3/27/12 +*/ +public class InstrumenterClassWriter extends ClassWriter { + private final InstrumentationClassFinder myFinder; + + public InstrumenterClassWriter(int flags, final InstrumentationClassFinder finder) { + super(flags); + myFinder = finder; + } + + protected String getCommonSuperClass(final String type1, final String type2) { + try { + final InstrumentationClassFinder.PseudoClass cls1 = myFinder.loadClass(type1); + final InstrumentationClassFinder.PseudoClass cls2 = myFinder.loadClass(type2); + if (cls1.isAssignableFrom(cls2)) { + return cls1.getName(); + } + if (cls2.isAssignableFrom(cls1)) { + return cls2.getName(); + } + if (cls1.isInterface() || cls2.isInterface()) { + return "java/lang/Object"; + } + else { + InstrumentationClassFinder.PseudoClass c = cls1; + do { + c = c.getSuperClass(); + } + while (!c.isAssignableFrom(cls2)); + return c.getName(); + } + } + catch (Exception e) { + throw new RuntimeException(e.toString(), e); + } + } +} diff --git a/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java similarity index 100% rename from java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java rename to java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java diff --git a/java/compiler/javac2/javac2.iml b/java/compiler/javac2/javac2.iml index 49c3fa54efee..a8680af11500 100644 --- a/java/compiler/javac2/javac2.iml +++ b/java/compiler/javac2/javac2.iml @@ -11,7 +11,7 @@ - + diff --git a/java/compiler/javac2/src/com/intellij/ant/AntClassWriter.java b/java/compiler/javac2/src/com/intellij/ant/AntClassWriter.java deleted file mode 100644 index dc8b6552147d..000000000000 --- a/java/compiler/javac2/src/com/intellij/ant/AntClassWriter.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed 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 com.intellij.ant; - -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; - -import java.io.IOException; - -/** - * @author yole - */ -public class AntClassWriter extends ClassWriter { - private final PseudoClassLoader myPseudoClassLoader; - - public AntClassWriter(int flags, final PseudoClassLoader pseudoLoader) { - super(flags); - myPseudoClassLoader = pseudoLoader; - } - - public AntClassWriter(ClassReader classReader, int flags, final PseudoClassLoader pseudoLoader) { - super(classReader, flags); - myPseudoClassLoader = pseudoLoader; - } - - protected String getCommonSuperClass(final String type1, final String type2) { - try { - PseudoClassLoader.PseudoClass p1 = myPseudoClassLoader.loadClass(type1); - PseudoClassLoader.PseudoClass p2 = myPseudoClassLoader.loadClass(type2); - - return p1.getCommonSuperClassName(p2); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - throw new RuntimeException(e.getMessage()); - } - catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e.getMessage()); - } - } -} diff --git a/java/compiler/javac2/src/com/intellij/ant/InstrumentationUtil.java b/java/compiler/javac2/src/com/intellij/ant/InstrumentationUtil.java index 8409618031a9..eb423ae59820 100644 --- a/java/compiler/javac2/src/com/intellij/ant/InstrumentationUtil.java +++ b/java/compiler/javac2/src/com/intellij/ant/InstrumentationUtil.java @@ -15,6 +15,8 @@ */ package com.intellij.ant; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; +import com.intellij.compiler.instrumentation.InstrumenterClassWriter; import com.intellij.compiler.notNullVerification.NotNullVerifyingInstrumenter; import com.intellij.uiDesigner.compiler.*; import com.intellij.uiDesigner.lw.CompiledClassPropertiesProvider; @@ -39,14 +41,14 @@ import java.util.*; */ public class InstrumentationUtil { - public static PseudoClassLoader createPseudoClassLoader(final String classPath) throws MalformedURLException { + public static InstrumentationClassFinder createInstrumentationClassFinder(final String classPath) throws MalformedURLException { final ArrayList urls = new ArrayList(); for (StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); tokenizer.hasMoreTokens();) { final String s = tokenizer.nextToken(); urls.add(new File(s).toURL()); } final URL[] urlsArr = (URL[])urls.toArray(new URL[urls.size()]); - return new PseudoClassLoader(urlsArr); + return new InstrumentationClassFinder(urlsArr); } public static int getClassFileVersion(ClassReader reader) { @@ -108,12 +110,12 @@ public class InstrumentationUtil { } private class AntNestedFormLoader implements NestedFormLoader { - private final PseudoClassLoader myLoader; + private final InstrumentationClassFinder myClassFinder; private final List myNestedFormPathList; private final HashMap myFormCache = new HashMap(); - public AntNestedFormLoader(final PseudoClassLoader loader, List nestedFormPathList) { - myLoader = loader; + public AntNestedFormLoader(final InstrumentationClassFinder classFinder, List nestedFormPathList) { + myClassFinder = classFinder; myNestedFormPathList = nestedFormPathList; } @@ -143,7 +145,7 @@ public class InstrumentationUtil { } } } - InputStream resourceStream = myLoader.getLoader ().getResourceAsStream(formFilePath); + InputStream resourceStream = myClassFinder.getResourceAsStream(formFilePath); if (resourceStream != null) { return loadForm(formFilePath, resourceStream); } @@ -164,11 +166,11 @@ public class InstrumentationUtil { } } - public void instrumentForm(final File file, final PseudoClassLoader loader) { + public void instrumentForm(final File file, final InstrumentationClassFinder classFinder) { log("compiling form " + file.getAbsolutePath(), Project.MSG_VERBOSE); final LwRootContainer rootContainer; try { - rootContainer = Utils.getRootContainer(file.toURL(), new CompiledClassPropertiesProvider(loader.getLoader ())); + rootContainer = Utils.getRootContainer(file.toURL(), new CompiledClassPropertiesProvider(classFinder.getLoader())); } catch (AlienFormFileException e) { // ignore non-IDEA forms @@ -216,9 +218,9 @@ public class InstrumentationUtil { finally { stream.close(); } - AntNestedFormLoader formLoader = new AntNestedFormLoader(loader, myNestedFormPathList); - AntClassWriter classWriter = new AntClassWriter(InstrumentationUtil.getAsmClassWriterFlags(version), loader); - final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, loader.getLoader(), formLoader, false, classWriter); + AntNestedFormLoader formLoader = new AntNestedFormLoader(classFinder, myNestedFormPathList); + InstrumenterClassWriter classWriter = new InstrumenterClassWriter(InstrumentationUtil.getAsmClassWriterFlags(version), classFinder); + final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, classFinder, formLoader, false, classWriter); codeGenerator.patchFile(classFile); final FormErrorInfo[] warnings = codeGenerator.getWarnings(); @@ -243,15 +245,15 @@ public class InstrumentationUtil { } } - public static byte[] instrumentNotNull(final byte[] buffer, final PseudoClassLoader loader) { + public static byte[] instrumentNotNull(final byte[] buffer, final InstrumentationClassFinder loader) { return instrumentNotNull(new ClassReader (buffer), loader); } - private static byte[] instrumentNotNull(final ClassReader reader, final PseudoClassLoader loader) { + private static byte[] instrumentNotNull(final ClassReader reader, final InstrumentationClassFinder loader) { int version = getClassFileVersion(reader); if (version >= Opcodes.V1_5) { - ClassWriter writer = new AntClassWriter(getAsmClassWriterFlags(version), loader); + ClassWriter writer = new InstrumenterClassWriter(getAsmClassWriterFlags(version), loader); final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer); reader.accept(instrumenter, 0); @@ -264,7 +266,7 @@ public class InstrumentationUtil { return null; } - public static int instrumentNotNull(final File file, final PseudoClassLoader loader) throws IOException { + public static int instrumentNotNull(final File file, final InstrumentationClassFinder loader) throws IOException { int instrumented = 0; final String path = file.getPath(); final FileInputStream inputStream = new FileInputStream(file); diff --git a/java/compiler/javac2/src/com/intellij/ant/Javac2.java b/java/compiler/javac2/src/com/intellij/ant/Javac2.java index be4354258ac8..5c3035534b44 100644 --- a/java/compiler/javac2/src/com/intellij/ant/Javac2.java +++ b/java/compiler/javac2/src/com/intellij/ant/Javac2.java @@ -15,6 +15,8 @@ */ package com.intellij.ant; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; +import com.intellij.compiler.instrumentation.InstrumenterClassWriter; import com.intellij.compiler.notNullVerification.NotNullVerifyingInstrumenter; import com.intellij.uiDesigner.compiler.*; import com.intellij.uiDesigner.lw.CompiledClassPropertiesProvider; @@ -201,26 +203,34 @@ public class Javac2 extends Javac { * class files. */ protected void compile() { - // compile java - if (areJavaClassesCompiled()) { - super.compile(); - } + // compile java + if (areJavaClassesCompiled()) { + super.compile(); + } - PseudoClassLoader loader = buildClasspathClassLoader(); - if (loader == null) return; - instrumentForms(loader); + InstrumentationClassFinder finder = buildClasspathClassLoader(); + if (finder == null) { + return; + } + try { + instrumentForms(finder); //NotNull instrumentation - final int instrumented = instrumentNotNull(getDestdir(), loader); + final int instrumented = instrumentNotNull(getDestdir(), finder); + log("Added @NotNull assertions to " + instrumented + " files", Project.MSG_INFO); + } + finally { + finder.releaseResources(); + } } - /** + /** * Instrument forms * - * @param loader a classloader to use + * @param finder a classloader to use */ - private void instrumentForms(final PseudoClassLoader loader) { + private void instrumentForms(final InstrumentationClassFinder finder) { // we instrument every file, because we cannot find which files should not be instrumented without dependency storage final ArrayList formsToInstrument = myFormFiles; @@ -237,7 +247,7 @@ public class Javac2 extends Javac { log("compiling form " + formFile.getAbsolutePath(), Project.MSG_VERBOSE); final LwRootContainer rootContainer; try { - rootContainer = Utils.getRootContainer(formFile.toURL(), new CompiledClassPropertiesProvider(loader.getLoader())); + rootContainer = Utils.getRootContainer(formFile.toURL(), new CompiledClassPropertiesProvider(finder.getLoader())); } catch (AlienFormFileException e) { // ignore non-IDEA forms @@ -283,9 +293,9 @@ public class Javac2 extends Javac { finally { stream.close(); } - AntNestedFormLoader formLoader = new AntNestedFormLoader(loader.getLoader(), myNestedFormPathList); - AntClassWriter classWriter = new AntClassWriter(getAsmClassWriterFlags(version), loader); - final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, loader.getLoader(), formLoader, false, classWriter); + AntNestedFormLoader formLoader = new AntNestedFormLoader(finder.getLoader(), myNestedFormPathList); + InstrumenterClassWriter classWriter = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder); + final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, finder, formLoader, false, classWriter); codeGenerator.patchFile(classFile); final FormErrorInfo[] warnings = codeGenerator.getWarnings(); @@ -322,7 +332,7 @@ public class Javac2 extends Javac { * * @return a URL classloader */ - private PseudoClassLoader buildClasspathClassLoader() { + private InstrumentationClassFinder buildClasspathClassLoader() { final StringBuffer classPathBuffer = new StringBuffer(); final Path cp = new Path(getProject()); appendPath(cp, getBootclasspath()); @@ -349,7 +359,7 @@ public class Javac2 extends Javac { log("classpath=" + classPath, Project.MSG_VERBOSE); try { - return InstrumentationUtil.createPseudoClassLoader(classPath); + return InstrumentationUtil.createInstrumentationClassFinder(classPath); } catch (MalformedURLException e) { fireError(e.getMessage()); @@ -373,10 +383,10 @@ public class Javac2 extends Javac { * Instrument classes with NotNull annotations * * @param dir the directory with classes to instrument (the directory is processed recursively) - * @param loader the classloader to use + * @param finder the classloader to use * @return the amount of classes actually affected by instrumentation */ - private int instrumentNotNull(File dir, final PseudoClassLoader loader) { + private int instrumentNotNull(File dir, final InstrumentationClassFinder finder) { int instrumented = 0; final File[] files = dir.listFiles(); for (int i = 0; i < files.length; i++) { @@ -393,7 +403,7 @@ public class Javac2 extends Javac { int version = getClassFileVersion(reader); if (version >= Opcodes.V1_5) { - ClassWriter writer = new AntClassWriter(getAsmClassWriterFlags(version), loader); + ClassWriter writer = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder); final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer); reader.accept(instrumenter, 0); @@ -421,7 +431,7 @@ public class Javac2 extends Javac { } } else if (file.isDirectory()) { - instrumented += instrumentNotNull(file, loader); + instrumented += instrumentNotNull(file, finder); } } diff --git a/java/compiler/javac2/src/com/intellij/ant/PseudoClassLoader.java b/java/compiler/javac2/src/com/intellij/ant/PseudoClassLoader.java deleted file mode 100644 index 97a4172d7b94..000000000000 --- a/java/compiler/javac2/src/com/intellij/ant/PseudoClassLoader.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2000-2011 JetBrains s.r.o. - * - * Licensed 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 com.intellij.ant; - -import org.objectweb.asm.Attribute; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.commons.EmptyVisitor; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; - -/** - * @author db - * Date: 06.06.11 - */ -public class PseudoClassLoader { - static final WeakHashMap myCache = new WeakHashMap(); - - final Map myDefinedClasses = new HashMap (); - final Map myDefinedClassesData = new HashMap (); - - private class MemoryClassLoader extends ClassLoader { - public MemoryClassLoader(URLClassLoader classpathLoader) { - super(classpathLoader); - } - - protected Class findClass(String name) throws ClassNotFoundException { - final byte[] data = (byte[])myDefinedClassesData.get(name); - if (data == null) { - throw new ClassNotFoundException(name); - } - return defineClass(name, data, 0, data.length); - } - - public URL findResource(String name) { - return super.findResource(name); - } - } - - public class PseudoClass { - final String myName; - final String mySuperClass; - final String[] myInterfaces; - final boolean isInterface; - - private PseudoClass(final Class repr) { - final Class superclass = repr.getSuperclass(); - final Class[] interfaces = repr.getInterfaces(); - - myName = repr.getName().replace('.', '/'); - mySuperClass = superclass == null ? null : superclass.getName().replace('.', '/'); - myInterfaces = interfaces.length == 0 ? null : new String[interfaces.length]; - - for (int i=0; i 0; - } - } - - private PseudoClass createPseudoClass(final ClassReader r) { - final V visitor = new V(); - - r.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); - - return new PseudoClass(visitor.name, visitor.superName, visitor.interfaces, visitor.isInterface); - } - - private class Tag { - final PseudoClass myClass; - final long myStamp; - - private Tag(PseudoClass aClass, long stamp) { - myClass = aClass; - myStamp = stamp; - } - } - - public PseudoClass loadClass(final String internalName) throws IOException, ClassNotFoundException { - final PseudoClass defined = (PseudoClass) myDefinedClasses.get(internalName); - - if (defined != null) { - return defined; - } - - final URL resource = myLoader.findResource(internalName + ".class"); - - if (resource == null) { - final Class lastResort = myLoader.loadClass(internalName.replace('/', '.')); - return new PseudoClass(lastResort); - } - - final String fileName = resource.getFile(); - final boolean isFile = fileName.length() > 0; - final File file = new File(fileName); - - if (isFile) { - final Tag cached = (Tag)myCache.get(internalName); - - if (cached != null && cached.myStamp == file.lastModified()) { - return cached.myClass; - } - } - - final InputStream content = (InputStream)resource.getContent(); - final ClassReader reader = new ClassReader(content); - final PseudoClass result = createPseudoClass(reader); - - if (isFile) { - myCache.put(internalName, new Tag(result, file.lastModified())); - } - - return result; - } -} diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index 1750b2ea3852..38e0eb6d4c3d 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -17,7 +17,7 @@ - + diff --git a/jps/jps-builders/jps-builders.iml b/jps/jps-builders/jps-builders.iml index 9c5878d1e2f3..e27c35289a56 100644 --- a/jps/jps-builders/jps-builders.iml +++ b/jps/jps-builders/jps-builders.iml @@ -12,7 +12,7 @@ - + diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 10b03b898693..5378c096312a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -1,5 +1,7 @@ package org.jetbrains.jps.incremental.java; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; +import com.intellij.compiler.instrumentation.InstrumenterClassWriter; import com.intellij.compiler.notNullVerification.NotNullVerifyingInstrumenter; import com.intellij.execution.process.BaseOSProcessHandler; import com.intellij.openapi.application.PathManager; @@ -37,7 +39,6 @@ import java.io.*; import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.URL; -import java.net.URLClassLoader; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -274,15 +275,14 @@ public class JavaBuilder extends ModuleLevelBuilder { context.checkCanceled(); if (!forms.isEmpty() || addNotNullAssertions) { - final InstrumentationClassFinder finder = createInstrumentationClassFinder(platformCp, classpath, outputSink); + final Map chunkSourcePath = ProjectPaths.getSourceRootsWithDependents(chunk, context.isCompilingTests()); + final InstrumentationClassFinder finder = createInstrumentationClassFinder(platformCp, classpath, chunkSourcePath, outputSink); try { if (!forms.isEmpty()) { try { context.processMessage(new ProgressMessage("Instrumenting forms [" + chunkName + "]")); - final Map chunkSourcePath = ProjectPaths.getSourceRootsWithDependents(chunk, context.isCompilingTests()); - final ClassLoader loader = createInstrumentationClassLoader(platformCp, classpath, chunkSourcePath, outputSink); - instrumentForms(context, chunk, chunkSourcePath, loader, finder, forms, outputSink); + instrumentForms(context, chunk, chunkSourcePath, finder, forms, outputSink); } finally { context.processMessage(new ProgressMessage("Finished instrumenting forms [" + chunkName + "]")); @@ -529,22 +529,24 @@ public class JavaBuilder extends ModuleLevelBuilder { private static InstrumentationClassFinder createInstrumentationClassFinder(Collection platformCp, Collection classpath, - final OutputFilesSink outputSink) throws MalformedURLException { + Map chunkSourcePath, final OutputFilesSink outputSink) throws MalformedURLException { final URL[] platformUrls = new URL[platformCp.size()]; int index = 0; for (File file : platformCp) { platformUrls[index++] = file.toURI().toURL(); } - final URL[] urls = new URL[classpath.size() + 1]; - index = 0; + final List urls = new ArrayList(classpath.size() + chunkSourcePath.size() + 1); for (File file : classpath) { - urls[index++] = file.toURI().toURL(); + urls.add(file.toURI().toURL()); } - urls[index++] = getResourcePath(GridConstraints.class).toURI().toURL(); // forms_rt.jar + urls.add(getResourcePath(GridConstraints.class).toURI().toURL()); // forms_rt.jar //urls.add(getResourcePath(CellConstraints.class).toURI().toURL()); // jgoodies-forms - - return new InstrumentationClassFinder(platformUrls, urls) { + for (File file : chunkSourcePath.keySet()) { // sourcepath for loading forms resources + urls.add(file.toURI().toURL()); + } + + return new InstrumentationClassFinder(platformUrls, urls.toArray(new URL[urls.size()])) { protected InputStream lookupClassBeforeClasspath(String internalClassName) { final OutputFileObject.Content content = outputSink.lookupClassBytes(internalClassName.replace("/", ".")); if (content != null) { @@ -555,23 +557,6 @@ public class JavaBuilder extends ModuleLevelBuilder { }; } - private static ClassLoader createInstrumentationClassLoader(Collection platformCp, Collection classpath, - Map chunkSourcePath, - OutputFilesSink outputSink) throws MalformedURLException { - final List urls = new ArrayList(); - for (Collection cp : Arrays.asList(platformCp, classpath)) { - for (File file : cp) { - urls.add(file.toURI().toURL()); - } - } - urls.add(getResourcePath(GridConstraints.class).toURI().toURL()); // forms_rt.jar - //urls.add(getResourcePath(CellConstraints.class).toURI().toURL()); // jgoodies-forms - for (File file : chunkSourcePath.keySet()) { - urls.add(file.toURI().toURL()); - } - return new CompiledClassesLoader(outputSink, urls.toArray(new URL[urls.size()])); - } - private static final Key> JAVAC_OPTIONS = Key.create("_javac_options_"); private static final Key> JAVAC_VM_OPTIONS = Key.create("_javac_vm_options_"); @@ -713,7 +698,6 @@ public class JavaBuilder extends ModuleLevelBuilder { private static void instrumentForms(CompileContext context, ModuleChunk chunk, final Map chunkSourcePath, - final ClassLoader loader, final InstrumentationClassFinder finder, Collection formsToInstrument, OutputFilesSink outputSink) throws ProjectBuildException { @@ -731,7 +715,7 @@ public class JavaBuilder extends ModuleLevelBuilder { for (File formFile : formsToInstrument) { final LwRootContainer rootContainer; try { - rootContainer = Utils.getRootContainer(formFile.toURI().toURL(), new CompiledClassPropertiesProvider(loader)); + rootContainer = Utils.getRootContainer(formFile.toURI().toURL(), new CompiledClassPropertiesProvider(finder.getLoader())); } catch (AlienFormFileException e) { // ignore non-IDEA forms @@ -774,7 +758,7 @@ public class JavaBuilder extends ModuleLevelBuilder { final int version = getClassFileVersion(classReader); final InstrumenterClassWriter classWriter = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder); - final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, loader, nestedFormsLoader, false, classWriter); + final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, finder, nestedFormsLoader, false, classWriter); final byte[] patchedBytes = codeGenerator.patchClass(classReader); if (patchedBytes != null) { outputClassFile.updateContent(patchedBytes); @@ -923,42 +907,6 @@ public class JavaBuilder extends ModuleLevelBuilder { } } - public static class InstrumenterClassWriter extends ClassWriter { - private final InstrumentationClassFinder myFinder; - - public InstrumenterClassWriter(int flags, final InstrumentationClassFinder finder) { - super(flags); - myFinder = finder; - } - - protected String getCommonSuperClass(final String type1, final String type2) { - try { - final InstrumentationClassFinder.PseudoClass cls1 = myFinder.loadClass(type1); - final InstrumentationClassFinder.PseudoClass cls2 = myFinder.loadClass(type2); - if (cls1.isAssignableFrom(cls2)) { - return cls1.getName(); - } - if (cls2.isAssignableFrom(cls1)) { - return cls2.getName(); - } - if (cls1.isInterface() || cls2.isInterface()) { - return "java/lang/Object"; - } - else { - InstrumentationClassFinder.PseudoClass c = cls1; - do { - c = c.getSuperClass(); - } - while (!c.isAssignableFrom(cls2)); - return c.getName(); - } - } - catch (Exception e) { - throw new RuntimeException(e.toString(), e); - } - } - } - private static class MyNestedFormLoader implements NestedFormLoader { private final Map mySourceRoots; private final Collection myOutputRoots; @@ -1039,27 +987,6 @@ public class JavaBuilder extends ModuleLevelBuilder { return new File(PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class")); } - private static class CompiledClassesLoader extends URLClassLoader { - private final OutputFilesSink mySink; - - public CompiledClassesLoader(OutputFilesSink sink, URL[] urls) { - super(urls, null); - mySink = sink; - } - - protected Class findClass(String name) throws ClassNotFoundException { - final OutputFileObject.Content content = mySink.lookupClassBytes(name); - if (content != null) { - return defineClass(name, content.getBuffer(), content.getOffset(), content.getLength()); - } - return super.findClass(name); - } - - public URL findResource(String name) { - return super.findResource(name); - } - } - private class ClassProcessingConsumer implements OutputFileConsumer { private final CompileContext myCompileContext; private final OutputFileConsumer myDelegateOutputFileSink; diff --git a/jps/jps.iml b/jps/jps.iml index 44d24bb8c46f..c08dac3c1a16 100644 --- a/jps/jps.iml +++ b/jps/jps.iml @@ -34,6 +34,7 @@ + diff --git a/jps/src/org/jetbrains/jps/ModuleBuildState.groovy b/jps/src/org/jetbrains/jps/ModuleBuildState.groovy index b82ef2422486..52f15608c417 100644 --- a/jps/src/org/jetbrains/jps/ModuleBuildState.groovy +++ b/jps/src/org/jetbrains/jps/ModuleBuildState.groovy @@ -1,7 +1,7 @@ package org.jetbrains.jps import com.intellij.ant.InstrumentationUtil.FormInstrumenter -import com.intellij.ant.PseudoClassLoader +import com.intellij.compiler.instrumentation.InstrumentationClassFinder import org.jetbrains.ether.ProjectWrapper import org.jetbrains.ether.dependencyView.Callbacks.Backend @@ -13,7 +13,7 @@ class ModuleBuildState { boolean tests Backend callback FormInstrumenter formInstrumenter - PseudoClassLoader loader + InstrumentationClassFinder loader ProjectWrapper projectWrapper List sourceFiles List sourceRoots diff --git a/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy b/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy index babacca78d33..1c7537687a48 100644 --- a/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy +++ b/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy @@ -3,6 +3,7 @@ package org.jetbrains.jps.builders import com.intellij.ant.InstrumentationUtil import com.intellij.ant.InstrumentationUtil.FormInstrumenter import com.intellij.ant.PrefixedPath +import com.intellij.compiler.instrumentation.InstrumentationClassFinder import org.apache.tools.ant.BuildListener import org.jetbrains.ether.ProjectWrapper import org.jetbrains.ether.dependencyView.AntListener @@ -332,17 +333,13 @@ class JetBrainsInstrumentations implements ModuleBuilder { def processModule(ModuleBuildState state, ModuleChunk moduleChunk, ProjectBuilder projectBuilder) { if (state.loader == null) { - final StringBuilder cp = new StringBuilder() - - cp.append(state.targetFolder) - cp.append(File.pathSeparator) - + final ArrayList urls = new ArrayList(); + urls.add(new File(state.targetFolder).toURL()); state.classpath.each { - cp.append(it) - cp.append(File.pathSeparator) + urls.add(new File(it).toURL()); } - state.loader = InstrumentationUtil.createPseudoClassLoader(cp.toString()) + state.loader = new InstrumentationClassFinder((URL[])urls.toArray(new URL[urls.size()])) final List formFiles = new ArrayList(); final ProjectWrapper pw = state.projectWrapper; @@ -409,6 +406,9 @@ class JetBrainsInstrumentations implements ModuleBuilder { InstrumentationUtil.instrumentNotNull(new File(state.targetFolder + File.separator + it + ".class"), state.loader) } } + if (state.loader != null) { + state.loader.releaseResources(); + } } } diff --git a/jps/src/org/jetbrains/jps/builders/javacApi/Java16ApiCompiler.groovy b/jps/src/org/jetbrains/jps/builders/javacApi/Java16ApiCompiler.groovy index 7ca3bb2dc8f3..a235e2fd9c97 100644 --- a/jps/src/org/jetbrains/jps/builders/javacApi/Java16ApiCompiler.groovy +++ b/jps/src/org/jetbrains/jps/builders/javacApi/Java16ApiCompiler.groovy @@ -1,17 +1,17 @@ package org.jetbrains.jps.builders.javacApi -import com.intellij.ant.InstrumentationUtil -import javax.tools.JavaCompiler -import javax.tools.JavaCompiler.CompilationTask -import javax.tools.JavaFileObject -import javax.tools.StandardLocation -import javax.tools.ToolProvider import org.jetbrains.jps.ModuleBuildState import org.jetbrains.jps.ModuleChunk import org.jetbrains.jps.ProjectBuilder import org.jetbrains.jps.Sdk import org.jetbrains.jps.builders.JavaFileCollector +import javax.tools.JavaCompiler +import javax.tools.JavaCompiler.CompilationTask +import javax.tools.JavaFileObject +import javax.tools.StandardLocation +import javax.tools.ToolProvider + /** * @author nik */ @@ -89,7 +89,7 @@ class Java16ApiCompiler { cp.append(state.targetFolder) fileManager.setLocation(StandardLocation.CLASS_PATH, classpath) - fileManager.setProperties(state.callback, InstrumentationUtil.createPseudoClassLoader(cp.toString())) + fileManager.setProperties(state.callback, toURLs(cp.toString())) Iterable toCompile = fileManager.getJavaFileObjectsFromFiles(filesToCompile) StringWriter out = new StringWriter() @@ -109,4 +109,13 @@ class Java16ApiCompiler { } } + private URL[] toURLs(final String classPath) { + final List urls = new ArrayList(); + for (StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); tokenizer.hasMoreTokens();) { + final String s = tokenizer.nextToken(); + urls.add(new File(s).toURL()); + } + return (URL[])urls.toArray(new URL[urls.size()]); + } + } diff --git a/jps/src/org/jetbrains/jps/builders/javacApi/OptimizedFileManager.java b/jps/src/org/jetbrains/jps/builders/javacApi/OptimizedFileManager.java index a4617a93289d..d7163000ea5d 100644 --- a/jps/src/org/jetbrains/jps/builders/javacApi/OptimizedFileManager.java +++ b/jps/src/org/jetbrains/jps/builders/javacApi/OptimizedFileManager.java @@ -1,7 +1,7 @@ package org.jetbrains.jps.builders.javacApi; import com.intellij.ant.InstrumentationUtil; -import com.intellij.ant.PseudoClassLoader; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.DefaultFileManager; import com.sun.tools.javac.util.List; @@ -14,10 +14,9 @@ import javax.lang.model.SourceVersion; import javax.tools.FileObject; import javax.tools.ForwardingJavaFileObject; import javax.tools.JavaFileObject; -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; +import java.io.*; import java.lang.reflect.Field; +import java.net.URL; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -34,11 +33,17 @@ public class OptimizedFileManager extends DefaultFileManager { private final Map myArchives; private final Map myIsFile = new ConcurrentHashMap(); private Callbacks.Backend callback; - private PseudoClassLoader loader; + private InstrumentationClassFinder classFinder; + private Map myCompiledClasses = new HashMap(); - public void setProperties(final Callbacks.Backend c, final PseudoClassLoader l) { + public void setProperties(final Callbacks.Backend c, final URL[] classpath) { callback = c; - loader = l; + classFinder = new InstrumentationClassFinder(classpath) { + protected InputStream lookupClassBeforeClasspath(String internalClassName) { + final byte[] bytes = myCompiledClasses.get(internalClassName); + return bytes != null? new ByteArrayInputStream(bytes) : null; + } + }; } public OptimizedFileManager() { @@ -201,7 +206,7 @@ public class OptimizedFileManager extends DefaultFileManager { final byte[] buffer = Arrays.copyOfRange(b, off, len); if (kind.equals(JavaFileObject.Kind.CLASS)) { - loader.defineClass(className.replaceAll("\\.", "/"), buffer); + myCompiledClasses.put(className.replace('.', '/'), buffer); if (callback != null) { final ClassReader reader = new ClassReader(buffer); @@ -212,7 +217,7 @@ public class OptimizedFileManager extends DefaultFileManager { public void commit() throws IOException { final OutputStream result = superOpenOutputStream(); - final byte[] instrumented = InstrumentationUtil.instrumentNotNull(buffer, loader); + final byte[] instrumented = InstrumentationUtil.instrumentNotNull(buffer, classFinder); if (instrumented != null) { result.write(instrumented); @@ -247,5 +252,8 @@ public class OptimizedFileManager extends DefaultFileManager { } myWriters.clear(); + if (classFinder != null) { + classFinder.releaseResources(); + } } } diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java b/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java index 15bb312a9e48..bff58a5533db 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/actions/PreviewFormAction.java @@ -18,6 +18,7 @@ package com.intellij.uiDesigner.actions; import com.intellij.CommonBundle; import com.intellij.compiler.PsiClassWriter; import com.intellij.compiler.impl.FileSetCompileScope; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; import com.intellij.execution.*; import com.intellij.execution.configurations.*; import com.intellij.execution.executors.DefaultRunExecutor; @@ -128,118 +129,124 @@ public final class PreviewFormAction extends AnAction{ final String classPath = OrderEnumerator.orderEntries(module).recursively().getPathsList().getPathsString() + File.pathSeparator + sources.getPathsString() + File.pathSeparator + /* resources bundles */ tempPath; - final ClassLoader loader = Form2ByteCodeCompiler.createClassLoader(classPath); + final InstrumentationClassFinder finder = Form2ByteCodeCompiler.createClassFinder(classPath); - final Document doc = FileDocumentManager.getInstance().getDocument(formFile); - final LwRootContainer rootContainer; try { - rootContainer = Utils.getRootContainer(doc.getText(), new CompiledClassPropertiesProvider(loader)); - } - catch (Exception e) { - Messages.showErrorDialog( - module.getProject(), - UIDesignerBundle.message("error.cannot.read.form", formFile.getPath().replace('/', File.separatorChar), e.getMessage()), - CommonBundle.getErrorTitle() - ); - return; - } - - if (rootContainer.getComponentCount() == 0) { - Messages.showErrorDialog( - module.getProject(), - UIDesignerBundle.message("error.cannot.preview.empty.form", formFile.getPath().replace('/', File.separatorChar)), - CommonBundle.getErrorTitle() - ); - return; - } - - setPreviewBindings(rootContainer, CLASS_TO_BIND_NAME); - - // 2. Copy previewer class and all its superclasses into TEMP directory and instrument it. - try { - PreviewNestedFormLoader nestedFormLoader = new PreviewNestedFormLoader(module, tempPath, loader); - - final File tempFile = CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME, true); - //CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$1", true); - CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MyExitAction", true); - CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MyPackAction", true); - CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MySetLafAction", true); - - Locale locale = Locale.getDefault(); - if (locale.getCountry().length() > 0 && locale.getLanguage().length() > 0) { - CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + - "_" + locale.getCountry() + PropertiesFileType.DOT_DEFAULT_EXTENSION); + final Document doc = FileDocumentManager.getInstance().getDocument(formFile); + final LwRootContainer rootContainer; + try { + rootContainer = Utils.getRootContainer(doc.getText(), new CompiledClassPropertiesProvider(finder.getLoader())); } - if (locale.getLanguage().length() > 0) { - CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + PropertiesFileType.DOT_DEFAULT_EXTENSION); - } - CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + PropertiesFileType.DOT_DEFAULT_EXTENSION); - CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + PropertiesFileType.DOT_DEFAULT_EXTENSION); - - final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, loader, nestedFormLoader, true, - new PsiClassWriter(module)); - codeGenerator.patchFile(tempFile); - final FormErrorInfo[] errors = codeGenerator.getErrors(); - if(errors.length != 0){ + catch (Exception e) { Messages.showErrorDialog( module.getProject(), - UIDesignerBundle.message("error.cannot.preview.form", - formFile.getPath().replace('/', File.separatorChar), - errors[0].getErrorMessage()), + UIDesignerBundle.message("error.cannot.read.form", formFile.getPath().replace('/', File.separatorChar), e.getMessage()), CommonBundle.getErrorTitle() ); return; } - } - catch (Exception e) { - LOG.debug(e); - Messages.showErrorDialog( - module.getProject(), - UIDesignerBundle.message("error.cannot.preview.form", formFile.getPath().replace('/', File.separatorChar), - e.getMessage() != null ? e.getMessage() : e.toString()), - CommonBundle.getErrorTitle() - ); - return; - } - // 2.5. Copy up-to-date properties files to the output directory. - final HashSet bundleSet = new HashSet(); - FormEditingUtil.iterateStringDescriptors( - rootContainer, - new FormEditingUtil.StringDescriptorVisitor() { - public boolean visit(final IComponent component, final StringDescriptor descriptor) { - if (descriptor.getBundleName() != null) { - bundleSet.add(descriptor.getDottedBundleName()); - } - return true; + if (rootContainer.getComponentCount() == 0) { + Messages.showErrorDialog( + module.getProject(), + UIDesignerBundle.message("error.cannot.preview.empty.form", formFile.getPath().replace('/', File.separatorChar)), + CommonBundle.getErrorTitle() + ); + return; + } + + setPreviewBindings(rootContainer, CLASS_TO_BIND_NAME); + + // 2. Copy previewer class and all its superclasses into TEMP directory and instrument it. + try { + PreviewNestedFormLoader nestedFormLoader = new PreviewNestedFormLoader(module, tempPath, finder); + + final File tempFile = CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME, true); + //CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$1", true); + CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MyExitAction", true); + CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MyPackAction", true); + CopyResourcesUtil.copyClass(tempPath, CLASS_TO_BIND_NAME + "$MySetLafAction", true); + + Locale locale = Locale.getDefault(); + if (locale.getCountry().length() > 0 && locale.getLanguage().length() > 0) { + CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + + "_" + locale.getCountry() + PropertiesFileType.DOT_DEFAULT_EXTENSION); } - }); + if (locale.getLanguage().length() > 0) { + CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + PropertiesFileType.DOT_DEFAULT_EXTENSION); + } + CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + "_" + locale.getLanguage() + PropertiesFileType.DOT_DEFAULT_EXTENSION); + CopyResourcesUtil.copyProperties(tempPath, RUNTIME_BUNDLE_PREFIX + PropertiesFileType.DOT_DEFAULT_EXTENSION); - if (bundleSet.size() > 0) { - HashSet virtualFiles = new HashSet(); - HashSet modules = new HashSet(); - PropertiesReferenceManager manager = PropertiesReferenceManager.getInstance(module.getProject()); - for(String bundleName: bundleSet) { - for(PropertiesFile propFile: manager.findPropertiesFiles(module, bundleName)) { - virtualFiles.add(propFile.getVirtualFile()); - final Module moduleForFile = ModuleUtil.findModuleForFile(propFile.getVirtualFile(), module.getProject()); - if (moduleForFile != null) { - modules.add(moduleForFile); - } + final AsmCodeGenerator codeGenerator = new AsmCodeGenerator( + rootContainer, finder, nestedFormLoader, true, new PsiClassWriter(module) + ); + codeGenerator.patchFile(tempFile); + final FormErrorInfo[] errors = codeGenerator.getErrors(); + if(errors.length != 0){ + Messages.showErrorDialog( + module.getProject(), + UIDesignerBundle.message("error.cannot.preview.form", + formFile.getPath().replace('/', File.separatorChar), + errors[0].getErrorMessage()), + CommonBundle.getErrorTitle() + ); + return; } } - FileSetCompileScope scope = new FileSetCompileScope(virtualFiles, modules.toArray(new Module[modules.size()])); + catch (Exception e) { + LOG.debug(e); + Messages.showErrorDialog( + module.getProject(), + UIDesignerBundle.message("error.cannot.preview.form", formFile.getPath().replace('/', File.separatorChar), + e.getMessage() != null ? e.getMessage() : e.toString()), + CommonBundle.getErrorTitle() + ); + return; + } - CompilerManager.getInstance(module.getProject()).make(scope, new CompileStatusNotification() { - public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) { - if (!aborted && errors == 0) { - runPreviewProcess(tempPath, sources, module, formFile, stringDescriptorLocale); + // 2.5. Copy up-to-date properties files to the output directory. + final HashSet bundleSet = new HashSet(); + FormEditingUtil.iterateStringDescriptors( + rootContainer, + new FormEditingUtil.StringDescriptorVisitor() { + public boolean visit(final IComponent component, final StringDescriptor descriptor) { + if (descriptor.getBundleName() != null) { + bundleSet.add(descriptor.getDottedBundleName()); + } + return true; + } + }); + + if (bundleSet.size() > 0) { + HashSet virtualFiles = new HashSet(); + HashSet modules = new HashSet(); + PropertiesReferenceManager manager = PropertiesReferenceManager.getInstance(module.getProject()); + for(String bundleName: bundleSet) { + for(PropertiesFile propFile: manager.findPropertiesFiles(module, bundleName)) { + virtualFiles.add(propFile.getVirtualFile()); + final Module moduleForFile = ModuleUtil.findModuleForFile(propFile.getVirtualFile(), module.getProject()); + if (moduleForFile != null) { + modules.add(moduleForFile); + } } } - }); + FileSetCompileScope scope = new FileSetCompileScope(virtualFiles, modules.toArray(new Module[modules.size()])); + + CompilerManager.getInstance(module.getProject()).make(scope, new CompileStatusNotification() { + public void finished(boolean aborted, int errors, int warnings, final CompileContext compileContext) { + if (!aborted && errors == 0) { + runPreviewProcess(tempPath, sources, module, formFile, stringDescriptorLocale); + } + } + }); + } + else { + runPreviewProcess(tempPath, sources, module, formFile, stringDescriptorLocale); + } } - else { - runPreviewProcess(tempPath, sources, module, formFile, stringDescriptorLocale); + finally { + finder.releaseResources(); } } diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java b/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java index b42fd6bf0520..8bcacec4b2ee 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java @@ -17,6 +17,7 @@ package com.intellij.uiDesigner.make; import com.intellij.compiler.PsiClassWriter; import com.intellij.compiler.impl.CompilerUtil; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.*; import com.intellij.openapi.diagnostic.Logger; @@ -50,7 +51,6 @@ import java.io.DataInput; import java.io.File; import java.io.IOException; import java.net.URL; -import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -71,7 +71,7 @@ public final class Form2ByteCodeCompiler implements ClassInstrumentingCompiler { } @NotNull - public static URLClassLoader createClassLoader(@NotNull final String classPath){ + public static InstrumentationClassFinder createClassFinder(@NotNull final String classPath){ final ArrayList urls = new ArrayList(); for (StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); tokenizer.hasMoreTokens();) { final String s = tokenizer.nextToken(); @@ -82,7 +82,7 @@ public final class Form2ByteCodeCompiler implements ClassInstrumentingCompiler { throw new RuntimeException(exc); } } - return new URLClassLoader(urls.toArray(new URL[urls.size()]), null); + return new InstrumentationClassFinder(urls.toArray(new URL[urls.size()])); } @Override @@ -274,81 +274,86 @@ public final class Form2ByteCodeCompiler implements ClassInstrumentingCompiler { List filesToRefresh = new ArrayList(); for (final Module module : module2itemsList.keySet()) { final String classPath = OrderEnumerator.orderEntries(module).recursively().getPathsList().getPathsString(); - final ClassLoader loader = createClassLoader(classPath); + final InstrumentationClassFinder finder = createClassFinder(classPath); - if (GuiDesignerConfiguration.getInstance(project).COPY_FORMS_RUNTIME_TO_OUTPUT) { - final String moduleOutputPath = CompilerPaths.getModuleOutputPath(module, false); - try { - if (moduleOutputPath != null) { - filesToRefresh.addAll(CopyResourcesUtil.copyFormsRuntime(moduleOutputPath, false)); + try { + if (GuiDesignerConfiguration.getInstance(project).COPY_FORMS_RUNTIME_TO_OUTPUT) { + final String moduleOutputPath = CompilerPaths.getModuleOutputPath(module, false); + try { + if (moduleOutputPath != null) { + filesToRefresh.addAll(CopyResourcesUtil.copyFormsRuntime(moduleOutputPath, false)); + } + final String testsOutputPath = CompilerPaths.getModuleOutputPath(module, true); + if (testsOutputPath != null && !testsOutputPath.equals(moduleOutputPath)) { + filesToRefresh.addAll(CopyResourcesUtil.copyFormsRuntime(testsOutputPath, false)); + } } - final String testsOutputPath = CompilerPaths.getModuleOutputPath(module, true); - if (testsOutputPath != null && !testsOutputPath.equals(moduleOutputPath)) { - filesToRefresh.addAll(CopyResourcesUtil.copyFormsRuntime(testsOutputPath, false)); + catch (IOException e) { + addMessage( + context, + UIDesignerBundle.message("error.cannot.copy.gui.designer.form.runtime", module.getName(), e.toString()), + null, CompilerMessageCategory.ERROR); } } - catch (IOException e) { - addMessage( - context, - UIDesignerBundle.message("error.cannot.copy.gui.designer.form.runtime", module.getName(), e.toString()), - null, CompilerMessageCategory.ERROR); + + final ArrayList list = module2itemsList.get(module); + + for (final MyInstrumentationItem item : list) { + //context.getProgressIndicator().setFraction((double)++formsProcessed / (double)items.length); + + final VirtualFile formFile = item.getFormFile(); + context.getProgressIndicator().setText2(formFile.getPresentableUrl()); + + final String text = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + if (!belongsToCompileScope(context, formFile, item.getClassToBindFQname())) { + return null; + } + Document document = FileDocumentManager.getInstance().getDocument(formFile); + return document == null ? null : document.getText(); + } + }); + if (text == null) { + continue; // does not belong to current scope + } + + final LwRootContainer rootContainer; + try { + rootContainer = Utils.getRootContainer(text, new CompiledClassPropertiesProvider(finder.getLoader())); + } + catch (Exception e) { + addMessage(context, UIDesignerBundle.message("error.cannot.process.form.file", e), formFile, CompilerMessageCategory.ERROR); + continue; + } + + final File classFile = VfsUtil.virtualToIoFile(item.getFile()); + LOG.assertTrue(classFile.exists(), classFile.getPath()); + + final AsmCodeGenerator codeGenerator = new AsmCodeGenerator( + rootContainer, finder, new PsiNestedFormLoader(module), false, new PsiClassWriter(module) + ); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + codeGenerator.patchFile(classFile); + } + }); + final FormErrorInfo[] errors = codeGenerator.getErrors(); + final FormErrorInfo[] warnings = codeGenerator.getWarnings(); + for (FormErrorInfo warning : warnings) { + addMessage(context, warning, formFile, CompilerMessageCategory.WARNING); + } + for (FormErrorInfo error : errors) { + addMessage(context, error, formFile, CompilerMessageCategory.ERROR); + } + if (errors.length == 0) { + compiledItems.add(item); + } } } - - final ArrayList list = module2itemsList.get(module); - - for (final MyInstrumentationItem item : list) { - //context.getProgressIndicator().setFraction((double)++formsProcessed / (double)items.length); - - final VirtualFile formFile = item.getFormFile(); - context.getProgressIndicator().setText2(formFile.getPresentableUrl()); - - final String text = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - if (!belongsToCompileScope(context, formFile, item.getClassToBindFQname())) { - return null; - } - Document document = FileDocumentManager.getInstance().getDocument(formFile); - return document == null ? null : document.getText(); - } - }); - if (text == null) { - continue; // does not belong to current scope - } - - final LwRootContainer rootContainer; - try { - rootContainer = Utils.getRootContainer(text, new CompiledClassPropertiesProvider(loader)); - } - catch (Exception e) { - addMessage(context, UIDesignerBundle.message("error.cannot.process.form.file", e), formFile, CompilerMessageCategory.ERROR); - continue; - } - - final File classFile = VfsUtil.virtualToIoFile(item.getFile()); - LOG.assertTrue(classFile.exists(), classFile.getPath()); - - final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, loader, - new PsiNestedFormLoader(module), false, - new PsiClassWriter(module)); - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - codeGenerator.patchFile(classFile); - } - }); - final FormErrorInfo[] errors = codeGenerator.getErrors(); - final FormErrorInfo[] warnings = codeGenerator.getWarnings(); - for (FormErrorInfo warning : warnings) { - addMessage(context, warning, formFile, CompilerMessageCategory.WARNING); - } - for (FormErrorInfo error : errors) { - addMessage(context, error, formFile, CompilerMessageCategory.ERROR); - } - if (errors.length == 0) { - compiledItems.add(item); - } + finally { + finder.releaseResources(); } } CompilerUtil.refreshIOFiles(filesToRefresh); diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/make/PreviewNestedFormLoader.java b/plugins/ui-designer/src/com/intellij/uiDesigner/make/PreviewNestedFormLoader.java index dbb2443c74a5..fbe4c8aaa86e 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/make/PreviewNestedFormLoader.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/make/PreviewNestedFormLoader.java @@ -17,6 +17,7 @@ package com.intellij.uiDesigner.make; import com.intellij.compiler.PsiClassWriter; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; import com.intellij.openapi.module.Module; import com.intellij.uiDesigner.actions.PreviewFormAction; import com.intellij.uiDesigner.compiler.AsmCodeGenerator; @@ -41,13 +42,13 @@ import java.util.Set; */ public class PreviewNestedFormLoader extends PsiNestedFormLoader { private final String myTempPath; - private final ClassLoader myLoader; + private final InstrumentationClassFinder myFinder; private final Set myGeneratedClasses = new HashSet(); - public PreviewNestedFormLoader(final Module module, final String tempPath, final ClassLoader loader) { + public PreviewNestedFormLoader(final Module module, final String tempPath, final InstrumentationClassFinder finder) { super(module); myTempPath = tempPath; - myLoader = loader; + myFinder = finder; } public LwRootContainer loadForm(String formFileName) throws Exception { @@ -82,7 +83,7 @@ public class PreviewNestedFormLoader extends PsiNestedFormLoader { cw.visitEnd(); ByteArrayInputStream bais = new ByteArrayInputStream(cw.toByteArray()); - AsmCodeGenerator acg = new AsmCodeGenerator(rootContainer, myLoader, this, true, new PsiClassWriter(myModule)); + AsmCodeGenerator acg = new AsmCodeGenerator(rootContainer, myFinder, this, true, new PsiClassWriter(myModule)); byte[] data = acg.patchClass(bais); FormErrorInfo[] errors = acg.getErrors(); if (errors.length > 0) { diff --git a/plugins/ui-designer/testSrc/com/intellij/uiDesigner/core/AsmCodeGeneratorTest.java b/plugins/ui-designer/testSrc/com/intellij/uiDesigner/core/AsmCodeGeneratorTest.java index 3bc39bccc584..d8ff9f5c68a6 100644 --- a/plugins/ui-designer/testSrc/com/intellij/uiDesigner/core/AsmCodeGeneratorTest.java +++ b/plugins/ui-designer/testSrc/com/intellij/uiDesigner/core/AsmCodeGeneratorTest.java @@ -15,14 +15,21 @@ */ package com.intellij.uiDesigner.core; +import com.intellij.compiler.instrumentation.InstrumentationClassFinder; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.PluginPathManager; +import com.intellij.openapi.util.SystemInfoRt; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.ui.components.JBTabbedPane; import com.intellij.uiDesigner.compiler.AsmCodeGenerator; import com.intellij.uiDesigner.compiler.FormErrorInfo; import com.intellij.uiDesigner.compiler.NestedFormLoader; import com.intellij.uiDesigner.compiler.Utils; import com.intellij.uiDesigner.lw.CompiledClassPropertiesProvider; import com.intellij.uiDesigner.lw.LwRootContainer; +import com.intellij.util.PathUtil; +import com.intellij.util.ui.UIUtil; import junit.framework.TestCase; import org.objectweb.asm.ClassWriter; @@ -32,7 +39,13 @@ import javax.swing.border.TitledBorder; import java.awt.*; import java.io.*; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -41,19 +54,46 @@ import java.util.Map; */ public class AsmCodeGeneratorTest extends TestCase { private MyNestedFormLoader myNestedFormLoader; - private MyClassLoader myClassLoader; + private MyClassFinder myClassFinder; @Override protected void setUp() throws Exception { super.setUp(); myNestedFormLoader = new MyNestedFormLoader(); - myClassLoader = new MyClassLoader(getClass().getClassLoader()); + + final String swingPath = PathUtil.getJarPathForClass(AbstractButton.class); + + java.util.List cp = new ArrayList(); + appendPath(cp, JBTabbedPane.class); + appendPath(cp, UIUtil.class); + appendPath(cp, SystemInfoRt.class); + appendPath(cp, ApplicationManager.class); + appendPath(cp, PathManager.getResourceRoot(this.getClass(), "/messages/UIBundle.properties")); + appendPath(cp, PathManager.getResourceRoot(this.getClass(), "/RuntimeBundle.properties")); + appendPath(cp, GridLayoutManager.class); // forms_rt + myClassFinder = new MyClassFinder( + new URL[] {new File(swingPath).toURI().toURL()}, + cp.toArray(new URL[cp.size()]) + ); + } + + private static void appendPath(Collection container, Class cls) throws MalformedURLException { + final String path = PathUtil.getJarPathForClass(cls); + appendPath(container, path); + } + + private static void appendPath(Collection container, String path) throws MalformedURLException { + container.add(new File(path).toURI().toURL()); } @Override protected void tearDown() throws Exception { - myClassLoader = null; myNestedFormLoader = null; + final MyClassFinder classFinder = myClassFinder; + if (classFinder != null) { + classFinder.releaseResources(); + myClassFinder = null; + } super.tearDown(); } @@ -70,7 +110,7 @@ public class AsmCodeGeneratorTest extends TestCase { String classPath = tmpPath + "/" + className + ".class"; final LwRootContainer rootContainer = loadFormData(formPath); - final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, myClassLoader, myNestedFormLoader, false, + final AsmCodeGenerator codeGenerator = new AsmCodeGenerator(rootContainer, myClassFinder, myNestedFormLoader, false, new ClassWriter(ClassWriter.COMPUTE_FRAMES)); final FileInputStream classStream = new FileInputStream(classPath); try { @@ -109,7 +149,8 @@ public class AsmCodeGeneratorTest extends TestCase { fos.close(); */ - return myClassLoader.doDefineClass(className, patchedData); + myClassFinder.addClassDefinition(className, patchedData); + return myClassFinder.getLoader().loadClass(className); } private static byte[] getVerifiedPatchedData(final AsmCodeGenerator codeGenerator) { @@ -168,10 +209,41 @@ public class AsmCodeGeneratorTest extends TestCase { public void testGridLayout() throws Exception { JComponent rootComponent = getInstrumentedRootComponent("TestGridConstraints.form", "BindingTest"); - assertTrue(rootComponent.getLayout() instanceof GridLayoutManager); - GridLayoutManager gridLayout = (GridLayoutManager) rootComponent.getLayout(); - assertEquals(1, gridLayout.getRowCount()); - assertEquals(1, gridLayout.getColumnCount()); + final LayoutManager layout = rootComponent.getLayout(); + assertTrue(isInstanceOf(layout, GridLayoutManager.class.getName())); + + + assertEquals(1, invokeMethod(layout, "getRowCount")); + assertEquals(1, invokeMethod(layout, "getColumnCount")); + } + + private static boolean isInstanceOf(Object object, final String className) throws ClassNotFoundException { + final Class ethalon = object.getClass().getClassLoader().loadClass(className); + return ethalon.isAssignableFrom(object.getClass()); + } + + private static Object invokeMethod(Object obj, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + return invokeMethod(obj, methodName, new Class[0], new Object[0]); + } + + private static Object invokeMethod(Object obj, String methodName, Class[] params, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + final Method method = findMethod(obj.getClass(), methodName, params); + return method.invoke(obj, args); + } + + private static Method findMethod(Class aClass, String methodName, Class[] params) { + try { + final Method method = aClass.getDeclaredMethod(methodName, params); + method.setAccessible(true); + return method; + } + catch (NoSuchMethodException ignored) { + } + final Class parent = aClass.getSuperclass(); + if (parent == null) { + return null; + } + return findMethod(parent, methodName, params); } public void testCardLayout() throws Exception { @@ -193,10 +265,14 @@ public class AsmCodeGeneratorTest extends TestCase { public void testGridConstraints() throws Exception { JComponent rootComponent = getInstrumentedRootComponent("TestGridConstraints.form", "BindingTest"); assertEquals(1, rootComponent.getComponentCount()); - GridLayoutManager gridLayout = (GridLayoutManager) rootComponent.getLayout(); - final GridConstraints constraints = gridLayout.getConstraints(0); - assertEquals(1, constraints.getColSpan()); - assertEquals(1, constraints.getRowSpan()); + final LayoutManager layout = rootComponent.getLayout(); + assertTrue(isInstanceOf(layout, GridLayoutManager.class.getName())); + + final Object constraints = invokeMethod(layout, "getConstraints", new Class[] {int.class}, new Object[] {0}); + assertTrue(isInstanceOf(constraints, GridConstraints.class.getName())); + + assertEquals(1, invokeMethod(constraints, "getColSpan")); + assertEquals(1, invokeMethod(constraints, "getRowSpan")); } public void testIntProperty() throws Exception { @@ -341,7 +417,7 @@ public class AsmCodeGeneratorTest extends TestCase { File.separatorChar + "formEmbedding" + File.separatorChar + "Ideadev14081" + File.separatorChar; AsmCodeGenerator embeddedClassGenerator = initCodeGenerator("Embedded.form", "Embedded", testDataPath); byte[] embeddedPatchedData = getVerifiedPatchedData(embeddedClassGenerator); - myClassLoader.doDefineClass("Embedded", embeddedPatchedData); + myClassFinder.addClassDefinition("Embedded", embeddedPatchedData); myNestedFormLoader.registerNestedForm("Embedded.form", testDataPath + "Embedded.form"); AsmCodeGenerator mainClassGenerator = initCodeGenerator("Main.form", "Main", testDataPath); byte[] mainPatchedData = getVerifiedPatchedData(mainClassGenerator); @@ -352,30 +428,34 @@ public class AsmCodeGeneratorTest extends TestCase { fos.close(); */ - final Class mainClass = myClassLoader.doDefineClass("Main", mainPatchedData); + myClassFinder.addClassDefinition("Main", mainPatchedData); + final Class mainClass = myClassFinder.getLoader().loadClass("Main"); Object instance = mainClass.newInstance(); assert instance != null : mainClass; } - private static class MyClassLoader extends ClassLoader { - private final byte[] myTestProperties = Charset.defaultCharset().encode(TEST_PROPERTY_CONTENT).array(); + private static class MyClassFinder extends InstrumentationClassFinder { private static final String TEST_PROPERTY_CONTENT = "test=Test Value\nmnemonic=Mne&monic"; + private final byte[] myTestProperties = Charset.defaultCharset().encode(TEST_PROPERTY_CONTENT).array(); + private final Map myClassData = new HashMap(); - public MyClassLoader(ClassLoader parent) { - super(parent); + private MyClassFinder(URL[] platformUrls, URL[] classpathUrls) { + super(platformUrls, classpathUrls); } - public Class doDefineClass(String name, byte[] data) { - return defineClass(name, data, 0, data.length); + public void addClassDefinition(String name, byte[] bytes) { + myClassData.put(name.replace('.', '/'), bytes); } - @Override - public Class loadClass(String name) throws ClassNotFoundException { - return super.loadClass(name); + protected InputStream lookupClassBeforeClasspath(String internalClassName) { + final byte[] bytes = myClassData.get(internalClassName); + if (bytes != null) { + return new ByteArrayInputStream(bytes); + } + return null; } - @Override - public InputStream getResourceAsStream(String name) { + public InputStream getResourceAsStream(String name) throws IOException { if (name.equals("TestProperties.properties")) { return new ByteArrayInputStream(myTestProperties, 0, TEST_PROPERTY_CONTENT.length()); }