* Fix many memory leaks in Android FS code
authorTim Angus <tim@blackcompanystudios.co.uk>
Fri, 26 Aug 2011 13:11:53 +0100
changeset 5650640c67302f8e
parent 5647 022880331cac
child 5658 91c9a69dd2ad
* Fix many memory leaks in Android FS code
* Set SDL error string with Java exception details when one occurs
* Fix tabulation of SDLActivity::getContext
android-project/src/org/libsdl/app/SDLActivity.java
src/core/android/SDL_android.cpp
     1.1 --- a/android-project/src/org/libsdl/app/SDLActivity.java	Fri Aug 26 03:38:46 2011 -0400
     1.2 +++ b/android-project/src/org/libsdl/app/SDLActivity.java	Fri Aug 26 13:11:53 2011 +0100
     1.3 @@ -114,9 +114,9 @@
     1.4          mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
     1.5      }
     1.6  
     1.7 -	public static Context getContext() {
     1.8 -		return mSingleton;
     1.9 -	}
    1.10 +    public static Context getContext() {
    1.11 +        return mSingleton;
    1.12 +    }
    1.13  
    1.14      // Audio
    1.15      private static Object buf;
     2.1 --- a/src/core/android/SDL_android.cpp	Fri Aug 26 03:38:46 2011 -0400
     2.2 +++ b/src/core/android/SDL_android.cpp	Fri Aug 26 13:11:53 2011 +0100
     2.3 @@ -259,35 +259,92 @@
     2.4      }
     2.5  }
     2.6  
     2.7 +// Test for an exception and call SDL_SetError with its detail if one occurs
     2.8 +static bool Android_JNI_ExceptionOccurred()
     2.9 +{
    2.10 +    jthrowable exception = mEnv->ExceptionOccurred();
    2.11 +    if (exception != NULL) {
    2.12 +        jmethodID mid;
    2.13 +
    2.14 +        // Until this happens most JNI operations have undefined behaviour
    2.15 +        mEnv->ExceptionClear();
    2.16 +
    2.17 +        jclass exceptionClass = mEnv->GetObjectClass(exception);
    2.18 +        jclass classClass = mEnv->FindClass("java/lang/Class");
    2.19 +
    2.20 +        mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;");
    2.21 +        jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
    2.22 +        const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0);
    2.23 +
    2.24 +        mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;");
    2.25 +        jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exceptionClass, mid);
    2.26 +
    2.27 +        if (exceptionMessage != NULL) {
    2.28 +            const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars(
    2.29 +                    exceptionMessage, 0);
    2.30 +            SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8);
    2.31 +            mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8);
    2.32 +            mEnv->DeleteLocalRef(exceptionMessage);
    2.33 +        } else {
    2.34 +            SDL_SetError("%s", exceptionNameUTF8);
    2.35 +        }
    2.36 +
    2.37 +        mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8);
    2.38 +        mEnv->DeleteLocalRef(exceptionName);
    2.39 +        mEnv->DeleteLocalRef(classClass);
    2.40 +        mEnv->DeleteLocalRef(exceptionClass);
    2.41 +        mEnv->DeleteLocalRef(exception);
    2.42 +
    2.43 +        return true;
    2.44 +    }
    2.45 +
    2.46 +    return false;
    2.47 +}
    2.48 +
    2.49  static int Android_JNI_FileOpen(SDL_RWops* ctx)
    2.50  {
    2.51 -    jstring fileNameJString = (jstring)ctx->hidden.androidio.fileName;
    2.52 +    int result = 0;
    2.53 +
    2.54 +    jmethodID mid;
    2.55 +    jobject context;
    2.56 +    jobject assetManager;
    2.57 +    jobject inputStream;
    2.58 +    jclass channels;
    2.59 +    jobject readableByteChannel;
    2.60 +    jstring fileNameJString;
    2.61 +
    2.62 +    bool allocatedLocalFrame = false;
    2.63 +
    2.64 +    if (mEnv->PushLocalFrame(16) < 0) {
    2.65 +        SDL_SetError("Failed to allocate enough JVM local references");
    2.66 +        goto failure;
    2.67 +    } else {
    2.68 +        allocatedLocalFrame = true;
    2.69 +    }
    2.70 +
    2.71 +    fileNameJString = (jstring)ctx->hidden.androidio.fileName;
    2.72  
    2.73      // context = SDLActivity.getContext();
    2.74 -    jmethodID mid = mEnv->GetStaticMethodID(mActivityClass,
    2.75 +    mid = mEnv->GetStaticMethodID(mActivityClass,
    2.76              "getContext","()Landroid/content/Context;");
    2.77 -    jobject context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
    2.78 +    context = mEnv->CallStaticObjectMethod(mActivityClass, mid);
    2.79  
    2.80      // assetManager = context.getAssets();
    2.81      mid = mEnv->GetMethodID(mEnv->GetObjectClass(context),
    2.82 -            "getAssets","()Landroid/content/res/AssetManager;");
    2.83 -    jobject assetManager = mEnv->CallObjectMethod(context, mid);
    2.84 +            "getAssets", "()Landroid/content/res/AssetManager;");
    2.85 +    assetManager = mEnv->CallObjectMethod(context, mid);
    2.86  
    2.87      // inputStream = assetManager.open(<filename>);
    2.88 -    mEnv->ExceptionClear();
    2.89      mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
    2.90              "open", "(Ljava/lang/String;)Ljava/io/InputStream;");
    2.91 -    jobject inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
    2.92 -    if (mEnv->ExceptionOccurred()) {
    2.93 -        mEnv->ExceptionDescribe();
    2.94 -        mEnv->ExceptionClear();
    2.95 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
    2.96 -        return -1;
    2.97 -    } else {
    2.98 -        ctx->hidden.androidio.inputStream = inputStream;
    2.99 -        ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
   2.100 +    inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString);
   2.101 +    if (Android_JNI_ExceptionOccurred()) {
   2.102 +        goto failure;
   2.103      }
   2.104  
   2.105 +    ctx->hidden.androidio.inputStream = inputStream;
   2.106 +    ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
   2.107 +
   2.108      // Store .skip id for seeking purposes
   2.109      mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
   2.110              "skip", "(J)J");
   2.111 @@ -300,38 +357,28 @@
   2.112      // AssetInputStream.available() /will/ always return the total file size
   2.113  
   2.114      // size = inputStream.available();
   2.115 -    mEnv->ExceptionClear();
   2.116      mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
   2.117              "available", "()I");
   2.118      ctx->hidden.androidio.size = mEnv->CallIntMethod(inputStream, mid);
   2.119 -    if (mEnv->ExceptionOccurred()) {
   2.120 -        mEnv->ExceptionDescribe();
   2.121 -        mEnv->ExceptionClear();
   2.122 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
   2.123 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   2.124 -        return -1;
   2.125 +    if (Android_JNI_ExceptionOccurred()) {
   2.126 +        goto failure;
   2.127      }
   2.128  
   2.129      // readableByteChannel = Channels.newChannel(inputStream);
   2.130 -    mEnv->ExceptionClear();
   2.131 -    jclass channels = mEnv->FindClass("java/nio/channels/Channels");
   2.132 +    channels = mEnv->FindClass("java/nio/channels/Channels");
   2.133      mid = mEnv->GetStaticMethodID(channels,
   2.134              "newChannel",
   2.135              "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
   2.136 -    jobject readableByteChannel = mEnv->CallStaticObjectMethod(
   2.137 +    readableByteChannel = mEnv->CallStaticObjectMethod(
   2.138              channels, mid, inputStream);
   2.139 -    if (mEnv->ExceptionOccurred()) {
   2.140 -        mEnv->ExceptionDescribe();
   2.141 -        mEnv->ExceptionClear();
   2.142 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
   2.143 -        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   2.144 -        return -1;
   2.145 -    } else {
   2.146 -        ctx->hidden.androidio.readableByteChannel = readableByteChannel;
   2.147 -        ctx->hidden.androidio.readableByteChannelRef =
   2.148 -            mEnv->NewGlobalRef(readableByteChannel);
   2.149 +    if (Android_JNI_ExceptionOccurred()) {
   2.150 +        goto failure;
   2.151      }
   2.152  
   2.153 +    ctx->hidden.androidio.readableByteChannel = readableByteChannel;
   2.154 +    ctx->hidden.androidio.readableByteChannelRef =
   2.155 +        mEnv->NewGlobalRef(readableByteChannel);
   2.156 +
   2.157      // Store .read id for reading purposes
   2.158      mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel),
   2.159              "read", "(Ljava/nio/ByteBuffer;)I");
   2.160 @@ -339,7 +386,22 @@
   2.161  
   2.162      ctx->hidden.androidio.position = 0;
   2.163  
   2.164 -    return 0;
   2.165 +    if (false) {
   2.166 +failure:
   2.167 +        result = -1;
   2.168 +
   2.169 +        mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
   2.170 +
   2.171 +        if(ctx->hidden.androidio.inputStreamRef != NULL) {
   2.172 +            mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   2.173 +        }
   2.174 +    }
   2.175 +
   2.176 +    if (allocatedLocalFrame) {
   2.177 +        mEnv->PopLocalFrame(NULL);
   2.178 +    }
   2.179 +
   2.180 +    return result;
   2.181  }
   2.182  
   2.183  extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx,
   2.184 @@ -352,6 +414,8 @@
   2.185      jstring fileNameJString = mEnv->NewStringUTF(fileName);
   2.186      ctx->hidden.androidio.fileName = fileNameJString;
   2.187      ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString);
   2.188 +    ctx->hidden.androidio.inputStreamRef = NULL;
   2.189 +    mEnv->DeleteLocalRef(fileNameJString);
   2.190  
   2.191      return Android_JNI_FileOpen(ctx);
   2.192  }
   2.193 @@ -366,14 +430,12 @@
   2.194      jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
   2.195      jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining);
   2.196  
   2.197 -    mEnv->ExceptionClear();
   2.198      while (bytesRemaining > 0) {
   2.199          // result = readableByteChannel.read(...);
   2.200          int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer);
   2.201  
   2.202 -        if (mEnv->ExceptionOccurred()) {
   2.203 -            mEnv->ExceptionDescribe();
   2.204 -            mEnv->ExceptionClear();
   2.205 +        if (Android_JNI_ExceptionOccurred()) {
   2.206 +            mEnv->DeleteLocalRef(byteBuffer);
   2.207              return 0;
   2.208          }
   2.209  
   2.210 @@ -386,6 +448,8 @@
   2.211          ctx->hidden.androidio.position += result;
   2.212      }
   2.213  
   2.214 +    mEnv->DeleteLocalRef(byteBuffer);
   2.215 +
   2.216      return bytesRead / size;
   2.217  }
   2.218  
   2.219 @@ -408,16 +472,13 @@
   2.220          jobject inputStream = (jobject)ctx->hidden.androidio.inputStream;
   2.221  
   2.222          // inputStream.close();
   2.223 -        mEnv->ExceptionClear();
   2.224          jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
   2.225                  "close", "()V");
   2.226          mEnv->CallVoidMethod(inputStream, mid);
   2.227          mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef);
   2.228          mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
   2.229 -        if (mEnv->ExceptionOccurred()) {
   2.230 +        if (Android_JNI_ExceptionOccurred()) {
   2.231              result = -1;
   2.232 -            mEnv->ExceptionDescribe();
   2.233 -            mEnv->ExceptionClear();
   2.234          }
   2.235  
   2.236          if (release) {
   2.237 @@ -460,14 +521,10 @@
   2.238  
   2.239      if (movement > 0) {
   2.240          // The easy case where we're seeking forwards
   2.241 -        mEnv->ExceptionClear();
   2.242          while (movement > 0) {
   2.243              // inputStream.skip(...);
   2.244              movement -= mEnv->CallLongMethod(inputStream, skipMethod, movement);
   2.245 -            if (mEnv->ExceptionOccurred()) {
   2.246 -                mEnv->ExceptionDescribe();
   2.247 -                mEnv->ExceptionClear();
   2.248 -                SDL_SetError("Exception while seeking");
   2.249 +            if (Android_JNI_ExceptionOccurred()) {
   2.250                  return -1;
   2.251              }
   2.252          }