/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * java.lang.VMClassLoader
 */
#include "Dalvik.h"
#include "native/InternalNativePriv.h"


/*
 * static Class defineClass(ClassLoader cl, String name,
 *     byte[] data, int offset, int len, ProtectionDomain pd)
 *     throws ClassFormatError
 *
 * Convert an array of bytes to a Class object.
 */
static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args,
    JValue* pResult)
{
    Object* loader = (Object*) args[0];
    StringObject* nameObj = (StringObject*) args[1];
    const u1* data = (const u1*) args[2];
    int offset = args[3];
    int len = args[4];
    Object* pd = (Object*) args[5];
    char* name = NULL;

    name = dvmCreateCstrFromString(nameObj);
    LOGE("ERROR: defineClass(%p, %s, %p, %d, %d, %p)\n",
        loader, name, data, offset, len, pd);
    dvmThrowException("Ljava/lang/UnsupportedOperationException;",
        "can't load this type of class file");

    free(name);
    RETURN_VOID();
}

/*
 * static Class defineClass(ClassLoader cl, byte[] data, int offset,
 *     int len, ProtectionDomain pd)
 *     throws ClassFormatError
 *
 * Convert an array of bytes to a Class object. Deprecated version of
 * previous method, lacks name parameter.
 */
static void Dalvik_java_lang_VMClassLoader_defineClass2(const u4* args,
    JValue* pResult)
{
    Object* loader = (Object*) args[0];
    const u1* data = (const u1*) args[1];
    int offset = args[2];
    int len = args[3];
    Object* pd = (Object*) args[4];

    LOGE("ERROR: defineClass(%p, %p, %d, %d, %p)\n",
        loader, data, offset, len, pd);
    dvmThrowException("Ljava/lang/UnsupportedOperationException;",
        "can't load this type of class file");

    RETURN_VOID();
}

/*
 * static Class findLoadedClass(ClassLoader cl, String name)
 */
static void Dalvik_java_lang_VMClassLoader_findLoadedClass(const u4* args,
    JValue* pResult)
{
    Object* loader = (Object*) args[0];
    StringObject* nameObj = (StringObject*) args[1];
    ClassObject* clazz = NULL;
    char* name = NULL;
    char* descriptor = NULL;

    if (nameObj == NULL) {
        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
        goto bail;
    }

    /*
     * Get a UTF-8 copy of the string, and convert dots to slashes.
     */
    name = dvmCreateCstrFromString(nameObj);
    if (name == NULL)
        goto bail;

    descriptor = dvmDotToDescriptor(name);
    if (descriptor == NULL)
        goto bail;

    clazz = dvmLookupClass(descriptor, loader, false);
    LOGVV("look: %s ldr=%p --> %p\n", descriptor, loader, clazz);

bail:
    free(name);
    free(descriptor);
    RETURN_PTR(clazz);
}

/*
 * private static int getBootClassPathSize()
 *
 * Get the number of entries in the boot class path.
 */
static void Dalvik_java_lang_VMClassLoader_getBootClassPathSize(const u4* args,
    JValue* pResult)
{
    int count = dvmGetBootPathSize();
    RETURN_INT(count);
}

/*
 * private static String getBootClassPathResource(String name, int index)
 *
 * Find a resource with a matching name in a boot class path entry.
 *
 * This mimics the previous VM interface, since we're sharing class libraries.
 */
static void Dalvik_java_lang_VMClassLoader_getBootClassPathResource(
    const u4* args, JValue* pResult)
{
    StringObject* nameObj = (StringObject*) args[0];
    StringObject* result;
    int idx = args[1];
    char* name;

    name = dvmCreateCstrFromString(nameObj);
    if (name == NULL)
        RETURN_PTR(NULL);

    result = dvmGetBootPathResource(name, idx);
    free(name);
    dvmReleaseTrackedAlloc((Object*)result, NULL);
    RETURN_PTR(result);
}

/*
 * static final Class getPrimitiveClass(char prim_type)
 */
static void Dalvik_java_lang_VMClassLoader_getPrimitiveClass(const u4* args,
    JValue* pResult)
{
    int primType = args[0];

    pResult->l = dvmFindPrimitiveClass(primType);
}

/*
 * static Class loadClass(String name, boolean resolve)
 *     throws ClassNotFoundException
 *
 * Load class using bootstrap class loader.
 *
 * Return the Class object associated with the class or interface with
 * the specified name.
 *
 * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
 */
static void Dalvik_java_lang_VMClassLoader_loadClass(const u4* args,
    JValue* pResult)
{
    StringObject* nameObj = (StringObject*) args[0];
    bool resolve = (args[1] != 0);
    ClassObject* clazz;

    clazz = dvmFindClassByName(nameObj, NULL, resolve);
    assert(clazz == NULL || dvmIsClassLinked(clazz));
    RETURN_PTR(clazz);
}

const DalvikNativeMethod dvm_java_lang_VMClassLoader[] = {
    { "defineClass",        "(Ljava/lang/ClassLoader;Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",
        Dalvik_java_lang_VMClassLoader_defineClass },
    { "defineClass",        "(Ljava/lang/ClassLoader;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",
        Dalvik_java_lang_VMClassLoader_defineClass2 },
    { "findLoadedClass",    "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;",
        Dalvik_java_lang_VMClassLoader_findLoadedClass },
    { "getBootClassPathSize", "()I",
        Dalvik_java_lang_VMClassLoader_getBootClassPathSize },
    { "getBootClassPathResource", "(Ljava/lang/String;I)Ljava/lang/String;",
        Dalvik_java_lang_VMClassLoader_getBootClassPathResource },
    { "getPrimitiveClass",  "(C)Ljava/lang/Class;",
        Dalvik_java_lang_VMClassLoader_getPrimitiveClass },
    { "loadClass",          "(Ljava/lang/String;Z)Ljava/lang/Class;",
        Dalvik_java_lang_VMClassLoader_loadClass },
    { NULL, NULL, NULL },
};