summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ian Rogers <irogers@google.com> 2012-10-29 16:34:15 -0700
committer Ian Rogers <irogers@google.com> 2012-11-01 17:26:15 -0700
commit64b6d145fa53b8dfb07a8fc2426af13f155d5a4d (patch)
tree727647da235e21508d6661c7e2a780ff8ee2003e
parent0f79d728370ab51b9b16a356b058c48330f43148 (diff)
Interpret class initializers when building image.
We initialize all image classes with the interpreter if they have a class/static initializer. Black list classes whose initializers need access to on device native code. If such classes are added to the image classes they will fail when they attempt to enter JNI code. A number of "intrinsic" style JNI routines are special cased to allow more than just trivial class initializers to run. Add a lock for initialization in the compiler to serialize the execution of class initializers and avoid deadlock. Remove InSourceSpace from image writer (cruft) and teach the image writer to fix up referent fields in references. Fix bugs in the interprerter and implement filled-new-array. Factor some VM code to more easily share between the interpreter and JNI entry points. Change-Id: I6bb811dea84f1b82260b1a4e73ac7412179c0b41
-rw-r--r--src/class_linker.cc15
-rw-r--r--src/compiler.cc245
-rw-r--r--src/compiler.h4
-rw-r--r--src/heap.cc13
-rw-r--r--src/image_writer.cc29
-rw-r--r--src/image_writer.h6
-rw-r--r--src/interpreter/interpreter.cc535
-rw-r--r--src/invoke_arg_array_builder.h20
-rw-r--r--src/native/java_lang_String.cc70
-rw-r--r--src/native/java_lang_reflect_Array.cc96
-rw-r--r--src/oatdump.cc8
-rw-r--r--src/object.cc155
-rw-r--r--src/object.h36
-rw-r--r--src/object_test.cc52
-rw-r--r--src/thread.cc5
-rw-r--r--src/thread.h2
-rw-r--r--src/verifier/method_verifier.cc1
17 files changed, 872 insertions, 420 deletions
diff --git a/src/class_linker.cc b/src/class_linker.cc
index abb6dcf8f5..dc86aed4a1 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -34,6 +34,7 @@
#include "dex_file.h"
#include "heap.h"
#include "intern_table.h"
+#include "interpreter/interpreter.h"
#include "leb128.h"
#include "logging.h"
#include "oat_file.h"
@@ -2574,7 +2575,6 @@ bool ClassLinker::InitializeClass(Class* klass, bool can_run_clinit, bool can_in
clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
if (clinit != NULL && !can_run_clinit) {
- DCHECK_EQ(klass->GetStatus(), Class::kStatusVerified) << PrettyClass(klass);
// if the class has a <clinit> but we can't run it during compilation,
// don't bother going to kStatusInitializing. We return false so that
// sub-classes don't believe this class is initialized.
@@ -2630,7 +2630,11 @@ bool ClassLinker::InitializeClass(Class* klass, bool can_run_clinit, bool can_in
bool has_static_field_initializers = InitializeStaticFields(klass);
if (clinit != NULL) {
- clinit->Invoke(self, NULL, NULL, NULL);
+ if (Runtime::Current()->IsStarted()) {
+ clinit->Invoke(self, NULL, NULL, NULL);
+ } else {
+ art::interpreter::EnterInterpreterFromInvoke(self, clinit, NULL, NULL, NULL);
+ }
}
FixupStaticTrampolines(klass);
@@ -3432,8 +3436,8 @@ bool ClassLinker::LinkFields(SirtRef<Class>& klass, bool is_static) {
}
// We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it.
- std::string descriptor(ClassHelper(klass.get(), this).GetDescriptor());
- if (!is_static && descriptor == "Ljava/lang/ref/Reference;") {
+ if (!is_static &&
+ StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;") {
// We know there are no non-reference fields in the Reference classes, and we know
// that 'referent' is alphabetically last, so this is easy...
CHECK_EQ(num_reference_fields, num_fields);
@@ -3457,7 +3461,8 @@ bool ClassLinker::LinkFields(SirtRef<Class>& klass, bool is_static) {
fh.ChangeField(field);
Primitive::Type type = fh.GetTypeAsPrimitiveType();
bool is_primitive = type != Primitive::kPrimNot;
- if (descriptor == "Ljava/lang/ref/Reference;" && StringPiece(fh.GetName()) == "referent") {
+ if (StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;" &&
+ StringPiece(fh.GetName()) == "referent") {
is_primitive = true; // We lied above, so we have to expect a lie here.
}
if (is_primitive) {
diff --git a/src/compiler.cc b/src/compiler.cc
index 4029a01c5d..09360c1e97 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -535,7 +535,7 @@ void Compiler::PreCompile(jobject class_loader, const std::vector<const DexFile*
Verify(class_loader, dex_files, timings);
- InitializeClassesWithoutClinit(class_loader, dex_files, timings);
+ InitializeClasses(class_loader, dex_files, timings);
}
bool Compiler::IsImageClass(const std::string& descriptor) const {
@@ -1235,8 +1235,186 @@ void Compiler::VerifyDexFile(jobject class_loader, const DexFile& dex_file, Timi
timings.AddSplit("Verify " + dex_file.GetLocation());
}
-static void InitializeClassWithoutClinit(const CompilationContext* context,
- size_t class_def_index)
+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/CameraProfile;", // Calls System.loadLibrary.
+ "Landroid/media/DecoderCapabilities;", // Calls System.loadLibrary.
+ "Landroid/media/MediaFile;", // Requires DecoderCapabilities.
+ "Landroid/media/MediaPlayer;", // Calls System.loadLibrary.
+ "Landroid/media/MediaRecorder;", // Calls System.loadLibrary.
+ "Landroid/media/MediaScanner;", // Calls System.loadLibrary.
+ "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;", // Calls Class.getSimpleName -> Class.isAnonymousClass -> Class.getDex.
+ "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/WebAddress;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl.
+ "Landroid/nfc/NdefRecord;", // Calls String.getBytes -> java.nio.charset.Charset.
+ "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/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/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/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/animation/Animation;", // Calls SystemProperties.native_get_boolean.
+ "Landroid/view/animation/AlphaAnimation;", // Requires Animation.
+ "Landroid/view/Choreographer;", // Calls SystemProperties.native_get_boolean.
+ "Landroid/view/GLES20Canvas;", // Calls android.view.GLES20Canvas.nIsAvailable.
+ "Landroid/view/GLES20RecordingCanvas;", // Requires android.view.GLES20Canvas.
+ "Landroid/view/HardwareRenderer$GlRenderer;", // Requires SystemProperties.native_get.
+ "Landroid/view/HardwareRenderer$Gl20Renderer;", // Requires SystemProperties.native_get.
+ "Landroid/view/InputEventConsistencyVerifier;", // Requires android.os.Build.
+ "Landroid/view/Surface;", // Requires SystemProperties.native_get.
+ "Landroid/webkit/JniUtil;", // Calls System.loadLibrary.
+ "Landroid/webkit/WebViewCore;", // Calls System.loadLibrary.
+ "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/PhoneNumberUtil;", // Requires java.util.logging.LogManager.
+ "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/crypto/digests/OpenSSLDigest$SHA1;", // Requires org.apache.harmony.xnet.provider.jsse.NativeCrypto.
+ "Lcom/android/org/bouncycastle/crypto/engines/RSABlindedEngine;", // 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/PKIXCertPathValidatorSpi;", // Calls System.getenv -> OsConstants.initConstants.
+ "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.
+ "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/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/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/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/security/Security;", // Tries to do disk IO for "security.properties".
+ "Ljava/util/Date;", // Calls Date.<init> -> System.currentTimeMillis -> OsConstants.initConstants.
+ "Ljava/util/Locale;", // Calls System.getProperty -> OsConstants.initConstants.
+ "Ljava/util/SimpleTimeZone;", // Sub-class of TimeZone.
+ "Ljava/util/TimeZone;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl.
+ "Ljava/util/concurrent/ConcurrentHashMap$Segment;", // Calls Runtime.getRuntime().availableProcessors().
+ "Ljava/util/logging/LogManager;", // Calls System.getProperty -> OsConstants.initConstants.
+ "Ljavax/microedition/khronos/egl/EGL10;", // Requires EGLContext.
+ "Ljavax/microedition/khronos/egl/EGLContext;", // Requires com.google.android.gles_jni.EGLImpl.
+ "Ljavax/net/ssl/HttpsURLConnection;", // Calls SSLSocketFactory.getDefault -> java.security.Security.getProperty.
+ "Llibcore/icu/LocaleData;", // Requires java.util.Locale.
+ "Llibcore/icu/TimeZones;", // Requires java.util.TimeZone.
+ "Llibcore/io/OsConstants;", // Platform specific.
+ "Llibcore/net/MimeUtils;", // Calls libcore.net.MimeUtils.getContentTypesPropertiesStream -> System.getProperty.
+ "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/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/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/Certificate;", // Requires org.apache.harmony.security.x509.TBSCertificate.
+ "Lorg/apache/harmony/security/x509/TBSCertificate;", // Requires org.apache.harmony.security.x501.Name.
+ "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/Time;", // Calls native ... -> java.math.NativeBN.BN_new().
+ "Lorg/apache/harmony/security/x509/Validity;", // Requires x509.Time.
+ "Lorg/apache/harmony/xml/ExpatParser;", // Calls native ExpatParser.staticInitialize.
+ "Lorg/apache/harmony/xnet/provider/jsse/NativeCrypto;", // Calls native NativeCrypto.clinit().
+ "Lorg/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK$MD5;", // Requires org.apache.harmony.xnet.provider.jsse.NativeCrypto.
+ "Lorg/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK$SHA1;", // Requires org.apache.harmony.xnet.provider.jsse.NativeCrypto.
+ "Lorg/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK$SHA512;", // Requires org.apache.harmony.xnet.provider.jsse.NativeCrypto.
+ "Lorg/apache/harmony/xnet/provider/jsse/TrustedCertificateStore;", // Calls System.getenv -> OsConstants.initConstants.
+ "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 CompilationContext* context, size_t class_def_index)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
const DexFile::ClassDef& class_def = context->GetDexFile()->GetClassDef(class_def_index);
ScopedObjectAccess soa(Thread::Current());
@@ -1244,14 +1422,47 @@ static void InitializeClassWithoutClinit(const CompilationContext* context,
const char* descriptor = context->GetDexFile()->GetClassDescriptor(class_def);
Class* klass = context->GetClassLinker()->FindClass(descriptor, class_loader);
Thread* self = Thread::Current();
+ bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1;
+ bool can_init_static_fields = compiling_boot &&
+ context->GetCompiler()->IsImageClass(descriptor);
if (klass != NULL) {
- ObjectLock lock(self, klass);
+ // We don't want class initialization occurring on multiple threads due to deadlock problems.
+ // For example, a parent class is initialized (holding its lock) that refers to a sub-class
+ // in its static/class initializer causing it to try to acquire the sub-class' lock. While
+ // on a second thread the sub-class is initialized (holding its lock) after first initializing
+ // its parents, whose locks are acquired. This leads to a parent-to-child and a child-to-parent
+ // lock ordering and consequent potential deadlock.
+ static Mutex lock1("Initializer lock", kMonitorLock);
+ MutexLock mu(self, lock1);
+ // The lock required to initialize the class.
+ ObjectLock lock2(self, klass);
+ // Only try to initialize classes that were successfully verified.
if (klass->IsVerified()) {
- // Only try to initialize classes that were successfully verified.
- bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1;
- bool can_init_static_fields = compiling_boot &&
- context->GetCompiler()->IsImageClass(descriptor);
context->GetClassLinker()->EnsureInitialized(klass, false, can_init_static_fields);
+ if (!klass->IsInitialized()) {
+ if (can_init_static_fields) {
+ bool is_black_listed = false;
+ for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
+ if (StringPiece(descriptor) == class_initializer_black_list[i]) {
+ is_black_listed = true;
+ break;
+ }
+ }
+ if (!is_black_listed) {
+ LOG(INFO) << "Initializing: " << descriptor;
+ if (StringPiece(descriptor) == "Ljava/lang/Void;"){
+ // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime.
+ ObjectArray<Field>* fields = klass->GetSFields();
+ CHECK_EQ(fields->GetLength(), 1);
+ fields->Get(0)->SetObj(klass, context->GetClassLinker()->FindPrimitiveClass('V'));
+ klass->SetStatus(Class::kStatusInitialized);
+ } else {
+ context->GetClassLinker()->EnsureInitialized(klass, true, can_init_static_fields);
+ }
+ CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
+ }
+ }
+ }
// If successfully initialized place in SSB array.
if (klass->IsInitialized()) {
klass->GetDexCache()->GetInitializedStaticStorage()->Set(klass->GetDexTypeIndex(), klass);
@@ -1272,21 +1483,27 @@ static void InitializeClassWithoutClinit(const CompilationContext* context,
self->ClearException();
}
-void Compiler::InitializeClassesWithoutClinit(jobject jni_class_loader, const DexFile& dex_file,
+void Compiler::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
TimingLogger& timings) {
+#ifndef NDEBUG
+ 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();
CompilationContext context(class_linker, jni_class_loader, this, &dex_file, thread_pool_.get());
- context.ForAll(0, dex_file.NumClassDefs(), InitializeClassWithoutClinit, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), InitializeClass, thread_count_);
timings.AddSplit("InitializeNoClinit " + dex_file.GetLocation());
}
-void Compiler::InitializeClassesWithoutClinit(jobject class_loader,
- const std::vector<const DexFile*>& dex_files,
- TimingLogger& timings) {
+void Compiler::InitializeClasses(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger& timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
- InitializeClassesWithoutClinit(class_loader, *dex_file, timings);
+ InitializeClasses(class_loader, *dex_file, timings);
}
}
diff --git a/src/compiler.h b/src/compiler.h
index ba5651336a..39ee5e4ddb 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -281,11 +281,11 @@ class Compiler {
void VerifyDexFile(jobject class_loader, const DexFile& dex_file, TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
- void InitializeClassesWithoutClinit(jobject class_loader,
+ void InitializeClasses(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_);
- void InitializeClassesWithoutClinit(jobject class_loader, const DexFile& dex_file,
+ void InitializeClasses(jobject class_loader, const DexFile& dex_file,
TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_);
diff --git a/src/heap.cc b/src/heap.cc
index cf13eaec9a..d12d20e5b8 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1984,11 +1984,14 @@ size_t Heap::GetConcurrentMinFree() const {
void Heap::EnqueueClearedReferences(Object** cleared) {
DCHECK(cleared != NULL);
if (*cleared != NULL) {
- ScopedObjectAccess soa(Thread::Current());
- JValue args[1];
- args[0].SetL(*cleared);
- soa.DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(soa.Self(), NULL,
- args, NULL);
+ // When a runtime isn't started there are no reference queues to care about so ignore.
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ ScopedObjectAccess soa(Thread::Current());
+ JValue args[1];
+ args[0].SetL(*cleared);
+ soa.DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(soa.Self(), NULL,
+ args, NULL);
+ }
*cleared = NULL;
}
}
diff --git a/src/image_writer.cc b/src/image_writer.cc
index 642cee6a63..a9aa2e0adf 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -61,9 +61,7 @@ bool ImageWriter::Write(const std::string& image_filename,
const std::vector<DexCache*>& all_dex_caches = class_linker->GetDexCaches();
for (size_t i = 0; i < all_dex_caches.size(); i++) {
DexCache* dex_cache = all_dex_caches[i];
- if (InSourceSpace(dex_cache)) {
- dex_caches_.insert(dex_cache);
- }
+ dex_caches_.insert(dex_cache);
}
oat_file_ = OatFile::Open(oat_filename, oat_location, NULL,
@@ -122,17 +120,6 @@ bool ImageWriter::Write(const std::string& image_filename,
return true;
}
-bool ImageWriter::InSourceSpace(const Object* object) const {
- const Spaces& spaces = Runtime::Current()->GetHeap()->GetSpaces();
- // TODO: C++0x auto
- for (Spaces::const_iterator cur = spaces.begin(); cur != spaces.end(); ++cur) {
- if ((*cur)->IsAllocSpace() && (*cur)->Contains(object)) {
- return true;
- }
- }
- return false;
-}
-
bool ImageWriter::AllocMemory() {
const Spaces& spaces = Runtime::Current()->GetHeap()->GetSpaces();
size_t size = 0;
@@ -307,9 +294,6 @@ void ImageWriter::CalculateNewObjectOffsetsCallback(Object* obj, void* arg) {
DCHECK(obj != NULL);
DCHECK(arg != NULL);
ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
- if (!image_writer->InSourceSpace(obj)) {
- return;
- }
// if it is a string, we want to intern it if its not interned.
if (obj->GetClass()->IsStringClass()) {
@@ -439,9 +423,6 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) {
DCHECK(arg != NULL);
const Object* obj = object;
ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg);
- if (!image_writer->InSourceSpace(object)) {
- return;
- }
// see GetLocalAddress for similar computation
size_t offset = image_writer->GetImageOffset(obj);
@@ -607,6 +588,14 @@ void ImageWriter::FixupFields(const Object* orig,
}
}
}
+ if (!is_static && orig->IsReferenceInstance()) {
+ // Fix-up referent, that isn't marked as an object field, for References.
+ Field* field = orig->GetClass()->FindInstanceField("referent", "Ljava/lang/Object;");
+ MemberOffset field_offset = field->GetOffset();
+ const Object* ref = orig->GetFieldObject<const Object*>(field_offset, false);
+ // Use SetFieldPtr to avoid card marking since we are writing to the image.
+ copy->SetFieldPtr(field_offset, GetImageAddress(ref), false);
+ }
}
static AbstractMethod* GetTargetMethod(const Compiler::PatchInformation* patch)
diff --git a/src/image_writer.h b/src/image_writer.h
index 638add7905..0a0854a400 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -81,16 +81,10 @@ class ImageWriter {
return offsets_.find(object)->second;
}
- bool InSourceSpace(const Object* object) const;
-
Object* GetImageAddress(const Object* object) const {
if (object == NULL) {
return NULL;
}
- // if object outside the relocating source_space_, assume unchanged
- if (!InSourceSpace(object)) {
- return const_cast<Object*>(object);
- }
return reinterpret_cast<Object*>(image_begin_ + GetImageOffset(object));
}
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index 550c6ee106..388eb85c55 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -22,6 +22,7 @@
#include "dex_instruction.h"
#include "invoke_arg_array_builder.h"
#include "logging.h"
+#include "nth_caller_visitor.h"
#include "object.h"
#include "object_utils.h"
#include "runtime_support.h"
@@ -32,6 +33,310 @@
namespace art {
namespace interpreter {
+static void UnstartedRuntimeInvoke(Thread* self, AbstractMethod* target_method,
+ Object* receiver, JValue* args, JValue* result)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // In a runtime that's not started we intercept certain methods to avoid complicated dependency
+ // problems in core libraries.
+ std::string name(PrettyMethod(target_method));
+ if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") {
+ std::string descriptor(DotToDescriptor(args[0].GetL()->AsString()->ToModifiedUtf8().c_str()));
+ ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
+ Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+ class_loader);
+ CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
+ << PrettyDescriptor(descriptor);
+ result->SetL(found);
+ } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
+ Class* klass = receiver->AsClass();
+ AbstractMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V");
+ CHECK(c != NULL);
+ Object* obj = klass->AllocObject(self);
+ CHECK(obj != NULL);
+ EnterInterpreterFromInvoke(self, c, obj, NULL, NULL);
+ result->SetL(obj);
+ } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
+ // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
+ // going the reflective Dex way.
+ Class* klass = receiver->AsClass();
+ String* name = args[0].GetL()->AsString();
+ Field* found = NULL;
+ FieldHelper fh;
+ ObjectArray<Field>* fields = klass->GetIFields();
+ for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
+ Field* f = fields->Get(i);
+ fh.ChangeField(f);
+ if (name->Equals(fh.GetName())) {
+ found = f;
+ }
+ }
+ if (found == NULL) {
+ fields = klass->GetSFields();
+ for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
+ Field* f = fields->Get(i);
+ fh.ChangeField(f);
+ if (name->Equals(fh.GetName())) {
+ found = f;
+ }
+ }
+ }
+ CHECK(found != NULL)
+ << "Failed to find field in Class.getDeclaredField in un-started runtime. name="
+ << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass);
+ // TODO: getDeclaredField calls GetType once the field is found to ensure a
+ // NoClassDefFoundError is thrown if the field's type cannot be resolved.
+ result->SetL(found);
+ } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") {
+ // Special case array copying without initializing System.
+ Class* ctype = args[0].GetL()->GetClass()->GetComponentType();
+ jint srcPos = args[1].GetI();
+ jint dstPos = args[3].GetI();
+ jint length = args[4].GetI();
+ if (!ctype->IsPrimitive()) {
+ ObjectArray<Object>* src = args[0].GetL()->AsObjectArray<Object>();
+ ObjectArray<Object>* dst = args[2].GetL()->AsObjectArray<Object>();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else if (ctype->IsPrimitiveChar()) {
+ CharArray* src = args[0].GetL()->AsCharArray();
+ CharArray* dst = args[2].GetL()->AsCharArray();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else if (ctype->IsPrimitiveInt()) {
+ IntArray* src = args[0].GetL()->AsIntArray();
+ IntArray* dst = args[2].GetL()->AsIntArray();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else {
+ UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype);
+ }
+ } else {
+ // Not special, continue with regular interpreter execution.
+ EnterInterpreterFromInvoke(self, target_method, receiver, args, result);
+ }
+}
+
+// Hand select a number of methods to be run in a not yet started runtime without using JNI.
+static void UnstartedRuntimeJni(Thread* self, AbstractMethod* method,
+ Object* receiver, JValue* args, JValue* result)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::string name(PrettyMethod(method));
+ if (name == "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") {
+ result->SetL(NULL);
+ } else if (name == "java.lang.Class dalvik.system.VMStack.getStackClass2()") {
+ NthCallerVisitor visitor(self->GetManagedStack(), NULL, 3);
+ visitor.WalkStack();
+ result->SetL(visitor.caller->GetDeclaringClass());
+ } else if (name == "double java.lang.Math.log(double)") {
+ result->SetD(log(args[0].GetD()));
+ } else if (name == "java.lang.String java.lang.Class.getNameNative()") {
+ result->SetL(receiver->AsClass()->ComputeName());
+ } else if (name == "int java.lang.Float.floatToRawIntBits(float)") {
+ result->SetI(args[0].GetI());
+ } else if (name == "float java.lang.Float.intBitsToFloat(int)") {
+ result->SetF(args[0].GetF());
+ } else if (name == "double java.lang.Math.exp(double)") {
+ result->SetD(exp(args[0].GetD()));
+ } else if (name == "java.lang.Object java.lang.Object.internalClone()") {
+ result->SetL(receiver->Clone(self));
+ } else if (name == "void java.lang.Object.notifyAll()") {
+ receiver->NotifyAll();
+ } else if (name == "int java.lang.String.compareTo(java.lang.String)") {
+ String* rhs = args[0].GetL()->AsString();
+ CHECK(rhs != NULL);
+ result->SetI(receiver->AsString()->CompareTo(rhs));
+ } else if (name == "java.lang.String java.lang.String.intern()") {
+ result->SetL(receiver->AsString()->Intern());
+ } else if (name == "int java.lang.String.fastIndexOf(int, int)") {
+ result->SetI(receiver->AsString()->FastIndexOf(args[0].GetI(), args[1].GetI()));
+ } else if (name == "java.lang.Object java.lang.reflect.Array.createMultiArray(java.lang.Class, int[])") {
+ result->SetL(Array::CreateMultiArray(self, args[0].GetL()->AsClass(), args[1].GetL()->AsIntArray()));
+ } else if (name == "java.lang.Object java.lang.Throwable.nativeFillInStackTrace()") {
+ ScopedObjectAccessUnchecked soa(self);
+ result->SetL(soa.Decode<Object*>(self->CreateInternalStackTrace(soa)));
+ } else if (name == "boolean java.nio.ByteOrder.isLittleEndian()") {
+ result->SetJ(JNI_TRUE);
+ } else if (name == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") {
+ Object* obj = args[0].GetL();
+ jlong offset = args[1].GetJ();
+ jint expectedValue = args[2].GetI();
+ jint newValue = args[3].GetI();
+ byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
+ volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
+ // 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 = args[0].GetL();
+ Object* newValue = args[2].GetL();
+ obj->SetFieldObject(MemberOffset(args[1].GetJ()), newValue, false);
+ } else {
+ LOG(FATAL) << "Attempt to invoke native method in non-started runtime: " << name;
+ }
+}
+
+static void InterpreterJni(Thread* self, AbstractMethod* method, StringPiece shorty,
+ Object* receiver, JValue* args, JValue* result)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // TODO: The following enters JNI code using a typedef-ed function rather than the JNI compiler,
+ // it should be removed and JNI compiled stubs used instead.
+ ScopedObjectAccessUnchecked soa(self);
+ if (method->IsStatic()) {
+ if (shorty == "L") {
+ typedef jobject (fnptr)(JNIEnv*, jclass);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetL(soa.Decode<Object*>(fn(soa.Env(), klass.get())));
+ } else if (shorty == "V") {
+ typedef void (fnptr)(JNIEnv*, jclass);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ fn(soa.Env(), klass.get());
+ } else if (shorty == "Z") {
+ typedef jboolean (fnptr)(JNIEnv*, jclass);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetZ(fn(soa.Env(), klass.get()));
+ } else if (shorty == "BI") {
+ typedef jbyte (fnptr)(JNIEnv*, jclass, jint);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetB(fn(soa.Env(), klass.get(), args[0].GetI()));
+ } else if (shorty == "II") {
+ typedef jint (fnptr)(JNIEnv*, jclass, jint);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetI(fn(soa.Env(), klass.get(), args[0].GetI()));
+ } else if (shorty == "LL") {
+ typedef jobject (fnptr)(JNIEnv*, jclass, jobject);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedLocalRef<jobject> arg0(soa.Env(),
+ soa.AddLocalReference<jobject>(args[0].GetL()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetL(soa.Decode<Object*>(fn(soa.Env(), klass.get(), arg0.get())));
+ } else if (shorty == "IIZ") {
+ typedef jint (fnptr)(JNIEnv*, jclass, jint, jboolean);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetI(fn(soa.Env(), klass.get(), args[0].GetI(), args[1].GetZ()));
+ } else if (shorty == "ILI") {
+ typedef jint (fnptr)(JNIEnv*, jclass, jobject, jint);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedLocalRef<jobject> arg0(soa.Env(),
+ soa.AddLocalReference<jobject>(args[0].GetL()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1].GetI()));
+ } else if (shorty == "SIZ") {
+ typedef jshort (fnptr)(JNIEnv*, jclass, jint, jboolean);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetS(fn(soa.Env(), klass.get(), args[0].GetI(), args[1].GetZ()));
+ } else if (shorty == "VIZ") {
+ typedef void (fnptr)(JNIEnv*, jclass, jint, jboolean);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedThreadStateChange tsc(self, kNative);
+ fn(soa.Env(), klass.get(), args[0].GetI(), args[1].GetZ());
+ } else if (shorty == "ZLL") {
+ typedef jboolean (fnptr)(JNIEnv*, jclass, jobject, jobject);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedLocalRef<jobject> arg0(soa.Env(),
+ soa.AddLocalReference<jobject>(args[0].GetL()));
+ ScopedLocalRef<jobject> arg1(soa.Env(),
+ soa.AddLocalReference<jobject>(args[1].GetL()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get()));
+ } else if (shorty == "ZILL") {
+ typedef jboolean (fnptr)(JNIEnv*, jclass, jint, jobject, jobject);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedLocalRef<jobject> arg1(soa.Env(),
+ soa.AddLocalReference<jobject>(args[1].GetL()));
+ ScopedLocalRef<jobject> arg2(soa.Env(),
+ soa.AddLocalReference<jobject>(args[2].GetL()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetZ(fn(soa.Env(), klass.get(), args[0].GetI(), arg1.get(), arg2.get()));
+ } else if (shorty == "VILII") {
+ typedef void (fnptr)(JNIEnv*, jclass, jint, jobject, jint, jint);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedLocalRef<jobject> arg1(soa.Env(),
+ soa.AddLocalReference<jobject>(args[1].GetL()));
+ ScopedThreadStateChange tsc(self, kNative);
+ fn(soa.Env(), klass.get(), args[0].GetI(), arg1.get(), args[2].GetI(), args[3].GetI());
+ } else if (shorty == "VLILII") {
+ typedef void (fnptr)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jclass> klass(soa.Env(),
+ soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
+ ScopedLocalRef<jobject> arg0(soa.Env(),
+ soa.AddLocalReference<jobject>(args[0].GetL()));
+ ScopedLocalRef<jobject> arg2(soa.Env(),
+ soa.AddLocalReference<jobject>(args[2].GetL()));
+ ScopedThreadStateChange tsc(self, kNative);
+ fn(soa.Env(), klass.get(), arg0.get(), args[1].GetI(), arg2.get(), args[3].GetI(),
+ args[4].GetI());
+ } else {
+ LOG(FATAL) << "Do something with static native method: " << PrettyMethod(method)
+ << " shorty: " << shorty;
+ }
+ } else {
+ if (shorty == "L") {
+ typedef jobject (fnptr)(JNIEnv*, jobject);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jobject> rcvr(soa.Env(),
+ soa.AddLocalReference<jobject>(receiver));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetL(soa.Decode<Object*>(fn(soa.Env(), rcvr.get())));
+ } else if (shorty == "LL") {
+ typedef jobject (fnptr)(JNIEnv*, jobject, jobject);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jobject> rcvr(soa.Env(),
+ soa.AddLocalReference<jobject>(receiver));
+ ScopedLocalRef<jobject> arg0(soa.Env(),
+ soa.AddLocalReference<jobject>(args[0].GetL()));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetL(soa.Decode<Object*>(fn(soa.Env(), rcvr.get(), arg0.get())));
+ } else if (shorty == "III") {
+ typedef jint (fnptr)(JNIEnv*, jobject, jint, jint);
+ fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
+ ScopedLocalRef<jobject> rcvr(soa.Env(),
+ soa.AddLocalReference<jobject>(receiver));
+ ScopedThreadStateChange tsc(self, kNative);
+ result->SetI(fn(soa.Env(), rcvr.get(), args[0].GetI(), args[1].GetI()));
+ } else {
+ LOG(FATAL) << "Do something with native method: " << PrettyMethod(method)
+ << " shorty: " << shorty;
+ }
+ }
+}
+
static void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
ref->MonitorEnter(self);
}
@@ -71,7 +376,11 @@ static void DoInvoke(Thread* self, MethodHelper& mh, ShadowFrame& shadow_frame,
} else {
arg_array.BuildArgArray(shadow_frame, dec_insn.arg + (type != kStatic ? 1 : 0));
}
- target_method->Invoke(self, receiver, arg_array.get(), result);
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ target_method->Invoke(self, receiver, arg_array.get(), result);
+ } else {
+ UnstartedRuntimeInvoke(self, target_method, receiver, arg_array.get(), result);
+ }
if (!mh.GetReturnType()->IsPrimitive() && result->GetL() != NULL) {
CHECK(mh.GetReturnType()->IsAssignableFrom(result->GetL()->GetClass()));
}
@@ -183,7 +492,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
while (true) {
shadow_frame.SetDexPC(inst->GetDexPc(insns));
DecodedInstruction dec_insn(inst);
- const bool kTracing = true;
+ const bool kTracing = false;
if (kTracing) {
LOG(INFO) << PrettyMethod(shadow_frame.GetMethod())
<< StringPrintf("\n0x%x: %s\nReferences:",
@@ -264,7 +573,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
return result;
}
case Instruction::CONST_4: {
- int32_t val = (dec_insn.vB << 28) >> 28;
+ int32_t val = static_cast<int32_t>(dec_insn.vB << 28) >> 28;
shadow_frame.SetVReg(dec_insn.vA, val);
if (val == 0) {
shadow_frame.SetReference(dec_insn.vA, NULL);
@@ -295,24 +604,17 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
}
break;
}
- case Instruction::CONST_WIDE_16: {
- int64_t val = static_cast<int16_t>(dec_insn.vB);
- shadow_frame.SetVReg(dec_insn.vA, val);
- shadow_frame.SetVReg(dec_insn.vA + 1, val >> 32);
+ case Instruction::CONST_WIDE_16:
+ shadow_frame.SetVRegLong(dec_insn.vA, static_cast<int16_t>(dec_insn.vB));
break;
- }
- case Instruction::CONST_WIDE_32: {
- int64_t val = static_cast<int32_t>(dec_insn.vB);
- shadow_frame.SetVReg(dec_insn.vA, val);
- shadow_frame.SetVReg(dec_insn.vA + 1, val >> 32);
+ case Instruction::CONST_WIDE_32:
+ shadow_frame.SetVRegLong(dec_insn.vA, static_cast<int32_t>(dec_insn.vB));
break;
- }
case Instruction::CONST_WIDE:
- shadow_frame.SetVReg(dec_insn.vA, dec_insn.vB_wide);
- shadow_frame.SetVReg(dec_insn.vA + 1, dec_insn.vB_wide >> 32);
+ shadow_frame.SetVRegLong(dec_insn.vA, dec_insn.vB_wide);
break;
case Instruction::CONST_WIDE_HIGH16:
- shadow_frame.SetVRegLong(dec_insn.vA + 1, static_cast<uint64_t>(dec_insn.vB) << 48);
+ shadow_frame.SetVRegLong(dec_insn.vA, static_cast<uint64_t>(dec_insn.vB) << 48);
break;
case Instruction::CONST_STRING:
case Instruction::CONST_STRING_JUMBO: {
@@ -351,12 +653,12 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
break;
}
case Instruction::ARRAY_LENGTH: {
- Array* array = shadow_frame.GetReference(dec_insn.vB)->AsArray();
+ Object* array = shadow_frame.GetReference(dec_insn.vB);
if (UNLIKELY(array == NULL)) {
ThrowNullPointerExceptionFromDexPC(shadow_frame.GetMethod(), inst->GetDexPc(insns));
break;
}
- shadow_frame.SetVReg(dec_insn.vA, array->GetLength());
+ shadow_frame.SetVReg(dec_insn.vA, array->AsArray()->GetLength());
break;
}
case Instruction::NEW_INSTANCE: {
@@ -371,9 +673,30 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
break;
}
case Instruction::FILLED_NEW_ARRAY:
- case Instruction::FILLED_NEW_ARRAY_RANGE:
- UNIMPLEMENTED(FATAL) << inst->DumpString(&mh.GetDexFile());
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ bool is_range = (dec_insn.opcode == Instruction::FILLED_NEW_ARRAY_RANGE);
+ int32_t length = dec_insn.vA;
+ CHECK(is_range || length <= 5);
+ Class* arrayClass = mh.ResolveClass(dec_insn.vB);
+ CHECK(arrayClass->IsArrayClass());
+ if (arrayClass->GetComponentType()->IsPrimitiveInt()) {
+ IntArray* newArray = IntArray::Alloc(self, length);
+ if (newArray != NULL) {
+ for (int32_t i = 0; i < length; ++i) {
+ if (is_range) {
+ newArray->Set(i, shadow_frame.GetVReg(dec_insn.vC + i));
+ } else {
+ newArray->Set(i, shadow_frame.GetVReg(dec_insn.arg[i]));
+ }
+ }
+ }
+ result_register.SetL(newArray);
+ } else {
+ UNIMPLEMENTED(FATAL) << inst->DumpString(&mh.GetDexFile())
+ << " for array type: " << PrettyDescriptor(arrayClass);
+ }
break;
+ }
case Instruction::CMPL_FLOAT: {
float val1 = shadow_frame.GetVRegFloat(dec_insn.vB);
float val2 = shadow_frame.GetVRegFloat(dec_insn.vC);
@@ -408,7 +731,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
int32_t result;
if (val1 == val2) {
result = 0;
- } else if (val1 > val2) {
+ } else if (val1 < val2) {
result = 1;
} else {
result = -1;
@@ -423,7 +746,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
int32_t result;
if (val1 == val2) {
result = 0;
- } else if (val1 < val2) {
+ } else if (val1 > val2) {
result = -1;
} else {
result = 1;
@@ -436,11 +759,11 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
int64_t val2 = shadow_frame.GetVRegLong(dec_insn.vC);
int32_t result;
if (val1 < val2) {
- result = -1;
+ result = 1;
} else if (val1 == val2) {
result = 0;
} else {
- result = 1;
+ result = -1;
}
shadow_frame.SetVReg(dec_insn.vA, result);
break;
@@ -1124,7 +1447,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
break;
case Instruction::MUL_LONG_2ADDR:
shadow_frame.SetVRegLong(dec_insn.vA,
- shadow_frame.GetVRegLong(dec_insn.vA) +
+ shadow_frame.GetVRegLong(dec_insn.vA) *
shadow_frame.GetVRegLong(dec_insn.vB));
break;
case Instruction::DIV_LONG_2ADDR:
@@ -1282,6 +1605,7 @@ static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* c
void EnterInterpreterFromInvoke(Thread* self, AbstractMethod* method, Object* receiver,
JValue* args, JValue* result) {
+ DCHECK_EQ(self, Thread::Current());
MethodHelper mh(method);
const DexFile::CodeItem* code_item = mh.GetCodeItem();
uint16_t num_regs;
@@ -1313,7 +1637,7 @@ void EnterInterpreterFromInvoke(Thread* self, AbstractMethod* method, Object* re
true, true);
CHECK(method->GetDeclaringClass()->IsInitializing());
}
- StringPiece shorty(mh.GetShorty());
+ const char* shorty = mh.GetShorty();
size_t arg_pos = 0;
for (; cur_reg < num_regs; ++cur_reg, ++arg_pos) {
DCHECK_LT(arg_pos + 1, mh.GetShortyLength());
@@ -1332,165 +1656,18 @@ void EnterInterpreterFromInvoke(Thread* self, AbstractMethod* method, Object* re
break;
}
}
- if (!method->IsNative()) {
+ if (LIKELY(!method->IsNative())) {
JValue r = Execute(self, mh, code_item, *shadow_frame.get());
if (result != NULL) {
*result = r;
}
} else {
- // TODO: The following enters JNI code using a typedef-ed function rather than the JNI compiler,
- // it should be removed and JNI compiled stubs used instead.
- ScopedObjectAccessUnchecked soa(self);
- if (method->IsStatic()) {
- if (shorty == "L") {
- typedef jobject (fnptr)(JNIEnv*, jclass);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetL(soa.Decode<Object*>(fn(soa.Env(), klass.get())));
- } else if (shorty == "V") {
- typedef void (fnptr)(JNIEnv*, jclass);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- fn(soa.Env(), klass.get());
- } else if (shorty == "Z") {
- typedef jboolean (fnptr)(JNIEnv*, jclass);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetZ(fn(soa.Env(), klass.get()));
- } else if (shorty == "BI") {
- typedef jbyte (fnptr)(JNIEnv*, jclass, jint);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetB(fn(soa.Env(), klass.get(), args[0].GetI()));
- } else if (shorty == "II") {
- typedef jint (fnptr)(JNIEnv*, jclass, jint);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetI(fn(soa.Env(), klass.get(), args[0].GetI()));
- } else if (shorty == "LL") {
- typedef jobject (fnptr)(JNIEnv*, jclass, jobject);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(args[0].GetL()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetL(soa.Decode<Object*>(fn(soa.Env(), klass.get(), arg0.get())));
- } else if (shorty == "IIZ") {
- typedef jint (fnptr)(JNIEnv*, jclass, jint, jboolean);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetI(fn(soa.Env(), klass.get(), args[0].GetI(), args[1].GetZ()));
- } else if (shorty == "ILI") {
- typedef jint (fnptr)(JNIEnv*, jclass, jobject, jint);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(args[0].GetL()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1].GetI()));
- } else if (shorty == "SIZ") {
- typedef jshort (fnptr)(JNIEnv*, jclass, jint, jboolean);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetS(fn(soa.Env(), klass.get(), args[0].GetI(), args[1].GetZ()));
- } else if (shorty == "VIZ") {
- typedef void (fnptr)(JNIEnv*, jclass, jint, jboolean);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedThreadStateChange tsc(self, kNative);
- fn(soa.Env(), klass.get(), args[0].GetI(), args[1].GetZ());
- } else if (shorty == "ZLL") {
- typedef jboolean (fnptr)(JNIEnv*, jclass, jobject, jobject);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(args[0].GetL()));
- ScopedLocalRef<jobject> arg1(soa.Env(),
- soa.AddLocalReference<jobject>(args[1].GetL()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get()));
- } else if (shorty == "ZILL") {
- typedef jboolean (fnptr)(JNIEnv*, jclass, jint, jobject, jobject);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedLocalRef<jobject> arg1(soa.Env(),
- soa.AddLocalReference<jobject>(args[1].GetL()));
- ScopedLocalRef<jobject> arg2(soa.Env(),
- soa.AddLocalReference<jobject>(args[2].GetL()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetZ(fn(soa.Env(), klass.get(), args[0].GetI(), arg1.get(), arg2.get()));
- } else if (shorty == "VILII") {
- typedef void (fnptr)(JNIEnv*, jclass, jint, jobject, jint, jint);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedLocalRef<jobject> arg1(soa.Env(),
- soa.AddLocalReference<jobject>(args[1].GetL()));
- ScopedThreadStateChange tsc(self, kNative);
- fn(soa.Env(), klass.get(), args[0].GetI(), arg1.get(), args[2].GetI(), args[3].GetI());
- } else if (shorty == "VLILII") {
- typedef void (fnptr)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jclass> klass(soa.Env(),
- soa.AddLocalReference<jclass>(method->GetDeclaringClass()));
- ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(args[0].GetL()));
- ScopedLocalRef<jobject> arg2(soa.Env(),
- soa.AddLocalReference<jobject>(args[2].GetL()));
- ScopedThreadStateChange tsc(self, kNative);
- fn(soa.Env(), klass.get(), arg0.get(), args[1].GetI(), arg2.get(), args[3].GetI(),
- args[4].GetI());
- } else {
- LOG(FATAL) << "Do something with static native method: " << PrettyMethod(method)
- << " shorty: " << shorty;
- }
+ // We don't expect to be asked to interpret native code (which is entered via a JNI compiler
+ // generated stub) except during testing and image writing.
+ if (!Runtime::Current()->IsStarted()) {
+ UnstartedRuntimeJni(self, method, receiver, args, result);
} else {
- if (shorty == "L") {
- typedef jobject (fnptr)(JNIEnv*, jobject);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jobject> rcvr(soa.Env(),
- soa.AddLocalReference<jobject>(receiver));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetL(soa.Decode<Object*>(fn(soa.Env(), rcvr.get())));
- } else if (shorty == "LL") {
- typedef jobject (fnptr)(JNIEnv*, jobject, jobject);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jobject> rcvr(soa.Env(),
- soa.AddLocalReference<jobject>(receiver));
- ScopedLocalRef<jobject> arg0(soa.Env(),
- soa.AddLocalReference<jobject>(args[0].GetL()));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetL(soa.Decode<Object*>(fn(soa.Env(), rcvr.get(), arg0.get())));
- } else if (shorty == "III") {
- typedef jint (fnptr)(JNIEnv*, jobject, jint, jint);
- fnptr* fn = reinterpret_cast<fnptr*>(method->GetNativeMethod());
- ScopedLocalRef<jobject> rcvr(soa.Env(),
- soa.AddLocalReference<jobject>(receiver));
- ScopedThreadStateChange tsc(self, kNative);
- result->SetI(fn(soa.Env(), rcvr.get(), args[0].GetI(), args[1].GetI()));
- } else {
- LOG(FATAL) << "Do something with native method: " << PrettyMethod(method)
- << " shorty: " << shorty;
- }
+ InterpreterJni(self, method, shorty, receiver, args, result);
}
}
self->PopShadowFrame();
diff --git a/src/invoke_arg_array_builder.h b/src/invoke_arg_array_builder.h
index e965a1ad18..0664ef8606 100644
--- a/src/invoke_arg_array_builder.h
+++ b/src/invoke_arg_array_builder.h
@@ -127,35 +127,35 @@ class ArgArray {
void BuildArgArray(const ShadowFrame& shadow_frame, uint32_t range_start)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- for (size_t i = 1, offset = range_start; i < shorty_len_; ++i, ++offset) {
+ for (size_t i = 1, offset = 0; i < shorty_len_; ++i, ++offset) {
switch (shorty_[i]) {
case 'Z':
- arg_array_[offset].SetZ(shadow_frame.GetVReg(offset));
+ arg_array_[offset].SetZ(shadow_frame.GetVReg(range_start + offset));
break;
case 'B':
- arg_array_[offset].SetB(shadow_frame.GetVReg(offset));
+ arg_array_[offset].SetB(shadow_frame.GetVReg(range_start + offset));
break;
case 'C':
- arg_array_[offset].SetC(shadow_frame.GetVReg(offset));
+ arg_array_[offset].SetC(shadow_frame.GetVReg(range_start + offset));
break;
case 'S':
- arg_array_[offset].SetS(shadow_frame.GetVReg(offset));
+ arg_array_[offset].SetS(shadow_frame.GetVReg(range_start + offset));
break;
case 'I':
- arg_array_[offset].SetI(shadow_frame.GetVReg(offset));
+ arg_array_[offset].SetI(shadow_frame.GetVReg(range_start + offset));
break;
case 'F':
- arg_array_[offset].SetF(shadow_frame.GetVRegFloat(offset));
+ arg_array_[offset].SetF(shadow_frame.GetVRegFloat(range_start + offset));
break;
case 'L':
- arg_array_[offset].SetL(shadow_frame.GetReference(offset));
+ arg_array_[offset].SetL(shadow_frame.GetReference(range_start + offset));
break;
case 'D':
- arg_array_[offset].SetD(shadow_frame.GetVRegDouble(offset));
+ arg_array_[offset].SetD(shadow_frame.GetVRegDouble(range_start + offset));
offset++;
break;
case 'J':
- arg_array_[offset].SetJ(shadow_frame.GetVRegLong(offset));
+ arg_array_[offset].SetJ(shadow_frame.GetVRegLong(range_start + offset));
offset++;
break;
}
diff --git a/src/native/java_lang_String.cc b/src/native/java_lang_String.cc
index bfdc31ac68..8b7a69162d 100644
--- a/src/native/java_lang_String.cc
+++ b/src/native/java_lang_String.cc
@@ -17,56 +17,19 @@
#include "jni_internal.h"
#include "object.h"
#include "scoped_thread_state_change.h"
-
-#ifdef HAVE__MEMCMP16
-// "count" is in 16-bit units.
-extern "C" uint32_t __memcmp16(const uint16_t* s0, const uint16_t* s1, size_t count);
-#define MemCmp16 __memcmp16
-#else
-uint32_t MemCmp16(const uint16_t* s0, const uint16_t* s1, size_t count) {
- for (size_t i = 0; i < count; i++) {
- if (s0[i] != s1[i]) {
- return static_cast<int32_t>(s0[i]) - static_cast<int32_t>(s1[i]);
- }
- }
- return 0;
-}
-#endif
+#include "ScopedLocalRef.h"
namespace art {
static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
- ScopedObjectAccess soa(env);
- String* lhs = soa.Decode<String*>(javaThis);
- String* rhs = soa.Decode<String*>(javaRhs);
-
- if (rhs == NULL) {
- Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "rhs == null");
+ if (UNLIKELY(javaRhs == NULL)) {
+ ScopedLocalRef<jclass> npe(env, env->FindClass("java/lang/NullPointerException"));
+ env->ThrowNew(npe.get(), "rhs == null");
return -1;
+ } else {
+ ScopedObjectAccess soa(env);
+ return soa.Decode<String*>(javaThis)->CompareTo(soa.Decode<String*>(javaRhs));
}
-
- // Quick test for comparison of a string with itself.
- if (lhs == rhs) {
- return 0;
- }
-
- // TODO: is this still true?
- // The annoying part here is that 0x00e9 - 0xffff != 0x00ea,
- // because the interpreter converts the characters to 32-bit integers
- // *without* sign extension before it subtracts them (which makes some
- // sense since "char" is unsigned). So what we get is the result of
- // 0x000000e9 - 0x0000ffff, which is 0xffff00ea.
- int lhsCount = lhs->GetLength();
- int rhsCount = rhs->GetLength();
- int countDiff = lhsCount - rhsCount;
- int minCount = (countDiff < 0) ? lhsCount : rhsCount;
- const uint16_t* lhsChars = lhs->GetCharArray()->GetData() + lhs->GetOffset();
- const uint16_t* rhsChars = rhs->GetCharArray()->GetData() + rhs->GetOffset();
- int otherRes = MemCmp16(lhsChars, rhsChars, minCount);
- if (otherRes != 0) {
- return otherRes;
- }
- return countDiff;
}
static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) {
@@ -75,24 +38,7 @@ static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint sta
DCHECK_LE(ch, 0xffff);
String* s = soa.Decode<String*>(java_this);
-
- jint count = s->GetLength();
- if (start < 0) {
- start = 0;
- } else if (start > count) {
- start = count;
- }
-
- const uint16_t* chars = s->GetCharArray()->GetData() + s->GetOffset();
- const uint16_t* p = chars + start;
- const uint16_t* end = chars + count;
- while (p < end) {
- if (*p++ == ch) {
- return (p - 1) - chars;
- }
- }
-
- return -1;
+ return s->FastIndexOf(ch, start);
}
static jstring String_intern(JNIEnv* env, jobject javaThis) {
diff --git a/src/native/java_lang_reflect_Array.cc b/src/native/java_lang_reflect_Array.cc
index 863f9fce7b..15aeed211d 100644
--- a/src/native/java_lang_reflect_Array.cc
+++ b/src/native/java_lang_reflect_Array.cc
@@ -23,55 +23,6 @@
namespace art {
-// Recursively create an array with multiple dimensions. Elements may be
-// Objects or primitive types.
-static Array* CreateMultiArray(Thread* self, Class* array_class, int current_dimension,
- IntArray* dimensions)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- int32_t array_length = dimensions->Get(current_dimension++);
- SirtRef<Array> new_array(self, Array::Alloc(self, array_class, array_length));
- if (new_array.get() == NULL) {
- CHECK(self->IsExceptionPending());
- return NULL;
- }
- if (current_dimension == dimensions->GetLength()) {
- return new_array.get();
- }
-
- if (!array_class->GetComponentType()->IsArrayClass()) {
- // TODO: throw an exception, not relying on class_linker->FindClass to throw.
- // old code assumed this but if you recurse from "[Foo" to "Foo" to "oo",
- // you shouldn't assume there isn't a class "oo".
- }
- std::string sub_array_descriptor(ClassHelper(array_class).GetDescriptor() + 1);
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Class* sub_array_class = class_linker->FindClass(sub_array_descriptor.c_str(),
- array_class->GetClassLoader());
- if (sub_array_class == NULL) {
- CHECK(self->IsExceptionPending());
- return NULL;
- }
- DCHECK(sub_array_class->IsArrayClass());
- // Create a new sub-array in every element of the array.
- SirtRef<ObjectArray<Array> > object_array(self, new_array->AsObjectArray<Array>());
- for (int32_t i = 0; i < array_length; i++) {
- SirtRef<Array> sub_array(self, CreateMultiArray(self, sub_array_class, current_dimension,
- dimensions));
- if (sub_array.get() == NULL) {
- CHECK(self->IsExceptionPending());
- return NULL;
- }
- object_array->Set(i, sub_array.get());
- }
- return new_array.get();
-}
-
-// Create a multi-dimensional array of Objects or primitive types.
-//
-// We have to generate the names for X[], X[][], X[][][], and so on. The
-// easiest way to deal with that is to create the full name once and then
-// subtract pieces off. Besides, we want to start with the outermost
-// piece and work our way in.
static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) {
ScopedObjectAccess soa(env);
DCHECK(javaElementClass != NULL);
@@ -82,41 +33,7 @@ static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementCla
DCHECK(dimensions_obj->IsArrayInstance());
DCHECK_STREQ(ClassHelper(dimensions_obj->GetClass()).GetDescriptor(), "[I");
IntArray* dimensions_array = down_cast<IntArray*>(dimensions_obj);
-
- // Verify dimensions.
- //
- // The caller is responsible for verifying that "dimArray" is non-null
- // and has a length > 0 and <= 255.
- int num_dimensions = dimensions_array->GetLength();
- DCHECK_GT(num_dimensions, 0);
- DCHECK_LE(num_dimensions, 255);
-
- for (int i = 0; i < num_dimensions; i++) {
- int dimension = dimensions_array->Get(i);
- if (dimension < 0) {
- soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;",
- "Dimension %d: %d", i, dimension);
- return NULL;
- }
- }
-
- // Generate the full name of the array class.
- std::string descriptor(num_dimensions, '[');
- descriptor += ClassHelper(element_class).GetDescriptor();
-
- // Find/generate the array class.
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader());
- if (array_class == NULL) {
- CHECK(soa.Self()->IsExceptionPending());
- return NULL;
- }
- // create the array
- Array* new_array = CreateMultiArray(soa.Self(), array_class, 0, dimensions_array);
- if (new_array == NULL) {
- CHECK(soa.Self()->IsExceptionPending());
- return NULL;
- }
+ Array* new_array = Array::CreateMultiArray(soa.Self(), element_class, dimensions_array);
return soa.AddLocalReference<jobject>(new_array);
}
@@ -124,26 +41,21 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl
ScopedObjectAccess soa(env);
DCHECK(javaElementClass != NULL);
Class* element_class = soa.Decode<Class*>(javaElementClass);
- if (length < 0) {
+ if (UNLIKELY(length < 0)) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
return NULL;
}
- std::string descriptor;
- descriptor += '[';
+ std::string descriptor("[");
descriptor += ClassHelper(element_class).GetDescriptor();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader());
- if (array_class == NULL) {
+ if (UNLIKELY(array_class == NULL)) {
CHECK(soa.Self()->IsExceptionPending());
return NULL;
}
DCHECK(array_class->IsArrayClass());
Array* new_array = Array::Alloc(soa.Self(), array_class, length);
- if (new_array == NULL) {
- CHECK(soa.Self()->IsExceptionPending());
- return NULL;
- }
return soa.AddLocalReference<jobject>(new_array);
}
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 0db71c9521..a371d284a7 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -720,13 +720,13 @@ class ImageDumper {
} else if (type->IsStringClass()) {
String* string = value->AsString();
StringAppendF(&summary, "%p String: \"%s\"\n", string, string->ToModifiedUtf8().c_str());
- } else if (value->IsClass()) {
+ } else if (type->IsClassClass()) {
Class* klass = value->AsClass();
StringAppendF(&summary, "%p Class: %s\n", klass, PrettyDescriptor(klass).c_str());
- } else if (value->IsField()) {
+ } else if (type->IsFieldClass()) {
Field* field = value->AsField();
StringAppendF(&summary, "%p Field: %s\n", field, PrettyField(field).c_str());
- } else if (value->IsMethod()) {
+ } else if (type->IsMethodClass()) {
AbstractMethod* method = value->AsMethod();
StringAppendF(&summary, "%p Method: %s\n", method, PrettyMethod(method).c_str());
} else {
@@ -880,7 +880,7 @@ class ImageDumper {
summary += "\t\tSTATICS:\n";
for (int32_t i = 0; i < sfields->GetLength(); i++) {
Field* field = sfields->Get(i);
- PrintField(summary, field, NULL);
+ PrintField(summary, field, field->GetDeclaringClass());
}
}
} else if (obj->IsMethod()) {
diff --git a/src/object.cc b/src/object.cc
index 9189f034fc..9a4588a74b 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -29,8 +29,8 @@
#include "dex_file.h"
#include "globals.h"
#include "heap.h"
-#include "interpreter/interpreter.h"
#include "intern_table.h"
+#include "interpreter/interpreter.h"
#include "logging.h"
#include "monitor.h"
#include "object_utils.h"
@@ -69,13 +69,15 @@ ShortArray* Object::AsShortArray() {
IntArray* Object::AsIntArray() {
DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveInt());
+ DCHECK(GetClass()->GetComponentType()->IsPrimitiveInt() ||
+ GetClass()->GetComponentType()->IsPrimitiveFloat());
return down_cast<IntArray*>(this);
}
LongArray* Object::AsLongArray() {
DCHECK(GetClass()->IsArrayClass());
- DCHECK(GetClass()->GetComponentType()->IsPrimitiveLong());
+ DCHECK(GetClass()->GetComponentType()->IsPrimitiveLong() ||
+ GetClass()->GetComponentType()->IsPrimitiveDouble());
return down_cast<LongArray*>(this);
}
@@ -314,26 +316,34 @@ void Field::SetShort(Object* object, int16_t s) const {
}
int32_t Field::GetInt(const Object* object) const {
- DCHECK_EQ(Primitive::kPrimInt, FieldHelper(this).GetTypeAsPrimitiveType())
- << PrettyField(this);
+#ifndef NDEBUG
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
+#endif
return Get32(object);
}
void Field::SetInt(Object* object, int32_t i) const {
- DCHECK_EQ(Primitive::kPrimInt, FieldHelper(this).GetTypeAsPrimitiveType())
- << PrettyField(this);
+#ifndef NDEBUG
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimInt || type == Primitive::kPrimFloat) << PrettyField(this);
+#endif
Set32(object, i);
}
int64_t Field::GetLong(const Object* object) const {
- DCHECK_EQ(Primitive::kPrimLong, FieldHelper(this).GetTypeAsPrimitiveType())
- << PrettyField(this);
+#ifndef NDEBUG
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
+#endif
return Get64(object);
}
void Field::SetLong(Object* object, int64_t j) const {
- DCHECK_EQ(Primitive::kPrimLong, FieldHelper(this).GetTypeAsPrimitiveType())
- << PrettyField(this);
+#ifndef NDEBUG
+ Primitive::Type type = FieldHelper(this).GetTypeAsPrimitiveType();
+ CHECK(type == Primitive::kPrimLong || type == Primitive::kPrimDouble) << PrettyField(this);
+#endif
Set64(object, j);
}
@@ -1031,6 +1041,19 @@ bool Class::IsThrowableClass() const {
return WellKnownClasses::ToClass(WellKnownClasses::java_lang_Throwable)->IsAssignableFrom(this);
}
+bool Class::IsFieldClass() const {
+ Class* java_lang_Class = GetClass();
+ Class* java_lang_reflect_Field = java_lang_Class->GetInstanceField(0)->GetClass();
+ return this == java_lang_reflect_Field;
+
+}
+
+bool Class::IsMethodClass() const {
+ return (this == AbstractMethod::GetMethodClass()) ||
+ (this == AbstractMethod::GetConstructorClass());
+
+}
+
ClassLoader* Class::GetClassLoader() const {
return GetFieldObject<ClassLoader*>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), false);
}
@@ -1358,6 +1381,76 @@ Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) {
return Alloc(self, array_class, component_count, array_class->GetComponentSize());
}
+// Create a multi-dimensional array of Objects or primitive types.
+//
+// We have to generate the names for X[], X[][], X[][][], and so on. The
+// easiest way to deal with that is to create the full name once and then
+// subtract pieces off. Besides, we want to start with the outermost
+// piece and work our way in.
+// Recursively create an array with multiple dimensions. Elements may be
+// Objects or primitive types.
+static Array* RecursiveCreateMultiArray(Thread* self, Class* array_class, int current_dimension,
+ IntArray* dimensions)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ int32_t array_length = dimensions->Get(current_dimension);
+ SirtRef<Array> new_array(self, Array::Alloc(self, array_class, array_length));
+ if (UNLIKELY(new_array.get() == NULL)) {
+ CHECK(self->IsExceptionPending());
+ return NULL;
+ }
+ if ((current_dimension + 1) < dimensions->GetLength()) {
+ // Create a new sub-array in every element of the array.
+ for (int32_t i = 0; i < array_length; i++) {
+ Array* sub_array = RecursiveCreateMultiArray(self, array_class->GetComponentType(),
+ current_dimension + 1, dimensions);
+ if (UNLIKELY(sub_array == NULL)) {
+ CHECK(self->IsExceptionPending());
+ return NULL;
+ }
+ new_array->AsObjectArray<Array>()->Set(i, sub_array);
+ }
+ }
+ return new_array.get();
+}
+
+Array* Array::CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions) {
+ // Verify dimensions.
+ //
+ // The caller is responsible for verifying that "dimArray" is non-null
+ // and has a length > 0 and <= 255.
+ int num_dimensions = dimensions->GetLength();
+ DCHECK_GT(num_dimensions, 0);
+ DCHECK_LE(num_dimensions, 255);
+
+ for (int i = 0; i < num_dimensions; i++) {
+ int dimension = dimensions->Get(i);
+ if (UNLIKELY(dimension < 0)) {
+ self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;",
+ "Dimension %d: %d", i, dimension);
+ return NULL;
+ }
+ }
+
+ // Generate the full name of the array class.
+ std::string descriptor(num_dimensions, '[');
+ descriptor += ClassHelper(element_class).GetDescriptor();
+
+ // Find/generate the array class.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader());
+ if (UNLIKELY(array_class == NULL)) {
+ CHECK(self->IsExceptionPending());
+ return NULL;
+ }
+ // create the array
+ Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions);
+ if (UNLIKELY(new_array == NULL)) {
+ CHECK(self->IsExceptionPending());
+ return NULL;
+ }
+ return new_array;
+}
+
bool Array::ThrowArrayIndexOutOfBoundsException(int32_t index) const {
Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
"length=%i; index=%i", length_, index);
@@ -1574,6 +1667,46 @@ std::string String::ToModifiedUtf8() const {
return result;
}
+#ifdef HAVE__MEMCMP16
+// "count" is in 16-bit units.
+extern "C" uint32_t __memcmp16(const uint16_t* s0, const uint16_t* s1, size_t count);
+#define MemCmp16 __memcmp16
+#else
+static uint32_t MemCmp16(const uint16_t* s0, const uint16_t* s1, size_t count) {
+ for (size_t i = 0; i < count; i++) {
+ if (s0[i] != s1[i]) {
+ return static_cast<int32_t>(s0[i]) - static_cast<int32_t>(s1[i]);
+ }
+ }
+ return 0;
+}
+#endif
+
+int32_t String::CompareTo(String* rhs) const {
+ // Quick test for comparison of a string with itself.
+ const String* lhs = this;
+ if (lhs == rhs) {
+ return 0;
+ }
+ // TODO: is this still true?
+ // The annoying part here is that 0x00e9 - 0xffff != 0x00ea,
+ // because the interpreter converts the characters to 32-bit integers
+ // *without* sign extension before it subtracts them (which makes some
+ // sense since "char" is unsigned). So what we get is the result of
+ // 0x000000e9 - 0x0000ffff, which is 0xffff00ea.
+ int lhsCount = lhs->GetLength();
+ int rhsCount = rhs->GetLength();
+ int countDiff = lhsCount - rhsCount;
+ int minCount = (countDiff < 0) ? lhsCount : rhsCount;
+ const uint16_t* lhsChars = lhs->GetCharArray()->GetData() + lhs->GetOffset();
+ const uint16_t* rhsChars = rhs->GetCharArray()->GetData() + rhs->GetOffset();
+ int otherRes = MemCmp16(lhsChars, rhsChars, minCount);
+ if (otherRes != 0) {
+ return otherRes;
+ }
+ return countDiff;
+}
+
void Throwable::SetCause(Throwable* cause) {
CHECK(cause != NULL);
CHECK(cause != this);
diff --git a/src/object.h b/src/object.h
index 87f132e458..79df4e217d 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1070,6 +1070,9 @@ class MANAGED Array : public Object {
size_t component_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static Array* CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
size_t SizeOf() const;
int32_t GetLength() const {
@@ -1493,6 +1496,10 @@ class MANAGED Class : public StaticStorageBase {
bool IsThrowableClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsFieldClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool IsMethodClass() const;
+
Class* GetComponentType() const {
return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), false);
}
@@ -2166,16 +2173,11 @@ inline bool Object::IsArrayInstance() const {
}
inline bool Object::IsField() const {
- Class* java_lang_Class = klass_->klass_;
- Class* java_lang_reflect_Field = java_lang_Class->GetInstanceField(0)->GetClass();
- return GetClass() == java_lang_reflect_Field;
+ return GetClass()->IsFieldClass();
}
inline bool Object::IsMethod() const {
- Class* c = GetClass();
- return
- c == AbstractMethod::GetMethodClass() ||
- c == AbstractMethod::GetConstructorClass();
+ return GetClass()->IsMethodClass();
}
inline bool Object::IsReferenceInstance() const {
@@ -2488,6 +2490,26 @@ class MANAGED String : public Object {
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string ToModifiedUtf8() const;
+ int32_t FastIndexOf(int32_t ch, int32_t start) {
+ int32_t count = GetLength();
+ if (start < 0) {
+ start = 0;
+ } else if (start > count) {
+ start = count;
+ }
+ const uint16_t* chars = GetCharArray()->GetData() + GetOffset();
+ const uint16_t* p = chars + start;
+ const uint16_t* end = chars + count;
+ while (p < end) {
+ if (*p++ == ch) {
+ return (p - 1) - chars;
+ }
+ }
+ return -1;
+ }
+
+ int32_t CompareTo(String* other) const;
+
static Class* GetJavaLangString() {
DCHECK(java_lang_String_ != NULL);
return java_lang_String_;
diff --git a/src/object_test.cc b/src/object_test.cc
index e0443d0d18..f3b6a19739 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -206,6 +206,40 @@ TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive());
}
+TEST_F(ObjectTest, CreateMultiArray) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ SirtRef<Class> c(soa.Self(), class_linker_->FindSystemClass("I"));
+ SirtRef<IntArray> dims(soa.Self(), IntArray::Alloc(soa.Self(), 1));
+ dims->Set(0, 1);
+ Array* multi = Array::CreateMultiArray(soa.Self(), c.get(), dims.get());
+ EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[I"));
+ EXPECT_EQ(1, multi->GetLength());
+
+ dims->Set(0, -1);
+ multi = Array::CreateMultiArray(soa.Self(), c.get(), dims.get());
+ EXPECT_TRUE(soa.Self()->IsExceptionPending());
+ EXPECT_EQ(PrettyDescriptor(soa.Self()->GetException()->GetClass()),
+ "java.lang.NegativeArraySizeException");
+ soa.Self()->ClearException();
+
+ 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);
+ multi = Array::CreateMultiArray(soa.Self(), c.get(), dims.get());
+ EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass("[[I"));
+ EXPECT_EQ(i, multi->GetLength());
+ for (int k = 0; k < i; ++k) {
+ Array* outer = multi->AsObjectArray<Array>()->Get(k);
+ EXPECT_TRUE(outer->GetClass() == class_linker_->FindSystemClass("[I"));
+ EXPECT_EQ(j, outer->GetLength());
+ }
+ }
+ }
+}
+
TEST_F(ObjectTest, StaticFieldFromCode) {
// pretend we are trying to access 'Static.s0' from StaticsFromCode.<clinit>
ScopedObjectAccess soa(Thread::Current());
@@ -239,7 +273,7 @@ TEST_F(ObjectTest, StaticFieldFromCode) {
Field* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), StaticObjectRead,
sizeof(Object*));
Object* s0 = field->GetObj(klass);
- EXPECT_EQ(NULL, s0);
+ EXPECT_TRUE(s0 != NULL);
SirtRef<CharArray> char_array(soa.Self(), CharArray::Alloc(soa.Self(), 0));
field->SetObj(field->GetDeclaringClass(), char_array.get());
@@ -306,6 +340,22 @@ TEST_F(ObjectTest, StringEquals) {
EXPECT_FALSE(empty->Equals("a"));
}
+TEST_F(ObjectTest, StringCompareTo) {
+ ScopedObjectAccess soa(Thread::Current());
+ SirtRef<String> string(soa.Self(), String::AllocFromModifiedUtf8(soa.Self(), "android"));
+ SirtRef<String> string_2(soa.Self(), String::AllocFromModifiedUtf8(soa.Self(), "android"));
+ SirtRef<String> string_3(soa.Self(), String::AllocFromModifiedUtf8(soa.Self(), "Android"));
+ SirtRef<String> string_4(soa.Self(), String::AllocFromModifiedUtf8(soa.Self(), "and"));
+ SirtRef<String> string_5(soa.Self(), String::AllocFromModifiedUtf8(soa.Self(), ""));
+ EXPECT_EQ(0, string->CompareTo(string_2.get()));
+ EXPECT_LT(0, string->CompareTo(string_3.get()));
+ EXPECT_GT(0, string_3->CompareTo(string.get()));
+ EXPECT_LT(0, string->CompareTo(string_4.get()));
+ EXPECT_GT(0, string_4->CompareTo(string.get()));
+ EXPECT_LT(0, string->CompareTo(string_5.get()));
+ EXPECT_GT(0, string_5->CompareTo(string.get()));
+}
+
TEST_F(ObjectTest, StringLength) {
ScopedObjectAccess soa(Thread::Current());
SirtRef<String> string(soa.Self(), String::AllocFromModifiedUtf8(soa.Self(), "android"));
diff --git a/src/thread.cc b/src/thread.cc
index 2f8a9a77f9..f6e7249fde 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1340,7 +1340,7 @@ class BuildInternalStackTraceVisitor : public StackVisitor {
ObjectArray<Object>* method_trace_;
};
-jobject Thread::CreateInternalStackTrace(const ScopedObjectAccess& soa) const {
+jobject Thread::CreateInternalStackTrace(const ScopedObjectAccessUnchecked& soa) const {
// Compute depth of stack
CountStackDepthVisitor count_visitor(GetManagedStack(), GetTraceStack());
count_visitor.WalkStack();
@@ -1483,6 +1483,9 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, co
ScopedObjectAccessUnchecked soa(env);
Throwable* t = reinterpret_cast<Throwable*>(soa.Self()->DecodeJObject(exception.get()));
t->SetDetailMessage(String::AllocFromModifiedUtf8(soa.Self(), msg));
+ if (cause != NULL) {
+ t->SetCause(soa.Decode<Throwable*>(cause));
+ }
soa.Self()->SetException(t);
} else {
LOG(ERROR) << "Couldn't throw new " << descriptor << " because JNI AllocObject failed: "
diff --git a/src/thread.h b/src/thread.h
index 798e96a908..d281ea21fc 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -399,7 +399,7 @@ class PACKED Thread {
// Create the internal representation of a stack trace, that is more time
// and space efficient to compute than the StackTraceElement[]
- jobject CreateInternalStackTrace(const ScopedObjectAccess& soa) const
+ jobject CreateInternalStackTrace(const ScopedObjectAccessUnchecked& soa) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Convert an internal stack trace representation (returned by CreateInternalStackTrace) to a
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 9a933bf7bc..2149490a8c 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -3258,6 +3258,7 @@ const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(Compiler::MethodReferenc
MutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref);
if (it == dex_gc_maps_->end()) {
+ LOG(WARNING) << "Didn't find GC map for: " << PrettyMethod(ref.second, *ref.first);
return NULL;
}
CHECK(it->second != NULL);