| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define MaxPath 1024 |
| |
| #include "JNIHelp.h" |
| |
| #include <string.h> |
| #include <fcntl.h> |
| #include <time.h> |
| #include <utime.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <assert.h> |
| |
| |
| /* these were copied from java.io.File */ |
| enum { |
| STAT_TYPE_EXISTS = 0x0001, |
| STAT_TYPE_DIR = 0x0002, |
| STAT_TYPE_FILE = 0x0004 |
| }; |
| |
| static void convertToPlatform(char *path) { |
| char *pathIndex; |
| |
| pathIndex = path; |
| while(*pathIndex != '\0') { |
| if(*pathIndex == '\\') { |
| *pathIndex = '/'; |
| } |
| pathIndex++; |
| } |
| } |
| |
| /* |
| * private static native byte[][] rootsImpl() |
| * |
| * Returns the linux root in an array of byte arrays |
| */ |
| static jobject java_io_File_rootsImpl(JNIEnv* env, jclass clazz) { |
| char rootStrings[3]; |
| jarray answer; |
| |
| rootStrings[0] = '/'; |
| rootStrings[1] = '\0'; |
| rootStrings[2] = '\0'; |
| |
| jclass arrayClass = (*env)->FindClass(env, "[B"); |
| if (arrayClass == NULL) |
| return NULL; |
| |
| answer = (*env)->NewObjectArray(env, 1, arrayClass, NULL); |
| if (!answer) |
| return NULL; |
| |
| jbyteArray rootname; |
| |
| rootname = (*env)->NewByteArray(env, 3); |
| (*env)->SetByteArrayRegion(env, rootname, 0, 3, (jbyte *) rootStrings); |
| (*env)->SetObjectArrayElement(env, answer, 0, rootname); |
| //(*env)->DeleteLocalRef(env, rootname); |
| |
| return answer; |
| } |
| |
| static jbyteArray java_io_File_getCanonImpl(JNIEnv * env, jobject recv, |
| jbyteArray path) { |
| /* This needs work. Currently it does no more or less than VAJ-20 ST |
| * implementationbut really should figure out '..', '.', and really |
| * resolve references. |
| */ |
| jbyteArray answer; |
| size_t answerlen; |
| char *pathIndex; |
| char pathCopy[MaxPath]; |
| jsize length = (jsize) (*env)->GetArrayLength(env, path); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); |
| pathCopy[length] = '\0'; |
| |
| convertToPlatform(pathCopy); |
| |
| answerlen = strlen(pathCopy); |
| answer = (*env)->NewByteArray(env, answerlen); |
| (*env)->SetByteArrayRegion(env, answer, 0, answerlen, (jbyte *) pathCopy); |
| |
| return answer; |
| } |
| |
| /* |
| * native private boolean deleteFileImpl() |
| * |
| * Returns "true" if the file exists and was successfully deleted. |
| */ |
| static jboolean java_io_File_deleteFileImpl(JNIEnv* env, jobject obj, |
| jbyteArray path) { |
| |
| int cc; |
| |
| if(path == NULL) { |
| return JNI_FALSE; /* exception thrown */ |
| } |
| |
| char pathCopy[MaxPath]; |
| jsize length = (jsize) (*env)->GetArrayLength(env, path); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| cc = unlink(pathCopy); |
| if(cc < 0) { |
| int err = errno; |
| |
| /* |
| * According to the man pages, Linux uses EISDIR and Mac OS X |
| * uses EPERM to indicate a non-super-user attempt to unlink |
| * a directory. Mac OS does have EISDIR in the header file. |
| * |
| * We should get EACCES if the problem is directory permissions. |
| */ |
| if(err == EISDIR || err == EPERM) { |
| cc = rmdir(pathCopy); |
| if(cc < 0) { |
| /* probably ENOTEMPTY */ |
| LOGD("unable to rmdir '%s': %s (errno=%d)\n", |
| pathCopy, strerror(err), err); |
| } |
| } else { |
| LOGD("unable to unlink '%s': %s (errno=%d)\n", |
| pathCopy, strerror(err), err); |
| } |
| } |
| |
| return (cc == 0); |
| } |
| |
| /* |
| * harmony implements this method practically identical to the deleteFileImpl, |
| * except that it uses a diffrent helper method from hyport. Dalvik seems to |
| * just need this one method to delete both |
| */ |
| static jboolean java_io_File_deleteDirImpl(JNIEnv * env, jobject recv, |
| jbyteArray path) { |
| return java_io_File_deleteFileImpl( env, recv, path); |
| } |
| |
| /* |
| * native public long lengthImpl() |
| * |
| * Returns the file length, or 0 if the file does not exist. The result for |
| * a directory is not defined. |
| */ |
| static jlong java_io_File_lengthImpl(JNIEnv* env, jobject obj, |
| jbyteArray path) { |
| struct stat sb; |
| jlong result = 0; |
| int cc; |
| |
| char pathCopy[MaxPath]; |
| jsize length = (jsize) (*env)->GetArrayLength(env, path); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| cc = stat(pathCopy, &sb); |
| if(cc == 0) { |
| // BEGIN android-added |
| /* |
| * This explicitly treats non-regular files (e.g., sockets and |
| * block-special devices) as having size zero. Some synthetic |
| * "regular" files may report an arbitrary non-zero size, but |
| * in these cases they generally report a block count of zero. |
| * So, use a zero block count to trump any other concept of |
| * size. |
| */ |
| if (S_ISREG(sb.st_mode) && (sb.st_blocks != 0)) { |
| result = sb.st_size; |
| } else { |
| result = 0; |
| } |
| // END android-added |
| // BEGIN android-deleted |
| //result = sb.st_size; |
| // END android-deleted |
| } |
| |
| return result; |
| } |
| |
| /* |
| * native public long lastModified() |
| * |
| * Get the last modified date of the file. Measured in milliseconds |
| * from epoch (00:00:00 GMT, January 1, 1970). Returns 0 if the file does |
| * not exist |
| */ |
| static jlong java_io_File_lastModifiedImpl(JNIEnv* env, jobject obj, |
| jbyteArray path) { |
| struct stat sb; |
| jlong result = 0; |
| int cc; |
| |
| char pathCopy[MaxPath]; |
| jsize length = (jsize) (*env)->GetArrayLength(env, path); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| cc = stat(pathCopy, &sb); |
| if(cc == 0) { |
| // sb.st_mtime is a time_t which is in seconds since epoch. |
| result = sb.st_mtime; |
| result *= 1000L; |
| } |
| |
| return result; |
| } |
| |
| /* |
| * private static native int stattype(String path) |
| */ |
| static jint java_io_File_stattype(JNIEnv* env, jobject recv, |
| jbyteArray pathStr) { |
| |
| char pathCopy[MaxPath]; |
| jsize length = (jsize) (*env)->GetArrayLength(env, pathStr); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)pathCopy); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| struct stat sb; |
| int cc, type; |
| |
| type = 0; |
| cc = stat(pathCopy, &sb); |
| |
| if(cc == 0) { |
| type |= STAT_TYPE_EXISTS; |
| if(S_ISDIR(sb.st_mode)) { |
| type |= STAT_TYPE_DIR; |
| } else if(S_ISREG(sb.st_mode)) { |
| type |= STAT_TYPE_FILE; |
| } |
| } |
| |
| return type; |
| } |
| |
| static jboolean java_io_File_isDirectoryImpl(JNIEnv* env, jobject recv, |
| jbyteArray pathStr) { |
| return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_DIR) |
| == STAT_TYPE_DIR); |
| } |
| |
| static jboolean java_io_File_existsImpl(JNIEnv* env, jobject recv, |
| jbyteArray pathStr) { |
| return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_EXISTS) |
| == STAT_TYPE_EXISTS); |
| } |
| |
| static jboolean java_io_File_isFileImpl(JNIEnv* env, jobject recv, |
| jbyteArray pathStr) { |
| return ((java_io_File_stattype(env, recv, pathStr) & STAT_TYPE_FILE) |
| == STAT_TYPE_FILE); |
| } |
| |
| static jboolean java_io_File_isHiddenImpl(JNIEnv * env, jobject recv, |
| jbyteArray path) { |
| |
| char pathCopy[MaxPath]; |
| jsize index; |
| jsize length = (*env)->GetArrayLength(env, path); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| |
| if(length == 0) { |
| return 0; |
| } |
| |
| ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| if(!java_io_File_existsImpl(env, recv, path)) { |
| return 0; |
| } |
| |
| for(index = length; index >= 0; index--) { |
| if(pathCopy[index] == '.' |
| && (index > 0 && pathCopy[index - 1] == '/')) { |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static jboolean java_io_File_readable(JNIEnv* env, jobject recv, |
| jbyteArray pathStr) { |
| char path[MaxPath]; |
| struct stat sb; |
| int cc, type; |
| |
| if(pathStr == NULL) { |
| jniThrowException(env, "java/lang/NullPointerException", NULL); |
| return -1; |
| } |
| |
| jsize length = (jsize) (*env)->GetArrayLength(env, pathStr); |
| |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)path); |
| path[length] = '\0'; |
| convertToPlatform(path); |
| |
| cc = access(path, R_OK); |
| |
| return cc == 0; |
| } |
| |
| static jboolean java_io_File_writable(JNIEnv* env, jobject recv, |
| jbyteArray pathStr) { |
| char path[MaxPath]; |
| struct stat sb; |
| int cc, type; |
| |
| if(pathStr == NULL) { |
| jniThrowException(env, "java/lang/NullPointerException", NULL); |
| return -1; |
| } |
| |
| jsize length = (jsize) (*env)->GetArrayLength(env, pathStr); |
| |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, pathStr, 0, length, (jbyte *)path); |
| path[length] = '\0'; |
| convertToPlatform(path); |
| |
| cc = access(path, W_OK); |
| |
| return cc == 0; |
| } |
| |
| // BEGIN android-deleted |
| #if 0 |
| static jboolean java_io_File_isReadOnlyImpl(JNIEnv* env, jobject recv, |
| jbyteArray path) { |
| |
| return (java_io_File_readable(env, recv, path) |
| && !java_io_File_writable(env, recv, path)); |
| } |
| |
| static jboolean java_io_File_isWriteOnlyImpl(JNIEnv* env, jobject recv, |
| jbyteArray path) { |
| |
| return (!java_io_File_readable(env, recv, path) |
| && java_io_File_writable(env, recv, path)); |
| } |
| #endif |
| // END android-deleted |
| |
| static jbyteArray java_io_File_getLinkImpl(JNIEnv* env, jobject recv, |
| jbyteArray path) { |
| jbyteArray answer; |
| jsize answerlen; |
| char pathCopy[MaxPath]; |
| |
| jsize length = (jsize) (*env)->GetArrayLength(env, path); |
| |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| jboolean test = -1; |
| |
| char *link = pathCopy; |
| |
| int size = readlink(link, link, MaxPath); |
| if(size <= 0) { |
| test = 0; |
| } else { |
| if(size >= MaxPath) { |
| link[MaxPath - 1] = 0; |
| } else { |
| link[size] = 0; |
| } |
| } |
| |
| if(test) { |
| answerlen = strlen(pathCopy); |
| answer = (*env)->NewByteArray(env, answerlen); |
| (*env)->SetByteArrayRegion(env, answer, 0, answerlen, |
| (jbyte *) pathCopy); |
| } else { |
| answer = path; |
| } |
| |
| return answer; |
| } |
| |
| static jboolean java_io_File_setLastModifiedImpl(JNIEnv* env, jobject recv, |
| jbyteArray path, jlong time) { |
| jboolean result; |
| |
| jsize length = (*env)->GetArrayLength(env, path); |
| char pathCopy[MaxPath]; |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| struct stat statbuf; |
| struct utimbuf timebuf; |
| if(stat(pathCopy, &statbuf)) { |
| result = 0; |
| } else { |
| timebuf.actime = statbuf.st_atime; |
| timebuf.modtime = (time_t) (time / 1000); |
| result = utime(pathCopy, &timebuf) == 0; |
| } |
| |
| return result; |
| } |
| |
| static jboolean java_io_File_setReadOnlyImpl(JNIEnv* env, jobject recv, |
| jbyteArray path) { |
| jsize length = (*env)->GetArrayLength(env, path); |
| char pathCopy[MaxPath]; |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| |
| ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); |
| |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| struct stat buffer; |
| mode_t mode; |
| if(stat(pathCopy, &buffer)) { |
| return 0; |
| } |
| mode = buffer.st_mode; |
| mode = mode & 07555; |
| |
| return chmod(pathCopy, mode) == 0; |
| } |
| |
| static jobject java_io_File_listImpl(JNIEnv* env, jclass clazz, |
| jbyteArray path) { |
| |
| struct dirEntry { |
| char pathEntry[MaxPath]; |
| struct dirEntry *next; |
| } *dirList, *currentEntry; |
| |
| jsize length = (*env)->GetArrayLength(env, path); |
| char pathCopy[MaxPath]; |
| char filename[MaxPath]; |
| jint result = 0, index; |
| jint numEntries = 0; |
| jarray answer = NULL; |
| jclass javaClass = NULL; |
| |
| dirList = NULL; |
| currentEntry = NULL; |
| |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); |
| if(length >= 1 && pathCopy[length - 1] != '\\' |
| && pathCopy[length - 1] != '/') { |
| pathCopy[length] = '/'; |
| length++; |
| } |
| pathCopy[length] = '\0'; |
| |
| convertToPlatform(pathCopy); |
| |
| DIR *dirp = NULL; |
| struct dirent *entry; |
| |
| dirp = opendir(pathCopy); |
| |
| if(dirp == NULL) { |
| return NULL; |
| } |
| |
| entry = readdir(dirp); |
| |
| if(entry == NULL) { |
| closedir(dirp); |
| return NULL; |
| } |
| strcpy(filename, entry->d_name); |
| |
| while(result > -1) { |
| if(strcmp(".", filename) != 0 && (strcmp("..", filename) != 0)) { |
| if(numEntries > 0) { |
| currentEntry->next = |
| (struct dirEntry *) malloc(sizeof(struct dirEntry)); |
| currentEntry = currentEntry->next; |
| } else { |
| dirList = (struct dirEntry *) malloc(sizeof(struct dirEntry)); |
| currentEntry = dirList; |
| } |
| if(currentEntry == NULL) { |
| closedir(dirp); |
| jniThrowException(env, "java/lang/OutOfMemoryError", NULL); |
| goto cleanup; |
| } |
| strcpy(currentEntry->pathEntry, filename); |
| numEntries++; |
| } |
| |
| entry = readdir(dirp); |
| |
| if(entry == NULL) { |
| result = -1; |
| } else { |
| strcpy(filename, entry->d_name); |
| } |
| } |
| closedir(dirp); |
| |
| if(numEntries == 0) { |
| return NULL; |
| } |
| |
| javaClass = (*env)->FindClass(env, "[B"); |
| if(javaClass == NULL) { |
| return NULL; |
| } |
| answer = (*env)->NewObjectArray(env, numEntries, javaClass, NULL); |
| |
| cleanup: |
| for(index = 0; index < numEntries; index++) |
| { |
| jbyteArray entrypath; |
| jsize entrylen = strlen(dirList->pathEntry); |
| currentEntry = dirList; |
| if(answer) |
| { |
| entrypath = (*env)->NewByteArray(env, entrylen); |
| (*env)->SetByteArrayRegion(env, entrypath, 0, entrylen, |
| (jbyte *) dirList->pathEntry); |
| (*env)->SetObjectArrayElement(env, answer, index, entrypath); |
| (*env)->DeleteLocalRef(env, entrypath); |
| } |
| dirList = dirList->next; |
| free((void *)currentEntry); |
| } |
| |
| return answer; |
| } |
| |
| static jboolean java_io_File_mkdirImpl(JNIEnv* env, jobject recv, |
| jbyteArray path) { |
| jint result; |
| char pathCopy[MaxPath]; |
| jsize length = (*env)->GetArrayLength(env, path); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| ((*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy)); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| // BEGIN android-changed |
| // don't want default permissions to allow global access. |
| result = mkdir(pathCopy, S_IRWXU); |
| // END android-changed |
| |
| if(-1 != result) |
| { |
| result = 0; |
| } |
| |
| return result == 0; |
| } |
| |
| static jint java_io_File_newFileImpl(JNIEnv* env, jobject recv, |
| jbyteArray path) { |
| |
| if(path == NULL) { |
| jniThrowException(env, "java/lang/NullPointerException", NULL); |
| return -1; |
| } |
| |
| jint result; |
| jsize length = (*env)->GetArrayLength(env, path); |
| char pathCopy[MaxPath]; |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| (*env)->GetByteArrayRegion(env, path, 0, length, (jbyte *)pathCopy); |
| pathCopy[length] = '\0'; |
| convertToPlatform(pathCopy); |
| |
| /* First check to see if file already exists */ |
| if(java_io_File_existsImpl(env, recv, path)) |
| { |
| return 1; |
| } |
| |
| /* Now create the file and close it */ |
| // BEGIN android-changed |
| // don't want default permissions to allow global access. |
| int fd = open(pathCopy, O_EXCL | O_CREAT, 0600); |
| // END android-changed |
| if(fd == -1) |
| { |
| if(errno == EEXIST) { |
| return 1; |
| } |
| return -1; |
| } |
| close(fd); |
| |
| return 0; |
| } |
| |
| static jboolean java_io_File_renameToImpl(JNIEnv* env, jobject recv, |
| jbyteArray pathExist, jbyteArray pathNew) { |
| jint result; |
| jsize length; |
| char pathExistCopy[MaxPath], pathNewCopy[MaxPath]; |
| |
| length = (*env)->GetArrayLength(env, pathExist); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| ((*env)->GetByteArrayRegion(env, pathExist, 0, length, |
| (jbyte *)pathExistCopy)); |
| pathExistCopy[length] = '\0'; |
| |
| length = (*env)->GetArrayLength(env, pathNew); |
| length = length < MaxPath - 1 ? length : MaxPath - 1; |
| ((*env)->GetByteArrayRegion(env, pathNew, 0, length, |
| (jbyte *)pathNewCopy)); |
| pathNewCopy[length] = '\0'; |
| |
| convertToPlatform(pathExistCopy); |
| convertToPlatform(pathNewCopy); |
| |
| result = rename(pathExistCopy, pathNewCopy); |
| |
| return result == 0; |
| } |
| |
| static void java_io_File_oneTimeInitialization(JNIEnv * env, jclass clazz) |
| { |
| // dummy to stay compatible to harmony |
| } |
| |
| /* |
| * JNI registration |
| */ |
| static JNINativeMethod gMethods[] = { |
| /* name, signature, funcPtr */ |
| { "rootsImpl", "()[[B", (void*) java_io_File_rootsImpl }, |
| { "deleteDirImpl", "([B)Z", (void*) java_io_File_deleteDirImpl }, |
| { "deleteFileImpl", "([B)Z", (void*) java_io_File_deleteFileImpl }, |
| { "existsImpl", "([B)Z", (void*) java_io_File_existsImpl }, |
| { "getCanonImpl", "([B)[B", (void*) java_io_File_getCanonImpl }, |
| { "isDirectoryImpl", "([B)Z", (void*) java_io_File_isDirectoryImpl }, |
| { "isFileImpl", "([B)Z", (void*) java_io_File_isFileImpl }, |
| { "isHiddenImpl", "([B)Z", (void*) java_io_File_isHiddenImpl }, |
| // BEGIN android-changed |
| { "isReadableImpl", "([B)Z", (void*) java_io_File_readable }, |
| { "isWriteableImpl", "([B)Z", (void*) java_io_File_writable }, |
| // END android-changed |
| { "getLinkImpl", "([B)[B", (void*) java_io_File_getLinkImpl }, |
| { "lastModifiedImpl", "([B)J", (void*) java_io_File_lastModifiedImpl }, |
| { "setReadOnlyImpl", "([B)Z", (void*) java_io_File_setReadOnlyImpl }, |
| { "lengthImpl", "([B)J", (void*) java_io_File_lengthImpl }, |
| { "listImpl", "([B)[[B",(void*) java_io_File_listImpl }, |
| { "mkdirImpl", "([B)Z", (void*) java_io_File_mkdirImpl }, |
| { "newFileImpl", "([B)I", (void*) java_io_File_newFileImpl }, |
| { "renameToImpl", "([B[B)Z",(void*) java_io_File_renameToImpl }, |
| { "setLastModifiedImpl","([BJ)Z", |
| (void*) java_io_File_setLastModifiedImpl }, |
| { "oneTimeInitialization","()V", |
| (void*) java_io_File_oneTimeInitialization } |
| }; |
| int register_java_io_File(JNIEnv* env) |
| { |
| return jniRegisterNativeMethods(env, "java/io/File", |
| gMethods, NELEM(gMethods)); |
| } |