blob: 491d13926f19ef70ec5e22da3741f86013c2a90a [file] [log] [blame]
/*
* Copyright (C) 2016 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 "method_handles.h"
#include "method_handles-inl.h"
#include "jvalue.h"
#include "jvalue-inl.h"
#include "reflection.h"
#include "reflection-inl.h"
namespace art {
namespace {
static const char* kBoxedBooleanClass = "Ljava/lang/Boolean;";
static const char* kBoxedByteClass = "Ljava/lang/Byte;";
static const char* kBoxedCharacterClass = "Ljava/lang/Character;";
static const char* kBoxedDoubleClass = "Ljava/lang/Double;";
static const char* kBoxedFloatClass = "Ljava/lang/Float;";
static const char* kBoxedIntegerClass = "Ljava/lang/Integer;";
static const char* kBoxedLongClass = "Ljava/lang/Long;";
static const char* kBoxedShortClass = "Ljava/lang/Short;";
// Assigns |type| to the primitive type associated with |klass|. Returns
// true iff. |klass| was a boxed type (Integer, Long etc.), false otherwise.
bool GetUnboxedPrimitiveType(ObjPtr<mirror::Class> klass, Primitive::Type* type)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
if (klass->DescriptorEquals(kBoxedBooleanClass)) {
(*type) = Primitive::kPrimBoolean;
return true;
} else if (klass->DescriptorEquals(kBoxedByteClass)) {
(*type) = Primitive::kPrimByte;
return true;
} else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
(*type) = Primitive::kPrimChar;
return true;
} else if (klass->DescriptorEquals(kBoxedFloatClass)) {
(*type) = Primitive::kPrimFloat;
return true;
} else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
(*type) = Primitive::kPrimDouble;
return true;
} else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
(*type) = Primitive::kPrimInt;
return true;
} else if (klass->DescriptorEquals(kBoxedLongClass)) {
(*type) = Primitive::kPrimLong;
return true;
} else if (klass->DescriptorEquals(kBoxedShortClass)) {
(*type) = Primitive::kPrimShort;
return true;
} else {
return false;
}
}
// Returns the class corresponding to the boxed type for the primitive |type|.
ObjPtr<mirror::Class> GetBoxedPrimitiveClass(Primitive::Type type)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
switch (type) {
case Primitive::kPrimBoolean:
return class_linker->FindSystemClass(Thread::Current(), kBoxedBooleanClass);
case Primitive::kPrimByte:
return class_linker->FindSystemClass(Thread::Current(), kBoxedByteClass);
case Primitive::kPrimChar:
return class_linker->FindSystemClass(Thread::Current(), kBoxedCharacterClass);
case Primitive::kPrimShort:
return class_linker->FindSystemClass(Thread::Current(), kBoxedShortClass);
case Primitive::kPrimInt:
return class_linker->FindSystemClass(Thread::Current(), kBoxedIntegerClass);
case Primitive::kPrimLong:
return class_linker->FindSystemClass(Thread::Current(), kBoxedLongClass);
case Primitive::kPrimFloat:
return class_linker->FindSystemClass(Thread::Current(), kBoxedFloatClass);
case Primitive::kPrimDouble:
return class_linker->FindSystemClass(Thread::Current(), kBoxedDoubleClass);
case Primitive::kPrimNot:
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable";
return nullptr;
}
}
// Returns true if |klass| is a boxed primitive type or a sub-class of a boxed primitive type.
bool IsSubClassOfBoxedPrimitive(const Handle<mirror::Class>& klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(Thread::Current());
MutableHandle<mirror::Class> h_klass(hs.NewHandle(klass.Get()));
do {
Primitive::Type type;
if (GetUnboxedPrimitiveType(h_klass.Get(), &type)) {
return true;
}
h_klass.Assign(h_klass->GetSuperClass());
} while (h_klass.Get() != nullptr);
return false;
}
// Unboxed the value |o| to |unboxed_value| of type |dst_class|.
// |unboxed_value| must be zero on entry to avoid dangling pointers.
// Returns true on success, false if an exception is raised.
bool UnboxPrimitiveForMethodHandles(ObjPtr<mirror::Object> o,
ObjPtr<mirror::Class> dst_class,
JValue* unboxed_value)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Check unboxed_value does not contain a dangling pointer.
DCHECK_EQ(unboxed_value->GetJ(), 0);
DCHECK(dst_class->IsPrimitive());
// This is derived from UnboxPrimitive() in reflection.cc, but with
// exceptions appropriate to method handles.
if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
ThrowClassCastException(o->GetClass(), dst_class);
return false;
}
if (UNLIKELY(o == nullptr)) {
ThrowNullPointerException(
StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
dst_class->PrettyDescriptor().c_str()).c_str());
return false;
}
JValue boxed_value;
ObjPtr<mirror::Class> klass = o->GetClass();
ObjPtr<mirror::Class> src_class = nullptr;
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
if (klass->DescriptorEquals(kBoxedBooleanClass)) {
src_class = class_linker->FindPrimitiveClass('Z');
boxed_value.SetZ(primitive_field->GetBoolean(o));
} else if (klass->DescriptorEquals(kBoxedByteClass)) {
src_class = class_linker->FindPrimitiveClass('B');
boxed_value.SetB(primitive_field->GetByte(o));
} else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
src_class = class_linker->FindPrimitiveClass('C');
boxed_value.SetC(primitive_field->GetChar(o));
} else if (klass->DescriptorEquals(kBoxedFloatClass)) {
src_class = class_linker->FindPrimitiveClass('F');
boxed_value.SetF(primitive_field->GetFloat(o));
} else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
src_class = class_linker->FindPrimitiveClass('D');
boxed_value.SetD(primitive_field->GetDouble(o));
} else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
src_class = class_linker->FindPrimitiveClass('I');
boxed_value.SetI(primitive_field->GetInt(o));
} else if (klass->DescriptorEquals(kBoxedLongClass)) {
src_class = class_linker->FindPrimitiveClass('J');
boxed_value.SetJ(primitive_field->GetLong(o));
} else if (klass->DescriptorEquals(kBoxedShortClass)) {
src_class = class_linker->FindPrimitiveClass('S');
boxed_value.SetS(primitive_field->GetShort(o));
} else {
std::string temp;
ThrowIllegalArgumentException(
StringPrintf("result has type %s, got %s",
dst_class->PrettyDescriptor().c_str(),
PrettyDescriptor(o->GetClass()->GetDescriptor(&temp)).c_str()).c_str());
return false;
}
if (!ConvertPrimitiveValueNoThrow(src_class->GetPrimitiveType(),
dst_class->GetPrimitiveType(),
boxed_value,
unboxed_value)) {
ThrowClassCastException(src_class, dst_class);
return false;
}
return true;
}
inline bool IsReferenceType(Primitive::Type type) {
return type == Primitive::kPrimNot;
}
inline bool IsPrimitiveType(Primitive::Type type) {
return !IsReferenceType(type);
}
} // namespace
bool ConvertJValueCommon(
Handle<mirror::MethodType> callsite_type,
Handle<mirror::MethodType> callee_type,
ObjPtr<mirror::Class> from,
ObjPtr<mirror::Class> to,
JValue* value) {
// The reader maybe concerned about the safety of the heap object
// that may be in |value|. There is only one case where allocation
// is obviously needed and that's for boxing. However, in the case
// of boxing |value| contains a non-reference type.
const Primitive::Type from_type = from->GetPrimitiveType();
const Primitive::Type to_type = to->GetPrimitiveType();
// This method must be called only when the types don't match.
DCHECK(from != to);
if (IsPrimitiveType(from_type) && IsPrimitiveType(to_type)) {
// The source and target types are both primitives.
if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, to_type, *value, value))) {
ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
value->SetJ(0);
return false;
}
return true;
} else if (IsReferenceType(from_type) && IsReferenceType(to_type)) {
// They're both reference types. If "from" is null, we can pass it
// through unchanged. If not, we must generate a cast exception if
// |to| is not assignable from the dynamic type of |ref|.
//
// Playing it safe with StackHandleScope here, not expecting any allocation
// in mirror::Class::IsAssignable().
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Class> h_to(hs.NewHandle(to));
Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
// |value| will now be the result value, invalidate its existing value
// as |h_obj| now owns it.
value->SetJ(0);
if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
ThrowClassCastException(h_to.Get(), h_obj->GetClass());
return false;
}
value->SetL(h_obj.Get());
return true;
} else if (IsReferenceType(to_type)) {
DCHECK(IsPrimitiveType(from_type));
// Playing it safe with StackHandleScope here with regards to
// GetUnboxedPrimitiveType() and GetBoxedPrimitiveClass().
StackHandleScope<1> hs(Thread::Current());
Handle<mirror::Class> h_to(hs.NewHandle(to));
// The source type is a primitive and the target type is a reference, so we must box.
// The target type maybe a super class of the boxed source type, for example,
// if the source type is int, it's boxed type is java.lang.Integer, and the target
// type could be java.lang.Number.
Primitive::Type type;
if (!GetUnboxedPrimitiveType(to, &type)) {
ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type);
if (boxed_from_class->IsSubClass(h_to.Get())) {
type = from_type;
} else {
value->SetJ(0);
ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
return false;
}
}
if (UNLIKELY(from_type != type)) {
value->SetJ(0);
ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
return false;
}
if (!ConvertPrimitiveValueNoThrow(from_type, type, *value, value)) {
value->SetJ(0);
ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
return false;
}
// Then perform the actual boxing, and then set the reference.
ObjPtr<mirror::Object> boxed = BoxPrimitive(type, *value);
value->SetL(boxed.Ptr());
return true;
} else {
// The source type is a reference and the target type is a primitive, so we must unbox.
DCHECK(IsReferenceType(from_type));
DCHECK(IsPrimitiveType(to_type));
// Use StackHandleScope to protect |from|, |to|, and the reference
// in |value| from heap re-arrangements that could be triggered
// ahead of unboxing step.
StackHandleScope<3> hs(Thread::Current());
Handle<mirror::Class> h_to(hs.NewHandle(to));
Handle<mirror::Class> h_from(hs.NewHandle(from));
Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
// |value| will now be the result value, invalidate its existing value
// as |h_obj| now owns it.
value->SetJ(0);
// Check source type is a boxed primitive or has a boxed primitive super-class.
ObjPtr<mirror::Class> boxed_to_class = GetBoxedPrimitiveClass(to_type);
if (!IsSubClassOfBoxedPrimitive(h_from) && !boxed_to_class->IsSubClass(h_from.Get())) {
ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
return false;
}
if (h_obj.Get() == nullptr) {
ThrowNullPointerException(
StringPrintf("Expected to unbox a '%s' but instance was null",
h_from->PrettyDescriptor().c_str()).c_str());
return false;
}
return UnboxPrimitiveForMethodHandles(h_obj.Get(), h_to.Get(), value);
}
}
} // namespace art