Esempio n. 1
0
def nthSiblingOfView(view, n):
  subviews = subviewsOfView(superviewOfView(view))
  numViews = fb.evaluateIntegerExpression('[(id)' + subviews + ' count]')

  idx = fb.evaluateIntegerExpression('[(id)' + subviews + ' indexOfObject:' + view + ']')

  newIdx = idx + n
  while newIdx < 0:
    newIdx += numViews
  newIdx = newIdx % numViews

  return fb.evaluateObjectExpression('[(id)' + subviews + ' objectAtIndex:' + str(newIdx) + ']')
Esempio n. 2
0
def nthSiblingOfView(view, n):
    subviews = subviewsOfView(superviewOfView(view))
    numViews = fb.evaluateIntegerExpression('[(id)' + subviews + ' count]')

    idx = fb.evaluateIntegerExpression('[(id)' + subviews + ' indexOfObject:' + view + ']')

    newIdx = idx + n
    while newIdx < 0:
        newIdx += numViews
    newIdx = newIdx % numViews

    return fb.evaluateObjectExpression('[(id)' + subviews + ' objectAtIndex:' + str(newIdx) + ']')
Esempio n. 3
0
def nthSiblingOfView(view, n):
    subviews = subviewsOfView(superviewOfView(view))
    numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]")

    idx = fb.evaluateIntegerExpression("[(id)" + subviews + " indexOfObject:" +
                                       view + "]")

    newIdx = idx + n
    while newIdx < 0:
        newIdx += numViews
    newIdx = newIdx % numViews

    return fb.evaluateObjectExpression("[(id)" + subviews + " objectAtIndex:" +
                                       str(newIdx) + "]")
def _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
        vc, string, prefix, childPrefix):
    s = '%s%s%s\n' % (prefix, '' if prefix == '' else ' ',
                      _viewControllerDescription(vc))

    nextPrefix = childPrefix + '   |'

    numChildViewControllers = fb.evaluateIntegerExpression(
        '(int)[(id)[%s childViewControllers] count]' % (vc))
    childViewControllers = fb.evaluateExpression(
        '(id)[%s childViewControllers]' % (vc))

    for i in range(0, numChildViewControllers):
        viewController = fb.evaluateExpression(
            '(id)[(id)[%s childViewControllers] objectAtIndex:%d]' % (vc, i))
        s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
            viewController, string, nextPrefix, nextPrefix)

    isModal = fb.evaluateBooleanExpression(
        '%s != nil && ((id)[(id)[(id)%s presentedViewController] presentingViewController]) == %s'
        % (vc, vc, vc))

    if isModal:
        modalVC = fb.evaluateObjectExpression(
            '(id)[(id)%s presentedViewController]' % (vc))
        s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
            modalVC, string, childPrefix + '  *M', nextPrefix)
        s += '\n// \'*M\' means the view controller is presented modally.'

    return string + s
Esempio n. 5
0
    def accessibilityGrepHierarchy(self, view, needle):
        a11yLabel = accessibilityLabel(view)
        #if we don't have any accessibility string - we should have some children
        if int(a11yLabel.GetValue(), 16) == 0:
            #We call private method that gives back all visible accessibility children for view
            accessibilityElements = fb.evaluateObjectExpression(
                '[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]'
                % view)
            accessibilityElementsCount = fb.evaluateIntegerExpression(
                '[%s count]' % accessibilityElements)
            for index in range(0, accessibilityElementsCount):
                subview = fb.evaluateObjectExpression(
                    '[%s objectAtIndex:%i]' % (accessibilityElements, index))
                self.accessibilityGrepHierarchy(subview, needle)
        elif re.match(r'.*' + needle + '.*', a11yLabel.GetObjectDescription(),
                      re.IGNORECASE):
            classDesc = objHelpers.className(view)
            print('({} {}) {}'.format(classDesc, view,
                                      a11yLabel.GetObjectDescription()))

            #First element that is found is copied to clipboard
            if not self.foundElement:
                self.foundElement = True
                cmd = 'echo %s | tr -d "\n" | pbcopy' % view
                os.system(cmd)
Esempio n. 6
0
    def accessibilityGrepHierarchy(self, view, needle):
        a11yLabel = accessibilityLabel(view)
        # if we don't have any accessibility string - we should have some children
        if int(a11yLabel.GetValue(), 16) == 0:
            # We call private method that gives back all visible accessibility children
            # for view iOS 10 and higher
            if fb.evaluateBooleanExpression(
                    "[UIView respondsToSelector:@selector(_accessibilityElementsAndContainersDescendingFromViews:options:sorted:)]"  # noqa B950
            ):
                accessibilityElements = fb.evaluateObjectExpression(
                    "[UIView _accessibilityElementsAndContainersDescendingFromViews:@[(id)%s] options:0 sorted:NO]"  # noqa B950
                    % view)
            else:
                accessibilityElements = fb.evaluateObjectExpression(
                    "[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]"  # noqa B950
                    % view)
            accessibilityElementsCount = fb.evaluateIntegerExpression(
                "[%s count]" % accessibilityElements)
            for index in range(0, accessibilityElementsCount):
                subview = fb.evaluateObjectExpression(
                    "[%s objectAtIndex:%i]" % (accessibilityElements, index))
                self.accessibilityGrepHierarchy(subview, needle)
        elif re.match(r".*" + needle + ".*", a11yLabel.GetObjectDescription(),
                      re.IGNORECASE):
            classDesc = objHelpers.className(view)
            print("({} {}) {}".format(classDesc, view,
                                      a11yLabel.GetObjectDescription()))

            # First element that is found is copied to clipboard
            if not self.foundElement:
                self.foundElement = True
                cmd = 'echo %s | tr -d "\n" | pbcopy' % view
                os.system(cmd)
Esempio n. 7
0
    def run(self, arguments, options):
        commandForObject, ivarName = arguments

        objectAddress = int(fb.evaluateObjectExpression(commandForObject), 0)

        ivarOffsetCommand = '(ptrdiff_t)ivar_getOffset((Ivar)object_getInstanceVariable((id){}, "{}", 0))'.format(
            objectAddress, ivarName)
        ivarOffset = fb.evaluateIntegerExpression(ivarOffsetCommand)

        # A multi-statement command allows for variables scoped to the command, not permanent in the session like $variables.
        ivarSizeCommand = (
            'unsigned int size = 0;'
            'char *typeEncoding = (char *)ivar_getTypeEncoding((Ivar)class_getInstanceVariable((Class)object_getClass((id){}), "{}"));'
            '(char *)NSGetSizeAndAlignment(typeEncoding, &size, 0);'
            'size').format(objectAddress, ivarName)
        ivarSize = int(fb.evaluateExpression(ivarSizeCommand), 0)

        error = lldb.SBError()
        watchpoint = lldb.debugger.GetSelectedTarget().WatchAddress(
            objectAddress + ivarOffset, ivarSize, False, True, error)

        if error.Success():
            print 'Remember to delete the watchpoint using: watchpoint delete {}'.format(
                watchpoint.GetID())
        else:
            print 'Could not create the watchpoint: {}'.format(
                error.GetCString())
Esempio n. 8
0
    def run(self, arguments, options):
        control = fb.evaluateInputExpression(arguments[0])
        targets = fb.evaluateObjectExpression(
            "[[{control} allTargets] allObjects]".format(control=control)
        )
        targetCount = fb.evaluateIntegerExpression(
            "[{targets} count]".format(targets=targets)
        )

        for index in range(0, targetCount):
            target = fb.evaluateObjectExpression(
                "[{targets} objectAtIndex:{index}]".format(targets=targets, index=index)
            )
            actions = fb.evaluateObjectExpression(
                "[{control} actionsForTarget:{target} forControlEvent:0]".format(
                    control=control, target=target
                )
            )

            targetDescription = fb.evaluateExpressionValue(
                "(id){target}".format(target=target)
            ).GetObjectDescription()
            actionsDescription = fb.evaluateExpressionValue(
                '(id)[{actions} componentsJoinedByString:@", "]'.format(actions=actions)
            ).GetObjectDescription()

            print(
                "{target}: {actions}".format(
                    target=targetDescription, actions=actionsDescription
                )
            )
Esempio n. 9
0
def _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
        vc, string, prefix, childPrefix):
    isMac = runtimeHelpers.isMacintoshArch()

    s = "%s%s%s\n" % (
        prefix,
        "" if prefix == "" else " ",
        _viewControllerDescription(vc),
    )

    nextPrefix = childPrefix + "   |"

    numChildViewControllers = fb.evaluateIntegerExpression(
        "(int)[(id)[%s childViewControllers] count]" % (vc))

    for i in range(0, numChildViewControllers):
        viewController = fb.evaluateExpression(
            "(id)[(id)[%s childViewControllers] objectAtIndex:%d]" % (vc, i))
        s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
            viewController, string, nextPrefix, nextPrefix)

    if not isMac:
        isModal = fb.evaluateBooleanExpression(
            "%s != nil && ((id)[(id)[(id)%s presentedViewController] presentingViewController]) == %s"  # noqa B950
            % (vc, vc, vc))

        if isModal:
            modalVC = fb.evaluateObjectExpression(
                "(id)[(id)%s presentedViewController]" % (vc))
            s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
                modalVC, string, childPrefix + "  *M", nextPrefix)
            s += "\n// '*M' means the view controller is presented modally."

    return string + s
Esempio n. 10
0
def _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(vc, string, prefix, childPrefix):
    isMac = runtimeHelpers.isMacintoshArch()

    s = "%s%s%s\n" % (prefix, "" if prefix == "" else " ", _viewControllerDescription(vc))

    nextPrefix = childPrefix + "   |"

    numChildViewControllers = fb.evaluateIntegerExpression("(int)[(id)[%s childViewControllers] count]" % (vc))
    childViewControllers = fb.evaluateExpression("(id)[%s childViewControllers]" % (vc))

    for i in range(0, numChildViewControllers):
        viewController = fb.evaluateExpression("(id)[(id)[%s childViewControllers] objectAtIndex:%d]" % (vc, i))
        s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(viewController, string, nextPrefix, nextPrefix)

    if not isMac:
        isModal = fb.evaluateBooleanExpression(
            "%s != nil && ((id)[(id)[(id)%s presentedViewController] presentingViewController]) == %s" % (vc, vc, vc)
        )

        if isModal:
            modalVC = fb.evaluateObjectExpression("(id)[(id)%s presentedViewController]" % (vc))
            s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(
                modalVC, string, childPrefix + "  *M", nextPrefix
            )
            s += "\n// '*M' means the view controller is presented modally."

    return string + s
Esempio n. 11
0
  def run(self, arguments, options):
    request = fb.evaluateInputExpression(arguments[0])
    HTTPHeaderSring = ''
    HTTPMethod = fb.evaluateExpressionValue('(id)[{} HTTPMethod]'.format(request)).GetObjectDescription()
    URL = fb.evaluateExpressionValue('(id)[{} URL]'.format(request)).GetObjectDescription()
    timeout = fb.evaluateExpression('(NSTimeInterval)[{} timeoutInterval]'.format(request))
    HTTPHeaders = fb.evaluateObjectExpression('(id)[{} allHTTPHeaderFields]'.format(request))
    HTTPHeadersCount = fb.evaluateIntegerExpression('[{} count]'.format(HTTPHeaders))
    allHTTPKeys = fb.evaluateObjectExpression('[{} allKeys]'.format(HTTPHeaders))
    for index in range(0, HTTPHeadersCount):
        key = fb.evaluateObjectExpression('[{} objectAtIndex:{}]'.format(allHTTPKeys, index))
        keyDescription = fb.evaluateExpressionValue('(id){}'.format(key)).GetObjectDescription()
        value = fb.evaluateExpressionValue('(id)[(id){} objectForKey:{}]'.format(HTTPHeaders, key)).GetObjectDescription()
        if len(HTTPHeaderSring) > 0:
            HTTPHeaderSring += ' '
        HTTPHeaderSring += '-H "{}: {}"'.format(keyDescription, value)
    HTTPData = fb.evaluateObjectExpression('[{} HTTPBody]'.format(request))
    dataFile = None
    dataAsString = None
    if fb.evaluateIntegerExpression('[{} length]'.format(HTTPData)) > 0:
        if options.embed:
          if fb.evaluateIntegerExpression('[{} respondsToSelector:@selector(base64EncodedStringWithOptions:)]'.format(HTTPData)):
            dataAsString = fb.evaluateExpressionValue('(id)[(id){} base64EncodedStringWithOptions:0]'.format(HTTPData)).GetObjectDescription()
          else :
            print 'This version of OS doesn\'t supports base64 data encoding'
            return False
        elif not runtimeHelpers.isIOSDevice():
          dataFile = self.generateTmpFilePath()
          if not fb.evaluateBooleanExpression('(BOOL)[{} writeToFile:@"{}" atomically:NO]'.format(HTTPData, dataFile)):
            print 'Can\'t write data to file {}'.format(dataFile)
            return False
        else:
          print 'HTTPBody data for iOS Device is supported only with "--embed-data" flag'
          return False

    commandString = ''
    if dataAsString is not None and len(dataAsString) > 0:
      dataFile = self.generateTmpFilePath()
      commandString += 'echo "{}" | base64 -D -o "{}" && '.format(dataAsString, dataFile)
    commandString += 'curl -X {} --connect-timeout {}'.format(HTTPMethod, timeout)
    if len(HTTPHeaderSring) > 0:
        commandString += ' ' + HTTPHeaderSring
    if dataFile is not None:
        commandString += ' --data-binary @"{}"'.format(dataFile)

    commandString += ' "{}"'.format(URL)
    print commandString
Esempio n. 12
0
  def run(self, arguments, options):
    request = arguments[0]
    HTTPHeaderSring = ''
    HTTPMethod = fb.evaluateExpressionValue('(id)[{} HTTPMethod]'.format(request)).GetObjectDescription()
    URL = fb.evaluateExpressionValue('(id)[{} URL]'.format(request)).GetObjectDescription()
    timeout = fb.evaluateExpression('(NSTimeInterval)[{} timeoutInterval]'.format(request))
    HTTPHeaders = fb.evaluateObjectExpression('(id)[{} allHTTPHeaderFields]'.format(request))
    HTTPHeadersCount = fb.evaluateIntegerExpression('[{} count]'.format(HTTPHeaders))
    allHTTPKeys = fb.evaluateObjectExpression('[{} allKeys]'.format(HTTPHeaders))
    for index in range(0, HTTPHeadersCount):
        key = fb.evaluateObjectExpression('[{} objectAtIndex:{}]'.format(allHTTPKeys, index))
        keyDescription = fb.evaluateExpressionValue('(id){}'.format(key)).GetObjectDescription()
        value = fb.evaluateExpressionValue('(id)[(id){} objectForKey:{}]'.format(HTTPHeaders, key)).GetObjectDescription()
        if len(HTTPHeaderSring) > 0:
            HTTPHeaderSring += ' '
        HTTPHeaderSring += '-H "{}: {}"'.format(keyDescription, value)
    HTTPData = fb.evaluateObjectExpression('[{} HTTPBody]'.format(request))
    dataFile = None
    dataAsString = None
    if fb.evaluateIntegerExpression('[{} length]'.format(HTTPData)) > 0:
        if options.embed:
          if fb.evaluateIntegerExpression('[{} respondsToSelector:@selector(base64EncodedStringWithOptions:)]'.format(HTTPData)):
            dataAsString = fb.evaluateExpressionValue('(id)[(id){} base64EncodedStringWithOptions:0]'.format(HTTPData)).GetObjectDescription()
          else :
            print('This version of OS doesn\'t supports base64 data encoding')
            return False
        elif not runtimeHelpers.isIOSDevice():
          dataFile = self.generateTmpFilePath()
          if not fb.evaluateBooleanExpression('(BOOL)[{} writeToFile:@"{}" atomically:NO]'.format(HTTPData, dataFile)):
            print('Can\'t write data to file {}'.format(dataFile))
            return False
        else:
          print('HTTPBody data for iOS Device is supported only with "--embed-data" flag')
          return False

    commandString = ''
    if dataAsString is not None and len(dataAsString) > 0:
      dataFile = self.generateTmpFilePath()
      commandString += 'echo "{}" | base64 -D -o "{}" && '.format(dataAsString, dataFile)
    commandString += 'curl -X {} --connect-timeout {}'.format(HTTPMethod, timeout)
    if len(HTTPHeaderSring) > 0:
        commandString += ' ' + HTTPHeaderSring
    if dataFile is not None:
        commandString += ' --data-binary @"{}"'.format(dataFile)

    commandString += ' "{}"'.format(URL)
    print(commandString)
Esempio n. 13
0
def firstSubviewOfView(view):
  subviews = subviewsOfView(view)
  numViews = fb.evaluateIntegerExpression('[(id)' + subviews + ' count]')

  if numViews == 0:
    return None
  else:
    return fb.evaluateObjectExpression('[' + subviews + ' objectAtIndex:0]')
Esempio n. 14
0
def firstSubviewOfView(view):
    subviews = subviewsOfView(view)
    numViews = fb.evaluateIntegerExpression('[(id)' + subviews + ' count]')

    if numViews == 0:
        return None
    else:
        return fb.evaluateObjectExpression('[' + subviews + ' objectAtIndex:0]')
Esempio n. 15
0
def firstSubviewOfView(view):
    subviews = subviewsOfView(view)
    numViews = fb.evaluateIntegerExpression("[(id)" + subviews + " count]")

    if numViews == 0:
        return None
    else:
        return fb.evaluateObjectExpression("[" + subviews +
                                           " objectAtIndex:0]")
Esempio n. 16
0
  def run(self, arguments, options):
    control = arguments[0]
    targets = fb.evaluateObjectExpression('[[{control} allTargets] allObjects]'.format(control=control))
    targetCount = fb.evaluateIntegerExpression('[{targets} count]'.format(targets=targets))

    for index in range(0, targetCount):
      target = fb.evaluateObjectExpression('[{targets} objectAtIndex:{index}]'.format(targets=targets, index=index))
      actions = fb.evaluateObjectExpression('[{control} actionsForTarget:{target} forControlEvent:0]'.format(control=control, target=target))

      targetDescription = fb.evaluateExpressionValue('(id){target}'.format(target=target)).GetObjectDescription()
      actionsDescription = fb.evaluateExpressionValue('(id)[{actions} componentsJoinedByString:@", "]'.format(actions=actions)).GetObjectDescription()

      print '{target}: {actions}'.format(target=targetDescription, actions=actionsDescription)
Esempio n. 17
0
    def run(self, arguments, options):
        # It's a good habit to explicitly cast the type of all return
        # values and arguments. LLDB can't always find them on its own.
        subviews = fb.evaluateInputExpression(arguments[0])
        # subviews = subviewsOfView(selfView)
        view = fb.evaluateInputExpression(arguments[1])

        index = fb.evaluateIntegerExpression('[[' + subviews + ' subviews] indexOfObject:' + view + ']')
        # index = fb.evaluateIntegerExpression('[(id)' + subviews + '.subviews indexOfObject:' + view + ']')

        # index = fb.evaluateExpression('[(UIView *)%s.subviews indexOfObject:(UIView *)%s]' % (subviews, view))

        # pdb.set_trace()
        print ('index: %d' % index)
Esempio n. 18
0
def setBorderOnAmbiguousViewRecursive(view, width, color):
  if not fb.evaluateBooleanExpression('[(id)%s isKindOfClass:(Class)[UIView class]]' % view):
    return

  isAmbiguous = fb.evaluateBooleanExpression('(BOOL)[%s hasAmbiguousLayout]' % view)
  if isAmbiguous:
    layer = viewHelpers.convertToLayer(view)
    lldb.debugger.HandleCommand('expr (void)[%s setBorderWidth:(CGFloat)%s]' % (layer, width))
    lldb.debugger.HandleCommand('expr (void)[%s setBorderColor:(CGColorRef)[(id)[UIColor %sColor] CGColor]]' % (layer, color))

  subviews = fb.evaluateExpression('(id)[%s subviews]' % view)
  subviewsCount = fb.evaluateIntegerExpression('(int)[(id)%s count]' % subviews)
  if subviewsCount > 0:
    for i in range(0, subviewsCount):
      subview = fb.evaluateExpression('(id)[%s objectAtIndex:%i]' % (subviews, i))
      setBorderOnAmbiguousViewRecursive(subview, width, color)
Esempio n. 19
0
def printAccessibilityHierarchy(view, indent = 0):
  a11yLabel = accessibilityLabel(view)
  classDesc = objHelpers.className(view)
  indentString = '   | ' * indent

  #if we don't have any accessibility string - we should have some children
  if int(a11yLabel.GetValue(), 16) == 0:
    print indentString + ('{} {}'.format(classDesc, view))
    #We call private method that gives back all visible accessibility children for view
    accessibilityElements = fb.evaluateObjectExpression('[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]' % view)
    accessibilityElementsCount = fb.evaluateIntegerExpression('(int)[%s count]' % accessibilityElements)
    for index in range(0, accessibilityElementsCount):
      subview = fb.evaluateObjectExpression('[%s objectAtIndex:%i]' % (accessibilityElements, index))
      printAccessibilityHierarchy(subview, indent + 1)
  else:
    print indentString + ('({} {}) {}'.format(classDesc, view, a11yLabel.GetObjectDescription()))
  def accessibilityGrepHierarchy(self, view, needle):
    a11yLabel = accessibilityLabel(view)
    #if we don't have any accessibility string - we should have some children
    if int(a11yLabel.GetValue(), 16) == 0:
      #We call private method that gives back all visible accessibility children for view
      accessibilityElements = fb.evaluateObjectExpression('[[[UIApplication sharedApplication] keyWindow] _accessibilityElementsInContainer:0 topLevel:%s includeKB:0]' % view)
      accessibilityElementsCount = fb.evaluateIntegerExpression('[%s count]' % accessibilityElements)
      for index in range(0, accessibilityElementsCount):
        subview = fb.evaluateObjectExpression('[%s objectAtIndex:%i]' % (accessibilityElements, index))
        self.accessibilityGrepHierarchy(subview, needle)
    elif re.match(r'.*' + needle + '.*', a11yLabel.GetObjectDescription(), re.IGNORECASE):
      classDesc = objHelpers.className(view)
      print('({} {}) {}'.format(classDesc, view, a11yLabel.GetObjectDescription()))

      #First element that is found is copied to clipboard
      if not self.foundElement:
        self.foundElement = True
        cmd = 'echo %s | tr -d "\n" | pbcopy' % view
        os.system(cmd)
def _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(vc, string, prefix, childPrefix):
  s = '%s%s%s\n' % (prefix, '' if prefix == '' else ' ', _viewControllerDescription(vc))

  nextPrefix = childPrefix + '   |'

  numChildViewControllers = fb.evaluateIntegerExpression('(int)[(id)[%s childViewControllers] count]' % (vc))
  childViewControllers = fb.evaluateExpression('(id)[%s childViewControllers]' % (vc))

  for i in range(0, numChildViewControllers):
    viewController = fb.evaluateExpression('(id)[(id)[%s childViewControllers] objectAtIndex:%d]' % (vc, i))
    s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(viewController, string, nextPrefix, nextPrefix)

  isModal = fb.evaluateBooleanExpression('((id)[(id)[(id)%s presentedViewController] presentingViewController]) == %s' % (vc, vc))

  if isModal:
    modalVC = fb.evaluateObjectExpression('(id)[(id)%s presentedViewController]' % (vc))
    s += _recursiveViewControllerDescriptionWithPrefixAndChildPrefix(modalVC, string, childPrefix + '  *M' , nextPrefix)
    s += '\n// \'*M\' means the view controller is presented modally.'

  return string + s
Esempio n. 22
0
  def run(self, arguments, options):
    commandForObject, ivarName = arguments

    objectAddress = int(fb.evaluateObjectExpression(commandForObject), 0)

    ivarOffsetCommand = '(ptrdiff_t)ivar_getOffset((Ivar)object_getInstanceVariable((id){}, "{}", 0))'.format(objectAddress, ivarName)
    ivarOffset = fb.evaluateIntegerExpression(ivarOffsetCommand)

    # A multi-statement command allows for variables scoped to the command, not permanent in the session like $variables.
    ivarSizeCommand = ('unsigned int size = 0;'
                       'char *typeEncoding = (char *)ivar_getTypeEncoding((Ivar)class_getInstanceVariable((Class)object_getClass((id){}), "{}"));'
                       '(char *)NSGetSizeAndAlignment(typeEncoding, &size, 0);'
                       'size').format(objectAddress, ivarName)
    ivarSize = int(fb.evaluateExpression(ivarSizeCommand), 0)

    error = lldb.SBError()
    watchpoint = lldb.debugger.GetSelectedTarget().WatchAddress(objectAddress + ivarOffset, ivarSize, False, True, error)

    if error.Success():
      print 'Remember to delete the watchpoint using: watchpoint delete {}'.format(watchpoint.GetID())
    else:
      print 'Could not create the watchpoint: {}'.format(error.GetCString())