001: /***
002: * Julia: France Telecom's implementation of the Fractal API
003: * Copyright (C) 2001-2002 France Telecom R&D
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: *
019: * Contact: Eric.Bruneton@rd.francetelecom.com
020: * Contact: Lionel.Seinturier@lifl.fr
021: *
022: * Author: Eric Bruneton
023: * Author: Lionel Seinturier
024: */package spoon.jdiet;
025:
026: import java.io.File;
027: import java.io.FileInputStream;
028: import java.io.InputStream;
029: import java.util.Enumeration;
030: import java.util.HashMap;
031: import java.util.HashSet;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.Set;
035: import java.util.zip.ZipEntry;
036: import java.util.zip.ZipFile;
037:
038: import org.apache.tools.ant.BuildException;
039: import org.apache.tools.ant.taskdefs.MatchingTask;
040: import org.objectweb.asm.AnnotationVisitor;
041: import org.objectweb.asm.Attribute;
042: import org.objectweb.asm.ClassAdapter;
043: import org.objectweb.asm.ClassReader;
044: import org.objectweb.asm.ClassVisitor;
045: import org.objectweb.asm.ClassWriter;
046: import org.objectweb.asm.FieldVisitor;
047: import org.objectweb.asm.Label;
048: import org.objectweb.asm.MethodAdapter;
049: import org.objectweb.asm.MethodVisitor;
050: import org.objectweb.asm.Opcodes;
051:
052: /**
053: * An Ant task for checking that some given bytecode files do not contain calls
054: * to an unsupported Java API. This task is used for example that a bytecode is
055: * compliant with the J2ME CLDC configuration.
056: *
057: * @author Eric Bruneton
058: * @author Lionel Seinturier <Lionel.Seinturier@lifl.fr>
059: */
060: public class ASMJDietVerifier extends MatchingTask {
061:
062: private File src;
063:
064: private File cldcJar;
065:
066: /**
067: * A set of strings describing the API supported by the cldc.jar library.
068: * The API includes public and protected methods and fields.
069: * Each string is of the form <classname>,<signature>.
070: * E.g. java/io/ByteArrayInputStream,read()I
071: */
072: // Set<String>
073: private Set cldcAPI;
074:
075: /**
076: * A map of the public and protected members (methods and fields) included in
077: * the cldc.jar library. The key is the class name (e.g.
078: * java/io/ByteArrayInputStream) and the value is the set of member signatures
079: * (e.g. read()I). This map is used to construct the cldcAPI set.
080: */
081: // Map<String,Set<String>>
082: private Map cldcMembers;
083:
084: /**
085: * A map of the super classes included in the cldc.jar library. The key is the
086: * class name and the value is a set of super classes.
087: */
088: // Map<String,Set<String>>
089: private Map cldcSupers;
090:
091: private int errors;
092:
093: public void setSrcdir(final File src) {
094: this .src = src;
095: }
096:
097: public void setCldcjar(final File cldcJar) {
098: this .cldcJar = cldcJar;
099: }
100:
101: /**
102: * Executes this task.
103: */
104:
105: public void execute() {
106: if (src == null) {
107: throw new BuildException("srcdir must be specified");
108: }
109:
110: try {
111: if (cldcJar != null) {
112: cldcAPI = new HashSet();
113: cldcMembers = new HashMap();
114: cldcSupers = new HashMap();
115:
116: ZipFile zf = new ZipFile(cldcJar);
117: Enumeration e = zf.entries();
118: while (e.hasMoreElements()) {
119: ZipEntry ze = (ZipEntry) e.nextElement();
120: if (ze.getName().endsWith(".class")) {
121: new ClassReader(zf.getInputStream(ze)).accept(
122: new ClassAnalyzer(), true);
123: }
124: }
125:
126: /*
127: * Compute the transitive closure of the inheritance tree.
128: */
129: // Iterate over classes
130: for (Iterator iter = cldcSupers.keySet().iterator(); iter
131: .hasNext();) {
132:
133: String clName = (String) iter.next();
134: Set super Names = (Set) cldcSupers.get(clName);
135:
136: int oldSize, newSize;
137: do {
138: Set closure = new HashSet();
139:
140: // Iterate over super classes
141: for (Iterator iterator2 = super Names.iterator(); iterator2
142: .hasNext();) {
143: String super Name = (String) iterator2
144: .next();
145:
146: Set ssuper s = (Set) cldcSupers
147: .get(super Name);
148: if (ssuper s != null) {
149: closure.addAll(ssuper s);
150: }
151: }
152:
153: oldSize = super Names.size();
154: super Names.addAll(closure);
155: newSize = super Names.size();
156: } while (oldSize != newSize);
157: }
158:
159: /*
160: * Add signatures for inherited members.
161: */
162: // Iterate over classes
163: for (Iterator iter = cldcSupers.keySet().iterator(); iter
164: .hasNext();) {
165:
166: String clName = (String) iter.next();
167: Set super Names = (Set) cldcSupers.get(clName);
168:
169: // Iterate over super classes
170: for (Iterator iterator2 = super Names.iterator(); iterator2
171: .hasNext();) {
172: String super Name = (String) iterator2.next();
173:
174: // members is null for java/lang/Object
175: Set members = (Set) cldcMembers.get(super Name);
176: if (members != null) {
177:
178: // Iterate over members of super classes
179: for (Iterator iterator3 = members
180: .iterator(); iterator3.hasNext();) {
181: String member = (String) iterator3
182: .next();
183:
184: // Add inherited members in the API
185: cldcAPI.add(clName + "," + member);
186: }
187: }
188: }
189: }
190: }
191: } catch (Exception e) {
192: log(e.getMessage());
193: }
194:
195: int total = 0;
196: String[] files = getDirectoryScanner(src).getIncludedFiles();
197: for (int i = 0; i < files.length; ++i) {
198: File srcFile = new File(src, files[i]);
199:
200: ClassReader cr;
201: try {
202: InputStream is = new FileInputStream(srcFile);
203: cr = new ClassReader(is);
204: } catch (Exception e) {
205: log(e.getMessage());
206: continue;
207: }
208:
209: ClassWriter cw = new ClassWriter(false);
210: ClassVerifier converter = new ClassVerifier(cw);
211: cr.accept(converter, true);
212:
213: ++total;
214: }
215:
216: if (errors > 0) {
217: throw new BuildException(errors + " errors");
218: } else if (total > 0) {
219: if (total == 1) {
220: log("1 class scanned");
221: } else {
222: log(total + " classes scanned");
223: }
224: }
225: }
226:
227: /**
228: * @author Eric Bruneton
229: * @author Lionel Seinturier <Lionel.Seinturier@lifl.fr>
230: */
231: class ClassAnalyzer implements ClassVisitor, Opcodes {
232:
233: private String name;
234:
235: public void visit(final int version, final int access,
236: final String name, final String signature,
237: final String super Name, final String[] interfaces) {
238: this .name = name;
239:
240: Set super s = (Set) cldcSupers.get(name);
241: if (super s == null) {
242: super s = new HashSet();
243: cldcSupers.put(name, super s);
244: }
245: super s.add(super Name);
246: }
247:
248: public FieldVisitor visitField(int access, String name,
249: String desc, String signature, Object value) {
250: FieldVisitor fv = null;
251: if ((access & (ACC_PUBLIC | ACC_PROTECTED)) != 0) {
252: cldcAPI.add(this .name + "," + name);
253: addMember(this .name, name);
254: }
255: return fv;
256: }
257:
258: private void addMember(String clName, String memberName) {
259: Set members = (Set) cldcMembers.get(clName);
260: if (members == null) {
261: members = new HashSet();
262: cldcMembers.put(clName, members);
263: }
264: members.add(memberName);
265: }
266:
267: public MethodVisitor visitMethod(final int access,
268: final String name, final String desc,
269: final String signature, final String[] exceptions) {
270: if ((access & (ACC_PUBLIC | ACC_PROTECTED)) != 0) {
271: cldcAPI.add(this .name + "," + name + desc);
272: addMember(this .name, name + desc);
273: }
274: MethodVisitor mv = null;
275: return mv;
276: }
277:
278: public void visitInnerClass(final String name,
279: final String outerName, final String innerName,
280: final int access) {
281: }
282:
283: public void visitEnd() {
284: }
285:
286: public void visitSource(String source, String debug) {
287: }
288:
289: public void visitOuterClass(String owner, String name,
290: String desc) {
291: }
292:
293: public AnnotationVisitor visitAnnotation(String desc,
294: boolean visible) {
295: return null;
296: }
297:
298: public void visitAttribute(Attribute attr) {
299: }
300: }
301:
302: /**
303: * @author Eric Bruneton
304: * @author Lionel Seinturier <Lionel.Seinturier@lifl.fr>
305: */
306: class ClassVerifier extends ClassAdapter implements Opcodes {
307:
308: private String name;
309:
310: public ClassVerifier(ClassVisitor cv) {
311: super (cv);
312: }
313:
314: public void visit(final int version, final int access,
315: final String name, final String signature,
316: final String super Name, final String[] interfaces) {
317: this .name = name;
318:
319: for (int i = 0; i < interfaces.length; i++) {
320: if (interfaces[i].startsWith("java/")) {
321: if (!cldcMembers.keySet().contains(interfaces[i])) {
322: log(name + ": must not use " + interfaces[i]
323: + "(not in CLDC API)");
324: ++errors;
325: }
326: }
327: }
328:
329: cv.visit(version, access, name, signature, super Name,
330: interfaces);
331: }
332:
333: public FieldVisitor visitField(int access, String name,
334: String desc, String signature, Object value) {
335: return cv.visitField(access, name, desc, signature, value);
336: }
337:
338: public MethodVisitor visitMethod(final int access,
339: final String name, final String desc,
340: final String signature, final String[] exceptions) {
341: return new CodeVerifier(this , cv.visitMethod(access, name,
342: desc, signature, exceptions));
343: }
344:
345: }
346:
347: class CodeVerifier extends MethodAdapter implements Opcodes {
348:
349: private ClassVerifier c;
350: private String lastMethodCallInsn;
351: private boolean mustNotUseResult;
352:
353: public CodeVerifier(ClassVerifier c, MethodVisitor mv) {
354: super (mv);
355: this .c = c;
356: }
357:
358: private void check(int opcode) {
359: if (mustNotUseResult && opcode != POP) {
360: log(c.name + ": must not use result of "
361: + lastMethodCallInsn);
362: ++errors;
363: }
364: mustNotUseResult = false;
365: }
366:
367: public void visitInsn(final int opcode) {
368: check(opcode);
369: mv.visitInsn(opcode);
370: }
371:
372: public void visitIntInsn(final int opcode, final int operand) {
373: check(opcode);
374: mv.visitIntInsn(opcode, operand);
375: }
376:
377: public void visitVarInsn(final int opcode, final int var) {
378: check(opcode);
379: mv.visitVarInsn(opcode, var);
380: }
381:
382: public void visitTypeInsn(final int opcode, final String desc) {
383: check(opcode);
384: mv.visitTypeInsn(opcode, desc);
385: }
386:
387: public void visitFieldInsn(final int opcode,
388: final String owner, final String name, final String desc) {
389: check(opcode);
390: if (cldcAPI != null && owner.startsWith("java")) {
391: String key = owner + "," + name;
392: if (!cldcAPI.contains(key)) {
393: log(c.name + ": must not use " + key
394: + "(not in CLDC API)");
395: ++errors;
396: }
397: }
398: mv.visitFieldInsn(opcode, owner, name, desc);
399: }
400:
401: public void visitMethodInsn(final int opcode,
402: final String owner, final String name, final String desc) {
403: check(opcode);
404: String key = owner + "," + name + desc;
405: if (cldcAPI != null && owner.startsWith("java")) {
406: if (!cldcAPI.contains(key)) {
407: log(c.name + ": must not use " + key
408: + " (not in CLDC API)");
409: ++errors;
410: }
411: }
412: mv.visitMethodInsn(opcode, owner, name, desc);
413: }
414:
415: public void visitJumpInsn(final int opcode, final Label label) {
416: check(opcode);
417: mv.visitJumpInsn(opcode, label);
418: }
419:
420: public void visitLdcInsn(final Object cst) {
421: check(LDC);
422: mv.visitLdcInsn(cst);
423: }
424:
425: public void visitIincInsn(final int var, final int increment) {
426: check(IINC);
427: mv.visitIincInsn(var, increment);
428: }
429:
430: public void visitTableSwitchInsn(final int min, final int max,
431: final Label dflt, final Label labels[]) {
432: check(TABLESWITCH);
433: mv.visitTableSwitchInsn(min, max, dflt, labels);
434: }
435:
436: public void visitLookupSwitchInsn(final Label dflt,
437: final int keys[], final Label labels[]) {
438: check(LOOKUPSWITCH);
439: mv.visitLookupSwitchInsn(dflt, keys, labels);
440: }
441:
442: public void visitMultiANewArrayInsn(final String desc,
443: final int dims) {
444: check(MULTIANEWARRAY);
445: mv.visitMultiANewArrayInsn(desc, dims);
446: }
447: }
448: }
|