| Sebastien Hertz | 8ece050 | 2013-08-07 11:26:41 +0200 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2012 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 "interpreter_common.h" | 
 | 18 |  | 
 | 19 | namespace art { | 
 | 20 | namespace interpreter { | 
 | 21 |  | 
 | 22 | template<InvokeType type, bool is_range, bool do_access_check> | 
 | 23 | bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, | 
 | 24 |               const Instruction* inst, JValue* result) { | 
 | 25 |   uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); | 
 | 26 |   uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); | 
 | 27 |   Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC); | 
 | 28 |   ArtMethod* method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self, | 
 | 29 |                                          do_access_check, type); | 
 | 30 |   if (UNLIKELY(method == NULL)) { | 
 | 31 |     CHECK(self->IsExceptionPending()); | 
 | 32 |     result->SetJ(0); | 
 | 33 |     return false; | 
 | 34 |   } else if (UNLIKELY(method->IsAbstract())) { | 
 | 35 |     ThrowAbstractMethodError(method); | 
 | 36 |     result->SetJ(0); | 
 | 37 |     return false; | 
 | 38 |   } | 
 | 39 |  | 
 | 40 |   MethodHelper mh(method); | 
 | 41 |   const DexFile::CodeItem* code_item = mh.GetCodeItem(); | 
 | 42 |   uint16_t num_regs; | 
 | 43 |   uint16_t num_ins; | 
 | 44 |   if (LIKELY(code_item != NULL)) { | 
 | 45 |     num_regs = code_item->registers_size_; | 
 | 46 |     num_ins = code_item->ins_size_; | 
 | 47 |   } else { | 
 | 48 |     DCHECK(method->IsNative() || method->IsProxyMethod()); | 
 | 49 |     num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); | 
 | 50 |     if (!method->IsStatic()) { | 
 | 51 |       num_regs++; | 
 | 52 |       num_ins++; | 
 | 53 |     } | 
 | 54 |   } | 
 | 55 |  | 
 | 56 |   void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); | 
 | 57 |   ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory)); | 
 | 58 |   size_t cur_reg = num_regs - num_ins; | 
 | 59 |   if (receiver != NULL) { | 
 | 60 |     new_shadow_frame->SetVRegReference(cur_reg, receiver); | 
 | 61 |     ++cur_reg; | 
 | 62 |   } | 
 | 63 |  | 
 | 64 |   size_t arg_offset = (receiver == NULL) ? 0 : 1; | 
 | 65 |   const char* shorty = mh.GetShorty(); | 
 | 66 |   uint32_t arg[5]; | 
 | 67 |   if (!is_range) { | 
 | 68 |     inst->GetArgs(arg); | 
 | 69 |   } | 
 | 70 |   for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { | 
 | 71 |     DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); | 
 | 72 |     size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; | 
 | 73 |     switch (shorty[shorty_pos + 1]) { | 
 | 74 |       case 'L': { | 
 | 75 |         Object* o = shadow_frame.GetVRegReference(arg_pos); | 
 | 76 |         new_shadow_frame->SetVRegReference(cur_reg, o); | 
 | 77 |         break; | 
 | 78 |       } | 
 | 79 |       case 'J': case 'D': { | 
 | 80 |         uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) | | 
 | 81 |                               static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos)); | 
 | 82 |         new_shadow_frame->SetVRegLong(cur_reg, wide_value); | 
 | 83 |         cur_reg++; | 
 | 84 |         arg_offset++; | 
 | 85 |         break; | 
 | 86 |       } | 
 | 87 |       default: | 
 | 88 |         new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); | 
 | 89 |         break; | 
 | 90 |     } | 
 | 91 |   } | 
 | 92 |  | 
 | 93 |   if (LIKELY(Runtime::Current()->IsStarted())) { | 
 | 94 |     (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); | 
 | 95 |   } else { | 
 | 96 |     UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); | 
 | 97 |   } | 
 | 98 |   return !self->IsExceptionPending(); | 
 | 99 | } | 
 | 100 |  | 
 | 101 | template<bool is_range> | 
 | 102 | bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, | 
 | 103 |                           const Instruction* inst, JValue* result) { | 
 | 104 |   uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); | 
 | 105 |   Object* receiver = shadow_frame.GetVRegReference(vregC); | 
 | 106 |   if (UNLIKELY(receiver == NULL)) { | 
 | 107 |     // We lost the reference to the method index so we cannot get a more | 
 | 108 |     // precised exception message. | 
 | 109 |     ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow()); | 
 | 110 |     return false; | 
 | 111 |   } | 
 | 112 |   uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); | 
 | 113 |   // TODO: use ObjectArray<T>::GetWithoutChecks ? | 
 | 114 |   ArtMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx); | 
 | 115 |   if (UNLIKELY(method == NULL)) { | 
 | 116 |     CHECK(self->IsExceptionPending()); | 
 | 117 |     result->SetJ(0); | 
 | 118 |     return false; | 
 | 119 |   } else if (UNLIKELY(method->IsAbstract())) { | 
 | 120 |     ThrowAbstractMethodError(method); | 
 | 121 |     result->SetJ(0); | 
 | 122 |     return false; | 
 | 123 |   } | 
 | 124 |  | 
 | 125 |   MethodHelper mh(method); | 
 | 126 |   const DexFile::CodeItem* code_item = mh.GetCodeItem(); | 
 | 127 |   uint16_t num_regs; | 
 | 128 |   uint16_t num_ins; | 
 | 129 |   if (code_item != NULL) { | 
 | 130 |     num_regs = code_item->registers_size_; | 
 | 131 |     num_ins = code_item->ins_size_; | 
 | 132 |   } else { | 
 | 133 |     DCHECK(method->IsNative() || method->IsProxyMethod()); | 
 | 134 |     num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty()); | 
 | 135 |     if (!method->IsStatic()) { | 
 | 136 |       num_regs++; | 
 | 137 |       num_ins++; | 
 | 138 |     } | 
 | 139 |   } | 
 | 140 |  | 
 | 141 |   void* memory = alloca(ShadowFrame::ComputeSize(num_regs)); | 
 | 142 |   ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, | 
 | 143 |                                                     method, 0, memory)); | 
 | 144 |   size_t cur_reg = num_regs - num_ins; | 
 | 145 |   if (receiver != NULL) { | 
 | 146 |     new_shadow_frame->SetVRegReference(cur_reg, receiver); | 
 | 147 |     ++cur_reg; | 
 | 148 |   } | 
 | 149 |  | 
 | 150 |   size_t arg_offset = (receiver == NULL) ? 0 : 1; | 
 | 151 |   const char* shorty = mh.GetShorty(); | 
 | 152 |   uint32_t arg[5]; | 
 | 153 |   if (!is_range) { | 
 | 154 |     inst->GetArgs(arg); | 
 | 155 |   } | 
 | 156 |   for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) { | 
 | 157 |     DCHECK_LT(shorty_pos + 1, mh.GetShortyLength()); | 
 | 158 |     size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset]; | 
 | 159 |     switch (shorty[shorty_pos + 1]) { | 
 | 160 |       case 'L': { | 
 | 161 |         Object* o = shadow_frame.GetVRegReference(arg_pos); | 
 | 162 |         new_shadow_frame->SetVRegReference(cur_reg, o); | 
 | 163 |         break; | 
 | 164 |       } | 
 | 165 |       case 'J': case 'D': { | 
 | 166 |         uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) | | 
 | 167 |                               static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos)); | 
 | 168 |         new_shadow_frame->SetVRegLong(cur_reg, wide_value); | 
 | 169 |         cur_reg++; | 
 | 170 |         arg_offset++; | 
 | 171 |         break; | 
 | 172 |       } | 
 | 173 |       default: | 
 | 174 |         new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos)); | 
 | 175 |         break; | 
 | 176 |     } | 
 | 177 |   } | 
 | 178 |  | 
 | 179 |   if (LIKELY(Runtime::Current()->IsStarted())) { | 
 | 180 |     (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result); | 
 | 181 |   } else { | 
 | 182 |     UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins); | 
 | 183 |   } | 
 | 184 |   return !self->IsExceptionPending(); | 
 | 185 | } | 
 | 186 |  | 
 | 187 | template <bool is_range, bool do_access_check> | 
 | 188 | bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, | 
 | 189 |                       Thread* self, JValue* result) { | 
 | 190 |   DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY || | 
 | 191 |          inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE); | 
 | 192 |   const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c(); | 
 | 193 |   if (!is_range) { | 
 | 194 |     // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments. | 
 | 195 |     CHECK_LE(length, 5); | 
 | 196 |   } | 
 | 197 |   if (UNLIKELY(length < 0)) { | 
 | 198 |     ThrowNegativeArraySizeException(length); | 
 | 199 |     return false; | 
 | 200 |   } | 
 | 201 |   uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); | 
 | 202 |   Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(), | 
 | 203 |                                              self, false, do_access_check); | 
 | 204 |   if (UNLIKELY(arrayClass == NULL)) { | 
 | 205 |     DCHECK(self->IsExceptionPending()); | 
 | 206 |     return false; | 
 | 207 |   } | 
 | 208 |   CHECK(arrayClass->IsArrayClass()); | 
 | 209 |   Class* componentClass = arrayClass->GetComponentType(); | 
 | 210 |   if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) { | 
 | 211 |     if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) { | 
 | 212 |       ThrowRuntimeException("Bad filled array request for type %s", | 
 | 213 |                             PrettyDescriptor(componentClass).c_str()); | 
 | 214 |     } else { | 
 | 215 |       self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(), | 
 | 216 |                                "Ljava/lang/InternalError;", | 
 | 217 |                                "Found type %s; filled-new-array not implemented for anything but \'int\'", | 
 | 218 |                                PrettyDescriptor(componentClass).c_str()); | 
 | 219 |     } | 
 | 220 |     return false; | 
 | 221 |   } | 
 | 222 |   Object* newArray = Array::Alloc(self, arrayClass, length); | 
 | 223 |   if (UNLIKELY(newArray == NULL)) { | 
 | 224 |     DCHECK(self->IsExceptionPending()); | 
 | 225 |     return false; | 
 | 226 |   } | 
 | 227 |   if (is_range) { | 
 | 228 |     uint32_t vregC = inst->VRegC_3rc(); | 
 | 229 |     const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); | 
 | 230 |     for (int32_t i = 0; i < length; ++i) { | 
 | 231 |       if (is_primitive_int_component) { | 
 | 232 |         newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i)); | 
 | 233 |       } else { | 
 | 234 |         newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(vregC + i)); | 
 | 235 |       } | 
 | 236 |     } | 
 | 237 |   } else { | 
 | 238 |     uint32_t arg[5]; | 
 | 239 |     inst->GetArgs(arg); | 
 | 240 |     const bool is_primitive_int_component = componentClass->IsPrimitiveInt(); | 
 | 241 |     for (int32_t i = 0; i < length; ++i) { | 
 | 242 |       if (is_primitive_int_component) { | 
 | 243 |         newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i])); | 
 | 244 |       } else { | 
 | 245 |         newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(arg[i])); | 
 | 246 |       } | 
 | 247 |     } | 
 | 248 |   } | 
 | 249 |  | 
 | 250 |   result->SetL(newArray); | 
 | 251 |   return true; | 
 | 252 | } | 
 | 253 |  | 
 | 254 | void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, | 
 | 255 |                             const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, | 
 | 256 |                             JValue* result, size_t arg_offset) { | 
 | 257 |   // In a runtime that's not started we intercept certain methods to avoid complicated dependency | 
 | 258 |   // problems in core libraries. | 
 | 259 |   std::string name(PrettyMethod(shadow_frame->GetMethod())); | 
 | 260 |   if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { | 
 | 261 |     std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); | 
 | 262 |     ClassLoader* class_loader = NULL;  // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); | 
 | 263 |     Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(), | 
 | 264 |                                                                    class_loader); | 
 | 265 |     CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: " | 
 | 266 |         << PrettyDescriptor(descriptor); | 
 | 267 |     result->SetL(found); | 
 | 268 |   } else if (name == "java.lang.Object java.lang.Class.newInstance()") { | 
 | 269 |     Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); | 
 | 270 |     ArtMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V"); | 
 | 271 |     CHECK(c != NULL); | 
 | 272 |     SirtRef<Object> obj(self, klass->AllocObject(self)); | 
 | 273 |     CHECK(obj.get() != NULL); | 
 | 274 |     EnterInterpreterFromInvoke(self, c, obj.get(), NULL, NULL); | 
 | 275 |     result->SetL(obj.get()); | 
 | 276 |   } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") { | 
 | 277 |     // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail | 
 | 278 |     // going the reflective Dex way. | 
 | 279 |     Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass(); | 
 | 280 |     String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); | 
 | 281 |     ArtField* found = NULL; | 
 | 282 |     FieldHelper fh; | 
 | 283 |     ObjectArray<ArtField>* fields = klass->GetIFields(); | 
 | 284 |     for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) { | 
 | 285 |       ArtField* f = fields->Get(i); | 
 | 286 |       fh.ChangeField(f); | 
 | 287 |       if (name->Equals(fh.GetName())) { | 
 | 288 |         found = f; | 
 | 289 |       } | 
 | 290 |     } | 
 | 291 |     if (found == NULL) { | 
 | 292 |       fields = klass->GetSFields(); | 
 | 293 |       for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) { | 
 | 294 |         ArtField* f = fields->Get(i); | 
 | 295 |         fh.ChangeField(f); | 
 | 296 |         if (name->Equals(fh.GetName())) { | 
 | 297 |           found = f; | 
 | 298 |         } | 
 | 299 |       } | 
 | 300 |     } | 
 | 301 |     CHECK(found != NULL) | 
 | 302 |       << "Failed to find field in Class.getDeclaredField in un-started runtime. name=" | 
 | 303 |       << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass); | 
 | 304 |     // TODO: getDeclaredField calls GetType once the field is found to ensure a | 
 | 305 |     //       NoClassDefFoundError is thrown if the field's type cannot be resolved. | 
 | 306 |     Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass(); | 
 | 307 |     SirtRef<Object> field(self, jlr_Field->AllocObject(self)); | 
 | 308 |     CHECK(field.get() != NULL); | 
 | 309 |     ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("<init>", "(Ljava/lang/reflect/ArtField;)V"); | 
 | 310 |     uint32_t args[1]; | 
 | 311 |     args[0] = reinterpret_cast<uint32_t>(found); | 
 | 312 |     EnterInterpreterFromInvoke(self, c, field.get(), args, NULL); | 
 | 313 |     result->SetL(field.get()); | 
 | 314 |   } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" || | 
 | 315 |              name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") { | 
 | 316 |     // Special case array copying without initializing System. | 
 | 317 |     Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType(); | 
 | 318 |     jint srcPos = shadow_frame->GetVReg(arg_offset + 1); | 
 | 319 |     jint dstPos = shadow_frame->GetVReg(arg_offset + 3); | 
 | 320 |     jint length = shadow_frame->GetVReg(arg_offset + 4); | 
 | 321 |     if (!ctype->IsPrimitive()) { | 
 | 322 |       ObjectArray<Object>* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray<Object>(); | 
 | 323 |       ObjectArray<Object>* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<Object>(); | 
 | 324 |       for (jint i = 0; i < length; ++i) { | 
 | 325 |         dst->Set(dstPos + i, src->Get(srcPos + i)); | 
 | 326 |       } | 
 | 327 |     } else if (ctype->IsPrimitiveChar()) { | 
 | 328 |       CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray(); | 
 | 329 |       CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray(); | 
 | 330 |       for (jint i = 0; i < length; ++i) { | 
 | 331 |         dst->Set(dstPos + i, src->Get(srcPos + i)); | 
 | 332 |       } | 
 | 333 |     } else if (ctype->IsPrimitiveInt()) { | 
 | 334 |       IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray(); | 
 | 335 |       IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray(); | 
 | 336 |       for (jint i = 0; i < length; ++i) { | 
 | 337 |         dst->Set(dstPos + i, src->Get(srcPos + i)); | 
 | 338 |       } | 
 | 339 |     } else { | 
 | 340 |       UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype); | 
 | 341 |     } | 
 | 342 |   } else { | 
 | 343 |     // Not special, continue with regular interpreter execution. | 
 | 344 |     artInterpreterToInterpreterBridge(self, mh, code_item, shadow_frame, result); | 
 | 345 |   } | 
 | 346 | } | 
 | 347 |  | 
 | 348 | // Explicit DoInvoke template function declarations. | 
 | 349 | #define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range_, _check) \ | 
 | 350 |   template bool DoInvoke<_type, _is_range_, _check>(Thread* self, ShadowFrame& shadow_frame, \ | 
 | 351 |                                                     const Instruction* inst, JValue* result) | 
 | 352 |  | 
 | 353 | #define EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(_type) \ | 
 | 354 |   EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false); \ | 
 | 355 |   EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, true);  \ | 
 | 356 |   EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false);  \ | 
 | 357 |   EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true) | 
 | 358 |  | 
 | 359 | EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kStatic); | 
 | 360 | EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kDirect); | 
 | 361 | EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kVirtual); | 
 | 362 | EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kSuper); | 
 | 363 | EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS(kInterface); | 
 | 364 |  | 
 | 365 | #undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL_VARIANTS | 
 | 366 | #undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL | 
 | 367 |  | 
 | 368 | // Explicit DoInvokeVirtualQuick template function declarations. | 
 | 369 | #define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range)                       \ | 
 | 370 | template bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame,  \ | 
 | 371 |                                               const Instruction* inst, JValue* result) | 
 | 372 |  | 
 | 373 | EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false); | 
 | 374 | EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); | 
 | 375 | #undef EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL | 
 | 376 |  | 
 | 377 | // Explicit DoFilledNewArray template function declarations. | 
 | 378 | #define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check)                \ | 
 | 379 |   template bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst,         \ | 
 | 380 |                                                      const ShadowFrame& shadow_frame, \ | 
 | 381 |                                                      Thread* self, JValue* result) | 
 | 382 | EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false); | 
 | 383 | EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true); | 
 | 384 | EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false); | 
 | 385 | EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true); | 
 | 386 | #undef EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL | 
 | 387 |  | 
 | 388 | }  // namespace interpreter | 
 | 389 | }  // namespace art |