closure.cc revision 457e874459ae638145cab6d572e34d48480e39d2
1/* 2 * Copyright (C) 2015 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 "lambda/closure.h" 18 19#include "base/logging.h" 20#include "lambda/art_lambda_method.h" 21#include "runtime/mirror/object_reference.h" 22 23namespace art { 24namespace lambda { 25 26template <typename T> 27// TODO: can I return T __attribute__((__aligned__(1)))* here instead? 28const uint8_t* Closure::GetUnsafeAtOffset(size_t offset) const { 29 // Do not DCHECK here with existing helpers since most of them will call into this function. 30 return reinterpret_cast<const uint8_t*>(captured_) + offset; 31} 32 33size_t Closure::GetCapturedVariableSize(ShortyFieldType variable_type, size_t offset) const { 34 switch (variable_type) { 35 case ShortyFieldType::kLambda: 36 { 37 return GetClosureSize(GetUnsafeAtOffset<Closure>(offset)); 38 } 39 default: 40 DCHECK(variable_type.IsStaticSize()); 41 return variable_type.GetStaticSize(); 42 } 43} 44 45// Templatize the flags to give the compiler a fighting chance to eliminate 46// any unnecessary code through different uses of this function. 47template <Closure::VariableInfo::Flags flags> 48inline Closure::VariableInfo Closure::ParseTypeDescriptor(const char* type_descriptor, 49 size_t upto_index) const { 50 DCHECK(type_descriptor != nullptr); 51 52 VariableInfo result; 53 54 ShortyFieldType last_type; 55 size_t offset = (flags & VariableInfo::kOffset) ? GetStartingOffset() : 0; 56 size_t prev_offset = 0; 57 size_t count = 0; 58 59 while ((type_descriptor = 60 ShortyFieldType::ParseFromFieldTypeDescriptor(type_descriptor, &last_type)) != nullptr) { 61 count++; 62 63 if (flags & VariableInfo::kOffset) { 64 // Accumulate the sizes of all preceding captured variables as the current offset only. 65 offset += prev_offset; 66 prev_offset = GetCapturedVariableSize(last_type, offset); 67 } 68 69 if ((count > upto_index)) { 70 break; 71 } 72 } 73 74 if (flags & VariableInfo::kVariableType) { 75 result.variable_type_ = last_type; 76 } 77 78 if (flags & VariableInfo::kIndex) { 79 result.index_ = count; 80 } 81 82 if (flags & VariableInfo::kCount) { 83 result.count_ = count; 84 } 85 86 if (flags & VariableInfo::kOffset) { 87 result.offset_ = offset; 88 } 89 90 // TODO: We should probably store the result of this in the ArtLambdaMethod, 91 // to avoid re-computing the data every single time for static closures. 92 return result; 93} 94 95size_t Closure::GetCapturedVariablesSize() const { 96 const size_t captured_variable_offset = offsetof(Closure, captured_); 97 DCHECK_GE(GetSize(), captured_variable_offset); // Prevent underflows. 98 return GetSize() - captured_variable_offset; 99} 100 101size_t Closure::GetSize() const { 102 const size_t static_closure_size = lambda_info_->GetStaticClosureSize(); 103 if (LIKELY(lambda_info_->IsStaticSize())) { 104 return static_closure_size; 105 } 106 107 DCHECK_GE(static_closure_size, sizeof(captured_[0].dynamic_.size_)); 108 const size_t dynamic_closure_size = captured_[0].dynamic_.size_; 109 // The dynamic size better be at least as big as the static size. 110 DCHECK_GE(dynamic_closure_size, static_closure_size); 111 112 return dynamic_closure_size; 113} 114 115void Closure::CopyTo(void* target, size_t target_size) const { 116 DCHECK_GE(target_size, GetSize()); 117 118 // TODO: using memcpy is unsafe with read barriers, fix this once we add reference support 119 static_assert(kClosureSupportsReferences == false, 120 "Do not use memcpy with readbarrier references"); 121 memcpy(target, this, GetSize()); 122} 123 124ArtMethod* Closure::GetTargetMethod() const { 125 return const_cast<ArtMethod*>(lambda_info_->GetArtMethod()); 126} 127 128ArtLambdaMethod* Closure::GetLambdaInfo() const { 129 return const_cast<ArtLambdaMethod*>(lambda_info_); 130} 131 132uint32_t Closure::GetHashCode() const { 133 // Start with a non-zero constant, a prime number. 134 uint32_t result = 17; 135 136 // Include the hash with the ArtMethod. 137 { 138 uintptr_t method = reinterpret_cast<uintptr_t>(GetTargetMethod()); 139 result = 31 * result + Low32Bits(method); 140 if (sizeof(method) == sizeof(uint64_t)) { 141 result = 31 * result + High32Bits(method); 142 } 143 } 144 145 // Include a hash for each captured variable. 146 for (size_t i = 0; i < GetCapturedVariablesSize(); ++i) { 147 // TODO: not safe for GC-able values since the address can move and the hash code would change. 148 uint8_t captured_variable_raw_value; 149 CopyUnsafeAtOffset<uint8_t>(i, /*out*/&captured_variable_raw_value); // NOLINT: [whitespace/comma] [3] 150 151 result = 31 * result + captured_variable_raw_value; 152 } 153 154 // TODO: Fix above loop to work for objects and lambdas. 155 static_assert(kClosureSupportsGarbageCollection == false, 156 "Need to update above loop to read the hash code from the " 157 "objects and lambdas recursively"); 158 159 return result; 160} 161 162bool Closure::ReferenceEquals(const Closure* other) const { 163 DCHECK(other != nullptr); 164 165 // TODO: Need rework to use read barriers once closures have references inside of them that can 166 // move. Until then, it's safe to just compare the data inside of it directly. 167 static_assert(kClosureSupportsReferences == false, 168 "Unsafe to use memcmp in read barrier collector"); 169 170 if (GetSize() != other->GetSize()) { 171 return false; 172 } 173 174 return memcmp(this, other, GetSize()); 175} 176 177size_t Closure::GetNumberOfCapturedVariables() const { 178 // TODO: refactor into art_lambda_method.h. Parsing should only be required here as a DCHECK. 179 VariableInfo variable_info = 180 ParseTypeDescriptor<VariableInfo::kCount>(GetCapturedVariablesTypeDescriptor(), 181 VariableInfo::kUpToIndexMax); 182 size_t count = variable_info.count_; 183 // Assuming each variable was 1 byte, the size should always be greater or equal than the count. 184 DCHECK_LE(count, GetCapturedVariablesSize()); 185 return count; 186} 187 188const char* Closure::GetCapturedVariablesTypeDescriptor() const { 189 return lambda_info_->GetCapturedVariablesTypeDescriptor(); 190} 191 192ShortyFieldType Closure::GetCapturedShortyType(size_t index) const { 193 DCHECK_LT(index, GetNumberOfCapturedVariables()); 194 195 VariableInfo variable_info = 196 ParseTypeDescriptor<VariableInfo::kVariableType>(GetCapturedVariablesTypeDescriptor(), 197 index); 198 199 return variable_info.variable_type_; 200} 201 202uint32_t Closure::GetCapturedPrimitiveNarrow(size_t index) const { 203 DCHECK(GetCapturedShortyType(index).IsPrimitiveNarrow()); 204 205 ShortyFieldType variable_type; 206 size_t offset; 207 GetCapturedVariableTypeAndOffset(index, &variable_type, &offset); 208 209 // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T> 210 // so that we can avoid this nonsense regarding memcpy always overflowing. 211 // Plus, this additional switching seems redundant since the interpreter 212 // would've done it already, and knows the exact type. 213 uint32_t result = 0; 214 static_assert(ShortyFieldTypeTraits::IsPrimitiveNarrowType<decltype(result)>(), 215 "result must be a primitive narrow type"); 216 switch (variable_type) { 217 case ShortyFieldType::kBoolean: 218 CopyUnsafeAtOffset<bool>(offset, &result); 219 break; 220 case ShortyFieldType::kByte: 221 CopyUnsafeAtOffset<uint8_t>(offset, &result); 222 break; 223 case ShortyFieldType::kChar: 224 CopyUnsafeAtOffset<uint16_t>(offset, &result); 225 break; 226 case ShortyFieldType::kShort: 227 CopyUnsafeAtOffset<int16_t>(offset, &result); 228 break; 229 case ShortyFieldType::kInt: 230 CopyUnsafeAtOffset<int32_t>(offset, &result); 231 break; 232 case ShortyFieldType::kFloat: 233 // XX: Maybe there should just be a GetCapturedPrimitive<T> to avoid this shuffle? 234 // The interpreter's invoke seems to only special case references and wides, 235 // everything else is treated as a generic 32-bit pattern. 236 CopyUnsafeAtOffset<float>(offset, &result); 237 break; 238 default: 239 LOG(FATAL) 240 << "expected a valid narrow primitive shorty type but got " 241 << static_cast<char>(variable_type); 242 UNREACHABLE(); 243 } 244 245 return result; 246} 247 248uint64_t Closure::GetCapturedPrimitiveWide(size_t index) const { 249 DCHECK(GetCapturedShortyType(index).IsPrimitiveWide()); 250 251 ShortyFieldType variable_type; 252 size_t offset; 253 GetCapturedVariableTypeAndOffset(index, &variable_type, &offset); 254 255 // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T> 256 // so that we can avoid this nonsense regarding memcpy always overflowing. 257 // Plus, this additional switching seems redundant since the interpreter 258 // would've done it already, and knows the exact type. 259 uint64_t result = 0; 260 static_assert(ShortyFieldTypeTraits::IsPrimitiveWideType<decltype(result)>(), 261 "result must be a primitive wide type"); 262 switch (variable_type) { 263 case ShortyFieldType::kLong: 264 CopyUnsafeAtOffset<int64_t>(offset, &result); 265 break; 266 case ShortyFieldType::kDouble: 267 CopyUnsafeAtOffset<double>(offset, &result); 268 break; 269 default: 270 LOG(FATAL) 271 << "expected a valid primitive wide shorty type but got " 272 << static_cast<char>(variable_type); 273 UNREACHABLE(); 274 } 275 276 return result; 277} 278 279mirror::Object* Closure::GetCapturedObject(size_t index) const { 280 DCHECK(GetCapturedShortyType(index).IsObject()); 281 282 ShortyFieldType variable_type; 283 size_t offset; 284 GetCapturedVariableTypeAndOffset(index, &variable_type, &offset); 285 286 // TODO: Restructure to use template specialization, e.g. GetCapturedPrimitive<T> 287 // so that we can avoid this nonsense regarding memcpy always overflowing. 288 // Plus, this additional switching seems redundant since the interpreter 289 // would've done it already, and knows the exact type. 290 mirror::Object* result = nullptr; 291 static_assert(ShortyFieldTypeTraits::IsObjectType<decltype(result)>(), 292 "result must be an object type"); 293 switch (variable_type) { 294 case ShortyFieldType::kObject: 295 // TODO: This seems unsafe. This may need to use gcroots. 296 static_assert(kClosureSupportsGarbageCollection == false, 297 "May need GcRoots and definitely need mutator locks"); 298 { 299 mirror::CompressedReference<mirror::Object> compressed_result; 300 CopyUnsafeAtOffset<uint32_t>(offset, &compressed_result); 301 result = compressed_result.AsMirrorPtr(); 302 } 303 break; 304 default: 305 CHECK(false) 306 << "expected a valid shorty type but got " << static_cast<char>(variable_type); 307 UNREACHABLE(); 308 } 309 310 return result; 311} 312 313size_t Closure::GetCapturedClosureSize(size_t index) const { 314 DCHECK(GetCapturedShortyType(index).IsLambda()); 315 size_t offset = GetCapturedVariableOffset(index); 316 317 auto* captured_ptr = reinterpret_cast<const uint8_t*>(&captured_); 318 size_t closure_size = GetClosureSize(captured_ptr + offset); 319 320 return closure_size; 321} 322 323void Closure::CopyCapturedClosure(size_t index, void* destination, size_t destination_room) const { 324 DCHECK(GetCapturedShortyType(index).IsLambda()); 325 size_t offset = GetCapturedVariableOffset(index); 326 327 auto* captured_ptr = reinterpret_cast<const uint8_t*>(&captured_); 328 size_t closure_size = GetClosureSize(captured_ptr + offset); 329 330 static_assert(ShortyFieldTypeTraits::IsLambdaType<Closure*>(), 331 "result must be a lambda type"); 332 333 CopyUnsafeAtOffset<Closure>(offset, destination, closure_size, destination_room); 334} 335 336size_t Closure::GetCapturedVariableOffset(size_t index) const { 337 VariableInfo variable_info = 338 ParseTypeDescriptor<VariableInfo::kOffset>(GetCapturedVariablesTypeDescriptor(), 339 index); 340 341 size_t offset = variable_info.offset_; 342 343 return offset; 344} 345 346void Closure::GetCapturedVariableTypeAndOffset(size_t index, 347 ShortyFieldType* out_type, 348 size_t* out_offset) const { 349 DCHECK(out_type != nullptr); 350 DCHECK(out_offset != nullptr); 351 352 static constexpr const VariableInfo::Flags kVariableTypeAndOffset = 353 static_cast<VariableInfo::Flags>(VariableInfo::kVariableType | VariableInfo::kOffset); 354 VariableInfo variable_info = 355 ParseTypeDescriptor<kVariableTypeAndOffset>(GetCapturedVariablesTypeDescriptor(), 356 index); 357 358 ShortyFieldType variable_type = variable_info.variable_type_; 359 size_t offset = variable_info.offset_; 360 361 *out_type = variable_type; 362 *out_offset = offset; 363} 364 365template <typename T> 366void Closure::CopyUnsafeAtOffset(size_t offset, 367 void* destination, 368 size_t src_size, 369 size_t destination_room) const { 370 DCHECK_GE(destination_room, src_size); 371 const uint8_t* data_ptr = GetUnsafeAtOffset<T>(offset); 372 memcpy(destination, data_ptr, sizeof(T)); 373} 374 375// TODO: This is kind of ugly. I would prefer an unaligned_ptr<Closure> here. 376// Unfortunately C++ doesn't let you lower the alignment (i.e. alignas(1) Closure*) is not legal. 377size_t Closure::GetClosureSize(const uint8_t* closure) { 378 DCHECK(closure != nullptr); 379 380 static_assert(!std::is_base_of<mirror::Object, Closure>::value, 381 "It might be unsafe to call memcpy on a managed object"); 382 383 // Safe as long as it's not a mirror Object. 384 // TODO: Should probably wrap this in like MemCpyNative or some such which statically asserts 385 // we aren't trying to copy mirror::Object data around. 386 ArtLambdaMethod* closure_info; 387 memcpy(&closure_info, closure + offsetof(Closure, lambda_info_), sizeof(closure_info)); 388 389 if (LIKELY(closure_info->IsStaticSize())) { 390 return closure_info->GetStaticClosureSize(); 391 } 392 393 // The size is dynamic, so we need to read it from captured_variables_ portion. 394 size_t dynamic_size; 395 memcpy(&dynamic_size, 396 closure + offsetof(Closure, captured_[0].dynamic_.size_), 397 sizeof(dynamic_size)); 398 static_assert(sizeof(dynamic_size) == sizeof(captured_[0].dynamic_.size_), 399 "Dynamic size type must match the structural type of the size"); 400 401 DCHECK_GE(dynamic_size, closure_info->GetStaticClosureSize()); 402 return dynamic_size; 403} 404 405size_t Closure::GetStartingOffset() const { 406 static constexpr const size_t captured_offset = offsetof(Closure, captured_); 407 if (LIKELY(lambda_info_->IsStaticSize())) { 408 return offsetof(Closure, captured_[0].static_variables_) - captured_offset; 409 } else { 410 return offsetof(Closure, captured_[0].dynamic_.variables_) - captured_offset; 411 } 412} 413 414} // namespace lambda 415} // namespace art 416