xref: /aosp_15_r20/art/runtime/var_handles.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2018 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 "var_handles.h"
18 
19 #include "art_method.h"
20 #include "common_throws.h"
21 #include "dex/dex_instruction.h"
22 #include "handle.h"
23 #include "method_handles-inl.h"
24 #include "mirror/method_type-inl.h"
25 #include "mirror/var_handle.h"
26 
27 namespace art HIDDEN {
28 
29 namespace {
30 
31 template <typename CallSiteType, typename CalleeType>
32 class ThrowWrongMethodTypeFunctionImpl final : public ThrowWrongMethodTypeFunction {
33  public:
ThrowWrongMethodTypeFunctionImpl(CallSiteType callsite_type,CalleeType callee_type)34   ThrowWrongMethodTypeFunctionImpl(CallSiteType callsite_type, CalleeType callee_type)
35       : callsite_type_(callsite_type),
36         callee_type_(callee_type) {}
37 
~ThrowWrongMethodTypeFunctionImpl()38   ~ThrowWrongMethodTypeFunctionImpl() {}
39 
operator ()() const40   void operator()() const override REQUIRES_SHARED(Locks::mutator_lock_) {
41     ThrowWrongMethodTypeException(mirror::MethodType::PrettyDescriptor(callee_type_),
42                                   mirror::MethodType::PrettyDescriptor(callsite_type_));
43   }
44 
45  private:
46   CallSiteType callsite_type_;
47   CalleeType callee_type_;
48 };
49 
50 template <typename CallSiteType>
VarHandleInvokeAccessorWithConversions(Thread * self,ShadowFrame & shadow_frame,Handle<mirror::VarHandle> var_handle,CallSiteType callsite_type,mirror::VarHandle::AccessMode access_mode,const InstructionOperands * operands,JValue * result)51 bool VarHandleInvokeAccessorWithConversions(Thread* self,
52                                             ShadowFrame& shadow_frame,
53                                             Handle<mirror::VarHandle> var_handle,
54                                             CallSiteType callsite_type,
55                                             mirror::VarHandle::AccessMode access_mode,
56                                             const InstructionOperands* operands,
57                                             JValue* result)
58     REQUIRES_SHARED(Locks::mutator_lock_) {
59   // Use a raw method handle for `accessor_type`, avoid allocating a managed `MethodType`.
60   VariableSizedHandleScope accessor_type_hs(self);
61   mirror::RawMethodType accessor_type(&accessor_type_hs);
62   var_handle->GetMethodTypeForAccessMode(access_mode, accessor_type);
63   using HandleScopeType = std::conditional_t<
64       std::is_same_v<VariableSizedHandleScope*, CallSiteType>,
65       Thread*,  // No handle scope needed, use `Thread*` that can be initialized from `self`.
66       StackHandleScope<3>>;
67   HandleScopeType hs(self);
68   ThrowWrongMethodTypeFunctionImpl throw_wmt(callsite_type, accessor_type);
69   auto from_types = mirror::MethodType::NewHandlePTypes(callsite_type, &hs);
70   auto to_types = mirror::MethodType::NewHandlePTypes(accessor_type, &hs);
71   const size_t num_vregs = mirror::MethodType::NumberOfVRegs(accessor_type);
72   ShadowFrameAllocaUniquePtr accessor_frame =
73       CREATE_SHADOW_FRAME(num_vregs, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
74   ShadowFrameGetter getter(shadow_frame, operands);
75   static const uint32_t kFirstDestinationReg = 0;
76   ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
77   if (!PerformConversions(throw_wmt, from_types, to_types, &getter, &setter)) {
78     DCHECK(self->IsExceptionPending());
79     return false;
80   }
81   RangeInstructionOperands accessor_operands(kFirstDestinationReg,
82                                              kFirstDestinationReg + num_vregs);
83   if (!var_handle->Access(access_mode, accessor_frame.get(), &accessor_operands, result)) {
84     DCHECK(self->IsExceptionPending());
85     return false;
86   }
87   if (!ConvertReturnValue(throw_wmt,
88                           mirror::MethodType::GetRType(accessor_type),
89                           mirror::MethodType::GetRType(callsite_type),
90                           result)) {
91     DCHECK(self->IsExceptionPending());
92     return false;
93   }
94   return true;
95 }
96 
97 template <typename CallSiteType>
VarHandleInvokeAccessorImpl(Thread * self,ShadowFrame & shadow_frame,Handle<mirror::VarHandle> var_handle,CallSiteType callsite_type,const mirror::VarHandle::AccessMode access_mode,const InstructionOperands * const operands,JValue * result)98 bool VarHandleInvokeAccessorImpl(Thread* self,
99                                  ShadowFrame& shadow_frame,
100                                  Handle<mirror::VarHandle> var_handle,
101                                  CallSiteType callsite_type,
102                                  const mirror::VarHandle::AccessMode access_mode,
103                                  const InstructionOperands* const operands,
104                                  JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
105   if (var_handle.IsNull()) {
106     ThrowNullPointerExceptionFromDexPC();
107     return false;
108   }
109 
110   if (!var_handle->IsAccessModeSupported(access_mode)) {
111     ThrowUnsupportedOperationException();
112     return false;
113   }
114 
115   mirror::VarHandle::MatchKind match_kind =
116       var_handle->GetMethodTypeMatchForAccessMode(access_mode, callsite_type);
117   if (LIKELY(match_kind == mirror::VarHandle::MatchKind::kExact)) {
118     return var_handle->Access(access_mode, &shadow_frame, operands, result);
119   } else if (match_kind == mirror::VarHandle::MatchKind::kWithConversions) {
120     return VarHandleInvokeAccessorWithConversions(self,
121                                                   shadow_frame,
122                                                   var_handle,
123                                                   callsite_type,
124                                                   access_mode,
125                                                   operands,
126                                                   result);
127   } else {
128     DCHECK_EQ(match_kind, mirror::VarHandle::MatchKind::kNone);
129     ThrowWrongMethodTypeException(var_handle->PrettyDescriptorForAccessMode(access_mode),
130                                   mirror::MethodType::PrettyDescriptor(callsite_type));
131     return false;
132   }
133 }
134 
135 }  // namespace
136 
VarHandleInvokeAccessor(Thread * self,ShadowFrame & shadow_frame,Handle<mirror::VarHandle> var_handle,Handle<mirror::MethodType> callsite_type,const mirror::VarHandle::AccessMode access_mode,const InstructionOperands * const operands,JValue * result)137 bool VarHandleInvokeAccessor(Thread* self,
138                              ShadowFrame& shadow_frame,
139                              Handle<mirror::VarHandle> var_handle,
140                              Handle<mirror::MethodType> callsite_type,
141                              const mirror::VarHandle::AccessMode access_mode,
142                              const InstructionOperands* const operands,
143                              JValue* result) {
144   return VarHandleInvokeAccessorImpl(
145       self, shadow_frame, var_handle, callsite_type, access_mode, operands, result);
146 }
147 
VarHandleInvokeAccessor(Thread * self,ShadowFrame & shadow_frame,Handle<mirror::VarHandle> var_handle,mirror::RawMethodType callsite_type,const mirror::VarHandle::AccessMode access_mode,const InstructionOperands * const operands,JValue * result)148 bool VarHandleInvokeAccessor(Thread* self,
149                              ShadowFrame& shadow_frame,
150                              Handle<mirror::VarHandle> var_handle,
151                              mirror::RawMethodType callsite_type,
152                              const mirror::VarHandle::AccessMode access_mode,
153                              const InstructionOperands* const operands,
154                              JValue* result) {
155   return VarHandleInvokeAccessorImpl(
156       self, shadow_frame, var_handle, callsite_type, access_mode, operands, result);
157 }
158 
VarHandleInvokeAccessor(Thread * self,ShadowFrame & shadow_frame,Handle<mirror::VarHandle> var_handle,ArtMethod * caller_method,const dex::ProtoIndex callsite_type_id,const mirror::VarHandle::AccessMode access_mode,const InstructionOperands * const operands,JValue * result)159 bool VarHandleInvokeAccessor(Thread* self,
160                              ShadowFrame& shadow_frame,
161                              Handle<mirror::VarHandle> var_handle,
162                              ArtMethod* caller_method,
163                              const dex::ProtoIndex callsite_type_id,
164                              const mirror::VarHandle::AccessMode access_mode,
165                              const InstructionOperands* const operands,
166                              JValue* result) {
167   StackHandleScope<3> hs(self);
168   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
169 
170   Handle<mirror::DexCache> dex_cache = hs.NewHandle(caller_method->GetDexCache());
171   Handle<mirror::ClassLoader> class_loader = hs.NewHandle(caller_method->GetClassLoader());
172 
173   // If the `ThreadLocalRandom` class is not yet initialized, do the `VarHandle` operation
174   // without creating a managed `MethodType` object. This avoids a circular initialization
175   // issue when `ThreadLocalRandom.<clinit>` indirectly calls `AtomicLong.compareAndSet()`
176   // (implemented with a `VarHandle`) and the `MethodType` caching circles back to the
177   // `ThreadLocalRandom` with uninitialized `seeder` and throws NPE.
178   //
179   // Do a quick test for "visibly initialized" without a read barrier and, if that fails,
180   // do a thorough test for "initialized" (including load acquire) with the read barrier.
181   ArtField* field = WellKnownClasses::java_util_concurrent_ThreadLocalRandom_seeder;
182   if (LIKELY(field->GetDeclaringClass<kWithoutReadBarrier>()->IsVisiblyInitialized()) ||
183       field->GetDeclaringClass()->IsInitialized()) {
184     Handle<mirror::MethodType> callsite_type(hs.NewHandle(
185         class_linker->ResolveMethodType(self, callsite_type_id, dex_cache, class_loader)));
186 
187     if (LIKELY(callsite_type != nullptr)) {
188       return VarHandleInvokeAccessor(self,
189                                      shadow_frame,
190                                      var_handle,
191                                      callsite_type,
192                                      access_mode,
193                                      operands,
194                                      result);
195     }
196     // This implies we couldn't resolve one or more types in this VarHandle,
197     // or we could not allocate the `MethodType` object.
198     CHECK(self->IsExceptionPending());
199     if (self->GetException()->GetClass() != WellKnownClasses::java_lang_OutOfMemoryError.Get()) {
200       return false;
201     }
202     // Clear the OOME and retry without creating an actual `MethodType` object.
203     // This prevents unexpected OOME for trivial `VarHandle` operations.
204     // It also prevents odd situations where a `VarHandle` operation succeeds but the same
205     // operation fails later because the `MethodType` object was evicted from the `DexCache`
206     // and we suddenly run out of memory to allocate a new one.
207     //
208     // We have previously seen OOMEs in the run-test `183-rmw-stress-test` with
209     // `--optimizng --no-image` (boot class path methods run in interpreter without JIT)
210     // but it probably happened on the first execution of a trivial `VarHandle` operation
211     // and not due to the `DexCache` eviction mentioned above.
212     self->ClearException();
213   }
214 
215   VariableSizedHandleScope callsite_type_hs(self);
216   mirror::RawMethodType callsite_type(&callsite_type_hs);
217   if (!class_linker->ResolveMethodType(self,
218                                        callsite_type_id,
219                                        dex_cache,
220                                        class_loader,
221                                        callsite_type)) {
222     CHECK(self->IsExceptionPending());
223     return false;
224   }
225   return VarHandleInvokeAccessor(self,
226                                  shadow_frame,
227                                  var_handle,
228                                  callsite_type,
229                                  access_mode,
230                                  operands,
231                                  result);
232 }
233 
234 }  // namespace art
235