1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.jdo.impl.enhancer.core;
19
20 import java.util.Enumeration;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Set;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.HashMap;
29
30 import org.apache.jdo.impl.enhancer.classfile.AttributeVector;
31 import org.apache.jdo.impl.enhancer.classfile.ClassField;
32 import org.apache.jdo.impl.enhancer.classfile.ClassFile;
33 import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
34 import org.apache.jdo.impl.enhancer.classfile.CodeAttribute;
35 import org.apache.jdo.impl.enhancer.classfile.ConstClass;
36 import org.apache.jdo.impl.enhancer.classfile.ConstantPool;
37 import org.apache.jdo.impl.enhancer.classfile.Descriptor;
38 import org.apache.jdo.impl.enhancer.classfile.ExceptionsAttribute;
39 import org.apache.jdo.impl.enhancer.classfile.Insn;
40 import org.apache.jdo.impl.enhancer.classfile.InsnTarget;
41 import org.apache.jdo.impl.enhancer.classfile.LineNumberTableAttribute;
42 import org.apache.jdo.impl.enhancer.classfile.SyntheticAttribute;
43 import org.apache.jdo.impl.enhancer.classfile.VMConstants;
44 import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
45 import org.apache.jdo.impl.enhancer.util.Support;
46
47
48
49
50 /***
51 * Handles the augmentation actions for a class.
52 */
53 final class Augmenter
54 extends Support
55 implements JDOConstants
56 {
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 static private final boolean addSyntheticAttr = false;
92 static private final boolean addLineNumberTableAttr = true;
93
94 /***
95 * The classfile's enhancement controller.
96 */
97 private final Controller control;
98
99 /***
100 * The class analyzer for this class.
101 */
102 private final Analyzer analyzer;
103
104 /***
105 * The classfile to be enhanced.
106 */
107 private final ClassFile classFile;
108
109 /***
110 * The class name in VM form.
111 */
112 private final String className;
113
114 /***
115 * The class name in user ('.' delimited) form.
116 */
117 private final String userClassName;
118
119 /***
120 * The classfile's constant pool.
121 */
122 private final ConstantPool pool;
123
124 /***
125 * Repository for the enhancement options.
126 */
127 private final Environment env;
128
129 /***
130 * The method builder helper object.
131 */
132 private final Builder builder;
133
134
135
136 /***
137 * Constructor
138 */
139 public Augmenter(Controller control,
140 Analyzer analyzer,
141 Environment env)
142 {
143 affirm(control != null);
144 affirm(analyzer != null);
145 affirm(env != null);
146
147 this.control = control;
148 this.analyzer = analyzer;
149 this.env = env;
150 this.classFile = control.getClassFile();
151 this.className = classFile.classNameString();
152 this.userClassName = classFile.userClassName();
153 this.pool = classFile.pool();
154 this.builder = new Builder(analyzer, this, env);
155
156 affirm(classFile != null);
157 affirm(className != null);
158 affirm(userClassName != null);
159 affirm(pool != null);
160 affirm(builder != null);
161 }
162
163
164
165
166
167 /***
168 * Adds the augmentation to the class.
169 */
170 public void augment()
171 {
172 affirm(analyzer.isAugmentable() && !env.noAugment());
173 env.message("augmenting class " + userClassName);
174
175 if (analyzer.isAugmentableAsRoot()) {
176 augmentGenericJDOFields();
177 augmentGenericJDOMethods();
178 }
179 augmentClassInterface(JDO_PersistenceCapable_Path);
180 augmentSpecificJDOFields();
181 augmentSpecificJDOMethods();
182 augmentJDOAccessorMutatorMethods();
183 augmentSerializableSupportMethods();
184 }
185
186 /***
187 * Adds the specified interface to the implements clause of the class.
188 */
189 private void augmentClassInterface(String interfaceName)
190 {
191 env.message("adding: implements "
192 + ClassFile.userClassFromVMClass(interfaceName));
193
194 final ConstClass iface = pool.addClass(interfaceName);
195 classFile.addInterface(iface);
196
197
198 control.noteUpdate();
199 }
200
201 /***
202 * Adds the generic JDO fields to the class.
203 */
204 public void augmentGenericJDOFields()
205 {
206
207 addField(
208 JDO_PC_jdoStateManager_Name,
209 JDO_PC_jdoStateManager_Sig,
210 JDO_PC_jdoStateManager_Mods);
211
212
213 addField(
214 JDO_PC_jdoFlags_Name,
215 JDO_PC_jdoFlags_Sig,
216 JDO_PC_jdoFlags_Mods);
217 }
218
219 /***
220 * Adds the specific JDO fields to the class.
221 */
222 public void augmentSpecificJDOFields()
223 {
224
225 addField(
226 JDO_PC_jdoInheritedFieldCount_Name,
227 JDO_PC_jdoInheritedFieldCount_Sig,
228 JDO_PC_jdoInheritedFieldCount_Mods);
229
230
231 addField(
232 JDO_PC_jdoFieldNames_Name,
233 JDO_PC_jdoFieldNames_Sig,
234 JDO_PC_jdoFieldNames_Mods);
235
236
237 addField(
238 JDO_PC_jdoFieldTypes_Name,
239 JDO_PC_jdoFieldTypes_Sig,
240 JDO_PC_jdoFieldTypes_Mods);
241
242
243 addField(
244 JDO_PC_jdoFieldFlags_Name,
245 JDO_PC_jdoFieldFlags_Sig,
246 JDO_PC_jdoFieldFlags_Mods);
247
248
249 addField(
250 JDO_PC_jdoPersistenceCapableSuperclass_Name,
251 JDO_PC_jdoPersistenceCapableSuperclass_Sig,
252 JDO_PC_jdoPersistenceCapableSuperclass_Mods);
253 }
254
255 /***
256 * Adds a field to the class.
257 */
258 private void addField(String fieldName,
259 String fieldSig,
260 int accessFlags)
261 {
262 affirm(fieldName != null);
263 affirm(fieldSig != null);
264
265 env.message("adding: "
266 + Descriptor.userFieldSig(fieldSig)
267 + " " + fieldName);
268
269
270 final AttributeVector fieldAttrs = new AttributeVector();
271 fieldAttrs.addElement(
272 new SyntheticAttribute(
273 pool.addUtf8(SyntheticAttribute.expectedAttrName)));
274
275
276 final ClassField field
277 = new ClassField(accessFlags,
278 pool.addUtf8(fieldName),
279 pool.addUtf8(fieldSig),
280 fieldAttrs);
281 affirm(classFile.findField(fieldName) == null,
282 "Attempt to add a repeated field.");
283 classFile.addField(field);
284
285
286 control.noteUpdate();
287 }
288
289 /***
290 * Adds the generic JDO methods to the class.
291 */
292 public void augmentGenericJDOMethods()
293 {
294 builder.addJDOReplaceFlags();
295 builder.addJDOIsPersistentMethod();
296 builder.addJDOIsTransactionalMethod();
297 builder.addJDOIsNewMethod();
298 builder.addJDOIsDeletedMethod();
299 builder.addJDOIsDirtyMethod();
300 builder.addJDOIsDetachedMethod();
301 builder.addJDOMakeDirtyMethod();
302 builder.addJDOPreSerializeMethod();
303 builder.addJDOGetPersistenceManagerMethod();
304 builder.addJDOGetObjectIdMethod();
305 builder.addJDOGetTransactionalObjectIdMethod();
306 builder.addJDOGetVersionMethod();
307 builder.addJDOReplaceStateManager();
308 builder.addJDOProvideFieldsMethod();
309 builder.addJDOReplaceFieldsMethod();
310
311 builder.addSunJDOClassForNameMethod();
312
313
314
315
316
317
318
319
320
321 }
322
323 /***
324 * Adds the specific JDO methods to the class.
325 */
326 public void augmentSpecificJDOMethods()
327 {
328
329 builder.addJDOGetManagedFieldCountMethod();
330 builder.addStaticInitialization();
331
332
333 builder.addJDONewInstanceMethod();
334 builder.addJDONewInstanceOidMethod();
335
336
337 builder.addJDOProvideFieldMethod();
338 builder.addJDOReplaceFieldMethod();
339 builder.addJDOCopyFieldMethod();
340 builder.addJDOCopyFieldsMethod();
341
342
343 if (analyzer.isAugmentableAsRoot()
344 || analyzer.getKeyClassName() != null) {
345 builder.addJDONewObjectIdInstanceMethod();
346 builder.addJDONewObjectIdInstanceObjectMethod();
347 builder.addJDOCopyKeyFieldsToObjectIdMethod();
348 builder.addJDOCopyKeyFieldsFromObjectIdMethod();
349 builder.addJDOCopyKeyFieldsToObjectIdOIFSMethod();
350 builder.addJDOCopyKeyFieldsFromObjectIdOIFCMethod();
351 }
352
353
354
355
356
357
358
359
360
361 }
362
363 /***
364 * Adds the JDO accessor+mutator method for a field.
365 */
366 public void augmentJDOAccessorMutatorMethod(String fieldName,
367 String fieldSig,
368 int fieldMods,
369 int fieldFlags,
370 int index)
371 {
372 affirm(fieldName != null);
373 affirm(fieldSig != null);
374 affirm((fieldMods & ACCStatic) == 0);
375 affirm((fieldFlags & CHECK_READ) == 0
376 | (fieldFlags & MEDIATE_READ) == 0);
377 affirm((fieldFlags & CHECK_WRITE) == 0
378 | (fieldFlags & MEDIATE_WRITE) == 0);
379
380
381 affirm((fieldFlags & CHECK_READ) == 0
382 | (fieldFlags & MEDIATE_WRITE) == 0);
383 affirm((fieldFlags & CHECK_WRITE) == 0
384 | (fieldFlags & MEDIATE_READ) == 0);
385
386
387 final String aName
388 = JDONameHelper.getJDO_PC_jdoAccessor_Name(fieldName);
389 final String aSig
390 = JDONameHelper.getJDO_PC_jdoAccessor_Sig(className, fieldSig);
391 final int aMods
392 = JDONameHelper.getJDO_PC_jdoAccessor_Mods(fieldMods);
393 if ((fieldFlags & CHECK_READ) != 0) {
394 builder.addJDOCheckedReadAccessMethod(aName, aSig, aMods, index);
395 } else if ((fieldFlags & MEDIATE_READ) != 0) {
396 builder.addJDOMediatedReadAccessMethod(aName, aSig, aMods, index);
397 } else {
398 builder.addJDODirectReadAccessMethod(aName, aSig, aMods, index);
399 }
400
401
402 final String mName
403 = JDONameHelper.getJDO_PC_jdoMutator_Name(fieldName);
404 final String mSig
405 = JDONameHelper.getJDO_PC_jdoMutator_Sig(className, fieldSig);
406 final int mMods
407 = JDONameHelper.getJDO_PC_jdoMutator_Mods(fieldMods);
408 if ((fieldFlags & CHECK_WRITE) != 0) {
409 builder.addJDOCheckedWriteAccessMethod(mName, mSig, mMods, index);
410 } else if ((fieldFlags & MEDIATE_WRITE) != 0) {
411 builder.addJDOMediatedWriteAccessMethod(mName, mSig, mMods, index);
412 } else {
413 builder.addJDODirectWriteAccessMethod(mName, mSig, mMods, index);
414 }
415 }
416
417 /***
418 * Adds the JDO accessor+mutator methods to the class.
419 */
420 public void augmentJDOAccessorMutatorMethods()
421 {
422 final int annotatedFieldCount = analyzer.getAnnotatedFieldCount();
423 final String[] annotatedFieldNames = analyzer.getAnnotatedFieldNames();
424 final String[] annotatedFieldSigs = analyzer.getAnnotatedFieldSigs();
425 final int[] annotatedFieldMods = analyzer.getAnnotatedFieldMods();
426 final int[] annotatedFieldFlags = analyzer.getAnnotatedFieldFlags();
427 affirm(annotatedFieldNames.length == annotatedFieldCount);
428 affirm(annotatedFieldSigs.length == annotatedFieldCount);
429 affirm(annotatedFieldMods.length == annotatedFieldCount);
430 affirm(annotatedFieldFlags.length == annotatedFieldCount);
431
432 for (int i = 0; i < annotatedFieldCount; i++) {
433 augmentJDOAccessorMutatorMethod(annotatedFieldNames[i],
434 annotatedFieldSigs[i],
435 annotatedFieldMods[i],
436 annotatedFieldFlags[i], i);
437 }
438 }
439
440 /***
441 *
442 */
443 public void augmentSerializableSupportMethods()
444 {
445 final EnhancerMetaData meta = env.getEnhancerMetaData();
446 final String pcSuperClassName = analyzer.getPCSuperClassName();
447
448
449
450
451 if (meta.isSerializableClass(className) &&
452 (pcSuperClassName == null ||
453 !meta.isSerializableClass(pcSuperClassName))) {
454
455
456 if (!analyzer.hasWriteObjectMethod() &&
457 !analyzer.hasWriteReplaceMethod()) {
458 builder.addWriteObjectMethod();
459 }
460 else {
461 if (analyzer.hasWriteObjectMethod()) {
462
463 builder.addJDOPreSerializeCall(
464 JAVA_Object_writeObject_Name,
465 JAVA_Object_writeObject_Sig);
466 }
467 if (analyzer.hasWriteReplaceMethod()) {
468
469 builder.addJDOPreSerializeCall(
470 JAVA_Object_writeReplace_Name,
471 JAVA_Object_writeReplace_Sig);
472 }
473 }
474 }
475 }
476
477 /***
478 * Adds a method to the class.
479 */
480 void addMethod(String methodName,
481 String methodSig,
482 int accessFlags,
483 CodeAttribute codeAttr,
484 ExceptionsAttribute exceptAttr)
485 {
486 affirm(methodName != null);
487 affirm(methodSig != null);
488 affirm(codeAttr != null);
489
490 env.message("adding: "
491 + Descriptor.userMethodResult(methodSig)
492 + " " + methodName
493 + Descriptor.userMethodArgs(methodSig));
494
495
496 if (addLineNumberTableAttr) {
497
498 affirm(codeAttr.theCode().opcode() == Insn.opc_target);
499 final InsnTarget begin = (InsnTarget)codeAttr.theCode();
500
501
502 final AttributeVector codeSpecificAttrs = codeAttr.attributes();
503 affirm(codeSpecificAttrs != null);
504
505
506 codeSpecificAttrs.addElement(
507 new LineNumberTableAttribute(
508 pool.addUtf8(LineNumberTableAttribute.expectedAttrName),
509 new short[]{ 0 }, new InsnTarget[]{ begin }));
510 }
511
512
513 final AttributeVector methodAttrs = new AttributeVector();
514 methodAttrs.addElement(codeAttr);
515 if (exceptAttr != null) {
516 methodAttrs.addElement(exceptAttr);
517 }
518
519
520 if (addSyntheticAttr) {
521 methodAttrs.addElement(
522 new SyntheticAttribute(
523 pool.addUtf8(SyntheticAttribute.expectedAttrName)));
524 }
525
526
527 final ClassMethod method
528 = new ClassMethod(accessFlags,
529 pool.addUtf8(methodName),
530 pool.addUtf8(methodSig),
531 methodAttrs);
532 affirm(classFile.findMethod(methodName, methodSig) == null,
533 "Attempt to add a repeated method.");
534 classFile.addMethod(method);
535
536
537 control.noteUpdate();
538 }
539
540 /***
541 * Extends an exisiting method by prepending code.
542 */
543 void prependMethod(String methodName,
544 String methodSig,
545 CodeAttribute codeAttr,
546 ExceptionsAttribute exceptAttr)
547 {
548 affirm(methodName != null);
549 affirm(methodSig != null);
550 affirm(codeAttr != null);
551
552 env.message("extending: "
553 + Descriptor.userMethodResult(methodSig)
554 + " " + methodName
555 + Descriptor.userMethodArgs(methodSig));
556
557
558 final ClassMethod method = classFile.findMethod(methodName, methodSig);
559 affirm(method != null,
560 "Attempt to add code to a non-existing method.");
561
562
563 affirm(!method.isAbstract(),
564 "Attempt to add code to an abstract method.");
565 affirm(!method.isNative(),
566 "Attempt to add code to a native method.");
567 final CodeAttribute foundCodeAttr = method.codeAttribute();
568 affirm(foundCodeAttr != null);
569
570
571 final Insn firstInsn = codeAttr.theCode();
572 affirm(firstInsn != null);
573 final Insn foundFirstInsn = foundCodeAttr.theCode();
574 affirm(foundFirstInsn != null);
575 final Insn lastInsn = firstInsn.append(foundFirstInsn);
576 affirm(lastInsn != null);
577 foundCodeAttr.setTheCode(firstInsn);
578
579
580 foundCodeAttr.setStackUsed(max(foundCodeAttr.stackUsed(),
581 codeAttr.stackUsed()));
582 foundCodeAttr.setLocalsUsed(max(foundCodeAttr.localsUsed(),
583 codeAttr.localsUsed()));
584
585
586 if (exceptAttr != null) {
587 affirm((exceptAttr.getExceptions().size()
588 == new HashSet(exceptAttr.getExceptions()).size()),
589 "Exception attribute contains duplicate exceptions.");
590
591 final ExceptionsAttribute foundExceptAttr
592 = method.exceptionsAttribute();
593 if (foundExceptAttr == null) {
594
595 final AttributeVector methodAttrs = method.attributes();
596 affirm(methodAttrs != null);
597 methodAttrs.addElement(exceptAttr);
598 } else {
599
600 final List foundEx = foundExceptAttr.getExceptions();
601 final List newEx = exceptAttr.getExceptions();
602 newEx.removeAll(foundEx);
603 foundEx.addAll(newEx);
604 }
605 }
606
607
608 control.noteUpdate();
609 }
610
611 static private int max(int i, int j)
612 {
613 return (i < j) ? j : i;
614 }
615 }