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