001: /***
002: * Retrotranslator: a Java bytecode transformer that translates Java classes
003: * compiled with JDK 5.0 into classes that can be run on JVM 1.4.
004: *
005: * Copyright (c) 2005 - 2008 Taras Puchko
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * 3. Neither the name of the copyright holders nor the names of its
017: * contributors may be used to endorse or promote products derived from
018: * this software without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
022: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
023: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
024: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
025: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
026: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
027: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
028: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
029: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
030: * THE POSSIBILITY OF SUCH DAMAGE.
031: */package net.sf.retrotranslator.transformer;
032:
033: import java.util.*;
034: import net.sf.retrotranslator.runtime.asm.*;
035: import static net.sf.retrotranslator.runtime.asm.Opcodes.*;
036: import net.sf.retrotranslator.runtime.impl.*;
037:
038: /**
039: * @author Taras Puchko
040: */
041: class ReplacementLocator {
042:
043: private static final ClassReplacement NULL = new ClassReplacement();
044:
045: private final OperationMode mode;
046: private final List<Backport> backports;
047: private final TargetEnvironment environment;
048: private final Map<String, ClassReplacement> replacements = new Hashtable<String, ClassReplacement>();
049: private final Set<String> unsupportedBackports = new HashSet<String>();
050:
051: interface KeyProvider {
052: MemberKey getKey(String name, String desc);
053: }
054:
055: public ReplacementLocator(OperationMode mode,
056: List<Backport> backports, TargetEnvironment environment) {
057: this .mode = mode;
058: this .backports = backports;
059: this .environment = environment;
060: for (String entry : environment.readRegistry("advanced", mode
061: .getTarget())) {
062: String[] pair = entry.split(":");
063: if (pair.length != 2) {
064: throw new IllegalArgumentException(entry);
065: }
066: if (isTotallyUnsupported(pair[1].split(","))) {
067: unsupportedBackports.add(pair[0]);
068: }
069: }
070: }
071:
072: public TargetEnvironment getEnvironment() {
073: return environment;
074: }
075:
076: private boolean isTotallyUnsupported(String[] features) {
077: for (String feature : features) {
078: if (mode.isSupportedFeature(feature)) {
079: return false;
080: }
081: }
082: return true;
083: }
084:
085: public ClassReplacement getReplacement(String className) {
086: if (className == null || className.length() == 0
087: || className.charAt(0) == '[') {
088: return null;
089: }
090: ClassReplacement replacement = replacements.get(className);
091: if (replacement != null) {
092: return replacement == NULL ? null : replacement;
093: }
094: replacement = buildReplacement(className);
095: replacements.put(className, replacement);
096: if (mode.isSmart()) {
097: addInheritedMembers(replacement);
098: }
099: if (replacement.isEmpty(className)) {
100: replacements.put(className, NULL);
101: return null;
102: }
103: return replacement;
104: }
105:
106: private void addInheritedMembers(ClassReplacement replacement) {
107: ClassReader classReader = environment
108: .findClassReader(replacement.getUniqueTypeName());
109: if (classReader != null) {
110: SmartReplacementVisitor visitor = new SmartReplacementVisitor(
111: this );
112: classReader.accept(visitor, true);
113: visitor.addInheritedMembers(replacement);
114: }
115: }
116:
117: private ClassReplacement buildReplacement(String originalName) {
118: ClassReplacement replacement = new ClassReplacement();
119: for (Backport backport : backports) {
120: if (backport instanceof ClassBackport) {
121: buildForClass(originalName, replacement,
122: (ClassBackport) backport);
123: } else if (backport instanceof MemberBackport) {
124: buildForMember(originalName, replacement,
125: (MemberBackport) backport);
126: } else if (backport instanceof PackageBackport) {
127: buildForPackage(originalName, replacement,
128: (PackageBackport) backport);
129: } else {
130: throw new IllegalStateException();
131: }
132: }
133: completeReplacement(replacement, originalName);
134: return replacement;
135: }
136:
137: private void buildForClass(String originalName,
138: ClassReplacement replacement, ClassBackport backport) {
139: if (replacement.getUniqueTypeName() == null
140: && originalName.equals(backport.getOriginalName())) {
141: replacement
142: .setUniqueTypeName(backport.getReplacementName());
143: }
144: }
145:
146: private void buildForMember(String originalName,
147: ClassReplacement replacement, final MemberBackport backport) {
148: if (!originalName.equals(backport.getOriginalClassName())) {
149: return;
150: }
151: ClassDescriptor classDescriptor = getClassDescriptor(backport
152: .getReplacementClassName());
153: if (classDescriptor == null) {
154: return;
155: }
156: loadAllMembers(replacement, classDescriptor, new KeyProvider() {
157: public MemberKey getKey(String name, String desc) {
158: if (name.equals(backport.getReplacementMemberName())) {
159: return new MemberKey(true, backport
160: .getOriginalMemberName(), desc);
161: }
162: return null;
163: }
164: });
165: }
166:
167: private void buildForPackage(String originalName,
168: ClassReplacement replacement, PackageBackport backport) {
169: String originalPrefix = backport.getOriginalPrefix();
170: String suffix = originalName;
171: if (originalPrefix.length() > 0) {
172: if (originalName.startsWith(originalPrefix)) {
173: suffix = originalName
174: .substring(originalPrefix.length());
175: } else {
176: return;
177: }
178: }
179: String prefix = backport.getReplacementPrefix();
180: if (replacement.getUniqueTypeName() == null) {
181: replacement
182: .setUniqueTypeName(findUniqueName(prefix, suffix));
183: }
184: int index = suffix.lastIndexOf('/');
185: String memberBackportName = prefix
186: + suffix.substring(0, index + 1) + '_'
187: + suffix.substring(index + 1).replace('$', '_');
188: ClassDescriptor classDescriptor = getClassDescriptor(memberBackportName);
189: if (classDescriptor != null) {
190: loadAllMembers(replacement, classDescriptor,
191: new KeyProvider() {
192: public MemberKey getKey(String name, String desc) {
193: return new MemberKey(true, name, desc);
194: }
195: });
196: }
197: }
198:
199: private String findUniqueName(String prefix, String suffix) {
200: String uniqueName = prefix + suffix + "_";
201: if (isClassAvailable(uniqueName)) {
202: return uniqueName;
203: }
204: int index = suffix.indexOf('$');
205: if (index >= 0) {
206: uniqueName = prefix + replaceChar(suffix, index, "_$");
207: if (isClassAvailable(uniqueName)) {
208: return uniqueName;
209: }
210: uniqueName = prefix + replaceChar(suffix, index, "_") + "_";
211: if (isClassAvailable(uniqueName)) {
212: return uniqueName;
213: }
214: }
215: uniqueName = prefix + suffix;
216: if (isClassAvailable(uniqueName)) {
217: return uniqueName;
218: }
219: return null;
220: }
221:
222: private static String replaceChar(String s, int index,
223: String replacement) {
224: return s.substring(0, index) + replacement
225: + s.substring(index + 1);
226: }
227:
228: private void loadAllMembers(ClassReplacement replacement,
229: ClassDescriptor descriptor, KeyProvider keyProvider) {
230: String owner = descriptor.getName();
231: loadMembers(owner, descriptor.getFieldDescriptors(),
232: keyProvider, replacement.getFieldReplacements());
233: loadMembers(owner, descriptor.getMethodDescriptors(),
234: keyProvider, replacement.getMethodReplacements());
235: }
236:
237: private <T extends AnnotatedElementDescriptor> void loadMembers(
238: String owner, Collection<T> members,
239: KeyProvider keyProvider,
240: Map<MemberKey, MemberReplacement> replacements) {
241: for (T member : members) {
242: if (!member.isAccess(ACC_PUBLIC)
243: || !member.isAccess(ACC_STATIC)) {
244: continue;
245: }
246: if (!isSupportedBackport(member)) {
247: continue;
248: }
249: MemberKey key = keyProvider.getKey(member.getName(), member
250: .getDesc());
251: if (key == null || replacements.containsKey(key)) {
252: continue;
253: }
254: replacements.put(key, new MemberReplacement(owner, member
255: .getName(), member.getDesc()));
256: }
257: }
258:
259: private void completeReplacement(ClassReplacement replacement,
260: String originalName) {
261: if (replacement.getUniqueTypeName() == null) {
262: replacement.setUniqueTypeName(originalName);
263: }
264: Map<MemberKey, MemberReplacement> replacements = replacement
265: .getMethodReplacements();
266: replacement
267: .setCheckCastReplacement(findCheckCastReplacement(replacements
268: .values()));
269: replacement
270: .setInstanceOfReplacement(findInstanceOfReplacement(replacements
271: .values()));
272: replacement
273: .setReferenceTypeName(replacement
274: .getCheckCastReplacement() != null ? Type
275: .getReturnType(
276: replacement.getCheckCastReplacement()
277: .getDesc()).getInternalName()
278: : replacement.getUniqueTypeName());
279: for (Map.Entry<MemberKey, MemberReplacement> entry : new HashMap<MemberKey, MemberReplacement>(
280: replacements).entrySet()) {
281: processMethod(replacement, entry.getKey().getName(), entry
282: .getValue());
283: }
284: }
285:
286: private MemberReplacement findCheckCastReplacement(
287: Collection<MemberReplacement> replacements) {
288: for (MemberReplacement replacement : replacements) {
289: if (replacement.getName().equals(
290: "executeCheckCastInstruction")) {
291: Type[] types = Type.getArgumentTypes(replacement
292: .getDesc());
293: if (types.length == 1
294: && types[0].equals(Type.getType(Object.class))) {
295: return replacement;
296: }
297: }
298: }
299: return null;
300: }
301:
302: private MemberReplacement findInstanceOfReplacement(
303: Collection<MemberReplacement> replacements) {
304: for (MemberReplacement replacement : replacements) {
305: if (replacement.getName().equals(
306: "executeInstanceOfInstruction")
307: && replacement.getDesc().equals(
308: TransformerTools.descriptor(boolean.class,
309: Object.class))) {
310: return replacement;
311: }
312: }
313: return null;
314: }
315:
316: private void processMethod(ClassReplacement classReplacement,
317: String methodName, MemberReplacement replacement) {
318: Type[] types = Type.getArgumentTypes(replacement.getDesc());
319: if (types.length > 0
320: && types[0].equals(TransformerTools
321: .getTypeByInternalName(classReplacement
322: .getReferenceTypeName()))) {
323: String descriptor = Type.getMethodDescriptor(Type
324: .getReturnType(replacement.getDesc()),
325: deleteFirst(types));
326: MemberKey key = new MemberKey(false, methodName, descriptor);
327: putIfAbsent(classReplacement.getMethodReplacements(), key,
328: replacement);
329: }
330: if (methodName.equals("convertConstructorArguments")) {
331: putForConstructor(classReplacement
332: .getConverterReplacements(), replacement);
333: } else if (methodName.equals("createNewInstance")) {
334: putForConstructor(classReplacement
335: .getInstantiationReplacements(), replacement);
336: } else if (methodName.equals("createInstanceBuilder")) {
337: loadConstructor(classReplacement, replacement);
338: }
339: }
340:
341: private static Type[] deleteFirst(Type[] types) {
342: Type[] result = new Type[types.length - 1];
343: System.arraycopy(types, 1, result, 0, result.length);
344: return result;
345: }
346:
347: private static void putForConstructor(
348: Map<String, MemberReplacement> map,
349: MemberReplacement replacement) {
350: putIfAbsent(map, getConstructorDesc(replacement.getDesc()),
351: replacement);
352: }
353:
354: private static String getConstructorDesc(String desc) {
355: return Type.getMethodDescriptor(Type.VOID_TYPE, Type
356: .getArgumentTypes(desc));
357: }
358:
359: private void loadConstructor(ClassReplacement classReplacement,
360: MemberReplacement replacement) {
361: Map<String, ConstructorReplacement> replacements = classReplacement
362: .getConstructorReplacements();
363: String constructorDesc = getConstructorDesc(replacement
364: .getDesc());
365: if (replacements.containsKey(constructorDesc)) {
366: return;
367: }
368: String owner = Type.getReturnType(replacement.getDesc())
369: .getInternalName();
370: ClassDescriptor classDescriptor = getClassDescriptor(owner);
371: if (classDescriptor == null) {
372: throw new TypeNotPresentException(owner, null);
373: }
374: List<MemberReplacement> arguments = new ArrayList<MemberReplacement>();
375: MemberReplacement initializer = null;
376: for (MethodDescriptor descriptor : classDescriptor
377: .getMethodDescriptors()) {
378: if (isArgument(descriptor)) {
379: arguments.add(new MemberReplacement(owner, descriptor
380: .getName(), descriptor.getDesc()));
381: } else if (isInitializer(descriptor, classReplacement
382: .getReferenceTypeName())) {
383: initializer = new MemberReplacement(owner, descriptor
384: .getName(), descriptor.getDesc());
385: }
386: }
387: Collections.sort(arguments,
388: new Comparator<MemberReplacement>() {
389: public int compare(MemberReplacement o1,
390: MemberReplacement o2) {
391: return o1.getName().compareTo(o2.getName());
392: }
393: });
394: List<Type> types = new ArrayList<Type>();
395: for (MemberReplacement argument : arguments) {
396: types.add(Type.getReturnType(argument.getDesc()));
397: }
398: String newConstructorDesc = Type.getMethodDescriptor(
399: Type.VOID_TYPE, types.toArray(new Type[0]));
400: replacements.put(constructorDesc, new ConstructorReplacement(
401: replacement, arguments
402: .toArray(new MemberReplacement[0]),
403: newConstructorDesc, initializer));
404: }
405:
406: private static boolean isArgument(MethodDescriptor descriptor) {
407: if (!descriptor.getName().startsWith("argument"))
408: return false;
409: if (Type.getReturnType(descriptor.getDesc()) == Type.VOID_TYPE)
410: return false;
411: if (Type.getArgumentTypes(descriptor.getDesc()).length > 0)
412: return false;
413: return descriptor.isAccess(ACC_PUBLIC)
414: && !descriptor.isAccess(ACC_STATIC);
415: }
416:
417: private static boolean isInitializer(MethodDescriptor descriptor,
418: String referenceTypeName) {
419: if (!descriptor.getName().equals("initialize"))
420: return false;
421: if (Type.getReturnType(descriptor.getDesc()) != Type.VOID_TYPE)
422: return false;
423: Type[] types = Type.getArgumentTypes(descriptor.getDesc());
424: if (types.length != 1)
425: return false;
426: if (!types[0].getInternalName().equals(referenceTypeName))
427: return false;
428: return descriptor.isAccess(ACC_PUBLIC)
429: && !descriptor.isAccess(ACC_STATIC);
430: }
431:
432: private boolean isClassAvailable(String internalName) {
433: return getClassDescriptor(internalName) != null;
434: }
435:
436: private ClassDescriptor getClassDescriptor(String internalName) {
437: byte[] content = environment.getClassContent(internalName);
438: if (content == null) {
439: return null;
440: }
441: ClassDescriptor descriptor = new ClassDescriptor(
442: ReplacementLocator.class, content);
443: return isSupportedBackport(descriptor) ? descriptor : null;
444: }
445:
446: private boolean isSupportedBackport(
447: AnnotatedElementDescriptor descriptor) {
448: return !unsupportedBackports.contains(descriptor.getInfo());
449: }
450:
451: public String getUniqueTypeName(String className) {
452: ClassReplacement replacement = getReplacement(className);
453: return mode.fixName(replacement != null ? replacement
454: .getUniqueTypeName() : className);
455: }
456:
457: public String getReferenceTypeName(String className) {
458: ClassReplacement replacement = getReplacement(className);
459: return mode.fixName(replacement != null ? replacement
460: .getReferenceTypeName() : className);
461: }
462:
463: private static <K, V> void putIfAbsent(Map<K, V> map, K key, V value) {
464: if (!map.containsKey(key)) {
465: map.put(key, value);
466: }
467: }
468:
469: public NameTranslator getTranslator() {
470: return new NameTranslator() {
471:
472: protected String identifier(String s) {
473: return fixIdentifier(s);
474: }
475:
476: protected String typeName(String s) {
477: return getReferenceTypeName(s);
478: }
479: };
480: }
481:
482: }
|