1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.enhance;
20
21 import java.io.Externalizable;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.ObjectInput;
25 import java.io.ObjectInputStream;
26 import java.io.ObjectOutput;
27 import java.io.ObjectOutputStream;
28 import java.io.ObjectStreamClass;
29 import java.io.Serializable;
30 import java.io.ObjectStreamException;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Modifier;
34 import java.security.AccessController;
35 import java.security.PrivilegedActionException;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Date;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Set;
46
47 import org.apache.commons.lang.StringUtils;
48 import org.apache.openjpa.conf.OpenJPAConfiguration;
49 import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
50 import org.apache.openjpa.lib.conf.Configurations;
51 import org.apache.openjpa.lib.log.Log;
52 import org.apache.openjpa.lib.meta.ClassArgParser;
53 import org.apache.openjpa.lib.util.BytecodeWriter;
54 import org.apache.openjpa.lib.util.Files;
55 import org.apache.openjpa.lib.util.J2DoPrivHelper;
56 import org.apache.openjpa.lib.util.Localizer;
57 import org.apache.openjpa.lib.util.Options;
58 import org.apache.openjpa.lib.util.Services;
59 import org.apache.openjpa.lib.util.Localizer.Message;
60 import org.apache.openjpa.meta.ClassMetaData;
61 import org.apache.openjpa.meta.FieldMetaData;
62 import org.apache.openjpa.meta.JavaTypes;
63 import org.apache.openjpa.meta.MetaDataRepository;
64 import org.apache.openjpa.meta.ValueStrategies;
65 import org.apache.openjpa.util.GeneralException;
66 import org.apache.openjpa.util.InternalException;
67 import org.apache.openjpa.util.BigDecimalId;
68 import org.apache.openjpa.util.BigIntegerId;
69 import org.apache.openjpa.util.ByteId;
70 import org.apache.openjpa.util.CharId;
71 import org.apache.openjpa.util.DateId;
72 import org.apache.openjpa.util.DoubleId;
73 import org.apache.openjpa.util.Id;
74 import org.apache.openjpa.util.IntId;
75 import org.apache.openjpa.util.FloatId;
76 import org.apache.openjpa.util.LongId;
77 import org.apache.openjpa.util.ObjectId;
78 import org.apache.openjpa.util.ShortId;
79 import org.apache.openjpa.util.StringId;
80 import org.apache.openjpa.util.OpenJPAException;
81 import org.apache.openjpa.util.UserException;
82 import org.apache.openjpa.util.ImplHelper;
83 import serp.bytecode.BCClass;
84 import serp.bytecode.BCField;
85 import serp.bytecode.BCMethod;
86 import serp.bytecode.Code;
87 import serp.bytecode.Constants;
88 import serp.bytecode.Exceptions;
89 import serp.bytecode.FieldInstruction;
90 import serp.bytecode.GetFieldInstruction;
91 import serp.bytecode.IfInstruction;
92 import serp.bytecode.Instruction;
93 import serp.bytecode.JumpInstruction;
94 import serp.bytecode.LoadInstruction;
95 import serp.bytecode.MethodInstruction;
96 import serp.bytecode.Project;
97 import serp.bytecode.TableSwitchInstruction;
98 import serp.bytecode.ClassInstruction;
99 import serp.util.Numbers;
100 import serp.util.Strings;
101
102 /**
103 * Bytecode enhancer used to enhance persistent classes from metadata. The
104 * enhancer must be invoked on all persistence-capable and persistence aware
105 * classes.
106 *
107 * @author Abe White
108 */
109 public class PCEnhancer {
110 // Designates a version for maintaining compatbility when PCEnhancer
111 // modifies enhancement that can break serialization or other contracts
112 // Each enhanced class will return the value of this field via
113 // public int getEnhancementContractVersion()
114 public static final int ENHANCER_VERSION = 2;
115
116 public static final int ENHANCE_NONE = 0;
117 public static final int ENHANCE_AWARE = 2 << 0;
118 public static final int ENHANCE_INTERFACE = 2 << 1;
119 public static final int ENHANCE_PC = 2 << 2;
120
121 public static final String PRE = "pc";
122 public static final String ISDETACHEDSTATEDEFINITIVE = PRE
123 + "isDetachedStateDefinitive";
124
125 private static final Class PCTYPE = PersistenceCapable.class;
126 private static final String SM = PRE + "StateManager";
127 private static final Class SMTYPE = StateManager.class;
128 private static final String INHERIT = PRE + "InheritedFieldCount";
129 private static final String CONTEXTNAME = "GenericContext";
130 private static final Class USEREXCEP = UserException.class;
131 private static final Class INTERNEXCEP = InternalException.class;
132 private static final Class HELPERTYPE = PCRegistry.class;
133 private static final String SUPER = PRE + "PCSuperclass";
134 private static final Class OIDFSTYPE = FieldSupplier.class;
135 private static final Class OIDFCTYPE = FieldConsumer.class;
136
137 private static final Localizer _loc = Localizer.forPackage
138 (PCEnhancer.class);
139 private static final String REDEFINED_ATTRIBUTE
140 = PCEnhancer.class.getName() + "#redefined-type";
141
142 private static final AuxiliaryEnhancer[] _auxEnhancers;
143 static {
144 Class[] classes = Services.getImplementorClasses(
145 AuxiliaryEnhancer.class,
146 (ClassLoader) AccessController.doPrivileged(
147 J2DoPrivHelper.getClassLoaderAction(AuxiliaryEnhancer.class)));
148 List auxEnhancers = new ArrayList(classes.length);
149 for (int i = 0; i < classes.length; i++) {
150 try {
151 auxEnhancers.add(AccessController.doPrivileged(
152 J2DoPrivHelper.newInstanceAction(classes[i])));
153 } catch (Throwable t) {
154 // aux enhancer may rely on non-existant spec classes, etc
155 }
156 }
157 _auxEnhancers = (AuxiliaryEnhancer[]) auxEnhancers.toArray
158 (new AuxiliaryEnhancer[auxEnhancers.size()]);
159 }
160
161 private BCClass _pc;
162 private final BCClass _managedType;
163 private final MetaDataRepository _repos;
164 private final ClassMetaData _meta;
165 private final Log _log;
166 private Collection _oids = null;
167 private boolean _defCons = true;
168 private boolean _redefine = false;
169 private boolean _subclass = false;
170 private boolean _fail = false;
171 private Set _violations = null;
172 private File _dir = null;
173 private BytecodeWriter _writer = null;
174 private Map _backingFields = null; // map of set / get names => field names
175 private Map _attrsToFields = null; // map of attr names => field names
176 private Map _fieldsToAttrs = null; // map of field names => attr names
177 private boolean _isAlreadyRedefined = false;
178 private boolean _isAlreadySubclassed = false;
179 private boolean _bcsConfigured = false;
180
181 /**
182 * Constructor. Supply configuration and type to enhance. This will look
183 * up the metadata for <code>type</code> from <code>conf</code>'s
184 * repository.
185 */
186 public PCEnhancer(OpenJPAConfiguration conf, Class type) {
187 this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper
188 .loadProjectClassAction(new Project(), type)),
189 (MetaDataRepository) null);
190 }
191
192 /**
193 * Constructor. Supply configuration and type to enhance. This will look
194 * up the metadata for <code>meta</code> by converting back to a class
195 * and then loading from <code>conf</code>'s repository.
196 */
197 public PCEnhancer(OpenJPAConfiguration conf, ClassMetaData meta) {
198 this(conf, (BCClass) AccessController.doPrivileged(J2DoPrivHelper
199 .loadProjectClassAction(new Project(), meta.getDescribedType())),
200 meta.getRepository());
201 }
202
203 /**
204 * Constructor. Supply configuration.
205 *
206 * @param type the bytecode representation fo the type to
207 * enhance; this can be created from any stream or file
208 * @param repos a metadata repository to use for metadata access,
209 * or null to create a new reporitory; the repository
210 * from the given configuration isn't used by default
211 * because the configuration might be an
212 * implementation-specific subclass whose metadata
213 * required more than just base metadata files
214 * @deprecated use {@link #PCEnhancer(OpenJPAConfiguration, BCClass,
215 MetaDataRepository, ClassLoader)} instead.
216 */
217 public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
218 MetaDataRepository repos) {
219 this(conf, type, repos, null);
220 }
221
222 /**
223 * Constructor. Supply configuration.
224 *
225 * @param type the bytecode representation fo the type to
226 * enhance; this can be created from any stream or file
227 * @param repos a metadata repository to use for metadata access,
228 * or null to create a new reporitory; the repository
229 * from the given configuration isn't used by default
230 * because the configuration might be an
231 * implementation-specific subclass whose metadata
232 * required more than just base metadata files
233 * @param loader the environment classloader to use for loading
234 * classes and resources.
235 */
236 public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
237 MetaDataRepository repos, ClassLoader loader) {
238 _managedType = type;
239 _pc = type;
240
241 _log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE);
242
243 if (repos == null) {
244 _repos = conf.newMetaDataRepositoryInstance();
245 _repos.setSourceMode(MetaDataRepository.MODE_META);
246 } else
247 _repos = repos;
248 _meta = _repos.getMetaData(type.getType(), loader, false);
249 }
250
251 /**
252 * Constructor. Supply repository. The repository's configuration will
253 * be used, and the metadata passed in will be used as-is without doing
254 * any additional lookups. This is useful when running the enhancer
255 * during metadata load.
256 *
257 * @param repos a metadata repository to use for metadata access,
258 * or null to create a new reporitory; the repository
259 * from the given configuration isn't used by default
260 * because the configuration might be an
261 * implementation-specific subclass whose metadata
262 * required more than just base metadata files
263 * @param type the bytecode representation fo the type to
264 * enhance; this can be created from any stream or file
265 * @param meta the metadata to use for processing this type.
266 *
267 * @since 1.1.0
268 */
269 public PCEnhancer(MetaDataRepository repos, BCClass type,
270 ClassMetaData meta) {
271 _managedType = type;
272 _pc = type;
273
274 _log = repos.getConfiguration()
275 .getLog(OpenJPAConfiguration.LOG_ENHANCE);
276
277 _repos = repos;
278 _meta = meta;
279 }
280
281 static String toPCSubclassName(Class cls) {
282 return Strings.getPackageName(PCEnhancer.class) + "."
283 + cls.getName().replace('.', '$') + "$pcsubclass";
284 }
285
286 /**
287 * Whether or not <code>className</code> is the name for a
288 * dynamically-created persistence-capable subclass.
289 *
290 * @since 1.1.0
291 */
292 public static boolean isPCSubclassName(String className) {
293 return className.startsWith(Strings.getPackageName(PCEnhancer.class))
294 && className.endsWith("$pcsubclass");
295 }
296
297 /**
298 * If <code>className</code> is a dynamically-created persistence-capable
299 * subclass name, returns the name of the class that it subclasses.
300 * Otherwise, returns <code>className</code>.
301 *
302 * @since 1.1.0
303 */
304 public static String toManagedTypeName(String className) {
305 if (isPCSubclassName(className)) {
306 className = className.substring(
307 Strings.getPackageName(PCEnhancer.class).length() + 1);
308 className = className.substring(0, className.lastIndexOf("$"));
309 // this is not correct for nested PCs
310 className = className.replace('$', '.');
311 }
312
313 return className;
314 }
315
316 /**
317 * Constructor. Supply configuration, type, and metadata.
318 */
319 public PCEnhancer(OpenJPAConfiguration conf, BCClass type,
320 ClassMetaData meta) {
321 this(conf, type, meta.getRepository());
322 }
323
324 /**
325 * Return the bytecode representation of the persistence-capable class
326 * being manipulated.
327 */
328 public BCClass getPCBytecode() {
329 return _pc;
330 }
331
332 /**
333 * Return the bytecode representation of the managed class being
334 * manipulated. This is usually the same as {@link #getPCBytecode},
335 * except when running the enhancer to redefine and subclass
336 * existing persistent types.
337 */
338 public BCClass getManagedTypeBytecode() {
339 return _managedType;
340 }
341
342 /**
343 * Return the metadata for the class being manipulated, or null if not
344 * a persistent type.
345 */
346 public ClassMetaData getMetaData() {
347 return _meta;
348 }
349
350 /**
351 * A boolean indicating whether the enhancer should add a no-args
352 * constructor if one is not already present in the class. OpenJPA
353 * requires that a no-arg constructor (whether created by the compiler
354 * or by the user) be present in a PC.
355 */
356 public boolean getAddDefaultConstructor() {
357 return _defCons;
358 }
359
360 /**
361 * A boolean indicating whether the enhancer should add a no-args
362 * constructor if one is not already present in the class. OpenJPA
363 * requires that a no-arg constructor (whether created by the compiler
364 * or by the user) be present in a PC.
365 */
366 public void setAddDefaultConstructor(boolean addDefaultConstructor) {
367 _defCons = addDefaultConstructor;
368 }
369
370 /**
371 * Whether the enhancer should mutate its arguments, or just run validation
372 * and optional subclassing logic on them. Usually used in conjunction with
373 * <code>setCreateSubclass(true)</code>.
374 *
375 * @since 1.0.0
376 */
377 public boolean getRedefine() {
378 return _redefine;
379 }
380
381 /**
382 * Whether the enhancer should mutate its arguments, or just run validation
383 * and optional subclassing logic on them. Usually used in conjunction with
384 * <code>setCreateSubclass(true)</code>.
385 *
386 * @since 1.0.0
387 */
388 public void setRedefine(boolean redefine) {
389 _redefine = redefine;
390 }
391
392 /**
393 * Whether the type that this instance is enhancing has already been
394 * redefined.
395 *
396 * @since 1.0.0
397 */
398 public boolean isAlreadyRedefined() {
399 return _isAlreadyRedefined;
400 }
401
402 /**
403 * Whether the type that this instance is enhancing has already been
404 * subclassed in this instance's environment classloader.
405 *
406 * @since 1.0.0
407 */
408 public boolean isAlreadySubclassed() {
409 return _isAlreadySubclassed;
410 }
411
412 /**
413 * Whether the enhancer should make its arguments persistence-capable,
414 * or generate a persistence-capable subclass.
415 *
416 * @since 1.0.0
417 */
418 public boolean getCreateSubclass() {
419 return _subclass;
420 }
421
422 /**
423 * Whether the enhancer should make its arguments persistence-capable,
424 * or generate a persistence-capable subclass.
425 *
426 * @since 1.0.0
427 */
428 public void setCreateSubclass(boolean subclass) {
429 _subclass = subclass;
430 }
431
432 /**
433 * Whether to fail if the persistent type uses property access and
434 * bytecode analysis shows that it may be violating OpenJPA's property
435 * access restrictions.
436 */
437 public boolean getEnforcePropertyRestrictions() {
438 return _fail;
439 }
440
441 /**
442 * Whether to fail if the persistent type uses property access and
443 * bytecode analysis shows that it may be violating OpenJPA's property
444 * access restrictions.
445 */
446 public void setEnforcePropertyRestrictions(boolean fail) {
447 _fail = fail;
448 }
449
450 /**
451 * The base build directory to generate code to. The proper package
452 * structure will be created beneath this directory. Defaults to
453 * overwriting the existing class file if null.
454 */
455 public File getDirectory() {
456 return _dir;
457 }
458
459 /**
460 * The base build directory to generate code to. The proper package
461 * structure will be creaed beneath this directory. Defaults to
462 * overwriting the existing class file if null.
463 */
464 public void setDirectory(File dir) {
465 _dir = dir;
466 }
467
468 /**
469 * Return the current {@link BytecodeWriter} to write to or null if none.
470 */
471 public BytecodeWriter getBytecodeWriter() {
472 return _writer;
473 }
474
475 /**
476 * Set the {@link BytecodeWriter} to write the bytecode to or null if none.
477 */
478 public void setBytecodeWriter(BytecodeWriter writer) {
479 _writer = writer;
480 }
481
482 /**
483 * Perform bytecode enhancements.
484 *
485 * @return <code>ENHANCE_*</code> constant
486 */
487 public int run() {
488 if (_log.isTraceEnabled())
489 _log.trace(_loc.get("enhance-start", _managedType.getType()));
490
491 try {
492 // if managed interface, skip
493 if (_pc.isInterface())
494 return ENHANCE_INTERFACE;
495
496 // check if already enhanced
497 Class[] interfaces = _managedType.getDeclaredInterfaceTypes();
498 for (int i = 0; i < interfaces.length; i++) {
499 if (interfaces[i].getName().equals(PCTYPE.getName())) {
500 if (_log.isTraceEnabled())
501 _log.trace(_loc.get("pc-type", _managedType.getType()));
502 return ENHANCE_NONE;
503 }
504 }
505
506 configureBCs();
507
508 // validate properties before replacing field access so that
509 // we build up a record of backing fields, etc
510 if (_meta != null
511 && _meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY) {
512 validateProperties();
513 if (getCreateSubclass())
514 addAttributeTranslation();
515 }
516 replaceAndValidateFieldAccess();
517 processViolations();
518
519 if (_meta != null) {
520 enhanceClass();
521 addFields();
522 addStaticInitializer();
523 addPCMethods();
524 addAccessors();
525 addAttachDetachCode();
526 addSerializationCode();
527 addCloningCode();
528 runAuxiliaryEnhancers();
529 return ENHANCE_PC;
530 }
531
532 if (_log.isWarnEnabled())
533 _log.warn(_loc.get("pers-aware", _managedType.getType()));
534 return ENHANCE_AWARE;
535 } catch (OpenJPAException ke) {
536 throw ke;
537 } catch (Exception e) {
538 throw new GeneralException(_loc.get("enhance-error",
539 _managedType.getType().getName(), e.getMessage()), e);
540 }
541 }
542
543 private void configureBCs() {
544 if (!_bcsConfigured) {
545 if (getRedefine()) {
546 if (_managedType.getAttribute(REDEFINED_ATTRIBUTE) == null)
547 _managedType.addAttribute(REDEFINED_ATTRIBUTE);
548 else
549 _isAlreadyRedefined = true;
550 }
551
552 if (getCreateSubclass()) {
553 PCSubclassValidator val = new PCSubclassValidator(
554 _meta, _managedType, _log, _fail);
555 val.assertCanSubclass();
556
557 _pc = _managedType.getProject().loadClass(
558 toPCSubclassName(_managedType.getType()));
559 if (_pc.getSuperclassBC() != _managedType) {
560 _pc.setSuperclass(_managedType);
561 _pc.setAbstract(_managedType.isAbstract());
562 _pc.declareInterface(DynamicPersistenceCapable.class);
563 } else {
564 _isAlreadySubclassed = true;
565 }
566 }
567
568 _bcsConfigured = true;
569 }
570 }
571
572 /**
573 * Write the generated bytecode.
574 */
575 public void record()
576 throws IOException {
577 if (_managedType != _pc && getRedefine())
578 record(_managedType);
579 record(_pc);
580 if (_oids != null)
581 for (Iterator itr = _oids.iterator(); itr.hasNext();)
582 record((BCClass) itr.next());
583 }
584
585 /**
586 * Write the given class.
587 */
588 private void record(BCClass bc)
589 throws IOException {
590 if (_writer != null)
591 _writer.write(bc);
592 else if (_dir == null)
593 bc.write();
594 else {
595 File dir = Files.getPackageFile(_dir, bc.getPackageName(), true);
596 bc.write(new File(dir, bc.getClassName() + ".class"));
597 }
598 }
599
600 /**
601 * Validate that the methods in this property-access instance are
602 * written correctly. This method also gathers information on each
603 * property's backing field.
604 */
605 private void validateProperties() {
606 FieldMetaData[] fmds;
607 if (getCreateSubclass())
608 fmds = _meta.getFields();
609 else
610 fmds = _meta.getDeclaredFields();
611 Method meth;
612 BCMethod getter, setter;
613 BCField returned, assigned = null;
614 for (int i = 0; i < fmds.length; i++) {
615 if (!(fmds[i].getBackingMember() instanceof Method)) {
616 addViolation("property-bad-member",
617 new Object[]{ fmds[i], fmds[i].getBackingMember() },
618 true);
619 continue;
620 }
621
622 meth = (Method) fmds[i].getBackingMember();
623 // ##### this will fail if we override and don't call super.
624 BCClass declaringType = _managedType.getProject()
625 .loadClass(fmds[i].getDeclaringType());
626 getter = declaringType.getDeclaredMethod(meth.getName(),
627 meth.getParameterTypes());
628 if (getter == null) {
629 addViolation("property-no-getter", new Object[]{ fmds[i] },
630 true);
631 continue;
632 }
633 returned = getReturnedField(getter);
634 if (returned != null)
635 registerBackingFieldInfo(fmds[i], getter, returned);
636
637 setter = declaringType.getDeclaredMethod(getSetterName(fmds[i]),
638 new Class[]{ fmds[i].getDeclaredType() });
639 if (setter == null) {
640 if (returned == null) {
641 addViolation("property-no-setter",
642 new Object[]{ fmds[i] }, true);
643 continue;
644 } else if (!getRedefine()) {
645 // create synthetic setter
646 setter = _managedType.declareMethod(getSetterName(fmds[i]),
647 void.class, new Class[]{ fmds[i].getDeclaredType() });
648 setter.makePrivate();
649 Code code = setter.getCode(true);
650 code.aload().setThis();
651 code.xload().setParam(0);
652 code.putfield().setField(returned);
653 code.vreturn();
654 code.calculateMaxStack();
655 code.calculateMaxLocals();
656 }
657 }
658
659 if (setter != null)
660 assigned = getAssignedField(setter);
661
662 if (assigned != null) {
663 if (setter != null)
664 registerBackingFieldInfo(fmds[i], setter, assigned);
665
666 if (assigned != returned)
667 addViolation("property-setter-getter-mismatch", new Object[]
668 { fmds[i], assigned.getName(), (returned == null)
669 ? null : returned.getName() }, false);
670 }
671 }
672 }
673
674 private void registerBackingFieldInfo(FieldMetaData fmd, BCMethod method,
675 BCField field) {
676 if (_backingFields == null)
677 _backingFields = new HashMap();
678 _backingFields.put(method.getName(), field.getName());
679
680 if (_attrsToFields == null)
681 _attrsToFields = new HashMap();
682 _attrsToFields.put(fmd.getName(), field.getName());
683
684 if (_fieldsToAttrs == null)
685 _fieldsToAttrs = new HashMap();
686 _fieldsToAttrs.put(field.getName(), fmd.getName());
687 }
688
689 private void addAttributeTranslation() {
690 _pc.declareInterface(AttributeTranslator.class);
691 BCMethod method = _pc.declareMethod(PRE + "AttributeIndexToFieldName",
692 String.class, new Class[] { int.class });
693 method.makePublic();
694 Code code = method.getCode(true);
695
696 FieldMetaData[] fmds = _meta.getFields();
697
698 // switch (val)
699 code.iload().setParam(0);
700 TableSwitchInstruction tabins = code.tableswitch();
701 tabins.setLow(0);
702 tabins.setHigh(fmds.length - 1);
703
704 // case i:
705 // return <_attrsToFields.get(fmds[i].getName())>
706 for (int i = 0; i < fmds.length; i++) {
707 tabins.addTarget(code.constant().setValue(
708 _attrsToFields.get(fmds[i].getName())));
709 code.areturn();
710 }
711
712 // default: throw new IllegalArgumentException ()
713 tabins.setDefaultTarget(throwException
714 (code, IllegalArgumentException.class));
715
716 code.calculateMaxLocals();
717 code.calculateMaxStack();
718 }
719
720 /**
721 * Return the name of the setter method for the given field.
722 */
723 private static String getSetterName(FieldMetaData fmd) {
724 return "set" + StringUtils.capitalize(fmd.getName());
725 }
726
727 /**
728 * Return the field returned by the given method, or null if none.
729 * Package-protected and static for testing.
730 */
731 static BCField getReturnedField(BCMethod meth) {
732 return findField(meth, ((Code) AccessController.doPrivileged(
733 J2DoPrivHelper.newCodeAction())).xreturn()
734 .setType(meth.getReturnType()), false);
735 }
736
737 /**
738 * Return the field assigned in the given method, or null if none.
739 * Package-protected and static for testing.
740 */
741 static BCField getAssignedField(BCMethod meth) {
742 return findField(meth, ((Code) AccessController.doPrivileged(
743 J2DoPrivHelper.newCodeAction())).putfield(), true);
744 }
745
746 /**
747 * Return the field returned / assigned by <code>meth</code>. Returns
748 * null if non-fields (methods, literals, parameters, variables) are
749 * returned, or if non-parameters are assigned to fields.
750 */
751 private static BCField findField(BCMethod meth, Instruction template,
752 boolean findAccessed) {
753 // ignore any static methods. OpenJPA only currently supports
754 // non-static setters and getters
755 if (meth.isStatic())
756 return null;
757
758 Code code = meth.getCode(false);
759 if (code == null)
760 return null;
761 code.beforeFirst();
762
763 BCField field = null, cur;
764 Instruction templateIns, prevIns, earlierIns;
765 while (code.searchForward(template)) {
766 int backupCount = 3;
767 templateIns = code.previous();
768 if (!code.hasPrevious())
769 return null;
770 prevIns = code.previous();
771
772 if (prevIns instanceof ClassInstruction
773 && code.hasPrevious()) {
774 prevIns = code.previous();
775 backupCount++;
776 }
777
778 if (!code.hasPrevious())
779 return null;
780 earlierIns = code.previous();
781
782 // if the opcode two before the template was an aload_0, check
783 // against the middle instruction based on what type of find
784 // we're doing
785 if (!(earlierIns instanceof LoadInstruction)
786 || !((LoadInstruction) earlierIns).isThis())
787 return null;
788
789 // if the middle instruction was a getfield, then it's the
790 // field that's being accessed
791 if (!findAccessed && prevIns instanceof GetFieldInstruction) {
792 final FieldInstruction fPrevIns = (FieldInstruction) prevIns;
793 cur = (BCField) AccessController.doPrivileged(
794 J2DoPrivHelper.getFieldInstructionFieldAction(fPrevIns));
795 // if the middle instruction was an xload_1, then the
796 // matched instruction is the field that's being set.
797 } else if (findAccessed && prevIns instanceof LoadInstruction
798 && ((LoadInstruction) prevIns).getParam() == 0) {
799 final FieldInstruction fTemplateIns =
800 (FieldInstruction) templateIns;
801 cur = (BCField) AccessController.doPrivileged(J2DoPrivHelper
802 .getFieldInstructionFieldAction(fTemplateIns));
803 } else
804 return null;
805
806 if (field != null && cur != field)
807 return null;
808 field = cur;
809
810 // ready for next search iteration
811 while (backupCount > 0) {
812 code.next();
813 backupCount--;
814 }
815 }
816 return field;
817 }
818
819 /**
820 * Record a violation of the property access restrictions.
821 */
822 private void addViolation(String key, Object[] args, boolean fatal) {
823 if (_violations == null)
824 _violations = new HashSet();
825 _violations.add(_loc.get(key, args));
826 _fail |= fatal;
827 }
828
829 /**
830 * Log / throw recorded property access violations.
831 */
832 private void processViolations() {
833 if (_violations == null)
834 return;
835
836 String sep = J2DoPrivHelper.getLineSeparator();
837 StringBuffer buf = new StringBuffer();
838 for (Iterator itr = _violations.iterator(); itr.hasNext();) {
839 buf.append(itr.next());
840 if (itr.hasNext())
841 buf.append(sep);
842 }
843 Message msg = _loc.get("property-violations", buf);
844
845 if (_fail)
846 throw new UserException(msg);
847 if (_log.isWarnEnabled())
848 _log.warn(msg);
849 }
850
851 /**
852 * Replaced all direct access to managed fields with the appropriate
853 * pcGet/pcSet method. Note that this includes access to fields
854 * owned by PersistenceCapable classes other than this one.
855 */
856 private void replaceAndValidateFieldAccess() throws NoSuchMethodException {
857 // create template putfield/getfield instructions to search for
858 Code template = (Code) AccessController.doPrivileged(
859 J2DoPrivHelper.newCodeAction());
860 Instruction put = template.putfield();
861 Instruction get = template.getfield();
862 Instruction stat = template.invokestatic();
863
864 // look through all methods; this is done before any methods are added
865 // so we don't need to worry about excluding synthetic methods.
866 BCMethod[] methods = _managedType.getDeclaredMethods();
867 Code code;
868 for (int i = 0; i < methods.length; i++) {
869 code = methods[i].getCode(false);
870
871 // don't modify the methods specified by the auxiliary enhancers
872 if (code != null && !skipEnhance(methods[i])) {
873 replaceAndValidateFieldAccess(code, get, true, stat);
874 replaceAndValidateFieldAccess(code, put, false, stat);
875 }
876 }
877 }
878
879 /**
880 * Replaces all instructions matching the given template in the given
881 * code block with calls to the appropriate generated getter/setter.
882 *
883 * @param code the code block to modify; the code iterator will
884 * be placed before the first instruction on method start,
885 * and will be after the last instruction on method completion
886 * @param ins the template instruction to search for; either a
887 * getfield or putfield instruction
888 * @param get boolean indicating if this is a get instruction
889 * @param stat template invokestatic instruction to replace with
890 */
891 private void replaceAndValidateFieldAccess(Code code, Instruction ins,
892 boolean get, Instruction stat) throws NoSuchMethodException {
893 code.beforeFirst();
894
895 FieldInstruction fi;
896 MethodInstruction mi;
897 ClassMetaData owner;
898 String name, typeName, methodName;
899 while (code.searchForward(ins)) {
900 // back up to the matched instruction
901 fi = (FieldInstruction) code.previous();
902 name = fi.getFieldName();
903 typeName = fi.getFieldTypeName();
904 owner = getPersistenceCapableOwner(name, fi.getFieldDeclarerType());
905
906 if (owner != null
907 && owner.getAccessType() == ClassMetaData.ACCESS_PROPERTY) {
908 // if we're directly accessing a field in another class
909 // hierarchy that uses property access, something is wrong
910 if (owner != _meta && owner.getDeclaredField(name) != null &&
911 _meta != null && !owner.getDescribedType()
912 .isAssignableFrom(_meta.getDescribedType()))
913 throw new UserException(_loc.get("property-field-access",
914 new Object[]{ _meta, owner, name,
915 code.getMethod().getName() }));
916
917 // if we're directly accessing a property-backing field outside
918 // the property in our own class, notify user
919 if (isBackingFieldOfAnotherProperty(name, code))
920 addViolation("property-field-access", new Object[]{ _meta,
921 owner, name, code.getMethod().getName() }, false);
922 }
923
924 if (owner == null ||
925 owner.getDeclaredField(fromBackingFieldName(name)) == null) {
926 // not persistent field?
927 code.next();
928 continue;
929 } else if (!getRedefine() && !getCreateSubclass()
930 && owner.getAccessType() == ClassMetaData.ACCESS_FIELD) {
931 // replace the instruction with a call to the generated access
932 // method
933 mi = (MethodInstruction) code.set(stat);
934
935 // invoke the proper access method, whether getter or setter
936 String prefix = (get) ? PRE + "Get" : PRE + "Set";
937 methodName = prefix + name;
938 if (get) {
939 mi.setMethod(getType(owner).getName(),
940 methodName, typeName, new String[]
941 { getType(owner).getName() });
942 } else {
943 mi.setMethod(getType(owner).getName(),
944 methodName, "void", new String[]
945 { getType(owner).getName(), typeName });
946 }
947 code.next();
948 } else if (getRedefine()) {
949 name = fromBackingFieldName(name);
950 if (get) {
951 addNotifyAccess(code, owner.getField(name));
952 code.next();
953 } else {
954 // insert the set operations after the field mutation, but
955 // first load the old value for use in the
956 // StateManager.settingXXX method.
957 loadManagedInstance(code, false);
958 final FieldInstruction fFi = fi;
959 code.getfield().setField(
960 (BCField) AccessController.doPrivileged(J2DoPrivHelper
961 .getFieldInstructionFieldAction(fFi)));
962 int val = code.getNextLocalsIndex();
963 code.xstore().setLocal(val).setType(fi.getFieldType());
964
965 // move past the putfield
966 code.next();
967 addNotifyMutation(code, owner.getField(name), val, -1);
968 }
969 } else {
970 code.next();
971 }
972 code.calculateMaxLocals();
973 code.calculateMaxStack();
974 }
975 }
976
977 private void addNotifyAccess(Code code, FieldMetaData fmd) {
978 // PCHelper.accessingField(this, <absolute-index>);
979 code.aload().setThis();
980 code.constant().setValue(fmd.getIndex());
981 code.invokestatic().setMethod(RedefinitionHelper.class,
982 "accessingField", void.class,
983 new Class[] { Object.class, int.class });
984 }
985
986 /**
987 * This must be called after setting the value in the object.
988 *
989 * @param code
990 * @param val the position in the local variable table where the
991 * old value is stored
992 * @param param the parameter position containing the new value, or
993 * -1 if the new value is unavailable and should therefore be looked
994 * up.
995 * @throws NoSuchMethodException
996 */
997 private void addNotifyMutation(Code code, FieldMetaData fmd, int val,
998 int param)
999 throws NoSuchMethodException {
1000 // PCHelper.settingField(this, <absolute-index>, old, new);
1001 code.aload().setThis();
1002 code.constant().setValue(fmd.getIndex());
1003 Class type = fmd.getDeclaredType();
1004 // we only have special signatures for primitives and Strings
1005 if (!type.isPrimitive() && type != String.class)
1006 type = Object.class;
1007 code.xload().setLocal(val).setType(type);
1008 if (param == -1) {
1009 loadManagedInstance(code, false);
1010 addGetManagedValueCode(code, fmd);
1011 } else {
1012 code.xload().setParam(param).setType(type);
1013 }
1014 code.invokestatic().setMethod(RedefinitionHelper.class, "settingField",
1015 void.class, new Class[] {
1016 Object.class, int.class, type, type
1017 });
1018 }
1019
1020 /**
1021 * Return true if the given instruction accesses a field that is a backing
1022 * field of another property in this property-access class.
1023 */
1024 private boolean isBackingFieldOfAnotherProperty(String name, Code code) {
1025 String methName = code.getMethod().getName();
1026 return !"<init>".equals(methName)
1027 && _backingFields != null
1028 && !name.equals(_backingFields.get(methName))
1029 && _backingFields.containsValue(name);
1030 }
1031
1032 /**
1033 * Helper method to return the declaring PersistenceCapable class of
1034 * the given field.
1035 *
1036 * @param fieldName the name of the field
1037 * @param owner the nominal owner of the field
1038 * @return the metadata for the PersistenceCapable type that
1039 * declares the field (and therefore has the static method), or null if none
1040 */
1041 private ClassMetaData getPersistenceCapableOwner(String fieldName,
1042 Class owner) {
1043 // find the actual ancestor class that declares the field, then
1044 // check if the class is persistent, and if the field is managed
1045 Field f = Reflection.findField(owner, fieldName, false);
1046 if (f == null)
1047 return null;
1048
1049 // managed interface
1050 if (_meta != null && _meta.getDescribedType().isInterface())
1051 return _meta;
1052
1053 return _repos.getMetaData(f.getDeclaringClass(), null, false);
1054 }
1055
1056 /**
1057 * Adds all synthetic methods to the bytecode by delegating to
1058 * the various addXXXMethods () functions in this class. Includes
1059 * all static field access methods.
1060 * Note that the 'stock' methods like <code>pcIsTransactional</code>,
1061 * <code>pcFetchObjectId</code>, etc are defined only in the
1062 * least-derived PersistenceCapable type.
1063 */
1064 private void addPCMethods()
1065 throws NoSuchMethodException {
1066 addClearFieldsMethod();
1067 addNewInstanceMethod(true);
1068 addNewInstanceMethod(false);
1069 addManagedFieldCountMethod();
1070 addReplaceFieldsMethods();
1071 addProvideFieldsMethods();
1072 addCopyFieldsMethod();
1073
1074 if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
1075 addStockMethods();
1076 addGetVersionMethod();
1077 addReplaceStateManagerMethod();
1078
1079 if (_meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
1080 addNoOpApplicationIdentityMethods();
1081 }
1082
1083 // add the app id methods to each subclass rather
1084 // than just the superclass, since it is possible to have
1085 // a subclass with an app id hierarchy that matches the
1086 // persistent class inheritance hierarchy
1087 if (_meta.getIdentityType() == ClassMetaData.ID_APPLICATION
1088 && (_meta.getPCSuperclass() == null || getCreateSubclass() ||
1089 _meta.getObjectIdType() !=
1090 _meta.getPCSuperclassMetaData().getObjectIdType())) {
1091 addCopyKeyFieldsToObjectIdMethod(true);
1092 addCopyKeyFieldsToObjectIdMethod(false);
1093 addCopyKeyFieldsFromObjectIdMethod(true);
1094 addCopyKeyFieldsFromObjectIdMethod(false);
1095 addNewObjectIdInstanceMethod(true);
1096 addNewObjectIdInstanceMethod(false);
1097 }
1098 }
1099
1100 /**
1101 * Add a method to clear all persistent fields; we'll call this from
1102 * the new instance method to ensure that unloaded fields have
1103 * default values.
1104 */
1105 private void addClearFieldsMethod()
1106 throws NoSuchMethodException {
1107 // protected void pcClearFields ()
1108 BCMethod method = _pc.declareMethod(PRE + "ClearFields", void.class,
1109 null);
1110 method.makeProtected();
1111 Code code = method.getCode(true);
1112
1113 // super.pcClearFields ()
1114 if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
1115 code.aload().setThis();
1116 code.invokespecial().setMethod(getType(_meta.
1117 getPCSuperclassMetaData()), PRE + "ClearFields", void.class,
1118 null);
1119 }
1120
1121 FieldMetaData[] fmds = _meta.getDeclaredFields();
1122 for (int i = 0; i < fmds.length; i++) {
1123 if (fmds[i].getManagement() != FieldMetaData.MANAGE_PERSISTENT)
1124 continue;
1125
1126 loadManagedInstance(code, false);
1127 switch (fmds[i].getDeclaredTypeCode()) {
1128 case JavaTypes.BOOLEAN:
1129 case JavaTypes.BYTE:
1130 case JavaTypes.CHAR:
1131 case JavaTypes.INT:
1132 case JavaTypes.SHORT:
1133 code.constant().setValue(0);
1134 break;
1135 case JavaTypes.DOUBLE:
1136 code.constant().setValue(0D);
1137 break;
1138 case JavaTypes.FLOAT:
1139 code.constant().setValue(0F);
1140 break;
1141 case JavaTypes.LONG:
1142 code.constant().setValue(0L);
1143 break;
1144 default:
1145 code.constant().setNull();
1146 break;
1147 }
1148
1149 addSetManagedValueCode(code, fmds[i]);
1150 }
1151
1152 code.vreturn();
1153 code.calculateMaxStack();
1154 code.calculateMaxLocals();
1155 }
1156
1157 /**
1158 * Adds the <code>pcNewInstance</code> method to the bytecode.
1159 * These methods are used by the impl helper to create new
1160 * managed instances efficiently without reflection.
1161 *
1162 * @param oid set to true to mimic the method version that takes
1163 * an oid value as well as a state manager
1164 */
1165 private void addNewInstanceMethod(boolean oid) {
1166 // public PersistenceCapable pcNewInstance (...)
1167 Class[] args =
1168 (oid) ? new Class[]{ SMTYPE, Object.class, boolean.class }
1169 : new Class[]{ SMTYPE, boolean.class };
1170 BCMethod method = _pc.declareMethod(PRE + "NewInstance", PCTYPE, args);
1171 Code code = method.getCode(true);
1172
1173 // if the type is abstract, throw a UserException
1174 if (_pc.isAbstract()) {
1175 throwException(code, USEREXCEP);
1176 code.vreturn();
1177
1178 code.calculateMaxStack();
1179 code.calculateMaxLocals();
1180 return;
1181 }
1182
1183 // XXX pc = new XXX ();
1184 code.anew().setType(_pc);
1185 code.dup();
1186 code.invokespecial().setMethod("<init>", void.class, null);
1187 int inst = code.getNextLocalsIndex();
1188 code.astore().setLocal(inst);
1189
1190 // if (clear)
1191 // pc.pcClearFields ();
1192 code.iload().setParam((oid) ? 2 : 1);
1193 JumpInstruction noclear = code.ifeq();
1194 code.aload().setLocal(inst);
1195 code.invokevirtual().setMethod(PRE + "ClearFields", void.class, null);
1196
1197 // pc.pcStateManager = sm;
1198 noclear.setTarget(code.aload().setLocal(inst));
1199 code.aload().setParam(0);
1200 code.putfield().setField(SM, SMTYPE);
1201
1202 // copy key fields from oid
1203 if (oid) {
1204 code.aload().setLocal(inst);
1205 code.aload().setParam(1);
1206 code.invokevirtual().setMethod(PRE + "CopyKeyFieldsFromObjectId",
1207 void.class, new Class[]{ Object.class });
1208 }
1209
1210 // return pc;
1211 code.aload().setLocal(inst);
1212 code.areturn();
1213
1214 code.calculateMaxStack();
1215 code.calculateMaxLocals();
1216 }
1217
1218 /**
1219 * Adds the <code>protected static int pcGetManagedFieldCount ()</code>
1220 * method to the bytecode, returning the inherited field count added
1221 * to the number of managed fields in the current PersistenceCapable class.
1222 */
1223 private void addManagedFieldCountMethod() {
1224 // protected static int pcGetManagedFieldCount ()
1225 BCMethod method = _pc.declareMethod(PRE + "GetManagedFieldCount",
1226 int.class, null);
1227 method.setStatic(true);
1228 method.makeProtected();
1229 Code code = method.getCode(true);
1230
1231 // return <fields> + pcInheritedFieldCount
1232 // awhite: the above should work, but I'm seeing a messed up situation
1233 // all of a sudden where when a subclass calls this method, it somehow
1234 // happens before <clinit> is ever invoked, and so our
1235 // pcInheritedFieldCount field isn't initialized! so instead,
1236 // return <fields> + <superclass>.pcGetManagedFieldCount ()
1237 code.constant().setValue(_meta.getDeclaredFields().length);
1238 if (_meta.getPCSuperclass() != null) {
1239 Class superClass = getType(_meta.getPCSuperclassMetaData());
1240 String superName = getCreateSubclass() ?
1241 PCEnhancer.toPCSubclassName(superClass) :
1242 superClass.getName();
1243 code.invokestatic().setMethod(superName,
1244 PRE + "GetManagedFieldCount", int.class.getName(), null);
1245 code.iadd();
1246 }
1247 code.ireturn();
1248 code.calculateMaxStack();
1249 }
1250
1251 /**
1252 * Adds the {@link PersistenceCapable#pcProvideField} and
1253 * {@link PersistenceCapable#pcProvideFields} methods to the bytecode.
1254 */
1255 private void addProvideFieldsMethods()
1256 throws NoSuchMethodException {
1257 // public void pcProvideField (int fieldNumber)
1258 BCMethod method = _pc.declareMethod(PRE + "ProvideField", void.class,
1259 new Class[]{ int.class });
1260 Code code = method.getCode(true);
1261
1262 // adds everything through the switch ()
1263 int relLocal = beginSwitchMethod(PRE + "ProvideField", code);
1264
1265 // if no fields in this inst, just throw exception
1266 FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
1267 : _meta.getDeclaredFields();
1268 if (fmds.length == 0)
1269 throwException(code, IllegalArgumentException.class);
1270 else {
1271 // switch (val)
1272 code.iload().setLocal(relLocal);
1273 TableSwitchInstruction tabins = code.tableswitch();
1274 tabins.setLow(0);
1275 tabins.setHigh(fmds.length - 1);
1276
1277 // <field> = pcStateManager.provided<type>Field
1278 // (this, fieldNumber);
1279 for (int i = 0; i < fmds.length; i++) {
1280 tabins.addTarget(loadManagedInstance(code, false));
1281 code.getfield().setField(SM, SMTYPE);
1282 loadManagedInstance(code, false);
1283 code.iload().setParam(0);
1284 loadManagedInstance(code, false);
1285 addGetManagedValueCode(code, fmds[i]);
1286 code.invokeinterface().setMethod(getStateManagerMethod
1287 (fmds[i].getDeclaredType(), "provided", false, false));
1288 code.vreturn();
1289 }
1290
1291 // default: throw new IllegalArgumentException ()
1292 tabins.setDefaultTarget(throwException
1293 (code, IllegalArgumentException.class));
1294 }
1295
1296 code.calculateMaxStack();
1297 code.calculateMaxLocals();
1298
1299 addMultipleFieldsMethodVersion(method);
1300 }
1301
1302 /**
1303 * Adds the {@link PersistenceCapable#pcReplaceField} and
1304 * {@link PersistenceCapable#pcReplaceFields} methods to the bytecode.
1305 */
1306 private void addReplaceFieldsMethods()
1307 throws NoSuchMethodException {
1308 // public void pcReplaceField (int fieldNumber)
1309 BCMethod method = _pc.declareMethod(PRE + "ReplaceField", void.class,
1310 new Class[]{ int.class });
1311 Code code = method.getCode(true);
1312
1313 // adds everything through the switch ()
1314 int relLocal = beginSwitchMethod(PRE + "ReplaceField", code);
1315
1316 // if no fields in this inst, just throw exception
1317 FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
1318 : _meta.getDeclaredFields();
1319 if (fmds.length == 0)
1320 throwException(code, IllegalArgumentException.class);
1321 else {
1322 // switch (val)
1323 code.iload().setLocal(relLocal);
1324 TableSwitchInstruction tabins = code.tableswitch();
1325 tabins.setLow(0);
1326 tabins.setHigh(fmds.length - 1);
1327
1328 // <field> = pcStateManager.replace<type>Field
1329 // (this, fieldNumber);
1330 for (int i = 0; i < fmds.length; i++) {
1331 // for the addSetManagedValueCode call below.
1332 tabins.addTarget(loadManagedInstance(code, false));
1333
1334 loadManagedInstance(code, false);
1335 code.getfield().setField(SM, SMTYPE);
1336 loadManagedInstance(code, false);
1337 code.iload().setParam(0);
1338 code.invokeinterface().setMethod(getStateManagerMethod
1339 (fmds[i].getDeclaredType(), "replace", true, false));
1340 if (!fmds[i].getDeclaredType().isPrimitive())
1341 code.checkcast().setType(fmds[i].getDeclaredType());
1342
1343 addSetManagedValueCode(code, fmds[i]);
1344 code.vreturn();
1345 }
1346
1347 // default: throw new IllegalArgumentException ()
1348 tabins.setDefaultTarget(throwException
1349 (code, IllegalArgumentException.class));
1350 }
1351
1352 code.calculateMaxStack();
1353 code.calculateMaxLocals();
1354
1355 addMultipleFieldsMethodVersion(method);
1356 }
1357
1358 /**
1359 * Adds the {@link PersistenceCapable#pcCopyFields} method to the bytecode.
1360 */
1361 private void addCopyFieldsMethod()
1362 throws NoSuchMethodException {
1363 // public void pcCopyField (Object pc, int field)
1364 BCMethod method = _pc.declareMethod(PRE + "CopyField",
1365 void.class.getName(),
1366 new String[]{ _managedType.getName(), int.class.getName() });
1367 method.makeProtected();
1368 Code code = method.getCode(true);
1369
1370 // adds everything through the switch ()
1371 int relLocal = beginSwitchMethod(PRE + "CopyField", code);
1372
1373 // if no fields in this inst, just throw exception
1374 FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
1375 : _meta.getDeclaredFields();
1376 if (fmds.length == 0)
1377 throwException(code, IllegalArgumentException.class);
1378 else {
1379 // switch (val)
1380 code.iload().setLocal(relLocal);
1381 TableSwitchInstruction tabins = code.tableswitch();
1382 tabins.setLow(0);
1383 tabins.setHigh(fmds.length - 1);
1384
1385 for (int i = 0; i < fmds.length; i++) {
1386 // <field> = other.<field>;
1387 // or set<field> (other.get<field>);
1388 tabins.addTarget(loadManagedInstance(code, false));
1389 code.aload().setParam(0);
1390 addGetManagedValueCode(code, fmds[i], false);
1391 addSetManagedValueCode(code, fmds[i]);
1392
1393 // break;
1394 code.vreturn();
1395 }
1396
1397 // default: throw new IllegalArgumentException ()
1398 tabins.setDefaultTarget(throwException
1399 (code, IllegalArgumentException.class));
1400 }
1401
1402 code.calculateMaxStack();
1403 code.calculateMaxLocals();
1404
1405 addMultipleFieldsMethodVersion(method);
1406 }
1407
1408 /**
1409 * Helper method to add the code common to the beginning of both the
1410 * pcReplaceField method and the pcProvideField method. This includes
1411 * calculating the relative field number of the desired field and calling
1412 * the superclass if necessary.
1413 *
1414 * @return the index in which the local variable holding the relative
1415 * field number is stored
1416 */
1417 private int beginSwitchMethod(String name, Code code) {
1418 boolean copy = (PRE + "CopyField").equals(name);
1419 int fieldNumber = (copy) ? 1 : 0;
1420
1421 int relLocal = code.getNextLocalsIndex();
1422 if (getCreateSubclass()) {
1423 code.iload().setParam(fieldNumber);
1424 code.istore().setLocal(relLocal);
1425 return relLocal;
1426 }
1427
1428 // int rel = fieldNumber - pcInheritedFieldCount
1429 code.iload().setParam(fieldNumber);
1430 code.getstatic().setField(INHERIT, int.class);
1431 code.isub();
1432 code.istore().setLocal(relLocal);
1433 code.iload().setLocal(relLocal);
1434
1435 // super: if (rel < 0) super.pcReplaceField (fieldNumber); return;
1436 // no super: if (rel < 0) throw new IllegalArgumentException ();
1437 JumpInstruction ifins = code.ifge();
1438 if (_meta.getPCSuperclass() != null) {
1439 loadManagedInstance(code, false);
1440 String[] args;
1441 if (copy) {
1442 args = new String[]{ getType(_meta.getPCSuperclassMetaData()).
1443 getName(), int.class.getName() };
1444 code.aload().setParam(0);
1445 } else
1446 args = new String[]{ int.class.getName() };
1447 code.iload().setParam(fieldNumber);
1448 code.invokespecial().setMethod(getType(_meta.
1449 getPCSuperclassMetaData()).getName(), name,
1450 void.class.getName(), args);
1451 code.vreturn();
1452 } else
1453 throwException(code, IllegalArgumentException.class);
1454
1455 ifins.setTarget(code.nop());
1456 return relLocal;
1457 }
1458
1459 /**
1460 * This helper method, given the pcReplaceField or pcProvideField
1461 * method, adds the bytecode for the corresponding 'plural' version
1462 * of the method -- the version that takes an int[] of fields to
1463 * to access rather than a single field. The multiple fields version
1464 * simply loops through the provided indexes and delegates to the
1465 * singular version for each one.
1466 */
1467 private void addMultipleFieldsMethodVersion(BCMethod single) {
1468 boolean copy = (PRE + "CopyField").equals(single.getName());
1469
1470 // public void <method>s (int[] fields)
1471 Class[] args = (copy) ? new Class[]{ Object.class, int[].class }
1472 : new Class[]{ int[].class };
1473 BCMethod method = _pc.declareMethod(single.getName() + "s",
1474 void.class, args);
1475 Code code = method.getCode(true);
1476
1477 int fieldNumbers = 0;
1478 int inst = 0;
1479 if (copy) {
1480 fieldNumbers = 1;
1481
1482 if (getCreateSubclass()) {
1483 // get the managed instance into the local variable table
1484 code.aload().setParam(0);
1485 code.invokestatic().setMethod(ImplHelper.class,
1486 "getManagedInstance", Object.class,
1487 new Class[] { Object.class });
1488 code.checkcast().setType(_managedType);
1489 inst = code.getNextLocalsIndex();
1490 code.astore().setLocal(inst);
1491
1492 // there might be a difference between the classes of 'this'
1493 // vs 'other' in this context; use the PC methods to get the SM
1494 code.aload().setParam(0);
1495 code.aload().setThis();
1496 code.getfield().setField(SM, SMTYPE);
1497 code.invokestatic().setMethod(ImplHelper.class,
1498 "toPersistenceCapable", PersistenceCapable.class,
1499 new Class[] { Object.class, Object.class });
1500 code.invokeinterface().setMethod(PersistenceCapable.class,
1501 "pcGetStateManager", StateManager.class, null);
1502 } else {
1503 // XXX other = (XXX) pc;
1504 code.aload().setParam(0);
1505 code.checkcast().setType(_pc);
1506 inst = code.getNextLocalsIndex();
1507 code.astore().setLocal(inst);
1508
1509 // access the other's sm field directly
1510 code.aload().setLocal(inst);
1511 code.getfield().setField(SM, SMTYPE);
1512 }
1513
1514 // if (other.pcStateManager != pcStateManager)
1515 // throw new IllegalArgumentException
1516
1517 loadManagedInstance(code, false);
1518 code.getfield().setField(SM, SMTYPE);
1519 JumpInstruction ifins = code.ifacmpeq();
1520 throwException(code, IllegalArgumentException.class);
1521 ifins.setTarget(code.nop());
1522
1523 // if (pcStateManager == null)
1524 // throw new IllegalStateException
1525 loadManagedInstance(code, false);
1526 code.getfield().setField(SM, SMTYPE);
1527 ifins = code.ifnonnull();
1528 throwException(code, IllegalStateException.class);
1529 ifins.setTarget(code.nop());
1530 }
1531
1532 // for (int i = 0;
1533 code.constant().setValue(0);
1534 int idx = code.getNextLocalsIndex();
1535 code.istore().setLocal(idx);
1536 JumpInstruction testins = code.go2();
1537
1538 // <method> (fields[i]);
1539 Instruction bodyins = loadManagedInstance(code, false);
1540 if (copy)
1541 code.aload().setLocal(inst);
1542 code.aload().setParam(fieldNumbers);
1543 code.iload().setLocal(idx);
1544 code.iaload();
1545 code.invokevirtual().setMethod(single);
1546
1547 // i++;
1548 code.iinc().setIncrement(1).setLocal(idx);
1549
1550 // i < fields.length
1551 testins.setTarget(code.iload().setLocal(idx));
1552 code.aload().setParam(fieldNumbers);
1553 code.arraylength();
1554 code.ificmplt().setTarget(bodyins);
1555 code.vreturn();
1556
1557 code.calculateMaxStack();
1558 code.calculateMaxLocals();
1559 }
1560
1561 /**
1562 * Adds the 'stock' methods to the bytecode; these include methods
1563 * like {@link PersistenceCapable#pcFetchObjectId}
1564 * and {@link PersistenceCapable#pcIsTransactional}.
1565 */
1566 private void addStockMethods()
1567 throws NoSuchMethodException {
1568 try {
1569 // pcGetGenericContext
1570 translateFromStateManagerMethod(
1571 (Method) AccessController.doPrivileged(
1572 J2DoPrivHelper.getDeclaredMethodAction(
1573 SMTYPE, "get" + CONTEXTNAME, (Class[]) null)), false);
1574
1575 // pcFetchObjectId
1576 translateFromStateManagerMethod(
1577 (Method) AccessController.doPrivileged(
1578 J2DoPrivHelper.getDeclaredMethodAction(
1579 SMTYPE, "fetchObjectId", (Class[]) null)), false);
1580
1581 // pcIsDeleted
1582 translateFromStateManagerMethod(
1583 (Method) AccessController.doPrivileged(
1584 J2DoPrivHelper.getDeclaredMethodAction(
1585 SMTYPE, "isDeleted", (Class[]) null)), false);
1586
1587 // pcIsDirty
1588 translateFromStateManagerMethod(
1589 (Method) AccessController.doPrivileged(
1590 J2DoPrivHelper.getDeclaredMethodAction(
1591 SMTYPE, "isDirty", (Class[]) null)), true);
1592
1593 // pcIsNew
1594 translateFromStateManagerMethod(
1595 (Method) AccessController.doPrivileged(
1596 J2DoPrivHelper.getDeclaredMethodAction(
1597 SMTYPE, "isNew", (Class[]) null)), false);
1598
1599 // pcIsPersistent
1600 translateFromStateManagerMethod(
1601 (Method) AccessController.doPrivileged(
1602 J2DoPrivHelper.getDeclaredMethodAction(
1603 SMTYPE, "isPersistent", (Class[]) null)), false);
1604
1605 // pcIsTransactional
1606 translateFromStateManagerMethod(
1607 (Method) AccessController.doPrivileged(
1608 J2DoPrivHelper.getDeclaredMethodAction(
1609 SMTYPE, "isTransactional", (Class[]) null)), false);
1610
1611 // pcSerializing
1612 translateFromStateManagerMethod(
1613 (Method) AccessController.doPrivileged(
1614 J2DoPrivHelper.getDeclaredMethodAction(
1615 SMTYPE, "serializing", (Class[]) null)), false);
1616
1617 // pcDirty
1618 translateFromStateManagerMethod(
1619 (Method) AccessController.doPrivileged(
1620 J2DoPrivHelper.getDeclaredMethodAction(
1621 SMTYPE, "dirty", new Class[]{ String.class })), false);
1622
1623 // pcGetStateManager
1624 BCMethod meth = _pc.declareMethod(PRE + "GetStateManager",
1625 StateManager.class, null);
1626 Code code = meth.getCode(true);
1627 loadManagedInstance(code, false);
1628 code.getfield().setField(SM, StateManager.class);
1629 code.areturn();
1630 code.calculateMaxStack();
1631 code.calculateMaxLocals();
1632 } catch (PrivilegedActionException pae) {
1633 throw (NoSuchMethodException) pae.getException();
1634 }
1635 }
1636
1637 /**
1638 * Helper method to add a stock method to the bytecode. Each
1639 * stock method simply delegates to a corresponding StateManager method.
1640 * Given the StateManager method, then, this function translates it into
1641 * the wrapper method that should be added to the bytecode.
1642 */
1643 private void translateFromStateManagerMethod(Method m,
1644 boolean isDirtyCheckMethod) {
1645 // form the name of the method by prepending 'pc' to the sm method
1646 String name = PRE + StringUtils.capitalize(m.getName());
1647 Class[] params = m.getParameterTypes();
1648 Class returnType = m.getReturnType();
1649
1650 // add the method to the pc
1651 BCMethod method = _pc.declareMethod(name, returnType, params);
1652 Code code = method.getCode(true);
1653
1654 // if (pcStateManager == null) return <default>;
1655 loadManagedInstance(code, false);
1656 code.getfield().setField(SM, SMTYPE);
1657 JumpInstruction ifins = code.ifnonnull();
1658 if (returnType.equals(boolean.class))
1659 code.constant().setValue(false);
1660 else if (!returnType.equals(void.class))
1661 code.constant().setNull();
1662 code.xreturn().setType(returnType);
1663
1664 // if this is the dirty-check method and we're subclassing but not
1665 // redefining, hook into PCHelper to do the dirty check
1666 if (isDirtyCheckMethod && !getRedefine()) {
1667 // RedefinitionHelper.dirtyCheck(sm);
1668 ifins.setTarget(loadManagedInstance(code, false));
1669 code.getfield().setField(SM, SMTYPE);
1670 code.dup(); // for the return statement below
1671 code.invokestatic().setMethod(RedefinitionHelper.class,
1672 "dirtyCheck", void.class, new Class[] { SMTYPE });
1673 } else {
1674 ifins.setTarget(loadManagedInstance(code, false));
1675 code.getfield().setField(SM, SMTYPE);
1676 }
1677
1678 // return pcStateManager.<method> (<args>);
1679 // managed instance loaded above in if-else block
1680 for (int i = 0; i < params.length; i++)
1681 code.xload().setParam(i);
1682 code.invokeinterface().setMethod(m);
1683 code.xreturn().setType(returnType);
1684
1685 code.calculateMaxStack();
1686 code.calculateMaxLocals();
1687 }
1688
1689 /**
1690 * Adds the {@link PersistenceCapable#pcGetVersion} method to the bytecode.
1691 */
1692 private void addGetVersionMethod()
1693 throws NoSuchMethodException {
1694 BCMethod method = _pc.declareMethod(PRE + "GetVersion", Object.class,
1695 null);
1696 Code code = method.getCode(true);
1697
1698 // if (pcStateManager == null)
1699 loadManagedInstance(code, false);
1700 code.getfield().setField(SM, SMTYPE);
1701 JumpInstruction ifins = code.ifnonnull();
1702 FieldMetaData versionField = _meta.getVersionField();
1703
1704 if (versionField == null)
1705 code.constant().setNull(); // return null;
1706 else {
1707 // return <versionField>;
1708 Class wrapper = toPrimitiveWrapper(versionField);
1709 if (wrapper != versionField.getDeclaredType()) {
1710 code.anew().setType(wrapper);
1711 code.dup();
1712 }
1713 loadManagedInstance(code, false);
1714 addGetManagedValueCode(code, versionField);
1715 if (wrapper != versionField.getDeclaredType())
1716 code.invokespecial().setMethod(wrapper, "<init>", void.class,
1717 new Class[]{ versionField.getDeclaredType() });
1718 }
1719 code.areturn();
1720
1721 // return pcStateManager.getVersion ();
1722 ifins.setTarget(loadManagedInstance(code, false));
1723 code.getfield().setField(SM, SMTYPE);
1724 code.invokeinterface().setMethod(SMTYPE, "getVersion", Object.class,
1725 null);
1726 code.areturn();
1727
1728 code.calculateMaxStack();
1729 code.calculateMaxLocals();
1730 }
1731
1732 /**
1733 * Return the version field type as a primitive wrapper, or null if
1734 * the version field is not primitive.
1735 */
1736 private Class toPrimitiveWrapper(FieldMetaData fmd) {
1737 switch (fmd.getDeclaredTypeCode()) {
1738 case JavaTypes.BOOLEAN:
1739 return Boolean.class;
1740 case JavaTypes.BYTE:
1741 return Byte.class;
1742 case JavaTypes.CHAR:
1743 return Character.class;
1744 case JavaTypes.DOUBLE:
1745 return Double.class;
1746 case JavaTypes.FLOAT:
1747 return Float.class;
1748 case JavaTypes.INT:
1749 return Integer.class;
1750 case JavaTypes.LONG:
1751 return Long.class;
1752 case JavaTypes.SHORT:
1753 return Short.class;
1754 }
1755 return fmd.getDeclaredType();
1756 }
1757
1758 /**
1759 * Adds the {@link PersistenceCapable#pcReplaceStateManager}
1760 * method to the bytecode.
1761 */
1762 private void addReplaceStateManagerMethod() {
1763 // public void pcReplaceStateManager (StateManager sm)
1764 BCMethod method = _pc.declareMethod(PRE + "ReplaceStateManager",
1765 void.class, new Class[]{ SMTYPE });
1766 method.setSynchronized(true);
1767 method.getExceptions(true).addException(SecurityException.class);
1768 Code code = method.getCode(true);
1769
1770 // if (pcStateManager != null)
1771 // pcStateManager = pcStateManager.replaceStateManager(sm);
1772 loadManagedInstance(code, false);
1773 code.getfield().setField(SM, SMTYPE);
1774 JumpInstruction ifins = code.ifnull();
1775 loadManagedInstance(code, false);
1776 loadManagedInstance(code, false);
1777 code.getfield().setField(SM, SMTYPE);
1778 code.aload().setParam(0);
1779 code.invokeinterface().setMethod(SMTYPE, "replaceStateManager",
1780 SMTYPE, new Class[]{ SMTYPE });
1781 code.putfield().setField(SM, SMTYPE);
1782 code.vreturn();
1783
1784 // SecurityManager sec = System.getSecurityManager ();
1785 // if (sec != null)
1786 // sec.checkPermission (Permission.SET_STATE_MANAGER);
1787 ifins.setTarget(code.invokestatic().setMethod(System.class,
1788 "getSecurityManager", SecurityManager.class, null));
1789
1790 // pcStateManager = sm;
1791 ifins.setTarget(loadManagedInstance(code, false));
1792 code.aload().setParam(0);
1793 code.putfield().setField(SM, SMTYPE);
1794 code.vreturn();
1795
1796 code.calculateMaxStack();
1797 code.calculateMaxLocals();
1798 }
1799
1800 /**
1801 * Creates the PersistenceCapable methods dealing with application
1802 * identity and gives them no-op implementations.
1803 */
1804 private void addNoOpApplicationIdentityMethods() {
1805 // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
1806 // Object oid)
1807 BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
1808 void.class, new Class[]{ OIDFSTYPE, Object.class });
1809 Code code = method.getCode(true);
1810 code.vreturn();
1811 code.calculateMaxLocals();
1812
1813 // public void pcCopyKeyFieldsToObjectId (Object oid)
1814 method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
1815 void.class, new Class[]{ Object.class });
1816 code = method.getCode(true);
1817 code.vreturn();
1818 code.calculateMaxLocals();
1819
1820 // public void pcCopyKeyFieldsFromObjectId (ObjectIdFieldConsumer fc,
1821 // Object oid)
1822 method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
1823 void.class, new Class[]{ OIDFCTYPE, Object.class });
1824 code = method.getCode(true);
1825 code.vreturn();
1826 code.calculateMaxLocals();
1827
1828 // public void pcCopyKeyFieldsFromObjectId (Object oid)
1829 method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
1830 void.class, new Class[]{ Object.class });
1831 code = method.getCode(true);
1832 code.vreturn();
1833 code.calculateMaxLocals();
1834
1835 // public Object pcNewObjectIdInstance ()
1836 method = _pc.declareMethod(PRE + "NewObjectIdInstance",
1837 Object.class, null);
1838 code = method.getCode(true);
1839 code.constant().setNull();
1840 code.areturn();
1841 code.calculateMaxStack();
1842 code.calculateMaxLocals();
1843
1844 // public Object pcNewObjectIdInstance (Object obj)
1845 method = _pc.declareMethod(PRE + "NewObjectIdInstance",
1846 Object.class, new Class[]{ Object.class });
1847 code = method.getCode(true);
1848 code.constant().setNull();
1849 code.areturn();
1850 code.calculateMaxStack();
1851 code.calculateMaxLocals();
1852 }
1853
1854 /**
1855 * Adds the <code>pcCopyKeyFieldsToObjectId</code> methods
1856 * to classes using application identity.
1857 */
1858 private void addCopyKeyFieldsToObjectIdMethod(boolean fieldManager)
1859 throws NoSuchMethodException {
1860 // public void pcCopyKeyFieldsToObjectId (ObjectIdFieldSupplier fs,
1861 // Object oid)
1862 String[] args = (fieldManager) ?
1863 new String[]{ OIDFSTYPE.getName(), Object.class.getName() }
1864 : new String[]{ Object.class.getName() };
1865 BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsToObjectId",
1866 void.class.getName(), args);
1867 Code code = method.getCode(true);
1868
1869 // single field identity always throws exception
1870 if (_meta.isOpenJPAIdentity()) {
1871 throwException(code, INTERNEXCEP);
1872 code.vreturn();
1873
1874 code.calculateMaxStack();
1875 code.calculateMaxLocals();
1876 return;
1877 }
1878
1879 // call superclass method
1880 if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
1881 loadManagedInstance(code, false);
1882 for (int i = 0; i < args.length; i++)
1883 code.aload().setParam(i);
1884 code.invokespecial().setMethod(getType(_meta.
1885 getPCSuperclassMetaData()).getName(),
1886 PRE + "CopyKeyFieldsToObjectId", void.class.getName(), args);
1887 }
1888
1889 // Object id = oid;
1890 if (fieldManager)
1891 code.aload().setParam(1);
1892 else
1893 code.aload().setParam(0);
1894
1895 if (_meta.isObjectIdTypeShared()) {
1896 // oid = ((ObjectId) id).getId ();
1897 code.checkcast().setType(ObjectId.class);
1898 code.invokevirtual().setMethod(ObjectId.class, "getId",
1899 Object.class, null);
1900 }
1901
1902 // <oid type> id = (<oid type>) oid;
1903 int id = code.getNextLocalsIndex();
1904 Class oidType = _meta.getObjectIdType();
1905 code.checkcast().setType(oidType);
1906 code.astore().setLocal(id);
1907
1908 // int inherited = pcInheritedFieldCount;
1909 int inherited = 0;
1910 if (fieldManager) {
1911 code.getstatic().setField(INHERIT, int.class);
1912 inherited = code.getNextLocalsIndex();
1913 code.istore().setLocal(inherited);
1914 }
1915
1916 // id.<field> = fs.fetch<type>Field (<index>); or...
1917 // id.<field> = pc.<field>;
1918 FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
1919 : _meta.getDeclaredFields();
1920 Class type;
1921 String name;
1922 Field field;
1923 Method setter;
1924 boolean reflect;
1925 for (int i = 0; i < fmds.length; i++) {
1926 if (!fmds[i].isPrimaryKey())
1927 continue;
1928 code.aload().setLocal(id);
1929
1930 name = fmds[i].getName();
1931 type = fmds[i].getObjectIdFieldType();
1932 if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
1933 setter = null;
1934 field = Reflection.findField(oidType, name, true);
1935 reflect = !Modifier.isPublic(field.getModifiers());
1936 if (reflect) {
1937 code.classconstant().setClass(oidType);
1938 code.constant().setValue(name);
1939 code.constant().setValue(true);
1940 code.invokestatic().setMethod(Reflection.class,
1941 "findField", Field.class, new Class[] { Class.class,
1942 String.class, boolean.class });
1943 }
1944 } else {
1945 field = null;
1946 setter = Reflection.findSetter(oidType, name, type, true);
1947 reflect = !Modifier.isPublic(setter.getModifiers());
1948 if (reflect) {
1949 code.classconstant().setClass(oidType);
1950 code.constant().setValue(name);
1951 code.classconstant().setClass(type);
1952 code.constant().setValue(true);
1953 code.invokestatic().setMethod(Reflection.class,
1954 "findSetter", Method.class, new Class[] { Class.class,
1955 String.class, Class.class, boolean.class });
1956 }
1957 }
1958
1959 if (fieldManager) {
1960 code.aload().setParam(0);
1961 code.constant().setValue(i);
1962 code.iload().setLocal(inherited);
1963 code.iadd();
1964 code.invokeinterface().setMethod
1965 (getFieldSupplierMethod(type));
1966
1967 // if the type of this field meta data is
1968 // non-primitive and non-string, be sure to cast
1969 // to the appropriate type.
1970 if (!reflect && !type.isPrimitive()
1971 && !type.getName().equals(String.class.getName()))
1972 code.checkcast().setType(type);
1973 } else {
1974 loadManagedInstance(code, false);
1975 addGetManagedValueCode(code, fmds[i]);
1976
1977 // get id/pk from pc instance
1978 if (fmds[i].getDeclaredTypeCode() == JavaTypes.PC)
1979 addExtractObjectIdFieldValueCode(code, fmds[i]);
1980 }
1981
1982 if (reflect && field != null) {
1983 code.invokestatic().setMethod(Reflection.class, "set",
1984 void.class, new Class[] { Object.class, Field.class,
1985 (type.isPrimitive()) ? type : Object.class });
1986 } else if (reflect) {
1987 code.invokestatic().setMethod(Reflection.class, "set",
1988 void.class, new Class[] { Object.class, Method.class,
1989 (type.isPrimitive()) ? type : Object.class });
1990 } else if (field != null)
1991 code.putfield().setField(field);
1992 else
1993 code.invokevirtual().setMethod(setter);
1994 }
1995 code.vreturn();
1996
1997 code.calculateMaxStack();
1998 code.calculateMaxLocals();
1999 }
2000
2001 /**
2002 * Add code to extract the id of the given primary key relation field for
2003 * setting into an objectid instance.
2004 */
2005 private void addExtractObjectIdFieldValueCode(Code code, FieldMetaData pk) {
2006 // if (val != null)
2007 // val = ((PersistenceCapable) val).pcFetchObjectId();
2008 int pc = code.getNextLocalsIndex();
2009 code.astore().setLocal(pc);
2010 code.aload().setLocal(pc);
2011 JumpInstruction ifnull1 = code.ifnull();
2012 code.aload().setLocal(pc);
2013 code.checkcast().setType(PersistenceCapable.class);
2014 code.invokeinterface().setMethod(PersistenceCapable.class,
2015 PRE + "FetchObjectId", Object.class, null);
2016 int oid = code.getNextLocalsIndex();
2017 code.astore().setLocal(oid);
2018 code.aload().setLocal(oid);
2019 JumpInstruction ifnull2 = code.ifnull();
2020
2021 // for datastore / single-field identity:
2022 // if (val != null)
2023 // val = ((OpenJPAId) val).getId();
2024 ClassMetaData pkmeta = pk.getDeclaredTypeMetaData();
2025 int pkcode = pk.getObjectIdFieldTypeCode();
2026 Class pktype = pk.getObjectIdFieldType();
2027 if (pkmeta.getIdentityType() == ClassMetaData.ID_DATASTORE
2028 && pkcode == JavaTypes.LONG) {
2029 code.aload().setLocal(oid);
2030 code.checkcast().setType(Id.class);
2031 code.invokevirtual().setMethod(Id.class, "getId",
2032 long.class, null);
2033 } else if (pkmeta.getIdentityType() == ClassMetaData.ID_DATASTORE) {
2034 code.aload().setLocal(oid);
2035 } else if (pkmeta.isOpenJPAIdentity()) {
2036 switch (pkcode) {
2037 case JavaTypes.BYTE_OBJ:
2038 code.anew().setType(Byte.class);
2039 code.dup();
2040 // no break
2041 case JavaTypes.BYTE:
2042 code.aload().setLocal(oid);
2043 code.checkcast().setType(ByteId.class);
2044 code.invokevirtual().setMethod(ByteId.class, "getId",
2045 byte.class, null);
2046 if (pkcode == JavaTypes.BYTE_OBJ)
2047 code.invokespecial().setMethod(Byte.class, "<init>",
2048 void.class, new Class[] {byte.class});
2049 break;
2050 case JavaTypes.CHAR_OBJ:
2051 code.anew().setType(Character.class);
2052 code.dup();
2053 // no break
2054 case JavaTypes.CHAR:
2055 code.aload().setLocal(oid);
2056 code.checkcast().setType(CharId.class);
2057 code.invokevirtual().setMethod(CharId.class, "getId",
2058 char.class, null);
2059 if (pkcode == JavaTypes.CHAR_OBJ)
2060 code.invokespecial().setMethod(Character.class,
2061 "<init>", void.class, new Class[] {char.class});
2062 break;
2063 case JavaTypes.DOUBLE_OBJ:
2064 code.anew().setType(Double.class);
2065 code.dup();
2066 // no break
2067 case JavaTypes.DOUBLE:
2068 code.aload().setLocal(oid);
2069 code.checkcast().setType(DoubleId.class);
2070 code.invokevirtual().setMethod(DoubleId.class, "getId",
2071 double.class, null);
2072 if (pkcode == JavaTypes.DOUBLE_OBJ)
2073 code.invokespecial().setMethod(Double.class, "<init>",
2074 void.class, new Class[]{double.class});
2075 break;
2076 case JavaTypes.FLOAT_OBJ:
2077 code.anew().setType(Float.class);
2078 code.dup();
2079 // no break
2080 case JavaTypes.FLOAT:
2081 code.aload().setLocal(oid);
2082 code.checkcast().setType(FloatId.class);
2083 code.invokevirtual().setMethod(FloatId.class, "getId",
2084 float.class, null);
2085 if (pkcode == JavaTypes.FLOAT_OBJ)
2086 code.invokespecial().setMethod(Float.class, "<init>",
2087 void.class, new Class[]{float.class});
2088 break;
2089 case JavaTypes.INT_OBJ:
2090 code.anew().setType(Integer.class);
2091 code.dup();
2092 // no break
2093 case JavaTypes.INT:
2094 code.aload().setLocal(oid);
2095 code.checkcast().setType(IntId.class);
2096 code.invokevirtual().setMethod(IntId.class, "getId",
2097 int.class, null);
2098 if (pkcode == JavaTypes.INT_OBJ)
2099 code.invokespecial().setMethod(Integer.class, "<init>",
2100 void.class, new Class[] {int.class});
2101 break;
2102 case JavaTypes.LONG_OBJ:
2103 code.anew().setType(Long.class);
2104 code.dup();
2105 // no break
2106 case JavaTypes.LONG:
2107 code.aload().setLocal(oid);
2108 code.checkcast().setType(LongId.class);
2109 code.invokevirtual().setMethod(LongId.class, "getId",
2110 long.class, null);
2111 if (pkcode == JavaTypes.LONG_OBJ)
2112 code.invokespecial().setMethod(Long.class, "<init>",
2113 void.class, new Class[] {long.class});
2114 break;
2115 case JavaTypes.SHORT_OBJ:
2116 code.anew().setType(Short.class);
2117 code.dup();
2118 // no break
2119 case JavaTypes.SHORT:
2120 code.aload().setLocal(oid);
2121 code.checkcast().setType(ShortId.class);
2122 code.invokevirtual().setMethod(ShortId.class, "getId",
2123 short.class, null);
2124 if (pkcode == JavaTypes.SHORT_OBJ)
2125 code.invokespecial().setMethod(Short.class, "<init>",
2126 void.class, new Class[]{short.class});
2127 break;
2128 case JavaTypes.DATE:
2129 code.aload().setLocal(oid);
2130 code.checkcast().setType(DateId.class);
2131 code.invokevirtual().setMethod(DateId.class, "getId",
2132 Date.class, null);
2133 break;
2134 case JavaTypes.STRING:
2135 code.aload().setLocal(oid);
2136 code.checkcast().setType(StringId.class);
2137 code.invokevirtual().setMethod(StringId.class, "getId",
2138 String.class, null);
2139 break;
2140 case JavaTypes.BIGDECIMAL:
2141 code.aload().setLocal(oid);
2142 code.checkcast().setType(BigDecimalId.class);
2143 code.invokevirtual().setMethod(BigDecimalId.class, "getId",
2144 BigDecimalId.class, null);
2145 break;
2146 case JavaTypes.BIGINTEGER:
2147 code.aload().setLocal(oid);
2148 code.checkcast().setType(BigIntegerId.class);
2149 code.invokevirtual().setMethod(BigIntegerId.class, "getId",
2150 BigIntegerId.class, null);
2151 break;
2152 default:
2153 code.aload().setLocal(oid);
2154 code.checkcast().setType(ObjectId.class);
2155 code.invokevirtual().setMethod(ObjectId.class, "getId",
2156 Object.class, null);
2157 }
2158 } else if (pkmeta.getObjectIdType() != null) {
2159 code.aload().setLocal(oid);
2160 code.checkcast().setType(pktype);
2161 } else
2162 code.aload().setLocal(oid);
2163 JumpInstruction go2 = code.go2();
2164
2165 // if (val == null)
2166 // val = <default>;
2167 Instruction def;
2168 switch (pkcode) {
2169 case JavaTypes.BOOLEAN:
2170 def = code.constant().setValue(false);
2171 break;
2172 case JavaTypes.BYTE:
2173 def = code.constant().setValue((byte) 0);
2174 break;
2175 case JavaTypes.CHAR:
2176 def = code.constant().setValue((char) 0);
2177 break;
2178 case JavaTypes.DOUBLE:
2179 def = code.constant().setValue(0D);
2180 break;
2181 case JavaTypes.FLOAT:
2182 def = code.constant().setValue(0F);
2183 break;
2184 case JavaTypes.INT:
2185 def = code.constant().setValue(0);
2186 break;
2187 case JavaTypes.LONG:
2188 def = code.constant().setValue(0L);
2189 break;
2190 case JavaTypes.SHORT:
2191 def = code.constant().setValue((short) 0);
2192 break;
2193 default:
2194 def = code.constant().setNull();
2195 }
2196 ifnull1.setTarget(def);
2197 ifnull2.setTarget(def);
2198 go2.setTarget(code.nop());
2199 }
2200
2201 /**
2202 * Adds the <code>pcCopyKeyFieldsFromObjectId</code> methods
2203 * to classes using application identity.
2204 */
2205 private void addCopyKeyFieldsFromObjectIdMethod(boolean fieldManager)
2206 throws NoSuchMethodException {
2207 // public void pcCopyKeyFieldsFromObjectId (ObjectIdFieldConsumer fc,
2208 // Object oid)
2209 String[] args = (fieldManager)
2210 ? new String[]{ OIDFCTYPE.getName(), Object.class.getName() }
2211 : new String[]{ Object.class.getName() };
2212 BCMethod method = _pc.declareMethod(PRE + "CopyKeyFieldsFromObjectId",
2213 void.class.getName(), args);
2214 Code code = method.getCode(true);
2215
2216 // call superclass method
2217 if (_meta.getPCSuperclass() != null && !getCreateSubclass()) {
2218 loadManagedInstance(code, false);
2219 for (int i = 0; i < args.length; i++)
2220 code.aload().setParam(i);
2221 code.invokespecial().setMethod(getType(_meta.
2222 getPCSuperclassMetaData()).getName(),
2223 PRE + "CopyKeyFieldsFromObjectId", void.class.getName(), args);
2224 }
2225
2226 if (fieldManager)
2227 code.aload().setParam(1);
2228 else
2229 code.aload().setParam(0);
2230
2231 if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) {
2232 // oid = ((ObjectId) id).getId ();
2233 code.checkcast().setType(ObjectId.class);
2234 code.invokevirtual().setMethod(ObjectId.class, "getId",
2235 Object.class, null);
2236 }
2237
2238 // <oid type> cast = (<oid type>) oid;
2239 int id = code.getNextLocalsIndex();
2240 Class oidType = _meta.getObjectIdType();
2241 code.checkcast().setType(oidType);
2242 code.astore().setLocal(id);
2243
2244 // fs.store<type>Field (<index>, id.<field>); or...
2245 // this.<field> = id.<field>
2246 // or for single field identity: id.getId ()
2247 FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
2248 : _meta.getDeclaredFields();
2249 String name;
2250 Class type;
2251 Class unwrapped;
2252 Field field;
2253 Method getter;
2254 for (int i = 0; i < fmds.length; i++) {
2255 if (!fmds[i].isPrimaryKey())
2256 continue;
2257
2258 name = fmds[i].getName();
2259 type = fmds[i].getObjectIdFieldType();
2260 if (!fieldManager
2261 && fmds[i].getDeclaredTypeCode() == JavaTypes.PC) {
2262 // sm.getPCPrimaryKey(oid, i + pcInheritedFieldCount);
2263 loadManagedInstance(code, false);
2264 code.dup(); // leave orig on stack to set value into
2265 code.getfield().setField(SM, SMTYPE);
2266 code.aload().setLocal(id);
2267 code.constant().setValue(i);
2268 code.getstatic().setField(INHERIT, int.class);
2269 code.iadd();
2270 code.invokeinterface().setMethod(StateManager.class,
2271 "getPCPrimaryKey", Object.class,
2272 new Class[] { Object.class, int.class });
2273 code.checkcast().setType(fmds[i].getDeclaredType());
2274 } else {
2275 unwrapped = (fmds[i].getDeclaredTypeCode() == JavaTypes.PC)
2276 ? type : unwrapSingleFieldIdentity(fmds[i]);
2277 if (fieldManager) {
2278 code.aload().setParam(0);
2279 code.constant().setValue(i);
2280 code.getstatic().setField(INHERIT, int.class);
2281 code.iadd();
2282 } else
2283 loadManagedInstance(code, false);
2284
2285 if (unwrapped != type) {
2286 code.anew().setType(type);
2287 code.dup();
2288 }
2289 code.aload().setLocal(id);
2290 if (_meta.isOpenJPAIdentity()) {
2291 if (oidType == ObjectId.class) {
2292 code.invokevirtual().setMethod(oidType, "getId",
2293 Object.class, null);
2294 if (!fieldManager && type != Object.class)
2295 code.checkcast().setType(fmds[i].getDeclaredType());
2296 } else if (oidType == DateId.class) {
2297 code.invokevirtual().setMethod(oidType, "getId",
2298 Date.class, null);
2299 if (!fieldManager && type != Date.class)
2300 code.checkcast().setType(fmds[i].getDeclaredType());
2301 } else {
2302 code.invokevirtual().setMethod(oidType, "getId",
2303 unwrapped, null);
2304 if (unwrapped != type)
2305 code.invokespecial().setMethod(type, "<init>",
2306 void.class, new Class[]{ unwrapped });
2307 }
2308 } else if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD){
2309 field = Reflection.findField(oidType, name, true);
2310 if (Modifier.isPublic(field.getModifiers()))
2311 code.getfield().setField(field);
2312 else {
2313 // Reflection.getXXX(oid, Reflection.findField(...));
2314 code.classconstant().setClass(oidType);
2315 code.constant().setValue(name);
2316 code.constant().setValue(true);
2317 code.invokestatic().setMethod(Reflection.class,
2318 "findField", Field.class, new Class[] {
2319 Class.class, String.class, boolean.class });
2320 code.invokestatic().setMethod
2321 (getReflectionGetterMethod(type, Field.class));
2322 if (!type.isPrimitive() && type != Object.class)
2323 code.checkcast().setType(type);
2324 }
2325 } else {
2326 getter = Reflection.findGetter(oidType, name, true);
2327 if (Modifier.isPublic(getter.getModifiers()))
2328 code.invokevirtual().setMethod(getter);
2329 else {
2330 // Reflection.getXXX(oid, Reflection.findGetter(...));
2331 code.classconstant().setClass(oidType);
2332 code.constant().setValue(name);
2333 code.constant().setValue(true);
2334 code.invokestatic().setMethod(Reflection.class,
2335 "findGetter", Method.class, new Class[] {
2336 Class.class, String.class, boolean.class });
2337 code.invokestatic().setMethod
2338 (getReflectionGetterMethod(type, Method.class));
2339 if (!type.isPrimitive() && type != Object.class)
2340 code.checkcast().setType(type);
2341 }
2342 }
2343 }
2344
2345 if (fieldManager)
2346 code.invokeinterface().setMethod(getFieldConsumerMethod(type));
2347 else
2348 addSetManagedValueCode(code, fmds[i]);
2349 }
2350 code.vreturn();
2351
2352 code.calculateMaxStack();
2353 code.calculateMaxLocals();
2354 }
2355
2356 /**
2357 * Return if the class uses the Class/String constructor
2358 * instead of just String.
2359 */
2360 private Boolean usesClassStringIdConstructor() {
2361 if (_meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
2362 return Boolean.FALSE;
2363
2364 if (_meta.isOpenJPAIdentity()) {
2365 if (_meta.getObjectIdType() == ObjectId.class)
2366 return null;
2367 return Boolean.TRUE;
2368 }
2369
2370 Class oidType = _meta.getObjectIdType();
2371 try {
2372 oidType.getConstructor(new Class[]{ Class.class, String.class });
2373 return Boolean.TRUE;
2374 } catch (Throwable t) {
2375 }
2376 try {
2377 oidType.getConstructor(new Class[]{ String.class });
2378 return Boolean.FALSE;
2379 } catch (Throwable t) {
2380 }
2381 return null;
2382 }
2383
2384 /**
2385 * If the given field is a wrapper-type single field identity primary key,
2386 * return its corresponding primitive class. Else return the field type.
2387 */
2388 private Class unwrapSingleFieldIdentity(FieldMetaData fmd) {
2389 if (!fmd.getDefiningMetaData().isOpenJPAIdentity())
2390 return fmd.getDeclaredType();
2391
2392 switch (fmd.getDeclaredTypeCode()) {
2393 case JavaTypes.BYTE_OBJ:
2394 return byte.class;
2395 case JavaTypes.CHAR_OBJ:
2396 return char.class;
2397 case JavaTypes.DOUBLE_OBJ:
2398 return double.class;
2399 case JavaTypes.FLOAT_OBJ:
2400 return float.class;
2401 case JavaTypes.INT_OBJ:
2402 return int.class;
2403 case JavaTypes.SHORT_OBJ:
2404 return short.class;
2405 case JavaTypes.LONG_OBJ:
2406 return long.class;
2407 default:
2408 return fmd.getDeclaredType();
2409 }
2410 }
2411
2412 /**
2413 * Return the proper getter method of the {@link Reflection} helper for
2414 * a field or getter method of the given type.
2415 */
2416 private Method getReflectionGetterMethod(Class type, Class argType)
2417 throws NoSuchMethodException {
2418 String name = "get";
2419 if (type.isPrimitive())
2420 name += StringUtils.capitalize(type.getName());
2421 return Reflection.class.getMethod(name, new Class[] { Object.class,
2422 argType });
2423 }
2424
2425 /**
2426 * Return the proper fetch method of the ObjectIdFieldSupplier for
2427 * a field of the given type.
2428 */
2429 private Method getFieldSupplierMethod(Class type)
2430 throws NoSuchMethodException {
2431 return getMethod(OIDFSTYPE, type, "fetch", true, false, false);
2432 }
2433
2434 /**
2435 * Return the proper fetch method of the ObjectIdFieldConsumer for
2436 * a field of the given type.
2437 */
2438 private Method getFieldConsumerMethod(Class type)
2439 throws NoSuchMethodException {
2440 return getMethod(OIDFCTYPE, type, "store", false, false, false);
2441 }
2442
2443 /**
2444 * Adds the pcNewObjectIdInstance method to classes using
2445 * application identity.
2446 */
2447 private void addNewObjectIdInstanceMethod(boolean obj)
2448 throws NoSuchMethodException {
2449 // public Object pcNewObjectIdInstance ()
2450 Class[] args = (obj) ? new Class[]{ Object.class } : null;
2451 BCMethod method = _pc.declareMethod(PRE + "NewObjectIdInstance",
2452 Object.class, args);
2453 Code code = method.getCode(true);
2454
2455 Boolean usesClsString = usesClassStringIdConstructor();
2456 Class oidType = _meta.getObjectIdType();
2457 if (obj && usesClsString == null) {
2458 // throw new IllegalArgumentException (...);
2459 String msg = _loc.get("str-cons", oidType,
2460 _meta.getDescribedType()).getMessage();
2461 code.anew().setType(IllegalArgumentException.class);
2462 code.dup();
2463 code.constant().setValue(msg);
2464 code.invokespecial().setMethod(IllegalArgumentException.class,
2465 "<init>", void.class, new Class[]{ String.class });
2466 code.athrow();
2467 code.vreturn();
2468
2469 code.calculateMaxStack();
2470 code.calculateMaxLocals();
2471 return;
2472 }
2473
2474 if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared()) {
2475 // new ObjectId (cls, oid)
2476 code.anew().setType(ObjectId.class);
2477 code.dup();
2478 code.classconstant().setClass(getType(_meta));
2479 }
2480
2481 // new <oid class> ();
2482 code.anew().setType(oidType);
2483 code.dup();
2484 if (_meta.isOpenJPAIdentity() || (obj && usesClsString == Boolean.TRUE))
2485 code.classconstant().setClass(getType(_meta));
2486 if (obj) {
2487 code.aload().setParam(0);
2488 code.checkcast().setType(String.class);
2489 if (usesClsString == Boolean.TRUE)
2490 args = new Class[]{ Class.class, String.class };
2491 else if (usesClsString == Boolean.FALSE)
2492 args = new Class[]{ String.class };
2493 } else if (_meta.isOpenJPAIdentity()) {
2494 // new <type>Identity (XXX.class, <pk>);
2495 loadManagedInstance(code, false);
2496 FieldMetaData pk = _meta.getPrimaryKeyFields()[0];
2497 addGetManagedValueCode(code, pk);
2498 if (pk.getDeclaredTypeCode() == JavaTypes.PC)
2499 addExtractObjectIdFieldValueCode(code, pk);
2500 if (_meta.getObjectIdType() == ObjectId.class)
2501 args = new Class[]{ Class.class, Object.class };
2502 else if (_meta.getObjectIdType() == Date.class)
2503 args = new Class[]{ Class.class, Date.class };
2504 else
2505 args = new Class[]{ Class.class, pk.getObjectIdFieldType() };
2506 }
2507
2508 code.invokespecial().setMethod(oidType, "<init>", void.class, args);
2509 if (!_meta.isOpenJPAIdentity() && _meta.isObjectIdTypeShared())
2510 code.invokespecial().setMethod(ObjectId.class, "<init>",
2511 void.class, new Class[]{ Class.class, Object.class });
2512 code.areturn();
2513
2514 code.calculateMaxStack();
2515 code.calculateMaxLocals();
2516 }
2517
2518 /**
2519 * When communicating with the StateManager, many methods are used
2520 * depending on the class of state being passed. This method,
2521 * given the type of information being passed and the prefix
2522 * ('provided', 'replace', etc) of the method to
2523 * call, returns the StateManager method that should be used.
2524 *
2525 * @param type the type of state being passed
2526 * @param prefix the prefix of the method to call; all methods
2527 * end in '[state type]Field'; only the prefix varies
2528 * @param get true if receiving information from the
2529 * StateManager, false if passing it to the SM
2530 * @param curValue true if the current state value is passed to
2531 * the StateManager as an extra argument
2532 */
2533 private Method getStateManagerMethod(Class type, String prefix,
2534 boolean get, boolean curValue)
2535 throws NoSuchMethodException {
2536 return getMethod(SMTYPE, type, prefix, get, true, curValue);
2537 }
2538
2539 /**
2540 * Return the method of the given owner type matching the given criteria.
2541 *
2542 * @param type the type of state being passed
2543 * @param prefix the prefix of the method to call; all methods
2544 * end in '[state type]Field'; only the prefix varies
2545 * @param get true if receiving information from the
2546 * owner, false if passing it to the owner
2547 * @param haspc true if the pc is passed as an extra argument
2548 * @param curValue true if the current state value is passed to
2549 * the owner as an extra argument
2550 */
2551 private Method getMethod(Class owner, Class type, String prefix,
2552 boolean get, boolean haspc, boolean curValue)
2553 throws NoSuchMethodException {
2554 // all methods end in [field type]Field, where the field type
2555 // can be any of the primitve types (but capitalized), 'String',
2556 // or 'Object'; figure out what type to use
2557 String typeName = type.getName();
2558 if (type.isPrimitive())
2559 typeName = typeName.substring(0, 1).toUpperCase()
2560 + typeName.substring(1);
2561 else if (type.equals(String.class))
2562 typeName = "String";
2563 else {
2564 typeName = "Object";
2565 type = Object.class;
2566 }
2567
2568 // the field index is always passed as an arg; the pc instance and
2569 // the current value may be passed; if setting the new value is
2570 // also passed
2571 List plist = new ArrayList(4);
2572 if (haspc)
2573 plist.add(PCTYPE);
2574 plist.add(int.class);
2575 if (!get || curValue)
2576 plist.add(type);
2577 if (!get && curValue) {
2578 plist.add(type);
2579 plist.add(int.class);
2580 }
2581
2582 // use reflection to return the right method
2583 String name = prefix + typeName + "Field";
2584 Class[] params = (Class[]) plist.toArray(new Class[plist.size()]);
2585
2586 try {
2587 return (Method) AccessController.doPrivileged(
2588 J2DoPrivHelper.getDeclaredMethodAction(owner, name, params));
2589 } catch (PrivilegedActionException pae) {
2590 throw (NoSuchMethodException) pae.getException();
2591 }
2592 }
2593
2594 /**
2595 * Helper method to add the code necessary to throw the given
2596 * exception type, sans message.
2597 */
2598 private Instruction throwException(Code code, Class type) {
2599 Instruction ins = code.anew().setType(type);
2600 code.dup();
2601 code.invokespecial().setMethod(type, "<init>", void.class, null);
2602 code.athrow();
2603 return ins;
2604 }
2605
2606 /**
2607 * Adds the PersistenceCapable interface to the class being
2608 * enhanced, and adds a default constructor for use by OpenJPA
2609 * if it is not already present.
2610 */
2611 private void enhanceClass() {
2612 // make the class implement PersistenceCapable
2613 _pc.declareInterface(PCTYPE);
2614
2615 // add a version stamp
2616 addGetEnhancementContractVersionMethod();
2617
2618 // find the default constructor
2619 BCMethod method = _pc.getDeclaredMethod("<init>", (String[]) null);
2620
2621 // a default constructor is required
2622 if (method == null) {
2623 String name = _pc.getName();
2624 if (!_defCons)
2625 throw new UserException(_loc.get("enhance-defaultconst", name));
2626
2627 method = _pc.addDefaultConstructor();
2628 String access;
2629 if (_meta.isDetachable()) {
2630 // externalizable requires that the constructor
2631 // be public, so make the added constructor public
2632 method.makePublic();
2633 access = "public";
2634 } else if (_pc.isFinal()) {
2635 method.makePrivate();
2636 access = "private";
2637 } else {
2638 method.makeProtected();
2639 access = "protected";
2640 }
2641 if (!(_meta.getDescribedType().isInterface() || getCreateSubclass())
2642 && _log.isWarnEnabled())
2643 _log.warn(_loc.get("enhance-adddefaultconst", name, access));
2644 }
2645 }
2646
2647 /**
2648 * Adds the following fields to the PersistenceCapable instance:
2649 * <ul>
2650 * <li><code>private static int pcInheritedFieldCount</code></li>
2651 * <li><code>private static Class pcPCSuperclass</code>
2652 * </li>
2653 * <li><code>private static String[] pcFieldNames</code></li>
2654 * <li><code>private static Class[] pcFieldTypes</code></li>
2655 * <li><code>private static byte[] pcFieldFlags</code></li>
2656 * <li><code>protected transient StateManager pcStateManager</code>
2657 * if no PersistenceCapable superclass present)</li>
2658 * </ul>
2659 */
2660 private void addFields() {
2661 _pc.declareField(INHERIT, int.class).setStatic(true);
2662 _pc.declareField(PRE + "FieldNames", String[].class).setStatic(true);
2663 _pc.declareField(PRE + "FieldTypes", Class[].class).setStatic(true);
2664 _pc.declareField(PRE + "FieldFlags", byte[].class).setStatic(true);
2665 _pc.declareField(SUPER, Class.class).setStatic(true);
2666
2667 if (_meta.getPCSuperclass() == null || getCreateSubclass()) {
2668 BCField field = _pc.declareField(SM, SMTYPE);
2669 field.makeProtected();
2670 field.setTransient(true);
2671 }
2672 }
2673
2674 /**
2675 * Modifies the class initialization method (creating one if necessary)
2676 * to initialize the static fields of the PersistenceCapable instance and
2677 * to register it with the impl helper.
2678 */
2679 private void addStaticInitializer() {
2680 Code code = getOrCreateClassInitCode(true);
2681 if (_meta.getPCSuperclass() != null) {
2682 if (getCreateSubclass()) {
2683 code.constant().setValue(0);
2684 code.putstatic().setField(INHERIT, int.class);
2685 } else {
2686 // pcInheritedFieldCount = <superClass>.pcGetManagedFieldCount()
2687 code.invokestatic().setMethod(getType(_meta.
2688 getPCSuperclassMetaData()).getName(),
2689 PRE + "GetManagedFieldCount", int.class.getName(), null);
2690 code.putstatic().setField(INHERIT, int.class);
2691 }
2692
2693 // pcPCSuperclass = <superClass>;
2694 // this intentionally calls getDescribedType() directly
2695 // instead of PCEnhancer.getType()
2696 code.classconstant().setClass(
2697 _meta.getPCSuperclassMetaData().getDescribedType());
2698 code.putstatic().setField(SUPER, Class.class);
2699 }
2700
2701 // pcFieldNames = new String[] { "<name1>", "<name2>", ... };
2702 FieldMetaData[] fmds = _meta.getDeclaredFields();
2703 code.constant().setValue(fmds.length);
2704 code.anewarray().setType(String.class);
2705 for (int i = 0; i < fmds.length; i++) {
2706 code.dup();
2707 code.constant().setValue(i);
2708 code.constant().setValue(fmds[i].getName());
2709 code.aastore();
2710 }
2711 code.putstatic().setField(PRE + "FieldNames", String[].class);
2712
2713 // pcFieldTypes = new Class[] { <type1>.class, <type2>.class, ... };
2714 code.constant().setValue(fmds.length);
2715 code.anewarray().setType(Class.class);
2716 for (int i = 0; i < fmds.length; i++) {
2717 code.dup();
2718 code.constant().setValue(i);
2719 code.classconstant().setClass(fmds[i].getDeclaredType());
2720 code.aastore();
2721 }
2722 code.putstatic().setField(PRE + "FieldTypes", Class[].class);
2723
2724 // pcFieldFlags = new byte[] { <flag1>, <flag2>, ... };
2725 code.constant().setValue(fmds.length);
2726 code.newarray().setType(byte.class);
2727 for (int i = 0; i < fmds.length; i++) {
2728 code.dup();
2729 code.constant().setValue(i);
2730 code.constant().setValue(getFieldFlag(fmds[i]));
2731 code.bastore();
2732 }
2733 code.putstatic().setField(PRE + "FieldFlags", byte[].class);
2734
2735 // PCRegistry.register (cls,
2736 // pcFieldNames, pcFieldTypes, pcFieldFlags,
2737 // pcPCSuperclass, alias, new XXX ());
2738 code.classconstant().setClass(_meta.getDescribedType());
2739 code.getstatic().setField(PRE + "FieldNames", String[].class);
2740 code.getstatic().setField(PRE + "FieldTypes", Class[].class);
2741 code.getstatic().setField(PRE + "FieldFlags", byte[].class);
2742 code.getstatic().setField(SUPER, Class.class);
2743
2744 if (_meta.isMapped())
2745 code.constant().setValue(_meta.getTypeAlias());
2746 else
2747 code.constant().setNull();
2748
2749 if (_pc.isAbstract())
2750 code.constant().setNull();
2751 else {
2752 code.anew().setType(_pc);
2753 code.dup();
2754 code.invokespecial().setMethod("<init>", void.class, null);
2755 }
2756
2757 code.invokestatic().setMethod(HELPERTYPE, "register", void.class,
2758 new Class[]{ Class.class, String[].class, Class[].class,
2759 byte[].class, Class.class, String.class, PCTYPE });
2760
2761 code.vreturn();
2762 code.calculateMaxStack();
2763 }
2764
2765 /**
2766 * Return the flag for the given field.
2767 */
2768 private static byte getFieldFlag(FieldMetaData fmd) {
2769 if (fmd.getManagement() == FieldMetaData.MANAGE_NONE)
2770 return -1;
2771
2772 byte flags = 0;
2773 if (fmd.getDeclaredType().isPrimitive()
2774 || Serializable.class.isAssignableFrom(fmd.getDeclaredType()))
2775 flags = PersistenceCapable.SERIALIZABLE;
2776
2777 if (fmd.getManagement() == FieldMetaData.MANAGE_TRANSACTIONAL)
2778 flags |= PersistenceCapable.CHECK_WRITE;
2779 else if (!fmd.isPrimaryKey() && !fmd.isInDefaultFetchGroup())
2780 flags |= PersistenceCapable.CHECK_WRITE
2781 | PersistenceCapable.CHECK_READ;
2782 else
2783 flags |= PersistenceCapable.MEDIATE_WRITE
2784 | PersistenceCapable.MEDIATE_READ;
2785 return flags;
2786 }
2787
2788 /**
2789 * Adds the code to properly handle PersistenceCapable serialization
2790 * to the bytecode. This includes creating and initializing the
2791 * static <code>serialVersionUID</code> constant if not already defined,
2792 * as well as creating a custom <code>writeObject</code> method if the
2793 * class is Serializable and does not define them.
2794 */
2795 private void addSerializationCode() {
2796 if (externalizeDetached()
2797 || !Serializable.class.isAssignableFrom(_meta.getDescribedType()))
2798 return;
2799
2800 if (getCreateSubclass()) {
2801 // ##### what should happen if a type is Externalizable? It looks
2802 // ##### like Externalizable classes will not be serialized as PCs
2803 // ##### based on this logic.
2804 if (!Externalizable.class.isAssignableFrom(
2805 _meta.getDescribedType()))
2806 addSubclassSerializationCode();
2807 return;
2808 }
2809
2810 // if not already present, add a serialVersionUID field; if the instance
2811 // is detachable and uses detached state without a declared field,
2812 // can't add a serial version UID because we'll be adding extra fields
2813 // to the enhanced version
2814 BCField field = _pc.getDeclaredField("serialVersionUID");
2815 if (field == null) {
2816 Long uid = null;
2817 try {
2818 uid = Numbers.valueOf(ObjectStreamClass.lookup
2819 (_meta.getDescribedType()).getSerialVersionUID());
2820 } catch (Throwable t) {
2821 // last-chance catch for bug #283 (which can happen
2822 // in a variety of ClassLoading environments)
2823 if (_log.isTraceEnabled())
2824 _log.warn(_loc.get("enhance-uid-access", _meta), t);
2825 else
2826 _log.warn(_loc.get("enhance-uid-access", _meta));
2827 }
2828
2829 // if we couldn't access the serialVersionUID, we will have to
2830 // skip the override of that field and not be serialization
2831 // compatible with non-enhanced classes
2832 if (uid != null) {
2833 field = _pc.declareField("serialVersionUID", long.class);
2834 field.makePrivate();
2835 field.setStatic(true);
2836 field.setFinal(true);
2837
2838 Code code = getOrCreateClassInitCode(false);
2839 code.beforeFirst();
2840 code.constant().setValue(uid.longValue());
2841 code.putstatic().setField(field);
2842
2843 code.calculateMaxStack();
2844 }
2845 }
2846
2847 // add write object method
2848 BCMethod write = _pc.getDeclaredMethod("writeObject",
2849 new Class[]{ ObjectOutputStream.class });
2850 boolean full = write == null;
2851 if (full) {
2852 // private void writeObject (ObjectOutputStream out)
2853 write = _pc.declareMethod("writeObject", void.class,
2854 new Class[]{ ObjectOutputStream.class });
2855 write.getExceptions(true).addException(IOException.class);
2856 write.makePrivate();
2857 }
2858 modifyWriteObjectMethod(write, full);
2859
2860 // and read object
2861 BCMethod read = _pc.getDeclaredMethod("readObject",
2862 new Class[]{ ObjectInputStream.class });
2863 full = read == null;
2864 if (full) {
2865 // private void readObject (ObjectInputStream in)
2866 read = _pc.declareMethod("readObject", void.class,
2867 new Class[]{ ObjectInputStream.class });
2868 read.getExceptions(true).addException(IOException.class);
2869 read.getExceptions(true).addException
2870 (ClassNotFoundException.class);
2871 read.makePrivate();
2872 }
2873 modifyReadObjectMethod(read, full);
2874 }
2875
2876 private void addSubclassSerializationCode() {
2877 // for generated subclasses, serialization must write an instance of
2878 // the superclass instead of the subclass, so that the client VM can
2879 // deserialize successfully.
2880
2881 // private Object writeReplace() throws ObjectStreamException
2882 BCMethod method = _pc.declareMethod("writeReplace", Object.class, null);
2883 method.getExceptions(true).addException(ObjectStreamException.class);
2884 Code code = method.getCode(true);
2885
2886 // Object o = new <managed-type>()
2887 code.anew().setType(_managedType); // for return
2888 code.dup(); // for post-<init> work
2889 code.dup(); // for <init>
2890 code.invokespecial().setMethod(_managedType.getType(), "<init>",
2891 void.class, null);
2892
2893 // copy all the fields.
2894 // ##### limiting to JPA @Transient limitations
2895 FieldMetaData[] fmds = _meta.getFields();
2896 for (int i = 0; i < fmds.length; i++) {
2897 if (fmds[i].isTransient())
2898 continue;
2899 // o.<field> = this.<field> (or reflective analog)
2900 code.dup(); // for putfield
2901 code.aload().setThis(); // for getfield
2902 getfield(code, _managedType, fmds[i].getName());
2903 putfield(code, _managedType, fmds[i].getName(),
2904 fmds[i].getDeclaredType());
2905 }
2906
2907 code.areturn().setType(Object.class);
2908
2909 code.calculateMaxLocals();
2910 code.calculateMaxStack();
2911 }
2912
2913 /**
2914 * Whether the class being enhanced should externalize to a detached
2915 * instance rather than serialize.
2916 */
2917 private boolean externalizeDetached() {
2918 return ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())
2919 && Serializable.class.isAssignableFrom(_meta.getDescribedType())
2920 && !_repos.getConfiguration().getDetachStateInstance().
2921 isDetachedStateTransient();
2922 }
2923
2924 /**
2925 * Adds a custom writeObject method that delegates to the
2926 * {@link ObjectOutputStream#defaultWriteObject} method,
2927 * but only after calling the internal <code>pcSerializing</code> method.
2928 */
2929 private void modifyWriteObjectMethod(BCMethod method, boolean full) {
2930 Code code = method.getCode(true);
2931 code.beforeFirst();
2932
2933 // bool clear = pcSerializing ();
2934 loadManagedInstance(code, false);
2935 code.invokevirtual().setMethod(PRE + "Serializing",
2936 boolean.class, null);
2937 int clear = code.getNextLocalsIndex();
2938 code.istore().setLocal(clear);
2939
2940 if (full) {
2941 // out.defaultWriteObject ();
2942 code.aload().setParam(0);
2943 code.invokevirtual().setMethod(ObjectOutputStream.class,
2944 "defaultWriteObject", void.class, null);
2945 code.vreturn();
2946 }
2947
2948 Instruction tmplate = ((Code) AccessController.doPrivileged(
2949 J2DoPrivHelper.newCodeAction())).vreturn();
2950 JumpInstruction toret;
2951 Instruction ret;
2952 code.beforeFirst();
2953 while (code.searchForward(tmplate)) {
2954 ret = code.previous();
2955 // if (clear) pcSetDetachedState (null);
2956 code.iload().setLocal(clear);
2957 toret = code.ifeq();
2958 loadManagedInstance(code, false);
2959 code.constant().setNull();
2960 code.invokevirtual().setMethod(PRE + "SetDetachedState",
2961 void.class, new Class[]{ Object.class });
2962 toret.setTarget(ret);
2963 code.next(); // jump over return
2964 }
2965 code.calculateMaxStack();
2966 code.calculateMaxLocals();
2967 }
2968
2969 /**
2970 * Adds a custom readObject method that delegates to the
2971 * {@link ObjectInputStream#readObject} method.
2972 */
2973 private void modifyReadObjectMethod(BCMethod method, boolean full) {
2974 Code code = method.getCode(true);
2975 code.beforeFirst();
2976
2977 // if this instance uses synthetic detached state, note that it has
2978 // been deserialized
2979 if (ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())) {
2980 loadManagedInstance(code, false);
2981 code.getstatic().setField(PersistenceCapable.class,
2982 "DESERIALIZED", Object.class);
2983 code.invokevirtual().setMethod(PRE + "SetDetachedState",
2984 void.class, new Class[]{ Object.class });
2985 }
2986
2987 if (full) {
2988 // in.defaultReadObject ();
2989 code.aload().setParam(0);
2990 code.invokevirtual().setMethod(ObjectInputStream.class,
2991 "defaultReadObject", void.class, null);
2992 code.vreturn();
2993 }
2994
2995 code.calculateMaxStack();
2996 code.calculateMaxLocals();
2997 }
2998
2999 /**
3000 * Creates the pcIsDetached() method to determine if an instance
3001 * is detached.
3002 */
3003 private void addIsDetachedMethod()
3004 throws NoSuchMethodException {
3005 // public boolean pcIsDetached()
3006 BCMethod method = _pc.declareMethod(PRE + "IsDetached",
3007 Boolean.class, null);
3008 method.makePublic();
3009 Code code = method.getCode(true);
3010 boolean needsDefinitiveMethod = writeIsDetachedMethod(code);
3011 code.calculateMaxStack();
3012 code.calculateMaxLocals();
3013 if (!needsDefinitiveMethod)
3014 return;
3015
3016 // private boolean pcIsDetachedStateDefinitive()
3017 // return false;
3018 // auxilliary enhancers may change the return value of this method
3019 // if their specs consider detached state definitive
3020 method = _pc.declareMethod(ISDETACHEDSTATEDEFINITIVE, boolean.class,
3021 null);
3022 method.makePrivate();
3023 code = method.getCode(true);
3024 code.constant().setValue(false);
3025 code.ireturn();
3026 code.calculateMaxStack();
3027 code.calculateMaxLocals();
3028 }
3029
3030 /**
3031 * Creates the body of the pcIsDetached() method to determine if an
3032 * instance is detached.
3033 *
3034 * @return true if we need a pcIsDetachedStateDefinitive method, false
3035 * otherwise
3036 */
3037 private boolean writeIsDetachedMethod(Code code)
3038 throws NoSuchMethodException {
3039 // not detachable: return Boolean.FALSE
3040 if (!_meta.isDetachable()) {
3041 code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
3042 code.areturn();
3043 return false;
3044 }
3045
3046 // if (sm != null)
3047 // return (sm.isDetached ()) ? Boolean.TRUE : Boolean.FALSE;
3048 loadManagedInstance(code, false);
3049 code.getfield().setField(SM, SMTYPE);
3050 JumpInstruction ifins = code.ifnull();
3051 loadManagedInstance(code, false);
3052 code.getfield().setField(SM, SMTYPE);
3053 code.invokeinterface().setMethod(SMTYPE, "isDetached",
3054 boolean.class, null);
3055 JumpInstruction iffalse = code.ifeq();
3056 code.getstatic().setField(Boolean.class, "TRUE", Boolean.class);
3057 code.areturn();
3058 iffalse.setTarget(code.getstatic().setField(Boolean.class, "FALSE",
3059 Boolean.class));
3060 code.areturn();
3061
3062 // if we use detached state:
3063 // if (pcGetDetachedState () != null
3064 // && pcGetDetachedState != DESERIALIZED)
3065 // return Boolean.TRUE;
3066 Boolean state = _meta.usesDetachedState();
3067 JumpInstruction notdeser = null;
3068 Instruction target;
3069 if (state != Boolean.FALSE) {
3070 ifins.setTarget(loadManagedInstance(code, false));
3071 code.invokevirtual().setMethod(PRE + "GetDetachedState",
3072 Object.class, null);
3073 ifins = code.ifnull();
3074 loadManagedInstance(code, false);
3075 code.invokevirtual().setMethod(PRE + "GetDetachedState",
3076 Object.class, null);
3077 code.getstatic().setField(PersistenceCapable.class,
3078 "DESERIALIZED", Object.class);
3079 notdeser = code.ifacmpeq();
3080 code.getstatic().setField(Boolean.class, "TRUE", Boolean.class);
3081 code.areturn();
3082
3083 if (state == Boolean.TRUE) {
3084 // if we have to use detached state:
3085 // return Boolean.FALSE;
3086 target = code.getstatic().setField(Boolean.class, "FALSE",
3087 Boolean.class);
3088 ifins.setTarget(target);
3089 notdeser.setTarget(target);
3090 code.areturn();
3091 return false;
3092 }
3093 }
3094
3095 // create artificial target to simplify
3096 target = code.nop();
3097 ifins.setTarget(target);
3098 if (notdeser != null)
3099 notdeser.setTarget(target);
3100
3101 // allow users with version or auto-assigned pk fields to manually
3102 // construct a "detached" instance, so check these before taking into
3103 // account non-existent detached state
3104
3105 // consider detached if version is non-default
3106 FieldMetaData version = _meta.getVersionField();
3107 if (state != Boolean.TRUE && version != null) {
3108 // if (<version> != <default>)
3109 // return true;
3110 loadManagedInstance(code, false);
3111 addGetManagedValueCode(code, version);
3112 ifins = ifDefaultValue(code, version);
3113 code.getstatic().setField(Boolean.class, "TRUE", Boolean.class);
3114 code.areturn();
3115 ifins.setTarget(code.getstatic().setField(Boolean.class, "FALSE",
3116 Boolean.class));
3117 code.areturn();
3118 return false;
3119 }
3120
3121 // consider detached if auto-genned primary keys are non-default
3122 ifins = null;
3123 JumpInstruction ifins2 = null;
3124 boolean hasAutoAssignedPK = false;
3125 if (state != Boolean.TRUE
3126 && _meta.getIdentityType() == ClassMetaData.ID_APPLICATION) {
3127 // for each pk field:
3128 // if (<pk> != <default> [&& !"".equals (<pk>)])
3129 // return Boolean.TRUE;
3130 FieldMetaData[] pks = _meta.getPrimaryKeyFields();
3131 for (int i = 0; i < pks.length; i++) {
3132 if (pks[i].getValueStrategy() == ValueStrategies.NONE)
3133 continue;
3134
3135 target = loadManagedInstance(code, false);
3136 if (ifins != null)
3137 ifins.setTarget(target);
3138 if (ifins2 != null)
3139 ifins2.setTarget(target);
3140 ifins2 = null;
3141
3142 addGetManagedValueCode(code, pks[i]);
3143 ifins = ifDefaultValue(code, pks[i]);
3144 if (pks[i].getDeclaredTypeCode() == JavaTypes.STRING) {
3145 code.constant().setValue("");
3146 loadManagedInstance(code, false);
3147 addGetManagedValueCode(code, pks[i]);
3148 code.invokevirtual().setMethod(String.class, "equals",
3149 boolean.class, new Class[]{ Object.class });
3150 ifins2 = code.ifne();
3151 }
3152 code.getstatic().setField(Boolean.class, "TRUE",
3153 Boolean.class);
3154 code.areturn();
3155 }
3156 }
3157
3158 // create artificial target to simplify
3159 target = code.nop();
3160 if (ifins != null)
3161 ifins.setTarget(target);
3162 if (ifins2 != null)
3163 ifins2.setTarget(target);
3164
3165 // if has auto-assigned pk and we get to this point, must have default
3166 // value, so must be new instance
3167 if (hasAutoAssignedPK) {
3168 code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
3169 code.areturn();
3170 return false;
3171 }
3172
3173 // if detached state is not definitive, just give up now and return
3174 // null so that the runtime will perform a DB lookup to determine
3175 // whether we're detached or new
3176 code.aload().setThis();
3177 code.invokespecial().setMethod(ISDETACHEDSTATEDEFINITIVE, boolean.class,
3178 null);
3179 ifins = code.ifne();
3180 code.constant().setNull();
3181 code.areturn();
3182 ifins.setTarget(code.nop());
3183
3184 // no detached state: if instance uses detached state and it's not
3185 // synthetic or the instance is not serializable or the state isn't
3186 // transient, must not be detached
3187 if (state == null
3188 && (!ClassMetaData.SYNTHETIC.equals(_meta.getDetachedState())
3189 || !Serializable.class.isAssignableFrom(_meta.getDescribedType())
3190 || !_repos.getConfiguration().getDetachStateInstance().
3191 isDetachedStateTransient())) {
3192 // return Boolean.FALSE
3193 code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
3194 code.areturn();
3195 return true;
3196 }
3197
3198 // no detached state: if instance uses detached state (and must be
3199 // synthetic and transient in serializable instance at this point),
3200 // not detached if state not set to DESERIALIZED
3201 if (state == null) {
3202 // if (pcGetDetachedState () == null) // instead of DESERIALIZED
3203 // return Boolean.FALSE;
3204 loadManagedInstance(code, false);
3205 code.invokevirtual().setMethod(PRE + "GetDetachedState",
3206 Object.class, null);
3207 ifins = code.ifnonnull();
3208 code.getstatic().setField(Boolean.class, "FALSE", Boolean.class);
3209 code.areturn();
3210 ifins.setTarget(code.nop());
3211 }
3212
3213 // give up; we just don't know
3214 code.constant().setNull();
3215 code.areturn();
3216 return true;
3217 }
3218
3219 /**
3220 * Compare the given field to its Java default, returning the
3221 * comparison instruction. The field value will already be on the stack.
3222 */
3223 private static JumpInstruction ifDefaultValue(Code code,
3224 FieldMetaData fmd) {
3225 switch (fmd.getDeclaredTypeCode()) {
3226 case JavaTypes.BOOLEAN:
3227 case JavaTypes.BYTE:
3228 case JavaTypes.CHAR:
3229 case JavaTypes.INT:
3230 case JavaTypes.SHORT:
3231 return code.ifeq();
3232 case JavaTypes.DOUBLE:
3233 code.constant().setValue(0D);
3234 code.dcmpl();
3235 return code.ifeq();
3236 case JavaTypes.FLOAT:
3237 code.constant().setValue(0F);
3238 code.fcmpl();
3239 return code.ifeq();
3240 case JavaTypes.LONG:
3241 code.constant().setValue(0L);
3242 code.lcmp();
3243 return code.ifeq();
3244 default:
3245 return code.ifnull();
3246 }
3247 }
3248
3249 /**
3250 * Helper method to get the code for the class initializer method,
3251 * creating the method if it does not already exist.
3252 */
3253 private Code getOrCreateClassInitCode(boolean replaceLast) {
3254 BCMethod clinit = _pc.getDeclaredMethod("<clinit>");
3255 Code code;
3256 if (clinit != null) {
3257 code = clinit.getCode(true);
3258 if (replaceLast) {
3259 Code template = (Code) AccessController.doPrivileged(
3260 J2DoPrivHelper.newCodeAction());
3261 code.searchForward(template.vreturn());
3262 code.previous();
3263 code.set(template.nop());
3264 code.next();
3265 }
3266 return code;
3267 }
3268
3269 // add static initializer method if non exists
3270 clinit = _pc.declareMethod("<clinit>", void.class, null);
3271 clinit.makePackage();
3272 clinit.setStatic(true);
3273 clinit.setFinal(true);
3274
3275 code = clinit.getCode(true);
3276 if (!replaceLast) {
3277 code.vreturn();
3278 code.previous();
3279 }
3280 return code;
3281 }
3282
3283 /**
3284 * Adds bytecode modifying the cloning behavior of the class being
3285 * enhanced to correctly replace the <code>pcStateManager</code>
3286 * instance fields of any clone created with their default values.
3287 * Also, if this class is the base PC type and does not declared
3288 * a clone method, one will be added. Also, if _pc is a synthetic
3289 * subclass, create the clone() method that clears the state manager
3290 * that may have been initialized in a super's clone() method.
3291 */
3292 private void addCloningCode() {
3293 if (_meta.getPCSuperclass() != null && !getCreateSubclass())
3294 return;
3295
3296 // add the clone method if necessary
3297 BCMethod clone = _pc.getDeclaredMethod("clone",
3298 (String[]) null);
3299 String superName = _managedType.getSuperclassName();
3300 Code code = null;
3301 if (clone == null) {
3302 // add clone support for base classes
3303 // which also implement cloneable
3304 boolean isCloneable = Cloneable.class.isAssignableFrom(
3305 _managedType.getType());
3306 boolean extendsObject =
3307 superName.equals(Object.class.getName());
3308 if (!isCloneable || (!extendsObject && !getCreateSubclass()))
3309 return;
3310
3311 if (!getCreateSubclass())
3312 if (_log.isTraceEnabled())
3313 _log.trace(
3314 _loc.get("enhance-cloneable", _managedType.getName()));
3315
3316 // add clone method
3317 // protected Object clone () throws CloneNotSupportedException
3318 clone = _pc.declareMethod("clone", Object.class, null);
3319 if (!setVisibilityToSuperMethod(clone))
3320 clone.makeProtected();
3321 clone.getExceptions(true).addException
3322 (CloneNotSupportedException.class);
3323 code = clone.getCode(true);
3324
3325 // return super.clone ();
3326 loadManagedInstance(code, false);
3327 code.invokespecial().setMethod(superName, "clone",
3328 Object.class.getName(), null);
3329 code.areturn();
3330 } else {
3331 // get the clone method code
3332 code = clone.getCode(false);
3333 if (code == null)
3334 return;
3335 }
3336
3337 // create template super.clone () instruction to match against
3338 Instruction template = ((Code) AccessController.doPrivileged(
3339 J2DoPrivHelper.newCodeAction())).invokespecial()
3340 .setMethod(superName, "clone", Object.class.getName(), null);
3341
3342 // find calls to the template instruction; on match
3343 // clone will be on stack
3344 code.beforeFirst();
3345 if (code.searchForward(template)) {
3346 // ((<type>) clone).pcStateManager = null;
3347 code.dup();
3348 code.checkcast().setType(_pc);
3349 code.constant().setNull();
3350 code.putfield().setField(SM, SMTYPE);
3351
3352 // if modified, increase stack
3353 code.calculateMaxStack();
3354 code.calculateMaxLocals();
3355 }
3356 }
3357
3358 /**
3359 * Gets the auxiliary enhancers registered as {@link Services services}.
3360 */
3361 public AuxiliaryEnhancer[] getAuxiliaryEnhancers() {
3362 return _auxEnhancers;
3363 }
3364
3365 /**
3366 * Allow any registered auxiliary code generators to run.
3367 */
3368 private void runAuxiliaryEnhancers() {
3369 for (int i = 0; i < _auxEnhancers.length; i++)
3370 _auxEnhancers[i].run(_pc, _meta);
3371 }
3372
3373 /**
3374 * Affirms if the given method be skipped.
3375 *
3376 * @param method method to be skipped or not
3377 * @return true if any of the auxiliary enhancers skips the given method,
3378 * or if the method is a constructor
3379 */
3380 private boolean skipEnhance(BCMethod method) {
3381 if ("<init>".equals(method.getName()))
3382 return true;
3383
3384 for (int i = 0; i < _auxEnhancers.length; i++)
3385 if (_auxEnhancers[i].skipEnhance(method))
3386 return true;
3387
3388 return false;
3389 }
3390
3391 /**
3392 * Adds synthetic field access methods that will replace all direct
3393 * field accesses.
3394 */
3395 private void addAccessors()
3396 throws NoSuchMethodException {
3397 FieldMetaData[] fmds = getCreateSubclass() ? _meta.getFields()
3398 : _meta.getDeclaredFields();
3399 for (int i = 0; i < fmds.length; i++) {
3400 if (getCreateSubclass()) {
3401 if (!getRedefine()
3402 && _meta.getAccessType() != ClassMetaData.ACCESS_FIELD) {
3403 addSubclassSetMethod(fmds[i]);
3404 addSubclassGetMethod(fmds[i]);
3405 }
3406 } else {
3407 addGetMethod(i, fmds[i]);
3408 addSetMethod(i, fmds[i]);
3409 }
3410 }
3411 }
3412
3413 /**
3414 * Adds a non-static setter that delegates to the super methods, and
3415 * performs any necessary field tracking.
3416 */
3417 private void addSubclassSetMethod(FieldMetaData fmd)
3418 throws NoSuchMethodException {
3419 Class propType = fmd.getDeclaredType();
3420 String setterName = getSetterName(fmd);
3421 BCMethod setter = _pc.declareMethod(setterName, void.class,
3422 new Class[] { propType });
3423 setVisibilityToSuperMethod(setter);
3424 Code code = setter.getCode(true);
3425
3426 // not necessary if we're already tracking access via redefinition
3427 if (!getRedefine()) {
3428 // get the orig value onto stack
3429 code.aload().setThis();
3430 addGetManagedValueCode(code, fmd);
3431 int val = code.getNextLocalsIndex();
3432 code.xstore().setLocal(val).setType(fmd.getDeclaredType());
3433 addNotifyMutation(code, fmd, val, 0);
3434 }
3435
3436 // ##### test case: B extends A. Methods defined in A. What
3437 // ##### happens?
3438 // super.setXXX(...)
3439 code.aload().setThis();
3440 code.xload().setParam(0).setType(propType);
3441 code.invokespecial().setMethod(_managedType.getType(),
3442 setterName, void.class, new Class[] { propType });
3443
3444 code.vreturn();
3445 code.calculateMaxLocals();
3446 code.calculateMaxStack();
3447 }
3448
3449 private boolean setVisibilityToSuperMethod(BCMethod method) {
3450 BCMethod[] methods = _managedType.getMethods(method.getName(),
3451 method.getParamTypes());
3452 if (methods.length == 0)
3453 throw new UserException(_loc.get("no-accessor",
3454 _managedType.getName(), method.getName()));
3455 BCMethod superMeth = methods[0];
3456 if (superMeth.isPrivate()) {
3457 method.makePrivate();
3458 return true;
3459 } else if (superMeth.isPackage()) {
3460 method.makePackage();
3461 return true;
3462 } else if (superMeth.isProtected()) {
3463 method.makeProtected();
3464 return true;
3465 } else if (superMeth.isPublic()) {
3466 method.makePublic();
3467 return true;
3468 }
3469 return false;
3470 }
3471
3472 /**
3473 * Adds a non-static getter that delegates to the super methods, and
3474 * performs any necessary field tracking.
3475 */
3476 private void addSubclassGetMethod(FieldMetaData fmd) {
3477 String methName = "get" + StringUtils.capitalize(fmd.getName());
3478 if (_managedType.getMethods(methName, new Class[0]).length == 0)
3479 methName = "is" + StringUtils.capitalize(fmd.getName());
3480 BCMethod getter = _pc.declareMethod(methName, fmd.getDeclaredType(),
3481 null);
3482 setVisibilityToSuperMethod(getter);
3483 getter.makePublic();
3484 Code code = getter.getCode(true);
3485
3486 // if we're not already tracking field access via reflection, then we
3487 // must make the getter hook in lazy loading before accessing the super
3488 // method.
3489 if (!getRedefine())
3490 addNotifyAccess(code, fmd);
3491
3492 code.aload().setThis();
3493 code.invokespecial().setMethod(_managedType.getType(), methName,
3494 fmd.getDeclaredType(), null);
3495 code.xreturn().setType(fmd.getDeclaredType());
3496 code.calculateMaxLocals();
3497 code.calculateMaxStack();
3498 }
3499
3500 /**
3501 * Adds a static getter method for the given field.
3502 * The generated method interacts with the instance state and the
3503 * StateManager to get the value of the field.
3504 *
3505 * @param index the relative number of the field
3506 * @param fmd metadata about the field to get
3507 */
3508 private void addGetMethod(int index, FieldMetaData fmd)
3509 throws NoSuchMethodException {
3510 BCMethod method = createGetMethod(fmd);
3511 Code code = method.getCode(true);
3512
3513 // if reads are not checked, just return the value
3514 byte fieldFlag = getFieldFlag(fmd);
3515 if ((fieldFlag & PersistenceCapable.CHECK_READ) == 0
3516 && (fieldFlag & PersistenceCapable.MEDIATE_READ) == 0) {
3517 loadManagedInstance(code, true);
3518 addGetManagedValueCode(code, fmd);
3519 code.xreturn().setType(fmd.getDeclaredType());
3520
3521 code.calculateMaxStack();
3522 code.calculateMaxLocals();
3523 return;
3524 }
3525
3526 // if (inst.pcStateManager == null) return inst.<field>;
3527 loadManagedInstance(code, true);
3528 code.getfield().setField(SM, SMTYPE);
3529 JumpInstruction ifins = code.ifnonnull();
3530 loadManagedInstance(code, true);
3531 addGetManagedValueCode(code, fmd);
3532 code.xreturn().setType(fmd.getDeclaredType());
3533
3534 // int field = pcInheritedFieldCount + <fieldindex>;
3535 int fieldLocal = code.getNextLocalsIndex();
3536 ifins.setTarget(code.getstatic().setField(INHERIT, int.class));
3537 code.constant().setValue(index);
3538 code.iadd();
3539 code.istore().setLocal(fieldLocal);
3540
3541 // inst.pcStateManager.accessingField (field);
3542 // return inst.<field>;
3543 loadManagedInstance(code, true);
3544 code.getfield().setField(SM, SMTYPE);
3545 code.iload().setLocal(fieldLocal);
3546 code.invokeinterface().setMethod(SMTYPE, "accessingField", void.class,
3547 new Class[]{ int.class });
3548 loadManagedInstance(code, true);
3549 addGetManagedValueCode(code, fmd);
3550 code.xreturn().setType(fmd.getDeclaredType());
3551
3552 code.calculateMaxStack();
3553 code.calculateMaxLocals();
3554 }
3555
3556 /**
3557 * Adds a static setter method for the given field.
3558 * The generated method interacts with the instance state and the
3559 * StateManager to set the value of the field.
3560 *
3561 * @param index the relative number of the field
3562 * @param fmd metadata about the field to set
3563 */
3564 private void addSetMethod(int index, FieldMetaData fmd)
3565 throws NoSuchMethodException {
3566 BCMethod method = createSetMethod(fmd);
3567 Code code = method.getCode(true);
3568
3569 // PCEnhancer uses static methods; PCSubclasser does not.
3570 int firstParamOffset = getAccessorParameterOffset();
3571
3572 // if (inst.pcStateManager == null) inst.<field> = value;
3573 loadManagedInstance(code, true);
3574 code.getfield().setField(SM, SMTYPE);
3575 JumpInstruction ifins = code.ifnonnull();
3576 loadManagedInstance(code, true);
3577 code.xload().setParam(firstParamOffset);
3578 addSetManagedValueCode(code, fmd);
3579 code.vreturn();
3580
3581 // inst.pcStateManager.setting<fieldType>Field (inst,
3582 // pcInheritedFieldCount + <index>, inst.<field>, value, 0);
3583 ifins.setTarget(loadManagedInstance(code, true));
3584 code.getfield().setField(SM, SMTYPE);
3585 loadManagedInstance(code, true);
3586 code.getstatic().setField(INHERIT, int.class);
3587 code.constant().setValue(index);
3588 code.iadd();
3589 loadManagedInstance(code, true);
3590 addGetManagedValueCode(code, fmd);
3591 code.xload().setParam(firstParamOffset);
3592 code.constant().setValue(0);
3593 code.invokeinterface().setMethod(getStateManagerMethod
3594 (fmd.getDeclaredType(), "setting", false, true));
3595 code.vreturn();
3596
3597 code.calculateMaxStack();
3598 code.calculateMaxLocals();
3599 }
3600
3601 /**
3602 * Determines which attach / detach methods to use.
3603 */
3604 private void addAttachDetachCode()
3605 throws NoSuchMethodException {
3606 // see if any superclasses are detachable
3607 boolean parentDetachable = false;
3608 for (ClassMetaData parent = _meta.getPCSuperclassMetaData();
3609 parent != null; parent = parent.getPCSuperclassMetaData()) {
3610 if (parent.isDetachable()) {
3611 parentDetachable = true;
3612 break;
3613 }
3614 }
3615
3616 // if parent not detachable, we need to add the detach state fields and
3617 // accessor methods
3618 if (_meta.getPCSuperclass() == null || getCreateSubclass()
3619 || parentDetachable != _meta.isDetachable()) {
3620 addIsDetachedMethod();
3621 addDetachedStateMethods(_meta.usesDetachedState()
3622 != Boolean.FALSE);
3623 }
3624
3625 // if we detach on serialize, we also need to implement the
3626 // externalizable interface to write just the state for the fields
3627 // being detached
3628 if (externalizeDetached()) {
3629 try {
3630 addDetachExternalize(parentDetachable,
3631 _meta.usesDetachedState() != Boolean.FALSE);
3632 } catch (NoSuchMethodException nsme) {
3633 throw new GeneralException(nsme);
3634 }
3635 }
3636 }
3637
3638 /**
3639 * Add the fields to hold detached state and their accessor methods.
3640 *
3641 * @param impl whether to fully implement detach state functionality
3642 */
3643 private void addDetachedStateMethods(boolean impl) {
3644 Field detachField = _meta.getDetachedStateField();
3645 String name = null;
3646 String declarer = null;
3647 if (impl && detachField == null) {
3648 name = PRE + "DetachedState";
3649 declarer = _pc.getName();
3650 BCField field = _pc.declareField(name, Object.class);
3651 field.makePrivate();
3652 field.setTransient(true);
3653 } else if (impl) {
3654 name = detachField.getName();
3655 declarer = detachField.getDeclaringClass().getName();
3656 }
3657
3658 // public Object pcGetDetachedState ()
3659 BCMethod method = _pc.declareMethod(PRE + "GetDetachedState",
3660 Object.class, null);
3661 method.setStatic(false);
3662 method.makePublic();
3663 int access = method.getAccessFlags();
3664
3665 Code code = method.getCode(true);
3666 if (impl) {
3667 // return pcDetachedState;
3668 loadManagedInstance(code, false);
3669 getfield(code, _managedType.getProject().loadClass(declarer),
3670 name);
3671 } else
3672 code.constant().setNull();
3673 code.areturn();
3674 code.calculateMaxLocals();
3675 code.calculateMaxStack();
3676
3677 // public void pcSetDetachedState (Object state)
3678 method = _pc.declareMethod(PRE + "SetDetachedState",
3679 void.class, new Class []{ Object.class });
3680 method.setAccessFlags(access);
3681 code = method.getCode(true);
3682 if (impl) {
3683 // pcDetachedState = state;
3684 loadManagedInstance(code, false);
3685 code.aload().setParam(0);
3686 putfield(code, _managedType.getProject().loadClass(declarer),
3687 name, Object.class);
3688 }
3689 code.vreturn();
3690 code.calculateMaxStack();
3691 code.calculateMaxLocals();
3692 }
3693
3694 /**
3695 * Adds to <code>code</code> the instructions to get field
3696 * <code>attrName</code> declared in type <code>declarer</code>
3697 * onto the top of the stack.
3698 *
3699 * The instance to access must already be on the top of the
3700 * stack when this is invoked.
3701 */
3702 private void getfield(Code code, BCClass declarer, String attrName) {
3703 if (declarer == null)
3704 declarer = _managedType;
3705
3706 // first, see if we can convert the attribute name to a field name
3707 String fieldName = toBackingFieldName(attrName);
3708
3709 // next, find the field in the managed type hierarchy
3710 BCField field = null;
3711 outer: for (BCClass bc = _pc; bc != null; bc = bc.getSuperclassBC()) {
3712 BCField[] fields = (BCField[]) AccessController
3713 .doPrivileged(J2DoPrivHelper.getBCClassFieldsAction(bc,
3714 fieldName));
3715 for (int i = 0; i < fields.length; i++) {
3716 field = fields[i];
3717 // if we reach a field declared in this type, then this is the
3718 // most-masking field, and is the one that we want.
3719 if (fields[i].getDeclarer() == declarer) {
3720 break outer;
3721 }
3722 }
3723 }
3724
3725 if (getCreateSubclass() && code.getMethod().getDeclarer() == _pc
3726 && (field == null || !field.isPublic())) {
3727 // we're creating the subclass, not redefining the user type.
3728
3729 // Reflection.getXXX(this, Reflection.findField(...));
3730 code.classconstant().setClass(declarer);
3731 code.constant().setValue(fieldName);
3732 code.constant().setValue(true);
3733 code.invokestatic().setMethod(Reflection.class,
3734 "findField", Field.class, new Class[] {
3735 Class.class, String.class, boolean.class });
3736 Class type = _meta.getField(attrName).getDeclaredType();
3737 try {
3738 code.invokestatic().setMethod(
3739 getReflectionGetterMethod(type, Field.class));
3740 } catch (NoSuchMethodException e) {
3741 // should never happen
3742 throw new InternalException(e);
3743 }
3744 if (!type.isPrimitive() && type != Object.class)
3745 code.checkcast().setType(type);
3746 } else {
3747 code.getfield().setField(declarer.getName(), fieldName,
3748 field.getType().getName());
3749 }
3750 }
3751
3752 /**
3753 * Adds to <code>code</code> the instructions to set field
3754 * <code>attrName</code> declared in type <code>declarer</code>
3755 * to the value of type <code>fieldType</code> on the top of the stack.
3756 *
3757 * When this method is invoked, the value to load must
3758 * already be on the top of the stack in <code>code</code>,
3759 * and the instance to load into must be second.
3760 */
3761 private void putfield(Code code, BCClass declarer, String attrName,
3762 Class fieldType) {
3763 if (declarer == null)
3764 declarer = _managedType;
3765
3766 String fieldName = toBackingFieldName(attrName);
3767
3768 if (getRedefine() || getCreateSubclass()) {
3769 // Reflection.set(this, Reflection.findField(...), value);
3770 code.classconstant().setClass(declarer);
3771 code.constant().setValue(fieldName);
3772 code.constant().setValue(true);
3773 code.invokestatic().setMethod(Reflection.class,
3774 "findField", Field.class, new Class[] {
3775 Class.class, String.class, boolean.class });
3776 code.invokestatic().setMethod(Reflection.class, "set",
3777 void.class,
3778 new Class[] {
3779 Object.class,
3780 fieldType.isPrimitive() ? fieldType : Object.class,
3781 Field.class });
3782 } else {
3783 code.putfield()
3784 .setField(declarer.getName(), fieldName, fieldType.getName());
3785 }
3786 }
3787
3788 /**
3789 * If using property access, see if there is a different backing field
3790 * name for the persistent attribute <code>name</code>.
3791 */
3792 private String toBackingFieldName(String name) {
3793 if (_meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY
3794 && _attrsToFields != null && _attrsToFields.containsKey(name))
3795 name = (String) _attrsToFields.get(name);
3796 return name;
3797 }
3798
3799 /**
3800 * If using property access, see if there is a different persistent
3801 * attribute name for the backing field <code>name</code>.
3802 */
3803 private String fromBackingFieldName(String name) {
3804 // meta is null when doing persistence-aware enhancement
3805 if (_meta != null
3806 && _meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY
3807 && _fieldsToAttrs != null && _fieldsToAttrs.containsKey(name))
3808 return (String) _fieldsToAttrs.get(name);
3809 else
3810 return name;
3811 }
3812
3813 /**
3814 * Implement the externalizable interface to detach on serialize.
3815 */
3816 private void addDetachExternalize(boolean parentDetachable,
3817 boolean detachedState)
3818 throws NoSuchMethodException {
3819 // ensure that the declared default constructor is public
3820 // for externalization
3821 BCMethod meth = _pc.getDeclaredMethod("<init>", (String[]) null);
3822 if (!meth.isPublic()) {
3823 if (_log.isWarnEnabled())
3824 _log.warn(_loc.get("enhance-defcons-extern",
3825 _meta.getDescribedType()));
3826 meth.makePublic();
3827 }
3828 // declare externalizable interface
3829 if (!Externalizable.class.isAssignableFrom(_meta.getDescribedType()))
3830 _pc.declareInterface(Externalizable.class);
3831
3832 // make sure the user doesn't already have custom externalization or
3833 // serialization methods
3834 Class[] input = new Class[]{ ObjectInputStream.class };
3835 Class[] output = new Class[]{ ObjectOutputStream.class };
3836 if (_managedType.getDeclaredMethod("readObject", input) != null
3837 || _managedType.getDeclaredMethod("writeObject", output) != null)
3838 throw new UserException(_loc.get("detach-custom-ser", _meta));
3839 input[0] = ObjectInput.class;
3840 output[0] = ObjectOutput.class;
3841 if (_managedType.getDeclaredMethod("readExternal", input) != null
3842 || _managedType.getDeclaredMethod("writeExternal", output) != null)
3843 throw new UserException(_loc.get("detach-custom-extern", _meta));
3844
3845 // create list of all unmanaged serializable fields
3846 BCField[] fields = _managedType.getDeclaredFields();
3847 Collection unmgd = new ArrayList(fields.length);
3848 for (int i = 0; i < fields.length; i++) {
3849 if (!fields[i].isTransient() && !fields[i].isStatic()
3850 && !fields[i].isFinal()
3851 && !fields[i].getName().startsWith(PRE)
3852 && _meta.getDeclaredField(fields[i].getName()) == null)
3853 unmgd.add(fields[i]);
3854 }
3855
3856 addReadExternal(parentDetachable, detachedState);
3857 addReadUnmanaged(unmgd, parentDetachable);
3858 addWriteExternal(parentDetachable, detachedState);
3859 addWriteUnmanaged(unmgd, parentDetachable);
3860 }
3861
3862 /**
3863 * Add custom readExternal method.
3864 */
3865 private void addReadExternal(boolean parentDetachable,
3866 boolean detachedState)
3867 throws NoSuchMethodException {
3868 Class[] inargs = new Class[]{ ObjectInput.class };
3869 BCMethod meth = _pc.declareMethod("readExternal", void.class, inargs);
3870 Exceptions exceps = meth.getExceptions(true);
3871 exceps.addException(IOException.class);
3872 exceps.addException(ClassNotFoundException.class);
3873 Code code = meth.getCode(true);
3874
3875 // super.readExternal (in);
3876 Class sup = _meta.getDescribedType().getSuperclass();
3877 if (!parentDetachable && Externalizable.class.isAssignableFrom(sup)) {
3878 loadManagedInstance(code, false);
3879 code.aload().setParam(0);
3880 code.invokespecial().setMethod(sup, "readExternal",
3881 void.class, inargs);
3882 }
3883
3884 // readUnmanaged (in);
3885 loadManagedInstance(code, false);
3886 code.aload().setParam(0);
3887 code.invokevirtual().setMethod(getType(_meta),
3888 PRE + "ReadUnmanaged", void.class, inargs);
3889
3890 if (detachedState) {
3891 // pcSetDetachedState (in.readObject ());
3892 loadManagedInstance(code, false);
3893 code.aload().setParam(0);
3894 code.invokeinterface().setMethod(ObjectInput.class, "readObject",
3895 Object.class, null);
3896 code.invokevirtual().setMethod(PRE + "SetDetachedState",
3897 void.class, new Class[]{ Object.class });
3898
3899 // pcReplaceStateManager ((StateManager) in.readObject ());
3900 loadManagedInstance(code, false);
3901 code.aload().setParam(0);
3902 code.invokeinterface().setMethod(ObjectInput.class, "readObject",
3903 Object.class, null);
3904 code.checkcast().setType(StateManager.class);
3905 code.invokevirtual().setMethod(PRE + "ReplaceStateManager",
3906 void.class, new Class[]{ StateManager.class });
3907 }
3908
3909 // read managed fields
3910 FieldMetaData[] fmds = _meta.getFields();
3911 for (int i = 0; i < fmds.length; i++)
3912 if (!fmds[i].isTransient())
3913 readExternal(code, fmds[i].getName(),
3914 fmds[i].getDeclaredType(), fmds[i]);
3915
3916 code.vreturn();
3917 code.calculateMaxStack();
3918 code.calculateMaxLocals();
3919 }
3920
3921 /**
3922 * Read unmanaged fields from the stream (pcReadUnmanaged).
3923 */
3924 private void addReadUnmanaged(Collection unmgd, boolean parentDetachable)
3925 throws NoSuchMethodException {
3926 Class[] inargs = new Class[]{ ObjectInput.class };
3927 BCMethod meth = _pc.declareMethod(PRE + "ReadUnmanaged", void.class,
3928 inargs);
3929 meth.makeProtected();
3930 Exceptions exceps = meth.getExceptions(true);
3931 exceps.addException(IOException.class);
3932 exceps.addException(ClassNotFoundException.class);
3933 Code code = meth.getCode(true);
3934
3935 // super.readUnmanaged (in);
3936 if (parentDetachable) {
3937 loadManagedInstance(code, false);
3938 code.aload().setParam(0);
3939 code.invokespecial().setMethod(getType(_meta.
3940 getPCSuperclassMetaData()), PRE + "ReadUnmanaged", void.class,
3941 inargs);
3942 }
3943
3944 // read declared unmanaged serializable fields
3945 BCField field;
3946 for (Iterator itr = unmgd.iterator(); itr.hasNext();) {
3947 field = (BCField) itr.next();
3948 readExternal(code, field.getName(), field.getType(), null);
3949 }
3950 code.vreturn();
3951 code.calculateMaxStack();
3952 code.calculateMaxLocals();
3953 }
3954
3955 /**
3956 * Helper method to read a field from an externalization input stream.
3957 */
3958 private void readExternal(Code code, String fieldName, Class type,
3959 FieldMetaData fmd)
3960 throws NoSuchMethodException {
3961 String methName;
3962 if (type.isPrimitive()) {
3963 methName = type.getName();
3964 methName = Character.toUpperCase(methName.charAt(0))
3965 + methName.substring(1);
3966 methName = "read" + methName;
3967 } else
3968 methName = "readObject";
3969
3970 // <field> = in.read<type> ();
3971 loadManagedInstance(code, false);
3972 code.aload().setParam(0);
3973 Class ret = (type.isPrimitive()) ? type : Object.class;
3974 code.invokeinterface().setMethod(ObjectInput.class, methName,
3975 ret, null);
3976 if (!type.isPrimitive() && type != Object.class)
3977 code.checkcast().setType(type);
3978 if (fmd == null)
3979 putfield(code, null, fieldName, type);
3980 else {
3981 addSetManagedValueCode(code, fmd);
3982 switch (fmd.getDeclaredTypeCode()) {
3983 case JavaTypes.DATE:
3984 case JavaTypes.ARRAY:
3985 case JavaTypes.COLLECTION:
3986 case JavaTypes.MAP:
3987 case JavaTypes.OBJECT:
3988 case JavaTypes.CALENDAR:
3989 // if (sm != null)
3990 // sm.proxyDetachedDeserialized (<index>);
3991 loadManagedInstance(code, false);
3992 code.getfield().setField(SM, SMTYPE);
3993 IfInstruction ifins = code.ifnull();
3994 loadManagedInstance(code, false);
3995 code.getfield().setField(SM, SMTYPE);
3996 code.constant().setValue(fmd.getIndex());
3997 code.invokeinterface().setMethod(SMTYPE,
3998 "proxyDetachedDeserialized", void.class,
3999 new Class[]{ int.class });
4000 ifins.setTarget(code.nop());
4001 }
4002 }
4003 }
4004
4005 /**
4006 * Add custom writeExternal method.
4007 */
4008 private void addWriteExternal(boolean parentDetachable,
4009 boolean detachedState)
4010 throws NoSuchMethodException {
4011 Class[] outargs = new Class[]{ ObjectOutput.class };
4012 BCMethod meth = _pc.declareMethod("writeExternal", void.class, outargs);
4013 Exceptions exceps = meth.getExceptions(true);
4014 exceps.addException(IOException.class);
4015 Code code = meth.getCode(true);
4016
4017 // super.writeExternal (out);
4018 Class sup = getType(_meta).getSuperclass();
4019 if (!parentDetachable && Externalizable.class.isAssignableFrom(sup)) {
4020 loadManagedInstance(code, false);
4021 code.aload().setParam(0);
4022 code.invokespecial().setMethod(sup, "writeExternal",
4023 void.class, outargs);
4024 }
4025
4026 // writeUnmanaged (out);
4027 loadManagedInstance(code, false);
4028 code.aload().setParam(0);
4029 code.invokevirtual().setMethod(getType(_meta),
4030 PRE + "WriteUnmanaged", void.class, outargs);
4031
4032 JumpInstruction go2 = null;
4033 if (detachedState) {
4034 // if (sm != null)
4035 // if (sm.writeDetached (out))
4036 // return;
4037 loadManagedInstance(code, false);
4038 code.getfield().setField(SM, SMTYPE);
4039 IfInstruction ifnull = code.ifnull();
4040 loadManagedInstance(code, false);
4041 code.getfield().setField(SM, SMTYPE);
4042 code.aload().setParam(0);
4043 code.invokeinterface().setMethod(SMTYPE, "writeDetached",
4044 boolean.class, outargs);
4045 go2 = code.ifeq();
4046 code.vreturn();
4047
4048 // else
4049 // out.writeObject (pcGetDetachedState ());
4050 Class[] objargs = new Class[]{ Object.class };
4051 ifnull.setTarget(code.aload().setParam(0));
4052 loadManagedInstance(code, false);
4053 code.invokevirtual().setMethod(PRE + "GetDetachedState",
4054 Object.class, null);
4055 code.invokeinterface().setMethod(ObjectOutput.class,
4056 "writeObject", void.class, objargs);
4057 // out.writeObject (null) // StateManager
4058 code.aload().setParam(0);
4059 code.constant().setValue((Object) null);
4060 code.invokeinterface().setMethod(ObjectOutput.class,
4061 "writeObject", void.class, objargs);
4062 }
4063 if (go2 != null)
4064 go2.setTarget(code.nop());
4065
4066 // write managed fields
4067 FieldMetaData[] fmds = _meta.getFields();
4068 for (int i = 0; i < fmds.length; i++)
4069 if (!fmds[i].isTransient())
4070 writeExternal(code, fmds[i].getName(),
4071 fmds[i].getDeclaredType(), fmds[i]);
4072
4073 // return
4074 code.vreturn();
4075 code.calculateMaxStack();
4076 code.calculateMaxLocals();
4077 }
4078
4079 /**
4080 * Write unmanaged fields to the stream (pcWriteUnmanaged).
4081 */
4082 private void addWriteUnmanaged(Collection unmgd, boolean parentDetachable)
4083 throws NoSuchMethodException {
4084 Class[] outargs = new Class[]{ ObjectOutput.class };
4085 BCMethod meth = _pc.declareMethod(PRE + "WriteUnmanaged", void.class,
4086 outargs);
4087 meth.makeProtected();
4088 Exceptions exceps = meth.getExceptions(true);
4089 exceps.addException(IOException.class);
4090 Code code = meth.getCode(true);
4091
4092 // super.writeUnmanaged (out);
4093 if (parentDetachable) {
4094 loadManagedInstance(code, false);
4095 code.aload().setParam(0);
4096 code.invokespecial().setMethod(getType(_meta.
4097 getPCSuperclassMetaData()), PRE + "WriteUnmanaged", void.class,
4098 outargs);
4099 }
4100
4101 // write declared unmanaged serializable fields
4102 BCField field;
4103 for (Iterator itr = unmgd.iterator(); itr.hasNext();) {
4104 field = (BCField) itr.next();
4105 writeExternal(code, field.getName(), field.getType(), null);
4106 }
4107 code.vreturn();
4108 code.calculateMaxStack();
4109 code.calculateMaxLocals();
4110 }
4111
4112 /**
4113 * Helper method to write a field to an externalization output stream.
4114 */
4115 private void writeExternal(Code code, String fieldName, Class type,
4116 FieldMetaData fmd)
4117 throws NoSuchMethodException {
4118 String methName;
4119 if (type.isPrimitive()) {
4120 methName = type.getName();
4121 methName = Character.toUpperCase(methName.charAt(0))
4122 + methName.substring(1);
4123 methName = "write" + methName;
4124 } else
4125 methName = "writeObject";
4126
4127 // out.write<type> (<field>);
4128 code.aload().setParam(0);
4129 loadManagedInstance(code, false);
4130 if (fmd == null)
4131 getfield(code, null, fieldName);
4132 else
4133 addGetManagedValueCode(code, fmd);
4134 Class[] args = new Class[]{ type };
4135 if (type == byte.class || type == char.class || type == short.class)
4136 args[0] = int.class;
4137 else if (!type.isPrimitive())
4138 args[0] = Object.class;
4139 code.invokeinterface().setMethod(ObjectOutput.class, methName,
4140 void.class, args);
4141 }
4142
4143 private void addGetManagedValueCode(Code code, FieldMetaData fmd)
4144 throws NoSuchMethodException {
4145 addGetManagedValueCode(code, fmd, true);
4146 }
4147
4148 /**
4149 * Load the field value specified by <code>fmd</code> onto the stack.
4150 * Before this method is called, the object that the data should be loaded
4151 * from will be on the top of the stack.
4152 *
4153 * @param fromSameClass if <code>true</code>, then <code>fmd</code> is
4154 * being loaded from an instance of the same class as the current execution
4155 * context. If <code>false</code>, then the instance on the top of the stack
4156 * might be a superclass of the current execution context's 'this' instance.
4157 */
4158 private void addGetManagedValueCode(Code code, FieldMetaData fmd,
4159 boolean fromSameClass)
4160 throws NoSuchMethodException {
4161 // if redefining, then we must always reflect (or access the field
4162 // directly if accessible), since the redefined methods will always
4163 // trigger method calls to StateManager, even from internal direct-
4164 // access usage. We could work around this by not redefining, and
4165 // just do a subclass approach instead. But this is not a good option,
4166 // since it would sacrifice lazy loading and efficient dirty tracking.
4167
4168 if (getRedefine()
4169 || _meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
4170 getfield(code, null, fmd.getName());
4171 } else if (getCreateSubclass()) {
4172 // property access, and we're not redefining. If we're operating
4173 // on an instance that is definitely the same type as 'this', then
4174 // call superclass method to bypass tracking. Otherwise, reflect
4175 // to both bypass tracking and avoid class verification errors.
4176 if (fromSameClass) {
4177 Method meth = (Method) fmd.getBackingMember();
4178 code.invokespecial().setMethod(meth);
4179 } else {
4180 getfield(code, null, fmd.getName());
4181 }
4182 } else {
4183 // regular enhancement + property access
4184 Method meth = (Method) fmd.getBackingMember();
4185 code.invokevirtual().setMethod(PRE + meth.getName(),
4186 meth.getReturnType(), meth.getParameterTypes());
4187 }
4188 }
4189
4190 /**
4191 * Store the value at the top of the stack into the field value specified
4192 * by <code>fmd</code>. Before this method is called, the data to load will
4193 * be on the top of the stack and the object that the data should be loaded
4194 * into will be second in the stack.
4195 */
4196 private void addSetManagedValueCode(Code code, FieldMetaData fmd)
4197 throws NoSuchMethodException {
4198 // if redefining, then we must always reflect (or access the field
4199 // directly if accessible), since the redefined methods will always
4200 // trigger method calls to StateManager, even from internal direct-
4201 // access usage. We could work around this by not redefining, and
4202 // just do a subclass approach instead. But this is not a good option,
4203 // since it would sacrifice lazy loading and efficient dirty tracking.
4204
4205 if (getRedefine()
4206 || _meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
4207 putfield(code, null, fmd.getName(), fmd.getDeclaredType());
4208 } else if (getCreateSubclass()) {
4209 // property access, and we're not redefining. invoke the
4210 // superclass method to bypass tracking.
4211 code.invokespecial().setMethod(_managedType.getType(),
4212 getSetterName(fmd), void.class,
4213 new Class[] { fmd.getDeclaredType() });
4214 } else {
4215 // regular enhancement + property access
4216 code.invokevirtual().setMethod(PRE + getSetterName(fmd),
4217 void.class, new Class[] { fmd.getDeclaredType() });
4218 }
4219 }
4220
4221 /**
4222 * Return the offset that the first meaningful accessor parameter is at.
4223 */
4224 private int getAccessorParameterOffset() {
4225 return (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) ? 1 : 0;
4226 }
4227
4228 /**
4229 * Add the {@link Instruction}s to load the instance to modify onto the
4230 * stack, and return it. If <code>forStatic</code> is set, then
4231 * <code>code</code> is in an accessor method or another static method;
4232 * otherwise, it is in one of the PC-specified methods.
4233 *
4234 * @return the first instruction added to <code>code</code>.
4235 */
4236 private Instruction loadManagedInstance(Code code, boolean forStatic) {
4237 if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD && forStatic)
4238 return code.aload().setParam(0);
4239 return code.aload().setThis();
4240 }
4241
4242 /**
4243 * Create the generated getter {@link BCMethod} for <code>fmd</code>. The
4244 * calling environment will then populate this method's code block.
4245 */
4246 private BCMethod createGetMethod(FieldMetaData fmd) {
4247 BCMethod getter;
4248 if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
4249 // static <fieldtype> pcGet<field> (XXX inst)
4250 BCField field = _pc.getDeclaredField(fmd.getName());
4251 getter = _pc.declareMethod(PRE + "Get" + fmd.getName(), fmd.
4252 getDeclaredType().getName(), new String[]{ _pc.getName() });
4253 getter.setAccessFlags(field.getAccessFlags()
4254 & ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE);
4255 getter.setStatic(true);
4256 getter.setFinal(true);
4257 return getter;
4258 }
4259
4260 // property access:
4261 // copy the user's getter method to a new name; we can't just reset
4262 // the name, because that will also reset all calls to the method
4263 Method meth = (Method) fmd.getBackingMember();
4264 getter = _pc.getDeclaredMethod(meth.getName(),
4265 meth.getParameterTypes());
4266 BCMethod newgetter = _pc.declareMethod(PRE + meth.getName(),
4267 meth.getReturnType(), meth.getParameterTypes());
4268 newgetter.setAccessFlags(getter.getAccessFlags());
4269 newgetter.makeProtected();
4270 transferCodeAttributes(getter, newgetter);
4271 return getter;
4272 }
4273
4274 /**
4275 * Create the generated setter {@link BCMethod} for <code>fmd</code>. The
4276 * calling environment will then populate this method's code block.
4277 */
4278 private BCMethod createSetMethod(FieldMetaData fmd) {
4279 BCMethod setter;
4280 if (_meta.getAccessType() == ClassMetaData.ACCESS_FIELD) {
4281 // static void pcSet<field> (XXX inst, <fieldtype> value)
4282 BCField field = _pc.getDeclaredField(fmd.getName());
4283 setter = _pc.declareMethod(PRE + "Set" + fmd.getName(), void.class,
4284 new Class[]{ getType(_meta), fmd.getDeclaredType() });
4285 setter.setAccessFlags(field.getAccessFlags()
4286 & ~Constants.ACCESS_TRANSIENT & ~Constants.ACCESS_VOLATILE);
4287 setter.setStatic(true);
4288 setter.setFinal(true);
4289 return setter;
4290 }
4291
4292 // property access:
4293 // copy the user's getter method to a new name; we can't just reset
4294 // the name, because that will also reset all calls to the method
4295 setter = _pc.getDeclaredMethod(getSetterName(fmd),
4296 new Class[]{ fmd.getDeclaredType() });
4297 BCMethod newsetter = _pc.declareMethod(PRE + setter.getName(),
4298 setter.getReturnName(), setter.getParamNames());
4299 newsetter.setAccessFlags(setter.getAccessFlags());
4300 newsetter.makeProtected();
4301 transferCodeAttributes(setter, newsetter);
4302 return setter;
4303 }
4304
4305 private void addGetEnhancementContractVersionMethod() {
4306 // public int getEnhancementContractVersion()
4307 BCMethod method = _pc.declareMethod(PRE +
4308 "GetEnhancementContractVersion", int.class, null);
4309 method.makePublic();
4310 Code code = method.getCode(true);
4311 code.constant().setValue(ENHANCER_VERSION);
4312 code.ireturn();
4313 code.calculateMaxStack();
4314 code.calculateMaxLocals();
4315 }
4316
4317 /**
4318 * Return the concrete type for the given class, i.e. impl for managed
4319 * interfaces
4320 */
4321 public Class getType(ClassMetaData meta) {
4322 if (meta.getInterfaceImpl() != null)
4323 return meta.getInterfaceImpl();
4324 return meta.getDescribedType();
4325 }
4326
4327 /**
4328 * Move code-related attributes from one method to another.
4329 */
4330 private static void transferCodeAttributes(BCMethod from, BCMethod to) {
4331 Code code = from.getCode(false);
4332 if (code != null) {
4333 to.addAttribute(code);
4334 from.removeCode();
4335 }
4336
4337 Exceptions exceps = from.getExceptions(false);
4338 if (exceps != null)
4339 to.addAttribute(exceps);
4340 }
4341
4342 /**
4343 * Usage: java org.apache.openjpa.enhance.PCEnhancer [option]*
4344 * <class name | .java file | .class file | .jdo file>+
4345 * Where the following options are recognized.
4346 * <ul>
4347 * <li><i>-properties/-p <properties file></i>: The path to a OpenJPA
4348 * properties file containing information as outlined in
4349 * {@link Configuration}; optional.</li>
4350 * <li><i>-<property name> <property value></i>: All bean
4351 * properties of the standard OpenJPA {@link OpenJPAConfiguration} can be
4352 * set by using their names and supplying a value; for example:
4353 * <li><i>-directory/-d <build directory></i>: The path to the base
4354 * directory where enhanced classes are stored. By default, the
4355 * enhancer overwrites the original .class file with the enhanced
4356 * version. Use this option to store the generated .class file in
4357 * another directory. The package structure will be created beneath
4358 * the given directory.</li>
4359 * <li><i>-addDefaultConstructor/-adc [true/t | false/f]</i>: Whether to
4360 * add a default constructor to persistent classes missing one, as
4361 * opposed to throwing an exception. Defaults to true.</li>
4362 * <li><i>-tmpClassLoader/-tcl [true/t | false/f]</i>: Whether to
4363 * load the pre-enhanced classes using a temporary class loader.
4364 * Defaults to true. Set this to false when attempting to debug
4365 * class loading errors.</li>
4366 * <li><i>-enforcePropertyRestrictions/-epr [true/t | false/f]</i>:
4367 * Whether to throw an exception if a PROPERTY access entity appears
4368 * to be violating standard property restrictions. Defaults to false.</li>
4369 * </ul>
4370 * Each additional argument can be either the full class name of the
4371 * type to enhance, the path to the .java file for the type, the path to
4372 * the .class file for the type, or the path to a .jdo file listing one
4373 * or more types to enhance.
4374 * If the type being enhanced has metadata, it will be enhanced as a
4375 * persistence capable class. If not, it will be considered a persistence
4376 * aware class, and all access to fields of persistence capable classes
4377 * will be replaced by the appropriate get/set method. If the type
4378 * explicitly declares the persistence-capable interface, it will
4379 * not be enhanced. Thus, it is safe to invoke the enhancer on classes
4380 * that are already enhanced.
4381 */
4382 public static void main(String[] args) {
4383 Options opts = new Options();
4384 args = opts.setFromCmdLine(args);
4385 if (!run(args, opts))
4386 System.err.println(_loc.get("enhance-usage"));
4387 }
4388
4389 /**
4390 * Run the tool. Returns false if invalid options given. Runs against all
4391 * the persistence units defined in the resource to parse.
4392 */
4393 public static boolean run(final String[] args, Options opts) {
4394 return Configurations.runAgainstAllAnchors(opts,
4395 new Configurations.Runnable() {
4396 public boolean run(Options opts) throws IOException {
4397 OpenJPAConfiguration conf = new OpenJPAConfigurationImpl();
4398 try {
4399 return PCEnhancer.run(conf, args, opts);
4400 } finally {
4401 conf.close();
4402 }
4403 }
4404 });
4405 }
4406
4407 /**
4408 * Run the tool. Returns false if invalid options given.
4409 */
4410 public static boolean run(OpenJPAConfiguration conf, String[] args,
4411 Options opts)
4412 throws IOException {
4413 Flags flags = new Flags();
4414 flags.directory = Files.getFile(opts.removeProperty("directory", "d",
4415 null), null);
4416 flags.addDefaultConstructor = opts.removeBooleanProperty
4417 ("addDefaultConstructor", "adc", flags.addDefaultConstructor);
4418 flags.tmpClassLoader = opts.removeBooleanProperty
4419 ("tmpClassLoader", "tcl", flags.tmpClassLoader);
4420 flags.enforcePropertyRestrictions = opts.removeBooleanProperty
4421 ("enforcePropertyRestrictions", "epr",
4422 flags.enforcePropertyRestrictions);
4423
4424 // for unit testing
4425 BytecodeWriter writer = (BytecodeWriter) opts.get(
4426 PCEnhancer.class.getName() + "#bytecodeWriter");
4427
4428 Configurations.populateConfiguration(conf, opts);
4429 return run(conf, args, flags, null, writer, null);
4430 }
4431
4432 /**
4433 * Enhance the given classes.
4434 */
4435 public static boolean run(OpenJPAConfiguration conf, String[] args,
4436 Flags flags, MetaDataRepository repos, BytecodeWriter writer,
4437 ClassLoader loader)
4438 throws IOException {
4439 if (loader == null)
4440 loader = conf.getClassResolverInstance().
4441 getClassLoader(PCEnhancer.class, null);
4442 if (flags.tmpClassLoader)
4443 loader = (ClassLoader) AccessController.doPrivileged(J2DoPrivHelper
4444 .newTemporaryClassLoaderAction(loader));
4445
4446 if (repos == null) {
4447 repos = conf.newMetaDataRepositoryInstance();
4448 repos.setSourceMode(MetaDataRepository.MODE_META);
4449 }
4450
4451 Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL);
4452 Collection classes;
4453 if (args == null || args.length == 0) {
4454 log.info(_loc.get("running-all-classes"));
4455 classes = repos.getPersistentTypeNames(true, loader);
4456 if (classes == null) {
4457 log.warn(_loc.get("no-class-to-enhance"));
4458 return false;
4459 }
4460 } else {
4461 ClassArgParser cap = conf.getMetaDataRepositoryInstance().
4462 getMetaDataFactory().newClassArgParser();
4463 cap.setClassLoader(loader);
4464 classes = new HashSet();
4465 for (int i = 0; i < args.length; i++)
4466 classes.addAll(Arrays.asList(cap.parseTypes(args[i])));
4467 }
4468
4469 Project project = new Project();
4470 BCClass bc;
4471 PCEnhancer enhancer;
4472 int status;
4473 for (Iterator itr = classes.iterator(); itr.hasNext();) {
4474 Object o = itr.next();
4475 if (log.isTraceEnabled())
4476 log.trace(_loc.get("enhance-running", o));
4477
4478 if (o instanceof String)
4479 bc = project.loadClass((String) o, loader);
4480 else
4481 bc = project.loadClass((Class) o);
4482 enhancer = new PCEnhancer(conf, bc, repos, loader);
4483 if (writer != null)
4484 enhancer.setBytecodeWriter(writer);
4485 enhancer.setDirectory(flags.directory);
4486 enhancer.setAddDefaultConstructor(flags.addDefaultConstructor);
4487 status = enhancer.run();
4488 if (status == ENHANCE_NONE) {
4489 if (log.isTraceEnabled())
4490 log.trace(_loc.get("enhance-norun"));
4491 } else if (status == ENHANCE_INTERFACE) {
4492 if (log.isTraceEnabled())
4493 log.trace(_loc.get("enhance-interface"));
4494 } else if (status == ENHANCE_AWARE) {
4495 if (log.isTraceEnabled())
4496 log.trace(_loc.get("enhance-aware"));
4497 enhancer.record();
4498 } else
4499 enhancer.record();
4500 project.clear();
4501 }
4502 return true;
4503 }
4504
4505 /**
4506 * Run flags.
4507 */
4508 public static class Flags {
4509
4510 public File directory = null;
4511 public boolean addDefaultConstructor = true;
4512 public boolean tmpClassLoader = true;
4513 public boolean enforcePropertyRestrictions = false;
4514 }
4515
4516 /**
4517 * Plugin interface for additional enhancement.
4518 */
4519 public static interface AuxiliaryEnhancer
4520 {
4521 public void run (BCClass bc, ClassMetaData meta);
4522 public boolean skipEnhance(BCMethod m);
4523 }
4524 }