1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx; 18 19 import com.android.dex.DexFormat; 20 import com.android.dx.dex.DexOptions; 21 import com.android.dx.dex.code.DalvCode; 22 import com.android.dx.dex.code.PositionList; 23 import com.android.dx.dex.code.RopTranslator; 24 import com.android.dx.dex.file.ClassDefItem; 25 import com.android.dx.dex.file.DexFile; 26 import com.android.dx.dex.file.EncodedField; 27 import com.android.dx.dex.file.EncodedMethod; 28 import com.android.dx.rop.code.AccessFlags; 29 import com.android.dx.rop.code.LocalVariableInfo; 30 import com.android.dx.rop.code.RopMethod; 31 import com.android.dx.rop.cst.CstString; 32 import com.android.dx.rop.cst.CstType; 33 import com.android.dx.rop.type.StdTypeList; 34 35 import java.io.File; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.lang.reflect.InvocationTargetException; 39 import java.lang.reflect.Modifier; 40 import java.util.Arrays; 41 import java.util.Iterator; 42 import java.util.LinkedHashMap; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.jar.JarEntry; 46 import java.util.jar.JarOutputStream; 47 48 import static com.android.dx.rop.code.AccessFlags.ACC_CONSTRUCTOR; 49 import static java.lang.reflect.Modifier.PRIVATE; 50 import static java.lang.reflect.Modifier.STATIC; 51 52 import android.util.Log; 53 54 /** 55 * Generates a <strong>D</strong>alvik <strong>EX</strong>ecutable (dex) 56 * file for execution on Android. Dex files define classes and interfaces, 57 * including their member methods and fields, executable code, and debugging 58 * information. They also define annotations, though this API currently has no 59 * facility to create a dex file that contains annotations. 60 * 61 * <p>This library is intended to satisfy two use cases: 62 * <ul> 63 * <li><strong>For runtime code generation.</strong> By embedding this library 64 * in your Android application, you can dynamically generate and load 65 * executable code. This approach takes advantage of the fact that the 66 * host environment and target environment are both Android. 67 * <li><strong>For compile time code generation.</strong> You may use this 68 * library as a part of a compiler that targets Android. In this scenario 69 * the generated dex file must be installed on an Android device before it 70 * can be executed. 71 * </ul> 72 * 73 * <h3>Example: Fibonacci</h3> 74 * To illustrate how this API is used, we'll use DexMaker to generate a class 75 * equivalent to the following Java source: <pre> {@code 76 * 77 * package com.publicobject.fib; 78 * 79 * public class Fibonacci { 80 * public static int fib(int i) { 81 * if (i < 2) { 82 * return i; 83 * } 84 * return fib(i - 1) + fib(i - 2); 85 * } 86 * }}</pre> 87 * 88 * <p>We start by creating a {@link TypeId} to identify the generated {@code 89 * Fibonacci} class. DexMaker identifies types by their internal names like 90 * {@code Ljava/lang/Object;} rather than their Java identifiers like {@code 91 * java.lang.Object}. <pre> {@code 92 * 93 * TypeId<?> fibonacci = TypeId.get("Lcom/google/dexmaker/examples/Fibonacci;"); 94 * }</pre> 95 * 96 * <p>Next we declare the class. It allows us to specify the type's source file 97 * for stack traces, its modifiers, its superclass, and the interfaces it 98 * implements. In this case, {@code Fibonacci} is a public class that extends 99 * from {@code Object}: <pre> {@code 100 * 101 * String fileName = "Fibonacci.generated"; 102 * DexMaker dexMaker = new DexMaker(); 103 * dexMaker.declare(fibonacci, fileName, Modifier.PUBLIC, TypeId.OBJECT); 104 * }</pre> 105 * It is illegal to declare members of a class without also declaring the class 106 * itself. 107 * 108 * <p>To make it easier to go from our Java method to dex instructions, we'll 109 * manually translate it to pseudocode fit for an assembler. We need to replace 110 * control flow like {@code if()} blocks and {@code for()} loops with labels and 111 * branches. We'll also avoid performing multiple operations in one statement, 112 * using local variables to hold intermediate values as necessary: 113 * <pre> {@code 114 * 115 * int constant1 = 1; 116 * int constant2 = 2; 117 * if (i < constant2) goto baseCase; 118 * int a = i - constant1; 119 * int b = i - constant2; 120 * int c = fib(a); 121 * int d = fib(b); 122 * int result = c + d; 123 * return result; 124 * baseCase: 125 * return i; 126 * }</pre> 127 * 128 * <p>We look up the {@code MethodId} for the method on the declaring type. This 129 * takes the method's return type (possibly {@link TypeId#VOID}), its name and 130 * its parameters types. Next we declare the method, specifying its modifiers by 131 * bitwise ORing constants from {@link java.lang.reflect.Modifier}. The declare 132 * call returns a {@link Code} object, which we'll use to define the method's 133 * instructions. <pre> {@code 134 * 135 * MethodId<?, Integer> fib = fibonacci.getMethod(TypeId.INT, "fib", TypeId.INT); 136 * Code code = dexMaker.declare(fib, Modifier.PUBLIC | Modifier.STATIC); 137 * }</pre> 138 * 139 * <p>One limitation of {@code DexMaker}'s API is that it requires all local 140 * variables to be created before any instructions are emitted. Use {@link 141 * Code#newLocal newLocal()} to create a new local variable. The method's 142 * parameters are exposed as locals using {@link Code#getParameter 143 * getParameter()}. For non-static methods the {@code this} pointer is exposed 144 * using {@link Code#getThis getThis()}. Here we declare all of the local 145 * variables that we'll need for our {@code fib()} method: <pre> {@code 146 * 147 * Local<Integer> i = code.getParameter(0, TypeId.INT); 148 * Local<Integer> constant1 = code.newLocal(TypeId.INT); 149 * Local<Integer> constant2 = code.newLocal(TypeId.INT); 150 * Local<Integer> a = code.newLocal(TypeId.INT); 151 * Local<Integer> b = code.newLocal(TypeId.INT); 152 * Local<Integer> c = code.newLocal(TypeId.INT); 153 * Local<Integer> d = code.newLocal(TypeId.INT); 154 * Local<Integer> result = code.newLocal(TypeId.INT); 155 * }</pre> 156 * 157 * <p>Notice that {@link Local} has a type parameter of {@code Integer}. This is 158 * useful for generating code that works with existing types like {@code String} 159 * and {@code Integer}, but it can be a hindrance when generating code that 160 * involves new types. For this reason you may prefer to use raw types only and 161 * add {@code @SuppressWarnings("unsafe")} on your calling code. This will yield 162 * the same result but you won't get IDE support if you make a type error. 163 * 164 * <p>We're ready to start defining our method's instructions. The {@link Code} 165 * class catalogs the available instructions and their use. <pre> {@code 166 * 167 * code.loadConstant(constant1, 1); 168 * code.loadConstant(constant2, 2); 169 * Label baseCase = new Label(); 170 * code.compare(Comparison.LT, baseCase, i, constant2); 171 * code.op(BinaryOp.SUBTRACT, a, i, constant1); 172 * code.op(BinaryOp.SUBTRACT, b, i, constant2); 173 * code.invokeStatic(fib, c, a); 174 * code.invokeStatic(fib, d, b); 175 * code.op(BinaryOp.ADD, result, c, d); 176 * code.returnValue(result); 177 * code.mark(baseCase); 178 * code.returnValue(i); 179 * }</pre> 180 * 181 * <p>We're done defining the dex file. We just need to write it to the 182 * filesystem or load it into the current process. For this example we'll load 183 * the generated code into the current process. This only works when the current 184 * process is running on Android. We use {@link #generateAndLoad 185 * generateAndLoad()} which takes the class loader that will be used as our 186 * generated code's parent class loader. It also requires a directory where 187 * temporary files can be written. <pre> {@code 188 * 189 * ClassLoader loader = dexMaker.generateAndLoad( 190 * FibonacciMaker.class.getClassLoader(), getDataDirectory()); 191 * }</pre> 192 * Finally we'll use reflection to lookup our generated class on its class 193 * loader and invoke its {@code fib()} method: <pre> {@code 194 * 195 * Class<?> fibonacciClass = loader.loadClass("com.google.dexmaker.examples.Fibonacci"); 196 * Method fibMethod = fibonacciClass.getMethod("fib", int.class); 197 * System.out.println(fibMethod.invoke(null, 8)); 198 * }</pre> 199 */ 200 public final class DexMaker { 201 private static final String LOG_TAG = DexMaker.class.getSimpleName(); 202 203 private final Map<TypeId<?>, TypeDeclaration> types = new LinkedHashMap<>(); 204 private ClassLoader sharedClassLoader; 205 private DexFile outputDex; 206 private boolean markAsTrusted; 207 208 /** 209 * Creates a new {@code DexMaker} instance, which can be used to create a 210 * single dex file. 211 */ DexMaker()212 public DexMaker() { 213 } 214 getTypeDeclaration(TypeId<?> type)215 TypeDeclaration getTypeDeclaration(TypeId<?> type) { 216 TypeDeclaration result = types.get(type); 217 if (result == null) { 218 result = new TypeDeclaration(type); 219 types.put(type, result); 220 } 221 return result; 222 } 223 224 /** 225 * Declares {@code type}. 226 * 227 * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link 228 * Modifier#FINAL} and {@link Modifier#ABSTRACT}. 229 */ declare(TypeId<?> type, String sourceFile, int flags, TypeId<?> supertype, TypeId<?>... interfaces)230 public void declare(TypeId<?> type, String sourceFile, int flags, 231 TypeId<?> supertype, TypeId<?>... interfaces) { 232 TypeDeclaration declaration = getTypeDeclaration(type); 233 int supportedFlags = Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT; 234 if ((flags & ~supportedFlags) != 0) { 235 throw new IllegalArgumentException("Unexpected flag: " 236 + Integer.toHexString(flags)); 237 } 238 if (declaration.declared) { 239 throw new IllegalStateException("already declared: " + type); 240 } 241 declaration.declared = true; 242 declaration.flags = flags; 243 declaration.supertype = supertype; 244 declaration.sourceFile = sourceFile; 245 declaration.interfaces = new TypeList(interfaces); 246 } 247 248 /** 249 * Declares a method or constructor. 250 * 251 * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link 252 * Modifier#PRIVATE}, {@link Modifier#PROTECTED}, {@link Modifier#STATIC}, 253 * {@link Modifier#FINAL} and {@link Modifier#SYNCHRONIZED}. 254 * <p><strong>Warning:</strong> the {@link Modifier#SYNCHRONIZED} flag 255 * is insufficient to generate a synchronized method. You must also use 256 * {@link Code#monitorEnter} and {@link Code#monitorExit} to acquire 257 * a monitor. 258 */ declare(MethodId<?, ?> method, int flags)259 public Code declare(MethodId<?, ?> method, int flags) { 260 TypeDeclaration typeDeclaration = getTypeDeclaration(method.declaringType); 261 if (typeDeclaration.methods.containsKey(method)) { 262 throw new IllegalStateException("already declared: " + method); 263 } 264 265 int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED 266 | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED; 267 if ((flags & ~supportedFlags) != 0) { 268 throw new IllegalArgumentException("Unexpected flag: " 269 + Integer.toHexString(flags)); 270 } 271 272 // replace the SYNCHRONIZED flag with the DECLARED_SYNCHRONIZED flag 273 if ((flags & Modifier.SYNCHRONIZED) != 0) { 274 flags = (flags & ~Modifier.SYNCHRONIZED) | AccessFlags.ACC_DECLARED_SYNCHRONIZED; 275 } 276 277 if (method.isConstructor() || method.isStaticInitializer()) { 278 flags |= ACC_CONSTRUCTOR; 279 } 280 281 MethodDeclaration methodDeclaration = new MethodDeclaration(method, flags); 282 typeDeclaration.methods.put(method, methodDeclaration); 283 return methodDeclaration.code; 284 } 285 286 /** 287 * Declares a field. 288 * 289 * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link 290 * Modifier#PRIVATE}, {@link Modifier#PROTECTED}, {@link Modifier#STATIC}, 291 * {@link Modifier#FINAL}, {@link Modifier#VOLATILE}, and {@link 292 * Modifier#TRANSIENT}. 293 * @param staticValue a constant representing the initial value for the 294 * static field, possibly null. This must be null if this field is 295 * non-static. 296 */ declare(FieldId<?, ?> fieldId, int flags, Object staticValue)297 public void declare(FieldId<?, ?> fieldId, int flags, Object staticValue) { 298 TypeDeclaration typeDeclaration = getTypeDeclaration(fieldId.declaringType); 299 if (typeDeclaration.fields.containsKey(fieldId)) { 300 throw new IllegalStateException("already declared: " + fieldId); 301 } 302 303 int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED 304 | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE | Modifier.TRANSIENT; 305 if ((flags & ~supportedFlags) != 0) { 306 throw new IllegalArgumentException("Unexpected flag: " 307 + Integer.toHexString(flags)); 308 } 309 310 if ((flags & Modifier.STATIC) == 0 && staticValue != null) { 311 throw new IllegalArgumentException("staticValue is non-null, but field is not static"); 312 } 313 314 FieldDeclaration fieldDeclaration = new FieldDeclaration(fieldId, flags, staticValue); 315 typeDeclaration.fields.put(fieldId, fieldDeclaration); 316 } 317 318 /** 319 * Generates a dex file and returns its bytes. 320 */ generate()321 public byte[] generate() { 322 if (outputDex == null) { 323 DexOptions options = new DexOptions(); 324 options.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; 325 outputDex = new DexFile(options); 326 } 327 328 for (TypeDeclaration typeDeclaration : types.values()) { 329 outputDex.add(typeDeclaration.toClassDefItem()); 330 } 331 332 try { 333 return outputDex.toDex(null, false); 334 } catch (IOException e) { 335 throw new RuntimeException(e); 336 } 337 } 338 339 // Generate a file name for the jar by taking a checksum of MethodIds and 340 // parent class types. generateFileName()341 private String generateFileName() { 342 int checksum = 1; 343 344 Set<TypeId<?>> typesKeySet = types.keySet(); 345 Iterator<TypeId<?>> it = typesKeySet.iterator(); 346 int[] checksums = new int[typesKeySet.size()]; 347 int i = 0; 348 349 while (it.hasNext()) { 350 TypeId<?> typeId = it.next(); 351 TypeDeclaration decl = getTypeDeclaration(typeId); 352 Set<MethodId> methodSet = decl.methods.keySet(); 353 if (decl.supertype != null) { 354 checksums[i++] = 31 * decl.supertype.hashCode() + methodSet.hashCode(); 355 } 356 } 357 Arrays.sort(checksums); 358 359 for (int sum : checksums) { 360 checksum *= 31; 361 checksum += sum; 362 } 363 364 return "Generated_" + checksum +".jar"; 365 } 366 367 /** 368 * Set shared class loader to use. 369 * 370 * <p>If a class wants to call package private methods of another class they need to share a 371 * class loader. One common case for this requirement is a mock class wanting to mock package 372 * private methods of the original class. 373 * 374 * @param classLoader the class loader the new class should be loaded by 375 */ setSharedClassLoader(ClassLoader classLoader)376 public void setSharedClassLoader(ClassLoader classLoader) { 377 this.sharedClassLoader = classLoader; 378 } 379 markAsTrusted()380 public void markAsTrusted() { 381 this.markAsTrusted = true; 382 } 383 generateClassLoader(File result, File dexCache, ClassLoader parent)384 private ClassLoader generateClassLoader(File result, File dexCache, ClassLoader parent) { 385 try { 386 // Try to load the class so that it can call hidden APIs. This is required for spying 387 // on system classes as real-methods of these classes might call blacklisted APIs 388 if (markAsTrusted) { 389 try { 390 if (sharedClassLoader != null) { 391 ClassLoader loader = parent != null ? parent : sharedClassLoader; 392 loader.getClass().getMethod("addDexPath", String.class, 393 Boolean.TYPE).invoke(loader, result.getPath(), true); 394 return loader; 395 } else { 396 return (ClassLoader) Class.forName("dalvik.system.BaseDexClassLoader") 397 .getConstructor(String.class, File.class, String.class, 398 ClassLoader.class, Boolean.TYPE) 399 .newInstance(result.getPath(), dexCache.getAbsoluteFile(), null, 400 parent, true); 401 } 402 } catch (InvocationTargetException e) { 403 if (e.getCause() instanceof SecurityException) { 404 Log.i(LOG_TAG, "Cannot allow to call blacklisted super methods. This might " 405 + "break spying on system classes.", e.getCause()); 406 } else { 407 throw e; 408 } 409 } 410 } 411 412 if (sharedClassLoader != null) { 413 ClassLoader loader = parent != null ? parent : sharedClassLoader; 414 loader.getClass().getMethod("addDexPath", String.class).invoke(loader, 415 result.getPath()); 416 return loader; 417 } else { 418 return (ClassLoader) Class.forName("dalvik.system.DexClassLoader") 419 .getConstructor(String.class, String.class, String.class, ClassLoader.class) 420 .newInstance(result.getPath(), dexCache.getAbsolutePath(), null, parent); 421 } 422 } catch (ClassNotFoundException e) { 423 throw new UnsupportedOperationException("load() requires a Dalvik VM", e); 424 } catch (InvocationTargetException e) { 425 throw new RuntimeException(e.getCause()); 426 } catch (InstantiationException e) { 427 throw new AssertionError(); 428 } catch (NoSuchMethodException e) { 429 throw new AssertionError(); 430 } catch (IllegalAccessException e) { 431 throw new AssertionError(); 432 } 433 } 434 435 /** 436 * Generates a dex file and loads its types into the current process. 437 * 438 * <h3>Picking a dex cache directory</h3> 439 * The {@code dexCache} should be an application-private directory. If 440 * you pass a world-writable directory like {@code /sdcard} a malicious app 441 * could inject code into your process. Most applications should use this: 442 * <pre> {@code 443 * 444 * File dexCache = getApplicationContext().getDir("dx", Context.MODE_PRIVATE); 445 * }</pre> 446 * If the {@code dexCache} is null, this method will consult the {@code 447 * dexmaker.dexcache} system property. If that exists, it will be used for 448 * the dex cache. If it doesn't exist, this method will attempt to guess 449 * the application's private data directory as a last resort. If that fails, 450 * this method will fail with an unchecked exception. You can avoid the 451 * exception by either providing a non-null value or setting the system 452 * property. 453 * 454 * @param parent the parent ClassLoader to be used when loading our 455 * generated types 456 * @param dexCache the destination directory where generated and optimized 457 * dex files will be written. If null, this class will try to guess the 458 * application's private data dir. 459 */ generateAndLoad(ClassLoader parent, File dexCache)460 public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException { 461 if (dexCache == null) { 462 String property = System.getProperty("dexmaker.dexcache"); 463 if (property != null) { 464 dexCache = new File(property); 465 } else { 466 dexCache = new AppDataDirGuesser().guess(); 467 if (dexCache == null) { 468 throw new IllegalArgumentException("dexcache == null (and no default could be" 469 + " found; consider setting the 'dexmaker.dexcache' system property)"); 470 } 471 } 472 } 473 474 File result = new File(dexCache, generateFileName()); 475 // Check that the file exists. If it does, return a DexClassLoader and skip all 476 // the dex bytecode generation. 477 if (result.exists()) { 478 return generateClassLoader(result, dexCache, parent); 479 } 480 481 byte[] dex = generate(); 482 483 /* 484 * This implementation currently dumps the dex to the filesystem. It 485 * jars the emitted .dex for the benefit of Gingerbread and earlier 486 * devices, which can't load .dex files directly. 487 * 488 * TODO: load the dex from memory where supported. 489 */ 490 result.createNewFile(); 491 JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(result)); 492 JarEntry entry = new JarEntry(DexFormat.DEX_IN_JAR_NAME); 493 entry.setSize(dex.length); 494 jarOut.putNextEntry(entry); 495 jarOut.write(dex); 496 jarOut.closeEntry(); 497 jarOut.close(); 498 return generateClassLoader(result, dexCache, parent); 499 } 500 getDexFile()501 DexFile getDexFile() { 502 if (outputDex == null) { 503 DexOptions options = new DexOptions(); 504 options.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; 505 outputDex = new DexFile(options); 506 } 507 return outputDex; 508 } 509 510 static class TypeDeclaration { 511 private final TypeId<?> type; 512 513 /** declared state */ 514 private boolean declared; 515 private int flags; 516 private TypeId<?> supertype; 517 private String sourceFile; 518 private TypeList interfaces; 519 private ClassDefItem classDefItem; 520 521 private final Map<FieldId, FieldDeclaration> fields = new LinkedHashMap<>(); 522 private final Map<MethodId, MethodDeclaration> methods = new LinkedHashMap<>(); 523 TypeDeclaration(TypeId<?> type)524 TypeDeclaration(TypeId<?> type) { 525 this.type = type; 526 } 527 toClassDefItem()528 ClassDefItem toClassDefItem() { 529 if (!declared) { 530 throw new IllegalStateException("Undeclared type " + type + " declares members: " 531 + fields.keySet() + " " + methods.keySet()); 532 } 533 534 DexOptions dexOptions = new DexOptions(); 535 dexOptions.targetApiLevel = DexFormat.API_NO_EXTENDED_OPCODES; 536 537 CstType thisType = type.constant; 538 539 if (classDefItem == null) { 540 classDefItem = new ClassDefItem(thisType, flags, supertype.constant, 541 interfaces.ropTypes, new CstString(sourceFile)); 542 543 for (MethodDeclaration method : methods.values()) { 544 EncodedMethod encoded = method.toEncodedMethod(dexOptions); 545 if (method.isDirect()) { 546 classDefItem.addDirectMethod(encoded); 547 } else { 548 classDefItem.addVirtualMethod(encoded); 549 } 550 } 551 for (FieldDeclaration field : fields.values()) { 552 EncodedField encoded = field.toEncodedField(); 553 if (field.isStatic()) { 554 classDefItem.addStaticField(encoded, Constants.getConstant(field.staticValue)); 555 } else { 556 classDefItem.addInstanceField(encoded); 557 } 558 } 559 } 560 561 return classDefItem; 562 } 563 } 564 565 static class FieldDeclaration { 566 final FieldId<?, ?> fieldId; 567 private final int accessFlags; 568 private final Object staticValue; 569 FieldDeclaration(FieldId<?, ?> fieldId, int accessFlags, Object staticValue)570 FieldDeclaration(FieldId<?, ?> fieldId, int accessFlags, Object staticValue) { 571 if ((accessFlags & STATIC) == 0 && staticValue != null) { 572 throw new IllegalArgumentException("instance fields may not have a value"); 573 } 574 this.fieldId = fieldId; 575 this.accessFlags = accessFlags; 576 this.staticValue = staticValue; 577 } 578 toEncodedField()579 EncodedField toEncodedField() { 580 return new EncodedField(fieldId.constant, accessFlags); 581 } 582 isStatic()583 public boolean isStatic() { 584 return (accessFlags & STATIC) != 0; 585 } 586 } 587 588 static class MethodDeclaration { 589 final MethodId<?, ?> method; 590 private final int flags; 591 private final Code code; 592 MethodDeclaration(MethodId<?, ?> method, int flags)593 public MethodDeclaration(MethodId<?, ?> method, int flags) { 594 this.method = method; 595 this.flags = flags; 596 this.code = new Code(this); 597 } 598 isStatic()599 boolean isStatic() { 600 return (flags & STATIC) != 0; 601 } 602 isDirect()603 boolean isDirect() { 604 return (flags & (STATIC | PRIVATE | ACC_CONSTRUCTOR)) != 0; 605 } 606 toEncodedMethod(DexOptions dexOptions)607 EncodedMethod toEncodedMethod(DexOptions dexOptions) { 608 RopMethod ropMethod = new RopMethod(code.toBasicBlocks(), 0); 609 LocalVariableInfo locals = null; 610 DalvCode dalvCode = RopTranslator.translate( 611 ropMethod, PositionList.NONE, locals, code.paramSize(), dexOptions); 612 return new EncodedMethod(method.constant, flags, dalvCode, StdTypeList.EMPTY); 613 } 614 } 615 } 616