diff options
71 files changed, 2391 insertions, 933 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index acaa0f8add..66a16b6036 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -59,6 +59,7 @@ TEST_COMMON_SRC_FILES := \ runtime/reference_table_test.cc \ runtime/runtime_test.cc \ runtime/thread_pool_test.cc \ + runtime/transaction_test.cc \ runtime/utils_test.cc \ runtime/verifier/method_verifier_test.cc \ runtime/verifier/reg_type_test.cc \ diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index f4ae18fe47..eab68eeab7 100644 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -978,6 +978,10 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation } // Nope. Do it the hard way + // Check for V*V. We can eliminate a multiply in that case, as 2L*1H == 2H*1L. + bool is_square = mir_graph_->SRegToVReg(rl_src1.s_reg_low) == + mir_graph_->SRegToVReg(rl_src2.s_reg_low); + FlushAllRegs(); LockCallTemps(); // Prepare for explicit register usage. rl_src1 = UpdateLocWide(rl_src1); @@ -995,36 +999,52 @@ void X86Mir2Lir::GenMulLong(Instruction::Code, RegLocation rl_dest, RegLocation kWord, GetSRegHi(rl_src1.s_reg_low)); } - // EAX <- 2H - if (src2_in_reg) { - NewLIR2(kX86Mov32RR, r0, rl_src2.high_reg); - } else { - LoadBaseDisp(rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, r0, - kWord, GetSRegHi(rl_src2.s_reg_low)); - } + if (is_square) { + // Take advantage of the fact that the values are the same. + // ECX <- ECX * 2L (1H * 2L) + if (src2_in_reg) { + NewLIR2(kX86Imul32RR, r1, rl_src2.low_reg); + } else { + int displacement = SRegOffset(rl_src2.s_reg_low); + LIR *m = NewLIR3(kX86Imul32RM, r1, rX86_SP, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is_64bit */); + } - // EAX <- EAX * 1L (2H * 1L) - if (src1_in_reg) { - NewLIR2(kX86Imul32RR, r0, rl_src1.low_reg); + // ECX <- 2*ECX (2H * 1L) + (1H * 2L) + NewLIR2(kX86Add32RR, r1, r1); } else { - int displacement = SRegOffset(rl_src1.s_reg_low); - LIR *m = NewLIR3(kX86Imul32RM, r0, rX86_SP, displacement + LOWORD_OFFSET); - AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, - true /* is_load */, true /* is_64bit */); - } + // EAX <- 2H + if (src2_in_reg) { + NewLIR2(kX86Mov32RR, r0, rl_src2.high_reg); + } else { + LoadBaseDisp(rX86_SP, SRegOffset(rl_src2.s_reg_low) + HIWORD_OFFSET, r0, + kWord, GetSRegHi(rl_src2.s_reg_low)); + } - // ECX <- ECX * 2L (1H * 2L) - if (src2_in_reg) { - NewLIR2(kX86Imul32RR, r1, rl_src2.low_reg); - } else { - int displacement = SRegOffset(rl_src2.s_reg_low); - LIR *m = NewLIR3(kX86Imul32RM, r1, rX86_SP, displacement + LOWORD_OFFSET); - AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, - true /* is_load */, true /* is_64bit */); - } + // EAX <- EAX * 1L (2H * 1L) + if (src1_in_reg) { + NewLIR2(kX86Imul32RR, r0, rl_src1.low_reg); + } else { + int displacement = SRegOffset(rl_src1.s_reg_low); + LIR *m = NewLIR3(kX86Imul32RM, r0, rX86_SP, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is_64bit */); + } - // ECX <- ECX + EAX (2H * 1L) + (1H * 2L) - NewLIR2(kX86Add32RR, r1, r0); + // ECX <- ECX * 2L (1H * 2L) + if (src2_in_reg) { + NewLIR2(kX86Imul32RR, r1, rl_src2.low_reg); + } else { + int displacement = SRegOffset(rl_src2.s_reg_low); + LIR *m = NewLIR3(kX86Imul32RM, r1, rX86_SP, displacement + LOWORD_OFFSET); + AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2, + true /* is_load */, true /* is_64bit */); + } + + // ECX <- ECX + EAX (2H * 1L) + (1H * 2L) + NewLIR2(kX86Add32RR, r1, r0); + } // EAX <- 2L if (src2_in_reg) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8bf2f425ab..c72c1c5580 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -50,6 +50,7 @@ #include "thread.h" #include "thread_pool.h" #include "trampolines/trampoline_compiler.h" +#include "transaction.h" #include "verifier/method_verifier.h" #include "verifier/method_verifier-inl.h" @@ -1706,425 +1707,6 @@ void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_); } -static const char* class_initializer_black_list[] = { - "Landroid/app/ActivityThread;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/bluetooth/BluetoothAudioGateway;", // Calls android.bluetooth.BluetoothAudioGateway.classInitNative(). - "Landroid/bluetooth/HeadsetBase;", // Calls android.bluetooth.HeadsetBase.classInitNative(). - "Landroid/content/res/CompatibilityInfo;", // Requires android.util.DisplayMetrics -..-> android.os.SystemProperties.native_get_int. - "Landroid/content/res/CompatibilityInfo$1;", // Requires android.util.DisplayMetrics -..-> android.os.SystemProperties.native_get_int. - "Landroid/content/UriMatcher;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/database/CursorWindow;", // Requires android.util.DisplayMetrics -..-> android.os.SystemProperties.native_get_int. - "Landroid/database/sqlite/SQLiteConnection;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/database/sqlite/SQLiteConnection$Operation;", // Requires SimpleDateFormat -> java.util.Locale. - "Landroid/database/sqlite/SQLiteDatabaseConfiguration;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/database/sqlite/SQLiteDebug;", // Calls android.util.Log.isLoggable. - "Landroid/database/sqlite/SQLiteOpenHelper;", // Calls Class.getSimpleName -> Class.isAnonymousClass -> Class.getDex. - "Landroid/database/sqlite/SQLiteQueryBuilder;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/drm/DrmManagerClient;", // Calls System.loadLibrary. - "Landroid/graphics/drawable/AnimatedRotateDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/AnimationDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/BitmapDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/ClipDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/ColorDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/Drawable;", // Requires android.graphics.Rect. - "Landroid/graphics/drawable/DrawableContainer;", // Sub-class of Drawable. - "Landroid/graphics/drawable/GradientDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/LayerDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/NinePatchDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/RotateDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/ScaleDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/ShapeDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/StateListDrawable;", // Sub-class of Drawable. - "Landroid/graphics/drawable/TransitionDrawable;", // Sub-class of Drawable. - "Landroid/graphics/Matrix;", // Calls android.graphics.Matrix.native_create. - "Landroid/graphics/Matrix$1;", // Requires Matrix. - "Landroid/graphics/PixelFormat;", // Calls android.graphics.PixelFormat.nativeClassInit(). - "Landroid/graphics/Rect;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/graphics/SurfaceTexture;", // Calls android.graphics.SurfaceTexture.nativeClassInit(). - "Landroid/graphics/Typeface;", // Calls android.graphics.Typeface.nativeCreate. - "Landroid/inputmethodservice/ExtractEditText;", // Requires android.widget.TextView. - "Landroid/media/AmrInputStream;", // Calls OsConstants.initConstants. - "Landroid/media/CamcorderProfile;", // Calls OsConstants.initConstants. - "Landroid/media/CameraProfile;", // Calls System.loadLibrary. - "Landroid/media/DecoderCapabilities;", // Calls System.loadLibrary. - "Landroid/media/EncoderCapabilities;", // Calls OsConstants.initConstants. - "Landroid/media/ExifInterface;", // Calls OsConstants.initConstants. - "Landroid/media/MediaCodec;", // Calls OsConstants.initConstants. - "Landroid/media/MediaCodecList;", // Calls OsConstants.initConstants. - "Landroid/media/MediaCrypto;", // Calls OsConstants.initConstants. - "Landroid/media/MediaDrm;", // Calls OsConstants.initConstants. - "Landroid/media/MediaExtractor;", // Calls OsConstants.initConstants. - "Landroid/media/MediaFile;", // Requires DecoderCapabilities. - "Landroid/media/MediaMetadataRetriever;", // Calls OsConstants.initConstants. - "Landroid/media/MediaMuxer;", // Calls OsConstants.initConstants. - "Landroid/media/MediaPlayer;", // Calls System.loadLibrary. - "Landroid/media/MediaRecorder;", // Calls System.loadLibrary. - "Landroid/media/MediaScanner;", // Calls System.loadLibrary. - "Landroid/media/ResampleInputStream;", // Calls OsConstants.initConstants. - "Landroid/media/SoundPool;", // Calls OsConstants.initConstants. - "Landroid/media/videoeditor/MediaArtistNativeHelper;", // Calls OsConstants.initConstants. - "Landroid/media/videoeditor/VideoEditorProfile;", // Calls OsConstants.initConstants. - "Landroid/mtp/MtpDatabase;", // Calls OsConstants.initConstants. - "Landroid/mtp/MtpDevice;", // Calls OsConstants.initConstants. - "Landroid/mtp/MtpServer;", // Calls OsConstants.initConstants. - "Landroid/net/NetworkInfo;", // Calls java.util.EnumMap.<init> -> java.lang.Enum.getSharedConstants -> System.identityHashCode. - "Landroid/net/Proxy;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/net/SSLCertificateSocketFactory;", // Requires javax.net.ssl.HttpsURLConnection. - "Landroid/net/Uri$AbstractHierarchicalUri;", // Requires Uri. - "Landroid/net/Uri$HierarchicalUri;", // Requires Uri. - "Landroid/net/Uri$OpaqueUri;", // Requires Uri. - "Landroid/net/Uri$StringUri;", // Requires Uri. - "Landroid/net/Uri;", // Calls Class.getSimpleName -> Class.isAnonymousClass -> Class.getDex. - "Landroid/net/WebAddress;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/net/wifi/WifiNative;", // Calls new LocalLog -> new Time -> TimeZone -> Pattern.compile. - "Landroid/nfc/NdefRecord;", // Calls String.getBytes -> java.nio.charset.Charset. - "Landroid/opengl/EGL14;", // Calls android.opengl.EGL14._nativeClassInit. - "Landroid/opengl/GLES10;", // Calls android.opengl.GLES10._nativeClassInit. - "Landroid/opengl/GLES10Ext;", // Calls android.opengl.GLES10Ext._nativeClassInit. - "Landroid/opengl/GLES11;", // Requires GLES10. - "Landroid/opengl/GLES11Ext;", // Calls android.opengl.GLES11Ext._nativeClassInit. - "Landroid/opengl/GLES20;", // Calls android.opengl.GLES20._nativeClassInit. - "Landroid/opengl/GLUtils;", // Calls android.opengl.GLUtils.nativeClassInit. - "Landroid/os/Build;", // Calls -..-> android.os.SystemProperties.native_get. - "Landroid/os/Build$VERSION;", // Requires Build. - "Landroid/os/Bundle;", // Calls android.os.Parcel.obtain -..> Parcel.nativeCreate. - "Landroid/os/Debug;", // Requires android.os.Environment. - "Landroid/os/Environment;", // Calls System.getenv. - "Landroid/os/FileUtils;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/os/StrictMode;", // Calls android.util.Log.isLoggable. - "Landroid/os/StrictMode$VmPolicy;", // Requires StrictMode. - "Landroid/os/Trace;", // Calls android.os.Trace.nativeGetEnabledTags. - "Landroid/os/UEventObserver;", // Calls Class.getSimpleName -> Class.isAnonymousClass -> Class.getDex. - "Landroid/provider/ContactsContract;", // Calls OsConstants.initConstants. - "Landroid/provider/Settings$Global;", // Calls OsConstants.initConstants. - "Landroid/provider/Settings$Secure;", // Requires android.net.Uri. - "Landroid/provider/Settings$System;", // Requires android.net.Uri. - "Landroid/renderscript/RenderScript;", // Calls System.loadLibrary. - "Landroid/server/BluetoothService;", // Calls android.server.BluetoothService.classInitNative. - "Landroid/server/BluetoothEventLoop;", // Calls android.server.BluetoothEventLoop.classInitNative. - "Landroid/telephony/PhoneNumberUtils;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/telephony/TelephonyManager;", // Calls OsConstants.initConstants. - "Landroid/text/AutoText;", // Requires android.util.DisplayMetrics -..-> android.os.SystemProperties.native_get_int. - "Landroid/text/Layout;", // Calls com.android.internal.util.ArrayUtils.emptyArray -> System.identityHashCode. - "Landroid/text/BoringLayout;", // Requires Layout. - "Landroid/text/DynamicLayout;", // Requires Layout. - "Landroid/text/Html$HtmlParser;", // Calls -..-> String.toLowerCase -> java.util.Locale. - "Landroid/text/StaticLayout;", // Requires Layout. - "Landroid/text/TextUtils;", // Requires android.util.DisplayMetrics. - "Landroid/util/DisplayMetrics;", // Calls SystemProperties.native_get_int. - "Landroid/util/Patterns;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/view/Choreographer;", // Calls SystemProperties.native_get_boolean. - "Landroid/util/Patterns;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/view/GLES20Canvas;", // Calls GLES20Canvas.nIsAvailable(). - "Landroid/view/GLES20RecordingCanvas;", // Requires android.view.GLES20Canvas. - "Landroid/view/GestureDetector;", // Calls android.view.GLES20Canvas.nIsAvailable. - "Landroid/view/HardwareRenderer$Gl20Renderer;", // Requires SystemProperties.native_get. - "Landroid/view/HardwareRenderer$GlRenderer;", // Requires SystemProperties.native_get. - "Landroid/view/InputEventConsistencyVerifier;", // Requires android.os.Build. - "Landroid/view/Surface;", // Requires SystemProperties.native_get. - "Landroid/view/SurfaceControl;", // Calls OsConstants.initConstants. - "Landroid/view/animation/AlphaAnimation;", // Requires Animation. - "Landroid/view/animation/Animation;", // Calls SystemProperties.native_get_boolean. - "Landroid/view/animation/AnimationSet;", // Calls OsConstants.initConstants. - "Landroid/view/textservice/SpellCheckerSubtype;", // Calls Class.getDex(). - "Landroid/webkit/JniUtil;", // Calls System.loadLibrary. - "Landroid/webkit/PluginManager;", // // Calls OsConstants.initConstants. - "Landroid/webkit/WebViewCore;", // Calls System.loadLibrary. - "Landroid/webkit/WebViewFactory;", // Calls -..-> android.os.SystemProperties.native_get. - "Landroid/webkit/WebViewFactory$Preloader;", // Calls to Class.forName. - "Landroid/webkit/WebViewInputDispatcher;", // Calls Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/webkit/URLUtil;", // Calls Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Landroid/widget/AutoCompleteTextView;", // Requires TextView. - "Landroid/widget/Button;", // Requires TextView. - "Landroid/widget/CheckBox;", // Requires TextView. - "Landroid/widget/CheckedTextView;", // Requires TextView. - "Landroid/widget/CompoundButton;", // Requires TextView. - "Landroid/widget/EditText;", // Requires TextView. - "Landroid/widget/NumberPicker;", // Requires java.util.Locale. - "Landroid/widget/ScrollBarDrawable;", // Sub-class of Drawable. - "Landroid/widget/SearchView$SearchAutoComplete;", // Requires TextView. - "Landroid/widget/Switch;", // Requires TextView. - "Landroid/widget/TextView;", // Calls Paint.<init> -> Paint.native_init. - "Lcom/android/i18n/phonenumbers/AsYouTypeFormatter;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Lcom/android/i18n/phonenumbers/MetadataManager;", // Calls OsConstants.initConstants. - "Lcom/android/i18n/phonenumbers/PhoneNumberMatcher;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Lcom/android/i18n/phonenumbers/PhoneNumberUtil;", // Requires java.util.logging.LogManager. - "Lcom/android/i18n/phonenumbers/geocoding/AreaCodeMap;", // Calls OsConstants.initConstants. - "Lcom/android/i18n/phonenumbers/geocoding/PhoneNumberOfflineGeocoder;", // Calls OsConstants.initConstants. - "Lcom/android/internal/os/SamplingProfilerIntegration;", // Calls SystemProperties.native_get_int. - "Lcom/android/internal/policy/impl/PhoneWindow;", // Calls android.os.Binder.init. - "Lcom/android/internal/view/menu/ActionMenuItemView;", // Requires TextView. - "Lcom/android/internal/widget/DialogTitle;", // Requires TextView. - "Lcom/android/org/bouncycastle/asn1/StreamUtil;", // Calls Runtime.getRuntime().maxMemory(). - "Lcom/android/org/bouncycastle/asn1/pkcs/MacData;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/asn1/pkcs/RSASSAPSSparams;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/asn1/cms/SignedData;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/asn1/x509/GeneralSubtree;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/asn1/x9/X9ECParameters;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest$MD5;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest$SHA1;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest$SHA256;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest$SHA384;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/bouncycastle/crypto/digests/OpenSSLDigest$SHA512;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/bouncycastle/crypto/engines/RSABlindedEngine;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/crypto/generators/DHKeyGeneratorHelper;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/crypto/generators/DHParametersGenerator;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/crypto/generators/DHParametersHelper;", // Calls System.getenv -> OsConstants.initConstants. - "Lcom/android/org/bouncycastle/crypto/generators/DSAKeyPairGenerator;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/crypto/generators/DSAParametersGenerator;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/crypto/generators/RSAKeyPairGenerator;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyPairGeneratorSpi;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/dsa/KeyPairGeneratorSpi;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi$EC;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi$ECDH;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi$ECDHC;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi$ECDSA;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi$ECMQV;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateCrtKey;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPrivateKey;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/jcajce/provider/asymmetric/rsa/KeyPairGeneratorSpi;", // Calls OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi$BCPKCS12KeyStore;", // Calls Thread.currentThread. - "Lcom/android/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi;", // Calls Thread.currentThread. - "Lcom/android/org/bouncycastle/jce/PKCS10CertificationRequest;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/jce/provider/CertBlacklist;", // Calls System.getenv -> OsConstants.initConstants. - "Lcom/android/org/bouncycastle/jce/provider/JCERSAPrivateCrtKey;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/jce/provider/JCERSAPrivateKey;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpi;", // Calls System.getenv -> OsConstants.initConstants. - "Lcom/android/org/bouncycastle/math/ec/ECConstants;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/math/ec/Tnaf;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/util/BigIntegers;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/bouncycastle/x509/X509Util;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lcom/android/org/conscrypt/CipherSuite;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/FileClientSessionCache$CacheFile;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/HandshakeIODataStream;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/Logger;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/NativeCrypto;", // Calls native NativeCrypto.clinit(). - "Lcom/android/org/conscrypt/OpenSSLECKeyPairGenerator;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/OpenSSLEngine;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/conscrypt/OpenSSLMac$HmacMD5;", // Calls native NativeCrypto.clinit(). - "Lcom/android/org/conscrypt/OpenSSLMac$HmacSHA1;", // Calls native NativeCrypto.clinit(). - "Lcom/android/org/conscrypt/OpenSSLMac$HmacSHA256;", // Calls native NativeCrypto.clinit(). - "Lcom/android/org/conscrypt/OpenSSLMac$HmacSHA384;", // Calls native NativeCrypto.clinit(). - "Lcom/android/org/conscrypt/OpenSSLMac$HmacSHA512;", // Calls native NativeCrypto.clinit(). - "Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$MD5;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$SHA1;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$SHA256;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$SHA384;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/conscrypt/OpenSSLMessageDigestJDK$SHA512;", // Requires com.android.org.conscrypt.NativeCrypto. - "Lcom/android/org/conscrypt/OpenSSLX509CertPath;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/OpenSSLX509CertificateFactory;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/PRF;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/SSLSessionImpl;", // Calls OsConstants.initConstants. - "Lcom/android/org/conscrypt/TrustedCertificateStore;", // Calls System.getenv -> OsConstants.initConstants. - "Lcom/android/okhttp/ConnectionPool;", // Calls OsConstants.initConstants. - "Lcom/android/okhttp/OkHttpClient;", // Calls OsConstants.initConstants. - "Lcom/android/okhttp/internal/DiskLruCache;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Lcom/android/okhttp/internal/Util;", // Calls OsConstants.initConstants. - "Lcom/android/okhttp/internal/http/HttpsURLConnectionImpl;", // Calls VMClassLoader.getBootClassPathSize. - "Lcom/android/okhttp/internal/spdy/SpdyConnection;", // Calls OsConstants.initConstants. - "Lcom/android/okhttp/internal/spdy/SpdyReader;", // Calls OsConstants.initConstants. - "Lcom/android/okhttp/internal/tls/OkHostnameVerifier;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Lcom/google/android/gles_jni/EGLContextImpl;", // Calls com.google.android.gles_jni.EGLImpl._nativeClassInit. - "Lcom/google/android/gles_jni/EGLImpl;", // Calls com.google.android.gles_jni.EGLImpl._nativeClassInit. - "Lcom/google/android/gles_jni/GLImpl;", // Calls com.google.android.gles_jni.GLImpl._nativeClassInit. - "Lgov/nist/core/GenericObject;", // Calls OsConstants.initConstants. - "Lgov/nist/core/Host;", // Calls OsConstants.initConstants. - "Lgov/nist/core/HostPort;", // Calls OsConstants.initConstants. - "Lgov/nist/core/NameValue;", // Calls OsConstants.initConstants. - "Lgov/nist/core/net/DefaultNetworkLayer;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/Utils;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/address/AddressImpl;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/address/Authority;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/address/GenericURI;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/address/NetObject;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/address/SipUri;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/address/TelephoneNumber;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/address/UserInfo;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Accept;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/AcceptEncoding;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/AcceptLanguage;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/AddressParametersHeader;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/AlertInfoList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/AllowEvents;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/AllowEventsList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/AuthenticationInfo;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Authorization;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/CSeq;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/CallIdentifier;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Challenge;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ContactList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ContentEncoding;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ContentEncodingList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ContentLanguageList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ContentType;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Credentials;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ErrorInfoList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Expires;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/From;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/MimeVersion;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/NameMap;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Priority;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Protocol;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ProxyAuthenticate;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ProxyAuthenticateList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ProxyAuthorizationList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ProxyRequire;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ProxyRequireList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/RSeq;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/RecordRoute;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ReferTo;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/RequestLine;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Require;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/RetryAfter;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/SIPETag;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/SIPHeader;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/SIPHeaderNamesCache;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/StatusLine;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/SubscriptionState;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/TimeStamp;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/UserAgent;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Unsupported;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/Warning;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ViaList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/extensions/Join;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/extensions/References;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/extensions/Replaces;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PAccessNetworkInfo;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PAssertedIdentity;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PAssertedIdentityList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PAssociatedURI;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PCalledPartyID;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PChargingVector;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PPreferredIdentity;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PVisitedNetworkIDList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/PathList;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/SecurityAgree;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/SecurityClient;", // Calls OsConstants.initConstants. - "Lgov/nist/javax/sip/header/ims/ServiceRoute;", // Calls OsConstants.initConstants. - "Ljava/io/Console;", // Has FileDescriptor(s). - "Ljava/io/File;", // Calls to Random.<init> -> System.currentTimeMillis -> OsConstants.initConstants. - "Ljava/io/FileDescriptor;", // Requires libcore.io.OsConstants. - "Ljava/io/ObjectInputStream;", // Requires java.lang.ClassLoader$SystemClassLoader. - "Ljava/io/ObjectStreamClass;", // Calls to Class.forName -> java.io.FileDescriptor. - "Ljava/io/ObjectStreamConstants;", // Instance of non-image class SerializablePermission. - "Ljava/lang/ClassLoader$SystemClassLoader;", // Calls System.getProperty -> OsConstants.initConstants. - "Ljava/lang/HexStringParser;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Ljava/lang/ProcessManager;", // Calls Thread.currentThread. - "Ljava/lang/Runtime;", // Calls System.getProperty -> OsConstants.initConstants. - "Ljava/lang/System;", // Calls OsConstants.initConstants. - "Ljava/math/BigDecimal;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Ljava/math/BigInteger;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Ljava/math/Primality;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Ljava/math/Multiplication;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Ljava/net/InetAddress;", // Requires libcore.io.OsConstants. - "Ljava/net/Inet4Address;", // Sub-class of InetAddress. - "Ljava/net/Inet6Address;", // Sub-class of InetAddress. - "Ljava/net/InetUnixAddress;", // Sub-class of InetAddress. - "Ljava/net/NetworkInterface;", // Calls to Random.<init> -> System.currentTimeMillis -> OsConstants.initConstants. - "Ljava/net/StandardSocketOptions;", // Call System.identityHashCode. - "Ljava/nio/charset/Charset;", // Calls Charset.getDefaultCharset -> System.getProperty -> OsConstants.initConstants. - "Ljava/nio/charset/CharsetICU;", // Sub-class of Charset. - "Ljava/nio/charset/Charsets;", // Calls Charset.forName. - "Ljava/nio/charset/StandardCharsets;", // Calls OsConstants.initConstants. - "Ljava/security/AlgorithmParameterGenerator;", // Calls OsConstants.initConstants. - "Ljava/security/KeyPairGenerator$KeyPairGeneratorImpl;", // Calls OsConstants.initConstants. - "Ljava/security/KeyPairGenerator;", // Calls OsConstants.initConstants. - "Ljava/security/Security;", // Tries to do disk IO for "security.properties". - "Ljava/security/spec/RSAKeyGenParameterSpec;", // java.math.NativeBN.BN_new() - "Ljava/sql/Date;", // Calls OsConstants.initConstants. - "Ljava/sql/DriverManager;", // Calls OsConstants.initConstants. - "Ljava/sql/Time;", // Calls OsConstants.initConstants. - "Ljava/sql/Timestamp;", // Calls OsConstants.initConstants. - "Ljava/util/Date;", // Calls Date.<init> -> System.currentTimeMillis -> OsConstants.initConstants. - "Ljava/util/ListResourceBundle;", // Calls OsConstants.initConstants. - "Ljava/util/Locale;", // Calls System.getProperty -> OsConstants.initConstants. - "Ljava/util/PropertyResourceBundle;", // Calls OsConstants.initConstants. - "Ljava/util/ResourceBundle;", // Calls OsConstants.initConstants. - "Ljava/util/ResourceBundle$MissingBundle;", // Calls OsConstants.initConstants. - "Ljava/util/Scanner;", // regex.Pattern.compileImpl. - "Ljava/util/SimpleTimeZone;", // Sub-class of TimeZone. - "Ljava/util/TimeZone;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. - "Ljava/util/concurrent/ConcurrentHashMap;", // Calls Runtime.getRuntime().availableProcessors(). - "Ljava/util/concurrent/ConcurrentHashMap$Segment;", // Calls Runtime.getRuntime().availableProcessors(). - "Ljava/util/concurrent/ConcurrentSkipListMap;", // Calls Random() -> OsConstants.initConstants. - "Ljava/util/concurrent/Exchanger;", // Calls Runtime.getRuntime().availableProcessors(). - "Ljava/util/concurrent/ForkJoinPool;", // Makes a thread pool ..-> calls OsConstants.initConstants. - "Ljava/util/concurrent/LinkedTransferQueue;", // Calls Runtime.getRuntime().availableProcessors(). - "Ljava/util/concurrent/Phaser;", // Calls Runtime.getRuntime().availableProcessors(). - "Ljava/util/concurrent/ScheduledThreadPoolExecutor;", // Calls AtomicLong.VMSupportsCS8() - "Ljava/util/concurrent/SynchronousQueue;", // Calls Runtime.getRuntime().availableProcessors(). - "Ljava/util/concurrent/atomic/AtomicLong;", // Calls AtomicLong.VMSupportsCS8() - "Ljava/util/logging/LogManager;", // Calls System.getProperty -> OsConstants.initConstants. - "Ljava/util/prefs/AbstractPreferences;", // Calls OsConstants.initConstants. - "Ljava/util/prefs/FilePreferencesImpl;", // Calls OsConstants.initConstants. - "Ljava/util/prefs/FilePreferencesFactoryImpl;", // Calls OsConstants.initConstants. - "Ljava/util/prefs/Preferences;", // Calls OsConstants.initConstants. - "Ljavax/crypto/KeyAgreement;", // Calls OsConstants.initConstants. - "Ljavax/crypto/KeyGenerator;", // Calls OsConstants.initConstants. - "Ljavax/security/cert/X509Certificate;", // Calls VMClassLoader.getBootClassPathSize. - "Ljavax/security/cert/X509Certificate$1;", // Calls VMClassLoader.getBootClassPathSize. - "Ljavax/microedition/khronos/egl/EGL10;", // Requires EGLContext. - "Ljavax/microedition/khronos/egl/EGLContext;", // Requires com.google.android.gles_jni.EGLImpl. - "Ljavax/xml/datatype/DatatypeConstants;", // Calls OsConstants.initConstants. - "Ljavax/xml/datatype/FactoryFinder;", // Calls OsConstants.initConstants. - "Ljavax/xml/namespace/QName;", // Calls OsConstants.initConstants. - "Ljavax/xml/validation/SchemaFactoryFinder;", // Calls OsConstants.initConstants. - "Ljavax/xml/xpath/XPathConstants;", // Calls OsConstants.initConstants. - "Ljavax/xml/xpath/XPathFactoryFinder;", // Calls OsConstants.initConstants. - "Llibcore/icu/LocaleData;", // Requires java.util.Locale. - "Llibcore/icu/TimeZoneNames;", // Requires java.util.TimeZone. - "Llibcore/io/IoUtils;", // Calls Random.<init> -> System.currentTimeMillis -> FileDescriptor -> OsConstants.initConstants. - "Llibcore/io/OsConstants;", // Platform specific. - "Llibcore/net/MimeUtils;", // Calls libcore.net.MimeUtils.getContentTypesPropertiesStream -> System.getProperty. - "Llibcore/reflect/Types;", // Calls OsConstants.initConstants. - "Llibcore/util/ZoneInfo;", // Sub-class of TimeZone. - "Llibcore/util/ZoneInfoDB;", // Calls System.getenv -> OsConstants.initConstants. - "Lorg/apache/commons/logging/LogFactory;", // Calls System.getProperty. - "Lorg/apache/commons/logging/impl/LogFactoryImpl;", // Calls OsConstants.initConstants. - "Lorg/apache/harmony/security/fortress/Services;", // Calls ClassLoader.getSystemClassLoader -> System.getProperty. - "Lorg/apache/harmony/security/provider/cert/X509CertFactoryImpl;", // Requires java.nio.charsets.Charsets. - "Lorg/apache/harmony/security/provider/crypto/RandomBitsSupplier;", // Requires java.io.File. - "Lorg/apache/harmony/security/utils/AlgNameMapper;", // Requires java.util.Locale. - "Lorg/apache/harmony/security/pkcs10/CertificationRequest;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/pkcs10/CertificationRequestInfo;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/pkcs7/AuthenticatedAttributes;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/pkcs7/SignedData;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/pkcs7/SignerInfo;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/pkcs8/PrivateKeyInfo;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl;", // Calls OsConstants.initConstants. - "Lorg/apache/harmony/security/x501/AttributeTypeAndValue;", // Calls IntegralToString.convertInt -> Thread.currentThread. - "Lorg/apache/harmony/security/x501/DirectoryString;", // Requires BigInteger. - "Lorg/apache/harmony/security/x501/Name;", // Requires org.apache.harmony.security.x501.AttributeTypeAndValue. - "Lorg/apache/harmony/security/x509/AccessDescription;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/AuthorityKeyIdentifier;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/CRLDistributionPoints;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/Certificate;", // Requires org.apache.harmony.security.x509.TBSCertificate. - "Lorg/apache/harmony/security/x509/CertificateIssuer;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/CertificateList;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/DistributionPoint;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/DistributionPointName;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/EDIPartyName;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lorg/apache/harmony/security/x509/GeneralName;", // Requires org.apache.harmony.security.x501.Name. - "Lorg/apache/harmony/security/x509/GeneralNames;", // Requires GeneralName. - "Lorg/apache/harmony/security/x509/GeneralSubtree;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/GeneralSubtrees;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/InfoAccessSyntax;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/IssuingDistributionPoint;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/NameConstraints;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/TBSCertList$RevokedCertificate;", // Calls NativeBN.BN_new(). - "Lorg/apache/harmony/security/x509/TBSCertList;", // Calls Thread.currentThread. - "Lorg/apache/harmony/security/x509/TBSCertificate;", // Requires org.apache.harmony.security.x501.Name. - "Lorg/apache/harmony/security/x509/Time;", // Calls native ... -> java.math.NativeBN.BN_new(). - "Lorg/apache/harmony/security/x509/Validity;", // Requires x509.Time. - "Lorg/apache/harmony/security/x509/tsp/TSTInfo;", // Calls Thread.currentThread. - "Lorg/apache/harmony/xml/ExpatParser;", // Calls native ExpatParser.staticInitialize. - "Lorg/apache/harmony/xml/ExpatParser$EntityParser;", // Calls ExpatParser.staticInitialize. - "Lorg/apache/http/conn/params/ConnRouteParams;", // Requires java.util.Locale. - "Lorg/apache/http/conn/ssl/SSLSocketFactory;", // Calls java.security.Security.getProperty. - "Lorg/apache/http/conn/util/InetAddressUtils;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl. -}; - static void InitializeClass(const ParallelCompilationManager* manager, size_t class_def_index) LOCKS_EXCLUDED(Locks::mutator_lock_) { ATRACE_CALL(); @@ -2162,33 +1744,49 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl manager->GetClassLinker()->EnsureInitialized(klass, false, true); if (!klass->IsInitialized()) { // We need to initialize static fields, we only do this for image classes that aren't - // black listed or marked with the $NoPreloadHolder. + // marked with the $NoPreloadHolder (which implies this should not be initialized early). bool can_init_static_fields = manager->GetCompiler()->IsImage() && - manager->GetCompiler()->IsImageClass(descriptor); + manager->GetCompiler()->IsImageClass(descriptor) && + !StringPiece(descriptor).ends_with("$NoPreloadHolder;"); if (can_init_static_fields) { - // NoPreloadHolder inner class implies this should not be initialized early. - bool is_black_listed = StringPiece(descriptor).ends_with("$NoPreloadHolder;"); - if (!is_black_listed) { - for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) { - if (strcmp(descriptor, class_initializer_black_list[i]) == 0) { - is_black_listed = true; - break; - } - } - } - if (!is_black_listed) { - VLOG(compiler) << "Initializing: " << descriptor; - if (strcmp("Ljava/lang/Void;", descriptor) == 0) { - // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime. - ObjectLock<mirror::Class> lock(soa.Self(), &klass); - mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields(); - CHECK_EQ(fields->GetLength(), 1); - fields->Get(0)->SetObj(klass.get(), - manager->GetClassLinker()->FindPrimitiveClass('V')); - klass->SetStatus(mirror::Class::kStatusInitialized, soa.Self()); - } else { - manager->GetClassLinker()->EnsureInitialized(klass, true, true); + VLOG(compiler) << "Initializing: " << descriptor; + if (strcmp("Ljava/lang/Void;", descriptor) == 0) { + // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime. + ObjectLock<mirror::Class> lock(soa.Self(), &klass); + mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields(); + CHECK_EQ(fields->GetLength(), 1); + fields->Get(0)->SetObj<false>(klass.get(), + manager->GetClassLinker()->FindPrimitiveClass('V')); + klass->SetStatus(mirror::Class::kStatusInitialized, soa.Self()); + } else { + // TODO multithreading support. We should ensure the current compilation thread has + // exclusive access to the runtime and the transaction. To achieve this, we could use + // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity + // checks in Thread::AssertThreadSuspensionIsAllowable. + Runtime* const runtime = Runtime::Current(); + Transaction transaction; + + // Run the class initializer in transaction mode. + runtime->EnterTransactionMode(&transaction); + const mirror::Class::Status old_status = klass->GetStatus(); + bool success = manager->GetClassLinker()->EnsureInitialized(klass, true, true); + // TODO we detach transaction from runtime to indicate we quit the transactional + // mode which prevents the GC from visiting objects modified during the transaction. + // Ensure GC is not run so don't access freed objects when aborting transaction. + const char* old_casue = soa.Self()->StartAssertNoThreadSuspension("Transaction end"); + runtime->ExitTransactionMode(); + + if (!success) { + CHECK(soa.Self()->IsExceptionPending()); + ThrowLocation throw_location; + mirror::Throwable* exception = soa.Self()->GetException(&throw_location); + VLOG(compiler) << "Initialization of " << descriptor << " aborted because of " + << exception->Dump(); + soa.Self()->ClearException(); + transaction.Abort(); + CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored"; } + soa.Self()->EndAssertNoThreadSuspension(old_casue); } } } @@ -2206,18 +1804,20 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file, ThreadPool& thread_pool, TimingLogger& timings) { timings.NewSplit("InitializeNoClinit"); -#ifndef NDEBUG - // Sanity check blacklist descriptors. - if (IsImage()) { - for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) { - const char* descriptor = class_initializer_black_list[i]; - CHECK(IsValidDescriptor(descriptor)) << descriptor; - } - } -#endif ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, thread_pool); - context.ForAll(0, dex_file.NumClassDefs(), InitializeClass, thread_count_); + size_t thread_count; + if (IsImage()) { + // TODO: remove this when transactional mode supports multithreading. + thread_count = 1U; + } else { + thread_count = thread_count_; + } + context.ForAll(0, dex_file.NumClassDefs(), InitializeClass, thread_count); + if (IsImage()) { + // Prune garbage objects created during aborted transactions. + Runtime::Current()->GetHeap()->CollectGarbage(true); + } } void CompilerDriver::InitializeClasses(jobject class_loader, diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index e5dfb9d5d7..60beebb842 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -408,25 +408,25 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { class_linker->GetDexCaches().size()); int i = 0; for (DexCache* dex_cache : class_linker->GetDexCaches()) { - dex_caches->Set(i++, dex_cache); + dex_caches->Set<false>(i++, dex_cache); } // build an Object[] of the roots needed to restore the runtime SirtRef<ObjectArray<Object> > image_roots( self, ObjectArray<Object>::Alloc(self, object_array_class.get(), ImageHeader::kImageRootsMax)); - image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); - image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod()); - image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt()); - image_roots->Set(ImageHeader::kCalleeSaveMethod, - runtime->GetCalleeSaveMethod(Runtime::kSaveAll)); - image_roots->Set(ImageHeader::kRefsOnlySaveMethod, - runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)); - image_roots->Set(ImageHeader::kRefsAndArgsSaveMethod, - runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); - image_roots->Set(ImageHeader::kOatLocation, - String::AllocFromModifiedUtf8(self, oat_file_->GetLocation().c_str())); - image_roots->Set(ImageHeader::kDexCaches, dex_caches); - image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); + image_roots->Set<false>(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); + image_roots->Set<false>(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod()); + image_roots->Set<false>(ImageHeader::kDefaultImt, runtime->GetDefaultImt()); + image_roots->Set<false>(ImageHeader::kCalleeSaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kSaveAll)); + image_roots->Set<false>(ImageHeader::kRefsOnlySaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)); + image_roots->Set<false>(ImageHeader::kRefsAndArgsSaveMethod, + runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); + image_roots->Set<false>(ImageHeader::kOatLocation, + String::AllocFromModifiedUtf8(self, oat_file_->GetLocation().c_str())); + image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches); + image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots()); for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { CHECK(image_roots->Get(i) != NULL); } @@ -663,7 +663,7 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { void ImageWriter::FixupObjectArray(ObjectArray<Object>* orig, ObjectArray<Object>* copy) { for (int32_t i = 0; i < orig->GetLength(); ++i) { Object* element = orig->Get(i); - copy->SetWithoutChecksAndWriteBarrier(i, GetImageAddress(element)); + copy->SetWithoutChecksAndWriteBarrier<false>(i, GetImageAddress(element)); } } @@ -693,7 +693,7 @@ void ImageWriter::FixupFields(Object* orig, Object* ref = orig->GetFieldObject<Object>(byte_offset, false); // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the // image. - copy->SetFieldObjectWithoutWriteBarrier(byte_offset, GetImageAddress(ref), false); + copy->SetFieldObjectWithoutWriteBarrier<false>(byte_offset, GetImageAddress(ref), false); ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift); } } else { @@ -715,7 +715,7 @@ void ImageWriter::FixupFields(Object* orig, Object* ref = orig->GetFieldObject<Object>(field_offset, false); // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the // image. - copy->SetFieldObjectWithoutWriteBarrier(field_offset, GetImageAddress(ref), false); + copy->SetFieldObjectWithoutWriteBarrier<false>(field_offset, GetImageAddress(ref), false); } } } @@ -726,7 +726,7 @@ void ImageWriter::FixupFields(Object* orig, Object* ref = orig->GetFieldObject<Object>(field_offset, false); // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the // image. - copy->SetFieldObjectWithoutWriteBarrier(field_offset, GetImageAddress(ref), false); + copy->SetFieldObjectWithoutWriteBarrier<false>(field_offset, GetImageAddress(ref), false); } } diff --git a/runtime/Android.mk b/runtime/Android.mk index 223ae7c16f..b6b4ff917a 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -136,6 +136,7 @@ LIBART_COMMON_SRC_FILES := \ thread_pool.cc \ throw_location.cc \ trace.cc \ + transaction.cc \ profiler.cc \ utf.cc \ utils.cc \ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index fac1e1406e..3863ee5113 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2832,8 +2832,8 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccess& soa, jstring na return nullptr; } - interfaces_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); - throws_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws)); + interfaces_sfield->SetObject<false>(klass.get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); + throws_sfield->SetObject<false>(klass.get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws)); klass->SetStatus(mirror::Class::kStatusInitialized, self); } @@ -3144,7 +3144,11 @@ bool ClassLinker::InitializeClass(const SirtRef<mirror::Class>& klass, bool can_ SafeMap<uint32_t, mirror::ArtField*> field_map; ConstructFieldMap(dex_file, *dex_class_def, klass.get(), field_map); for (size_t i = 0; it.HasNext(); i++, it.Next()) { - it.ReadValueToField(field_map.Get(i)); + if (Runtime::Current()->IsActiveTransaction()) { + it.ReadValueToField<true>(field_map.Get(i)); + } else { + it.ReadValueToField<false>(field_map.Get(i)); + } } } } @@ -3530,7 +3534,7 @@ bool ClassLinker::LinkVirtualMethods(const SirtRef<mirror::Class>& klass) { super_mh.GetDeclaringClassDescriptor()); return false; } - vtable->Set(j, local_method); + vtable->Set<false>(j, local_method); local_method->SetMethodIndex(j); break; } else { @@ -3542,7 +3546,7 @@ bool ClassLinker::LinkVirtualMethods(const SirtRef<mirror::Class>& klass) { } if (j == actual_count) { // Not overriding, append. - vtable->Set(actual_count, local_method); + vtable->Set<false>(actual_count, local_method); local_method->SetMethodIndex(actual_count); actual_count += 1; } @@ -3576,7 +3580,7 @@ bool ClassLinker::LinkVirtualMethods(const SirtRef<mirror::Class>& klass) { } for (size_t i = 0; i < num_virtual_methods; ++i) { mirror::ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i); - vtable->Set(i, virtual_method); + vtable->Set<false>(i, virtual_method); virtual_method->SetMethodIndex(i & 0xFFFF); } klass->SetVTable(vtable.get()); @@ -3745,14 +3749,14 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass, PrettyMethod(interface_method).c_str()); return false; } - method_array->Set(j, vtable_method); + method_array->Set<false>(j, vtable_method); // Place method in imt if entry is empty, place conflict otherwise. uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize; if (imtable->Get(imt_index) == NULL) { - imtable->Set(imt_index, vtable_method); + imtable->Set<false>(imt_index, vtable_method); imtable_changed = true; } else { - imtable->Set(imt_index, Runtime::Current()->GetImtConflictMethod()); + imtable->Set<false>(imt_index, Runtime::Current()->GetImtConflictMethod()); } break; } @@ -3777,7 +3781,7 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass, // TODO: If a methods move then the miranda_list may hold stale references. miranda_list.push_back(miranda_method.get()); } - method_array->Set(j, miranda_method.get()); + method_array->Set<false>(j, miranda_method.get()); } } } @@ -3787,7 +3791,7 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass, mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod(); for (size_t i = 0; i < kImtSize; i++) { if (imtable->Get(i) == NULL) { - imtable->Set(i, imt_conflict_method); + imtable->Set<false>(i, imt_conflict_method); } } klass->SetImTable(imtable.get()); @@ -3823,7 +3827,7 @@ bool ClassLinker::LinkInterfaceMethods(const SirtRef<mirror::Class>& klass, method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda); method->SetMethodIndex(0xFFFF & (old_vtable_count + i)); klass->SetVirtualMethod(old_method_count + i, method); - vtable->Set(old_vtable_count + i, method); + vtable->Set<false>(old_vtable_count + i, method); } // TODO: do not assign to the vtable field until it is fully constructed. klass->SetVTable(vtable.get()); @@ -3929,7 +3933,7 @@ bool ClassLinker::LinkFields(const SirtRef<mirror::Class>& klass, bool is_static } grouped_and_sorted_fields.pop_front(); num_reference_fields++; - fields->Set(current_field, field); + fields->Set<false>(current_field, field); field->SetOffset(field_offset); field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t)); } @@ -3946,7 +3950,7 @@ bool ClassLinker::LinkFields(const SirtRef<mirror::Class>& klass, bool is_static if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) { continue; } - fields->Set(current_field++, field); + fields->Set<false>(current_field++, field); field->SetOffset(field_offset); // drop the consumed field grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i); @@ -3965,7 +3969,7 @@ bool ClassLinker::LinkFields(const SirtRef<mirror::Class>& klass, bool is_static FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); CHECK(type != Primitive::kPrimNot); // should only be working on primitive types - fields->Set(current_field, field); + fields->Set<false>(current_field, field); field->SetOffset(field_offset); field_offset = MemberOffset(field_offset.Uint32Value() + ((type == Primitive::kPrimLong || type == Primitive::kPrimDouble) @@ -4418,7 +4422,7 @@ void ClassLinker::SetClassRoot(ClassRoot class_root, mirror::Class* klass) { DCHECK(class_roots_ != NULL); DCHECK(class_roots_->Get(class_root) == NULL); - class_roots_->Set(class_root, klass); + class_roots_->Set<false>(class_root, klass); } } // namespace art diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index d9ef0c18ea..28ed6c4881 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -867,55 +867,56 @@ TEST_F(ClassLinkerTest, StaticFields) { EXPECT_STREQ(ClassHelper(s0->GetClass()).GetDescriptor(), "Ljava/lang/reflect/ArtField;"); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimBoolean); EXPECT_EQ(true, s0->GetBoolean(statics.get())); - s0->SetBoolean(statics.get(), false); + s0->SetBoolean<false>(statics.get(), false); mirror::ArtField* s1 = statics->FindStaticField("s1", "B"); fh.ChangeField(s1); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimByte); EXPECT_EQ(5, s1->GetByte(statics.get())); - s1->SetByte(statics.get(), 6); + s1->SetByte<false>(statics.get(), 6); mirror::ArtField* s2 = statics->FindStaticField("s2", "C"); fh.ChangeField(s2); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimChar); EXPECT_EQ('a', s2->GetChar(statics.get())); - s2->SetChar(statics.get(), 'b'); + s2->SetChar<false>(statics.get(), 'b'); mirror::ArtField* s3 = statics->FindStaticField("s3", "S"); fh.ChangeField(s3); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimShort); EXPECT_EQ(-536, s3->GetShort(statics.get())); - s3->SetShort(statics.get(), -535); + s3->SetShort<false>(statics.get(), -535); mirror::ArtField* s4 = statics->FindStaticField("s4", "I"); fh.ChangeField(s4); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimInt); EXPECT_EQ(2000000000, s4->GetInt(statics.get())); - s4->SetInt(statics.get(), 2000000001); + s4->SetInt<false>(statics.get(), 2000000001); mirror::ArtField* s5 = statics->FindStaticField("s5", "J"); fh.ChangeField(s5); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimLong); EXPECT_EQ(0x1234567890abcdefLL, s5->GetLong(statics.get())); - s5->SetLong(statics.get(), 0x34567890abcdef12LL); + s5->SetLong<false>(statics.get(), 0x34567890abcdef12LL); mirror::ArtField* s6 = statics->FindStaticField("s6", "F"); fh.ChangeField(s6); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimFloat); EXPECT_EQ(0.5, s6->GetFloat(statics.get())); - s6->SetFloat(statics.get(), 0.75); + s6->SetFloat<false>(statics.get(), 0.75); mirror::ArtField* s7 = statics->FindStaticField("s7", "D"); fh.ChangeField(s7); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimDouble); EXPECT_EQ(16777217, s7->GetDouble(statics.get())); - s7->SetDouble(statics.get(), 16777219); + s7->SetDouble<false>(statics.get(), 16777219); mirror::ArtField* s8 = statics->FindStaticField("s8", "Ljava/lang/String;"); fh.ChangeField(s8); EXPECT_TRUE(fh.GetTypeAsPrimitiveType() == Primitive::kPrimNot); EXPECT_TRUE(s8->GetObject(statics.get())->AsString()->Equals("android")); - s8->SetObject(s8->GetDeclaringClass(), mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot")); + s8->SetObject<false>(s8->GetDeclaringClass(), + mirror::String::AllocFromModifiedUtf8(soa.Self(), "robot")); // TODO: Remove EXPECT_FALSE when GCC can handle EXPECT_EQ // http://code.google.com/p/googletest/issues/detail?id=322 diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 733e8430c8..20ad372d3f 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1149,7 +1149,7 @@ JDWP::JdwpError Dbg::SetArrayElements(JDWP::ObjectId array_id, int offset, int c if (o == ObjectRegistry::kInvalidObject) { return JDWP::ERR_INVALID_OBJECT; } - oa->Set(offset + i, o); + oa->Set<false>(offset + i, o); } } @@ -1582,10 +1582,12 @@ static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId if (IsPrimitiveTag(tag)) { if (tag == JDWP::JT_DOUBLE || tag == JDWP::JT_LONG) { CHECK_EQ(width, 8); - f->Set64(o, value); + // Debugging can't use transactional mode (runtime only). + f->Set64<false>(o, value); } else { CHECK_LE(width, 4); - f->Set32(o, value); + // Debugging can't use transactional mode (runtime only). + f->Set32<false>(o, value); } } else { mirror::Object* v = gRegistry->Get<mirror::Object*>(value); @@ -1598,7 +1600,8 @@ static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId return JDWP::ERR_INVALID_OBJECT; } } - f->SetObject(o, v); + // Debugging can't use transactional mode (runtime only). + f->SetObject<false>(o, v); } return JDWP::ERR_NONE; diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index eaba7ebd8d..5e2b9ff5ad 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -1082,33 +1082,36 @@ void EncodedStaticFieldValueIterator::Next() { ptr_ += width; } +template<bool kTransactionActive> void EncodedStaticFieldValueIterator::ReadValueToField(mirror::ArtField* field) const { switch (type_) { - case kBoolean: field->SetBoolean(field->GetDeclaringClass(), jval_.z); break; - case kByte: field->SetByte(field->GetDeclaringClass(), jval_.b); break; - case kShort: field->SetShort(field->GetDeclaringClass(), jval_.s); break; - case kChar: field->SetChar(field->GetDeclaringClass(), jval_.c); break; - case kInt: field->SetInt(field->GetDeclaringClass(), jval_.i); break; - case kLong: field->SetLong(field->GetDeclaringClass(), jval_.j); break; - case kFloat: field->SetFloat(field->GetDeclaringClass(), jval_.f); break; - case kDouble: field->SetDouble(field->GetDeclaringClass(), jval_.d); break; - case kNull: field->SetObject(field->GetDeclaringClass(), NULL); break; + case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z); break; + case kByte: field->SetByte<kTransactionActive>(field->GetDeclaringClass(), jval_.b); break; + case kShort: field->SetShort<kTransactionActive>(field->GetDeclaringClass(), jval_.s); break; + case kChar: field->SetChar<kTransactionActive>(field->GetDeclaringClass(), jval_.c); break; + case kInt: field->SetInt<kTransactionActive>(field->GetDeclaringClass(), jval_.i); break; + case kLong: field->SetLong<kTransactionActive>(field->GetDeclaringClass(), jval_.j); break; + case kFloat: field->SetFloat<kTransactionActive>(field->GetDeclaringClass(), jval_.f); break; + case kDouble: field->SetDouble<kTransactionActive>(field->GetDeclaringClass(), jval_.d); break; + case kNull: field->SetObject<kTransactionActive>(field->GetDeclaringClass(), NULL); break; case kString: { CHECK(!kMovingFields); mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, *dex_cache_); - field->SetObject(field->GetDeclaringClass(), resolved); + field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved); break; } case kType: { CHECK(!kMovingFields); mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, *dex_cache_, *class_loader_); - field->SetObject(field->GetDeclaringClass(), resolved); + field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved); break; } default: UNIMPLEMENTED(FATAL) << ": type " << type_; } } +template void EncodedStaticFieldValueIterator::ReadValueToField<true>(mirror::ArtField* field) const; +template void EncodedStaticFieldValueIterator::ReadValueToField<false>(mirror::ArtField* field) const; CatchHandlerIterator::CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address) { handler_.address_ = -1; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 46df455a1f..e9d18b5e5e 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1142,6 +1142,7 @@ class EncodedStaticFieldValueIterator { ClassLinker* linker, const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void ReadValueToField(mirror::ArtField* field) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool HasNext() { return pos_ < array_size_; } diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 4e58a72fc7..4078cac077 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -162,7 +162,7 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char CHECK(soa.Self()->IsExceptionPending()); return zero; } - soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set(i, val); + soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set<false>(i, val); } } } diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 20532f4399..8b94b5abe1 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -136,6 +136,7 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_id gc::Heap* heap = Runtime::Current()->GetHeap(); return klass->Alloc<kInstrumented>(self, heap->GetCurrentAllocator()); } + DCHECK(klass != nullptr); return klass->Alloc<kInstrumented>(self, allocator_type); } @@ -377,10 +378,11 @@ EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite); #undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL template<InvokeType type, bool access_check> -static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object, +static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, + mirror::Object* this_object, mirror::ArtMethod* referrer, Thread* self) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - SirtRef<mirror::Object> sirt_this(self, this_object); + SirtRef<mirror::Object> sirt_this(self, type == kStatic ? nullptr : this_object); mirror::ArtMethod* resolved_method = class_linker->ResolveMethod(method_idx, referrer, type); if (UNLIKELY(resolved_method == nullptr)) { DCHECK(self->IsExceptionPending()); // Throw exception and unwind. diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc index 0b54b9c9f1..f48f1a9016 100644 --- a/runtime/entrypoints/portable/portable_field_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc @@ -30,13 +30,15 @@ extern "C" int32_t art_portable_set32_static_from_code(uint32_t field_idx, StaticPrimitiveWrite, sizeof(uint32_t)); if (LIKELY(field != NULL)) { - field->Set32(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(field->GetDeclaringClass(), new_value); return 0; } field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(), sizeof(uint32_t)); if (LIKELY(field != NULL)) { - field->Set32(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(field->GetDeclaringClass(), new_value); return 0; } return -1; @@ -48,13 +50,15 @@ extern "C" int32_t art_portable_set64_static_from_code(uint32_t field_idx, SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(uint64_t)); if (LIKELY(field != NULL)) { - field->Set64(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(field->GetDeclaringClass(), new_value); return 0; } field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(), sizeof(uint64_t)); if (LIKELY(field != NULL)) { - field->Set64(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(field->GetDeclaringClass(), new_value); return 0; } return -1; @@ -67,13 +71,15 @@ extern "C" int32_t art_portable_set_obj_static_from_code(uint32_t field_idx, mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticObjectWrite, sizeof(mirror::HeapReference<mirror::Object>)); if (LIKELY(field != NULL)) { - field->SetObj(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(field->GetDeclaringClass(), new_value); return 0; } field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, Thread::Current(), sizeof(mirror::HeapReference<mirror::Object>)); if (LIKELY(field != NULL)) { - field->SetObj(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(field->GetDeclaringClass(), new_value); return 0; } return -1; @@ -131,13 +137,15 @@ extern "C" int32_t art_portable_set32_instance_from_code(uint32_t field_idx, SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint32_t)); if (LIKELY(field != NULL)) { - field->Set32(obj, new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(obj, new_value); return 0; } field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(), sizeof(uint32_t)); if (LIKELY(field != NULL)) { - field->Set32(obj, new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(obj, new_value); return 0; } return -1; @@ -149,13 +157,15 @@ extern "C" int32_t art_portable_set64_instance_from_code(uint32_t field_idx, SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(uint64_t)); if (LIKELY(field != NULL)) { - field->Set64(obj, new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(obj, new_value); return 0; } field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(), sizeof(uint64_t)); if (LIKELY(field != NULL)) { - field->Set64(obj, new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(obj, new_value); return 0; } return -1; @@ -169,13 +179,15 @@ extern "C" int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx, mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite, sizeof(mirror::HeapReference<mirror::Object>)); if (LIKELY(field != NULL)) { - field->SetObj(obj, new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(obj, new_value); return 0; } field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, Thread::Current(), sizeof(mirror::HeapReference<mirror::Object>)); if (LIKELY(field != NULL)) { - field->SetObj(obj, new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(obj, new_value); return 0; } return -1; diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index 93ff7aa9fa..2d5c07d060 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -154,13 +154,15 @@ extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value, mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int32_t)); if (LIKELY(field != NULL)) { - field->Set32(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(field->GetDeclaringClass(), new_value); return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t)); if (LIKELY(field != NULL)) { - field->Set32(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(field->GetDeclaringClass(), new_value); return 0; // success } return -1; // failure @@ -172,13 +174,15 @@ extern "C" int artSet64StaticFromCode(uint32_t field_idx, mirror::ArtMethod* ref mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int64_t)); if (LIKELY(field != NULL)) { - field->Set64(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(field->GetDeclaringClass(), new_value); return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t)); if (LIKELY(field != NULL)) { - field->Set64(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(field->GetDeclaringClass(), new_value); return 0; // success } return -1; // failure @@ -192,7 +196,8 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v sizeof(mirror::HeapReference<mirror::Object>)); if (LIKELY(field != NULL)) { if (LIKELY(!FieldHelper(field).IsPrimitiveType())) { - field->SetObj(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(field->GetDeclaringClass(), new_value); return 0; // success } } @@ -200,7 +205,8 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self, sizeof(mirror::HeapReference<mirror::Object>)); if (LIKELY(field != NULL)) { - field->SetObj(field->GetDeclaringClass(), new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(field->GetDeclaringClass(), new_value); return 0; // success } return -1; // failure @@ -213,7 +219,8 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int32_t)); if (LIKELY(field != NULL && obj != NULL)) { - field->Set32(obj, new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(obj, new_value); return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); @@ -224,7 +231,8 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { - field->Set32(obj, new_value); + // Compiled code can't use transactional mode. + field->Set32<false>(obj, new_value); return 0; // success } } @@ -240,7 +248,8 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int64_t)); if (LIKELY(field != NULL && obj != NULL)) { - field->Set64(obj, new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(obj, new_value); return 0; // success } *sp = callee_save; @@ -252,7 +261,8 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj, ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { - field->Set64(obj, new_value); + // Compiled code can't use transactional mode. + field->Set64<false>(obj, new_value); return 0; // success } } @@ -267,7 +277,8 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite, sizeof(mirror::HeapReference<mirror::Object>)); if (LIKELY(field != NULL && obj != NULL)) { - field->SetObj(obj, new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(obj, new_value); return 0; // success } FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); @@ -278,7 +289,8 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionForFieldAccess(throw_location, field, false); } else { - field->SetObj(obj, new_value); + // Compiled code can't use transactional mode. + field->SetObj<false>(obj, new_value); return 0; // success } } diff --git a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc index c081768b73..e024a904a4 100644 --- a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc @@ -147,8 +147,8 @@ template<InvokeType type, bool access_check> uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object, mirror::ArtMethod* caller_method, Thread* self, mirror::ArtMethod** sp) { - mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, - access_check, type); + mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, + type); if (UNLIKELY(method == NULL)) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); method = FindMethodFromCode<type, access_check>(method_idx, this_object, caller_method, self); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index f9486c3506..012dabbeef 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -558,7 +558,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, // We came here because of sharpening. Ensure the dex cache is up-to-date on the method index // of the sharpened method. if (called->GetDexCacheResolvedMethods() == caller->GetDexCacheResolvedMethods()) { - caller->GetDexCacheResolvedMethods()->Set(called->GetDexMethodIndex(), called); + caller->GetDexCacheResolvedMethods()->Set<false>(called->GetDexMethodIndex(), called); } else { // Calling from one dex file to another, need to compute the method index appropriate to // the caller's dex file. Since we get here only if the original called was a runtime @@ -567,7 +567,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, uint32_t method_index = MethodHelper(called).FindDexMethodIndexInOtherDexFile(*dex_file, dex_method_idx); if (method_index != DexFile::kDexNoIndex) { - caller->GetDexCacheResolvedMethods()->Set(method_index, called); + caller->GetDexCacheResolvedMethods()->Set<false>(method_index, called); } } } diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index aad214af3e..625807048a 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -84,7 +84,11 @@ class ModUnionUpdateObjectReferencesVisitor { if (new_ref != ref) { // Use SetFieldObjectWithoutWriteBarrier to avoid card mark as an optimization which // reduces dirtied pages and improves performance. - obj->SetFieldObjectWithoutWriteBarrier(offset, new_ref, true); + if (Runtime::Current()->IsActiveTransaction()) { + obj->SetFieldObjectWithoutWriteBarrier<true>(offset, new_ref, true); + } else { + obj->SetFieldObjectWithoutWriteBarrier<false>(offset, new_ref, true); + } } } } diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index b1122b9b65..6d9496eb34 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -662,7 +662,9 @@ void SemiSpace::ScanObject(Object* obj) { // Don't need to mark the card since we updating the object address and not changing the // actual objects its pointing to. Using SetFieldObjectWithoutWriteBarrier is better in this // case since it does not dirty cards and use additional memory. - obj->SetFieldObjectWithoutWriteBarrier(offset, new_address, false); + // Since we do not change the actual object, we can safely use non-transactional mode. Also + // disable check as we could run inside a transaction. + obj->SetFieldObjectWithoutWriteBarrier<false, false>(offset, new_address, false); } }, kMovingClasses); mirror::Class* klass = obj->GetClass(); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f1126ef404..7613a3143e 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2335,7 +2335,7 @@ void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset, void Heap::SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) { DCHECK(reference != NULL); DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); - reference->SetFieldObject(reference_referent_offset_, referent, true); + reference->SetFieldObject<false, false>(reference_referent_offset_, referent, true); } mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) { diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc index 8af2725e1d..b02b8bb022 100644 --- a/runtime/gc/heap_test.cc +++ b/runtime/gc/heap_test.cc @@ -50,7 +50,7 @@ TEST_F(HeapTest, GarbageCollectClassLinkerInit) { for (size_t j = 0; j < 2048; ++j) { mirror::String* string = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"); // SIRT operator -> deferences the SIRT before running the method. - array->Set(j, string); + array->Set<false>(j, string); } } } diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc index fae4caccad..203701ff5e 100644 --- a/runtime/gc/reference_queue.cc +++ b/runtime/gc/reference_queue.cc @@ -49,12 +49,21 @@ void ReferenceQueue::EnqueuePendingReference(mirror::Object* ref) { DCHECK_NE(pending_next_offset.Uint32Value(), 0U); if (IsEmpty()) { // 1 element cyclic queue, ie: Reference ref = ..; ref.pendingNext = ref; - ref->SetFieldObject(pending_next_offset, ref, false); + if (Runtime::Current()->IsActiveTransaction()) { + ref->SetFieldObject<true>(pending_next_offset, ref, false); + } else { + ref->SetFieldObject<false>(pending_next_offset, ref, false); + } list_ = ref; } else { mirror::Object* head = list_->GetFieldObject<mirror::Object>(pending_next_offset, false); - ref->SetFieldObject(pending_next_offset, head, false); - list_->SetFieldObject(pending_next_offset, ref, false); + if (Runtime::Current()->IsActiveTransaction()) { + ref->SetFieldObject<true>(pending_next_offset, head, false); + list_->SetFieldObject<true>(pending_next_offset, ref, false); + } else { + ref->SetFieldObject<false>(pending_next_offset, head, false); + list_->SetFieldObject<false>(pending_next_offset, ref, false); + } } } @@ -71,10 +80,18 @@ mirror::Object* ReferenceQueue::DequeuePendingReference() { list_ = nullptr; } else { mirror::Object* next = head->GetFieldObject<mirror::Object>(pending_next_offset, false); - list_->SetFieldObject(pending_next_offset, next, false); + if (Runtime::Current()->IsActiveTransaction()) { + list_->SetFieldObject<true>(pending_next_offset, next, false); + } else { + list_->SetFieldObject<false>(pending_next_offset, next, false); + } ref = head; } - ref->SetFieldObject(pending_next_offset, nullptr, false); + if (Runtime::Current()->IsActiveTransaction()) { + ref->SetFieldObject<true>(pending_next_offset, nullptr, false); + } else { + ref->SetFieldObject<false>(pending_next_offset, nullptr, false); + } return ref; } @@ -131,7 +148,11 @@ void ReferenceQueue::EnqueueFinalizerReferences(ReferenceQueue& cleared_referenc // If the referent is non-null the reference must queuable. DCHECK(heap_->IsEnqueuable(ref)); // Move the updated referent to the zombie field. - ref->SetFieldObject(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false); + if (Runtime::Current()->IsActiveTransaction()) { + ref->SetFieldObject<true>(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false); + } else { + ref->SetFieldObject<false>(heap_->GetFinalizerReferenceZombieOffset(), forward_address, false); + } heap_->ClearReferenceReferent(ref); cleared_references.EnqueueReference(ref); } else if (referent != forward_address) { diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 5693747af5..cc49d6713f 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -28,24 +28,24 @@ namespace art { InternTable::InternTable() - : intern_table_lock_("InternTable lock"), is_dirty_(false), allow_new_interns_(true), - new_intern_condition_("New intern condition", intern_table_lock_) { + : is_dirty_(false), allow_new_interns_(true), + new_intern_condition_("New intern condition", *Locks::intern_table_lock_) { } size_t InternTable::Size() const { - MutexLock mu(Thread::Current(), intern_table_lock_); + MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); return strong_interns_.size() + weak_interns_.size(); } void InternTable::DumpForSigQuit(std::ostream& os) const { - MutexLock mu(Thread::Current(), intern_table_lock_); + MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); os << "Intern table: " << strong_interns_.size() << " strong; " << weak_interns_.size() << " weak\n"; } void InternTable::VisitRoots(RootCallback* callback, void* arg, bool only_dirty, bool clean_dirty) { - MutexLock mu(Thread::Current(), intern_table_lock_); + MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); if (!only_dirty || is_dirty_) { for (auto& strong_intern : strong_interns_) { strong_intern.second = @@ -61,7 +61,7 @@ void InternTable::VisitRoots(RootCallback* callback, void* arg, } mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t hash_code) { - intern_table_lock_.AssertHeld(Thread::Current()); + Locks::intern_table_lock_->AssertHeld(Thread::Current()); for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) { mirror::String* existing_string = it->second; if (existing_string->Equals(s)) { @@ -71,15 +71,38 @@ mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t ha return NULL; } +mirror::String* InternTable::InsertStrong(mirror::String* s, uint32_t hash_code) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + runtime->RecordStrongStringInsertion(s, hash_code); + } + return Insert(strong_interns_, s, hash_code); +} + +mirror::String* InternTable::InsertWeak(mirror::String* s, uint32_t hash_code) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + runtime->RecordWeakStringInsertion(s, hash_code); + } + return Insert(weak_interns_, s, hash_code); +} + mirror::String* InternTable::Insert(Table& table, mirror::String* s, uint32_t hash_code) { - intern_table_lock_.AssertHeld(Thread::Current()); + Locks::intern_table_lock_->AssertHeld(Thread::Current()); table.insert(std::make_pair(hash_code, s)); return s; } -void InternTable::Remove(Table& table, const mirror::String* s, - uint32_t hash_code) { - intern_table_lock_.AssertHeld(Thread::Current()); +void InternTable::RemoveWeak(mirror::String* s, uint32_t hash_code) { + Runtime* runtime = Runtime::Current(); + if (runtime->IsActiveTransaction()) { + runtime->RecordWeakStringRemoval(s, hash_code); + } + Remove(weak_interns_, s, hash_code); +} + +void InternTable::Remove(Table& table, mirror::String* s, uint32_t hash_code) { + Locks::intern_table_lock_->AssertHeld(Thread::Current()); for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) { if (it->second == s) { table.erase(it); @@ -88,6 +111,24 @@ void InternTable::Remove(Table& table, const mirror::String* s, } } +// Insert/remove methods used to undo changes made during an aborted transaction. +mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s, uint32_t hash_code) { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + return InsertStrong(s, hash_code); +} +mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s, uint32_t hash_code) { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + return InsertWeak(s, hash_code); +} +void InternTable::RemoveStrongFromTransaction(mirror::String* s, uint32_t hash_code) { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + Remove(strong_interns_, s, hash_code); +} +void InternTable::RemoveWeakFromTransaction(mirror::String* s, uint32_t hash_code) { + DCHECK(!Runtime::Current()->IsActiveTransaction()); + Remove(weak_interns_, s, hash_code); +} + static mirror::String* LookupStringFromImage(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace(); @@ -115,20 +156,20 @@ static mirror::String* LookupStringFromImage(mirror::String* s) void InternTable::AllowNewInterns() { Thread* self = Thread::Current(); - MutexLock mu(self, intern_table_lock_); + MutexLock mu(self, *Locks::intern_table_lock_); allow_new_interns_ = true; new_intern_condition_.Broadcast(self); } void InternTable::DisallowNewInterns() { Thread* self = Thread::Current(); - MutexLock mu(self, intern_table_lock_); + MutexLock mu(self, *Locks::intern_table_lock_); allow_new_interns_ = false; } mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { Thread* self = Thread::Current(); - MutexLock mu(self, intern_table_lock_); + MutexLock mu(self, *Locks::intern_table_lock_); DCHECK(s != NULL); uint32_t hash_code = s->GetHashCode(); @@ -150,20 +191,20 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { // Check the image for a match. mirror::String* image = LookupStringFromImage(s); if (image != NULL) { - return Insert(strong_interns_, image, hash_code); + return InsertStrong(image, hash_code); } // There is no match in the strong table, check the weak table. mirror::String* weak = Lookup(weak_interns_, s, hash_code); if (weak != NULL) { // A match was found in the weak table. Promote to the strong table. - Remove(weak_interns_, weak, hash_code); - return Insert(strong_interns_, weak, hash_code); + RemoveWeak(weak, hash_code); + return InsertStrong(weak, hash_code); } // No match in the strong table or the weak table. Insert into the strong // table. - return Insert(strong_interns_, s, hash_code); + return InsertStrong(s, hash_code); } // Check the strong table for a match. @@ -174,7 +215,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { // Check the image for a match. mirror::String* image = LookupStringFromImage(s); if (image != NULL) { - return Insert(weak_interns_, image, hash_code); + return InsertWeak(image, hash_code); } // Check the weak table for a match. mirror::String* weak = Lookup(weak_interns_, s, hash_code); @@ -182,7 +223,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { return weak; } // Insert into the weak table. - return Insert(weak_interns_, s, hash_code); + return InsertWeak(s, hash_code); } mirror::String* InternTable::InternStrong(int32_t utf16_length, @@ -211,13 +252,13 @@ mirror::String* InternTable::InternWeak(mirror::String* s) { } bool InternTable::ContainsWeak(mirror::String* s) { - MutexLock mu(Thread::Current(), intern_table_lock_); + MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); const mirror::String* found = Lookup(weak_interns_, s, s->GetHashCode()); return found == s; } void InternTable::SweepInternTableWeaks(IsMarkedCallback* callback, void* arg) { - MutexLock mu(Thread::Current(), intern_table_lock_); + MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) { mirror::Object* object = it->second; mirror::Object* new_object = callback(object, arg); diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 9f09fb917f..cc48480f50 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_INTERN_TABLE_H_ #include "base/mutex.h" +#include "locks.h" #include "object_callbacks.h" #include <map> @@ -26,6 +27,7 @@ namespace art { namespace mirror { class String; } // namespace mirror +class Transaction; /** * Used to intern strings. @@ -72,19 +74,38 @@ class InternTable { typedef std::multimap<int32_t, mirror::String*> Table; mirror::String* Insert(mirror::String* s, bool is_strong) + LOCKS_EXCLUDED(Locks::intern_table_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::String* Lookup(Table& table, mirror::String* s, uint32_t hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::String* Insert(Table& table, mirror::String* s, uint32_t hash_code); - void Remove(Table& table, const mirror::String* s, uint32_t hash_code); - - mutable Mutex intern_table_lock_; - bool is_dirty_ GUARDED_BY(intern_table_lock_); - bool allow_new_interns_ GUARDED_BY(intern_table_lock_); - ConditionVariable new_intern_condition_ GUARDED_BY(intern_table_lock_); - Table strong_interns_ GUARDED_BY(intern_table_lock_); - Table weak_interns_ GUARDED_BY(intern_table_lock_); + mirror::String* InsertStrong(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + mirror::String* InsertWeak(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + mirror::String* Insert(Table& table, mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void RemoveWeak(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void Remove(Table& table, mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + + // Transaction rollback access. + mirror::String* InsertStrongFromTransaction(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + mirror::String* InsertWeakFromTransaction(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void RemoveStrongFromTransaction(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void RemoveWeakFromTransaction(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + friend class Transaction; + + bool is_dirty_ GUARDED_BY(Locks::intern_table_lock_); + bool allow_new_interns_ GUARDED_BY(Locks::intern_table_lock_); + ConditionVariable new_intern_condition_ GUARDED_BY(Locks::intern_table_lock_); + Table strong_interns_ GUARDED_BY(Locks::intern_table_lock_); + Table weak_interns_ GUARDED_BY(Locks::intern_table_lock_); }; } // namespace art diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index c6faf4493f..a674571a4a 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -15,6 +15,7 @@ */ #include "interpreter_common.h" +#include <limits> namespace art { namespace interpreter { @@ -23,6 +24,10 @@ namespace interpreter { static void UnstartedRuntimeJni(Thread* self, ArtMethod* method, Object* receiver, uint32_t* args, JValue* result) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(Runtime::Current()->IsActiveTransaction()) << "Calling native method " + << PrettyMethod(method) + << " in unstarted runtime should only happen" + << " in a transaction"; std::string name(PrettyMethod(method)); if (name == "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") { result->SetL(NULL); @@ -73,13 +78,18 @@ static void UnstartedRuntimeJni(Thread* self, ArtMethod* method, jint newValue = args[4]; byte* raw_addr = reinterpret_cast<byte*>(obj) + offset; volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr); + // Check offset is 32bits to fit in MemberOffset. + CHECK_GE(offset, static_cast<jlong>(std::numeric_limits<int32_t>::min())); + CHECK_LE(offset, static_cast<jlong>(std::numeric_limits<int32_t>::max())); + Runtime::Current()->RecordWriteField32(obj, MemberOffset(offset), *address, true); // Note: android_atomic_release_cas() returns 0 on success, not failure. int r = android_atomic_release_cas(expectedValue, newValue, address); result->SetZ(r == 0); } else if (name == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") { Object* obj = reinterpret_cast<Object*>(args[0]); Object* newValue = reinterpret_cast<Object*>(args[3]); - obj->SetFieldObject(MemberOffset((static_cast<uint64_t>(args[2]) << 32) | args[1]), newValue, false); + obj->SetFieldObject<true>(MemberOffset((static_cast<uint64_t>(args[2]) << 32) | args[1]), + newValue, false); } else if (name == "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)") { mirror::Class* component = reinterpret_cast<Object*>(args[0])->AsClass(); Primitive::Type primitive_type = component->GetPrimitiveType(); @@ -89,7 +99,11 @@ static void UnstartedRuntimeJni(Thread* self, ArtMethod* method, Primitive::Type primitive_type = component->GetPrimitiveType(); result->SetI(Primitive::ComponentSize(primitive_type)); } else { - LOG(FATAL) << "Attempt to invoke native method in non-started runtime: " << name; + // Throw an exception so we can abort the transaction and undo every change. + ThrowLocation throw_location; + self->ThrowNewExceptionF(throw_location, "Ljava/lang/InternalError;", + "Attempt to invoke native method in non-started runtime: %s", + name.c_str()); } } @@ -293,21 +307,38 @@ static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::Code DCHECK(!shadow_frame.GetMethod()->IsAbstract()); DCHECK(!shadow_frame.GetMethod()->IsNative()); + bool transaction_active = Runtime::Current()->IsActiveTransaction(); if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) { // Enter the "without access check" interpreter. if (kInterpreterImplKind == kSwitchImpl) { - return ExecuteSwitchImpl<false>(self, mh, code_item, shadow_frame, result_register); + if (transaction_active) { + return ExecuteSwitchImpl<false, true>(self, mh, code_item, shadow_frame, result_register); + } else { + return ExecuteSwitchImpl<false, false>(self, mh, code_item, shadow_frame, result_register); + } } else { DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind); - return ExecuteGotoImpl<false>(self, mh, code_item, shadow_frame, result_register); + if (transaction_active) { + return ExecuteGotoImpl<false, true>(self, mh, code_item, shadow_frame, result_register); + } else { + return ExecuteGotoImpl<false, false>(self, mh, code_item, shadow_frame, result_register); + } } } else { // Enter the "with access check" interpreter. if (kInterpreterImplKind == kSwitchImpl) { - return ExecuteSwitchImpl<true>(self, mh, code_item, shadow_frame, result_register); + if (transaction_active) { + return ExecuteSwitchImpl<true, true>(self, mh, code_item, shadow_frame, result_register); + } else { + return ExecuteSwitchImpl<true, false>(self, mh, code_item, shadow_frame, result_register); + } } else { DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind); - return ExecuteGotoImpl<true>(self, mh, code_item, shadow_frame, result_register); + if (transaction_active) { + return ExecuteGotoImpl<true, true>(self, mh, code_item, shadow_frame, result_register); + } else { + return ExecuteGotoImpl<true, false>(self, mh, code_item, shadow_frame, result_register); + } } } } diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 0b959fb560..e37fb61f47 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -15,6 +15,7 @@ */ #include "interpreter_common.h" +#include "mirror/array-inl.h" namespace art { namespace interpreter { @@ -161,7 +162,7 @@ bool DoCall(ArtMethod* method, Thread* self, ShadowFrame& shadow_frame, return !self->IsExceptionPending(); } -template <bool is_range, bool do_access_check> +template <bool is_range, bool do_access_check, bool transaction_active> bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, Thread* self, JValue* result) { DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY || @@ -212,9 +213,9 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, for (int32_t i = 0; i < length; ++i) { size_t src_reg = is_range ? vregC + i : arg[i]; if (is_primitive_int_component) { - newArray->AsIntArray()->SetWithoutChecks(i, shadow_frame.GetVReg(src_reg)); + newArray->AsIntArray()->SetWithoutChecks<transaction_active>(i, shadow_frame.GetVReg(src_reg)); } else { - newArray->AsObjectArray<Object>()->SetWithoutChecks(i, shadow_frame.GetVRegReference(src_reg)); + newArray->AsObjectArray<Object>()->SetWithoutChecks<transaction_active>(i, shadow_frame.GetVRegReference(src_reg)); } } @@ -222,6 +223,50 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, return true; } +// TODO fix thread analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_). +template<typename T> +static void RecordArrayElementsInTransactionImpl(mirror::PrimitiveArray<T>* array, int32_t count) + NO_THREAD_SAFETY_ANALYSIS { + Runtime* runtime = Runtime::Current(); + for (int32_t i = 0; i < count; ++i) { + runtime->RecordWriteArray(array, i, array->GetWithoutChecks(i)); + } +} + +void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(Runtime::Current()->IsActiveTransaction()); + DCHECK(array != nullptr); + DCHECK_LE(count, array->GetLength()); + Primitive::Type primitive_component_type = array->GetClass()->GetComponentType()->GetPrimitiveType(); + switch (primitive_component_type) { + case Primitive::kPrimBoolean: + RecordArrayElementsInTransactionImpl(array->AsBooleanArray(), count); + break; + case Primitive::kPrimByte: + RecordArrayElementsInTransactionImpl(array->AsByteArray(), count); + break; + case Primitive::kPrimChar: + RecordArrayElementsInTransactionImpl(array->AsCharArray(), count); + break; + case Primitive::kPrimShort: + RecordArrayElementsInTransactionImpl(array->AsShortArray(), count); + break; + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + RecordArrayElementsInTransactionImpl(array->AsIntArray(), count); + break; + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + RecordArrayElementsInTransactionImpl(array->AsLongArray(), count); + break; + default: + LOG(FATAL) << "Unsupported primitive type " << primitive_component_type + << " in fill-array-data"; + break; + } +} + static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { @@ -341,15 +386,19 @@ EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true); #undef EXPLICIT_DO_CALL_TEMPLATE_DECL // Explicit DoFilledNewArray template function declarations. -#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ - bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst, \ - const ShadowFrame& shadow_frame, \ - Thread* self, JValue* result) -EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false); -EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true); -EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false); -EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true); +#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \ + bool DoFilledNewArray<_is_range_, _check, _transaction_active>(const Instruction* inst, \ + const ShadowFrame& shadow_frame, \ + Thread* self, JValue* result) +#define EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(_transaction_active) \ + EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false, _transaction_active); \ + EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true, _transaction_active); \ + EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false, _transaction_active); \ + EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true, _transaction_active) +EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(false); +EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL(true); +#undef EXPLICIT_DO_FILLED_NEW_ARRAY_ALL_TEMPLATE_DECL #undef EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL } // namespace interpreter diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 768ca336db..a03e420514 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -65,12 +65,12 @@ namespace interpreter { // External references to both interpreter implementations. -template<bool do_access_check> +template<bool do_access_check, bool transaction_active> extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register); -template<bool do_access_check> +template<bool do_access_check, bool transaction_active> extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register); @@ -83,6 +83,9 @@ static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANA ref->MonitorExit(self); } +void RecordArrayElementsInTransaction(mirror::Array* array, int32_t count) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Invokes the given method. This is part of the invocation support and is used by DoInvoke and // DoInvokeVirtualQuick functions. // Returns true on success, otherwise throws an exception and returns false. @@ -228,7 +231,7 @@ static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* ins // Handles iput-XXX and sput-XXX instructions. // Returns true on success, otherwise throws an exception and returns false. -template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check> +template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check, bool transaction_active> static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { bool do_assignability_check = do_access_check; @@ -254,22 +257,22 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimBoolean: - f->SetBoolean(obj, shadow_frame.GetVReg(vregA)); + f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA)); break; case Primitive::kPrimByte: - f->SetByte(obj, shadow_frame.GetVReg(vregA)); + f->SetByte<transaction_active>(obj, shadow_frame.GetVReg(vregA)); break; case Primitive::kPrimChar: - f->SetChar(obj, shadow_frame.GetVReg(vregA)); + f->SetChar<transaction_active>(obj, shadow_frame.GetVReg(vregA)); break; case Primitive::kPrimShort: - f->SetShort(obj, shadow_frame.GetVReg(vregA)); + f->SetShort<transaction_active>(obj, shadow_frame.GetVReg(vregA)); break; case Primitive::kPrimInt: - f->SetInt(obj, shadow_frame.GetVReg(vregA)); + f->SetInt<transaction_active>(obj, shadow_frame.GetVReg(vregA)); break; case Primitive::kPrimLong: - f->SetLong(obj, shadow_frame.GetVRegLong(vregA)); + f->SetLong<transaction_active>(obj, shadow_frame.GetVRegLong(vregA)); break; case Primitive::kPrimNot: { Object* reg = shadow_frame.GetVRegReference(vregA); @@ -286,7 +289,7 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, return false; } } - f->SetObj(obj, reg); + f->SetObj<transaction_active>(obj, reg); break; } default: @@ -297,7 +300,7 @@ static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, // Handles iput-quick, iput-wide-quick and iput-object-quick instructions. // Returns true on success, otherwise throws an exception and returns false. -template<Primitive::Type field_type> +template<Primitive::Type field_type, bool transaction_active> static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); if (UNLIKELY(obj == nullptr)) { @@ -311,13 +314,15 @@ static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instructio const uint32_t vregA = inst->VRegA_22c(inst_data); switch (field_type) { case Primitive::kPrimInt: - obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile); + obj->SetField32<transaction_active>(field_offset, shadow_frame.GetVReg(vregA), is_volatile); break; case Primitive::kPrimLong: - obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile); + obj->SetField64<transaction_active>(field_offset, shadow_frame.GetVRegLong(vregA), + is_volatile); break; case Primitive::kPrimNot: - obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile); + obj->SetFieldObject<transaction_active>(field_offset, shadow_frame.GetVRegReference(vregA), + is_volatile); break; default: LOG(FATAL) << "Unreachable: " << field_type; @@ -416,7 +421,7 @@ static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg, // Handles filled-new-array and filled-new-array-range instructions. // Returns true on success, otherwise throws an exception and returns false. -template <bool is_range, bool do_access_check> +template <bool is_range, bool do_access_check, bool transaction_active> bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, Thread* self, JValue* result); @@ -604,14 +609,16 @@ EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot); #undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL // Explicitly instantiate all DoFieldPut functions. -#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check) \ +#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check, _transaction_active) \ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \ - const Instruction* inst, uint16_t inst_data) + bool DoFieldPut<_find_type, _field_type, _do_check, _transaction_active>(Thread* self, const ShadowFrame& shadow_frame, \ + const Instruction* inst, uint16_t inst_data) #define EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(_find_type, _field_type) \ - EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false); \ - EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true); + EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false, false); \ + EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, false); \ + EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false, true); \ + EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, true); // iput-XXX EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean); @@ -657,14 +664,20 @@ EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-qui #undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL // Explicitly instantiate all DoIPutQuick functions. -#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type) \ - template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ - bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst, \ - uint16_t inst_data) - -EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick. -EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick. -EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick. +#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, _transaction_active) \ + template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \ + bool DoIPutQuick<_field_type, _transaction_active>(const ShadowFrame& shadow_frame, \ + const Instruction* inst, \ + uint16_t inst_data) + +#define EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(_field_type) \ + EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, false); \ + EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, true); + +EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick. +EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick. +EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick. +#undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL #undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL } // namespace interpreter diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index e8504b7a3f..d0bb0010cb 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -109,7 +109,7 @@ namespace interpreter { * ---------------------+---------------+ * */ -template<bool do_access_check> +template<bool do_access_check, bool transaction_active> JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { // Define handler tables: @@ -536,15 +536,17 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY) { - bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame, - self, &result_register); + bool success = + DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, + self, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY_RANGE) { - bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame, - self, &result_register); + bool success = + DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame, + self, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3); } HANDLE_INSTRUCTION_END(); @@ -567,6 +569,9 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* array->GetLength(), payload->element_count); HANDLE_PENDING_EXCEPTION(); } else { + if (transaction_active) { + RecordArrayElementsInTransaction(array, payload->element_count); + } uint32_t size_in_bytes = payload->element_count * payload->element_width; memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes); ADVANCE(3); @@ -1060,7 +1065,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); BooleanArray* array = a->AsBooleanArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -1079,7 +1084,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ByteArray* array = a->AsByteArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -1098,7 +1103,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); CharArray* array = a->AsCharArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -1117,7 +1122,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ShortArray* array = a->AsShortArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -1136,7 +1141,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); IntArray* array = a->AsIntArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -1155,7 +1160,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); LongArray* array = a->AsLongArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -1174,7 +1179,7 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data)); ObjectArray<Object>* array = a->AsObjectArray<Object>(); if (LIKELY(array->CheckIsValidIndex(index) && array->CheckAssignable(val))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); ADVANCE(2); } else { HANDLE_PENDING_EXCEPTION(); @@ -1286,103 +1291,103 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) { - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_BYTE) { - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_CHAR) { - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_SHORT) { - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT) { - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_WIDE) { - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_OBJECT) { - bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_QUICK) { - bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data); + bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) { - bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data); + bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) { - bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data); + bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) { - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_BYTE) { - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_CHAR) { - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_SHORT) { - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT) { - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_WIDE) { - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); HANDLE_INSTRUCTION_START(SPUT_OBJECT) { - bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2); } HANDLE_INSTRUCTION_END(); @@ -2390,13 +2395,21 @@ JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* // Explicit definitions of ExecuteGotoImpl. template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR -JValue ExecuteGotoImpl<true>(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); +JValue ExecuteGotoImpl<true, false>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR -JValue ExecuteGotoImpl<false>(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); +JValue ExecuteGotoImpl<false, false>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) +JValue ExecuteGotoImpl<true, true>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) +JValue ExecuteGotoImpl<false, true>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index e5d15b17ce..abee1dbec6 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -50,7 +50,7 @@ namespace interpreter { // Code to run before each dex instruction. #define PREAMBLE() -template<bool do_access_check> +template<bool do_access_check, bool transaction_active> JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame, JValue result_register) { bool do_assignability_check = do_access_check; @@ -449,15 +449,17 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem } case Instruction::FILLED_NEW_ARRAY: { PREAMBLE(); - bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame, - self, &result_register); + bool success = + DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self, + &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } case Instruction::FILLED_NEW_ARRAY_RANGE: { PREAMBLE(); - bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame, - self, &result_register); + bool success = + DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame, + self, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); break; } @@ -482,6 +484,9 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem HANDLE_PENDING_EXCEPTION(); break; } + if (transaction_active) { + RecordArrayElementsInTransaction(array, payload->element_count); + } uint32_t size_in_bytes = payload->element_count * payload->element_width; memcpy(array->GetRawData(payload->element_width, 0), payload->data, size_in_bytes); inst = inst->Next_3xx(); @@ -958,7 +963,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); BooleanArray* array = a->AsBooleanArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -977,7 +982,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ByteArray* array = a->AsByteArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -996,7 +1001,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); CharArray* array = a->AsCharArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -1015,7 +1020,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); ShortArray* array = a->AsShortArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -1034,7 +1039,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); IntArray* array = a->AsIntArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -1053,7 +1058,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); LongArray* array = a->AsLongArray(); if (LIKELY(array->CheckIsValidIndex(index))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -1072,7 +1077,7 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data)); ObjectArray<Object>* array = a->AsObjectArray<Object>(); if (LIKELY(array->CheckIsValidIndex(index) && array->CheckAssignable(val))) { - array->SetWithoutChecks(index, val); + array->SetWithoutChecks<transaction_active>(index, val); inst = inst->Next_2xx(); } else { HANDLE_PENDING_EXCEPTION(); @@ -1183,103 +1188,103 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem } case Instruction::IPUT_BOOLEAN: { PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_BYTE: { PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_CHAR: { PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_SHORT: { PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT: { PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_WIDE: { PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_OBJECT: { PREAMBLE(); - bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_QUICK: { PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data); + bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_WIDE_QUICK: { PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data); + bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::IPUT_OBJECT_QUICK: { PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data); + bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_BOOLEAN: { PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_BYTE: { PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_CHAR: { PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_SHORT: { PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT: { PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_WIDE: { PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } case Instruction::SPUT_OBJECT: { PREAMBLE(); - bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data); + bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, transaction_active>(self, shadow_frame, inst, inst_data); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); break; } @@ -2137,13 +2142,21 @@ JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem // Explicit definitions of ExecuteSwitchImpl. template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR -JValue ExecuteSwitchImpl<true>(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); +JValue ExecuteSwitchImpl<true, false>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR -JValue ExecuteSwitchImpl<false>(Thread* self, MethodHelper& mh, - const DexFile::CodeItem* code_item, - ShadowFrame& shadow_frame, JValue result_register); +JValue ExecuteSwitchImpl<false, false>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) +JValue ExecuteSwitchImpl<true, true>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); +template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) +JValue ExecuteSwitchImpl<false, true>(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame& shadow_frame, JValue result_register); } // namespace interpreter } // namespace art diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index f9bcc71f05..876de055ad 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -1551,7 +1551,7 @@ class JNI { mirror::Object* o = soa.Decode<mirror::Object*>(java_object); mirror::Object* v = soa.Decode<mirror::Object*>(java_value); mirror::ArtField* f = soa.DecodeField(fid); - f->SetObject(o, v); + f->SetObject<false>(o, v); } static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) { @@ -1559,7 +1559,7 @@ class JNI { ScopedObjectAccess soa(env); mirror::Object* v = soa.Decode<mirror::Object*>(java_value); mirror::ArtField* f = soa.DecodeField(fid); - f->SetObject(f->GetDeclaringClass(), v); + f->SetObject<false>(f->GetDeclaringClass(), v); } #define GET_PRIMITIVE_FIELD(fn, instance) \ @@ -1582,13 +1582,13 @@ class JNI { ScopedObjectAccess soa(env); \ mirror::Object* o = soa.Decode<mirror::Object*>(instance); \ mirror::ArtField* f = soa.DecodeField(fid); \ - f->Set ##fn(o, value) + f->Set ##fn <false>(o, value) #define SET_STATIC_PRIMITIVE_FIELD(fn, value) \ CHECK_NON_NULL_ARGUMENT(SetStatic #fn Field, fid); \ ScopedObjectAccess soa(env); \ mirror::ArtField* f = soa.DecodeField(fid); \ - f->Set ##fn(f->GetDeclaringClass(), value) + f->Set ##fn <false>(f->GetDeclaringClass(), value) static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fid) { GET_PRIMITIVE_FIELD(Boolean, obj); @@ -2085,7 +2085,7 @@ class JNI { mirror::ObjectArray<mirror::Object>* array = soa.Decode<mirror::ObjectArray<mirror::Object>*>(java_array); mirror::Object* value = soa.Decode<mirror::Object*>(java_value); - array->Set(index, value); + array->Set<false>(index, value); } static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) { @@ -2166,7 +2166,7 @@ class JNI { } else { for (jsize i = 0; i < length; ++i) { - result->SetWithoutChecks(i, initial_object); + result->SetWithoutChecks<false>(i, initial_object); } } } diff --git a/runtime/locks.cc b/runtime/locks.cc index d08206aafb..246e339ce9 100644 --- a/runtime/locks.cc +++ b/runtime/locks.cc @@ -33,6 +33,7 @@ Mutex* Locks::thread_suspend_count_lock_ = NULL; Mutex* Locks::trace_lock_ = NULL; Mutex* Locks::profiler_lock_ = NULL; Mutex* Locks::unexpected_signal_lock_ = NULL; +Mutex* Locks::intern_table_lock_ = NULL; void Locks::Init() { if (logging_lock_ != NULL) { @@ -49,6 +50,7 @@ void Locks::Init() { DCHECK(trace_lock_ != NULL); DCHECK(profiler_lock_ != NULL); DCHECK(unexpected_signal_lock_ != NULL); + DCHECK(intern_table_lock_ != NULL); } else { logging_lock_ = new Mutex("logging lock", kLoggingLock, true); abort_lock_ = new Mutex("abort lock", kAbortLock, true); @@ -76,6 +78,8 @@ void Locks::Init() { profiler_lock_ = new Mutex("profiler lock", kProfilerLock); DCHECK(unexpected_signal_lock_ == NULL); unexpected_signal_lock_ = new Mutex("unexpected signal lock", kUnexpectedSignalLock, true); + DCHECK(intern_table_lock_ == NULL); + intern_table_lock_ = new Mutex("InternTable lock", kInternTableLock); } } diff --git a/runtime/locks.h b/runtime/locks.h index d4fbd913f1..2f9810d05b 100644 --- a/runtime/locks.h +++ b/runtime/locks.h @@ -44,6 +44,8 @@ enum LockLevel { kDexFileMethodInlinerLock, kDexFileToMethodInlinerMapLock, kMarkSweepMarkStackLock, + kTransactionLogLock, + kInternTableLock, kDefaultMutexLevel, kMarkSweepLargeObjectLock, kPinTableLock, @@ -163,6 +165,9 @@ class Locks { // doesn't try to hold a higher level Mutex. #define DEFAULT_MUTEX_ACQUIRED_AFTER ACQUIRED_AFTER(classlinker_classes_lock_) + // Guards intern table. + static Mutex* intern_table_lock_ ACQUIRED_AFTER(classlinker_classes_lock_); + // Have an exclusive aborting thread. static Mutex* abort_lock_ ACQUIRED_AFTER(classlinker_classes_lock_); diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index c23234ee0b..2180857019 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -60,7 +60,8 @@ static Array* RecursiveCreateMultiArray(Thread* self, CHECK(self->IsExceptionPending()); return nullptr; } - new_array->AsObjectArray<Array>()->Set(i, sub_array); + // Use non-transactional mode without check. + new_array->AsObjectArray<Array>()->Set<false, false>(i, sub_array); } } return new_array.get(); diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 2e123ef215..7555975c41 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -20,6 +20,7 @@ #include "object.h" #include "object_callbacks.h" #include "gc/heap.h" +#include "runtime.h" #include "thread.h" namespace art { @@ -60,7 +61,9 @@ class MANAGED Array : public Object { void SetLength(int32_t length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK_GE(length, 0); - SetField32(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false); + // We use non transactional version since we can't undo this write. We also disable checking + // since it would fail during a transaction. + SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Array, length_), length, false, false); } static MemberOffset LengthOffset() { @@ -144,14 +147,34 @@ class MANAGED PrimitiveArray : public Array { } void Set(int32_t i, T value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (Runtime::Current()->IsActiveTransaction()) { + Set<true>(i, value); + } else { + Set<false>(i, value); + } + } + + // TODO fix thread safety analysis broken by the use of template. This should be + // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_). + template<bool kTransactionActive, bool kCheckTransaction = true> + void Set(int32_t i, T value) NO_THREAD_SAFETY_ANALYSIS { if (LIKELY(CheckIsValidIndex(i))) { - SetWithoutChecks(i, value); + SetWithoutChecks<kTransactionActive, kCheckTransaction>(i, value); } else { DCHECK(Thread::Current()->IsExceptionPending()); } } - void SetWithoutChecks(int32_t i, T value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // TODO fix thread safety analysis broken by the use of template. This should be + // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_). + template<bool kTransactionActive, bool kCheckTransaction = true> + void SetWithoutChecks(int32_t i, T value) NO_THREAD_SAFETY_ANALYSIS { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteArray(this, i, GetWithoutChecks(i)); + } DCHECK(CheckIsValidIndex(i)); GetData()[i] = value; } diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h index 530226b4dd..6253edd88c 100644 --- a/runtime/mirror/art_field-inl.h +++ b/runtime/mirror/art_field-inl.h @@ -37,7 +37,8 @@ inline Class* ArtField::GetDeclaringClass() { } inline void ArtField::SetDeclaringClass(Class *new_declaring_class) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_), new_declaring_class, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, declaring_class_), + new_declaring_class, false); } inline uint32_t ArtField::GetAccessFlags() { @@ -61,10 +62,11 @@ inline uint32_t ArtField::Get32(Object* object) { return object->GetField32(GetOffset(), IsVolatile()); } +template<bool kTransactionActive> inline void ArtField::Set32(Object* object, uint32_t new_value) { DCHECK(object != NULL) << PrettyField(this); DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted()); - object->SetField32(GetOffset(), new_value, IsVolatile()); + object->SetField32<kTransactionActive>(GetOffset(), new_value, IsVolatile()); } inline uint64_t ArtField::Get64(Object* object) { @@ -73,10 +75,11 @@ inline uint64_t ArtField::Get64(Object* object) { return object->GetField64(GetOffset(), IsVolatile()); } +template<bool kTransactionActive> inline void ArtField::Set64(Object* object, uint64_t new_value) { DCHECK(object != NULL) << PrettyField(this); DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted()); - object->SetField64(GetOffset(), new_value, IsVolatile()); + object->SetField64<kTransactionActive>(GetOffset(), new_value, IsVolatile()); } inline Object* ArtField::GetObj(Object* object) { @@ -85,10 +88,11 @@ inline Object* ArtField::GetObj(Object* object) { return object->GetFieldObject<Object>(GetOffset(), IsVolatile()); } +template<bool kTransactionActive> inline void ArtField::SetObj(Object* object, Object* new_value) { DCHECK(object != NULL) << PrettyField(this); DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted()); - object->SetFieldObject(GetOffset(), new_value, IsVolatile()); + object->SetFieldObject<kTransactionActive>(GetOffset(), new_value, IsVolatile()); } inline bool ArtField::GetBoolean(Object* object) { @@ -97,10 +101,11 @@ inline bool ArtField::GetBoolean(Object* object) { return Get32(object); } +template<bool kTransactionActive> inline void ArtField::SetBoolean(Object* object, bool z) { DCHECK_EQ(Primitive::kPrimBoolean, FieldHelper(this).GetTypeAsPrimitiveType()) << PrettyField(this); - Set32(object, z); + Set32<kTransactionActive>(object, z); } inline int8_t ArtField::GetByte(Object* object) { @@ -109,10 +114,11 @@ inline int8_t ArtField::GetByte(Object* object) { return Get32(object); } +template<bool kTransactionActive> inline void ArtField::SetByte(Object* object, int8_t b) { DCHECK_EQ(Primitive::kPrimByte, FieldHelper(this).GetTypeAsPrimitiveType()) << PrettyField(this); - Set32(object, b); + Set32<kTransactionActive>(object, b); } inline uint16_t ArtField::GetChar(Object* object) { @@ -121,10 +127,11 @@ inline uint16_t ArtField::GetChar(Object* object) { return Get32(object); } +template<bool kTransactionActive> inline void ArtField::SetChar(Object* object, uint16_t c) { DCHECK_EQ(Primitive::kPrimChar, FieldHelper(this).GetTypeAsPrimitiveType()) << PrettyField(this); - Set32(object, c); + Set32<kTransactionActive>(object, c); } inline int16_t ArtField::GetShort(Object* object) { @@ -133,42 +140,45 @@ inline int16_t ArtField::GetShort(Object* object) { return Get32(object); } +template<bool kTransactionActive> inline void ArtField::SetShort(Object* object, int16_t s) { DCHECK_EQ(Primitive::kPrimShort, FieldHelper(this).GetTypeAsPrimitiveType()) << PrettyField(this); - Set32(object, s); + Set32<kTransactionActive>(object, s); } inline int32_t ArtField::GetInt(Object* object) { -#ifndef NDEBUG - Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); - CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this); -#endif + if (kIsDebugBuild) { + Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); + CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this); + } return Get32(object); } +template<bool kTransactionActive> inline void ArtField::SetInt(Object* object, int32_t i) { -#ifndef NDEBUG - Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); - CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this); -#endif - Set32(object, i); + if (kIsDebugBuild) { + Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); + CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this); + } + Set32<kTransactionActive>(object, i); } inline int64_t ArtField::GetLong(Object* object) { -#ifndef NDEBUG - Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); - CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this); -#endif + if (kIsDebugBuild) { + Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); + CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this); + } return Get64(object); } +template<bool kTransactionActive> inline void ArtField::SetLong(Object* object, int64_t j) { -#ifndef NDEBUG - Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); - CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this); -#endif - Set64(object, j); + if (kIsDebugBuild) { + Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); + CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this); + } + Set64<kTransactionActive>(object, j); } inline float ArtField::GetFloat(Object* object) { @@ -179,12 +189,13 @@ inline float ArtField::GetFloat(Object* object) { return bits.GetF(); } +template<bool kTransactionActive> inline void ArtField::SetFloat(Object* object, float f) { DCHECK_EQ(Primitive::kPrimFloat, FieldHelper(this).GetTypeAsPrimitiveType()) << PrettyField(this); JValue bits; bits.SetF(f); - Set32(object, bits.GetI()); + Set32<kTransactionActive>(object, bits.GetI()); } inline double ArtField::GetDouble(Object* object) { @@ -195,12 +206,13 @@ inline double ArtField::GetDouble(Object* object) { return bits.GetD(); } +template<bool kTransactionActive> inline void ArtField::SetDouble(Object* object, double d) { DCHECK_EQ(Primitive::kPrimDouble, FieldHelper(this).GetTypeAsPrimitiveType()) << PrettyField(this); JValue bits; bits.SetD(d); - Set64(object, bits.GetJ()); + Set64<kTransactionActive>(object, bits.GetJ()); } inline Object* ArtField::GetObject(Object* object) { @@ -209,10 +221,11 @@ inline Object* ArtField::GetObject(Object* object) { return GetObj(object); } +template<bool kTransactionActive> inline void ArtField::SetObject(Object* object, Object* l) { DCHECK_EQ(Primitive::kPrimNot, FieldHelper(this).GetTypeAsPrimitiveType()) << PrettyField(this); - SetObj(object, l); + SetObj<kTransactionActive>(object, l); } } // namespace mirror diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc index 29aade9bd3..dd628eabee 100644 --- a/runtime/mirror/art_field.cc +++ b/runtime/mirror/art_field.cc @@ -42,14 +42,15 @@ void ArtField::ResetClass() { void ArtField::SetOffset(MemberOffset num_bytes) { DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous()); -#if 0 // TODO enable later in boot and under !NDEBUG - FieldHelper fh(this); - Primitive::Type type = fh.GetTypeAsPrimitiveType(); - if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) { - DCHECK_ALIGNED(num_bytes.Uint32Value(), 8); + if (kIsDebugBuild && Runtime::Current()->IsCompiler() && + !Runtime::Current()->UseCompileTimeClassPath()) { + Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType(); + if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) { + DCHECK_ALIGNED(num_bytes.Uint32Value(), 8); + } } -#endif - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value(), false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, offset_), num_bytes.Uint32Value(), false); } void ArtField::VisitRoots(RootCallback* callback, void* arg) { diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h index 716b736c3d..46287c3ea4 100644 --- a/runtime/mirror/art_field.h +++ b/runtime/mirror/art_field.h @@ -38,7 +38,8 @@ class MANAGED ArtField : public Object { uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, access_flags_), new_access_flags, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, access_flags_), new_access_flags, false); } bool IsPublic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -58,7 +59,8 @@ class MANAGED ArtField : public Object { } void SetDexFieldIndex(uint32_t new_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtField, field_dex_idx_), new_idx, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtField, field_dex_idx_), new_idx, false); } // Offset to field within an Object. @@ -74,30 +76,42 @@ class MANAGED ArtField : public Object { // field access, null object for static fields bool GetBoolean(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetBoolean(Object* object, bool z) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int8_t GetByte(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetByte(Object* object, int8_t b) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); uint16_t GetChar(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetChar(Object* object, uint16_t c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int16_t GetShort(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetShort(Object* object, int16_t s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int32_t GetInt(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetInt(Object* object, int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int64_t GetLong(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetLong(Object* object, int64_t j) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); float GetFloat(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetFloat(Object* object, float f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); double GetDouble(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetDouble(Object* object, double d) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Object* GetObject(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetObject(Object* object, Object* l) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Raw field accesses. uint32_t Get32(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void Set32(Object* object, uint32_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); uint64_t Get64(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void Set64(Object* object, uint64_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Object* GetObj(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetObj(Object* object, Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static Class* GetJavaLangReflectArtField() { diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 8ef3be821b..d347724658 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -36,7 +36,8 @@ inline Class* ArtMethod::GetDeclaringClass() { } inline void ArtMethod::SetDeclaringClass(Class *new_declaring_class) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_), new_declaring_class, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, declaring_class_), + new_declaring_class, false); } inline uint32_t ArtMethod::GetAccessFlags() { diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 67e6c7db3b..b5c87ad2e6 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -73,18 +73,18 @@ void ArtMethod::ResetClass() { } void ArtMethod::SetDexCacheStrings(ObjectArray<String>* new_dex_cache_strings) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_), - new_dex_cache_strings, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_strings_), + new_dex_cache_strings, false); } void ArtMethod::SetDexCacheResolvedMethods(ObjectArray<ArtMethod>* new_dex_cache_methods) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_), - new_dex_cache_methods, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_methods_), + new_dex_cache_methods, false); } void ArtMethod::SetDexCacheResolvedTypes(ObjectArray<Class>* new_dex_cache_classes) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_), - new_dex_cache_classes, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_cache_resolved_types_), + new_dex_cache_classes, false); } size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) { @@ -337,8 +337,8 @@ void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_ #else SetNativeMethod(reinterpret_cast<void*>(art_work_around_app_jni_bugs)); #endif - SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), - reinterpret_cast<const uint8_t*>(native_method), false); + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), + reinterpret_cast<const uint8_t*>(native_method), false); } } @@ -349,8 +349,8 @@ void ArtMethod::UnregisterNative(Thread* self) { } void ArtMethod::SetNativeMethod(const void* native_method) { - SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), - native_method, false); + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), + native_method, false); } } // namespace mirror diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index e678503e4c..71cc7af2e2 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -56,7 +56,8 @@ class MANAGED ArtMethod : public Object { uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, access_flags_), new_access_flags, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, access_flags_), new_access_flags, false); } // Approximate what kind of method call would be used for this method. @@ -156,7 +157,8 @@ class MANAGED ArtMethod : public Object { } void SetMethodIndex(uint16_t new_method_index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_), new_method_index, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_), new_method_index, false); } static MemberOffset MethodIndexOffset() { @@ -168,7 +170,8 @@ class MANAGED ArtMethod : public Object { } void SetCodeItemOffset(uint32_t new_code_off) { - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_code_item_offset_), new_code_off, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_code_item_offset_), new_code_off, false); } // Number of 32bit registers that would be required to hold all the arguments @@ -177,7 +180,8 @@ class MANAGED ArtMethod : public Object { uint32_t GetDexMethodIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetDexMethodIndex(uint32_t new_idx) { - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_), new_idx, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, dex_method_index_), new_idx, false); } ObjectArray<String>* GetDexCacheStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -216,9 +220,8 @@ class MANAGED ArtMethod : public Object { } void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) { - SetFieldPtr<EntryPointFromInterpreter*>( - OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_), - entry_point_from_interpreter, false); + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_), + entry_point_from_interpreter, false); } static MemberOffset EntryPointFromPortableCompiledCodeOffset() { @@ -230,8 +233,8 @@ class MANAGED ArtMethod : public Object { } void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code) { - SetFieldPtr<const void*>(EntryPointFromPortableCompiledCodeOffset(), - entry_point_from_portable_compiled_code, false); + SetFieldPtr<false>(EntryPointFromPortableCompiledCodeOffset(), + entry_point_from_portable_compiled_code, false); } static MemberOffset EntryPointFromQuickCompiledCodeOffset() { @@ -243,8 +246,8 @@ class MANAGED ArtMethod : public Object { } void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) { - SetFieldPtr<const void*>(EntryPointFromQuickCompiledCodeOffset(), - entry_point_from_quick_compiled_code, false); + SetFieldPtr<false>(EntryPointFromQuickCompiledCodeOffset(), + entry_point_from_quick_compiled_code, false); } @@ -277,8 +280,8 @@ class MANAGED ArtMethod : public Object { } void SetMappingTable(const uint8_t* mapping_table) { - SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_), - mapping_table, false); + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_mapping_table_), + mapping_table, false); } uint32_t GetOatMappingTableOffset(); @@ -292,8 +295,7 @@ class MANAGED ArtMethod : public Object { } void SetVmapTable(const uint8_t* vmap_table) { - SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, - false); + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_vmap_table_), vmap_table, false); } uint32_t GetOatVmapTableOffset(); @@ -304,7 +306,7 @@ class MANAGED ArtMethod : public Object { return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), false); } void SetNativeGcMap(const uint8_t* data) { - SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false); + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data, false); } // When building the oat need a convenient place to stuff the offset of the native GC map. @@ -319,8 +321,9 @@ class MANAGED ArtMethod : public Object { } void SetFrameSizeInBytes(size_t new_frame_size_in_bytes) { - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_), - new_frame_size_in_bytes, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_), + new_frame_size_in_bytes, false); } size_t GetReturnPcOffsetInBytes() { @@ -358,8 +361,9 @@ class MANAGED ArtMethod : public Object { } void SetCoreSpillMask(uint32_t core_spill_mask) { - // Computed during compilation - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_core_spill_mask_), core_spill_mask, false); + // Computed during compilation. + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_core_spill_mask_), core_spill_mask, false); } uint32_t GetFpSpillMask() { @@ -367,8 +371,9 @@ class MANAGED ArtMethod : public Object { } void SetFpSpillMask(uint32_t fp_spill_mask) { - // Computed during compilation - SetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_fp_spill_mask_), fp_spill_mask, false); + // Computed during compilation. + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_fp_spill_mask_), fp_spill_mask, false); } // Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index a5f743ba7c..4c2bdb02dd 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -64,8 +64,8 @@ inline void Class::SetDirectMethods(ObjectArray<ArtMethod>* new_direct_methods) DCHECK(NULL == GetFieldObject<ObjectArray<ArtMethod> >( OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), false)); DCHECK_NE(0, new_direct_methods->GetLength()); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), - new_direct_methods, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), + new_direct_methods, false); } inline ArtMethod* Class::GetDirectMethod(int32_t i) { @@ -77,7 +77,7 @@ inline void Class::SetDirectMethod(uint32_t i, ArtMethod* f) // TODO: uint16_t ObjectArray<ArtMethod>* direct_methods = GetFieldObject<ObjectArray<ArtMethod> >( OFFSET_OF_OBJECT_MEMBER(Class, direct_methods_), false); - direct_methods->Set(i, f); + direct_methods->Set<false>(i, f); } // Returns the number of static, private, and constructor methods. @@ -93,10 +93,10 @@ inline ObjectArray<ArtMethod>* Class::GetVirtualMethods() { inline void Class::SetVirtualMethods(ObjectArray<ArtMethod>* new_virtual_methods) { // TODO: we reassign virtual methods to grow the table for miranda - // methods.. they should really just be assigned once + // methods.. they should really just be assigned once. DCHECK_NE(0, new_virtual_methods->GetLength()); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_), - new_virtual_methods, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_), + new_virtual_methods, false); } inline uint32_t Class::NumVirtualMethods() { @@ -118,7 +118,7 @@ inline void Class::SetVirtualMethod(uint32_t i, ArtMethod* f) // TODO: uint16_t ObjectArray<ArtMethod>* virtual_methods = GetFieldObject<ObjectArray<ArtMethod> >( OFFSET_OF_OBJECT_MEMBER(Class, virtual_methods_), false); - virtual_methods->Set(i, f); + virtual_methods->Set<false>(i, f); } inline ObjectArray<ArtMethod>* Class::GetVTable() { @@ -132,7 +132,7 @@ inline ObjectArray<ArtMethod>* Class::GetVTableDuringLinking() { } inline void Class::SetVTable(ObjectArray<ArtMethod>* new_vtable) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false); } inline ObjectArray<ArtMethod>* Class::GetImTable() { @@ -140,7 +140,7 @@ inline ObjectArray<ArtMethod>* Class::GetImTable() { } inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false); } inline bool Class::Implements(Class* klass) { @@ -347,7 +347,7 @@ inline int32_t Class::GetIfTableCount() { } inline void Class::SetIfTable(IfTable* new_iftable) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable, false); } inline ObjectArray<ArtField>* Class::GetIFields() { @@ -359,7 +359,7 @@ inline void Class::SetIFields(ObjectArray<ArtField>* new_ifields) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(NULL == GetFieldObject<ObjectArray<ArtField> >( OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false)); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_), new_ifields, false); } inline ObjectArray<ArtField>* Class::GetSFields() { @@ -371,7 +371,7 @@ inline void Class::SetSFields(ObjectArray<ArtField>* new_sfields) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(NULL == GetFieldObject<ObjectArray<ArtField> >( OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false)); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, sfields_), new_sfields, false); } inline uint32_t Class::NumStaticFields() { @@ -387,7 +387,7 @@ inline void Class::SetStaticField(uint32_t i, ArtField* f) // TODO: uint16_t SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ObjectArray<ArtField>* sfields= GetFieldObject<ObjectArray<ArtField> >( OFFSET_OF_OBJECT_MEMBER(Class, sfields_), false); - sfields->Set(i, f); + sfields->Set<false>(i, f); } inline uint32_t Class::NumInstanceFields() { @@ -403,12 +403,16 @@ inline void Class::SetInstanceField(uint32_t i, ArtField* f) // TODO: uint16_t SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ObjectArray<ArtField>* ifields= GetFieldObject<ObjectArray<ArtField> >( OFFSET_OF_OBJECT_MEMBER(Class, ifields_), false); - ifields->Set(i, f); + ifields->Set<false>(i, f); } inline void Class::SetVerifyErrorClass(Class* klass) { CHECK(klass != NULL) << PrettyClass(this); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false); + } else { + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, verify_error_class_), klass, false); + } } inline uint32_t Class::GetAccessFlags() { @@ -425,7 +429,11 @@ inline String* Class::GetName() { return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Class, name_), false); } inline void Class::SetName(String* name) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false); + } else { + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false); + } } inline void Class::CheckObjectAlloc() { diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 99a35e30ac..6446d02ba3 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -113,7 +113,11 @@ void Class::SetStatus(Status new_status, Thread* self) { self->SetException(gc_safe_throw_location, old_exception.get()); } CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this); - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false); + } else { + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false); + } // Classes that are being resolved or initialized need to notify waiters that the class status // changed. See ClassLinker::EnsureResolved and ClassLinker::WaitForInitializeClass. if ((old_status >= kStatusResolved || new_status >= kStatusResolved) && @@ -123,7 +127,7 @@ void Class::SetStatus(Status new_status, Thread* self) { } void Class::SetDexCache(DexCache* new_dex_cache) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false); } void Class::SetClassSize(uint32_t new_class_size) { @@ -131,7 +135,8 @@ void Class::SetClassSize(uint32_t new_class_size) { DumpClass(LOG(ERROR), kDumpClassFullDetail); CHECK_GE(new_class_size, GetClassSize()) << " class=" << PrettyTypeOf(this); } - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false); } // Return the class' name. The exact format is bizarre, but it's the specified behavior for @@ -254,8 +259,9 @@ void Class::SetReferenceInstanceOffsets(uint32_t new_reference_offsets) { } CHECK_EQ((size_t)__builtin_popcount(new_reference_offsets), count); } - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), - new_reference_offsets, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_instance_offsets_), + new_reference_offsets, false); } void Class::SetReferenceStaticOffsets(uint32_t new_reference_offsets) { @@ -265,8 +271,9 @@ void Class::SetReferenceStaticOffsets(uint32_t new_reference_offsets) { CHECK_EQ((size_t)__builtin_popcount(new_reference_offsets), NumReferenceStaticFieldsDuringLinking()); } - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), - new_reference_offsets, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_), + new_reference_offsets, false); } bool Class::IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2) { @@ -332,7 +339,11 @@ bool Class::IsArtMethodClass() { } void Class::SetClassLoader(ClassLoader* new_class_loader) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false); + } else { + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false); + } } ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const Signature& signature) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 82c8264114..cd8504bb0c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -174,7 +174,8 @@ class MANAGED Class : public Object { uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetAccessFlags(uint32_t new_access_flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_), new_access_flags, false); } // Returns true if the class is an interface. @@ -275,7 +276,7 @@ class MANAGED Class : public Object { void SetPrimitiveType(Primitive::Type new_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t)); - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false); + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type, false); } // Returns true if the class is a primitive type. @@ -357,7 +358,11 @@ class MANAGED Class : public Object { void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(GetComponentType() == NULL); DCHECK(new_component_type != NULL); - SetFieldObject(ComponentTypeOffset(), new_component_type, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetFieldObject<true>(ComponentTypeOffset(), new_component_type, false); + } else { + SetFieldObject<false>(ComponentTypeOffset(), new_component_type, false); + } } size_t GetComponentSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -406,7 +411,8 @@ class MANAGED Class : public Object { void SetObjectSize(uint32_t new_object_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(!IsVariableSize()); - return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false); + // Not called within a transaction. + return SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size, false); } // Returns true if this class is in the same packages as that class. @@ -499,7 +505,7 @@ class MANAGED Class : public Object { false); DCHECK(old_super_class == nullptr || old_super_class == new_super_class); DCHECK(new_super_class != nullptr); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, super_class_), new_super_class, false); } bool HasSuperClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -672,7 +678,9 @@ class MANAGED Class : public Object { } void SetNumReferenceInstanceFields(uint32_t new_num) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_), new_num, + false); } uint32_t GetReferenceInstanceOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -700,7 +708,8 @@ class MANAGED Class : public Object { } void SetNumReferenceStaticFields(uint32_t new_num) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_), new_num, false); } // Gets the static fields of the class. @@ -763,7 +772,13 @@ class MANAGED Class : public Object { } void SetClinitThreadId(pid_t new_clinit_thread_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetField32<true>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id, + false); + } else { + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), new_clinit_thread_id, + false); + } } Class* GetVerifyErrorClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -776,7 +791,8 @@ class MANAGED Class : public Object { } void SetDexClassDefIndex(uint16_t class_def_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_class_def_idx_), class_def_idx, false); } uint16_t GetDexTypeIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -784,7 +800,8 @@ class MANAGED Class : public Object { } void SetDexTypeIndex(uint16_t type_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx, false); + // Not called within a transaction. + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx, false); } static Class* GetJavaLangClass() { diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index fa0900c0ec..0a77db3187 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -44,12 +44,12 @@ void DexCache::Init(const DexFile* dex_file, CHECK(resolved_methods != nullptr); CHECK(resolved_fields != nullptr); - SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location, false); - SetFieldObject(StringsOffset(), strings, false); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types, false); - SetFieldObject(ResolvedMethodsOffset(), resolved_methods, false); - SetFieldObject(ResolvedFieldsOffset(), resolved_fields, false); + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location, false); + SetFieldObject<false>(StringsOffset(), strings, false); + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types, false); + SetFieldObject<false>(ResolvedMethodsOffset(), resolved_methods, false); + SetFieldObject<false>(ResolvedFieldsOffset(), resolved_fields, false); Runtime* runtime = Runtime::Current(); if (runtime->HasResolutionMethod()) { @@ -57,7 +57,7 @@ void DexCache::Init(const DexFile* dex_file, ArtMethod* trampoline = runtime->GetResolutionMethod(); size_t length = resolved_methods->GetLength(); for (size_t i = 0; i < length; i++) { - resolved_methods->SetWithoutChecks(i, trampoline); + resolved_methods->SetWithoutChecks<false>(i, trampoline); } } } @@ -69,7 +69,7 @@ void DexCache::Fixup(ArtMethod* trampoline) { size_t length = resolved_methods->GetLength(); for (size_t i = 0; i < length; i++) { if (resolved_methods->GetWithoutChecks(i) == nullptr) { - resolved_methods->SetWithoutChecks(i, trampoline); + resolved_methods->SetWithoutChecks<false>(i, trampoline); } } } diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 99529f09b4..843f860185 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -90,6 +90,7 @@ class MANAGED DexCache : public Object { void SetResolvedString(uint32_t string_idx, String* resolved) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // TODO default transaction support. GetStrings()->Set(string_idx, resolved); } @@ -99,6 +100,7 @@ class MANAGED DexCache : public Object { void SetResolvedType(uint32_t type_idx, Class* resolved) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // TODO default transaction support. GetResolvedTypes()->Set(type_idx, resolved); } @@ -140,7 +142,7 @@ class MANAGED DexCache : public Object { } void SetDexFile(const DexFile* dex_file) { - return SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false); + return SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file, false); } private: diff --git a/runtime/mirror/iftable-inl.h b/runtime/mirror/iftable-inl.h index 9d5fa7475a..ec3e51495b 100644 --- a/runtime/mirror/iftable-inl.h +++ b/runtime/mirror/iftable-inl.h @@ -26,7 +26,7 @@ inline void IfTable::SetInterface(int32_t i, Class* interface) { DCHECK(interface != NULL); DCHECK(interface->IsInterface()); DCHECK(Get((i * kMax) + kInterface) == NULL); - Set((i * kMax) + kInterface, interface); + Set<false>((i * kMax) + kInterface, interface); } } // namespace mirror diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h index be83d034b8..bb4cd41717 100644 --- a/runtime/mirror/iftable.h +++ b/runtime/mirror/iftable.h @@ -52,7 +52,7 @@ class MANAGED IfTable : public ObjectArray<Object> { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(new_ma != NULL); DCHECK(Get((i * kMax) + kMethodArray) == NULL); - Set((i * kMax) + kMethodArray, new_ma); + Set<false>((i * kMax) + kMethodArray, new_ma); } size_t Count() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 2392561848..70291c1ad6 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -40,7 +40,10 @@ inline void Object::SetClass(Class* new_klass) { // new_klass may be NULL prior to class linker initialization. // We don't mark the card as this occurs as part of object allocation. Not all objects have // backing cards, such as large objects. - SetFieldObjectWithoutWriteBarrier(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false); + // We use non transactional version since we can't undo this write. We also disable checking as + // we may run in transaction mode here. + SetFieldObjectWithoutWriteBarrier<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, klass_), + new_klass, false, false); } inline LockWord Object::GetLockWord() { @@ -48,12 +51,14 @@ inline LockWord Object::GetLockWord() { } inline void Object::SetLockWord(LockWord new_val) { - SetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true); + // Force use of non-transactional mode and do not check. + SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true); } inline bool Object::CasLockWord(LockWord old_val, LockWord new_val) { - return CasField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), - new_val.GetValue()); + // Force use of non-transactional mode and do not check. + return CasField32<false, false>(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), + new_val.GetValue()); } inline uint32_t Object::GetLockOwnerThreadId() { @@ -199,6 +204,18 @@ inline LongArray* Object::AsLongArray() { return down_cast<LongArray*>(this); } +inline FloatArray* Object::AsFloatArray() { + DCHECK(GetClass()->IsArrayClass()); + DCHECK(GetClass()->GetComponentType()->IsPrimitiveFloat()); + return down_cast<FloatArray*>(this); +} + +inline DoubleArray* Object::AsDoubleArray() { + DCHECK(GetClass()->IsArrayClass()); + DCHECK(GetClass()->GetComponentType()->IsPrimitiveDouble()); + return down_cast<DoubleArray*>(this); +} + inline String* Object::AsString() { DCHECK(GetClass()->IsStringClass()); return down_cast<String*>(this); @@ -253,8 +270,16 @@ inline int32_t Object::GetField32(MemberOffset field_offset, bool is_volatile) { } } +template<bool kTransactionActive, bool kCheckTransaction> inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile, bool this_is_valid) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteField32(this, field_offset, GetField32(field_offset, is_volatile), + is_volatile); + } if (this_is_valid) { VerifyObject(this); } @@ -269,7 +294,14 @@ inline void Object::SetField32(MemberOffset field_offset, int32_t new_value, boo } } +template<bool kTransactionActive, bool kCheckTransaction> inline bool Object::CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true); + } VerifyObject(this); byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value(); volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr); @@ -289,8 +321,16 @@ inline int64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) { } } +template<bool kTransactionActive, bool kCheckTransaction> inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile, bool this_is_valid) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteField64(this, field_offset, GetField64(field_offset, is_volatile), + is_volatile); + } if (this_is_valid) { VerifyObject(this); } @@ -309,7 +349,14 @@ inline void Object::SetField64(MemberOffset field_offset, int64_t new_value, boo } } +template<bool kTransactionActive, bool kCheckTransaction> inline bool Object::CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteField64(this, field_offset, old_value, true); + } VerifyObject(this); byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value(); volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(raw_addr); @@ -331,8 +378,17 @@ inline T* Object::GetFieldObject(MemberOffset field_offset, bool is_volatile) { return result; } +template<bool kTransactionActive, bool kCheckTransaction> inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value, bool is_volatile, bool this_is_valid) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteFieldReference(this, field_offset, + GetFieldObject<Object>(field_offset, is_volatile), + true); + } if (this_is_valid) { VerifyObject(this); } @@ -349,16 +405,26 @@ inline void Object::SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, } } +template<bool kTransactionActive, bool kCheckTransaction> inline void Object::SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile, bool this_is_valid) { - SetFieldObjectWithoutWriteBarrier(field_offset, new_value, is_volatile, this_is_valid); + SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(field_offset, new_value, + is_volatile, + this_is_valid); if (new_value != nullptr) { CheckFieldAssignment(field_offset, new_value); Runtime::Current()->GetHeap()->WriteBarrierField(this, field_offset, new_value); } } +template<bool kTransactionActive, bool kCheckTransaction> inline bool Object::CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value) { + if (kCheckTransaction) { + DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction()); + } + if (kTransactionActive) { + Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true); + } VerifyObject(this); byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value(); volatile int32_t* addr = reinterpret_cast<volatile int32_t*>(raw_addr); diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 58749765a1..eb118c7d2f 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -23,6 +23,7 @@ #include "cutils/atomic-inline.h" #include "object_reference.h" #include "offsets.h" +#include "runtime.h" namespace art { @@ -130,6 +131,9 @@ class MANAGED Object { IntArray* AsIntArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); LongArray* AsLongArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + FloatArray* AsFloatArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + DoubleArray* AsDoubleArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + String* AsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Throwable* AsThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -155,12 +159,15 @@ class MANAGED Object { // Accessor for Java type fields. template<class T> T* GetFieldObject(MemberOffset field_offset, bool is_volatile) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true> void SetFieldObjectWithoutWriteBarrier(MemberOffset field_offset, Object* new_value, bool is_volatile, bool this_is_valid = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true> void SetFieldObject(MemberOffset field_offset, Object* new_value, bool is_volatile, bool this_is_valid = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive, bool kCheckTransaction = true> bool CasFieldObject(MemberOffset field_offset, Object* old_value, Object* new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -172,27 +179,35 @@ class MANAGED Object { int32_t GetField32(MemberOffset field_offset, bool is_volatile); + template<bool kTransactionActive, bool kCheckTransaction = true> void SetField32(MemberOffset field_offset, int32_t new_value, bool is_volatile, bool this_is_valid = true); + template<bool kTransactionActive, bool kCheckTransaction = true> bool CasField32(MemberOffset field_offset, int32_t old_value, int32_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int64_t GetField64(MemberOffset field_offset, bool is_volatile); + template<bool kTransactionActive, bool kCheckTransaction = true> void SetField64(MemberOffset field_offset, int64_t new_value, bool is_volatile, bool this_is_valid = true); + template<bool kTransactionActive, bool kCheckTransaction = true> bool CasField64(MemberOffset field_offset, int64_t old_value, int64_t new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - template<typename T> + template<bool kTransactionActive, bool kCheckTransaction = true, typename T> void SetFieldPtr(MemberOffset field_offset, T new_value, bool is_volatile, bool this_is_valid = true) { #ifndef __LP64__ - SetField32(field_offset, reinterpret_cast<int32_t>(new_value), is_volatile, this_is_valid); + SetField32<kTransactionActive, kCheckTransaction>(field_offset, + reinterpret_cast<int32_t>(new_value), + is_volatile, this_is_valid); #else - SetField64(field_offset, reinterpret_cast<int64_t>(new_value), is_volatile, this_is_valid); + SetField64<kTransactionActive, kCheckTransaction>(field_offset, + reinterpret_cast<int64_t>(new_value), + is_volatile, this_is_valid); #endif } diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index c3424793e9..521b6ce799 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -72,26 +72,39 @@ inline bool ObjectArray<T>::CheckAssignable(T* object) { template<class T> inline void ObjectArray<T>::Set(int32_t i, T* object) { + if (Runtime::Current()->IsActiveTransaction()) { + Set<true>(i, object); + } else { + Set<false>(i, object); + } +} + +template<class T> +template<bool kTransactionActive, bool kCheckTransaction> +inline void ObjectArray<T>::Set(int32_t i, T* object) { if (LIKELY(CheckIsValidIndex(i) && CheckAssignable(object))) { - SetFieldObject(OffsetOfElement(i), object, false); + SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false); } else { DCHECK(Thread::Current()->IsExceptionPending()); } } template<class T> +template<bool kTransactionActive, bool kCheckTransaction> inline void ObjectArray<T>::SetWithoutChecks(int32_t i, T* object) { DCHECK(CheckIsValidIndex(i)); DCHECK(CheckAssignable(object)); - SetFieldObject(OffsetOfElement(i), object, false); + SetFieldObject<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), object, false); } template<class T> +template<bool kTransactionActive, bool kCheckTransaction> inline void ObjectArray<T>::SetWithoutChecksAndWriteBarrier(int32_t i, T* object) { DCHECK(CheckIsValidIndex(i)); // TODO: enable this check. It fails when writing the image in ImageWriter::FixupObjectArray. // DCHECK(CheckAssignable(object)); - SetFieldObjectWithoutWriteBarrier(OffsetOfElement(i), object, false); + SetFieldObjectWithoutWriteBarrier<kTransactionActive, kCheckTransaction>(OffsetOfElement(i), + object, false); } template<class T> @@ -164,15 +177,15 @@ inline void ObjectArray<T>::AssignableCheckingMemcpy(int32_t dst_pos, ObjectArra o = src->GetWithoutChecks(src_pos + i); if (o == nullptr) { // Null is always assignable. - SetWithoutChecks(dst_pos + i, nullptr); + SetWithoutChecks<false>(dst_pos + i, nullptr); } else { // TODO: use the underlying class reference to avoid uncompression when not necessary. Class* o_class = o->GetClass(); if (LIKELY(lastAssignableElementClass == o_class)) { - SetWithoutChecks(dst_pos + i, o); + SetWithoutChecks<false>(dst_pos + i, o); } else if (LIKELY(dst_class->IsAssignableFrom(o_class))) { lastAssignableElementClass = o_class; - SetWithoutChecks(dst_pos + i, o); + SetWithoutChecks<false>(dst_pos + i, o); } else { // Can't put this element into the array, break to perform write-barrier and throw // exception. diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h index 347494e2ab..668b276dc9 100644 --- a/runtime/mirror/object_array.h +++ b/runtime/mirror/object_array.h @@ -40,12 +40,20 @@ class MANAGED ObjectArray : public Array { bool CheckAssignable(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void Set(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // TODO fix thread safety analysis: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_). + template<bool kTransactionActive, bool kCheckTransaction = true> + void Set(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS; // Set element without bound and element type checks, to be used in limited - // circumstances, such as during boot image writing - void SetWithoutChecks(int32_t i, T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // circumstances, such as during boot image writing. + // TODO fix thread safety analysis broken by the use of template. This should be + // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_). + template<bool kTransactionActive, bool kCheckTransaction = true> + void SetWithoutChecks(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS; + // TODO fix thread safety analysis broken by the use of template. This should be + // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_). + template<bool kTransactionActive, bool kCheckTransaction = true> + void SetWithoutChecksAndWriteBarrier(int32_t i, T* object) NO_THREAD_SAFETY_ANALYSIS; T* GetWithoutChecks(int32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index db9723b2de..40c37489cc 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -116,10 +116,10 @@ TEST_F(ObjectTest, AllocObjectArray) { EXPECT_EQ(2, oa->GetLength()); EXPECT_TRUE(oa->Get(0) == NULL); EXPECT_TRUE(oa->Get(1) == NULL); - oa->Set(0, oa.get()); + oa->Set<false>(0, oa.get()); EXPECT_TRUE(oa->Get(0) == oa.get()); EXPECT_TRUE(oa->Get(1) == NULL); - oa->Set(1, oa.get()); + oa->Set<false>(1, oa.get()); EXPECT_TRUE(oa->Get(0) == oa.get()); EXPECT_TRUE(oa->Get(1) == oa.get()); @@ -235,12 +235,12 @@ TEST_F(ObjectTest, CreateMultiArray) { SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass("I")); SirtRef<IntArray> dims(soa.Self(), IntArray::Alloc(soa.Self(), 1)); - dims->Set(0, 1); + dims->Set<false>(0, 1); Array* multi = Array::CreateMultiArray(soa.Self(), c, dims); EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[I")); EXPECT_EQ(1, multi->GetLength()); - dims->Set(0, -1); + dims->Set<false>(0, -1); multi = Array::CreateMultiArray(soa.Self(), c, dims); EXPECT_TRUE(soa.Self()->IsExceptionPending()); EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException(NULL)->GetClass()), @@ -250,8 +250,8 @@ TEST_F(ObjectTest, CreateMultiArray) { dims.reset(IntArray::Alloc(soa.Self(), 2)); for (int i = 1; i < 20; ++i) { for (int j = 0; j < 20; ++j) { - dims->Set(0, i); - dims->Set(1, j); + dims->Set<false>(0, i); + dims->Set<false>(1, j); multi = Array::CreateMultiArray(soa.Self(), c, dims); EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[[I")); EXPECT_EQ(i, multi->GetLength()); @@ -301,10 +301,10 @@ TEST_F(ObjectTest, StaticFieldFromCode) { EXPECT_TRUE(s0 != NULL); SirtRef<CharArray> char_array(soa.Self(), CharArray::Alloc(soa.Self(), 0)); - field->SetObj(field->GetDeclaringClass(), char_array.get()); + field->SetObj<false>(field->GetDeclaringClass(), char_array.get()); EXPECT_EQ(char_array.get(), field->GetObj(klass)); - field->SetObj(field->GetDeclaringClass(), NULL); + field->SetObj<false>(field->GetDeclaringClass(), NULL); EXPECT_EQ(NULL, field->GetObj(klass)); // TODO: more exhaustive tests of all 6 cases of ArtField::*FromCode @@ -387,8 +387,8 @@ TEST_F(ObjectTest, StringLength) { EXPECT_EQ(string->GetLength(), 7); EXPECT_EQ(string->GetUtfLength(), 7); - string->SetOffset(2); - string->SetCount(5); + string->SetOffset<false>(2); + string->SetCount<false>(5); EXPECT_TRUE(string->Equals("droid")); EXPECT_EQ(string->GetLength(), 5); EXPECT_EQ(string->GetUtfLength(), 5); diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index 2e33198884..6bc695dacc 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -46,18 +46,28 @@ StackTraceElement* StackTraceElement::Alloc(Thread* self, StackTraceElement* trace = down_cast<StackTraceElement*>(GetStackTraceElement()->AllocObject(self)); if (LIKELY(trace != NULL)) { - trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_), - declaring_class.get(), false); - trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_), - method_name.get(), false); - trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_), - file_name.get(), false); - trace->SetField32(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_), - line_number, false); + if (Runtime::Current()->IsActiveTransaction()) { + trace->Init<true>(declaring_class, method_name, file_name, line_number); + } else { + trace->Init<false>(declaring_class, method_name, file_name, line_number); + } } return trace; } +template<bool kTransactionActive> +void StackTraceElement::Init(SirtRef<String>& declaring_class, SirtRef<String>& method_name, + SirtRef<String>& file_name, int32_t line_number) { + SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_), + declaring_class.get(), false); + SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_), + method_name.get(), false); + SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_), + file_name.get(), false); + SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_), + line_number, false); +} + void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) { if (java_lang_StackTraceElement_ != nullptr) { java_lang_StackTraceElement_ = down_cast<Class*>( diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index 51817f6fa9..779ec4b780 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -67,6 +67,11 @@ class MANAGED StackTraceElement : public Object { HeapReference<String> method_name_; int32_t line_number_; + template<bool kTransactionActive> + void Init(SirtRef<String>& declaring_class, SirtRef<String>& method_name, + SirtRef<String>& file_name, int32_t line_number) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static Class* GetStackTraceElement() { DCHECK(java_lang_StackTraceElement_ != NULL); return java_lang_StackTraceElement_; diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index 6f4ead9a7a..cd63c39b1d 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -33,7 +33,7 @@ CharArray* String::GetCharArray() { return GetFieldObject<CharArray>(ValueOffset(), false); } -void String::ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +void String::ComputeHashCode() { SetHashCode(ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength())); } @@ -59,9 +59,10 @@ int32_t String::FastIndexOf(int32_t ch, int32_t start) { return -1; } -void String::SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +template<bool kTransactionActive> +void String::SetArray(CharArray* new_array) { DCHECK(new_array != NULL); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(String, array_), new_array, false); + SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, array_), new_array, false); } // TODO: get global references for these @@ -169,8 +170,13 @@ String* String::Alloc(Thread* self, const SirtRef<CharArray>& array) { // Hold reference in case AllocObject causes GC. String* string = down_cast<String*>(GetJavaLangString()->AllocObject(self)); if (LIKELY(string != nullptr)) { - string->SetArray(array.get()); - string->SetCount(array->GetLength()); + if (Runtime::Current()->IsActiveTransaction()) { + string->SetArray<true>(array.get()); + string->SetCount<true>(array->GetLength()); + } else { + string->SetArray<false>(array.get()); + string->SetCount<false>(array->GetLength()); + } } return string; } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 57ec314f14..1340e7d39c 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -112,19 +112,23 @@ class MANAGED String : public Object { private: void SetHashCode(int32_t new_hash_code) { + // Hash code is invariant so use non-transactional mode. Also disable check as we may run inside + // a transaction. DCHECK_EQ(0, GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), false)); - SetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false); + SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false); } + template<bool kTransactionActive> void SetCount(int32_t new_count) { DCHECK_LE(0, new_count); - SetField32(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count, false); + SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count, false); } + template<bool kTransactionActive> void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK_LE(0, new_offset); DCHECK_GE(GetLength(), new_offset); - SetField32(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset, false); + SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset, false); } static String* Alloc(Thread* self, int32_t utf16_length) @@ -133,6 +137,7 @@ class MANAGED String : public Object { static String* Alloc(Thread* self, const SirtRef<CharArray>& array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template<bool kTransactionActive> void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index a57bd43875..fef7d36837 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -38,7 +38,11 @@ void Throwable::SetCause(Throwable* cause) { Throwable* current_cause = GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), false); CHECK(current_cause == NULL || current_cause == this); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false); + } else { + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_), cause, false); + } } bool Throwable::IsCheckedException() { diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h index de71957c84..c1438d7ed5 100644 --- a/runtime/mirror/throwable.h +++ b/runtime/mirror/throwable.h @@ -31,7 +31,13 @@ namespace mirror { class MANAGED Throwable : public Object { public: void SetDetailMessage(String* new_detail_message) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message, false); + if (Runtime::Current()->IsActiveTransaction()) { + SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message, + false); + } else { + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), new_detail_message, + false); + } } String* GetDetailMessage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(Throwable, detail_message_), false); diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 269a4a32b3..40aebfa7da 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -183,32 +183,32 @@ static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o, o = sirt_obj.get(); switch (FieldHelper(f).GetTypeAsPrimitiveType()) { case Primitive::kPrimBoolean: - f->SetBoolean(o, new_value.GetZ()); + f->SetBoolean<false>(o, new_value.GetZ()); break; case Primitive::kPrimByte: - f->SetByte(o, new_value.GetB()); + f->SetByte<false>(o, new_value.GetB()); break; case Primitive::kPrimChar: - f->SetChar(o, new_value.GetC()); + f->SetChar<false>(o, new_value.GetC()); break; case Primitive::kPrimDouble: - f->SetDouble(o, new_value.GetD()); + f->SetDouble<false>(o, new_value.GetD()); break; case Primitive::kPrimFloat: - f->SetFloat(o, new_value.GetF()); + f->SetFloat<false>(o, new_value.GetF()); break; case Primitive::kPrimInt: - f->SetInt(o, new_value.GetI()); + f->SetInt<false>(o, new_value.GetI()); break; case Primitive::kPrimLong: - f->SetLong(o, new_value.GetJ()); + f->SetLong<false>(o, new_value.GetJ()); break; case Primitive::kPrimShort: - f->SetShort(o, new_value.GetS()); + f->SetShort<false>(o, new_value.GetS()); break; case Primitive::kPrimNot: if (allow_references) { - f->SetObject(o, new_value.GetL()); + f->SetObject<false>(o, new_value.GetL()); break; } // Else fall through to report an error. diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 6727862ffe..ad0f317b2a 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -27,7 +27,8 @@ static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jint expectedValue, jint newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); - bool success = obj->CasField32(MemberOffset(offset), expectedValue, newValue); + // JNI must use non transactional mode. + bool success = obj->CasField32<false>(MemberOffset(offset), expectedValue, newValue); return success ? JNI_TRUE : JNI_FALSE; } @@ -35,7 +36,8 @@ static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong expectedValue, jlong newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); - bool success = obj->CasField64(MemberOffset(offset), expectedValue, newValue); + // JNI must use non transactional mode. + bool success = obj->CasField64<false>(MemberOffset(offset), expectedValue, newValue); return success ? JNI_TRUE : JNI_FALSE; } @@ -45,7 +47,8 @@ static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaOb mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue); mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); - bool success = obj->CasFieldObject(MemberOffset(offset), expectedValue, newValue); + // JNI must use non transactional mode. + bool success = obj->CasFieldObject<false>(MemberOffset(offset), expectedValue, newValue); return success ? JNI_TRUE : JNI_FALSE; } @@ -64,14 +67,16 @@ static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong o static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); - obj->SetField32(MemberOffset(offset), newValue, false); + // JNI must use non transactional mode. + obj->SetField32<false>(MemberOffset(offset), newValue, false); } static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); - obj->SetField32(MemberOffset(offset), newValue, true); + // JNI must use non transactional mode. + obj->SetField32<false>(MemberOffset(offset), newValue, true); } static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, @@ -79,7 +84,8 @@ static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong of ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); QuasiAtomic::MembarStoreStore(); - obj->SetField32(MemberOffset(offset), newValue, false); + // JNI must use non transactional mode. + obj->SetField32<false>(MemberOffset(offset), newValue, false); } static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) { @@ -97,14 +103,16 @@ static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); - obj->SetField64(MemberOffset(offset), newValue, false); + // JNI must use non transactional mode. + obj->SetField64<false>(MemberOffset(offset), newValue, false); } static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) { ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); - obj->SetField64(MemberOffset(offset), newValue, true); + // JNI must use non transactional mode. + obj->SetField64<false>(MemberOffset(offset), newValue, true); } static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, @@ -112,7 +120,8 @@ static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong o ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); QuasiAtomic::MembarStoreStore(); - obj->SetField64(MemberOffset(offset), newValue, false); + // JNI must use non transactional mode. + obj->SetField64<false>(MemberOffset(offset), newValue, false); } static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) { @@ -134,7 +143,8 @@ static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); - obj->SetFieldObject(MemberOffset(offset), newValue, false); + // JNI must use non transactional mode. + obj->SetFieldObject<false>(MemberOffset(offset), newValue, false); } static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, @@ -142,7 +152,8 @@ static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlon ScopedFastNativeObjectAccess soa(env); mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); - obj->SetFieldObject(MemberOffset(offset), newValue, true); + // JNI must use non transactional mode. + obj->SetFieldObject<false>(MemberOffset(offset), newValue, true); } static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, @@ -151,7 +162,8 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj); mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue); QuasiAtomic::MembarStoreStore(); - obj->SetFieldObject(MemberOffset(offset), newValue, false); + // JNI must use non transactional mode. + obj->SetFieldObject<false>(MemberOffset(offset), newValue, false); } static jint Unsafe_getArrayBaseOffsetForComponentType(JNIEnv* env, jclass, jobject component_class) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 6ca45e8ecc..e1b0ed423b 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -60,6 +60,7 @@ #include "thread.h" #include "thread_list.h" #include "trace.h" +#include "transaction.h" #include "profiler.h" #include "UniquePtr.h" #include "verifier/method_verifier.h" @@ -120,7 +121,8 @@ Runtime::Runtime() main_thread_group_(nullptr), system_thread_group_(nullptr), system_class_loader_(nullptr), - dump_gc_performance_on_shutdown_(false) { + dump_gc_performance_on_shutdown_(false), + preinitialization_transaction(nullptr) { for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = nullptr; } @@ -826,7 +828,8 @@ jobject CreateSystemClassLoader() { thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;"); CHECK(contextClassLoader != NULL); - contextClassLoader->SetObject(soa.Self()->GetPeer(), class_loader.get()); + // We can't run in a transaction yet. + contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), class_loader.get()); return env->NewGlobalRef(system_class_loader.get()); } @@ -1305,6 +1308,10 @@ void Runtime::VisitConcurrentRoots(RootCallback* callback, void* arg, bool only_ bool clean_dirty) { intern_table_->VisitRoots(callback, arg, only_dirty, clean_dirty); class_linker_->VisitRoots(callback, arg, only_dirty, clean_dirty); + // TODO: is it the right place ? + if (preinitialization_transaction != nullptr) { + preinitialization_transaction->VisitRoots(callback, arg); + } } void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) { @@ -1371,7 +1378,7 @@ mirror::ObjectArray<mirror::ArtMethod>* Runtime::CreateDefaultImt(ClassLinker* c SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, cl->AllocArtMethodArray(self, 64)); mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod(); for (size_t i = 0; i < static_cast<size_t>(imtable->GetLength()); i++) { - imtable->Set(i, imt_conflict_method); + imtable->Set<false>(i, imt_conflict_method); } return imtable.get(); } @@ -1547,4 +1554,74 @@ void Runtime::StartProfiler(const char *appDir, bool startImmediately) { BackgroundMethodSamplingProfiler::Start(profile_period_s_, profile_duration_s_, appDir, profile_interval_us_, profile_backoff_coefficient_, startImmediately); } + +// Transaction support. +// TODO move them to header file for inlining. +bool Runtime::IsActiveTransaction() const { + return preinitialization_transaction != nullptr; +} + +void Runtime::EnterTransactionMode(Transaction* transaction) { + DCHECK(IsCompiler()); + DCHECK(transaction != nullptr); + DCHECK(!IsActiveTransaction()); + preinitialization_transaction = transaction; +} + +void Runtime::ExitTransactionMode() { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction = nullptr; +} + +void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, + uint32_t value, bool is_volatile) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordWriteField32(obj, field_offset, value, is_volatile); +} + +void Runtime::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, + uint64_t value, bool is_volatile) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordWriteField64(obj, field_offset, value, is_volatile); +} + +void Runtime::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset, + mirror::Object* value, bool is_volatile) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordWriteFieldReference(obj, field_offset, value, is_volatile); +} + +void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordWriteArray(array, index, value); +} + +void Runtime::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordStrongStringInsertion(s, hash_code); +} + +void Runtime::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordWeakStringInsertion(s, hash_code); +} + +void Runtime::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordStrongStringRemoval(s, hash_code); +} + +void Runtime::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const { + DCHECK(IsCompiler()); + DCHECK(IsActiveTransaction()); + preinitialization_transaction->RecordWeakStringRemoval(s, hash_code); +} } // namespace art diff --git a/runtime/runtime.h b/runtime/runtime.h index 07f3d7d780..159de2ef2f 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -46,6 +46,7 @@ namespace gc { namespace mirror { class ArtMethod; class ClassLoader; + class Array; template<class T> class ObjectArray; template<class T> class PrimitiveArray; typedef PrimitiveArray<int8_t> ByteArray; @@ -65,6 +66,7 @@ class MonitorPool; class SignalCatcher; class ThreadList; class Trace; +class Transaction; class Runtime { public: @@ -474,6 +476,27 @@ class Runtime { void StartProfiler(const char *appDir, bool startImmediately = false); + // Transaction support. + bool IsActiveTransaction() const; + void EnterTransactionMode(Transaction* transaction); + void ExitTransactionMode(); + void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value, + bool is_volatile) const; + void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value, + bool is_volatile) const; + void RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset, + mirror::Object* value, bool is_volatile) const; + void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + private: static void InitPlatformSignalHandlers(); @@ -612,6 +635,9 @@ class Runtime { // If true, then we dump the GC cumulative timings on shutdown. bool dump_gc_performance_on_shutdown_; + // Transaction used for pre-initializing classes at compilation time. + Transaction* preinitialization_transaction; + DISALLOW_COPY_AND_ASSIGN(Runtime); }; diff --git a/runtime/thread.cc b/runtime/thread.cc index 3382811dc9..5728391b56 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -398,14 +398,11 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) // non-null value. However, because we can run without code // available (in the compiler, in tests), we manually assign the // fields the constructor should have set. - soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)-> - SetBoolean(opeer_, thread_is_daemon); - soa.DecodeField(WellKnownClasses::java_lang_Thread_group)-> - SetObject(opeer_, soa.Decode<mirror::Object*>(thread_group)); - soa.DecodeField(WellKnownClasses::java_lang_Thread_name)-> - SetObject(opeer_, soa.Decode<mirror::Object*>(thread_name.get())); - soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)-> - SetInt(opeer_, thread_priority); + if (runtime->IsActiveTransaction()) { + InitPeer<true>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority); + } else { + InitPeer<false>(soa, thread_is_daemon, thread_group, thread_name.get(), thread_priority); + } peer_thread_name.reset(GetThreadName(soa)); } // 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null. @@ -414,6 +411,19 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) } } +template<bool kTransactionActive> +void Thread::InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group, + jobject thread_name, jint thread_priority) { + soa.DecodeField(WellKnownClasses::java_lang_Thread_daemon)-> + SetBoolean<kTransactionActive>(opeer_, thread_is_daemon); + soa.DecodeField(WellKnownClasses::java_lang_Thread_group)-> + SetObject<kTransactionActive>(opeer_, soa.Decode<mirror::Object*>(thread_group)); + soa.DecodeField(WellKnownClasses::java_lang_Thread_name)-> + SetObject<kTransactionActive>(opeer_, soa.Decode<mirror::Object*>(thread_name)); + soa.DecodeField(WellKnownClasses::java_lang_Thread_priority)-> + SetInt<kTransactionActive>(opeer_, thread_priority); +} + void Thread::SetThreadName(const char* name) { name_->assign(name); ::art::SetThreadName(name); @@ -1020,7 +1030,11 @@ void Thread::Destroy() { RemoveFromThreadGroup(soa); // this.nativePeer = 0; - soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong(opeer_, 0); + if (Runtime::Current()->IsActiveTransaction()) { + soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong<true>(opeer_, 0); + } else { + soa.DecodeField(WellKnownClasses::java_lang_Thread_nativePeer)->SetLong<false>(opeer_, 0); + } Dbg::PostThreadDeath(self); // Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone @@ -1309,7 +1323,8 @@ class BuildInternalStackTraceVisitor : public StackVisitor { } // Save PC trace in last element of method trace, also places it into the // object graph. - method_trace->Set(depth, dex_pc_trace); + // We are called from native: use non-transactional mode. + method_trace->Set<false>(depth, dex_pc_trace); // Set the Object*s and assert that no thread suspension is now possible. const char* last_no_suspend_cause = self_->StartAssertNoThreadSuspension("Building internal stack trace"); @@ -1337,8 +1352,14 @@ class BuildInternalStackTraceVisitor : public StackVisitor { if (m->IsRuntimeMethod()) { return true; // Ignore runtime frames (in particular callee save). } - method_trace_->Set(count_, m); - dex_pc_trace_->Set(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc()); + // TODO dedup this code. + if (Runtime::Current()->IsActiveTransaction()) { + method_trace_->Set<true>(count_, m); + dex_pc_trace_->Set<true>(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc()); + } else { + method_trace_->Set<false>(count_, m); + dex_pc_trace_->Set<false>(count_, m->IsProxyMethod() ? DexFile::kDexNoIndex : GetDexPc()); + } ++count_; return true; } @@ -1461,7 +1482,8 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job if (obj == nullptr) { return nullptr; } - soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set(i, obj); + // We are called from native: use non-transactional mode. + soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set<false>(i, obj); } return result; } diff --git a/runtime/thread.h b/runtime/thread.h index 6c072ba952..48912d1738 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -591,6 +591,11 @@ class PACKED(4) Thread { void CreatePeer(const char* name, bool as_daemon, jobject thread_group); + template<bool kTransactionActive> + void InitPeer(ScopedObjectAccess& soa, jboolean thread_is_daemon, jobject thread_group, + jobject thread_name, jint thread_priority) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and // Dbg::Disconnected. ThreadState SetStateUnsafe(ThreadState new_state) { diff --git a/runtime/transaction.cc b/runtime/transaction.cc new file mode 100644 index 0000000000..6adcfec9f6 --- /dev/null +++ b/runtime/transaction.cc @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transaction.h" + +#include "base/stl_util.h" +#include "base/logging.h" +#include "gc/accounting/card_table-inl.h" +#include "intern_table.h" +#include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" + +#include <list> + +namespace art { + +// TODO: remove (only used for debugging purpose). +static constexpr bool kEnableTransactionStats = false; + +Transaction::Transaction() : log_lock_("transaction log lock", kTransactionLogLock) { + CHECK(Runtime::Current()->IsCompiler()); +} + +Transaction::~Transaction() { + if (kEnableTransactionStats) { + MutexLock mu(Thread::Current(), log_lock_); + size_t objects_count = object_logs_.size(); + size_t field_values_count = 0; + for (auto it : object_logs_) { + field_values_count += it.second.Size(); + } + size_t array_count = array_logs_.size(); + size_t array_values_count = 0; + for (auto it : array_logs_) { + array_values_count += it.second.Size(); + } + size_t string_count = intern_string_logs_.size(); + LOG(INFO) << "Transaction::~Transaction" + << ": objects_count=" << objects_count + << ", field_values_count=" << field_values_count + << ", array_count=" << array_count + << ", array_values_count=" << array_values_count + << ", string_count=" << string_count; + } +} + +void Transaction::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value, + bool is_volatile) { + DCHECK(obj != nullptr); + MutexLock mu(Thread::Current(), log_lock_); + ObjectLog& object_log = object_logs_[obj]; + object_log.Log32BitsValue(field_offset, value, is_volatile); +} + +void Transaction::RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value, + bool is_volatile) { + DCHECK(obj != nullptr); + MutexLock mu(Thread::Current(), log_lock_); + ObjectLog& object_log = object_logs_[obj]; + object_log.Log64BitsValue(field_offset, value, is_volatile); +} + +void Transaction::RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset, + mirror::Object* value, bool is_volatile) { + DCHECK(obj != nullptr); + MutexLock mu(Thread::Current(), log_lock_); + ObjectLog& object_log = object_logs_[obj]; + object_log.LogReferenceValue(field_offset, value, is_volatile); +} + +void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) { + DCHECK(array != nullptr); + DCHECK(array->IsArrayInstance()); + MutexLock mu(Thread::Current(), log_lock_); + ArrayLog& array_log = array_logs_[array]; + array_log.LogValue(index, value); +} + +void Transaction::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) { + DCHECK(s != nullptr); + InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kInsert); + LogInternedString(log); +} + +void Transaction::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) { + DCHECK(s != nullptr); + InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kInsert); + LogInternedString(log); +} + +void Transaction::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) { + InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kRemove); + LogInternedString(log); +} + +void Transaction::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) { + InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kRemove); + LogInternedString(log); +} + +void Transaction::LogInternedString(InternStringLog& log) { + Locks::intern_table_lock_->AssertExclusiveHeld(Thread::Current()); + MutexLock mu(Thread::Current(), log_lock_); + intern_string_logs_.push_front(log); +} + +void Transaction::Abort() { + CHECK(!Runtime::Current()->IsActiveTransaction()); + Thread* self = Thread::Current(); + self->AssertNoPendingException(); + MutexLock mu1(self, *Locks::intern_table_lock_); + MutexLock mu2(self, log_lock_); + UndoObjectModifications(); + UndoArrayModifications(); + UndoInternStringTableModifications(); +} + +void Transaction::UndoObjectModifications() { + // TODO we may not need to restore objects allocated during this transaction. Or we could directly + // remove them from the heap. + for (auto it : object_logs_) { + it.second.Undo(it.first); + } + object_logs_.clear(); +} + +void Transaction::UndoArrayModifications() { + // TODO we may not need to restore array allocated during this transaction. Or we could directly + // remove them from the heap. + for (auto it : array_logs_) { + it.second.Undo(it.first); + } + array_logs_.clear(); +} + +void Transaction::UndoInternStringTableModifications() { + InternTable* const intern_table = Runtime::Current()->GetInternTable(); + // We want to undo each operation from the most recent to the oldest. List has been filled so the + // most recent operation is at list begin so just have to iterate over it. + for (InternStringLog& string_log : intern_string_logs_) { + string_log.Undo(intern_table); + } + intern_string_logs_.clear(); +} + +void Transaction::VisitRoots(RootCallback* callback, void* arg) { + LOG(INFO) << "Transaction::VisitRoots"; + MutexLock mu(Thread::Current(), log_lock_); + VisitObjectLogs(callback, arg); + VisitArrayLogs(callback, arg); + VisitStringLogs(callback, arg); +} + +void Transaction::VisitObjectLogs(RootCallback* callback, void* arg) { + // List of moving roots. + typedef std::pair<mirror::Object*, mirror::Object*> ObjectPair; + std::list<ObjectPair> moving_roots; + + // Visit roots. + for (auto it : object_logs_) { + it.second.VisitRoots(callback, arg); + mirror::Object* old_root = it.first; + mirror::Object* new_root = callback(old_root, arg, 0, kRootUnknown); + if (new_root != old_root) { + moving_roots.push_back(std::make_pair(old_root, new_root)); + } + } + + // Update object logs with moving roots. + for (const ObjectPair& pair : moving_roots) { + mirror::Object* old_root = pair.first; + mirror::Object* new_root = pair.second; + auto old_root_it = object_logs_.find(old_root); + CHECK(old_root_it != object_logs_.end()); + CHECK(object_logs_.find(new_root) == object_logs_.end()); + object_logs_.insert(std::make_pair(new_root, old_root_it->second)); + object_logs_.erase(old_root_it); + } +} + +void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) { + // List of moving roots. + typedef std::pair<mirror::Array*, mirror::Array*> ArrayPair; + std::list<ArrayPair> moving_roots; + + for (auto it : array_logs_) { + mirror::Array* old_root = it.first; + if (old_root->IsObjectArray()) { + it.second.VisitRoots(callback, arg); + } + mirror::Array* new_root = down_cast<mirror::Array*>(callback(old_root, arg, 0, kRootUnknown)); + if (new_root != old_root) { + moving_roots.push_back(std::make_pair(old_root, new_root)); + } + } + + // Update array logs with moving roots. + for (const ArrayPair& pair : moving_roots) { + mirror::Array* old_root = pair.first; + mirror::Array* new_root = pair.second; + auto old_root_it = array_logs_.find(old_root); + CHECK(old_root_it != array_logs_.end()); + CHECK(array_logs_.find(new_root) == array_logs_.end()); + array_logs_.insert(std::make_pair(new_root, old_root_it->second)); + array_logs_.erase(old_root_it); + } +} + +void Transaction::VisitStringLogs(RootCallback* callback, void* arg) { + for (InternStringLog& log : intern_string_logs_) { + log.VisitRoots(callback, arg); + } +} + +void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) { + auto it = field_values_.find(offset.Uint32Value()); + if (it == field_values_.end()) { + ObjectLog::FieldValue field_value; + field_value.value = value; + field_value.is_volatile = is_volatile; + field_value.kind = ObjectLog::k32Bits; + field_values_.insert(std::make_pair(offset.Uint32Value(), field_value)); + } +} + +void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) { + auto it = field_values_.find(offset.Uint32Value()); + if (it == field_values_.end()) { + ObjectLog::FieldValue field_value; + field_value.value = value; + field_value.is_volatile = is_volatile; + field_value.kind = ObjectLog::k64Bits; + field_values_.insert(std::make_pair(offset.Uint32Value(), field_value)); + } +} + +void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) { + auto it = field_values_.find(offset.Uint32Value()); + if (it == field_values_.end()) { + ObjectLog::FieldValue field_value; + field_value.value = reinterpret_cast<uintptr_t>(obj); + field_value.is_volatile = is_volatile; + field_value.kind = ObjectLog::kReference; + field_values_.insert(std::make_pair(offset.Uint32Value(), field_value)); + } +} + +void Transaction::ObjectLog::Undo(mirror::Object* obj) { + for (auto& it : field_values_) { + // Garbage collector needs to access object's class and array's length. So we don't rollback + // these values. + MemberOffset field_offset(it.first); + if (field_offset.Uint32Value() == mirror::Class::ClassOffset().Uint32Value()) { + // Skip Object::class field. + continue; + } + if (obj->IsArrayInstance() && + field_offset.Uint32Value() == mirror::Array::LengthOffset().Uint32Value()) { + // Skip Array::length field. + continue; + } + FieldValue& field_value = it.second; + UndoFieldWrite(obj, field_offset, field_value); + } +} + +void Transaction::ObjectLog::UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset, + const FieldValue& field_value) { + // TODO We may want to abort a transaction while still being in transaction mode. In this case, + // we'd need to disable the check. + constexpr bool kCheckTransaction = true; + switch (field_value.kind) { + case k32Bits: + obj->SetField32<false, kCheckTransaction>(field_offset, static_cast<uint32_t>(field_value.value), + field_value.is_volatile); + break; + case k64Bits: + obj->SetField64<false, kCheckTransaction>(field_offset, field_value.value, + field_value.is_volatile); + break; + case kReference: + obj->SetFieldObject<false, kCheckTransaction>(field_offset, + reinterpret_cast<mirror::Object*>(field_value.value), + field_value.is_volatile); + break; + default: + LOG(FATAL) << "Unknown value kind " << field_value.kind; + break; + } +} + +void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) { + for (auto it : field_values_) { + FieldValue& field_value = it.second; + if (field_value.kind == ObjectLog::kReference) { + mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value)); + field_value.value = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown)); + } + } +} + +void Transaction::InternStringLog::Undo(InternTable* intern_table) { + DCHECK(intern_table != nullptr); + switch (string_op_) { + case InternStringLog::kInsert: { + switch (string_kind_) { + case InternStringLog::kStrongString: + intern_table->RemoveStrongFromTransaction(str_, hash_code_); + break; + case InternStringLog::kWeakString: + intern_table->RemoveWeakFromTransaction(str_, hash_code_); + break; + default: + LOG(FATAL) << "Unknown interned string kind"; + break; + } + break; + } + case InternStringLog::kRemove: { + switch (string_kind_) { + case InternStringLog::kStrongString: + intern_table->InsertStrongFromTransaction(str_, hash_code_); + break; + case InternStringLog::kWeakString: + intern_table->InsertWeakFromTransaction(str_, hash_code_); + break; + default: + LOG(FATAL) << "Unknown interned string kind"; + break; + } + break; + } + default: + LOG(FATAL) << "Unknown interned string op"; + break; + } +} + +void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) { + str_ = down_cast<mirror::String*>(callback(str_, arg, 0, kRootInternedString)); +} + +void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) { + auto it = array_values_.find(index); + if (it == array_values_.end()) { + array_values_.insert(std::make_pair(index, value)); + } +} + +void Transaction::ArrayLog::Undo(mirror::Array* array) { + DCHECK(array != nullptr); + DCHECK(array->IsArrayInstance()); + Primitive::Type type = array->GetClass()->GetComponentType()->GetPrimitiveType(); + for (auto it : array_values_) { + UndoArrayWrite(array, type, it.first, it.second); + } +} + +void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Primitive::Type array_type, + size_t index, uint64_t value) { + // TODO We may want to abort a transaction while still being in transaction mode. In this case, + // we'd need to disable the check. + switch (array_type) { + case Primitive::kPrimBoolean: + array->AsBooleanArray()->SetWithoutChecks<false>(index, static_cast<uint8_t>(value)); + break; + case Primitive::kPrimByte: + array->AsByteArray()->SetWithoutChecks<false>(index, static_cast<int8_t>(value)); + break; + case Primitive::kPrimChar: + array->AsCharArray()->SetWithoutChecks<false>(index, static_cast<uint16_t>(value)); + break; + case Primitive::kPrimShort: + array->AsShortArray()->SetWithoutChecks<false>(index, static_cast<int16_t>(value)); + break; + case Primitive::kPrimInt: + array->AsIntArray()->SetWithoutChecks<false>(index, static_cast<int32_t>(value)); + break; + case Primitive::kPrimFloat: + array->AsFloatArray()->SetWithoutChecks<false>(index, static_cast<float>(value)); + break; + case Primitive::kPrimLong: + array->AsLongArray()->SetWithoutChecks<false>(index, static_cast<int64_t>(value)); + break; + case Primitive::kPrimDouble: + array->AsDoubleArray()->SetWithoutChecks<false>(index, static_cast<double>(value)); + break; + case Primitive::kPrimNot: { + mirror::ObjectArray<mirror::Object>* obj_array = array->AsObjectArray<mirror::Object>(); + obj_array->SetWithoutChecks<false>(index, reinterpret_cast<mirror::Object*>( + static_cast<uintptr_t>(value))); + break; + } + default: + LOG(FATAL) << "Unsupported type " << array_type; + } +} + +void Transaction::ArrayLog::VisitRoots(RootCallback* callback, void* arg) { + for (auto& it : array_values_) { + mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(it.second)); + it.second = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown)); + } +} + +} // namespace art diff --git a/runtime/transaction.h b/runtime/transaction.h new file mode 100644 index 0000000000..68f95402c6 --- /dev/null +++ b/runtime/transaction.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_TRANSACTION_H_ +#define ART_RUNTIME_TRANSACTION_H_ + +#include "base/macros.h" +#include "base/mutex.h" +#include "locks.h" +#include "offsets.h" +#include "primitive.h" +#include "object_callbacks.h" +#include "safe_map.h" + +#include <list> +#include <map> + +namespace art { +namespace mirror { +class Array; +class Object; +class String; +} +class InternTable; + +class Transaction { + public: + Transaction(); + ~Transaction(); + + // Record object field changes. + void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value, + bool is_volatile) + LOCKS_EXCLUDED(log_lock_); + void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value, + bool is_volatile) + LOCKS_EXCLUDED(log_lock_); + void RecordWriteFieldReference(mirror::Object* obj, MemberOffset field_offset, + mirror::Object* value, bool is_volatile) + LOCKS_EXCLUDED(log_lock_); + + // Record array change. + void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) + LOCKS_EXCLUDED(log_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Record intern string table changes. + void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) + LOCKS_EXCLUDED(log_lock_); + void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) + LOCKS_EXCLUDED(log_lock_); + void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) + LOCKS_EXCLUDED(log_lock_); + void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) + LOCKS_EXCLUDED(log_lock_); + + // Abort transaction by undoing all recorded changes. + void Abort() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + LOCKS_EXCLUDED(log_lock_); + + void VisitRoots(RootCallback* callback, void* arg) + LOCKS_EXCLUDED(log_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + class ObjectLog { + public: + void Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile); + void Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile); + void LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile); + + void Undo(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VisitRoots(RootCallback* callback, void* arg); + + size_t Size() const { + return field_values_.size(); + } + + private: + enum FieldValueKind { + k32Bits, + k64Bits, + kReference + }; + struct FieldValue { + // TODO use JValue instead ? + uint64_t value; + FieldValueKind kind; + bool is_volatile; + }; + + void UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset, + const FieldValue& field_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Maps field's offset to its value. + std::map<uint32_t, FieldValue> field_values_; + }; + + class ArrayLog { + public: + void LogValue(size_t index, uint64_t value); + + void Undo(mirror::Array* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VisitRoots(RootCallback* callback, void* arg); + + size_t Size() const { + return array_values_.size(); + } + + private: + void UndoArrayWrite(mirror::Array* array, Primitive::Type array_type, size_t index, + uint64_t value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Maps index to value. + // TODO use JValue instead ? + std::map<size_t, uint64_t> array_values_; + }; + + class InternStringLog { + public: + enum StringKind { + kStrongString, + kWeakString + }; + enum StringOp { + kInsert, + kRemove + }; + InternStringLog(mirror::String* s, uint32_t hash_code, StringKind kind, StringOp op) + : str_(s), hash_code_(hash_code), string_kind_(kind), string_op_(op) { + } + + void Undo(InternTable* intern_table) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); + void VisitRoots(RootCallback* callback, void* arg); + + private: + mirror::String* str_; + uint32_t hash_code_; + StringKind string_kind_; + StringOp string_op_; + }; + + void LogInternedString(InternStringLog& log) + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) + LOCKS_EXCLUDED(log_lock_); + + void UndoObjectModifications() + EXCLUSIVE_LOCKS_REQUIRED(log_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void UndoArrayModifications() + EXCLUSIVE_LOCKS_REQUIRED(log_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void UndoInternStringTableModifications() + EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) + EXCLUSIVE_LOCKS_REQUIRED(log_lock_); + + void VisitObjectLogs(RootCallback* callback, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(log_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VisitArrayLogs(RootCallback* callback, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(log_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void VisitStringLogs(RootCallback* callback, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(log_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + Mutex log_lock_ ACQUIRED_AFTER(Locks::intern_table_lock_); + std::map<mirror::Object*, ObjectLog> object_logs_ GUARDED_BY(log_lock_); + std::map<mirror::Array*, ArrayLog> array_logs_ GUARDED_BY(log_lock_); + std::list<InternStringLog> intern_string_logs_ GUARDED_BY(log_lock_); + + DISALLOW_COPY_AND_ASSIGN(Transaction); +}; + +} // namespace art + +#endif // ART_RUNTIME_TRANSACTION_H_ diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc new file mode 100644 index 0000000000..dcfa24bea1 --- /dev/null +++ b/runtime/transaction_test.cc @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common_test.h" +#include "invoke_arg_array_builder.h" +#include "mirror/array-inl.h" +#include "mirror/art_field-inl.h" +#include "mirror/art_method-inl.h" +#include "transaction.h" + +namespace art { + +class TransactionTest : public CommonTest { +}; + +TEST_F(TransactionTest, Object_class) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindSystemClass("Ljava/lang/Object;")); + ASSERT_TRUE(sirt_klass.get() != nullptr); + + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); + ASSERT_TRUE(sirt_obj.get() != nullptr); + ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); + Runtime::Current()->ExitTransactionMode(); + + // Aborting transaction must not clear the Object::class field. + transaction.Abort(); + EXPECT_EQ(sirt_obj->GetClass(), sirt_klass.get()); +} + +TEST_F(TransactionTest, Object_monitor) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindSystemClass("Ljava/lang/Object;")); + ASSERT_TRUE(sirt_klass.get() != nullptr); + SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); + ASSERT_TRUE(sirt_obj.get() != nullptr); + ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); + + // Lock object's monitor outside the transaction. + sirt_obj->MonitorEnter(soa.Self()); + uint32_t old_lock_word = sirt_obj->GetLockWord().GetValue(); + + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + // Unlock object's monitor inside the transaction. + sirt_obj->MonitorExit(soa.Self()); + uint32_t new_lock_word = sirt_obj->GetLockWord().GetValue(); + Runtime::Current()->ExitTransactionMode(); + + // Aborting transaction must not clear the Object::class field. + transaction.Abort(); + uint32_t aborted_lock_word = sirt_obj->GetLockWord().GetValue(); + EXPECT_NE(old_lock_word, new_lock_word); + EXPECT_EQ(aborted_lock_word, new_lock_word); +} + +TEST_F(TransactionTest, Array_length) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindSystemClass("[Ljava/lang/Object;")); + ASSERT_TRUE(sirt_klass.get() != nullptr); + + constexpr int32_t kArraySize = 2; + + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + + // Allocate an array during transaction. + SirtRef<mirror::Array> sirt_obj(soa.Self(), + mirror::Array::Alloc<false>(soa.Self(), sirt_klass.get(), kArraySize)); + ASSERT_TRUE(sirt_obj.get() != nullptr); + ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); + Runtime::Current()->ExitTransactionMode(); + + // Aborting transaction must not clear the Object::class field. + transaction.Abort(); + EXPECT_EQ(sirt_obj->GetLength(), kArraySize); +} + +TEST_F(TransactionTest, StaticFieldsTest) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::ClassLoader> class_loader( + soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))); + ASSERT_TRUE(class_loader.get() != nullptr); + + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindClass("LStaticFieldsTest;", class_loader)); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->EnsureInitialized(sirt_klass, true, true); + ASSERT_TRUE(sirt_klass->IsInitialized()); + + // Lookup fields. + mirror::ArtField* booleanField = sirt_klass->FindDeclaredStaticField("booleanField", "Z"); + ASSERT_TRUE(booleanField != nullptr); + ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean); + ASSERT_EQ(booleanField->GetBoolean(sirt_klass.get()), false); + + mirror::ArtField* byteField = sirt_klass->FindDeclaredStaticField("byteField", "B"); + ASSERT_TRUE(byteField != nullptr); + ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte); + ASSERT_EQ(byteField->GetByte(sirt_klass.get()), 0); + + mirror::ArtField* charField = sirt_klass->FindDeclaredStaticField("charField", "C"); + ASSERT_TRUE(charField != nullptr); + ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar); + ASSERT_EQ(charField->GetChar(sirt_klass.get()), 0u); + + mirror::ArtField* shortField = sirt_klass->FindDeclaredStaticField("shortField", "S"); + ASSERT_TRUE(shortField != nullptr); + ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort); + ASSERT_EQ(shortField->GetShort(sirt_klass.get()), 0); + + mirror::ArtField* intField = sirt_klass->FindDeclaredStaticField("intField", "I"); + ASSERT_TRUE(intField != nullptr); + ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt); + ASSERT_EQ(intField->GetInt(sirt_klass.get()), 0); + + mirror::ArtField* longField = sirt_klass->FindDeclaredStaticField("longField", "J"); + ASSERT_TRUE(longField != nullptr); + ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong); + ASSERT_EQ(longField->GetLong(sirt_klass.get()), static_cast<int64_t>(0)); + + mirror::ArtField* floatField = sirt_klass->FindDeclaredStaticField("floatField", "F"); + ASSERT_TRUE(floatField != nullptr); + ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat); + ASSERT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast<float>(0.0f)); + + mirror::ArtField* doubleField = sirt_klass->FindDeclaredStaticField("doubleField", "D"); + ASSERT_TRUE(doubleField != nullptr); + ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble); + ASSERT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0)); + + mirror::ArtField* objectField = sirt_klass->FindDeclaredStaticField("objectField", "Ljava/lang/Object;"); + ASSERT_TRUE(objectField != nullptr); + ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot); + ASSERT_EQ(objectField->GetObject(sirt_klass.get()), nullptr); + + // Create a java.lang.Object instance to set objectField. + SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;")); + ASSERT_TRUE(object_klass.get() != nullptr); + SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); + ASSERT_TRUE(sirt_obj.get() != nullptr); + ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); + + // Modify fields inside transaction and abort it. + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + booleanField->SetBoolean<true>(sirt_klass.get(), true); + byteField->SetByte<true>(sirt_klass.get(), 1); + charField->SetChar<true>(sirt_klass.get(), 1u); + shortField->SetShort<true>(sirt_klass.get(), 1); + intField->SetInt<true>(sirt_klass.get(), 1); + longField->SetLong<true>(sirt_klass.get(), 1); + floatField->SetFloat<true>(sirt_klass.get(), 1.0); + doubleField->SetDouble<true>(sirt_klass.get(), 1.0); + objectField->SetObject<true>(sirt_klass.get(), sirt_obj.get()); + Runtime::Current()->ExitTransactionMode(); + transaction.Abort(); + + // Check values have properly been restored to their original (default) value. + EXPECT_EQ(booleanField->GetBoolean(sirt_klass.get()), false); + EXPECT_EQ(byteField->GetByte(sirt_klass.get()), 0); + EXPECT_EQ(charField->GetChar(sirt_klass.get()), 0u); + EXPECT_EQ(shortField->GetShort(sirt_klass.get()), 0); + EXPECT_EQ(intField->GetInt(sirt_klass.get()), 0); + EXPECT_EQ(longField->GetLong(sirt_klass.get()), static_cast<int64_t>(0)); + EXPECT_EQ(floatField->GetFloat(sirt_klass.get()), static_cast<float>(0.0f)); + EXPECT_EQ(doubleField->GetDouble(sirt_klass.get()), static_cast<double>(0.0)); + EXPECT_EQ(objectField->GetObject(sirt_klass.get()), nullptr); +} + +TEST_F(TransactionTest, InstanceFieldsTest) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::ClassLoader> class_loader( + soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))); + ASSERT_TRUE(class_loader.get() != nullptr); + + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindClass("LInstanceFieldsTest;", class_loader)); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->EnsureInitialized(sirt_klass, true, true); + ASSERT_TRUE(sirt_klass->IsInitialized()); + + // Allocate an InstanceFieldTest object. + SirtRef<mirror::Object> sirt_instance(soa.Self(), sirt_klass->AllocObject(soa.Self())); + ASSERT_TRUE(sirt_instance.get() != nullptr); + + // Lookup fields. + mirror::ArtField* booleanField = sirt_klass->FindDeclaredInstanceField("booleanField", "Z"); + ASSERT_TRUE(booleanField != nullptr); + ASSERT_EQ(FieldHelper(booleanField).GetTypeAsPrimitiveType(), Primitive::kPrimBoolean); + ASSERT_EQ(booleanField->GetBoolean(sirt_instance.get()), false); + + mirror::ArtField* byteField = sirt_klass->FindDeclaredInstanceField("byteField", "B"); + ASSERT_TRUE(byteField != nullptr); + ASSERT_EQ(FieldHelper(byteField).GetTypeAsPrimitiveType(), Primitive::kPrimByte); + ASSERT_EQ(byteField->GetByte(sirt_instance.get()), 0); + + mirror::ArtField* charField = sirt_klass->FindDeclaredInstanceField("charField", "C"); + ASSERT_TRUE(charField != nullptr); + ASSERT_EQ(FieldHelper(charField).GetTypeAsPrimitiveType(), Primitive::kPrimChar); + ASSERT_EQ(charField->GetChar(sirt_instance.get()), 0u); + + mirror::ArtField* shortField = sirt_klass->FindDeclaredInstanceField("shortField", "S"); + ASSERT_TRUE(shortField != nullptr); + ASSERT_EQ(FieldHelper(shortField).GetTypeAsPrimitiveType(), Primitive::kPrimShort); + ASSERT_EQ(shortField->GetShort(sirt_instance.get()), 0); + + mirror::ArtField* intField = sirt_klass->FindDeclaredInstanceField("intField", "I"); + ASSERT_TRUE(intField != nullptr); + ASSERT_EQ(FieldHelper(intField).GetTypeAsPrimitiveType(), Primitive::kPrimInt); + ASSERT_EQ(intField->GetInt(sirt_instance.get()), 0); + + mirror::ArtField* longField = sirt_klass->FindDeclaredInstanceField("longField", "J"); + ASSERT_TRUE(longField != nullptr); + ASSERT_EQ(FieldHelper(longField).GetTypeAsPrimitiveType(), Primitive::kPrimLong); + ASSERT_EQ(longField->GetLong(sirt_instance.get()), static_cast<int64_t>(0)); + + mirror::ArtField* floatField = sirt_klass->FindDeclaredInstanceField("floatField", "F"); + ASSERT_TRUE(floatField != nullptr); + ASSERT_EQ(FieldHelper(floatField).GetTypeAsPrimitiveType(), Primitive::kPrimFloat); + ASSERT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast<float>(0.0f)); + + mirror::ArtField* doubleField = sirt_klass->FindDeclaredInstanceField("doubleField", "D"); + ASSERT_TRUE(doubleField != nullptr); + ASSERT_EQ(FieldHelper(doubleField).GetTypeAsPrimitiveType(), Primitive::kPrimDouble); + ASSERT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0)); + + mirror::ArtField* objectField = sirt_klass->FindDeclaredInstanceField("objectField", "Ljava/lang/Object;"); + ASSERT_TRUE(objectField != nullptr); + ASSERT_EQ(FieldHelper(objectField).GetTypeAsPrimitiveType(), Primitive::kPrimNot); + ASSERT_EQ(objectField->GetObject(sirt_instance.get()), nullptr); + + // Create a java.lang.Object instance to set objectField. + SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;")); + ASSERT_TRUE(object_klass.get() != nullptr); + SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); + ASSERT_TRUE(sirt_obj.get() != nullptr); + ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); + + // Modify fields inside transaction and abort it. + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + booleanField->SetBoolean<true>(sirt_instance.get(), true); + byteField->SetByte<true>(sirt_instance.get(), 1); + charField->SetChar<true>(sirt_instance.get(), 1u); + shortField->SetShort<true>(sirt_instance.get(), 1); + intField->SetInt<true>(sirt_instance.get(), 1); + longField->SetLong<true>(sirt_instance.get(), 1); + floatField->SetFloat<true>(sirt_instance.get(), 1.0); + doubleField->SetDouble<true>(sirt_instance.get(), 1.0); + objectField->SetObject<true>(sirt_instance.get(), sirt_obj.get()); + Runtime::Current()->ExitTransactionMode(); + transaction.Abort(); + + // Check values have properly been restored to their original (default) value. + EXPECT_EQ(booleanField->GetBoolean(sirt_instance.get()), false); + EXPECT_EQ(byteField->GetByte(sirt_instance.get()), 0); + EXPECT_EQ(charField->GetChar(sirt_instance.get()), 0u); + EXPECT_EQ(shortField->GetShort(sirt_instance.get()), 0); + EXPECT_EQ(intField->GetInt(sirt_instance.get()), 0); + EXPECT_EQ(longField->GetLong(sirt_instance.get()), static_cast<int64_t>(0)); + EXPECT_EQ(floatField->GetFloat(sirt_instance.get()), static_cast<float>(0.0f)); + EXPECT_EQ(doubleField->GetDouble(sirt_instance.get()), static_cast<double>(0.0)); + EXPECT_EQ(objectField->GetObject(sirt_instance.get()), nullptr); +} + + +TEST_F(TransactionTest, StaticArrayFieldsTest) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::ClassLoader> class_loader( + soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))); + ASSERT_TRUE(class_loader.get() != nullptr); + + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindClass("LStaticArrayFieldsTest;", class_loader)); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->EnsureInitialized(sirt_klass, true, true); + ASSERT_TRUE(sirt_klass->IsInitialized()); + + // Lookup fields. + mirror::ArtField* booleanArrayField = sirt_klass->FindDeclaredStaticField("booleanArrayField", "[Z"); + ASSERT_TRUE(booleanArrayField != nullptr); + mirror::BooleanArray* booleanArray = booleanArrayField->GetObject(sirt_klass.get())->AsBooleanArray(); + ASSERT_TRUE(booleanArray != nullptr); + ASSERT_EQ(booleanArray->GetLength(), 1); + ASSERT_EQ(booleanArray->GetWithoutChecks(0), false); + + mirror::ArtField* byteArrayField = sirt_klass->FindDeclaredStaticField("byteArrayField", "[B"); + ASSERT_TRUE(byteArrayField != nullptr); + mirror::ByteArray* byteArray = byteArrayField->GetObject(sirt_klass.get())->AsByteArray(); + ASSERT_TRUE(byteArray != nullptr); + ASSERT_EQ(byteArray->GetLength(), 1); + ASSERT_EQ(byteArray->GetWithoutChecks(0), 0); + + mirror::ArtField* charArrayField = sirt_klass->FindDeclaredStaticField("charArrayField", "[C"); + ASSERT_TRUE(charArrayField != nullptr); + mirror::CharArray* charArray = charArrayField->GetObject(sirt_klass.get())->AsCharArray(); + ASSERT_TRUE(charArray != nullptr); + ASSERT_EQ(charArray->GetLength(), 1); + ASSERT_EQ(charArray->GetWithoutChecks(0), 0u); + + mirror::ArtField* shortArrayField = sirt_klass->FindDeclaredStaticField("shortArrayField", "[S"); + ASSERT_TRUE(shortArrayField != nullptr); + mirror::ShortArray* shortArray = shortArrayField->GetObject(sirt_klass.get())->AsShortArray(); + ASSERT_TRUE(shortArray != nullptr); + ASSERT_EQ(shortArray->GetLength(), 1); + ASSERT_EQ(shortArray->GetWithoutChecks(0), 0); + + mirror::ArtField* intArrayField = sirt_klass->FindDeclaredStaticField("intArrayField", "[I"); + ASSERT_TRUE(intArrayField != nullptr); + mirror::IntArray* intArray = intArrayField->GetObject(sirt_klass.get())->AsIntArray(); + ASSERT_TRUE(intArray != nullptr); + ASSERT_EQ(intArray->GetLength(), 1); + ASSERT_EQ(intArray->GetWithoutChecks(0), 0); + + mirror::ArtField* longArrayField = sirt_klass->FindDeclaredStaticField("longArrayField", "[J"); + ASSERT_TRUE(longArrayField != nullptr); + mirror::LongArray* longArray = longArrayField->GetObject(sirt_klass.get())->AsLongArray(); + ASSERT_TRUE(longArray != nullptr); + ASSERT_EQ(longArray->GetLength(), 1); + ASSERT_EQ(longArray->GetWithoutChecks(0), static_cast<int64_t>(0)); + + mirror::ArtField* floatArrayField = sirt_klass->FindDeclaredStaticField("floatArrayField", "[F"); + ASSERT_TRUE(floatArrayField != nullptr); + mirror::FloatArray* floatArray = floatArrayField->GetObject(sirt_klass.get())->AsFloatArray(); + ASSERT_TRUE(floatArray != nullptr); + ASSERT_EQ(floatArray->GetLength(), 1); + ASSERT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f)); + + mirror::ArtField* doubleArrayField = sirt_klass->FindDeclaredStaticField("doubleArrayField", "[D"); + ASSERT_TRUE(doubleArrayField != nullptr); + mirror::DoubleArray* doubleArray = doubleArrayField->GetObject(sirt_klass.get())->AsDoubleArray(); + ASSERT_TRUE(doubleArray != nullptr); + ASSERT_EQ(doubleArray->GetLength(), 1); + ASSERT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f)); + + mirror::ArtField* objectArrayField = sirt_klass->FindDeclaredStaticField("objectArrayField", "[Ljava/lang/Object;"); + ASSERT_TRUE(objectArrayField != nullptr); + mirror::ObjectArray<mirror::Object>* objectArray = objectArrayField->GetObject(sirt_klass.get())->AsObjectArray<mirror::Object>(); + ASSERT_TRUE(objectArray != nullptr); + ASSERT_EQ(objectArray->GetLength(), 1); + ASSERT_EQ(objectArray->GetWithoutChecks(0), nullptr); + + // Create a java.lang.Object instance to set objectField. + SirtRef<mirror::Class> object_klass(soa.Self(), class_linker_->FindSystemClass("Ljava/lang/Object;")); + ASSERT_TRUE(object_klass.get() != nullptr); + SirtRef<mirror::Object> sirt_obj(soa.Self(), sirt_klass->AllocObject(soa.Self())); + ASSERT_TRUE(sirt_obj.get() != nullptr); + ASSERT_EQ(sirt_obj->GetClass(), sirt_klass.get()); + + // Modify fields inside transaction and abort it. + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + booleanArray->SetWithoutChecks<true>(0, true); + byteArray->SetWithoutChecks<true>(0, 1); + charArray->SetWithoutChecks<true>(0, 1u); + shortArray->SetWithoutChecks<true>(0, 1); + intArray->SetWithoutChecks<true>(0, 1); + longArray->SetWithoutChecks<true>(0, 1); + floatArray->SetWithoutChecks<true>(0, 1.0); + doubleArray->SetWithoutChecks<true>(0, 1.0); + objectArray->SetWithoutChecks<true>(0, sirt_obj.get()); + Runtime::Current()->ExitTransactionMode(); + transaction.Abort(); + + // Check values have properly been restored to their original (default) value. + EXPECT_EQ(booleanArray->GetWithoutChecks(0), false); + EXPECT_EQ(byteArray->GetWithoutChecks(0), 0); + EXPECT_EQ(charArray->GetWithoutChecks(0), 0u); + EXPECT_EQ(shortArray->GetWithoutChecks(0), 0); + EXPECT_EQ(intArray->GetWithoutChecks(0), 0); + EXPECT_EQ(longArray->GetWithoutChecks(0), static_cast<int64_t>(0)); + EXPECT_EQ(floatArray->GetWithoutChecks(0), static_cast<float>(0.0f)); + EXPECT_EQ(doubleArray->GetWithoutChecks(0), static_cast<double>(0.0f)); + EXPECT_EQ(objectArray->GetWithoutChecks(0), nullptr); +} + +TEST_F(TransactionTest, EmptyClass) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::ClassLoader> class_loader( + soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))); + ASSERT_TRUE(class_loader.get() != nullptr); + + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindClass("LTransaction$EmptyStatic;", class_loader)); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->VerifyClass(sirt_klass); + ASSERT_TRUE(sirt_klass->IsVerified()); + + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + class_linker_->EnsureInitialized(sirt_klass, true, true); + Runtime::Current()->ExitTransactionMode(); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); +} + +TEST_F(TransactionTest, StaticFieldClass) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::ClassLoader> class_loader( + soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Transaction"))); + ASSERT_TRUE(class_loader.get() != nullptr); + + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindClass("LTransaction$StaticFieldClass;", class_loader)); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->VerifyClass(sirt_klass); + ASSERT_TRUE(sirt_klass->IsVerified()); + + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + class_linker_->EnsureInitialized(sirt_klass, true, true); + Runtime::Current()->ExitTransactionMode(); + ASSERT_FALSE(soa.Self()->IsExceptionPending()); +} + +TEST_F(TransactionTest, BlacklistedClass) { + ScopedObjectAccess soa(Thread::Current()); + jobject jclass_loader = LoadDex("Transaction"); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + soa.Decode<mirror::ClassLoader*>(jclass_loader)); + ASSERT_TRUE(class_loader.get() != nullptr); + + // Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will + // be thrown by class initialization due to native call. + SirtRef<mirror::Class> sirt_klass(soa.Self(), + class_linker_->FindSystemClass("Ljava/lang/ExceptionInInitializerError;")); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->VerifyClass(sirt_klass); + ASSERT_TRUE(sirt_klass->IsVerified()); + sirt_klass.reset(class_linker_->FindSystemClass("Ljava/lang/InternalError;")); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->VerifyClass(sirt_klass); + ASSERT_TRUE(sirt_klass->IsVerified()); + + // Load and verify Transaction$NativeSupport used in class initialization. + sirt_klass.reset(class_linker_->FindClass("LTransaction$NativeSupport;", class_loader)); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->VerifyClass(sirt_klass); + ASSERT_TRUE(sirt_klass->IsVerified()); + + sirt_klass.reset(class_linker_->FindClass("LTransaction$BlacklistedClass;", class_loader)); + ASSERT_TRUE(sirt_klass.get() != nullptr); + class_linker_->VerifyClass(sirt_klass); + ASSERT_TRUE(sirt_klass->IsVerified()); + + Transaction transaction; + Runtime::Current()->EnterTransactionMode(&transaction); + class_linker_->EnsureInitialized(sirt_klass, true, true); + Runtime::Current()->ExitTransactionMode(); + ASSERT_TRUE(soa.Self()->IsExceptionPending()); +} + + +} // namespace art diff --git a/test/Android.mk b/test/Android.mk index 4d47651bf9..dcf75d02f0 100644 --- a/test/Android.mk +++ b/test/Android.mk @@ -36,6 +36,7 @@ TEST_DEX_DIRECTORIES := \ StaticLeafMethods \ Statics \ StaticsFromCode \ + Transaction \ XandY # subdirectories of which are used with test-art-target-oat diff --git a/test/Transaction/InstanceFieldsTest.java b/test/Transaction/InstanceFieldsTest.java new file mode 100644 index 0000000000..38a44d2654 --- /dev/null +++ b/test/Transaction/InstanceFieldsTest.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class InstanceFieldsTest { + public boolean booleanField; + public byte byteField; + public char charField; + public short shortField; + public int intField; + public long longField; + public float floatField; + public double doubleField; + public Object objectField; +} diff --git a/test/Transaction/StaticArrayFieldsTest.java b/test/Transaction/StaticArrayFieldsTest.java new file mode 100644 index 0000000000..6436839637 --- /dev/null +++ b/test/Transaction/StaticArrayFieldsTest.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class StaticArrayFieldsTest { + public static boolean[] booleanArrayField = new boolean[1]; + public static byte[] byteArrayField = new byte[1]; + public static char [] charArrayField = new char[1]; + public static short[] shortArrayField = new short[1]; + public static int[] intArrayField = new int[1]; + public static long[] longArrayField = new long[1]; + public static float[] floatArrayField = new float[1]; + public static double[] doubleArrayField = new double[1]; + public static Object[] objectArrayField = new Object[1]; +} diff --git a/test/Transaction/StaticFieldsTest.java b/test/Transaction/StaticFieldsTest.java new file mode 100644 index 0000000000..2316f3b393 --- /dev/null +++ b/test/Transaction/StaticFieldsTest.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class StaticFieldsTest { + public static boolean booleanField; + public static byte byteField; + public static char charField; + public static short shortField; + public static int intField; + public static long longField; + public static float floatField; + public static double doubleField; + public static Object objectField; +} diff --git a/test/Transaction/Transaction.java b/test/Transaction/Transaction.java new file mode 100644 index 0000000000..9ca7fbfb33 --- /dev/null +++ b/test/Transaction/Transaction.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Transaction { + static class EmptyStatic { + } + + static class StaticFieldClass { + public static int intField; + static { + intField = 5; + } + } + + static class BlacklistedClass { + static { + NativeSupport.native_call(); + } + } + + static class NativeSupport { + public static native void native_call(); + } +} |