new api class: SBFile
authorLawrence D'Anna <lawrence_danna@apple.com>
Thu, 3 Oct 2019 04:01:07 +0000 (04:01 +0000)
committerLawrence D'Anna <lawrence_danna@apple.com>
Thu, 3 Oct 2019 04:01:07 +0000 (04:01 +0000)
Summary:
SBFile is a scripting API wrapper for lldb_private::File

This is the first step in a project to enable arbitrary python
io.IOBase file objects -- including those that override the read()
and write() methods -- to be used as the main debugger IOStreams.

Currently this is impossible because python file objects must first
be converted into FILE* streams by SWIG in order to be passed into
the debugger.

full prototype: https://github.com/smoofra/llvm-project/tree/files

Reviewers: JDevlieghere, jasonmolenda, zturner, jingham, labath

Reviewed By: labath

Subscribers: labath, mgorny, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D67793

llvm-svn: 373562

16 files changed:
lldb/include/lldb/API/LLDB.h
lldb/include/lldb/API/SBDefines.h
lldb/include/lldb/API/SBError.h
lldb/include/lldb/API/SBFile.h [new file with mode: 0644]
lldb/include/lldb/Host/File.h
lldb/packages/Python/lldbsuite/test/python_api/default-constructor/TestDefaultConstructorForAPIObjects.py
lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py
lldb/scripts/Python/python-typemaps.swig
lldb/scripts/interface/SBFile.i [new file with mode: 0644]
lldb/scripts/lldb.swig
lldb/source/API/CMakeLists.txt
lldb/source/API/SBFile.cpp [new file with mode: 0644]
lldb/source/API/SBReproducer.cpp
lldb/source/Host/common/File.cpp
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h

index 0806ab7..75e2d70 100644 (file)
@@ -13,8 +13,8 @@
 #include "lldb/API/SBAttachInfo.h"
 #include "lldb/API/SBBlock.h"
 #include "lldb/API/SBBreakpoint.h"
-#include "lldb/API/SBBreakpointName.h"
 #include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBBreakpointName.h"
 #include "lldb/API/SBBroadcaster.h"
 #include "lldb/API/SBCommandInterpreter.h"
 #include "lldb/API/SBCommandReturnObject.h"
@@ -28,6 +28,7 @@
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBExecutionContext.h"
 #include "lldb/API/SBExpressionOptions.h"
+#include "lldb/API/SBFile.h"
 #include "lldb/API/SBFileSpec.h"
 #include "lldb/API/SBFileSpecList.h"
 #include "lldb/API/SBFrame.h"
index 838c84c..68aca93 100644 (file)
@@ -41,6 +41,7 @@ class LLDB_API SBEvent;
 class LLDB_API SBEventList;
 class LLDB_API SBExecutionContext;
 class LLDB_API SBExpressionOptions;
+class LLDB_API SBFile;
 class LLDB_API SBFileSpec;
 class LLDB_API SBFileSpecList;
 class LLDB_API SBFrame;
index b076f87..3db2658 100644 (file)
@@ -70,6 +70,7 @@ protected:
   friend class SBTrace;
   friend class SBValue;
   friend class SBWatchpoint;
+  friend class SBFile;
 
   lldb_private::Status *get();
 
diff --git a/lldb/include/lldb/API/SBFile.h b/lldb/include/lldb/API/SBFile.h
new file mode 100644 (file)
index 0000000..da33044
--- /dev/null
@@ -0,0 +1,38 @@
+//===-- SBFile.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SBFile_h_
+#define LLDB_SBFile_h_
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+
+class LLDB_API SBFile {
+public:
+  SBFile();
+  SBFile(FILE *file, bool transfer_ownership);
+  SBFile(int fd, const char *mode, bool transfer_ownership);
+  ~SBFile();
+
+  SBError Read(uint8_t *buf, size_t num_bytes, size_t *bytes_read);
+  SBError Write(const uint8_t *buf, size_t num_bytes, size_t *bytes_written);
+  SBError Flush();
+  bool IsValid() const;
+  SBError Close();
+
+  operator bool() const;
+  bool operator!() const;
+
+private:
+  FileSP m_opaque_sp;
+};
+
+} // namespace lldb
+
+#endif // LLDB_SBFile_h_
index 9ad5f47..effd0be 100644 (file)
@@ -136,6 +136,8 @@ public:
 
   int GetDescriptor() const;
 
+  static uint32_t GetOptionsFromMode(llvm::StringRef mode);
+
   WaitableHandle GetWaitableHandle() override;
 
   FILE *GetStream();
index e48b90e..75bd4a6 100644 (file)
@@ -194,6 +194,20 @@ class APIDefaultConstructorTestCase(TestBase):
 
     @add_test_categories(['pyapi'])
     @no_debug_info_test
+    def test_SBFile(self):
+        sbf = lldb.SBFile()
+        self.assertFalse(sbf.IsValid())
+        self.assertFalse(bool(sbf))
+        e, n = sbf.Write(b'foo')
+        self.assertTrue(e.Fail())
+        self.assertEqual(n, 0)
+        buffer = bytearray(100)
+        e, n = sbf.Read(buffer)
+        self.assertEqual(n, 0)
+        self.assertTrue(e.Fail())
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
     def test_SBInstruction(self):
         obj = lldb.SBInstruction()
         if self.TraceOn():
index e69de29..fe75521 100644 (file)
@@ -0,0 +1,150 @@
+"""
+Test lldb Python API for file handles.
+"""
+
+from __future__ import print_function
+
+import os
+import io
+import re
+import sys
+from contextlib import contextmanager
+
+import lldb
+from lldbsuite.test import  lldbtest
+from lldbsuite.test.decorators import add_test_categories, no_debug_info_test
+
+
+def readStrippedLines(f):
+    def i():
+        for line in f:
+            line = line.strip()
+            if line:
+                yield line
+    return list(i())
+
+
+class FileHandleTestCase(lldbtest.TestBase):
+
+    mydir = lldbtest.Base.compute_mydir(__file__)
+
+    # The way this class interacts with the debugger is different
+    # than normal.   Most of these test cases will mess with the
+    # debugger I/O streams, so we want a fresh debugger for each
+    # test so those mutations don't interfere with each other.
+    #
+    # Also, the way normal tests evaluate debugger commands is
+    # by using a SBCommandInterpreter directly, which captures
+    # the output in a result object.   For many of tests tests
+    # we want the debugger to write the  output directly to
+    # its I/O streams like it would have done interactively.
+    #
+    # For this reason we also define handleCmd() here, even though
+    # it is similar to runCmd().
+
+    def setUp(self):
+        super(FileHandleTestCase, self).setUp()
+        self.debugger = lldb.SBDebugger.Create()
+        self.out_filename = self.getBuildArtifact('output')
+        self.in_filename = self.getBuildArtifact('input')
+
+    def tearDown(self):
+        lldb.SBDebugger.Destroy(self.debugger)
+        super(FileHandleTestCase, self).tearDown()
+        for name in (self.out_filename, self.in_filename):
+            if os.path.exists(name):
+                os.unlink(name)
+
+    # Similar to runCmd(), but this uses the per-test debugger, and it
+    # supports, letting the debugger just print the results instead
+    # of collecting them.
+    def handleCmd(self, cmd, check=True, collect_result=True):
+        assert not check or collect_result
+        ret = lldb.SBCommandReturnObject()
+        if collect_result:
+            interpreter = self.debugger.GetCommandInterpreter()
+            interpreter.HandleCommand(cmd, ret)
+        else:
+            self.debugger.HandleCommand(cmd)
+        if collect_result and check:
+            self.assertTrue(ret.Succeeded())
+        return ret.GetOutput()
+
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
+    def test_legacy_file_out_script(self):
+        with open(self.out_filename, 'w') as f:
+            self.debugger.SetOutputFileHandle(f, False)
+            # scripts print to output even if you capture the results
+            # I'm not sure I love that behavior, but that's the way
+            # it's been for a long time.  That's why this test works
+            # even with collect_result=True.
+            self.handleCmd('script 1+1')
+            self.debugger.GetOutputFileHandle().write('FOO\n')
+        lldb.SBDebugger.Destroy(self.debugger)
+        with open(self.out_filename, 'r') as f:
+            self.assertEqual(readStrippedLines(f), ['2', 'FOO'])
+
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
+    def test_legacy_file_out(self):
+        with open(self.out_filename, 'w') as f:
+            self.debugger.SetOutputFileHandle(f, False)
+            self.handleCmd('p/x 3735928559', collect_result=False, check=False)
+        lldb.SBDebugger.Destroy(self.debugger)
+        with open(self.out_filename, 'r') as f:
+            self.assertIn('deadbeef', f.read())
+
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
+    def test_legacy_file_err(self):
+        with open(self.out_filename, 'w') as f:
+            self.debugger.SetErrorFileHandle(f, False)
+            self.handleCmd('lol', check=False, collect_result=False)
+        lldb.SBDebugger.Destroy(self.debugger)
+        with open(self.out_filename, 'r') as f:
+            self.assertIn("is not a valid command", f.read())
+
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
+    def test_sbfile_type_errors(self):
+        sbf = lldb.SBFile()
+        self.assertRaises(TypeError, sbf.Write, None)
+        self.assertRaises(TypeError, sbf.Read, None)
+        self.assertRaises(TypeError, sbf.Read, b'this bytes is not mutable')
+        self.assertRaises(TypeError, sbf.Write, u"ham sandwich")
+        self.assertRaises(TypeError, sbf.Read, u"ham sandwich")
+
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
+    def test_sbfile_write(self):
+        with open(self.out_filename, 'w') as f:
+            sbf = lldb.SBFile(f.fileno(), "w", False)
+            self.assertTrue(sbf.IsValid())
+            e, n = sbf.Write(b'FOO\nBAR')
+            self.assertTrue(e.Success())
+            self.assertEqual(n, 7)
+            sbf.Close()
+            self.assertFalse(sbf.IsValid())
+        with open(self.out_filename, 'r') as f:
+            self.assertEqual(readStrippedLines(f), ['FOO', 'BAR'])
+
+
+    @add_test_categories(['pyapi'])
+    @no_debug_info_test
+    def test_sbfile_read(self):
+        with open(self.out_filename, 'w') as f:
+            f.write('FOO')
+        with open(self.out_filename, 'r') as f:
+            sbf = lldb.SBFile(f.fileno(), "r", False)
+            self.assertTrue(sbf.IsValid())
+            buffer = bytearray(100)
+            e, n = sbf.Read(buffer)
+            self.assertTrue(e.Success())
+            self.assertEqual(buffer[:n], b'FOO')
+
index fe6a179..9c43ef1 100644 (file)
 namespace {
 template <class T>
 T PyLongAsT(PyObject *obj) {
-  static_assert(true, "unsupported type"); 
+  static_assert(true, "unsupported type");
 }
 
 template <> uint64_t PyLongAsT<uint64_t>(PyObject *obj) {
@@ -461,3 +461,44 @@ bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
         return NULL;
     }
 }
+
+// These two pybuffer macros are copied out of swig/Lib/python/pybuffer.i,
+// and fixed so they will not crash if PyObject_GetBuffer fails.
+// https://github.com/swig/swig/issues/1640
+
+%define %pybuffer_mutable_binary(TYPEMAP, SIZE)
+%typemap(in) (TYPEMAP, SIZE) {
+  int res; Py_ssize_t size = 0; void *buf = 0;
+  Py_buffer view;
+  res = PyObject_GetBuffer($input, &view, PyBUF_WRITABLE);
+  if (res < 0) {
+    PyErr_Clear();
+    %argument_fail(res, "(TYPEMAP, SIZE)", $symname, $argnum);
+  }
+  size = view.len;
+  buf = view.buf;
+  PyBuffer_Release(&view);
+  $1 = ($1_ltype) buf;
+  $2 = ($2_ltype) (size/sizeof($*1_type));
+}
+%enddef
+
+%define %pybuffer_binary(TYPEMAP, SIZE)
+%typemap(in) (TYPEMAP, SIZE) {
+  int res; Py_ssize_t size = 0; const void *buf = 0;
+  Py_buffer view;
+  res = PyObject_GetBuffer($input, &view, PyBUF_CONTIG_RO);
+  if (res < 0) {
+    PyErr_Clear();
+    %argument_fail(res, "(TYPEMAP, SIZE)", $symname, $argnum);
+  }
+  size = view.len;
+  buf = view.buf;
+  PyBuffer_Release(&view);
+  $1 = ($1_ltype) buf;
+  $2 = ($2_ltype) (size / sizeof($*1_type));
+}
+%enddef
+
+%pybuffer_binary(const uint8_t *buf, size_t num_bytes);
+%pybuffer_mutable_binary(uint8_t *buf, size_t num_bytes);
diff --git a/lldb/scripts/interface/SBFile.i b/lldb/scripts/interface/SBFile.i
new file mode 100644 (file)
index 0000000..6cdb192
--- /dev/null
@@ -0,0 +1,38 @@
+//===-- SWIG Interface for SBFile -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+namespace lldb {
+
+%feature("docstring",
+"Represents a file."
+) SBFile;
+
+class SBFile
+{
+public:
+    SBFile();
+    SBFile(int fd, const char *mode, bool transfer_ownership);
+
+    ~SBFile ();
+
+    %feature("autodoc", "Read(buffer) -> SBError, bytes_read") Read;
+    SBError Read(uint8_t *buf, size_t num_bytes, size_t *OUTPUT);
+
+    %feature("autodoc", "Write(buffer) -> SBError, written_read") Write;
+    SBError Write(const uint8_t *buf, size_t num_bytes, size_t *OUTPUT);
+
+    void Flush();
+
+    bool IsValid() const;
+
+    operator bool() const;
+
+    SBError Close();
+};
+
+} // namespace lldb
index ec39ad1..1634e18 100644 (file)
@@ -123,6 +123,7 @@ def lldb_iter(obj, getsize, getelem):
 #include "lldb/API/SBExecutionContext.h"
 #include "lldb/API/SBExpressionOptions.h"
 #include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBFile.h"
 #include "lldb/API/SBFileSpecList.h"
 #include "lldb/API/SBFrame.h"
 #include "lldb/API/SBFunction.h"
@@ -210,6 +211,7 @@ def lldb_iter(obj, getsize, getelem):
 %include "./interface/SBExecutionContext.i"
 %include "./interface/SBExpressionOptions.i"
 %include "./interface/SBFileSpec.i"
+%include "./interface/SBFile.i"
 %include "./interface/SBFileSpecList.i"
 %include "./interface/SBFrame.i"
 %include "./interface/SBFunction.i"
index d93b8b5..f06ed1f 100644 (file)
@@ -34,6 +34,7 @@ add_lldb_library(liblldb SHARED
   SBExecutionContext.cpp
   SBExpressionOptions.cpp
   SBFileSpec.cpp
+  SBFile.cpp
   SBFileSpecList.cpp
   SBFrame.cpp
   SBFunction.cpp
diff --git a/lldb/source/API/SBFile.cpp b/lldb/source/API/SBFile.cpp
new file mode 100644 (file)
index 0000000..fc4f557
--- /dev/null
@@ -0,0 +1,113 @@
+//===-- SBFile.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/API/SBFile.h"
+#include "SBReproducerPrivate.h"
+#include "lldb/API/SBError.h"
+#include "lldb/Host/File.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+SBFile::~SBFile() {}
+
+SBFile::SBFile() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBFile); }
+
+SBFile::SBFile(FILE *file, bool transfer_ownership) {
+  m_opaque_sp = std::make_shared<File>(file, transfer_ownership);
+}
+
+SBFile::SBFile(int fd, const char *mode, bool transfer_owndership) {
+  LLDB_RECORD_CONSTRUCTOR(SBFile, (int, const char *, bool), fd, mode,
+                          transfer_owndership);
+  auto options = File::GetOptionsFromMode(mode);
+  m_opaque_sp = std::make_shared<File>(fd, options, transfer_owndership);
+}
+
+SBError SBFile::Read(uint8_t *buf, size_t num_bytes, size_t *bytes_read) {
+  LLDB_RECORD_DUMMY(lldb::SBError, SBFile, Read, (uint8_t *, size_t, size_t *),
+                    buf, num_bytes, bytes_read);
+  SBError error;
+  if (!m_opaque_sp) {
+    error.SetErrorString("invalid SBFile");
+    *bytes_read = 0;
+  } else {
+    Status status = m_opaque_sp->Read(buf, num_bytes);
+    error.SetError(status);
+    *bytes_read = num_bytes;
+  }
+  return LLDB_RECORD_RESULT(error);
+}
+
+SBError SBFile::Write(const uint8_t *buf, size_t num_bytes,
+                      size_t *bytes_written) {
+  LLDB_RECORD_DUMMY(lldb::SBError, SBFile, Write,
+                    (const uint8_t *, size_t, size_t *), buf, num_bytes,
+                    bytes_written);
+  SBError error;
+  if (!m_opaque_sp) {
+    error.SetErrorString("invalid SBFile");
+    *bytes_written = 0;
+  } else {
+    Status status = m_opaque_sp->Write(buf, num_bytes);
+    error.SetError(status);
+    *bytes_written = num_bytes;
+  }
+  return LLDB_RECORD_RESULT(error);
+}
+
+SBError SBFile::Flush() {
+  LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBFile, Flush);
+  SBError error;
+  if (!m_opaque_sp) {
+    error.SetErrorString("invalid SBFile");
+  } else {
+    Status status = m_opaque_sp->Flush();
+    error.SetError(status);
+  }
+  return LLDB_RECORD_RESULT(error);
+}
+
+bool SBFile::IsValid() const {
+  LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFile, IsValid);
+  return m_opaque_sp && m_opaque_sp->IsValid();
+}
+
+SBError SBFile::Close() {
+  LLDB_RECORD_METHOD_NO_ARGS(lldb::SBError, SBFile, Close);
+  SBError error;
+  if (m_opaque_sp) {
+    Status status = m_opaque_sp->Close();
+    error.SetError(status);
+  }
+  return LLDB_RECORD_RESULT(error);
+}
+
+SBFile::operator bool() const {
+  LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFile, operator bool);
+  return LLDB_RECORD_RESULT(IsValid());
+}
+
+bool SBFile::operator!() const {
+  LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFile, operator!);
+  return LLDB_RECORD_RESULT(!IsValid());
+}
+
+namespace lldb_private {
+namespace repro {
+template <> void RegisterMethods<SBFile>(Registry &R) {
+  LLDB_REGISTER_CONSTRUCTOR(SBFile, ());
+  LLDB_REGISTER_CONSTRUCTOR(SBFile, (int, const char *, bool));
+  LLDB_REGISTER_METHOD(lldb::SBError, SBFile, Flush, ());
+  LLDB_REGISTER_METHOD_CONST(bool, SBFile, IsValid, ());
+  LLDB_REGISTER_METHOD_CONST(bool, SBFile, operator bool,());
+  LLDB_REGISTER_METHOD_CONST(bool, SBFile, operator!,());
+  LLDB_REGISTER_METHOD(lldb::SBError, SBFile, Close, ());
+}
+} // namespace repro
+} // namespace lldb_private
index 439ee5a..6e11b2c 100644 (file)
@@ -52,6 +52,7 @@ SBRegistry::SBRegistry() {
   RegisterMethods<SBEvent>(R);
   RegisterMethods<SBExecutionContext>(R);
   RegisterMethods<SBExpressionOptions>(R);
+  RegisterMethods<SBFile>(R);
   RegisterMethods<SBFileSpec>(R);
   RegisterMethods<SBFileSpecList>(R);
   RegisterMethods<SBFrame>(R);
index b79c003..eff4197 100644 (file)
@@ -68,6 +68,20 @@ static const char *GetStreamOpenModeFromOptions(uint32_t options) {
   return nullptr;
 }
 
+uint32_t File::GetOptionsFromMode(llvm::StringRef mode) {
+  return llvm::StringSwitch<uint32_t>(mode)
+      .Case("r", File::eOpenOptionRead)
+      .Case("w", File::eOpenOptionWrite)
+      .Case("a", File::eOpenOptionWrite | File::eOpenOptionAppend |
+                     File::eOpenOptionCanCreate)
+      .Case("r+", File::eOpenOptionRead | File::eOpenOptionWrite)
+      .Case("w+", File::eOpenOptionRead | File::eOpenOptionWrite |
+                      File::eOpenOptionCanCreate | File::eOpenOptionTruncate)
+      .Case("a+", File::eOpenOptionRead | File::eOpenOptionWrite |
+                      File::eOpenOptionAppend | File::eOpenOptionCanCreate)
+      .Default(0);
+}
+
 int File::kInvalidDescriptor = -1;
 FILE *File::kInvalidStream = nullptr;
 
@@ -143,9 +157,14 @@ uint32_t File::GetPermissions(Status &error) const {
 
 Status File::Close() {
   Status error;
-  if (StreamIsValid() && m_own_stream) {
-    if (::fclose(m_stream) == EOF)
-      error.SetErrorToErrno();
+  if (StreamIsValid()) {
+    if (m_own_stream) {
+      if (::fclose(m_stream) == EOF)
+        error.SetErrorToErrno();
+    } else {
+      if (::fflush(m_stream) == EOF)
+        error.SetErrorToErrno();
+    }
   }
 
   if (DescriptorIsValid() && m_own_descriptor) {
index 6d9a74d..20745d4 100644 (file)
@@ -949,7 +949,6 @@ PythonFile::PythonFile() : PythonObject() {}
 
 PythonFile::PythonFile(File &file, const char *mode) { Reset(file, mode); }
 
-
 PythonFile::PythonFile(PyRefType type, PyObject *o) { Reset(type, o); }
 
 PythonFile::~PythonFile() {}
@@ -1014,22 +1013,6 @@ void PythonFile::Reset(File &file, const char *mode) {
 #endif
 }
 
-uint32_t PythonFile::GetOptionsFromMode(llvm::StringRef mode) {
-  if (mode.empty())
-    return 0;
-
-  return llvm::StringSwitch<uint32_t>(mode.str())
-      .Case("r", File::eOpenOptionRead)
-      .Case("w", File::eOpenOptionWrite)
-      .Case("a", File::eOpenOptionWrite | File::eOpenOptionAppend |
-                     File::eOpenOptionCanCreate)
-      .Case("r+", File::eOpenOptionRead | File::eOpenOptionWrite)
-      .Case("w+", File::eOpenOptionRead | File::eOpenOptionWrite |
-                      File::eOpenOptionCanCreate | File::eOpenOptionTruncate)
-      .Case("a+", File::eOpenOptionRead | File::eOpenOptionWrite |
-                      File::eOpenOptionAppend | File::eOpenOptionCanCreate)
-      .Default(0);
-}
 
 FileUP PythonFile::GetUnderlyingFile() const {
   if (!IsValid())
@@ -1038,7 +1021,7 @@ FileUP PythonFile::GetUnderlyingFile() const {
   // We don't own the file descriptor returned by this function, make sure the
   // File object knows about that.
   PythonString py_mode = GetAttributeValue("mode").AsType<PythonString>();
-  auto options = PythonFile::GetOptionsFromMode(py_mode.GetString());
+  auto options = File::GetOptionsFromMode(py_mode.GetString());
   auto file = std::make_unique<File>(PyObject_AsFileDescriptor(m_py_obj),
                                      options, false);
   if (!file->IsValid())
index 14484d9..8fd2be1 100644 (file)
@@ -466,8 +466,6 @@ public:
   void Reset(PyRefType type, PyObject *py_obj) override;
   void Reset(File &file, const char *mode);
 
-  static uint32_t GetOptionsFromMode(llvm::StringRef mode);
-
   lldb::FileUP GetUnderlyingFile() const;
 };