diff options
Diffstat (limited to 'src/cuchaz/enigma/convert/ClassIdentity.java')
| -rw-r--r-- | src/cuchaz/enigma/convert/ClassIdentity.java | 436 |
1 files changed, 178 insertions, 258 deletions
diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index 1de345f..7340403 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java | |||
| @@ -57,8 +57,8 @@ import cuchaz.enigma.mapping.MethodEntry; | |||
| 57 | import cuchaz.enigma.mapping.SignatureUpdater; | 57 | import cuchaz.enigma.mapping.SignatureUpdater; |
| 58 | import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; | 58 | import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; |
| 59 | 59 | ||
| 60 | public class ClassIdentity | 60 | public class ClassIdentity { |
| 61 | { | 61 | |
| 62 | private ClassEntry m_classEntry; | 62 | private ClassEntry m_classEntry; |
| 63 | private SidedClassNamer m_namer; | 63 | private SidedClassNamer m_namer; |
| 64 | private Multiset<String> m_fields; | 64 | private Multiset<String> m_fields; |
| @@ -70,419 +70,339 @@ public class ClassIdentity | |||
| 70 | private Multiset<String> m_implementations; | 70 | private Multiset<String> m_implementations; |
| 71 | private Multiset<String> m_references; | 71 | private Multiset<String> m_references; |
| 72 | 72 | ||
| 73 | public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences ) | 73 | public ClassIdentity(CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences) { |
| 74 | { | ||
| 75 | m_namer = namer; | 74 | m_namer = namer; |
| 76 | 75 | ||
| 77 | // stuff from the bytecode | 76 | // stuff from the bytecode |
| 78 | 77 | ||
| 79 | m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); | 78 | m_classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 80 | m_fields = HashMultiset.create(); | 79 | m_fields = HashMultiset.create(); |
| 81 | for( CtField field : c.getDeclaredFields() ) | 80 | for (CtField field : c.getDeclaredFields()) { |
| 82 | { | 81 | m_fields.add(scrubSignature(field.getSignature())); |
| 83 | m_fields.add( scrubSignature( field.getSignature() ) ); | ||
| 84 | } | 82 | } |
| 85 | m_methods = HashMultiset.create(); | 83 | m_methods = HashMultiset.create(); |
| 86 | for( CtMethod method : c.getDeclaredMethods() ) | 84 | for (CtMethod method : c.getDeclaredMethods()) { |
| 87 | { | 85 | m_methods.add(scrubSignature(method.getSignature()) + "0x" + getBehaviorSignature(method)); |
| 88 | m_methods.add( scrubSignature( method.getSignature() ) + "0x" + getBehaviorSignature( method ) ); | ||
| 89 | } | 86 | } |
| 90 | m_constructors = HashMultiset.create(); | 87 | m_constructors = HashMultiset.create(); |
| 91 | for( CtConstructor constructor : c.getDeclaredConstructors() ) | 88 | for (CtConstructor constructor : c.getDeclaredConstructors()) { |
| 92 | { | 89 | m_constructors.add(scrubSignature(constructor.getSignature()) + "0x" + getBehaviorSignature(constructor)); |
| 93 | m_constructors.add( scrubSignature( constructor.getSignature() ) + "0x" + getBehaviorSignature( constructor ) ); | ||
| 94 | } | 90 | } |
| 95 | m_staticInitializer = ""; | 91 | m_staticInitializer = ""; |
| 96 | if( c.getClassInitializer() != null ) | 92 | if (c.getClassInitializer() != null) { |
| 97 | { | 93 | m_staticInitializer = getBehaviorSignature(c.getClassInitializer()); |
| 98 | m_staticInitializer = getBehaviorSignature( c.getClassInitializer() ); | ||
| 99 | } | 94 | } |
| 100 | m_extends = ""; | 95 | m_extends = ""; |
| 101 | if( c.getClassFile().getSuperclass() != null ) | 96 | if (c.getClassFile().getSuperclass() != null) { |
| 102 | { | 97 | m_extends = scrubClassName(c.getClassFile().getSuperclass()); |
| 103 | m_extends = scrubClassName( c.getClassFile().getSuperclass() ); | ||
| 104 | } | 98 | } |
| 105 | m_implements = HashMultiset.create(); | 99 | m_implements = HashMultiset.create(); |
| 106 | for( String interfaceName : c.getClassFile().getInterfaces() ) | 100 | for (String interfaceName : c.getClassFile().getInterfaces()) { |
| 107 | { | 101 | m_implements.add(scrubClassName(interfaceName)); |
| 108 | m_implements.add( scrubClassName( interfaceName ) ); | ||
| 109 | } | 102 | } |
| 110 | 103 | ||
| 111 | // stuff from the jar index | 104 | // stuff from the jar index |
| 112 | 105 | ||
| 113 | m_implementations = HashMultiset.create(); | 106 | m_implementations = HashMultiset.create(); |
| 114 | ClassImplementationsTreeNode implementationsNode = index.getClassImplementations( null, m_classEntry ); | 107 | ClassImplementationsTreeNode implementationsNode = index.getClassImplementations(null, m_classEntry); |
| 115 | if( implementationsNode != null ) | 108 | if (implementationsNode != null) { |
| 116 | { | 109 | @SuppressWarnings("unchecked") |
| 117 | @SuppressWarnings( "unchecked" ) | ||
| 118 | Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); | 110 | Enumeration<ClassImplementationsTreeNode> implementations = implementationsNode.children(); |
| 119 | while( implementations.hasMoreElements() ) | 111 | while (implementations.hasMoreElements()) { |
| 120 | { | ||
| 121 | ClassImplementationsTreeNode node = implementations.nextElement(); | 112 | ClassImplementationsTreeNode node = implementations.nextElement(); |
| 122 | m_implementations.add( scrubClassName( node.getClassEntry().getName() ) ); | 113 | m_implementations.add(scrubClassName(node.getClassEntry().getName())); |
| 123 | } | 114 | } |
| 124 | } | 115 | } |
| 125 | 116 | ||
| 126 | m_references = HashMultiset.create(); | 117 | m_references = HashMultiset.create(); |
| 127 | if( useReferences ) | 118 | if (useReferences) { |
| 128 | { | 119 | for (CtField field : c.getDeclaredFields()) { |
| 129 | for( CtField field : c.getDeclaredFields() ) | 120 | FieldEntry fieldEntry = new FieldEntry(m_classEntry, field.getName()); |
| 130 | { | 121 | for (EntryReference<FieldEntry,BehaviorEntry> reference : index.getFieldReferences(fieldEntry)) { |
| 131 | FieldEntry fieldEntry = new FieldEntry( m_classEntry, field.getName() ); | 122 | addReference(reference); |
| 132 | for( EntryReference<FieldEntry,BehaviorEntry> reference : index.getFieldReferences( fieldEntry ) ) | ||
| 133 | { | ||
| 134 | addReference( reference ); | ||
| 135 | } | 123 | } |
| 136 | } | 124 | } |
| 137 | for( CtMethod method : c.getDeclaredMethods() ) | 125 | for (CtMethod method : c.getDeclaredMethods()) { |
| 138 | { | 126 | MethodEntry methodEntry = new MethodEntry(m_classEntry, method.getName(), method.getSignature()); |
| 139 | MethodEntry methodEntry = new MethodEntry( m_classEntry, method.getName(), method.getSignature() ); | 127 | for (EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences(methodEntry)) { |
| 140 | for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( methodEntry ) ) | 128 | addReference(reference); |
| 141 | { | ||
| 142 | addReference( reference ); | ||
| 143 | } | 129 | } |
| 144 | } | 130 | } |
| 145 | for( CtConstructor constructor : c.getDeclaredConstructors() ) | 131 | for (CtConstructor constructor : c.getDeclaredConstructors()) { |
| 146 | { | 132 | ConstructorEntry constructorEntry = new ConstructorEntry(m_classEntry, constructor.getSignature()); |
| 147 | ConstructorEntry constructorEntry = new ConstructorEntry( m_classEntry, constructor.getSignature() ); | 133 | for (EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences(constructorEntry)) { |
| 148 | for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( constructorEntry ) ) | 134 | addReference(reference); |
| 149 | { | ||
| 150 | addReference( reference ); | ||
| 151 | } | 135 | } |
| 152 | } | 136 | } |
| 153 | } | 137 | } |
| 154 | } | 138 | } |
| 155 | 139 | ||
| 156 | private void addReference( EntryReference<? extends Entry,BehaviorEntry> reference ) | 140 | private void addReference(EntryReference<? extends Entry,BehaviorEntry> reference) { |
| 157 | { | 141 | if (reference.context.getSignature() != null) { |
| 158 | if( reference.context.getSignature() != null ) | 142 | m_references.add(String.format("%s_%s", scrubClassName(reference.context.getClassName()), scrubSignature(reference.context.getSignature()))); |
| 159 | { | 143 | } else { |
| 160 | m_references.add( String.format( "%s_%s", | 144 | m_references.add(String.format("%s_<clinit>", scrubClassName(reference.context.getClassName()))); |
| 161 | scrubClassName( reference.context.getClassName() ), | ||
| 162 | scrubSignature( reference.context.getSignature() ) | ||
| 163 | ) ); | ||
| 164 | } | ||
| 165 | else | ||
| 166 | { | ||
| 167 | m_references.add( String.format( "%s_<clinit>", | ||
| 168 | scrubClassName( reference.context.getClassName() ) | ||
| 169 | ) ); | ||
| 170 | } | 145 | } |
| 171 | } | 146 | } |
| 172 | 147 | ||
| 173 | public ClassEntry getClassEntry( ) | 148 | public ClassEntry getClassEntry() { |
| 174 | { | ||
| 175 | return m_classEntry; | 149 | return m_classEntry; |
| 176 | } | 150 | } |
| 177 | 151 | ||
| 178 | @Override | 152 | @Override |
| 179 | public String toString( ) | 153 | public String toString() { |
| 180 | { | ||
| 181 | StringBuilder buf = new StringBuilder(); | 154 | StringBuilder buf = new StringBuilder(); |
| 182 | buf.append( "class: " ); | 155 | buf.append("class: "); |
| 183 | buf.append( m_classEntry.getName() ); | 156 | buf.append(m_classEntry.getName()); |
| 184 | buf.append( " " ); | 157 | buf.append(" "); |
| 185 | buf.append( hashCode() ); | 158 | buf.append(hashCode()); |
| 186 | buf.append( "\n" ); | 159 | buf.append("\n"); |
| 187 | for( String field : m_fields ) | 160 | for (String field : m_fields) { |
| 188 | { | 161 | buf.append("\tfield "); |
| 189 | buf.append( "\tfield " ); | 162 | buf.append(field); |
| 190 | buf.append( field ); | 163 | buf.append("\n"); |
| 191 | buf.append( "\n" ); | ||
| 192 | } | 164 | } |
| 193 | for( String method : m_methods ) | 165 | for (String method : m_methods) { |
| 194 | { | 166 | buf.append("\tmethod "); |
| 195 | buf.append( "\tmethod " ); | 167 | buf.append(method); |
| 196 | buf.append( method ); | 168 | buf.append("\n"); |
| 197 | buf.append( "\n" ); | ||
| 198 | } | 169 | } |
| 199 | for( String constructor : m_constructors ) | 170 | for (String constructor : m_constructors) { |
| 200 | { | 171 | buf.append("\tconstructor "); |
| 201 | buf.append( "\tconstructor " ); | 172 | buf.append(constructor); |
| 202 | buf.append( constructor ); | 173 | buf.append("\n"); |
| 203 | buf.append( "\n" ); | ||
| 204 | } | 174 | } |
| 205 | if( m_staticInitializer.length() > 0 ) | 175 | if (m_staticInitializer.length() > 0) { |
| 206 | { | 176 | buf.append("\tinitializer "); |
| 207 | buf.append( "\tinitializer " ); | 177 | buf.append(m_staticInitializer); |
| 208 | buf.append( m_staticInitializer ); | 178 | buf.append("\n"); |
| 209 | buf.append( "\n" ); | ||
| 210 | } | 179 | } |
| 211 | if( m_extends.length() > 0 ) | 180 | if (m_extends.length() > 0) { |
| 212 | { | 181 | buf.append("\textends "); |
| 213 | buf.append( "\textends " ); | 182 | buf.append(m_extends); |
| 214 | buf.append( m_extends ); | 183 | buf.append("\n"); |
| 215 | buf.append( "\n" ); | ||
| 216 | } | 184 | } |
| 217 | for( String interfaceName : m_implements ) | 185 | for (String interfaceName : m_implements) { |
| 218 | { | 186 | buf.append("\timplements "); |
| 219 | buf.append( "\timplements " ); | 187 | buf.append(interfaceName); |
| 220 | buf.append( interfaceName ); | 188 | buf.append("\n"); |
| 221 | buf.append( "\n" ); | ||
| 222 | } | 189 | } |
| 223 | for( String implementation : m_implementations ) | 190 | for (String implementation : m_implementations) { |
| 224 | { | 191 | buf.append("\timplemented by "); |
| 225 | buf.append( "\timplemented by " ); | 192 | buf.append(implementation); |
| 226 | buf.append( implementation ); | 193 | buf.append("\n"); |
| 227 | buf.append( "\n" ); | ||
| 228 | } | 194 | } |
| 229 | for( String reference : m_references ) | 195 | for (String reference : m_references) { |
| 230 | { | 196 | buf.append("\treference "); |
| 231 | buf.append( "\treference " ); | 197 | buf.append(reference); |
| 232 | buf.append( reference ); | 198 | buf.append("\n"); |
| 233 | buf.append( "\n" ); | ||
| 234 | } | 199 | } |
| 235 | return buf.toString(); | 200 | return buf.toString(); |
| 236 | } | 201 | } |
| 237 | 202 | ||
| 238 | private String scrubClassName( String className ) | 203 | private String scrubClassName(String className) { |
| 239 | { | 204 | return scrubSignature("L" + Descriptor.toJvmName(className) + ";"); |
| 240 | return scrubSignature( "L" + Descriptor.toJvmName( className ) + ";" ); | ||
| 241 | } | 205 | } |
| 242 | 206 | ||
| 243 | private String scrubSignature( String signature ) | 207 | private String scrubSignature(String signature) { |
| 244 | { | 208 | return SignatureUpdater.update(signature, new ClassNameUpdater() { |
| 245 | return SignatureUpdater.update( signature, new ClassNameUpdater( ) | ||
| 246 | { | ||
| 247 | private Map<String,String> m_classNames = Maps.newHashMap(); | 209 | private Map<String,String> m_classNames = Maps.newHashMap(); |
| 248 | 210 | ||
| 249 | @Override | 211 | @Override |
| 250 | public String update( String className ) | 212 | public String update(String className) { |
| 251 | { | ||
| 252 | // classes not in the none package can be passed through | 213 | // classes not in the none package can be passed through |
| 253 | ClassEntry classEntry = new ClassEntry( className ); | 214 | ClassEntry classEntry = new ClassEntry(className); |
| 254 | if( !classEntry.getPackageName().equals( Constants.NonePackage ) ) | 215 | if (!classEntry.getPackageName().equals(Constants.NonePackage)) { |
| 255 | { | ||
| 256 | return className; | 216 | return className; |
| 257 | } | 217 | } |
| 258 | 218 | ||
| 259 | // is this class ourself? | 219 | // is this class ourself? |
| 260 | if( className.equals( m_classEntry.getName() ) ) | 220 | if (className.equals(m_classEntry.getName())) { |
| 261 | { | ||
| 262 | return "CSelf"; | 221 | return "CSelf"; |
| 263 | } | 222 | } |
| 264 | 223 | ||
| 265 | // try the namer | 224 | // try the namer |
| 266 | if( m_namer != null ) | 225 | if (m_namer != null) { |
| 267 | { | 226 | String newName = m_namer.getName(className); |
| 268 | String newName = m_namer.getName( className ); | 227 | if (newName != null) { |
| 269 | if( newName != null ) | ||
| 270 | { | ||
| 271 | return newName; | 228 | return newName; |
| 272 | } | 229 | } |
| 273 | } | 230 | } |
| 274 | 231 | ||
| 275 | // otherwise, use local naming | 232 | // otherwise, use local naming |
| 276 | if( !m_classNames.containsKey( className ) ) | 233 | if (!m_classNames.containsKey(className)) { |
| 277 | { | 234 | m_classNames.put(className, getNewClassName()); |
| 278 | m_classNames.put( className, getNewClassName() ); | ||
| 279 | } | 235 | } |
| 280 | return m_classNames.get( className ); | 236 | return m_classNames.get(className); |
| 281 | } | 237 | } |
| 282 | 238 | ||
| 283 | private String getNewClassName( ) | 239 | private String getNewClassName() { |
| 284 | { | 240 | return String.format("C%03d", m_classNames.size()); |
| 285 | return String.format( "C%03d", m_classNames.size() ); | ||
| 286 | } | 241 | } |
| 287 | } ); | 242 | }); |
| 288 | } | 243 | } |
| 289 | 244 | ||
| 290 | private boolean isClassMatchedUniquely( String className ) | 245 | private boolean isClassMatchedUniquely(String className) { |
| 291 | { | 246 | return m_namer != null && m_namer.getName(Descriptor.toJvmName(className)) != null; |
| 292 | return m_namer != null && m_namer.getName( Descriptor.toJvmName( className ) ) != null; | ||
| 293 | } | 247 | } |
| 294 | 248 | ||
| 295 | private String getBehaviorSignature( CtBehavior behavior ) | 249 | private String getBehaviorSignature(CtBehavior behavior) { |
| 296 | { | 250 | try { |
| 297 | try | ||
| 298 | { | ||
| 299 | // does this method have an implementation? | 251 | // does this method have an implementation? |
| 300 | if( behavior.getMethodInfo().getCodeAttribute() == null ) | 252 | if (behavior.getMethodInfo().getCodeAttribute() == null) { |
| 301 | { | ||
| 302 | return "(none)"; | 253 | return "(none)"; |
| 303 | } | 254 | } |
| 304 | 255 | ||
| 305 | // compute the hash from the opcodes | 256 | // compute the hash from the opcodes |
| 306 | ConstPool constants = behavior.getMethodInfo().getConstPool(); | 257 | ConstPool constants = behavior.getMethodInfo().getConstPool(); |
| 307 | final MessageDigest digest = MessageDigest.getInstance( "MD5" ); | 258 | final MessageDigest digest = MessageDigest.getInstance("MD5"); |
| 308 | CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); | 259 | CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); |
| 309 | while( iter.hasNext() ) | 260 | while (iter.hasNext()) { |
| 310 | { | ||
| 311 | int pos = iter.next(); | 261 | int pos = iter.next(); |
| 312 | 262 | ||
| 313 | // update the hash with the opcode | 263 | // update the hash with the opcode |
| 314 | int opcode = iter.byteAt( pos ); | 264 | int opcode = iter.byteAt(pos); |
| 315 | digest.update( (byte)opcode ); | 265 | digest.update((byte)opcode); |
| 316 | 266 | ||
| 317 | switch( opcode ) | 267 | switch (opcode) { |
| 318 | { | 268 | case Opcode.LDC: { |
| 319 | case Opcode.LDC: | 269 | int constIndex = iter.byteAt(pos + 1); |
| 320 | { | 270 | updateHashWithConstant(digest, constants, constIndex); |
| 321 | int constIndex = iter.byteAt( pos + 1 ); | ||
| 322 | updateHashWithConstant( digest, constants, constIndex ); | ||
| 323 | } | 271 | } |
| 324 | break; | 272 | break; |
| 325 | 273 | ||
| 326 | case Opcode.LDC_W: | 274 | case Opcode.LDC_W: |
| 327 | case Opcode.LDC2_W: | 275 | case Opcode.LDC2_W: { |
| 328 | { | 276 | int constIndex = (iter.byteAt(pos + 1) << 8) | iter.byteAt(pos + 2); |
| 329 | int constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); | 277 | updateHashWithConstant(digest, constants, constIndex); |
| 330 | updateHashWithConstant( digest, constants, constIndex ); | ||
| 331 | } | 278 | } |
| 332 | break; | 279 | break; |
| 333 | } | 280 | } |
| 334 | } | 281 | } |
| 335 | 282 | ||
| 336 | // update hash with method and field accesses | 283 | // update hash with method and field accesses |
| 337 | behavior.instrument( new ExprEditor( ) | 284 | behavior.instrument(new ExprEditor() { |
| 338 | { | ||
| 339 | @Override | 285 | @Override |
| 340 | public void edit( MethodCall call ) | 286 | public void edit(MethodCall call) { |
| 341 | { | 287 | updateHashWithString(digest, scrubClassName(call.getClassName())); |
| 342 | updateHashWithString( digest, scrubClassName( call.getClassName() ) ); | 288 | updateHashWithString(digest, scrubSignature(call.getSignature())); |
| 343 | updateHashWithString( digest, scrubSignature( call.getSignature() ) ); | 289 | if (isClassMatchedUniquely(call.getClassName())) { |
| 344 | if( isClassMatchedUniquely( call.getClassName() ) ) | 290 | updateHashWithString(digest, call.getMethodName()); |
| 345 | { | ||
| 346 | updateHashWithString( digest, call.getMethodName() ); | ||
| 347 | } | 291 | } |
| 348 | } | 292 | } |
| 349 | 293 | ||
| 350 | @Override | 294 | @Override |
| 351 | public void edit( FieldAccess access ) | 295 | public void edit(FieldAccess access) { |
| 352 | { | 296 | updateHashWithString(digest, scrubClassName(access.getClassName())); |
| 353 | updateHashWithString( digest, scrubClassName( access.getClassName() ) ); | 297 | updateHashWithString(digest, scrubSignature(access.getSignature())); |
| 354 | updateHashWithString( digest, scrubSignature( access.getSignature() ) ); | 298 | if (isClassMatchedUniquely(access.getClassName())) { |
| 355 | if( isClassMatchedUniquely( access.getClassName() ) ) | 299 | updateHashWithString(digest, access.getFieldName()); |
| 356 | { | ||
| 357 | updateHashWithString( digest, access.getFieldName() ); | ||
| 358 | } | 300 | } |
| 359 | } | 301 | } |
| 360 | 302 | ||
| 361 | @Override | 303 | @Override |
| 362 | public void edit( ConstructorCall call ) | 304 | public void edit(ConstructorCall call) { |
| 363 | { | 305 | updateHashWithString(digest, scrubClassName(call.getClassName())); |
| 364 | updateHashWithString( digest, scrubClassName( call.getClassName() ) ); | 306 | updateHashWithString(digest, scrubSignature(call.getSignature())); |
| 365 | updateHashWithString( digest, scrubSignature( call.getSignature() ) ); | ||
| 366 | } | 307 | } |
| 367 | 308 | ||
| 368 | @Override | 309 | @Override |
| 369 | public void edit( NewExpr expr ) | 310 | public void edit(NewExpr expr) { |
| 370 | { | 311 | updateHashWithString(digest, scrubClassName(expr.getClassName())); |
| 371 | updateHashWithString( digest, scrubClassName( expr.getClassName() ) ); | ||
| 372 | } | 312 | } |
| 373 | } ); | 313 | }); |
| 374 | 314 | ||
| 375 | // convert the hash to a hex string | 315 | // convert the hash to a hex string |
| 376 | return toHex( digest.digest() ); | 316 | return toHex(digest.digest()); |
| 377 | } | 317 | } catch (BadBytecode | NoSuchAlgorithmException | CannotCompileException ex) { |
| 378 | catch( BadBytecode | NoSuchAlgorithmException | CannotCompileException ex ) | 318 | throw new Error(ex); |
| 379 | { | ||
| 380 | throw new Error( ex ); | ||
| 381 | } | 319 | } |
| 382 | } | 320 | } |
| 383 | 321 | ||
| 384 | private void updateHashWithConstant( MessageDigest digest, ConstPool constants, int index ) | 322 | private void updateHashWithConstant(MessageDigest digest, ConstPool constants, int index) { |
| 385 | { | 323 | ConstPoolEditor editor = new ConstPoolEditor(constants); |
| 386 | ConstPoolEditor editor = new ConstPoolEditor( constants ); | 324 | ConstInfoAccessor item = editor.getItem(index); |
| 387 | ConstInfoAccessor item = editor.getItem( index ); | 325 | if (item.getType() == InfoType.StringInfo) { |
| 388 | if( item.getType() == InfoType.StringInfo ) | 326 | updateHashWithString(digest, constants.getStringInfo(index)); |
| 389 | { | ||
| 390 | updateHashWithString( digest, constants.getStringInfo( index ) ); | ||
| 391 | } | 327 | } |
| 392 | // TODO: other constants | 328 | // TODO: other constants |
| 393 | } | 329 | } |
| 394 | 330 | ||
| 395 | private void updateHashWithString( MessageDigest digest, String val ) | 331 | private void updateHashWithString(MessageDigest digest, String val) { |
| 396 | { | 332 | try { |
| 397 | try | 333 | digest.update(val.getBytes("UTF8")); |
| 398 | { | 334 | } catch (UnsupportedEncodingException ex) { |
| 399 | digest.update( val.getBytes( "UTF8" ) ); | 335 | throw new Error(ex); |
| 400 | } | ||
| 401 | catch( UnsupportedEncodingException ex ) | ||
| 402 | { | ||
| 403 | throw new Error( ex ); | ||
| 404 | } | 336 | } |
| 405 | } | 337 | } |
| 406 | 338 | ||
| 407 | private String toHex( byte[] bytes ) | 339 | private String toHex(byte[] bytes) { |
| 408 | { | ||
| 409 | // function taken from: | 340 | // function taken from: |
| 410 | // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java | 341 | // http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java |
| 411 | final char[] hexArray = "0123456789ABCDEF".toCharArray(); | 342 | final char[] hexArray = "0123456789ABCDEF".toCharArray(); |
| 412 | char[] hexChars = new char[bytes.length * 2]; | 343 | char[] hexChars = new char[bytes.length * 2]; |
| 413 | for( int j = 0; j < bytes.length; j++ ) | 344 | for (int j = 0; j < bytes.length; j++) { |
| 414 | { | ||
| 415 | int v = bytes[j] & 0xFF; | 345 | int v = bytes[j] & 0xFF; |
| 416 | hexChars[j * 2] = hexArray[v >>> 4]; | 346 | hexChars[j * 2] = hexArray[v >>> 4]; |
| 417 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; | 347 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; |
| 418 | } | 348 | } |
| 419 | return new String( hexChars ); | 349 | return new String(hexChars); |
| 420 | } | 350 | } |
| 421 | 351 | ||
| 422 | @Override | 352 | @Override |
| 423 | public boolean equals( Object other ) | 353 | public boolean equals(Object other) { |
| 424 | { | 354 | if (other instanceof ClassIdentity) { |
| 425 | if( other instanceof ClassIdentity ) | 355 | return equals((ClassIdentity)other); |
| 426 | { | ||
| 427 | return equals( (ClassIdentity)other ); | ||
| 428 | } | 356 | } |
| 429 | return false; | 357 | return false; |
| 430 | } | 358 | } |
| 431 | 359 | ||
| 432 | public boolean equals( ClassIdentity other ) | 360 | public boolean equals(ClassIdentity other) { |
| 433 | { | 361 | return m_fields.equals(other.m_fields) |
| 434 | return m_fields.equals( other.m_fields ) | 362 | && m_methods.equals(other.m_methods) |
| 435 | && m_methods.equals( other.m_methods ) | 363 | && m_constructors.equals(other.m_constructors) |
| 436 | && m_constructors.equals( other.m_constructors ) | 364 | && m_staticInitializer.equals(other.m_staticInitializer) |
| 437 | && m_staticInitializer.equals( other.m_staticInitializer ) | 365 | && m_extends.equals(other.m_extends) |
| 438 | && m_extends.equals( other.m_extends ) | 366 | && m_implements.equals(other.m_implements) |
| 439 | && m_implements.equals( other.m_implements ) | 367 | && m_implementations.equals(other.m_implementations) |
| 440 | && m_implementations.equals( other.m_implementations ) | 368 | && m_references.equals(other.m_references); |
| 441 | && m_references.equals( other.m_references ); | ||
| 442 | } | 369 | } |
| 443 | 370 | ||
| 444 | @Override | 371 | @Override |
| 445 | public int hashCode( ) | 372 | public int hashCode() { |
| 446 | { | ||
| 447 | List<Object> objs = Lists.newArrayList(); | 373 | List<Object> objs = Lists.newArrayList(); |
| 448 | objs.addAll( m_fields ); | 374 | objs.addAll(m_fields); |
| 449 | objs.addAll( m_methods ); | 375 | objs.addAll(m_methods); |
| 450 | objs.addAll( m_constructors ); | 376 | objs.addAll(m_constructors); |
| 451 | objs.add( m_staticInitializer ); | 377 | objs.add(m_staticInitializer); |
| 452 | objs.add( m_extends ); | 378 | objs.add(m_extends); |
| 453 | objs.addAll( m_implements ); | 379 | objs.addAll(m_implements); |
| 454 | objs.addAll( m_implementations ); | 380 | objs.addAll(m_implementations); |
| 455 | objs.addAll( m_references ); | 381 | objs.addAll(m_references); |
| 456 | return Util.combineHashesOrdered( objs ); | 382 | return Util.combineHashesOrdered(objs); |
| 457 | } | 383 | } |
| 458 | 384 | ||
| 459 | public int getMatchScore( ClassIdentity other ) | 385 | public int getMatchScore(ClassIdentity other) { |
| 460 | { | 386 | return getNumMatches(m_fields, other.m_fields) |
| 461 | return getNumMatches( m_fields, other.m_fields ) | 387 | + getNumMatches(m_methods, other.m_methods) |
| 462 | + getNumMatches( m_methods, other.m_methods ) | 388 | + getNumMatches(m_constructors, other.m_constructors); |
| 463 | + getNumMatches( m_constructors, other.m_constructors ); | ||
| 464 | } | 389 | } |
| 465 | 390 | ||
| 466 | public int getMaxMatchScore( ) | 391 | public int getMaxMatchScore() { |
| 467 | { | ||
| 468 | return m_fields.size() + m_methods.size() + m_constructors.size(); | 392 | return m_fields.size() + m_methods.size() + m_constructors.size(); |
| 469 | } | 393 | } |
| 470 | 394 | ||
| 471 | public boolean matches( CtClass c ) | 395 | public boolean matches(CtClass c) { |
| 472 | { | ||
| 473 | // just compare declaration counts | 396 | // just compare declaration counts |
| 474 | return m_fields.size() == c.getDeclaredFields().length | 397 | return m_fields.size() == c.getDeclaredFields().length |
| 475 | && m_methods.size() == c.getDeclaredMethods().length | 398 | && m_methods.size() == c.getDeclaredMethods().length |
| 476 | && m_constructors.size() == c.getDeclaredConstructors().length; | 399 | && m_constructors.size() == c.getDeclaredConstructors().length; |
| 477 | } | 400 | } |
| 478 | 401 | ||
| 479 | private int getNumMatches( Multiset<String> a, Multiset<String> b ) | 402 | private int getNumMatches(Multiset<String> a, Multiset<String> b) { |
| 480 | { | ||
| 481 | int numMatches = 0; | 403 | int numMatches = 0; |
| 482 | for( String val : a ) | 404 | for (String val : a) { |
| 483 | { | 405 | if (b.contains(val)) { |
| 484 | if( b.contains( val ) ) | ||
| 485 | { | ||
| 486 | numMatches++; | 406 | numMatches++; |
| 487 | } | 407 | } |
| 488 | } | 408 | } |