SBData неверен, когда SBValue происходит из Swift Dictionary - PullRequest
0 голосов
/ 11 октября 2018

Я пытаюсь написать функцию Python для форматирования Foundation.Decimal, для использования в качестве сумматора типов.Я отправил это в этот ответ .Я также включу его в конец этого ответа с дополнительными отладочными отпечатками.

Я сейчас обнаружил ошибку, но я не знаю, есть ли ошибка в моей функции или в lldb,или, возможно, в компиляторе Swift.

Вот расшифровка, которая демонстрирует ошибку.Я загружаю свой сумматор типов в ~/.lldbinit, поэтому Swift REPL использует его.

:; xcrun swift
registering Decimal type summaries
Welcome to Apple Swift version 4.2 (swiftlang-1000.11.37.1 clang-1000.11.45.1). Type :help for assistance.
  1> import Foundation
  2> let dec: Decimal = 7
dec: Decimal = 7

Выше 7 в выходных данных отладчика взят из моего сумматора типов и является правильным.

  3> var dict = [String: Decimal]()
dict: [String : Decimal] = 0 key/value pairs
  4> dict["x"] = dec
  5> dict["x"]
$R0: Decimal? = 7

Выше, 7 снова от моего сумматора типов и является правильным.

  6> dict
$R1: [String : Decimal] = 1 key/value pair {
  [0] = {
    key = "x"
    value = 0
  }
}

Выше 0value = 0) от моего сумматора типов, и это неправильно .Это должно быть 7.

Так почему же это ноль?Моя функция Python получает SBValue.Он вызывает GetData() на SBValue, чтобы получить SBData.Я добавил отладочную печать в функцию для печати байтов в SBData, а также для печати результата sbValue.GetLoadAddress().Вот расшифровка с этими отладочными отпечатками:

:; xcrun swift
registering Decimal type summaries
Welcome to Apple Swift version 4.2 (swiftlang-1000.11.37.1 clang-1000.11.45.1). Type :help for assistance.
  1> import Foundation
  2> let dec: Decimal = 7
dec: Decimal =    loadAddress: ffffffffffffffff
    data: 00 21 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 7

Выше мы можем видеть, что адрес загрузки поддельный, но байты SBData правильные (байт 1, 21, содержит длинуи флаги; байт 4, '07', является первым байтом значенияand).

  3> var dict = [String: Decimal]()
dict: [String : Decimal] = 0 key/value pairs
  4> dict["x"] = dec
  5> dict
$R0: [String : Decimal] = 1 key/value pair {
  [0] = {
    key = "x"
    value =    loadAddress: ffffffffffffffff
    data: 00 00 00 00 00 21 00 00 07 00 00 00 00 00 00 00 00 00 00 00
 0
  }
}

Выше мы можем видеть, что адрес загрузки по-прежнему поддельный, и теперь байты SBDataневерны.SBData по-прежнему содержит 20 байтов (правильное число для Foundation.Decimal, иначе NSDecimal), но теперь четыре 00 байта были вставлены спереди, а последние четыре байта были отброшены.

Итак, вот мои конкретные вопросы:

  1. Неправильно ли я использую API lldb и, таким образом, получаю неправильные ответы?Если да, что я делаю не так и как мне это исправить?

  2. Если я правильно использую lldb API, то это ошибка в lldb или компилятор Swiftиспускает неверные метаданные?Как я могу выяснить, какой инструмент имеет ошибку?(Потому что, если это ошибка в одном из инструментов, я бы хотел подать отчет об ошибке.)

  3. Если это ошибка в lldb или Swift, как я могу обойтипроблема, поэтому я могу правильно отформатировать Decimal, когда он является частью Dictionary?


Вот мой форматировщик типов с отладочными отпечатками:

# Decimal / NSDecimal support for lldb
#
# Put this file somewhere, e.g. ~/.../lldb/Decimal.py
# Then add this line to ~/.lldbinit:
#     command script import ~/.../lldb/Decimal.py

import lldb

def stringForDecimal(sbValue, internal_dict):
    from decimal import Decimal, getcontext

    print('    loadAddress: %x' % sbValue.GetLoadAddress())

    sbData = sbValue.GetData()
    if not sbData.IsValid():
        raise Exception('unable to get data: ' + sbError.GetCString())
    if sbData.GetByteSize() != 20:
        raise Exception('expected data to be 20 bytes but found ' + repr(sbData.GetByteSize()))

    sbError = lldb.SBError()
    exponent = sbData.GetSignedInt8(sbError, 0)
    if sbError.Fail():
        raise Exception('unable to read exponent byte: ' + sbError.GetCString())

    flags = sbData.GetUnsignedInt8(sbError, 1)
    if sbError.Fail():
        raise Exception('unable to read flags byte: ' + sbError.GetCString())
    length = flags & 0xf
    isNegative = (flags & 0x10) != 0

    debugString = ''
    for i in range(20):
        debugString += ' %02x' % sbData.GetUnsignedInt8(sbError, i)
    print('    data:' + debugString)

    if length == 0 and isNegative:
        return 'NaN'

    if length == 0:
        return '0'

    getcontext().prec = 200
    value = Decimal(0)
    scale = Decimal(1)
    for i in range(length):
        digit = sbData.GetUnsignedInt16(sbError, 4 + 2 * i)
        if sbError.Fail():
            raise Exception('unable to read memory: ' + sbError.GetCString())
        value += scale * Decimal(digit)
        scale *= 65536

    value = value.scaleb(exponent)
    if isNegative:
        value = -value

    return str(value)

def __lldb_init_module(debugger, internal_dict):
    print('registering Decimal type summaries')
    debugger.HandleCommand('type summary add Foundation.Decimal -F "' + __name__ + '.stringForDecimal"')
    debugger.HandleCommand('type summary add NSDecimal -F "' + __name__ + '.stringForDecimal"')

1 Ответ

0 голосов
/ 13 октября 2018

Это похоже на ошибку lldb.Пожалуйста, отправьте сообщение об ошибке в lldb с помощью http://bugs.swift.org.

Для справки: в вашем Словаре происходит волшебство за вашей спиной.Я не могу показать это в REPL, но если у вас есть массив [String: Decimal] в качестве локальной переменной в каком-то реальном коде и вы делаете:

(lldb) frame variable --raw dec_array
(Swift.Dictionary<Swift.String, Foundation.Decimal>) dec_array = {
  _variantBuffer = native {
    native = {
      _storage = 0x0000000100d05780 {
        Swift._SwiftNativeNSDictionary = {}
        bucketCount = {
          _value = 2
        }
        count = {
          _value = 1
        }
        initializedEntries = {
          values = {
            _rawValue = 0x0000000100d057d0
          }
          bitCount = {
            _value = 2
          }
        }
        keys = {
          _rawValue = 0x0000000100d057d8
        }
        values = {
          _rawValue = 0x0000000100d057f8
        }
        seed = {
          0 = {
            _value = -5794706384231184310
          }
          1 = {
            _value = 8361200869849021207
          }
        }
      }
    }
    cocoa = {
      cocoaDictionary = 0x00000001000021b0
    }
  }
}

Быстрый словарь на самом деле не содержитэлементы словаря везде очевидны, и, конечно, не так, как ивары.Таким образом, у lldb есть «Синтетический дочерний поставщик» для быстрых словарей, который составляет SBValues ​​для ключей и значений словаря, и это один из тех синтетических потомков, который передается вашему форматеру.

Также адрес загрузки равен -1.Это действительно означает, что «это искусственная вещь, которой напрямую управляет lldb, а не вещь по адресу где-то в вашей программе».То же самое относится и к результатам REPL, они больше являются фикцией, которую поддерживает lldb.Но если бы вы посмотрели на локальную переменную типа Decimal, вы бы увидели действительный адрес загрузки, потому что это вещь, которая живет где-то в памяти.чтобы представлять значения словаря, не устанавливайте начало данных правильно.Интересно, что если вы создаете словарь [Decimal: String], SBData ключевого поля является правильным, и ваш форматтер работает.Это просто неправильные значения.

Я пытался сделать то же самое со словарями, в которых значениями являются Strings, и SBData там выглядит правильно.Так что в Decimal есть что-то смешное.В любом случае, спасибо за это, и, пожалуйста, сообщите об ошибке.

...