| /* |
| * Copyright (C) 2012 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. |
| */ |
| |
| #include "object.h" |
| #include "object_utils.h" |
| #include "reflection.h" |
| #include "runtime_support.h" |
| #include "scoped_thread_state_change.h" |
| #include "thread.h" |
| #include "well_known_classes.h" |
| |
| #include "ScopedLocalRef.h" |
| |
| #if defined(__arm__) |
| #define SP_OFFSET_IN_BYTES 12 |
| #define FRAME_SIZE_IN_BYTES 48u |
| #define ARG2_OFFSET_IN_WORDS 11 // offset to 3rd arg; skip callee saves, LR, Method* and out arg spills for OUT0 to OUT2 |
| #elif defined(__mips__) |
| #define SP_OFFSET_IN_BYTES 8 |
| #define FRAME_SIZE_IN_BYTES 32u |
| #define ARG2_OFFSET_IN_WORDS 8 // offset to 3rd arg; skip callee saves, LR, Method* and out arg spills for OUT0 to OUT2 |
| #elif defined(__i386__) |
| #define SP_OFFSET_IN_BYTES 8 |
| #define FRAME_SIZE_IN_BYTES 32u |
| #define ARG2_OFFSET_IN_WORDS 8 // offset to 3rd arg; skip callee saves, LR, Method* and out arg spills for OUT0 to OUT2 |
| #else |
| #error "Unsupported architecture" |
| #endif |
| |
| namespace art { |
| |
| // Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method |
| // which is responsible for recording callee save registers. We explicitly handlerize incoming |
| // reference arguments (so they survive GC) and create a boxed argument array. Finally we invoke |
| // the invocation handler which is a field within the proxy object receiver. |
| extern "C" void artProxyInvokeHandler(AbstractMethod* proxy_method, Object* receiver, |
| Thread* self, byte* stack_args) |
| SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { |
| // Register the top of the managed stack |
| AbstractMethod** proxy_sp = reinterpret_cast<AbstractMethod**>(stack_args - SP_OFFSET_IN_BYTES); |
| DCHECK_EQ(*proxy_sp, proxy_method); |
| self->SetTopOfStack(proxy_sp, 0); |
| DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), FRAME_SIZE_IN_BYTES); |
| // Start new JNI local reference state |
| JNIEnvExt* env = self->GetJniEnv(); |
| ScopedObjectAccessUnchecked soa(env); |
| ScopedJniEnvLocalRefState env_state(env); |
| // Create local ref. copies of proxy method and the receiver |
| jobject rcvr_jobj = soa.AddLocalReference<jobject>(receiver); |
| jobject proxy_method_jobj = soa.AddLocalReference<jobject>(proxy_method); |
| |
| // Placing into local references incoming arguments from the caller's register arguments, |
| // replacing original Object* with jobject |
| MethodHelper proxy_mh(proxy_method); |
| const size_t num_params = proxy_mh.NumArgs(); |
| size_t args_in_regs = 0; |
| for (size_t i = 1; i < num_params; i++) { // skip receiver |
| args_in_regs = args_in_regs + (proxy_mh.IsParamALongOrDouble(i) ? 2 : 1); |
| if (args_in_regs > 2) { |
| args_in_regs = 2; |
| break; |
| } |
| } |
| size_t cur_arg = 0; // current stack location to read |
| size_t param_index = 1; // skip receiver |
| while (cur_arg < args_in_regs && param_index < num_params) { |
| if (proxy_mh.IsParamAReference(param_index)) { |
| Object* obj = *reinterpret_cast<Object**>(stack_args + (cur_arg * kPointerSize)); |
| jobject jobj = soa.AddLocalReference<jobject>(obj); |
| *reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize)) = jobj; |
| } |
| cur_arg = cur_arg + (proxy_mh.IsParamALongOrDouble(param_index) ? 2 : 1); |
| param_index++; |
| } |
| // Placing into local references incoming arguments from the caller's stack arguments |
| cur_arg += ARG2_OFFSET_IN_WORDS; |
| while (param_index < num_params) { |
| if (proxy_mh.IsParamAReference(param_index)) { |
| Object* obj = *reinterpret_cast<Object**>(stack_args + (cur_arg * kPointerSize)); |
| jobject jobj = soa.AddLocalReference<jobject>(obj); |
| *reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize)) = jobj; |
| } |
| cur_arg = cur_arg + (proxy_mh.IsParamALongOrDouble(param_index) ? 2 : 1); |
| param_index++; |
| } |
| // Set up arguments array and place in local IRT during boxing (which may allocate/GC) |
| jvalue args_jobj[3]; |
| args_jobj[0].l = rcvr_jobj; |
| args_jobj[1].l = proxy_method_jobj; |
| // Args array, if no arguments then NULL (don't include receiver in argument count) |
| args_jobj[2].l = NULL; |
| ObjectArray<Object>* args = NULL; |
| if ((num_params - 1) > 0) { |
| args = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(num_params - 1); |
| if (args == NULL) { |
| CHECK(self->IsExceptionPending()); |
| return; |
| } |
| args_jobj[2].l = soa.AddLocalReference<jobjectArray>(args); |
| } |
| // Convert proxy method into expected interface method |
| AbstractMethod* interface_method = proxy_method->FindOverriddenMethod(); |
| DCHECK(interface_method != NULL); |
| DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method); |
| args_jobj[1].l = soa.AddLocalReference<jobject>(interface_method); |
| // Box arguments |
| cur_arg = 0; // reset stack location to read to start |
| // reset index, will index into param type array which doesn't include the receiver |
| param_index = 0; |
| ObjectArray<Class>* param_types = proxy_mh.GetParameterTypes(); |
| if (param_types == NULL) { |
| CHECK(self->IsExceptionPending()); |
| return; |
| } |
| // Check number of parameter types agrees with number from the Method - less 1 for the receiver. |
| DCHECK_EQ(static_cast<size_t>(param_types->GetLength()), num_params - 1); |
| while (cur_arg < args_in_regs && param_index < (num_params - 1)) { |
| Class* param_type = param_types->Get(param_index); |
| Object* obj; |
| if (!param_type->IsPrimitive()) { |
| obj = self->DecodeJObject(*reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize))); |
| } else { |
| JValue val = *reinterpret_cast<JValue*>(stack_args + (cur_arg * kPointerSize)); |
| if (cur_arg == 1 && (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble())) { |
| // long/double split over regs and stack, mask in high half from stack arguments |
| uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args + ((ARG2_OFFSET_IN_WORDS + 2) * kPointerSize)); |
| val.SetJ((val.GetJ() & 0xffffffffULL) | (high_half << 32)); |
| } |
| BoxPrimitive(param_type->GetPrimitiveType(), val); |
| if (self->IsExceptionPending()) { |
| return; |
| } |
| obj = val.GetL(); |
| } |
| args->Set(param_index, obj); |
| cur_arg = cur_arg + (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble() ? 2 : 1); |
| param_index++; |
| } |
| // Placing into local references incoming arguments from the caller's stack arguments |
| cur_arg += ARG2_OFFSET_IN_WORDS; |
| while (param_index < (num_params - 1)) { |
| Class* param_type = param_types->Get(param_index); |
| Object* obj; |
| if (!param_type->IsPrimitive()) { |
| obj = self->DecodeJObject(*reinterpret_cast<jobject*>(stack_args + (cur_arg * kPointerSize))); |
| } else { |
| JValue val = *reinterpret_cast<JValue*>(stack_args + (cur_arg * kPointerSize)); |
| BoxPrimitive(param_type->GetPrimitiveType(), val); |
| if (self->IsExceptionPending()) { |
| return; |
| } |
| obj = val.GetL(); |
| } |
| args->Set(param_index, obj); |
| cur_arg = cur_arg + (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble() ? 2 : 1); |
| param_index++; |
| } |
| // Get the InvocationHandler method and the field that holds it within the Proxy object |
| DCHECK(env->IsInstanceOf(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy)); |
| jobject inv_hand = env->GetObjectField(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy_h); |
| // Call InvocationHandler.invoke |
| jobject result = env->CallObjectMethodA(inv_hand, WellKnownClasses::java_lang_reflect_InvocationHandler_invoke, args_jobj); |
| // Place result in stack args |
| if (!self->IsExceptionPending()) { |
| Object* result_ref = self->DecodeJObject(result); |
| if (result_ref != NULL) { |
| JValue result_unboxed; |
| bool unboxed_okay = UnboxPrimitiveForResult(result_ref, proxy_mh.GetReturnType(), result_unboxed); |
| if (!unboxed_okay) { |
| self->ClearException(); |
| self->ThrowNewExceptionF("Ljava/lang/ClassCastException;", |
| "Couldn't convert result of type %s to %s", |
| PrettyTypeOf(result_ref).c_str(), |
| PrettyDescriptor(proxy_mh.GetReturnType()).c_str()); |
| return; |
| } |
| *reinterpret_cast<JValue*>(stack_args) = result_unboxed; |
| } else { |
| *reinterpret_cast<jobject*>(stack_args) = NULL; |
| } |
| } else { |
| // In the case of checked exceptions that aren't declared, the exception must be wrapped by |
| // a UndeclaredThrowableException. |
| Throwable* exception = self->GetException(); |
| if (exception->IsCheckedException()) { |
| SynthesizedProxyClass* proxy_class = |
| down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass()); |
| int throws_index = -1; |
| size_t num_virt_methods = proxy_class->NumVirtualMethods(); |
| for (size_t i = 0; i < num_virt_methods; i++) { |
| if (proxy_class->GetVirtualMethod(i) == proxy_method) { |
| throws_index = i; |
| break; |
| } |
| } |
| CHECK_NE(throws_index, -1); |
| ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index); |
| Class* exception_class = exception->GetClass(); |
| bool declares_exception = false; |
| for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) { |
| Class* declared_exception = declared_exceptions->Get(i); |
| declares_exception = declared_exception->IsAssignableFrom(exception_class); |
| } |
| if (!declares_exception) { |
| self->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;", NULL); |
| } |
| } |
| } |
| } |
| |
| } // namespace art |