1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.LOLLIPOP; 4 import static android.os.Build.VERSION_CODES.Q; 5 6 import android.annotation.TargetApi; 7 import dalvik.system.VMRuntime; 8 import java.lang.ref.WeakReference; 9 import java.lang.reflect.Array; 10 import javax.annotation.Nullable; 11 import org.robolectric.annotation.Implementation; 12 import org.robolectric.annotation.Implements; 13 import org.robolectric.annotation.Resetter; 14 import org.robolectric.res.android.NativeObjRegistry; 15 16 @Implements(value = VMRuntime.class, isInAndroidSdk = false) 17 public class ShadowVMRuntime { 18 19 private final NativeObjRegistry<WeakReference<Object>> nativeObjRegistry = 20 new NativeObjRegistry<>("VRRuntime.nativeObjectRegistry"); 21 // There actually isn't any android JNI code to call through to in Robolectric due to 22 // cross-platform compatibility issues. We default to a reasonable value that reflects the devices 23 // that would commonly run this code. 24 private static boolean is64Bit = true; 25 26 @Nullable private static String currentInstructionSet = null; 27 28 @Implementation newUnpaddedArray(Class<?> klass, int size)29 public Object newUnpaddedArray(Class<?> klass, int size) { 30 return Array.newInstance(klass, size); 31 } 32 33 @Implementation newNonMovableArray(Class<?> type, int size)34 public Object newNonMovableArray(Class<?> type, int size) { 35 if (type.equals(int.class)) { 36 return new int[size]; 37 } 38 return null; 39 } 40 41 /** 42 * Returns a unique identifier of the object instead of a 'native' address. 43 */ 44 @Implementation addressOf(Object obj)45 public long addressOf(Object obj) { 46 return nativeObjRegistry.register(new WeakReference<>(obj)); 47 } 48 49 /** 50 * Returns the object previously registered with {@link #addressOf(Object)}. 51 */ 52 public @Nullable getObjectForAddress(long address)53 Object getObjectForAddress(long address) { 54 return nativeObjRegistry.getNativeObject(address).get(); 55 } 56 57 /** 58 * Returns whether the VM is running in 64-bit mode. Available in Android L+. Defaults to true. 59 */ 60 @Implementation is64Bit()61 protected boolean is64Bit() { 62 return ShadowVMRuntime.is64Bit; 63 } 64 65 /** Sets whether the VM is running in 64-bit mode. */ 66 @TargetApi(LOLLIPOP) setIs64Bit(boolean is64Bit)67 public static void setIs64Bit(boolean is64Bit) { 68 ShadowVMRuntime.is64Bit = is64Bit; 69 } 70 71 /** Returns the instruction set of the current runtime. */ 72 @Implementation getCurrentInstructionSet()73 protected static String getCurrentInstructionSet() { 74 return currentInstructionSet; 75 } 76 77 /** Sets the instruction set of the current runtime. */ 78 @TargetApi(LOLLIPOP) setCurrentInstructionSet(@ullable String currentInstructionSet)79 public static void setCurrentInstructionSet(@Nullable String currentInstructionSet) { 80 ShadowVMRuntime.currentInstructionSet = currentInstructionSet; 81 } 82 83 @Resetter reset()84 public static void reset() { 85 ShadowVMRuntime.is64Bit = true; 86 ShadowVMRuntime.currentInstructionSet = null; 87 } 88 89 @Implementation(minSdk = Q) getNotifyNativeInterval()90 protected static int getNotifyNativeInterval() { 91 // The value '384' is from 92 // https://cs.android.com/android/platform/superproject/+/android-12.0.0_r18:art/runtime/gc/heap.h;l=172 93 // Note that value returned is irrelevant for the JVM, it just has to be greater than zero to 94 // avoid a divide-by-zero error in VMRuntime.notifyNativeAllocation. 95 return 384; // must be greater than 0 96 } 97 } 98