001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.perftest;
043:
044: import java.io.File;
045: import java.util.Enumeration;
046: import java.util.HashSet;
047: import java.util.Set;
048: import java.util.jar.JarEntry;
049: import java.util.jar.JarFile;
050: import junit.framework.TestCase;
051: import junit.framework.*;
052: import com.sun.org.apache.bcel.internal.classfile.ClassParser;
053: import com.sun.org.apache.bcel.internal.classfile.DescendingVisitor;
054: import com.sun.org.apache.bcel.internal.classfile.EmptyVisitor;
055: import com.sun.org.apache.bcel.internal.classfile.Field;
056: import com.sun.org.apache.bcel.internal.classfile.JavaClass;
057: import com.sun.org.apache.bcel.internal.classfile.LineNumberTable;
058: import com.sun.org.apache.bcel.internal.classfile.LocalVariableTable;
059: import com.sun.org.apache.bcel.internal.classfile.Method;
060: import com.sun.org.apache.bcel.internal.generic.Type;
061: import java.lang.reflect.Constructor;
062: import java.util.ArrayList;
063: import java.util.LinkedList;
064: import java.util.List;
065: import java.util.Map;
066: import java.util.SortedMap;
067: import java.util.TreeMap;
068: import java.util.TreeSet;
069: import java.util.logging.Level;
070: import java.util.logging.Logger;
071: import org.netbeans.*;
072: import org.netbeans.junit.NbTestCase;
073: import org.netbeans.junit.NbTestSuite;
074: import org.openide.loaders.DataLoader;
075: import org.openide.loaders.DataLoaderPool;
076:
077: /**
078: *
079: * @author radim
080: */
081: public class BytecodeTest extends NbTestCase {
082:
083: private Logger LOG;
084:
085: public BytecodeTest(String testName) {
086: super (testName);
087: }
088:
089: public static Test suite() {
090: TestSuite suite = new NbTestSuite(BytecodeTest.class);
091:
092: return suite;
093: }
094:
095: @Override
096: protected Level logLevel() {
097: return Level.INFO;
098: }
099:
100: protected void setUp() throws Exception {
101: LOG = Logger.getLogger("TEST-" + getName());
102:
103: super .setUp();
104: }
105:
106: // TODO test for ModuleInstall subclasses - they should override at least one important method
107:
108: /** Verification that classfiles built in production build do not contain
109: * variable information to reduce their size and improve performance.
110: * Line table and source info are OK.
111: * Likely to fail for custom CVS unless they used -Dbuild.compiler.debuglevel=source,lines
112: */
113: public void testBytecode() throws Exception {
114: JavaClass clz = new ClassParser(Main.class
115: .getResourceAsStream("Main.class"), "Main.class")
116: .parse();
117: assertNotNull("classfile of Main parsed", clz);
118:
119: Set<Violation> violations = new HashSet<Violation>();
120: MyVisitor v = new MyVisitor();
121: new DescendingVisitor(clz, v).visit();
122: if (v.foundLocalVarTable()) {
123: violations.add(new Violation(Main.class.getName(),
124: "startup classpath", "local var table found"));
125: }
126: for (File f : org.netbeans.core.startup.Main.getModuleSystem()
127: .getModuleJars()) {
128: if (!f.getName().endsWith(".jar"))
129: continue;
130:
131: // list of 3rd party libs
132: // perhaps we can strip this debug info from these
133: if ("commons-logging-1.0.4.jar".equals(f.getName())
134: || "servlet-2.2.jar".equals(f.getName())
135: || "servlet2.5-jsp2.1-api.jar".equals(f.getName())
136: || "jaxws-tools.jar".equals(f.getName())
137: || "jaxws-rt.jar".equals(f.getName())
138: || "jaxb-impl.jar".equals(f.getName())
139: || "jaxb-xjc.jar".equals(f.getName())
140: || "jaxb-api.jar".equals(f.getName())
141: || "saaj-impl.jar".equals(f.getName())
142: || "activation.jar".equals(f.getName())
143: || "streambuffer.jar".equals(f.getName())
144: || "sjsxp.jar".equals(f.getName())
145: || "resolver-1_1_nb.jar".equals(f.getName())
146: || "webserver.jar".equals(f.getName())
147: || "swing-layout-1.0.2.jar".equals(f.getName())
148: || "beansbinding-0.5.jar".equals(f.getName())
149: || f.getName().matches("appframework-.*\\.jar")
150: // || "jmi.jar".equals(f.getName())
151: || "persistence-tool-support.jar".equals(f
152: .getName())
153: || "ini4j.jar".equals(f.getName())
154: || "svnClientAdapter.jar".equals(f.getName())
155: || "lucene-core-2.1.0.jar".equals(f.getName())
156: || "javac-impl-nb-7.0-b07.jar".equals(f.getName())
157: || "java-parser.jar".equals(f.getName()))
158: continue;
159:
160: // #97282 - profiler
161: if (f.getName().contains("profiler")) {
162: continue;
163: }
164:
165: JarFile jar = new JarFile(f);
166: Enumeration<JarEntry> entries = jar.entries();
167: JarEntry entry;
168: while (entries.hasMoreElements()) {
169: entry = entries.nextElement();
170: if (entry.getName().endsWith(".class")) {
171: LOG.log(Level.FINE, "testing entry {0}", entry);
172: clz = new ClassParser(jar.getInputStream(entry),
173: entry.getName()).parse();
174: assertNotNull("classfile of " + entry.toString()
175: + " parsed");
176:
177: v = new MyVisitor();
178: new DescendingVisitor(clz, v).visit();
179: if (v.foundLocalVarTable()) {
180: violations
181: .add(new Violation(entry.toString(),
182: jar.getName(),
183: "local var table found"));
184: }
185:
186: break;
187: }
188: }
189: }
190: if (!violations.isEmpty()) {
191: StringBuilder msg = new StringBuilder();
192: msg
193: .append("Some classes in IDE contain variable table information:\n");
194: for (Violation viol : violations) {
195: msg.append(viol.entry).append(" in ").append(
196: viol.jarFile).append('\n');
197: }
198: fail(msg.toString());
199: }
200: // assertTrue (entry.toString()+" should have line number table", v.foundLineNumberTable());
201: }
202:
203: private static class Violation implements Comparable<Violation> {
204: String entry;
205: String jarFile;
206: String comment;
207:
208: Violation(String entry, String jarFile, String comment) {
209: this .entry = entry;
210: this .jarFile = jarFile;
211: this .comment = comment;
212: }
213:
214: public int compareTo(Violation v2) {
215: String second = v2.entry + v2.jarFile;
216: return (entry + jarFile).compareTo(second);
217: }
218: }
219:
220: private static class MyVisitor extends EmptyVisitor {
221: private boolean localVarTable;
222: private boolean lineNumberTable;
223:
224: public void visitLocalVariableTable(LocalVariableTable obj) {
225: localVarTable = true;
226: }
227:
228: public boolean foundLocalVarTable() {
229: return localVarTable;
230: }
231:
232: public void visitLineNumberTable(LineNumberTable obj) {
233: lineNumberTable = true;
234: }
235:
236: public boolean foundLineNumberTable() {
237: return lineNumberTable;
238: }
239:
240: }
241:
242: private static class BIVisitor extends EmptyVisitor {
243:
244: private static Type pdType = Type
245: .getType("[Ljava/beans/PropertyDescriptor;");
246: private static Type bdType = Type
247: .getType("Ljava/beans/BeanDescriptor;");
248: private static Type mdType = Type
249: .getType("[Ljava/beans/MethodDescriptor;");
250: private static Type edType = Type
251: .getType("[Ljava/beans/EventSetDescriptor;");
252: private boolean hasDescFields;
253: private boolean hasStaticMethods;
254:
255: public void visitField(Field obj) {
256: if (obj.isStatic()) {
257: // System.out.println("signature "+obj.getSignature());
258: Type name = Type.getReturnType(obj.getSignature());
259: if (pdType.equals(name) || bdType.equals(name)
260: || mdType.equals(name) || edType.equals(name)) {
261: hasDescFields = true;
262: }
263: }
264: }
265:
266: public void visitMethod(Method obj) {
267: if (obj.isStatic()) { // && obj.getArgumentTypes().length == 0) {
268: String name = obj.getName();
269: if ("getBdescriptor".equals(name)
270: || "getMdescriptor".equals(name)
271: || "getEdescriptor".equals(name)
272: || "getPdescriptor".equals(name)) {
273: hasStaticMethods = true;
274: }
275: }
276: }
277:
278: public boolean foundDescFields() {
279: return hasDescFields;
280: }
281:
282: public boolean foundStaticMethods() {
283: return hasStaticMethods;
284: }
285: }
286:
287: private static class StaticsVisitor extends EmptyVisitor {
288:
289: private static Type imageType = Type
290: .getType("Ljava/awt/Image;");
291: private static Type image1Type = Type
292: .getType("Ljavax/swing/ImageIcon;");
293: private static Type image2Type = Type
294: .getType("Ljavax/swing/Icon;");
295: private static Type bType = Type
296: .getType("Ljava/util/ResourceBundle;");
297: private static Type b2Type = Type
298: .getType("Lorg/openide/util/NbBundle;");
299:
300: private boolean hasImageFields;
301: private boolean hasPropFields;
302:
303: @Override
304: public void visitField(Field obj) {
305: if (obj.isStatic()) {
306: // System.out.println("signature "+obj.getSignature());
307: Type name = Type.getReturnType(obj.getSignature());
308: if (imageType.equals(name) || image1Type.equals(name)
309: || image2Type.equals(name)) {
310: hasImageFields = true;
311: }
312: if (bType.equals(name) || b2Type.equals(name)) {
313: hasPropFields = true;
314: }
315: }
316: }
317:
318: public boolean foundStaticFields() {
319: return hasImageFields | hasPropFields;
320: }
321:
322: public String fieldTypes() {
323: if (hasImageFields) {
324: return hasPropFields ? "images and bundle resources"
325: : "images";
326: } else {
327: return hasPropFields ? "bundle resources" : "none";
328: }
329: }
330: }
331:
332: /** Scan of BeanInfo classes to check if they held descriptors statically
333: */
334: public void testBeanInfos() throws Exception {
335: JavaClass clz;
336:
337: Set<Violation> violations = new TreeSet<Violation>();
338: for (File f : org.netbeans.core.startup.Main.getModuleSystem()
339: .getModuleJars()) {
340: if (!f.getName().endsWith(".jar"))
341: continue;
342:
343: JarFile jar = new JarFile(f);
344: Enumeration<JarEntry> entries = jar.entries();
345: JarEntry entry;
346: while (entries.hasMoreElements()) {
347: entry = entries.nextElement();
348: if (entry.getName().endsWith("BeanInfo.class")) {
349: LOG.log(Level.FINE, "testing entry {0}", entry);
350: if (entry.getName().endsWith(
351: "JXPathBasicBeanInfo.class")) {
352: continue;
353: }
354:
355: clz = new ClassParser(jar.getInputStream(entry),
356: entry.getName()).parse();
357: assertNotNull("classfile of " + entry.toString()
358: + " parsed");
359:
360: BIVisitor v = new BIVisitor();
361: new DescendingVisitor(clz, v).visit();
362: if (v.foundDescFields()) {
363: violations
364: .add(new Violation(entry.toString(),
365: jar.getName(),
366: " found fields that should be avoided"));
367: }
368: if (v.foundStaticMethods()) {
369: violations
370: .add(new Violation(entry.toString(),
371: jar.getName(),
372: " found methods that should be avoided"));
373: }
374: }
375: }
376: }
377: if (!violations.isEmpty()) {
378: StringBuilder msg = new StringBuilder();
379: msg
380: .append("Some BeanInfo classes should be more optimized:\n");
381: for (Violation v : violations) {
382: msg.append(v.entry).append(" in ").append(v.jarFile)
383: .append(v.comment).append('\n');
384: }
385: fail(msg.toString());
386: }
387: }
388:
389: /** Scan of all classes to check if they held statically things like Images or ResourceBundles
390: */
391: public void testStaticRefs() throws Exception {
392: JavaClass clz;
393:
394: // TODO need to exclude some usages that are justified
395:
396: Set<Violation> violations = new TreeSet<Violation>();
397: for (File f : org.netbeans.core.startup.Main.getModuleSystem()
398: .getModuleJars()) {
399: if (!f.getName().endsWith(".jar"))
400: continue;
401:
402: if (f.getName().endsWith("servlet-2.2.jar")
403: || f.getName()
404: .endsWith("servlet2.5-jsp2.1-api.jar")
405: || f.getName().endsWith("javaee.jar")
406: || f.getName()
407: .endsWith("javac-impl-nb-7.0-b07.jar")
408: || f.getName().endsWith("jaxb-impl.jar")
409: || f.getName().endsWith("jaxb-xjc.jar")
410: || f.getName().endsWith("saaj-impl.jar")
411: || f.getName().endsWith("jh-2.0_05.jar")
412: || f.getName().endsWith("xerces-2.8.0.jar")
413: || f.getName().endsWith("svnClientAdapter.jar")
414: || f.getName().endsWith("beansbinding-0.5.jar")
415: || f.getName().endsWith(
416: "persistence-tool-support.jar") // issue #96439
417: || f.getName().endsWith(
418: "org-netbeans-modules-websvc-core.jar") // issue #96453
419: || f.getName().endsWith(
420: "org-netbeans-modules-websvc-jaxrpc.jar")
421: || f.getName().endsWith(
422: "org-netbeans-modules-websvc-design.jar") // issue #99971
423: || f.getName().endsWith(
424: "org-netbeans-modules-j2ee-sun-appsrv.jar") // issue #96439
425: || f
426: .getName()
427: .endsWith(
428: "org-netbeans-modules-j2ee-sun-appsrv81.jar")
429: || f
430: .getName()
431: .endsWith(
432: "org-netbeans-modules-j2ee-ejbjarproject.jar") // issue #96423
433: || f.getName().endsWith(
434: "org-netbeans-modules-j2ee-earproject.jar")
435: || f
436: .getName()
437: .endsWith(
438: "org-netbeans-modules-j2ee-clientproject.jar")
439: || f.getName().endsWith(
440: "org-netbeans-modules-j2ee-blueprints.jar")
441: || f.getName().endsWith(
442: "org-netbeans-modules-j2ee-archive.jar")
443: || f.getName().endsWith(
444: "org-netbeans-modules-j2ee-ddloaders.jar")
445: || f.getName().endsWith(
446: "org-netbeans-modules-j2ee-dd.jar")
447: || f
448: .getName()
449: .endsWith(
450: "org-netbeans-modules-j2ee-api-ejbmodule.jar")
451: || f.getName().endsWith(
452: "org-netbeans-modules-web-project.jar") // issue #96427
453: || f.getName().endsWith(
454: "org-netbeans-modules-web-core-syntax.jar")
455: || f.getName().endsWith(
456: "org-netbeans-modules-java-source.jar") // issue #96461
457: || f.getName().endsWith(
458: "org-netbeans-modules-java-project.jar")
459: || f
460: .getName()
461: .endsWith(
462: "org-netbeans-modules-java-j2seproject.jar")
463: || f.getName().endsWith(
464: "org-netbeans-modules-java-platform.jar")
465: || f.getName().endsWith(
466: "org-netbeans-modules-j2ee-sun-ddui.jar")) { // issue #96422
467: continue;
468: }
469: // #97283 - profiler
470: if (f.getName().contains("jfluid")
471: || f.getName().contains("profiler")) {
472: continue;
473: }
474: JarFile jar = new JarFile(f);
475: Enumeration<JarEntry> entries = jar.entries();
476: JarEntry entry;
477: while (entries.hasMoreElements()) {
478: entry = entries.nextElement();
479: if (entry.getName().endsWith(".class")) {
480: if ("org/openide/explorer/view/VisualizerNode.class"
481: .equals(entry.getName()) // default node icon si OK
482: || "org/openide/awt/JInlineMenu.class"
483: .equals(entry.getName()) // empty icon si OK
484: || "org/openide/awt/DynaMenuModel.class"
485: .equals(entry.getName()) // empty icon si OK
486: || "org/netbeans/swing/tabcontrol/TabData.class"
487: .equals(entry.getName()) // empty icon si OK
488: || "org/openide/explorer/propertysheet/PropertySheet.class"
489: .equals(entry.getName())) { // deprecated kept for compat
490: continue;
491: } else if (entry
492: .getName()
493: .startsWith(
494: "org/netbeans/modules/editor/java/JavaCompletionItem") // #96442
495: // || entry.getName().startsWith("org/netbeans/api/visual") // 99964
496: ) {
497: continue;
498: }
499:
500: LOG.log(Level.FINE, "testing entry {0}", entry);
501: clz = new ClassParser(jar.getInputStream(entry),
502: entry.getName()).parse();
503: assertNotNull("classfile of " + entry.toString()
504: + " parsed");
505:
506: StaticsVisitor v = new StaticsVisitor();
507: new DescendingVisitor(clz, v).visit();
508: if (v.foundStaticFields()) {
509: violations.add(new Violation(entry.toString(),
510: jar.getName(),
511: " has static fields of type "
512: + v.fieldTypes()));
513: }
514: }
515: }
516: }
517: if (!violations.isEmpty()) {
518: StringBuilder msg = new StringBuilder();
519: msg.append("Some classes retain memory permanently (")
520: .append(violations.size()).append("):\n");
521: for (Violation v : violations) {
522: msg.append(v.entry).append(v.comment).append(" (")
523: .append(v.jarFile).append(")\n");
524: }
525: fail(msg.toString());
526: }
527: }
528:
529: /** Check that we are not loading classes hungrily.
530: * DataLoader(String) is prefered to avoid loading of DataObject classes.
531: */
532: public void testDataLoaders() throws Exception {
533: Enumeration<DataLoader> loaders = DataLoaderPool.getDefault()
534: .allLoaders();
535: while (loaders.hasMoreElements()) {
536: DataLoader ldr = loaders.nextElement();
537: if ("org.netbeans.modules.cnd.loaders.CCDataLoader"
538: .equals(ldr.getClass().getName())) { // #97612
539: continue;
540: }
541:
542: try {
543: // XXX not enough better is to test that all ctors only call super(String)
544: Constructor ctor = ldr.getClass()
545: .getDeclaredConstructor(Class.class);
546: assertNull(
547: ldr.getClass().getName()
548: + ".<init>(String) is better are usualy enough",
549: ctor);
550: } catch (NoSuchMethodException ex) {
551: // expected path - OK
552: }
553: }
554: }
555:
556: public void testDuplicateClasses() throws Exception {
557: SortedMap<String, List<String>> res2jars = new TreeMap<String, List<String>>();
558:
559: Set<Violation> violations = new TreeSet<Violation>();
560: for (File f : org.netbeans.core.startup.Main.getModuleSystem()
561: .getModuleJars()) {
562: if (!f.getName().endsWith(".jar"))
563: continue;
564:
565: if (f.getName().endsWith("servlet-2.2.jar")
566: || f.getName()
567: .endsWith("servlet2.5-jsp2.1-api.jar")
568: || f.getName().endsWith("cdc-pp-awt-layout.jar")) // #105314
569: continue;
570:
571: if (f.getName().endsWith("tsalljlayoutclient601dev.jar") // #105628
572: || f.getName().endsWith(
573: "tsalljlayoutserver601dev.jar") // #105628
574: || f.getName().endsWith("lucene-core-2.1.0.jar") // #105329
575: || f.getName().endsWith("batik-mod.jar") // #100892
576: || f.getName().endsWith("batik-all.jar") // #100892
577: || f.getName().endsWith("JGo5.1.jar") // #105319
578: || f.getName().endsWith("JGoLayout5.1.jar") // #105319
579: || f.getName().endsWith("JGoInstruments5.1.jar")) // #105319
580: continue;
581:
582: JarFile jar = new JarFile(f);
583: Enumeration<JarEntry> entries = jar.entries();
584: JarEntry entry;
585: while (entries.hasMoreElements()) {
586: entry = entries.nextElement();
587: String name = entry.getName();
588: if (!name.endsWith(".class"))
589: continue;
590:
591: LOG.log(Level.FINE, "testing entry {0}", entry);
592: List<String> jars = res2jars.get(name);
593: if (jars == null) {
594: jars = new LinkedList<String>();
595: res2jars.put(name, jars);
596: }
597: if (!jars.contains(jar.getName())) { // avoid errors for multiply loaded JARs
598: jars.add(jar.getName());
599: }
600: }
601: }
602: boolean fail = false;
603: StringBuilder msg = new StringBuilder(
604: "There are some duplicated classes in IDE\n");
605: for (Map.Entry<String, List<String>> entry : res2jars
606: .entrySet()) {
607: if (entry.getValue().size() > 1) {
608: fail = true;
609: msg.append(entry.getKey()).append(" is contained in ")
610: .append(entry.getValue().size()).append(
611: " files: ").append(
612: entry.getValue().toString()).append(
613: '\n');
614: }
615: }
616: if (fail) {
617: fail(msg.toString());
618: }
619: }
620: }
|