1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <jni.h>
18
19#include <cstring>
20#include <cstdlib>
21#include <sstream>
22
23#include "jvmti.h"
24
25#include <slicer/dex_ir.h>
26#include <slicer/writer.h>
27#include <slicer/reader.h>
28
29using namespace dex;
30
31namespace com_android_dx_mockito_inline_tests {
32    static jvmtiEnv *localJvmtiEnv;
33
34    // Converts a class name to a type descriptor
35    // (ex. "java.lang.String" to "Ljava/lang/String;")
36    static std::string
37    ClassNameToDescriptor(const char* class_name) {
38        std::stringstream ss;
39        ss << "L";
40        for (auto p = class_name; *p != '\0'; ++p) {
41            ss << (*p == '.' ? '/' : *p);
42        }
43        ss << ";";
44        return ss.str();
45    }
46
47    static void
48    Transform(jvmtiEnv *jvmti_env,
49              JNIEnv *env,
50              jclass classBeingRedefined,
51              jobject loader,
52              const char *name,
53              jobject protectionDomain,
54              jint classDataLen,
55              const unsigned char *classData,
56              jint *newClassDataLen,
57              unsigned char **newClassData) {
58        // Isolate byte code of class class. This is needed as Android usually gives us more
59        // than the class we need.
60        // Then just return the isolated byte code without modification.
61        Reader reader(classData, (size_t) classDataLen);
62
63        u4 index = reader.FindClassIndex(ClassNameToDescriptor(name).c_str());
64        reader.CreateClassIr(index);
65        std::shared_ptr<ir::DexFile> ir = reader.GetIr();
66
67        class Allocator : public Writer::Allocator {
68            jvmtiEnv *jvmti_env;
69
70        public:
71            Allocator(jvmtiEnv *jvmti_env) : Writer::Allocator(), jvmti_env(jvmti_env) {
72            }
73
74            virtual void *Allocate(size_t size) {
75                unsigned char *mem;
76                jvmti_env->Allocate(size, &mem);
77                return mem;
78            }
79
80            virtual void Free(void *ptr) { ::free(ptr); }
81        };
82
83        Allocator allocator(jvmti_env);
84        Writer writer(ir);
85        size_t newClassLen;
86        *newClassData = writer.CreateImage(&allocator, &newClassLen);
87        *newClassDataLen = (jint) newClassLen;
88    }
89
90    // Initializes the agent
91    extern "C" jint Agent_OnAttach(JavaVM *vm,
92                                   char *options,
93                                   void *reserved) {
94        jint jvmError = vm->GetEnv(reinterpret_cast<void **>(&localJvmtiEnv), JVMTI_VERSION_1_2);
95        if (jvmError != JNI_OK) {
96            return jvmError;
97        }
98
99        jvmtiCapabilities caps;
100        memset(&caps, 0, sizeof(caps));
101        caps.can_retransform_classes = 1;
102
103        jvmtiError error = localJvmtiEnv->AddCapabilities(&caps);
104        if (error != JVMTI_ERROR_NONE) {
105            return error;
106        }
107
108        jvmtiEventCallbacks cb;
109        memset(&cb, 0, sizeof(cb));
110        cb.ClassFileLoadHook = Transform;
111
112        error = localJvmtiEnv->SetEventCallbacks(&cb, sizeof(cb));
113        if (error != JVMTI_ERROR_NONE) {
114            return error;
115        }
116
117        error = localJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,
118                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
119                                                        NULL);
120        if (error != JVMTI_ERROR_NONE) {
121            return error;
122        }
123
124        return JVMTI_ERROR_NONE;
125    }
126
127
128    // Triggers retransformation of classes via this file's Transform method
129    extern "C" JNIEXPORT jint JNICALL
130    Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_nativeRetransformClasses(
131            JNIEnv *env,
132            jobject thiz,
133            jobjectArray classes) {
134        jsize numTransformedClasses = env->GetArrayLength(classes);
135        jclass *transformedClasses = (jclass *) malloc(numTransformedClasses * sizeof(jclass));
136        for (int i = 0; i < numTransformedClasses; i++) {
137            transformedClasses[i] = (jclass) env->NewGlobalRef(env->GetObjectArrayElement(classes,
138                                                                                          i));
139        }
140
141        jvmtiError error = localJvmtiEnv->RetransformClasses(numTransformedClasses,
142                                                             transformedClasses);
143
144        for (int i = 0; i < numTransformedClasses; i++) {
145            env->DeleteGlobalRef(transformedClasses[i]);
146        }
147        free(transformedClasses);
148
149        return error;
150    }
151
152    // Disable hook to not slow down test
153    extern "C" JNIEXPORT jint JNICALL
154    Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_disableRetransformHook(
155            JNIEnv *env,
156            jclass ignored) {
157        return localJvmtiEnv->SetEventNotificationMode(JVMTI_DISABLE,
158                                                       JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
159                                                       NULL);
160
161    }
162}
163