1 23 24 package org.objectweb.fractal.julia.ant; 25 26 import org.apache.tools.ant.BuildException; 27 import org.apache.tools.ant.taskdefs.MatchingTask; 28 import org.objectweb.asm.ClassReader; 29 import org.objectweb.asm.ClassVisitor; 30 import org.objectweb.asm.CodeVisitor; 31 import org.objectweb.asm.Attribute; 32 import org.objectweb.asm.ClassWriter; 33 import org.objectweb.asm.ClassAdapter; 34 import org.objectweb.asm.CodeAdapter; 35 import org.objectweb.asm.Constants; 36 import org.objectweb.asm.Label; 37 38 import java.io.File ; 39 import java.io.FileInputStream ; 40 import java.io.InputStream ; 41 import java.io.OutputStream ; 42 import java.io.FileOutputStream ; 43 import java.util.HashMap ; 44 import java.util.Map ; 45 import java.util.Arrays ; 46 import java.util.List ; 47 import java.util.ArrayList ; 48 import java.util.Enumeration ; 49 import java.util.Set ; 50 import java.util.HashSet ; 51 import java.util.zip.ZipFile ; 52 import java.util.zip.ZipEntry ; 53 54 public class J2MEConverter extends MatchingTask { 55 56 private File src; 57 58 private File dst; 59 60 private File cldcJar; 61 62 private Set cldcAPI; 63 64 private int errors; 65 66 private final static String [] METHODS = new String [] { 67 "java/util/List,size()I", 68 "java/util/List,get(I)Ljava/lang/Object;", 69 "java/util/List,contains(Ljava/lang/Object;)Z", 70 "java/util/List,set(ILjava/lang/Object;)Ljava/lang/Object;", 71 "java/util/List,add(Ljava/lang/Object;)Z", 72 "java/util/List,add(ILjava/lang/Object;)V", 73 "java/util/List,remove(Ljava/lang/Object;)Z", 74 "java/util/List,remove(I)Ljava/lang/Object;", 75 "java/util/List,toArray()[Ljava/lang/Object;", 76 "java/util/List,toArray([Ljava/lang/Object;)[Ljava/lang/Object;", 77 "java/util/Set,size()I", 78 "java/util/Set,contains(Ljava/lang/Object;)Z", 79 "java/util/Set,add(Ljava/lang/Object;)Z", 80 "java/util/Set,toArray()[Ljava/lang/Object;", 81 "java/util/Set,toArray([Ljava/lang/Object;)[Ljava/lang/Object;", 82 "java/util/Map,size()I", 83 "java/util/Map,get(Ljava/lang/Object;)Ljava/lang/Object;", 84 "java/util/Map,put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 85 "java/util/Map,remove(Ljava/lang/Object;)Ljava/lang/Object;", 86 "java/util/Map,keySet()Ljava/util/Set;", 87 "java/util/ArrayList,<init>()V", 88 "java/util/HashSet,<init>()V", 89 "java/util/HashMap,<init>()V", 90 }; 91 92 private final static Map INDEXES = new HashMap (); 93 94 static { 95 for (int i = 0; i < METHODS.length; ++i) { 96 INDEXES.put(METHODS[i], new Integer (i)); 97 } 98 } 99 100 public void setSrcdir (final File src) { 101 this.src = src; 102 } 103 104 public void setDestdir (final File dst) { 105 this.dst = dst; 106 } 107 108 public void setCldcjar (final File cldcJar) { 109 this.cldcJar = cldcJar; 110 } 111 112 115 116 public void execute () { 117 if (src == null) { 118 throw new BuildException("srcdir must be specified"); 119 } 120 if (dst == null) { 121 throw new BuildException("destdir must be specified"); 122 } 123 124 try { 125 if (cldcJar != null) { 126 cldcAPI = new HashSet (); 127 ZipFile zf = new ZipFile (cldcJar); 128 Enumeration e = zf.entries(); 129 while (e.hasMoreElements()) { 130 ZipEntry ze = (ZipEntry )e.nextElement(); 131 if (ze.getName().endsWith(".class")) { 132 new ClassReader(zf.getInputStream(ze)).accept(new ClassAnalyzer(), true); 133 } 134 } 135 } 136 } catch (Exception e) { 137 log(e.getMessage()); 138 } 139 140 int total = 0; 141 String [] files = getDirectoryScanner(src).getIncludedFiles(); 142 for (int i = 0; i < files.length; ++i) { 143 File srcFile = new File (src, files[i]); 144 File dstFile = new File (dst, files[i]); 145 if (dstFile.exists() && dstFile.lastModified() > srcFile.lastModified()) { 146 continue; 147 } 148 149 ClassReader cr; 150 try { 151 InputStream is = new FileInputStream (srcFile); 152 cr = new ClassReader(is); 153 } catch (Exception e) { 154 log(e.getMessage()); 155 continue; 156 } 157 158 ClassWriter cw = new ClassWriter(false); 159 ClassConverter converter = new ClassConverter(cw); 160 cr.accept(converter, true); 161 162 try { 163 dstFile.getParentFile().mkdirs(); 164 OutputStream os = new FileOutputStream (dstFile); 165 os.write(cw.toByteArray()); 166 os.close(); 167 } catch (Exception e) { 168 log(e.getMessage()); 169 continue; 170 } 171 ++total; 172 } 173 174 if (errors > 0) { 175 throw new BuildException(); 176 } else if (total > 0) { 177 if (total == 1) { 178 log("1 class transformed into " + dst); 179 } else { 180 log(total + " classes transformed into " + dst); 181 } 182 } 183 } 184 185 static String convertDescriptor (final String desc) { 186 if (desc.equals("java/util/ArrayList")) { 187 return "java/util/Vector"; 188 } else if (desc.equals("java/util/HashSet")) { 189 return "java/util/Hashtable"; 190 } else if (desc.equals("java/util/HashMap")) { 191 return "java/util/Hashtable"; 192 } 193 boolean done; 194 String newDesc = desc; 195 do { 196 done = true; 197 int i = newDesc.indexOf("java/util/List"); 198 if (i != -1) { 199 newDesc = newDesc.substring(0, i) 200 + "java/util/Vector" 201 + newDesc.substring(i + 14); 202 done = false; 203 } 204 i = newDesc.indexOf("java/util/Set"); 205 if (i != -1) { 206 newDesc = newDesc.substring(0, i) 207 + "java/util/Hashtable" 208 + newDesc.substring(i + 13); 209 done = false; 210 } 211 i = newDesc.indexOf("java/util/Map"); 212 if (i != -1) { 213 newDesc = newDesc.substring(0, i) 214 + "java/util/Hashtable" 215 + newDesc.substring(i + 13); 216 done = false; 217 } 218 } while (!done); 219 return newDesc; 220 } 221 222 class ClassAnalyzer implements ClassVisitor, Constants { 223 224 private String name; 225 226 public void visit ( 227 final int version, 228 final int access, 229 final String name, 230 final String superName, 231 final String [] interfaces, 232 final String sourceFile) 233 { 234 this.name = name; 235 } 236 237 public void visitField ( 238 int access, 239 String name, 240 String desc, 241 Object value, 242 Attribute attrs) 243 { 244 if ((access & (ACC_PUBLIC | ACC_PROTECTED)) != 0) { 245 cldcAPI.add(this.name+","+name); 246 } 247 } 248 249 public CodeVisitor visitMethod ( 250 final int access, 251 final String name, 252 final String desc, 253 final String [] exceptions, 254 final Attribute attrs) 255 { 256 if ((access & (ACC_PUBLIC | ACC_PROTECTED)) != 0) { 257 cldcAPI.add(this.name+","+name+desc); 258 } 259 return null; 260 } 261 262 public void visitInnerClass ( 263 final String name, 264 final String outerName, 265 final String innerName, 266 final int access) 267 { 268 } 269 270 public void visitAttribute (final Attribute attribute) { 271 } 272 273 public void visitEnd () { 274 } 275 } 276 277 class ClassConverter extends ClassAdapter implements Constants { 278 279 private String name; 280 private boolean hasVectorToArray; 281 private boolean hasHashtableToArray; 282 283 public ClassConverter (ClassVisitor cv) { 284 super(cv); 285 } 286 287 public void visit ( 288 final int version, 289 final int access, 290 final String name, 291 final String superName, 292 final String [] interfaces, 293 final String sourceFile) 294 { 295 this.name = name; 296 List itfList = new ArrayList (Arrays.asList(interfaces)); 297 itfList.remove("java/io/Serializable"); 298 String [] itfs = (String [])itfList.toArray(new String [itfList.size()]); 299 cv.visit(version, access, name, superName, itfs, sourceFile); 300 } 301 302 public void visitField ( 303 int access, 304 String name, 305 String desc, 306 Object value, 307 Attribute attrs) 308 { 309 cv.visitField(access, name, convertDescriptor(desc), value, attrs); 310 } 311 312 public CodeVisitor visitMethod ( 313 final int access, 314 final String name, 315 final String desc, 316 final String [] exceptions, 317 final Attribute attrs) 318 { 319 if (name.equals("writeObject") && desc.equals("(Ljava/io/ObjectOutputStream;)V")) { 320 return null; 321 } 322 if (name.equals("readObject") && desc.equals("(Ljava/io/ObjectInputStream;)V")) { 323 return null; 324 } 325 if (name.equals("printStackTrace") && !desc.equals("()V")) { 326 return null; 327 } 328 if (name.equals("getInputStream")) { 329 CodeVisitor c = cv.visitMethod(access, name, desc, exceptions, attrs); 330 c.visitTypeInsn(NEW, "java/io/IOException"); 331 c.visitInsn(DUP); 332 c.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "()V"); 333 c.visitInsn(ATHROW); 334 c.visitMaxs(2, 2); 335 return null; 336 } 337 if (name.equals("_forName")) { 338 CodeVisitor c = cv.visitMethod(access, name, desc, exceptions, attrs); 339 c.visitVarInsn(ALOAD, 1); 340 c.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", desc); 341 c.visitInsn(ARETURN); 342 c.visitMaxs(2, 2); 343 return null; 344 } 345 String newDesc = convertDescriptor(desc); 346 return new CodeConverter( 347 this, cv.visitMethod(access, name, newDesc, exceptions, attrs)); 348 } 349 350 public void generateVectorToArrayMethod () { 351 if (hasVectorToArray) { 352 return; 353 } else { 354 hasVectorToArray = true; 355 } 356 CodeVisitor c = cv.visitMethod(ACC_STATIC, "toArray", "(Ljava/util/Vector;[Ljava/lang/Object;)[Ljava/lang/Object;", null, null); 357 c.visitInsn(ICONST_0); 358 c.visitVarInsn(ISTORE, 2); 359 Label l0 = new Label(); 360 c.visitJumpInsn(GOTO, l0); 361 Label l1 = new Label(); 362 c.visitLabel(l1); 363 c.visitVarInsn(ALOAD, 1); 364 c.visitVarInsn(ILOAD, 2); 365 c.visitVarInsn(ALOAD, 0); 366 c.visitVarInsn(ILOAD, 2); 367 c.visitMethodInsn(INVOKEVIRTUAL, "java/util/Vector", "elementAt", "(I)Ljava/lang/Object;"); 368 c.visitInsn(AASTORE); 369 c.visitIincInsn(2, 1); 370 c.visitLabel(l0); 371 c.visitVarInsn(ILOAD, 2); 372 c.visitVarInsn(ALOAD, 1); 373 c.visitInsn(ARRAYLENGTH); 374 c.visitJumpInsn(IF_ICMPLT, l1); 375 c.visitVarInsn(ALOAD, 1); 376 c.visitInsn(ARETURN); 377 c.visitMaxs(4, 3); 378 } 379 380 public void generateHashtableToArrayMethod () { 381 if (hasHashtableToArray) { 382 return; 383 } else { 384 hasHashtableToArray = true; 385 } 386 CodeVisitor c = cv.visitMethod(ACC_STATIC, "toArray", "(Ljava/util/Hashtable;[Ljava/lang/Object;)[Ljava/lang/Object;", null, null); 387 c.visitInsn(ICONST_0); 388 c.visitVarInsn(ISTORE, 2); 389 c.visitVarInsn(ALOAD, 0); 390 c.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", "keys", "()Ljava/util/Enumeration;"); 391 c.visitVarInsn(ASTORE, 3); 392 Label l0 = new Label(); 393 c.visitJumpInsn(GOTO, l0); 394 Label l1 = new Label(); 395 c.visitLabel(l1); 396 c.visitVarInsn(ALOAD, 1); 397 c.visitVarInsn(ILOAD, 2); 398 c.visitIincInsn(2, 1); 399 c.visitVarInsn(ALOAD, 3); 400 c.visitMethodInsn(INVOKEINTERFACE, "java/util/Enumeration", "nextElement", "()Ljava/lang/Object;"); 401 c.visitInsn(AASTORE); 402 c.visitLabel(l0); 403 c.visitVarInsn(ALOAD, 3); 404 c.visitMethodInsn(INVOKEINTERFACE, "java/util/Enumeration", "hasMoreElements", "()Z"); 405 c.visitJumpInsn(IFNE, l1); 406 c.visitVarInsn(ALOAD, 1); 407 c.visitInsn(ARETURN); 408 c.visitMaxs(3, 4); 409 } 410 } 411 412 class CodeConverter extends CodeAdapter implements Constants { 413 414 ClassConverter c; 415 String lastMethodCallInsn; 416 boolean mustNotUseResult; 417 418 public CodeConverter (ClassConverter c, CodeVisitor cv) { 419 super(cv); 420 this.c = c; 421 } 422 423 private void check (int opcode) { 424 if (mustNotUseResult && opcode != POP) { 425 log(c.name + ": must not use result of " + lastMethodCallInsn); 426 ++errors; 427 } 428 mustNotUseResult = false; 429 } 430 431 public void visitInsn (final int opcode) { 432 check(opcode); 433 cv.visitInsn(opcode); 434 } 435 436 public void visitIntInsn (final int opcode, final int operand) { 437 check(opcode); 438 cv.visitIntInsn(opcode, operand); 439 } 440 441 public void visitVarInsn (final int opcode, final int var) { 442 check(opcode); 443 cv.visitVarInsn(opcode, var); 444 } 445 446 public void visitTypeInsn (final int opcode, final String desc) { 447 check(opcode); 448 cv.visitTypeInsn(opcode, convertDescriptor(desc)); 449 } 450 451 public void visitFieldInsn ( 452 final int opcode, 453 final String owner, 454 final String name, 455 final String desc) 456 { 457 check(opcode); 458 if (cldcAPI != null && owner.startsWith("java")) { 459 String key = owner+","+name; 460 if (!cldcAPI.contains(key)) { 461 log(c.name + ": must not use " + key + "(not in CLDC API)"); 462 ++errors; 463 } 464 } 465 cv.visitFieldInsn(opcode, owner, name, convertDescriptor(desc)); 466 } 467 468 public void visitMethodInsn ( 469 final int opcode, 470 final String owner, 471 final String name, 472 final String desc) 473 { 474 check(opcode); 475 String key = owner + "," + name + desc; 476 if (cldcAPI != null && owner.startsWith("java")) { 477 if (!cldcAPI.contains(key) && INDEXES.get(key) == null) { 478 log(c.name + ": must not use " + key + " (not in CLDC API)"); 479 ++errors; 480 } 481 } 482 String newDesc = convertDescriptor(desc); 483 if (owner.startsWith("java/util") 484 && !owner.equals("java/util/Vector") 485 && !owner.equals("java/util/Hashtable") 486 && !owner.equals("java/util/Enumeration")) 487 { 488 Integer id = (Integer )INDEXES.get(key); 489 if (id == null) { 490 log(c.name + ": unauthorized method " + key); 491 ++errors; 492 } 493 lastMethodCallInsn = key; 494 switch (id.intValue()) { 495 case 0: cv.visitMethodInsn( 497 INVOKEVIRTUAL, "java/util/Vector", name, desc); 498 break; 499 case 1: cv.visitMethodInsn( 501 INVOKEVIRTUAL, "java/util/Vector", name, desc); 502 break; 503 case 2: cv.visitMethodInsn( 505 INVOKEVIRTUAL, "java/util/Vector", name, desc); 506 break; 507 case 3: mustNotUseResult = true; 509 cv.visitInsn(SWAP); 510 cv.visitMethodInsn( 511 INVOKEVIRTUAL, "java/util/Vector", "setElementAt", "(Ljava/lang/Object;I)V"); 512 cv.visitInsn(ACONST_NULL); 513 break; 514 case 4: mustNotUseResult = true; 516 cv.visitMethodInsn( 517 INVOKEVIRTUAL, "java/util/Vector", "addElement", "(Ljava/lang/Object;)V"); 518 cv.visitInsn(ICONST_0); 519 break; 520 case 5: cv.visitInsn(SWAP); 522 cv.visitMethodInsn( 523 INVOKEVIRTUAL, "java/util/Vector", "insertElementAt", "(Ljava/lang/Object;I)V"); 524 break; 525 case 6: cv.visitMethodInsn( 527 INVOKEVIRTUAL, "java/util/Vector", "removeElement", desc); 528 break; 529 case 7: mustNotUseResult = true; 531 cv.visitMethodInsn( 532 INVOKEVIRTUAL, "java/util/Vector", "removeElementAt", "(I)V"); 533 cv.visitInsn(ACONST_NULL); 534 break; 535 case 8: cv.visitInsn(DUP); 537 cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Vector", "size", "()I"); 538 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 539 c.generateVectorToArrayMethod(); 540 cv.visitMethodInsn(INVOKESTATIC, c.name, name, "(Ljava/util/Vector;[Ljava/lang/Object;)[Ljava/lang/Object;"); 541 break; 542 case 9: c.generateVectorToArrayMethod(); 544 cv.visitMethodInsn(INVOKESTATIC, c.name, name, "(Ljava/util/Vector;[Ljava/lang/Object;)[Ljava/lang/Object;"); 545 break; 546 case 10: cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", name, desc); 548 break; 549 case 11: cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", "containsKey", desc); 551 break; 552 case 12: mustNotUseResult = true; 554 cv.visitInsn(DUP); 555 cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 556 cv.visitInsn(POP); 557 cv.visitInsn(ICONST_0); 558 break; 559 case 13: cv.visitInsn(DUP); 561 cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", "size", "()I"); 562 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 563 c.generateHashtableToArrayMethod(); 564 cv.visitMethodInsn(INVOKESTATIC, c.name, name, "(Ljava/util/Hashtable;[Ljava/lang/Object;)[Ljava/lang/Object;"); 565 break; 566 case 14: c.generateHashtableToArrayMethod(); 568 cv.visitMethodInsn(INVOKESTATIC, c.name, name, "(Ljava/util/Hashtable;[Ljava/lang/Object;)[Ljava/lang/Object;"); 569 break; 570 case 15: cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", name, desc); 572 break; 573 case 16: cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", name, desc); 575 break; 576 case 17: cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", name, desc); 578 break; 579 case 18: cv.visitMethodInsn(INVOKEVIRTUAL, "java/util/Hashtable", name, desc); 581 break; 582 case 19: break; 585 case 20: cv.visitMethodInsn(opcode, "java/util/Vector", name, desc); 587 break; 588 case 21: cv.visitMethodInsn(opcode, "java/util/Hashtable", name, desc); 590 break; 591 case 22: cv.visitMethodInsn(opcode, "java/util/Hashtable", name, desc); 593 break; 594 default: 595 throw new RuntimeException ("Internal error"); } 597 } else { 598 cv.visitMethodInsn(opcode, owner, name, newDesc); 599 } 600 } 601 602 public void visitJumpInsn (final int opcode, final Label label) { 603 check(opcode); 604 cv.visitJumpInsn(opcode, label); 605 } 606 607 public void visitLdcInsn (final Object cst) { 608 check(LDC); 609 cv.visitLdcInsn(cst); 610 } 611 612 public void visitIincInsn (final int var, final int increment) { 613 check(IINC); 614 cv.visitIincInsn(var, increment); 615 } 616 617 public void visitTableSwitchInsn ( 618 final int min, 619 final int max, 620 final Label dflt, 621 final Label labels[]) 622 { 623 check(TABLESWITCH); 624 cv.visitTableSwitchInsn(min, max, dflt, labels); 625 } 626 627 public void visitLookupSwitchInsn ( 628 final Label dflt, 629 final int keys[], 630 final Label labels[]) 631 { 632 check(LOOKUPSWITCH); 633 cv.visitLookupSwitchInsn(dflt, keys, labels); 634 } 635 636 public void visitMultiANewArrayInsn (final String desc, final int dims) { 637 check(MULTIANEWARRAY); 638 cv.visitMultiANewArrayInsn(desc, dims); 639 } 640 } 641 } 642 | Popular Tags |