10b2c9d72SSingleAccretion use crate::debug::Reader;
290ac295eSAlex Crichton use crate::debug::transform::utils::resolve_die_ref;
30b2c9d72SSingleAccretion
487c33c29SAlex Crichton use super::address_transform::AddressTransform;
5*c4a38d67SPhilip Craig use super::dbi_log;
690ac295eSAlex Crichton use super::expression::{CompiledExpression, FunctionFrameInfo, compile_expression};
787c33c29SAlex Crichton use super::range_info_builder::RangeInfoBuilder;
80b2c9d72SSingleAccretion use super::unit::InheritedAttr;
987c33c29SAlex Crichton use cranelift_codegen::isa::TargetIsa;
10*c4a38d67SPhilip Craig use gimli::{AttributeValue, UnitOffset, write};
11*c4a38d67SPhilip Craig use wasmtime_environ::error::Error;
1287c33c29SAlex Crichton
1387c33c29SAlex Crichton #[derive(Debug)]
14*c4a38d67SPhilip Craig pub(crate) struct EntryAttributesContext<'a> {
15*c4a38d67SPhilip Craig pub subprograms: &'a mut InheritedAttr<SubprogramContext>,
16*c4a38d67SPhilip Craig pub frame_base: Option<&'a CompiledExpression>,
1787c33c29SAlex Crichton }
1887c33c29SAlex Crichton
190b2c9d72SSingleAccretion #[derive(Debug)]
200b2c9d72SSingleAccretion pub struct SubprogramContext {
210b2c9d72SSingleAccretion pub obj_ptr: UnitOffset,
220b2c9d72SSingleAccretion pub param_num: isize,
230b2c9d72SSingleAccretion }
240b2c9d72SSingleAccretion
is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool2587c33c29SAlex Crichton fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool {
2687c33c29SAlex Crichton match attr_name {
2787c33c29SAlex Crichton gimli::DW_AT_location
2887c33c29SAlex Crichton | gimli::DW_AT_string_length
2987c33c29SAlex Crichton | gimli::DW_AT_return_addr
3087c33c29SAlex Crichton | gimli::DW_AT_data_member_location
3187c33c29SAlex Crichton | gimli::DW_AT_frame_base
3287c33c29SAlex Crichton | gimli::DW_AT_segment
3387c33c29SAlex Crichton | gimli::DW_AT_static_link
3487c33c29SAlex Crichton | gimli::DW_AT_use_location
3587c33c29SAlex Crichton | gimli::DW_AT_vtable_elem_location => true,
3687c33c29SAlex Crichton _ => false,
3787c33c29SAlex Crichton }
3887c33c29SAlex Crichton }
3987c33c29SAlex Crichton
clone_die_attributes<'a>( convert_unit: &mut gimli::write::ConvertUnit<Reader<'a>>, entry: &gimli::write::ConvertUnitEntry<Reader<'a>>, addr_tr: &AddressTransform, frame_info: Option<&FunctionFrameInfo>, out_entry_id: write::UnitEntryId, subprogram_range_builder: Option<RangeInfoBuilder>, scope_ranges: Option<&Vec<(u64, u64)>>, mut attr_context: EntryAttributesContext, isa: &dyn TargetIsa, ) -> Result<(), Error>400b2c9d72SSingleAccretion pub(crate) fn clone_die_attributes<'a>(
41*c4a38d67SPhilip Craig convert_unit: &mut gimli::write::ConvertUnit<Reader<'a>>,
42*c4a38d67SPhilip Craig entry: &gimli::write::ConvertUnitEntry<Reader<'a>>,
43bd7b59daSPhilip Craig addr_tr: &AddressTransform,
4487c33c29SAlex Crichton frame_info: Option<&FunctionFrameInfo>,
450b2c9d72SSingleAccretion out_entry_id: write::UnitEntryId,
4687c33c29SAlex Crichton subprogram_range_builder: Option<RangeInfoBuilder>,
4787c33c29SAlex Crichton scope_ranges: Option<&Vec<(u64, u64)>>,
48*c4a38d67SPhilip Craig mut attr_context: EntryAttributesContext,
4987c33c29SAlex Crichton isa: &dyn TargetIsa,
500b2c9d72SSingleAccretion ) -> Result<(), Error> {
51*c4a38d67SPhilip Craig let unit = entry.read_unit;
5287c33c29SAlex Crichton let unit_encoding = unit.encoding();
5387c33c29SAlex Crichton
5487c33c29SAlex Crichton let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder {
5587c33c29SAlex Crichton subprogram_range_builder
5687c33c29SAlex Crichton } else {
5787c33c29SAlex Crichton // FIXME for CU: currently address_transform operate on a single
5887c33c29SAlex Crichton // function range, and when CU spans multiple ranges the
5987c33c29SAlex Crichton // transformation may be incomplete.
60*c4a38d67SPhilip Craig RangeInfoBuilder::from(entry)?
6187c33c29SAlex Crichton };
62*c4a38d67SPhilip Craig range_info.build(addr_tr, convert_unit.unit, out_entry_id);
630b2c9d72SSingleAccretion
640b2c9d72SSingleAccretion let mut is_obj_ptr = false;
65*c4a38d67SPhilip Craig prepare_die_context(entry, &mut attr_context, &mut is_obj_ptr)?;
6687c33c29SAlex Crichton
67*c4a38d67SPhilip Craig for attr in &entry.attrs {
687e15d87bSPhilip Craig match attr.name() {
697e15d87bSPhilip Craig gimli::DW_AT_low_pc | gimli::DW_AT_high_pc | gimli::DW_AT_ranges => {
707e15d87bSPhilip Craig // Handled by RangeInfoBuilder.
7187c33c29SAlex Crichton continue;
7287c33c29SAlex Crichton }
730b2c9d72SSingleAccretion gimli::DW_AT_object_pointer => {
740b2c9d72SSingleAccretion // Our consumers cannot handle 'this' typed as a non-pointer (recall
750b2c9d72SSingleAccretion // we translate all pointers to wrapper types), making it unusable.
760b2c9d72SSingleAccretion // To remedy this, we 'strip' instance-ness off of methods by removing
770b2c9d72SSingleAccretion // DW_AT_object_pointer and renaming 'this' to '__this'.
78*c4a38d67SPhilip Craig if let Some(ref mut subprogram) =
79*c4a38d67SPhilip Craig attr_context.subprograms.top_with_depth_mut(entry.depth)
800b2c9d72SSingleAccretion {
810b2c9d72SSingleAccretion // We expect this to reference a child entry in the same unit.
820b2c9d72SSingleAccretion if let Some(unit_offs) = match attr.value() {
83*c4a38d67SPhilip Craig AttributeValue::DebugInfoRef(di_ref) => di_ref.to_unit_offset(&unit.header),
840b2c9d72SSingleAccretion AttributeValue::UnitRef(unit_ref) => Some(unit_ref),
850b2c9d72SSingleAccretion _ => None,
860b2c9d72SSingleAccretion } {
870b2c9d72SSingleAccretion subprogram.obj_ptr = unit_offs;
880b2c9d72SSingleAccretion dbi_log!("Stripped DW_AT_object_pointer");
890b2c9d72SSingleAccretion continue;
900b2c9d72SSingleAccretion }
910b2c9d72SSingleAccretion }
920b2c9d72SSingleAccretion }
937e15d87bSPhilip Craig _ => {}
9487c33c29SAlex Crichton }
950b2c9d72SSingleAccretion
960b2c9d72SSingleAccretion if is_obj_ptr {
970b2c9d72SSingleAccretion match attr.name() {
980b2c9d72SSingleAccretion gimli::DW_AT_artificial => {
990b2c9d72SSingleAccretion dbi_log!("Object pointer: stripped DW_AT_artificial");
1000b2c9d72SSingleAccretion continue;
1010b2c9d72SSingleAccretion }
1020b2c9d72SSingleAccretion gimli::DW_AT_name => {
103bd7b59daSPhilip Craig let old_name: &str = &unit.attr_string(attr.value())?.to_string_lossy();
1040b2c9d72SSingleAccretion let new_name = format!("__{old_name}");
1050b2c9d72SSingleAccretion dbi_log!(
1060b2c9d72SSingleAccretion "Object pointer: renamed '{}' -> '{}'",
1070b2c9d72SSingleAccretion old_name,
1080b2c9d72SSingleAccretion new_name.as_str()
1090b2c9d72SSingleAccretion );
1100b2c9d72SSingleAccretion
111*c4a38d67SPhilip Craig let attr_value =
112*c4a38d67SPhilip Craig write::AttributeValue::StringRef(convert_unit.strings.add(new_name));
113*c4a38d67SPhilip Craig convert_unit
114*c4a38d67SPhilip Craig .unit
1150b2c9d72SSingleAccretion .get_mut(out_entry_id)
1160b2c9d72SSingleAccretion .set(gimli::DW_AT_name, attr_value);
1170b2c9d72SSingleAccretion continue;
1180b2c9d72SSingleAccretion }
1190b2c9d72SSingleAccretion _ => {}
1200b2c9d72SSingleAccretion }
1210b2c9d72SSingleAccretion }
1220b2c9d72SSingleAccretion
1237e15d87bSPhilip Craig let attr_value = attr.value();
1247e15d87bSPhilip Craig let out_attr_value = match attr_value {
12587c33c29SAlex Crichton AttributeValue::Addr(u) => {
12687c33c29SAlex Crichton let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
12787c33c29SAlex Crichton write::AttributeValue::Address(addr)
12887c33c29SAlex Crichton }
12987c33c29SAlex Crichton AttributeValue::DebugAddrIndex(i) => {
130bd7b59daSPhilip Craig let u = unit.address(i)?;
13187c33c29SAlex Crichton let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0));
13287c33c29SAlex Crichton write::AttributeValue::Address(addr)
13387c33c29SAlex Crichton }
1347e15d87bSPhilip Craig AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => {
135bd7b59daSPhilip Craig let r = unit.attr_ranges_offset(attr_value)?.unwrap();
136bd7b59daSPhilip Craig let range_info = RangeInfoBuilder::from_ranges_ref(unit, r)?;
137*c4a38d67SPhilip Craig let range_list_id = range_info.build_ranges(addr_tr, &mut convert_unit.unit.ranges);
13887c33c29SAlex Crichton write::AttributeValue::RangeListRef(range_list_id)
13987c33c29SAlex Crichton }
1407e15d87bSPhilip Craig AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => {
141*c4a38d67SPhilip Craig let mut locs = unit.attr_locations(attr_value)?.unwrap();
142*c4a38d67SPhilip Craig let frame_base = attr_context.frame_base;
14387c33c29SAlex Crichton
14487c33c29SAlex Crichton let mut result: Option<Vec<_>> = None;
14587c33c29SAlex Crichton while let Some(loc) = locs.next()? {
14687c33c29SAlex Crichton if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? {
14787c33c29SAlex Crichton let chunk = expr
14887c33c29SAlex Crichton .build_with_locals(
14987c33c29SAlex Crichton &[(loc.range.begin, loc.range.end)],
15087c33c29SAlex Crichton addr_tr,
15187c33c29SAlex Crichton frame_info,
15287c33c29SAlex Crichton isa,
15387c33c29SAlex Crichton )
154d2c9d2a2SSingleAccretion .expressions
15587c33c29SAlex Crichton .filter(|i| {
15687c33c29SAlex Crichton // Ignore empty range
15790ac295eSAlex Crichton if let Ok((_, 0, _)) = i { false } else { true }
15887c33c29SAlex Crichton })
15987c33c29SAlex Crichton .map(|i| {
16087c33c29SAlex Crichton i.map(|(start, len, expr)| write::Location::StartLength {
16187c33c29SAlex Crichton begin: start,
16287c33c29SAlex Crichton length: len,
16387c33c29SAlex Crichton data: expr,
16487c33c29SAlex Crichton })
16587c33c29SAlex Crichton })
16687c33c29SAlex Crichton .collect::<Result<Vec<_>, _>>()?;
16787c33c29SAlex Crichton match &mut result {
16887c33c29SAlex Crichton Some(r) => r.extend(chunk),
16987c33c29SAlex Crichton x @ None => *x = Some(chunk),
17087c33c29SAlex Crichton }
17187c33c29SAlex Crichton } else {
17287c33c29SAlex Crichton // FIXME _expr contains invalid expression
17387c33c29SAlex Crichton continue; // ignore entry
17487c33c29SAlex Crichton }
17587c33c29SAlex Crichton }
17687c33c29SAlex Crichton if result.is_none() {
17787c33c29SAlex Crichton continue; // no valid locations
17887c33c29SAlex Crichton }
179*c4a38d67SPhilip Craig let list_id = convert_unit
180*c4a38d67SPhilip Craig .unit
181*c4a38d67SPhilip Craig .locations
182*c4a38d67SPhilip Craig .add(write::LocationList(result.unwrap()));
18387c33c29SAlex Crichton write::AttributeValue::LocationListRef(list_id)
18487c33c29SAlex Crichton }
1857b16eccdSSingleAccretion AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
1867b16eccdSSingleAccretion // We do not really "rewrite" the frame base so much as replace it outright.
1877b16eccdSSingleAccretion // References to it through the DW_OP_fbreg opcode will be expanded below.
1887b16eccdSSingleAccretion let mut cfa = write::Expression::new();
1897b16eccdSSingleAccretion cfa.op(gimli::DW_OP_call_frame_cfa);
1907b16eccdSSingleAccretion write::AttributeValue::Exprloc(cfa)
1917b16eccdSSingleAccretion }
19287c33c29SAlex Crichton AttributeValue::Exprloc(ref expr) => {
193*c4a38d67SPhilip Craig let frame_base = attr_context.frame_base;
19487c33c29SAlex Crichton if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? {
19587c33c29SAlex Crichton if expr.is_simple() {
19687c33c29SAlex Crichton if let Some(expr) = expr.build() {
19787c33c29SAlex Crichton write::AttributeValue::Exprloc(expr)
19887c33c29SAlex Crichton } else {
19987c33c29SAlex Crichton continue;
20087c33c29SAlex Crichton }
20187c33c29SAlex Crichton } else {
20287c33c29SAlex Crichton // Conversion to loclist is required.
20387c33c29SAlex Crichton if let Some(scope_ranges) = scope_ranges {
204d2c9d2a2SSingleAccretion let built_expression =
205d2c9d2a2SSingleAccretion expr.build_with_locals(scope_ranges, addr_tr, frame_info, isa);
206d2c9d2a2SSingleAccretion let exprs = built_expression
207d2c9d2a2SSingleAccretion .expressions
20887c33c29SAlex Crichton .collect::<Result<Vec<_>, _>>()?;
20987c33c29SAlex Crichton if exprs.is_empty() {
21087c33c29SAlex Crichton continue;
21187c33c29SAlex Crichton }
21287c33c29SAlex Crichton // Micro-optimization all expressions alike, use one exprloc.
213d2c9d2a2SSingleAccretion let mut single_expr: Option<write::Expression> = None;
214d2c9d2a2SSingleAccretion if built_expression.covers_entire_scope {
21587c33c29SAlex Crichton for (_, _, expr) in &exprs {
216d2c9d2a2SSingleAccretion if let Some(ref prev_expr) = single_expr {
21787c33c29SAlex Crichton if expr == prev_expr {
21887c33c29SAlex Crichton continue; // the same expression
21987c33c29SAlex Crichton }
220d2c9d2a2SSingleAccretion single_expr = None;
22187c33c29SAlex Crichton break;
22287c33c29SAlex Crichton }
223d2c9d2a2SSingleAccretion single_expr = Some(expr.clone())
22487c33c29SAlex Crichton }
225d2c9d2a2SSingleAccretion }
226d2c9d2a2SSingleAccretion if let Some(expr) = single_expr {
22787c33c29SAlex Crichton write::AttributeValue::Exprloc(expr)
22887c33c29SAlex Crichton } else if is_exprloc_to_loclist_allowed(attr.name()) {
22987c33c29SAlex Crichton // Converting exprloc to loclist.
23087c33c29SAlex Crichton let mut locs = Vec::new();
23187c33c29SAlex Crichton for (begin, length, data) in exprs {
23287c33c29SAlex Crichton if length == 0 {
23387c33c29SAlex Crichton // Ignore empty range
23487c33c29SAlex Crichton continue;
23587c33c29SAlex Crichton }
23687c33c29SAlex Crichton locs.push(write::Location::StartLength {
23787c33c29SAlex Crichton begin,
23887c33c29SAlex Crichton length,
23987c33c29SAlex Crichton data,
24087c33c29SAlex Crichton });
24187c33c29SAlex Crichton }
242*c4a38d67SPhilip Craig let list_id =
243*c4a38d67SPhilip Craig convert_unit.unit.locations.add(write::LocationList(locs));
24487c33c29SAlex Crichton write::AttributeValue::LocationListRef(list_id)
24587c33c29SAlex Crichton } else {
24687c33c29SAlex Crichton continue;
24787c33c29SAlex Crichton }
24887c33c29SAlex Crichton } else {
24987c33c29SAlex Crichton continue;
25087c33c29SAlex Crichton }
25187c33c29SAlex Crichton }
25287c33c29SAlex Crichton } else {
25387c33c29SAlex Crichton // FIXME _expr contains invalid expression
25487c33c29SAlex Crichton continue; // ignore attribute
25587c33c29SAlex Crichton }
25687c33c29SAlex Crichton }
257*c4a38d67SPhilip Craig // No other attributes contain addresses or address offsets.
258*c4a38d67SPhilip Craig _ => match convert_unit.convert_attribute_value(unit, attr, &|_| None) {
259*c4a38d67SPhilip Craig Ok(value) => value,
260*c4a38d67SPhilip Craig Err(e) => {
261*c4a38d67SPhilip Craig // Invalid `FileIndex` was seen in #8884 and #8904. In general it's
262*c4a38d67SPhilip Craig // better to ignore invalid or unknown DWARF rather then failing outright.
263*c4a38d67SPhilip Craig dbi_log!(
264*c4a38d67SPhilip Craig "Ignoring entry {:x?} attribute {} = {:x?}: {e}",
265*c4a38d67SPhilip Craig entry.offset.to_unit_section_offset(&convert_unit.read_unit),
266*c4a38d67SPhilip Craig attr.name(),
267*c4a38d67SPhilip Craig attr_value,
268*c4a38d67SPhilip Craig );
26987c33c29SAlex Crichton continue;
27087c33c29SAlex Crichton }
271*c4a38d67SPhilip Craig },
27287c33c29SAlex Crichton };
273*c4a38d67SPhilip Craig let out_entry = convert_unit.unit.get_mut(out_entry_id);
2740b2c9d72SSingleAccretion out_entry.set(attr.name(), out_attr_value);
27587c33c29SAlex Crichton }
27687c33c29SAlex Crichton Ok(())
27787c33c29SAlex Crichton }
2780b2c9d72SSingleAccretion
prepare_die_context<'a>( entry: &gimli::write::ConvertUnitEntry<Reader<'a>>, attr_context: &mut EntryAttributesContext, is_obj_ptr: &mut bool, ) -> Result<(), Error>279bd7b59daSPhilip Craig fn prepare_die_context<'a>(
280*c4a38d67SPhilip Craig entry: &gimli::write::ConvertUnitEntry<Reader<'a>>,
281*c4a38d67SPhilip Craig attr_context: &mut EntryAttributesContext,
2820b2c9d72SSingleAccretion is_obj_ptr: &mut bool,
2830b2c9d72SSingleAccretion ) -> Result<(), Error> {
284*c4a38d67SPhilip Craig let subprograms = &mut attr_context.subprograms;
2850b2c9d72SSingleAccretion
2860b2c9d72SSingleAccretion // Update the current context based on what kind of entry this is.
287*c4a38d67SPhilip Craig match entry.tag {
2880b2c9d72SSingleAccretion gimli::DW_TAG_subprogram | gimli::DW_TAG_inlined_subroutine | gimli::DW_TAG_entry_point => {
2890b2c9d72SSingleAccretion // Push the 'context' of there being no parameters (yet).
2900b2c9d72SSingleAccretion subprograms.push(
291*c4a38d67SPhilip Craig entry.depth,
2920b2c9d72SSingleAccretion SubprogramContext {
2930b2c9d72SSingleAccretion obj_ptr: UnitOffset { 0: 0 },
2940b2c9d72SSingleAccretion param_num: -1,
2950b2c9d72SSingleAccretion },
2960b2c9d72SSingleAccretion );
2970b2c9d72SSingleAccretion }
2980b2c9d72SSingleAccretion gimli::DW_TAG_formal_parameter => {
2990b2c9d72SSingleAccretion // Formal parameter tags can be parented by catch blocks
3000b2c9d72SSingleAccretion // and such - not just subprogram DIEs. So we need to check
3010b2c9d72SSingleAccretion // that this DIE is indeed a direct child of a subprogram.
302*c4a38d67SPhilip Craig if let Some(subprogram) = subprograms.top_with_depth_mut(entry.depth - 1) {
3030b2c9d72SSingleAccretion subprogram.param_num += 1;
3040b2c9d72SSingleAccretion
305*c4a38d67SPhilip Craig if subprogram.obj_ptr == entry.offset
306*c4a38d67SPhilip Craig || is_obj_ptr_param(entry, subprogram.param_num)?
3070b2c9d72SSingleAccretion {
3080b2c9d72SSingleAccretion *is_obj_ptr = true;
3090b2c9d72SSingleAccretion }
3100b2c9d72SSingleAccretion }
3110b2c9d72SSingleAccretion }
3120b2c9d72SSingleAccretion _ => {}
3130b2c9d72SSingleAccretion }
3140b2c9d72SSingleAccretion Ok(())
3150b2c9d72SSingleAccretion }
3160b2c9d72SSingleAccretion
is_obj_ptr_param( entry: &gimli::write::ConvertUnitEntry<Reader<'_>>, param_num: isize, ) -> Result<bool, Error>317*c4a38d67SPhilip Craig fn is_obj_ptr_param(
318*c4a38d67SPhilip Craig entry: &gimli::write::ConvertUnitEntry<Reader<'_>>,
3190b2c9d72SSingleAccretion param_num: isize,
3200b2c9d72SSingleAccretion ) -> Result<bool, Error> {
321*c4a38d67SPhilip Craig debug_assert_eq!(entry.tag, gimli::DW_TAG_formal_parameter);
322*c4a38d67SPhilip Craig let unit = entry.read_unit;
3230b2c9d72SSingleAccretion
3240b2c9d72SSingleAccretion // This logic was taken loosely from LLDB. It is known
3250b2c9d72SSingleAccretion // that it is not fully correct (doesn't handle 'deduced
3260b2c9d72SSingleAccretion // this', for example).
3270b2c9d72SSingleAccretion // Q: DWARF includes DW_AT_object_pointer as we use it,
3280b2c9d72SSingleAccretion // why do we need this heuristic as well?
3290b2c9d72SSingleAccretion // A: Declarations do not include DW_AT_object_pointer.
3300b2c9d72SSingleAccretion if param_num == 0
3317ee620a5SPhilip Craig && entry.attr_value(gimli::DW_AT_artificial) == Some(AttributeValue::Flag(true))
3320b2c9d72SSingleAccretion {
3330b2c9d72SSingleAccretion // Either this has no name (declarations omit them), or its explicitly "this".
3347ee620a5SPhilip Craig let name = entry.attr_value(gimli::DW_AT_name);
335bd7b59daSPhilip Craig if name.is_none() || unit.attr_string(name.unwrap())?.slice().eq(b"this") {
3360b2c9d72SSingleAccretion // Finally, a type check. We expect a pointer.
3377ee620a5SPhilip Craig if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type) {
3380b2c9d72SSingleAccretion if let Some(type_die) = resolve_die_ref(unit, &type_attr)? {
339*c4a38d67SPhilip Craig return Ok(type_die.tag == gimli::DW_TAG_pointer_type);
3400b2c9d72SSingleAccretion }
3410b2c9d72SSingleAccretion }
3420b2c9d72SSingleAccretion }
3430b2c9d72SSingleAccretion };
3440b2c9d72SSingleAccretion
3450b2c9d72SSingleAccretion return Ok(false);
3460b2c9d72SSingleAccretion }
347