content: #!/usr/bin/python
  
  # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
  #
  # This source code is licensed under the MIT license found in the
  # LICENSE file in the root directory of this source tree.
  
  import json
  import shlex
  
  import lldb
  
  
  class FBCommandArgument:  # noqa B903
      def __init__(
          self, short="", long="", arg="", type="", help="", default="", boolean=False
      ):
          self.shortName = short
          self.longName = long
          self.argName = arg
          self.argType = type
          self.help = help
          self.default = default
          self.boolean = boolean
  
  
  class FBCommand:
      def name(self):
          return None
  
      def options(self):
          return []
  
      def args(self):
          return []
  
      def description(self):
          return ""
  
      def lex(self, commandLine):
          return shlex.split(commandLine)
  
      def run(self, arguments, option):
          pass
  
  
  def isSuccess(error):
      # When evaluating a `void` expression, the returned value will indicate an
      # error. This error is named: kNoResult. This error value does *not* mean
      # there was a problem. This logic follows what the builtin `expression`
      # command does. See: https://git.io/vwpjl (UserExpression.h)
      kNoResult = 0x1001
      return error.success or error.value == kNoResult
  
  
  def importModule(frame, module):
      options = lldb.SBExpressionOptions()
      options.SetLanguage(lldb.eLanguageTypeObjC)
      value = frame.EvaluateExpression("@import " + module, options)
      return isSuccess(value.error)
  
  
  # evaluates expression in Objective-C++ context, so it will work even for
  # Swift projects
  def evaluateExpressionValue(
      expression,
      printErrors=True,
      language=lldb.eLanguageTypeObjC_plus_plus,
      tryAllThreads=False,
  ):
      frame = (
          lldb.debugger.GetSelectedTarget()
          .GetProcess()
          .GetSelectedThread()
          .GetSelectedFrame()
      )
      options = lldb.SBExpressionOptions()
      options.SetLanguage(language)
  
      # Allow evaluation that contains a @throw/@catch.
      #   By default, ObjC @throw will cause evaluation to be aborted. At the time
      #   of a @throw, it's not known if the exception will be handled by a @catch.
      #   An exception that's caught, should not cause evaluation to fail.
      options.SetTrapExceptions(False)
  
      # Give evaluation more time.
      options.SetTimeoutInMicroSeconds(5000000)  # 5s
  
      # Most Chisel commands are not multithreaded.
      options.SetTryAllThreads(tryAllThreads)
  
      value = frame.EvaluateExpression(expression, options)
      error = value.GetError()
  
      # Retry if the error could be resolved by first importing UIKit.
      if (
          error.type == lldb.eErrorTypeExpression
          and error.value == lldb.eExpressionParseError
          and importModule(frame, "UIKit")
      ):
          value = frame.EvaluateExpression(expression, options)
          error = value.GetError()
  
      if printErrors and not isSuccess(error):
          print(error)
  
      return value
  
  
  def evaluateInputExpression(expression, printErrors=True):
      # HACK
      if expression.startswith("(id)"):
          return evaluateExpressionValue(expression, printErrors=printErrors).GetValue()
  
      frame = (
          lldb.debugger.GetSelectedTarget()
          .GetProcess()
          .GetSelectedThread()
          .GetSelectedFrame()
      )
      options = lldb.SBExpressionOptions()
      options.SetTrapExceptions(False)
      value = frame.EvaluateExpression(expression, options)
      error = value.GetError()
  
      if printErrors and error.Fail():
          print(error)
  
      return value.GetValue()
  
  
  def evaluateIntegerExpression(expression, printErrors=True):
      output = evaluateExpression("(int)(" + expression + ")", printErrors).replace(
          "'", ""
      )
      if output.startswith("\\x"):  # Booleans may display as \x01 (Hex)
          output = output[2:]
      elif output.startswith("\\"):  # Or as \0 (Dec)
          output = output[1:]
      return int(output, 0)
  
  
  def evaluateBooleanExpression(expression, printErrors=True):
      return (
          int(evaluateIntegerExpression("(BOOL)(" + expression + ")", printErrors)) != 0
      )
  
  
  def evaluateExpression(expression, printErrors=True):
      return evaluateExpressionValue(expression, printErrors=printErrors).GetValue()
  
  
  def describeObject(expression, printErrors=True):
      return evaluateExpressionValue(
          "(id)(" + expression + ")", printErrors
      ).GetObjectDescription()
  
  
  def evaluateEffect(expression, printErrors=True):
      evaluateExpressionValue("(void)(" + expression + ")", printErrors=printErrors)
  
  
  def evaluateObjectExpression(expression, printErrors=True):
      return evaluateExpression("(id)(" + expression + ")", printErrors)
  
  
  def evaluateCStringExpression(expression, printErrors=True):
      ret = evaluateExpression(expression, printErrors)
  
      process = lldb.debugger.GetSelectedTarget().GetProcess()
      error = lldb.SBError()
      ret = process.ReadCStringFromMemory(int(ret, 16), 256, error)
      if error.Success():
          return ret
      else:
          if printErrors:
              print(error)
          return None
  
  
  RETURN_MACRO = """
  #define IS_JSON_OBJ(obj)\
      (obj != nil && ((bool)[NSJSONSerialization isValidJSONObject:obj] ||\
      (bool)[obj isKindOfClass:[NSString class]] ||\
      (bool)[obj isKindOfClass:[NSNumber class]]))
  #define RETURN(ret) ({\
      if (!IS_JSON_OBJ(ret)) {\
          (void)[NSException raise:@"Invalid RETURN argument" format:@""];\
      }\
      NSDictionary *__dict = @{@"return":ret};\
      NSData *__data = (id)[NSJSONSerialization dataWithJSONObject:__dict options:0 error:NULL];\
      NSString *__str = (id)[[NSString alloc] initWithData:__data encoding:4];\
      (char *)[__str UTF8String];})
  #define RETURNCString(ret)\
      ({NSString *___cstring_ret = [NSString stringWithUTF8String:ret];\
      RETURN(___cstring_ret);})
  """
  
  
  def check_expr(expr):
      return expr.strip().split(";")[-2].find("RETURN") != -1
  
  
  # evaluate a batch of Objective-C expressions, the last expression
  # must contain a RETURN marco and it will automatic transform the
  # Objective-C object to Python object
  # Example:
  #       >>> fbchisellldbbase.evaluate('NSString *str = @"hello world"; RETURN(@{@"key": str});')
  #       {u'key': u'hello world'}
  def evaluate(expr):
      if not check_expr(expr):
          raise Exception(
              "Invalid Expression, the last expression not include a RETURN family marco"
          )
  
      command = "({" + RETURN_MACRO + "\n" + expr + "})"
      ret = evaluateExpressionValue(command, printErrors=True)
      if not ret.GetError().Success():
          print(ret.GetError())
          return None
      else:
          process = lldb.debugger.GetSelectedTarget().GetProcess()
          error = lldb.SBError()
          ret = process.ReadCStringFromMemory(int(ret.GetValue(), 16), 2**20, error)
          if not error.Success():
              print(error)
              return None
          else:
              ret = json.loads(ret)
              return ret["return"]
  
  
  def currentLanguage():
      return (
          lldb.debugger.GetSelectedTarget()
          .GetProcess()
          .GetSelectedThread()
          .GetSelectedFrame()
          .GetCompileUnit()
          .GetLanguage()
      )
  