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