1 23 24 package org.objectweb.fractal.julia.asm; 25 26 import org.objectweb.fractal.julia.loader.Generated; 27 import org.objectweb.fractal.julia.loader.Loader; 28 import org.objectweb.fractal.julia.loader.Tree; 29 30 import org.objectweb.asm.ClassAdapter; 31 import org.objectweb.asm.ClassReader; 32 import org.objectweb.asm.ClassVisitor; 33 import org.objectweb.asm.ClassWriter; 34 import org.objectweb.asm.CodeAdapter; 35 import org.objectweb.asm.CodeVisitor; 36 import org.objectweb.asm.Constants; 37 import org.objectweb.asm.Label; 38 import org.objectweb.asm.Type; 39 import org.objectweb.asm.Attribute; 40 41 import java.io.IOException ; 42 import java.lang.reflect.Field ; 43 import java.lang.reflect.Method ; 44 import java.util.ArrayList ; 45 import java.util.HashMap ; 46 import java.util.HashSet ; 47 import java.util.List ; 48 import java.util.Map ; 49 import java.util.Set ; 50 51 137 138 public class MixinClassGenerator implements ClassGenerator, Constants { 139 140 143 144 String mixedClassName; 145 146 149 150 List mixins; 151 152 156 157 int currentMixin; 158 159 163 164 String currentMixinClass; 165 166 172 173 Map counters; 174 175 178 179 final static String THIS = "_this_"; 180 181 184 185 final static String SUPER = "_super_"; 186 187 205 206 public byte[] generateClass ( 207 final String name, 208 final Tree args, 209 final Loader loader, 210 final ClassLoader classLoader) throws ClassGenerationException 211 { 212 Set providedMethods = new HashSet (); 214 Set providedFields = new HashSet (); 215 Set interfaces = new HashSet (); 216 mixins = new ArrayList (); 217 218 String source = args.getSubTree(1).toString(); 219 220 for (int i = 2; i < args.getSize(); ++i) { 221 Class mixinClass; 222 try { 223 mixinClass = loader.loadClass(args.getSubTree(i), classLoader); 224 } catch (ClassNotFoundException e) { 225 throw new ClassGenerationException( 226 e, 227 args.toString(), 228 "Cannot load the '" + args.getSubTree(i) + "' mixin class"); 229 } 230 231 Method [] meths = mixinClass.getDeclaredMethods(); 234 for (int j = 0; j < meths.length; ++j) { 235 String m = meths[j].getName() + Type.getMethodDescriptor(meths[j]); 236 if (m.startsWith(THIS)) { 237 if (!providedMethods.contains(m)) { 238 throw new ClassGenerationException( 239 null, 240 args.toString(), 241 "The method '" + m + "' required by the '" + 242 mixinClass.getName() + "' mixin is missing"); 243 } 244 } else if (m.startsWith(SUPER)) { 245 m = m.substring(SUPER.length()); 246 boolean overriden = false; 248 for (int k = 0; k < meths.length; ++k) { 249 String n = meths[k].getName() + Type.getMethodDescriptor(meths[k]); 250 if (n.equals(m)) { 251 overriden = true; 252 break; 253 } 254 } 255 if (!overriden) { 256 throw new ClassGenerationException( 257 null, 258 args.toString(), 259 "Illegal method '" + meths[j] + "' in '" + mixinClass.getName() + 260 "': this method is not overriden by this mixin class"); 261 } 262 if (!providedMethods.contains(THIS + m)) { 263 throw new ClassGenerationException( 264 null, 265 args.toString(), 266 "The method '" + meths[j] + "' overriden in '" + 267 mixinClass.getName() + 268 "' is not provided by the preceding mixins"); 269 } 270 } 271 } 272 for (int j = 0; j < meths.length; ++j) { 273 String m = meths[j].getName() + Type.getMethodDescriptor(meths[j]); 274 if (!m.startsWith(THIS) && !m.startsWith(SUPER)) { 275 providedMethods.add(THIS + m); 276 } 277 } 278 279 Field [] fields = mixinClass.getDeclaredFields(); 282 for (int j = 0; j < fields.length; ++j) { 283 String f = fields[j].getName(); 284 if (f.startsWith(THIS)) { 285 if (!providedFields.contains(f)) { 286 throw new ClassGenerationException( 287 null, 288 args.toString(), 289 "The field '" + f + "' required by the '" + mixinClass.getName() + 290 "' mixin is missing"); 291 } 292 } else if (f.startsWith(SUPER)) { 293 throw new ClassGenerationException( 294 null, 295 args.toString(), 296 "Illegal field '" + f + "' in '" + mixinClass.getName() + 297 "': " + SUPER + " must not be used for fields"); 298 } else { 299 if (providedFields.contains(THIS + f)) { 300 throw new ClassGenerationException( 301 null, 302 args.toString(), 303 "The field '" + f + "' provided by the '" + mixinClass.getName() + 304 "': mixin is already provided by a preceding mixin"); 305 } 306 providedFields.add(THIS + f); 307 } 308 } 309 310 mixins.add(mixinClass); 311 Class [] itfs = mixinClass.getInterfaces(); 312 for (int j = 0; j < itfs.length; ++j) { 313 interfaces.add(Type.getInternalName(itfs[j])); 314 } 315 } 316 317 mixedClassName = name.replace('.', '/'); 319 ClassWriter cw = new ClassWriter(false); 320 interfaces.add(Type.getInternalName(Generated.class)); 321 cw.visit( 322 V1_1, 323 ACC_PUBLIC, 324 mixedClassName, 325 "java/lang/Object", 326 (String [])interfaces.toArray(new String [interfaces.size()]), 327 "MIXIN["+source+']'); 328 329 CodeVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 331 mw.visitVarInsn(ALOAD, 0); 332 mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); 333 mw.visitInsn(RETURN); 334 mw.visitMaxs(1, 1); 335 336 String mName = "getFcGeneratorParameters"; 338 String mDesc = "()Ljava/lang/String;"; 339 CodeVisitor mv = cw.visitMethod(ACC_PUBLIC, mName, mDesc, null, null); 340 mv.visitLdcInsn(args.toString()); 341 mv.visitInsn(ARETURN); 342 mv.visitMaxs(1, 1); 343 344 counters = new HashMap (); 346 for (currentMixin = mixins.size() - 1; currentMixin >= 0; --currentMixin) { 347 Class c = (Class )mixins.get(currentMixin); 348 currentMixinClass = Type.getInternalName(c); 349 MixinClassAdapter mca = new MixinClassAdapter(cw); 350 try { 351 getClassReader(c).accept(mca, false); 352 } catch (IOException e) { 353 throw new ClassGenerationException( 354 e, args.toString(), "Cannot read the mixin class " + c.getName()); 355 } 356 updateCounters(c); 357 } 358 359 return cw.toByteArray(); 361 } 362 363 370 371 ClassReader getClassReader (final Class c) throws IOException { 372 try { 373 return new ClassReader(c.getName()); 374 } catch (IOException e) { 375 String s = Type.getInternalName(c) + ".class"; 378 return new ClassReader(c.getClassLoader().getResourceAsStream(s)); 379 } 380 } 381 382 389 390 int getCounter (final String name, final String desc) { 391 Integer value = (Integer )counters.get(name + desc); 392 return value == null ? -1 : value.intValue(); 393 } 394 395 402 403 void updateCounters (final Class c) { 404 Method [] meths = c.getDeclaredMethods(); 405 for (int i = 0; i < meths.length; ++i) { 406 Method meth = meths[i]; 407 String key = meth.getName(); 408 if (key.startsWith(SUPER) || 409 key.startsWith(THIS) || 410 (meth.getModifiers() & (ACC_STATIC | ACC_ABSTRACT)) == 0) 411 { 412 key += Type.getMethodDescriptor(meth); 413 Integer value = (Integer )counters.get(key); 414 int count = value == null ? 0 : value.intValue() + 1; 415 counters.put(key, new Integer (count)); 416 } 417 } 418 } 419 420 430 431 static boolean providesMethod (final String m, final Class c) { 432 Method [] meths = c.getMethods(); 433 for (int i = 0; i < meths.length; ++i) { 434 if (equals(m, meths[i])) { 435 return true; 436 } 437 } 438 return false; 439 } 440 441 449 450 static boolean providesField (final String f, final Class c) { 451 Field [] fields = c.getFields(); 452 for (int i = 0; i < fields.length; ++i) { 453 if (f.equals(fields[i].getName())) { 454 return true; 455 } 456 } 457 return false; 458 } 459 460 472 473 static boolean equals (final String m, final Method n) { 474 String ms = m.substring(SUPER.length()); 475 String ns = n.getName() + Type.getMethodDescriptor(n); 476 return ms.equals(ns); 477 } 478 479 482 483 class MixinClassAdapter extends ClassAdapter implements Constants { 484 485 490 491 public MixinClassAdapter (final ClassVisitor cv) { 492 super(cv); 493 } 494 495 public void visit ( 496 final int version, 497 final int access, 498 final String name, 499 final String superName, 500 final String [] interfaces, 501 final String sourceFile) 502 { 503 } 505 506 public void visitField ( 507 final int access, 508 final String name, 509 final String desc, 510 final Object value, 511 final Attribute attrs) 512 { 513 if (!name.startsWith(THIS) && (access & ACC_STATIC) == 0) { 514 super.visitField(access, name, desc, value, attrs); 515 } 516 } 517 518 public CodeVisitor visitMethod ( 519 final int access, 520 final String name, 521 final String desc, 522 final String [] exceptions, 523 final Attribute attrs) 524 { 525 if ((access & (ACC_NATIVE | ACC_STATIC | ACC_ABSTRACT)) != 0) { 526 return null; 527 } 528 if (name.equals("<init>") || 529 name.startsWith(SUPER) || 530 name.startsWith(THIS)) 531 { 532 return null; 533 } 534 int count = getCounter(name, desc); 535 CodeVisitor mv; 536 if (count == -1) { 537 mv = cv.visitMethod(access, name, desc, exceptions, attrs); 538 } else { 539 int newAccess = access & ~(ACC_PUBLIC | ACC_PROTECTED) | ACC_PRIVATE; 540 String newName = name + "$$" + count; 541 mv = cv.visitMethod(newAccess, newName, desc, exceptions, attrs); 542 } 543 return new MixinCodeAdapter(mv); 544 } 545 } 546 547 550 551 class MixinCodeAdapter extends CodeAdapter implements Constants { 552 553 558 559 public MixinCodeAdapter (final CodeVisitor cv) { 560 super(cv); 561 } 562 563 public void visitFieldInsn ( 564 final int opcode, 565 final String owner, 566 final String name, 567 final String desc) 568 { 569 if (name.startsWith(THIS)) { 570 String normalName = name.substring(THIS.length()); 571 cv.visitFieldInsn(opcode, mixedClassName, normalName, desc); 572 } else if ( 573 opcode != GETSTATIC && 574 opcode != PUTSTATIC && 575 owner.equals(currentMixinClass)) 576 { 577 cv.visitFieldInsn(opcode, mixedClassName, name, desc); 578 } else { 579 cv.visitFieldInsn(opcode, owner, name, desc); 580 } 581 } 582 583 public void visitMethodInsn ( 584 final int opcode, 585 final String owner, 586 final String name, 587 final String desc) 588 { 589 if (name.startsWith(THIS)) { 590 String normalName = name.substring(THIS.length()); 591 cv.visitMethodInsn(opcode, mixedClassName, normalName, desc); 592 } else if (name.startsWith(SUPER)) { 593 String normalName = name.substring(SUPER.length()); 594 String superName = normalName + "$$" + (getCounter(name, desc) + 1); 595 cv.visitMethodInsn(INVOKESPECIAL, mixedClassName, superName, desc); 596 } else if (opcode != INVOKESTATIC && owner.equals(currentMixinClass)) { 597 cv.visitMethodInsn(opcode, mixedClassName, name, desc); 598 } else { 599 cv.visitMethodInsn(opcode, owner, name, desc); 600 } 601 } 602 603 public void visitLineNumber (int i, Label label) { 604 cv.visitLineNumber((currentMixin + 1) * 1000 + i, label); 605 } 606 } 607 } 608 | Popular Tags |