| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/expr/resolve_variant.h" |
| |
| #include <inttypes.h> |
| |
| #include "src/developer/debug/zxdb/common/ref_ptr_to.h" |
| #include "src/developer/debug/zxdb/expr/eval_context.h" |
| #include "src/developer/debug/zxdb/expr/expr_value.h" |
| #include "src/developer/debug/zxdb/expr/resolve_collection.h" |
| #include "src/developer/debug/zxdb/symbols/data_member.h" |
| #include "src/developer/debug/zxdb/symbols/variant.h" |
| #include "src/developer/debug/zxdb/symbols/variant_part.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| // Gets the active variant for the given value and extracts the single data member inside of it. |
| // Effectively, this gets the DataMember corresponding to the active value of a Rust enum. |
| // |
| // If the value isn't a variant or there isn't a single data member inside of it, returns an error. |
| // Rust enums currently look like this (this is an Option): |
| // |
| // DW_TAG_structure_type |
| // DW_AT_name ("Option<alloc::sync::Arc<fidl::client::ClientInner>>") |
| // DW_AT_byte_size (0x08) |
| // DW_AT_alignment (8) |
| // |
| // DW_TAG_variant_part |
| // // Disciminant (which enum value is active). |
| // DW_AT_discr |
| // DW_TAG_member <==== The DW_AT_discr value refers to this record. |
| // DW_AT_type (0x0000d042 "u64") |
| // DW_AT_alignment (8) |
| // DW_AT_data_member_location (0x00) |
| // DW_AT_artificial (true) |
| // |
| // // Definition for the "None" variant. |
| // DW_TAG_variant |
| // DW_AT_discr_value (0x00) |
| // DW_TAG_member |
| // DW_AT_name ("None") |
| // DW_AT_type (Reference to the "None" member structure defined below) |
| // DW_AT_alignment (8) |
| // DW_AT_data_member_location (0x00) |
| // |
| // // Definition for the "Some" variant. Note this starts at 0 offset which overlaps the |
| // // discriminant, but that's OK because the "Some" structure defined below has 8 bytes |
| // // of padding at the beginning. |
| // DW_TAG_variant |
| // DW_TAG_member |
| // DW_AT_name ("Some") |
| // DW_AT_type (Reference to the "Some" member structure defined below) |
| // DW_AT_alignment (8) |
| // DW_AT_data_member_location (0x00) |
| // |
| // // Type of data for the contents of the "None" data. This contains no members. |
| // DW_TAG_structure_type |
| // DW_AT_name ("None") |
| // DW_AT_byte_size (0x08) |
| // DW_AT_alignment (8) |
| // DW_TAG_template_type_parameter |
| // DW_AT_type (0x00003d77 "alloc::sync::Arc<fidl::client::ClientInner>") |
| // DW_AT_name ("T") |
| // |
| // // Type of data for the contents of the "Some" data. |
| // DW_TAG_structure_type |
| // DW_AT_name ("Some") |
| // DW_AT_byte_size (0x08) |
| // DW_AT_alignment (8) |
| // DW_TAG_template_type_parameter |
| // DW_AT_type (0x00003d77 "alloc::sync::Arc<fidl::client::ClientInner>") |
| // DW_AT_name ("T") |
| // |
| // // Actual data of the "Some". |
| // DW_TAG_member |
| // DW_AT_name ("__0") |
| // DW_AT_type (0x00003d77 "alloc::sync::Arc<fidl::client::ClientInner>") |
| // DW_AT_alignment (8) |
| // DW_AT_data_member_location (0x00) |
| // |
| // So this function will return the DW_TAG_member (of either "Some" or "None" structure type) inside |
| // of the DW_TAG_variant that's active, as indicated by the discriminant. |
| // |
| // In the non-error case, this will always return a valid data member (it won't be is_null()). |
| ErrOr<FoundMember> GetSingleActiveDataMember(const fxl::RefPtr<EvalContext>& context, |
| const ExprValue& value) { |
| fxl::RefPtr<Type> concrete = context->GetConcreteType(value.type()); |
| if (!concrete) |
| return Err("Missing type information."); |
| const Collection* collection = concrete->As<Collection>(); |
| if (!collection) |
| return Err("Attempting to extract a variant from a non-collection."); |
| |
| const VariantPart* part = collection->variant_part().Get()->As<VariantPart>(); |
| if (!part) |
| return Err("Missing variant part for variant."); |
| |
| fxl::RefPtr<Variant> variant; |
| if (Err err = ResolveVariant(context, value, collection, part, &variant); err.has_error()) |
| return err; |
| |
| // Extract the one expected data member. |
| if (variant->data_members().size() > 1) |
| return Err("Expected a single variant data member, got %zu.", variant->data_members().size()); |
| const DataMember* member = variant->data_members()[0].Get()->As<DataMember>(); |
| if (!member) |
| return Err("Invalid data member in variant symbol."); |
| |
| return FoundMember(collection, member); |
| } |
| |
| } // namespace |
| |
| Err ResolveVariant(const fxl::RefPtr<EvalContext>& context, const ExprValue& value, |
| const Collection* collection, const VariantPart* variant_part, |
| fxl::RefPtr<Variant>* result) { |
| // Resolve the discriminant value. It is effectively a member of the enclosing structure. |
| const DataMember* discr_member = variant_part->discriminant().Get()->As<DataMember>(); |
| if (!discr_member) |
| return Err("Missing discriminant for variant."); |
| |
| // Variants don't have static variant members or virtual inheritance. |
| ErrOrValue discr_value = |
| ResolveNonstaticMember(context, value, FoundMember(collection, discr_member)); |
| if (discr_value.has_error()) |
| return discr_value.err(); |
| |
| // Expect the discriminant value to resolve to a <= 64-bit number. |
| // |
| // NOTE: there is some trickery with signed/unsigned values as described in the |
| // Variant.discr_value() getter. If we need to support signed discriminants this block will have |
| // to be updated. |
| uint64_t discr = 0; |
| if (Err err = discr_value.value().PromoteTo64(&discr); err.has_error()) |
| return err; |
| |
| // Check against all variants and also look for the default variant. |
| const Variant* default_var = nullptr; |
| for (const auto& lazy_var : variant_part->variants()) { |
| const Variant* var = lazy_var.Get()->As<Variant>(); |
| if (!var) |
| continue; |
| |
| if (var->discr_value()) { |
| if (*var->discr_value() == discr) { |
| // Found match. |
| *result = RefPtrTo(var); |
| return Err(); |
| } |
| } else { |
| // No discriminant value set on the variant means it's the default one. |
| default_var = var; |
| } |
| } |
| |
| // No match means use the default value if there is one. |
| if (default_var) { |
| *result = RefPtrTo(default_var); |
| return Err(); |
| } |
| |
| return Err("Discriminant value of 0x%" PRIx64 " does not match any of the Variants.", discr); |
| } |
| |
| ErrOr<std::string> GetActiveRustVariantName(const fxl::RefPtr<EvalContext>& context, |
| const ExprValue& value) { |
| ErrOr<FoundMember> found_member = GetSingleActiveDataMember(context, value); |
| if (found_member.has_error()) |
| return found_member.err(); |
| FX_DCHECK(!found_member.value().is_null()); // Should always be valid in non-error cases. |
| |
| // The name of the enum in Rust is the name of the data member. |
| return found_member.value().data_member()->GetAssignedName(); |
| } |
| |
| ErrOrValue ResolveSingleVariantValue(const fxl::RefPtr<EvalContext>& context, |
| const ExprValue& value, |
| fxl::RefPtr<DataMember>* member_to_return) { |
| fxl::RefPtr<Type> concrete = context->GetConcreteType(value.type()); |
| if (!concrete) |
| return Err("Missing type information."); |
| const Collection* collection = concrete->As<Collection>(); |
| if (!collection) |
| return Err("Attempting to extract a variant from a non-collection."); |
| |
| const VariantPart* part = collection->variant_part().Get()->As<VariantPart>(); |
| if (!part) |
| return Err("Missing variant part for variant."); |
| |
| fxl::RefPtr<Variant> variant; |
| if (Err err = ResolveVariant(context, value, collection, part, &variant); err.has_error()) |
| return err; |
| |
| if (variant->data_members().empty()) |
| return ExprValue(); // Allow empty. |
| |
| // Extract the one expected data member. |
| if (variant->data_members().size() > 1) |
| return Err("Expected a single variant data member, got %zu.", variant->data_members().size()); |
| const DataMember* member = variant->data_members()[0].Get()->As<DataMember>(); |
| if (!member) |
| return Err("Invalid data member in variant symbol."); |
| |
| if (member_to_return) |
| *member_to_return = RefPtrTo(member); |
| |
| return ResolveNonstaticMember(context, value, FoundMember(collection, member)); |
| } |
| |
| } // namespace zxdb |