1 /* 2 * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 package com.sun.org.apache.bcel.internal.generic; 21 22 import java.util.ArrayList; 23 import java.util.List; 24 import java.util.Objects; 25 26 import com.sun.org.apache.bcel.internal.Const; 27 import com.sun.org.apache.bcel.internal.classfile.AccessFlags; 28 import com.sun.org.apache.bcel.internal.classfile.AnnotationEntry; 29 import com.sun.org.apache.bcel.internal.classfile.Annotations; 30 import com.sun.org.apache.bcel.internal.classfile.Attribute; 31 import com.sun.org.apache.bcel.internal.classfile.ConstantPool; 32 import com.sun.org.apache.bcel.internal.classfile.Field; 33 import com.sun.org.apache.bcel.internal.classfile.JavaClass; 34 import com.sun.org.apache.bcel.internal.classfile.Method; 35 import com.sun.org.apache.bcel.internal.classfile.RuntimeInvisibleAnnotations; 36 import com.sun.org.apache.bcel.internal.classfile.RuntimeVisibleAnnotations; 37 import com.sun.org.apache.bcel.internal.classfile.SourceFile; 38 import com.sun.org.apache.bcel.internal.util.BCELComparator; 39 40 /** 41 * Template class for building up a java class. May be initialized with an 42 * existing java class (file). 43 * 44 * @see JavaClass 45 * @LastModified: Jan 2020 46 */ 47 public class ClassGen extends AccessFlags implements Cloneable { 48 49 /* Corresponds to the fields found in a JavaClass object. 50 */ 51 private String class_name; 52 private String super_class_name; 53 private final String file_name; 54 private int class_name_index = -1; 55 private int superclass_name_index = -1; 56 private int major = Const.MAJOR; 57 private int minor = Const.MINOR; 58 private ConstantPoolGen cp; // Template for building up constant pool 59 // ArrayLists instead of arrays to gather fields, methods, etc. 60 private final List<Field> field_vec = new ArrayList<>(); 61 private final List<Method> method_vec = new ArrayList<>(); 62 private final List<Attribute> attribute_vec = new ArrayList<>(); 63 private final List<String> interface_vec = new ArrayList<>(); 64 private final List<AnnotationEntryGen> annotation_vec = new ArrayList<>(); 65 66 private static BCELComparator _cmp = new BCELComparator() { 67 68 @Override 69 public boolean equals( final Object o1, final Object o2 ) { 70 final ClassGen THIS = (ClassGen) o1; 71 final ClassGen THAT = (ClassGen) o2; 72 return Objects.equals(THIS.getClassName(), THAT.getClassName()); 73 } 74 75 76 @Override 77 public int hashCode( final Object o ) { 78 final ClassGen THIS = (ClassGen) o; 79 return THIS.getClassName().hashCode(); 80 } 81 }; 82 83 84 /** Convenience constructor to set up some important values initially. 85 * 86 * @param class_name fully qualified class name 87 * @param super_class_name fully qualified superclass name 88 * @param file_name source file name 89 * @param access_flags access qualifiers 90 * @param interfaces implemented interfaces 91 * @param cp constant pool to use 92 */ ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, final String[] interfaces, final ConstantPoolGen cp)93 public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, 94 final String[] interfaces, final ConstantPoolGen cp) { 95 super(access_flags); 96 this.class_name = class_name; 97 this.super_class_name = super_class_name; 98 this.file_name = file_name; 99 this.cp = cp; 100 // Put everything needed by default into the constant pool and the vectors 101 if (file_name != null) { 102 addAttribute(new SourceFile(cp.addUtf8("SourceFile"), 2, cp.addUtf8(file_name), cp 103 .getConstantPool())); 104 } 105 class_name_index = cp.addClass(class_name); 106 superclass_name_index = cp.addClass(super_class_name); 107 if (interfaces != null) { 108 for (final String interface1 : interfaces) { 109 addInterface(interface1); 110 } 111 } 112 } 113 114 115 /** Convenience constructor to set up some important values initially. 116 * 117 * @param class_name fully qualified class name 118 * @param super_class_name fully qualified superclass name 119 * @param file_name source file name 120 * @param access_flags access qualifiers 121 * @param interfaces implemented interfaces 122 */ ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, final String[] interfaces)123 public ClassGen(final String class_name, final String super_class_name, final String file_name, final int access_flags, 124 final String[] interfaces) { 125 this(class_name, super_class_name, file_name, access_flags, interfaces, 126 new ConstantPoolGen()); 127 } 128 129 130 /** 131 * Initialize with existing class. 132 * @param clazz JavaClass object (e.g. read from file) 133 */ ClassGen(final JavaClass clazz)134 public ClassGen(final JavaClass clazz) { 135 super(clazz.getAccessFlags()); 136 class_name_index = clazz.getClassNameIndex(); 137 superclass_name_index = clazz.getSuperclassNameIndex(); 138 class_name = clazz.getClassName(); 139 super_class_name = clazz.getSuperclassName(); 140 file_name = clazz.getSourceFileName(); 141 cp = new ConstantPoolGen(clazz.getConstantPool()); 142 major = clazz.getMajor(); 143 minor = clazz.getMinor(); 144 final Attribute[] attributes = clazz.getAttributes(); 145 // J5TODO: Could make unpacking lazy, done on first reference 146 final AnnotationEntryGen[] annotations = unpackAnnotations(attributes); 147 final Method[] methods = clazz.getMethods(); 148 final Field[] fields = clazz.getFields(); 149 final String[] interfaces = clazz.getInterfaceNames(); 150 for (final String interface1 : interfaces) { 151 addInterface(interface1); 152 } 153 for (final Attribute attribute : attributes) { 154 if (!(attribute instanceof Annotations)) { 155 addAttribute(attribute); 156 } 157 } 158 for (final AnnotationEntryGen annotation : annotations) { 159 addAnnotationEntry(annotation); 160 } 161 for (final Method method : methods) { 162 addMethod(method); 163 } 164 for (final Field field : fields) { 165 addField(field); 166 } 167 } 168 169 /** 170 * Look for attributes representing annotations and unpack them. 171 */ unpackAnnotations(final Attribute[] attrs)172 private AnnotationEntryGen[] unpackAnnotations(final Attribute[] attrs) 173 { 174 final List<AnnotationEntryGen> annotationGenObjs = new ArrayList<>(); 175 for (final Attribute attr : attrs) { 176 if (attr instanceof RuntimeVisibleAnnotations) 177 { 178 final RuntimeVisibleAnnotations rva = (RuntimeVisibleAnnotations) attr; 179 final AnnotationEntry[] annos = rva.getAnnotationEntries(); 180 for (final AnnotationEntry a : annos) { 181 annotationGenObjs.add(new AnnotationEntryGen(a, 182 getConstantPool(), false)); 183 } 184 } 185 else 186 if (attr instanceof RuntimeInvisibleAnnotations) 187 { 188 final RuntimeInvisibleAnnotations ria = (RuntimeInvisibleAnnotations) attr; 189 final AnnotationEntry[] annos = ria.getAnnotationEntries(); 190 for (final AnnotationEntry a : annos) { 191 annotationGenObjs.add(new AnnotationEntryGen(a, 192 getConstantPool(), false)); 193 } 194 } 195 } 196 return annotationGenObjs.toArray(new AnnotationEntryGen[annotationGenObjs.size()]); 197 } 198 199 200 /** 201 * @return the (finally) built up Java class object. 202 */ getJavaClass()203 public JavaClass getJavaClass() { 204 final int[] interfaces = getInterfaces(); 205 final Field[] fields = getFields(); 206 final Method[] methods = getMethods(); 207 Attribute[] attributes = null; 208 if (annotation_vec.isEmpty()) { 209 attributes = getAttributes(); 210 } else { 211 // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations' 212 final Attribute[] annAttributes = AnnotationEntryGen.getAnnotationAttributes(cp, getAnnotationEntries()); 213 attributes = new Attribute[attribute_vec.size()+annAttributes.length]; 214 attribute_vec.toArray(attributes); 215 System.arraycopy(annAttributes,0,attributes,attribute_vec.size(),annAttributes.length); 216 } 217 // Must be last since the above calls may still add something to it 218 final ConstantPool _cp = this.cp.getFinalConstantPool(); 219 return new JavaClass(class_name_index, superclass_name_index, file_name, major, minor, 220 super.getAccessFlags(), _cp, interfaces, fields, methods, attributes); 221 } 222 223 224 /** 225 * Add an interface to this class, i.e., this class has to implement it. 226 * @param name interface to implement (fully qualified class name) 227 */ addInterface( final String name )228 public void addInterface( final String name ) { 229 interface_vec.add(name); 230 } 231 232 233 /** 234 * Remove an interface from this class. 235 * @param name interface to remove (fully qualified name) 236 */ removeInterface( final String name )237 public void removeInterface( final String name ) { 238 interface_vec.remove(name); 239 } 240 241 242 /** 243 * @return major version number of class file 244 */ getMajor()245 public int getMajor() { 246 return major; 247 } 248 249 250 /** Set major version number of class file, default value is 45 (JDK 1.1) 251 * @param major major version number 252 */ setMajor( final int major )253 public void setMajor( final int major ) { // TODO could be package-protected - only called by test code 254 this.major = major; 255 } 256 257 258 /** Set minor version number of class file, default value is 3 (JDK 1.1) 259 * @param minor minor version number 260 */ setMinor( final int minor )261 public void setMinor( final int minor ) { // TODO could be package-protected - only called by test code 262 this.minor = minor; 263 } 264 265 /** 266 * @return minor version number of class file 267 */ getMinor()268 public int getMinor() { 269 return minor; 270 } 271 272 273 /** 274 * Add an attribute to this class. 275 * @param a attribute to add 276 */ addAttribute( final Attribute a )277 public void addAttribute( final Attribute a ) { 278 attribute_vec.add(a); 279 } 280 addAnnotationEntry(final AnnotationEntryGen a)281 public void addAnnotationEntry(final AnnotationEntryGen a) { 282 annotation_vec.add(a); 283 } 284 285 286 /** 287 * Add a method to this class. 288 * @param m method to add 289 */ addMethod( final Method m )290 public void addMethod( final Method m ) { 291 method_vec.add(m); 292 } 293 294 295 /** 296 * Convenience method. 297 * 298 * Add an empty constructor to this class that does nothing but calling super(). 299 * @param access_flags rights for constructor 300 */ addEmptyConstructor( final int access_flags )301 public void addEmptyConstructor( final int access_flags ) { 302 final InstructionList il = new InstructionList(); 303 il.append(InstructionConst.THIS); // Push `this' 304 il.append(new INVOKESPECIAL(cp.addMethodref(super_class_name, "<init>", "()V"))); 305 il.append(InstructionConst.RETURN); 306 final MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>", 307 class_name, il, cp); 308 mg.setMaxStack(1); 309 addMethod(mg.getMethod()); 310 } 311 312 313 /** 314 * Add a field to this class. 315 * @param f field to add 316 */ addField( final Field f )317 public void addField( final Field f ) { 318 field_vec.add(f); 319 } 320 321 containsField( final Field f )322 public boolean containsField( final Field f ) { 323 return field_vec.contains(f); 324 } 325 326 327 /** @return field object with given name, or null 328 */ containsField( final String name )329 public Field containsField( final String name ) { 330 for (final Field f : field_vec) { 331 if (f.getName().equals(name)) { 332 return f; 333 } 334 } 335 return null; 336 } 337 338 339 /** @return method object with given name and signature, or null 340 */ containsMethod( final String name, final String signature )341 public Method containsMethod( final String name, final String signature ) { 342 for (final Method m : method_vec) { 343 if (m.getName().equals(name) && m.getSignature().equals(signature)) { 344 return m; 345 } 346 } 347 return null; 348 } 349 350 351 /** 352 * Remove an attribute from this class. 353 * @param a attribute to remove 354 */ removeAttribute( final Attribute a )355 public void removeAttribute( final Attribute a ) { 356 attribute_vec.remove(a); 357 } 358 359 360 /** 361 * Remove a method from this class. 362 * @param m method to remove 363 */ removeMethod( final Method m )364 public void removeMethod( final Method m ) { 365 method_vec.remove(m); 366 } 367 368 369 /** Replace given method with new one. If the old one does not exist 370 * add the new_ method to the class anyway. 371 */ replaceMethod( final Method old, final Method new_ )372 public void replaceMethod( final Method old, final Method new_ ) { 373 if (new_ == null) { 374 throw new ClassGenException("Replacement method must not be null"); 375 } 376 final int i = method_vec.indexOf(old); 377 if (i < 0) { 378 method_vec.add(new_); 379 } else { 380 method_vec.set(i, new_); 381 } 382 } 383 384 385 /** Replace given field with new one. If the old one does not exist 386 * add the new_ field to the class anyway. 387 */ replaceField( final Field old, final Field new_ )388 public void replaceField( final Field old, final Field new_ ) { 389 if (new_ == null) { 390 throw new ClassGenException("Replacement method must not be null"); 391 } 392 final int i = field_vec.indexOf(old); 393 if (i < 0) { 394 field_vec.add(new_); 395 } else { 396 field_vec.set(i, new_); 397 } 398 } 399 400 401 /** 402 * Remove a field to this class. 403 * @param f field to remove 404 */ removeField( final Field f )405 public void removeField( final Field f ) { 406 field_vec.remove(f); 407 } 408 409 getClassName()410 public String getClassName() { 411 return class_name; 412 } 413 414 getSuperclassName()415 public String getSuperclassName() { 416 return super_class_name; 417 } 418 419 getFileName()420 public String getFileName() { 421 return file_name; 422 } 423 424 setClassName( final String name )425 public void setClassName( final String name ) { 426 class_name = name.replace('/', '.'); 427 class_name_index = cp.addClass(name); 428 } 429 430 setSuperclassName( final String name )431 public void setSuperclassName( final String name ) { 432 super_class_name = name.replace('/', '.'); 433 superclass_name_index = cp.addClass(name); 434 } 435 436 getMethods()437 public Method[] getMethods() { 438 return method_vec.toArray(new Method[method_vec.size()]); 439 } 440 441 setMethods( final Method[] methods )442 public void setMethods( final Method[] methods ) { 443 method_vec.clear(); 444 for (final Method method : methods) { 445 addMethod(method); 446 } 447 } 448 449 setMethodAt( final Method method, final int pos )450 public void setMethodAt( final Method method, final int pos ) { 451 method_vec.set(pos, method); 452 } 453 454 getMethodAt( final int pos )455 public Method getMethodAt( final int pos ) { 456 return method_vec.get(pos); 457 } 458 459 getInterfaceNames()460 public String[] getInterfaceNames() { 461 final int size = interface_vec.size(); 462 final String[] interfaces = new String[size]; 463 interface_vec.toArray(interfaces); 464 return interfaces; 465 } 466 467 getInterfaces()468 public int[] getInterfaces() { 469 final int size = interface_vec.size(); 470 final int[] interfaces = new int[size]; 471 for (int i = 0; i < size; i++) { 472 interfaces[i] = cp.addClass(interface_vec.get(i)); 473 } 474 return interfaces; 475 } 476 477 getFields()478 public Field[] getFields() { 479 return field_vec.toArray(new Field[field_vec.size()]); 480 } 481 482 getAttributes()483 public Attribute[] getAttributes() { 484 return attribute_vec.toArray(new Attribute[attribute_vec.size()]); 485 } 486 487 // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here? getAnnotationEntries()488 public AnnotationEntryGen[] getAnnotationEntries() { 489 return annotation_vec.toArray(new AnnotationEntryGen[annotation_vec.size()]); 490 } 491 492 getConstantPool()493 public ConstantPoolGen getConstantPool() { 494 return cp; 495 } 496 497 setConstantPool( final ConstantPoolGen constant_pool )498 public void setConstantPool( final ConstantPoolGen constant_pool ) { 499 cp = constant_pool; 500 } 501 502 setClassNameIndex( final int class_name_index )503 public void setClassNameIndex( final int class_name_index ) { 504 this.class_name_index = class_name_index; 505 class_name = cp.getConstantPool().getConstantString(class_name_index, 506 Const.CONSTANT_Class).replace('/', '.'); 507 } 508 509 setSuperclassNameIndex( final int superclass_name_index )510 public void setSuperclassNameIndex( final int superclass_name_index ) { 511 this.superclass_name_index = superclass_name_index; 512 super_class_name = cp.getConstantPool().getConstantString(superclass_name_index, 513 Const.CONSTANT_Class).replace('/', '.'); 514 } 515 516 getSuperclassNameIndex()517 public int getSuperclassNameIndex() { 518 return superclass_name_index; 519 } 520 521 getClassNameIndex()522 public int getClassNameIndex() { 523 return class_name_index; 524 } 525 526 private List<ClassObserver> observers; 527 528 529 /** Add observer for this object. 530 */ addObserver( final ClassObserver o )531 public void addObserver( final ClassObserver o ) { 532 if (observers == null) { 533 observers = new ArrayList<>(); 534 } 535 observers.add(o); 536 } 537 538 539 /** Remove observer for this object. 540 */ removeObserver( final ClassObserver o )541 public void removeObserver( final ClassObserver o ) { 542 if (observers != null) { 543 observers.remove(o); 544 } 545 } 546 547 548 /** Call notify() method on all observers. This method is not called 549 * automatically whenever the state has changed, but has to be 550 * called by the user after he has finished editing the object. 551 */ update()552 public void update() { 553 if (observers != null) { 554 for (final ClassObserver observer : observers) { 555 observer.notify(this); 556 } 557 } 558 } 559 560 561 @Override clone()562 public Object clone() { 563 try { 564 return super.clone(); 565 } catch (final CloneNotSupportedException e) { 566 throw new Error("Clone Not Supported"); // never happens 567 } 568 } 569 570 571 /** 572 * @return Comparison strategy object 573 */ getComparator()574 public static BCELComparator getComparator() { 575 return _cmp; 576 } 577 578 579 /** 580 * @param comparator Comparison strategy object 581 */ setComparator( final BCELComparator comparator )582 public static void setComparator( final BCELComparator comparator ) { 583 _cmp = comparator; 584 } 585 586 587 /** 588 * Return value as defined by given BCELComparator strategy. 589 * By default two ClassGen objects are said to be equal when 590 * their class names are equal. 591 * 592 * @see java.lang.Object#equals(java.lang.Object) 593 */ 594 @Override equals( final Object obj )595 public boolean equals( final Object obj ) { 596 return _cmp.equals(this, obj); 597 } 598 599 600 /** 601 * Return value as defined by given BCELComparator strategy. 602 * By default return the hashcode of the class name. 603 * 604 * @see java.lang.Object#hashCode() 605 */ 606 @Override hashCode()607 public int hashCode() { 608 return _cmp.hashCode(this); 609 } 610 } 611