1 /*
2 * Copyright (C) 2016 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 "common_helper.h"
18
19 #include <cstdio>
20 #include <deque>
21 #include <map>
22 #include <sstream>
23 #include <string>
24 #include <vector>
25
26 #include "jni.h"
27 #include "jvmti.h"
28
29 #include "jvmti_helper.h"
30 #include "test_env.h"
31
32 namespace art {
33
34 enum class RedefineType {
35 kNormal,
36 kStructural,
37 };
38
39 static void SetupCommonRedefine();
40 static void SetupCommonRetransform(RedefineType type);
41 static void SetupCommonTransform();
42 template <bool is_redefine>
throwCommonRedefinitionError(jvmtiEnv * jvmti,JNIEnv * env,jint num_targets,jclass * target,jvmtiError res)43 static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
44 JNIEnv* env,
45 jint num_targets,
46 jclass* target,
47 jvmtiError res) {
48 // Get the last error message which might give more details on what went wrong.
49 using GetLastError = jvmtiError (*)(jvmtiEnv* env, char** msg);
50 GetLastError get_last_error =
51 GetExtensionFunction<GetLastError>(env, jvmti, "com.android.art.misc.get_last_error_message");
52 char* error_msg = nullptr;
53 if (get_last_error != nullptr) {
54 get_last_error(jvmti_env, &error_msg);
55 }
56
57 std::stringstream err;
58 char* error = nullptr;
59 jvmti->GetErrorName(res, &error);
60 err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
61 if (num_targets > 1) {
62 err << "es";
63 }
64 err << " <";
65 for (jint i = 0; i < num_targets; i++) {
66 char* signature = nullptr;
67 char* generic = nullptr;
68 jvmti->GetClassSignature(target[i], &signature, &generic);
69 if (i != 0) {
70 err << ", ";
71 }
72 err << signature;
73 jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
74 jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
75 }
76 err << "> due to " << error;
77 if (error_msg != nullptr) {
78 err << " (" << error_msg << ")";
79 }
80 std::string message = err.str();
81 jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
82 jvmti->Deallocate(reinterpret_cast<unsigned char*>(error_msg));
83 env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
84 }
85
86 #define CONFIGURATION_COMMON_REDEFINE 0
87 #define CONFIGURATION_COMMON_RETRANSFORM 1
88 #define CONFIGURATION_COMMON_TRANSFORM 2
89 #define CONFIGURATION_STRUCTURAL_TRANSFORM 3
90
Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv *,jclass,jint type)91 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv*,
92 jclass,
93 jint type) {
94 switch (type) {
95 case CONFIGURATION_COMMON_REDEFINE: {
96 SetupCommonRedefine();
97 return;
98 }
99 case CONFIGURATION_COMMON_RETRANSFORM: {
100 SetupCommonRetransform(RedefineType::kNormal);
101 return;
102 }
103 case CONFIGURATION_COMMON_TRANSFORM: {
104 SetupCommonTransform();
105 return;
106 }
107 case CONFIGURATION_STRUCTURAL_TRANSFORM: {
108 SetupCommonRetransform(RedefineType::kStructural);
109 return;
110 }
111 default: {
112 LOG(FATAL) << "Unknown test configuration: " << type;
113 }
114 }
115 }
116
117 template<RedefineType kType>
SupportsAndIsJVM()118 static bool SupportsAndIsJVM() {
119 if constexpr (kType == RedefineType::kStructural) {
120 return false;
121 } else {
122 return IsJVM();
123 }
124 }
125
126
127 namespace common_redefine {
128
129 template <RedefineType kType>
CallRedefineEntrypoint(JNIEnv * env,jvmtiEnv * jvmti,jint num_defs,const jvmtiClassDefinition * defs)130 static jvmtiError CallRedefineEntrypoint(JNIEnv* env,
131 jvmtiEnv* jvmti,
132 jint num_defs,
133 const jvmtiClassDefinition* defs) {
134 decltype(jvmti->functions->RedefineClasses) entrypoint = nullptr;
135 if constexpr (kType == RedefineType::kNormal) {
136 entrypoint = jvmti->functions->RedefineClasses;
137 } else {
138 entrypoint = GetExtensionFunction<decltype(entrypoint)>(
139 env, jvmti_env, "com.android.art.class.structurally_redefine_classes");
140 }
141 if (entrypoint == nullptr) {
142 LOG(INFO) << "Could not find entrypoint!";
143 return JVMTI_ERROR_NOT_AVAILABLE;
144 }
145 return entrypoint(jvmti, num_defs, defs);
146 }
147
throwRedefinitionError(jvmtiEnv * jvmti,JNIEnv * env,jint num_targets,jclass * target,jvmtiError res)148 static void throwRedefinitionError(jvmtiEnv* jvmti,
149 JNIEnv* env,
150 jint num_targets,
151 jclass* target,
152 jvmtiError res) {
153 return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
154 }
155
156 template<RedefineType kType>
DoMultiClassRedefine(jvmtiEnv * jvmti_env,JNIEnv * env,jint num_redefines,jclass * targets,jbyteArray * class_file_bytes,jbyteArray * dex_file_bytes)157 static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
158 JNIEnv* env,
159 jint num_redefines,
160 jclass* targets,
161 jbyteArray* class_file_bytes,
162 jbyteArray* dex_file_bytes) {
163 std::vector<jvmtiClassDefinition> defs;
164 for (jint i = 0; i < num_redefines; i++) {
165 jbyteArray desired_array = SupportsAndIsJVM<kType>() ? class_file_bytes[i] : dex_file_bytes[i];
166 jint len = static_cast<jint>(env->GetArrayLength(desired_array));
167 const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
168 env->GetByteArrayElements(desired_array, nullptr));
169 defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
170 }
171 jvmtiError res = CallRedefineEntrypoint<kType>(env, jvmti_env, num_redefines, defs.data());
172 if (res != JVMTI_ERROR_NONE) {
173 throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
174 }
175 }
176
177 template<RedefineType kType>
DoClassRedefine(jvmtiEnv * jvmti_env,JNIEnv * env,jclass target,jbyteArray class_file_bytes,jbyteArray dex_file_bytes)178 static void DoClassRedefine(jvmtiEnv* jvmti_env,
179 JNIEnv* env,
180 jclass target,
181 jbyteArray class_file_bytes,
182 jbyteArray dex_file_bytes) {
183 return DoMultiClassRedefine<kType>(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
184 }
185
186 extern "C" JNIEXPORT jboolean JNICALL
Java_art_Redefinition_isStructurallyModifiable(JNIEnv * env,jclass,jclass target)187 Java_art_Redefinition_isStructurallyModifiable(JNIEnv* env, jclass, jclass target) {
188 using ArtCanStructurallyRedefineClass =
189 jvmtiError (*)(jvmtiEnv * env, jclass k, jboolean * result);
190 ArtCanStructurallyRedefineClass can_redef = GetExtensionFunction<ArtCanStructurallyRedefineClass>(
191 env, jvmti_env, "com.android.art.class.is_structurally_modifiable_class");
192 if (can_redef == nullptr || env->ExceptionCheck()) {
193 return false;
194 }
195 jboolean result = false;
196 JvmtiErrorToException(env, jvmti_env, can_redef(jvmti_env, target, &result));
197 return result;
198 }
199
Java_art_Redefinition_doCommonStructuralClassRedefinition(JNIEnv * env,jclass,jclass target,jbyteArray dex_file_bytes)200 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonStructuralClassRedefinition(
201 JNIEnv* env, jclass, jclass target, jbyteArray dex_file_bytes) {
202 DoClassRedefine<RedefineType::kStructural>(jvmti_env, env, target, nullptr, dex_file_bytes);
203 }
204
205 // Magic JNI export that classes can use for redefining classes.
206 // To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
Java_art_Redefinition_doCommonClassRedefinition(JNIEnv * env,jclass,jclass target,jbyteArray class_file_bytes,jbyteArray dex_file_bytes)207 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRedefinition(
208 JNIEnv* env, jclass, jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) {
209 DoClassRedefine<RedefineType::kNormal>(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
210 }
211
212 // Magic JNI export that classes can use for redefining classes.
213 // To use classes should declare this as a native function with signature
214 // ([Ljava/lang/Class;[[B[[B)V
Java_art_Redefinition_doCommonMultiStructuralClassRedefinition(JNIEnv * env,jclass,jobjectArray targets,jobjectArray dex_file_bytes)215 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiStructuralClassRedefinition(
216 JNIEnv* env,
217 jclass,
218 jobjectArray targets,
219 jobjectArray dex_file_bytes) {
220 std::vector<jclass> classes;
221 std::vector<jbyteArray> class_files;
222 std::vector<jbyteArray> dex_files;
223 jint len = env->GetArrayLength(targets);
224 if (len != env->GetArrayLength(dex_file_bytes)) {
225 env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
226 "the three array arguments passed to this function have different lengths!");
227 return;
228 }
229 for (jint i = 0; i < len; i++) {
230 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
231 dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
232 class_files.push_back(nullptr);
233 }
234 return DoMultiClassRedefine<RedefineType::kStructural>(jvmti_env,
235 env,
236 len,
237 classes.data(),
238 class_files.data(),
239 dex_files.data());
240 }
241
242 // Magic JNI export that classes can use for redefining classes.
243 // To use classes should declare this as a native function with signature
244 // ([Ljava/lang/Class;[[B[[B)V
Java_art_Redefinition_doCommonMultiClassRedefinition(JNIEnv * env,jclass,jobjectArray targets,jobjectArray class_file_bytes,jobjectArray dex_file_bytes)245 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiClassRedefinition(
246 JNIEnv* env,
247 jclass,
248 jobjectArray targets,
249 jobjectArray class_file_bytes,
250 jobjectArray dex_file_bytes) {
251 std::vector<jclass> classes;
252 std::vector<jbyteArray> class_files;
253 std::vector<jbyteArray> dex_files;
254 jint len = env->GetArrayLength(targets);
255 if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
256 env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
257 "the three array arguments passed to this function have different lengths!");
258 return;
259 }
260 for (jint i = 0; i < len; i++) {
261 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
262 dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
263 class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
264 }
265 return DoMultiClassRedefine<RedefineType::kNormal>(jvmti_env,
266 env,
267 len,
268 classes.data(),
269 class_files.data(),
270 dex_files.data());
271 }
272
273 // Get all capabilities except those related to retransformation.
OnLoad(JavaVM * vm,char * options,void * reserved)274 jint OnLoad(JavaVM* vm,
275 [[maybe_unused]] char* options,
276 [[maybe_unused]] void* reserved) {
277 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
278 printf("Unable to get jvmti env!\n");
279 return 1;
280 }
281 SetupCommonRedefine();
282 return 0;
283 }
284
285 } // namespace common_redefine
286
287 namespace common_retransform {
288
289 struct CommonTransformationResult {
290 std::vector<unsigned char> class_bytes;
291 std::vector<unsigned char> dex_bytes;
292
CommonTransformationResultart::common_retransform::CommonTransformationResult293 CommonTransformationResult(size_t class_size, size_t dex_size)
294 : class_bytes(class_size), dex_bytes(dex_size) {}
295
296 CommonTransformationResult() = default;
297 CommonTransformationResult(CommonTransformationResult&&) = default;
298 CommonTransformationResult(CommonTransformationResult&) = default;
299 };
300
301 // Map from class name to transformation result.
302 std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
303 bool gPopTransformations = true;
304
Java_art_Redefinition_addCommonTransformationResult(JNIEnv * env,jclass,jstring class_name,jbyteArray class_array,jbyteArray dex_array)305 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_addCommonTransformationResult(
306 JNIEnv* env, jclass, jstring class_name, jbyteArray class_array, jbyteArray dex_array) {
307 const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
308 std::string name_str(name_chrs);
309 env->ReleaseStringUTFChars(class_name, name_chrs);
310 CommonTransformationResult trans(env->GetArrayLength(class_array),
311 env->GetArrayLength(dex_array));
312 if (env->ExceptionOccurred()) {
313 return;
314 }
315 env->GetByteArrayRegion(class_array,
316 0,
317 env->GetArrayLength(class_array),
318 reinterpret_cast<jbyte*>(trans.class_bytes.data()));
319 if (env->ExceptionOccurred()) {
320 return;
321 }
322 env->GetByteArrayRegion(dex_array,
323 0,
324 env->GetArrayLength(dex_array),
325 reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
326 if (env->ExceptionOccurred()) {
327 return;
328 }
329 if (gTransformations.find(name_str) == gTransformations.end()) {
330 std::deque<CommonTransformationResult> list;
331 gTransformations[name_str] = std::move(list);
332 }
333 gTransformations[name_str].push_back(std::move(trans));
334 }
335
336 // The hook we are using.
CommonClassFileLoadHookRetransformable(jvmtiEnv * jvmti_env,JNIEnv * jni_env,jclass class_being_redefined,jobject loader,const char * name,jobject protection_domain,jint class_data_len,const unsigned char * class_dat,jint * new_class_data_len,unsigned char ** new_class_data)337 void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
338 [[maybe_unused]] JNIEnv* jni_env,
339 [[maybe_unused]] jclass class_being_redefined,
340 [[maybe_unused]] jobject loader,
341 const char* name,
342 [[maybe_unused]] jobject protection_domain,
343 [[maybe_unused]] jint class_data_len,
344 [[maybe_unused]] const unsigned char* class_dat,
345 jint* new_class_data_len,
346 unsigned char** new_class_data) {
347 std::string name_str(name);
348 if (gTransformations.find(name_str) != gTransformations.end() &&
349 gTransformations[name_str].size() > 0) {
350 CommonTransformationResult& res = gTransformations[name_str][0];
351 const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
352 unsigned char* new_data;
353 CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
354 memcpy(new_data, desired_array.data(), desired_array.size());
355 *new_class_data = new_data;
356 *new_class_data_len = desired_array.size();
357 if (gPopTransformations) {
358 gTransformations[name_str].pop_front();
359 }
360 }
361 }
362
Java_art_Redefinition_setPopRetransformations(JNIEnv *,jclass,jboolean enable)363 extern "C" JNIEXPORT void Java_art_Redefinition_setPopRetransformations(JNIEnv*,
364 jclass,
365 jboolean enable) {
366 gPopTransformations = enable;
367 }
368
Java_art_Redefinition_popTransformationFor(JNIEnv * env,jclass,jstring class_name)369 extern "C" JNIEXPORT void Java_art_Redefinition_popTransformationFor(JNIEnv* env,
370 jclass,
371 jstring class_name) {
372 const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
373 std::string name_str(name_chrs);
374 env->ReleaseStringUTFChars(class_name, name_chrs);
375 if (gTransformations.find(name_str) != gTransformations.end() &&
376 gTransformations[name_str].size() > 0) {
377 gTransformations[name_str].pop_front();
378 } else {
379 std::stringstream err;
380 err << "No transformations found for class " << name_str;
381 std::string message = err.str();
382 env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
383 }
384 }
385
Java_art_Redefinition_enableCommonRetransformation(JNIEnv * env,jclass,jboolean enable)386 extern "C" JNIEXPORT void Java_art_Redefinition_enableCommonRetransformation(JNIEnv* env,
387 jclass,
388 jboolean enable) {
389 jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
390 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
391 nullptr);
392 if (res != JVMTI_ERROR_NONE) {
393 JvmtiErrorToException(env, jvmti_env, res);
394 }
395 }
396
throwRetransformationError(jvmtiEnv * jvmti,JNIEnv * env,jint num_targets,jclass * targets,jvmtiError res)397 static void throwRetransformationError(jvmtiEnv* jvmti,
398 JNIEnv* env,
399 jint num_targets,
400 jclass* targets,
401 jvmtiError res) {
402 return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
403 }
404
DoClassRetransformation(jvmtiEnv * jvmti_env,JNIEnv * env,jobjectArray targets)405 static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
406 std::vector<jclass> classes;
407 jint len = env->GetArrayLength(targets);
408 classes.reserve(len);
409 for (jint i = 0; i < len; i++) {
410 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
411 }
412 jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
413 if (res != JVMTI_ERROR_NONE) {
414 throwRetransformationError(jvmti_env, env, len, classes.data(), res);
415 }
416 }
417
Java_art_Redefinition_doCommonClassRetransformation(JNIEnv * env,jclass,jobjectArray targets)418 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRetransformation(
419 JNIEnv* env, jclass, jobjectArray targets) {
420 jvmtiCapabilities caps;
421 jvmtiError caps_err = jvmti_env->GetCapabilities(&caps);
422 if (caps_err != JVMTI_ERROR_NONE) {
423 env->ThrowNew(env->FindClass("java/lang/Exception"),
424 "Unable to get current jvmtiEnv capabilities");
425 return;
426 }
427
428 // Allocate a new environment if we don't have the can_retransform_classes capability needed to
429 // call the RetransformClasses function.
430 jvmtiEnv* real_env = nullptr;
431 if (caps.can_retransform_classes != 1) {
432 JavaVM* vm = nullptr;
433 if (env->GetJavaVM(&vm) != 0 ||
434 vm->GetEnv(reinterpret_cast<void**>(&real_env), JVMTI_VERSION_1_0) != 0) {
435 env->ThrowNew(env->FindClass("java/lang/Exception"),
436 "Unable to create temporary jvmtiEnv for RetransformClasses call.");
437 return;
438 }
439 SetStandardCapabilities(real_env);
440 } else {
441 real_env = jvmti_env;
442 }
443 DoClassRetransformation(real_env, env, targets);
444 if (caps.can_retransform_classes != 1) {
445 real_env->DisposeEnvironment();
446 }
447 }
448
449 // Get all capabilities except those related to retransformation.
OnLoad(JavaVM * vm,char * options,void * reserved)450 jint OnLoad(JavaVM* vm,
451 [[maybe_unused]] char* options,
452 [[maybe_unused]] void* reserved) {
453 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
454 printf("Unable to get jvmti env!\n");
455 return 1;
456 }
457 SetupCommonRetransform(RedefineType::kNormal);
458 return 0;
459 }
460
461 } // namespace common_retransform
462
463 namespace common_transform {
464
465 // Get all capabilities except those related to retransformation.
OnLoad(JavaVM * vm,char * options,void * reserved)466 jint OnLoad(JavaVM* vm,
467 [[maybe_unused]] char* options,
468 [[maybe_unused]] void* reserved) {
469 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
470 printf("Unable to get jvmti env!\n");
471 return 1;
472 }
473 SetupCommonTransform();
474 return 0;
475 }
476
477 } // namespace common_transform
478
SetupCommonRedefine()479 static void SetupCommonRedefine() {
480 jvmtiCapabilities caps = GetStandardCapabilities();
481 caps.can_retransform_classes = 0;
482 caps.can_retransform_any_class = 0;
483 jvmti_env->AddCapabilities(&caps);
484 }
485
SetupCommonRetransform(RedefineType type)486 static void SetupCommonRetransform(RedefineType type) {
487 SetStandardCapabilities(jvmti_env);
488 if (type == RedefineType::kNormal) {
489 current_callbacks.ClassFileLoadHook =
490 common_retransform::CommonClassFileLoadHookRetransformable;
491 jvmtiError res = jvmti_env->SetEventCallbacks(¤t_callbacks, sizeof(current_callbacks));
492 CHECK_EQ(res, JVMTI_ERROR_NONE);
493 } else {
494 jvmtiError res = jvmti_env->SetExtensionEventCallback(
495 GetExtensionEventId(jvmti_env, "com.android.art.class.structural_dex_file_load_hook"),
496 reinterpret_cast<jvmtiExtensionEvent>(
497 common_retransform::CommonClassFileLoadHookRetransformable));
498 CHECK_EQ(res, JVMTI_ERROR_NONE);
499 }
500 common_retransform::gTransformations.clear();
501 }
502
SetupCommonTransform()503 static void SetupCommonTransform() {
504 // Don't set the retransform caps
505 jvmtiCapabilities caps = GetStandardCapabilities();
506 caps.can_retransform_classes = 0;
507 caps.can_retransform_any_class = 0;
508 jvmti_env->AddCapabilities(&caps);
509
510 // Use the same callback as the retransform test.
511 current_callbacks.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
512 jvmtiError res = jvmti_env->SetEventCallbacks(¤t_callbacks, sizeof(current_callbacks));
513 CHECK_EQ(res, JVMTI_ERROR_NONE);
514 common_retransform::gTransformations.clear();
515 }
516
517 } // namespace art
518