diff --git a/apps/code/app.cpp b/apps/code/app.cpp index 256bec485..a7b372d46 100644 --- a/apps/code/app.cpp +++ b/apps/code/app.cpp @@ -77,18 +77,24 @@ void App::Snapshot::setOpt(const char * name, char * value) { App::App(Container * container, Snapshot * snapshot) : ::App(container, snapshot, &m_codeStackViewController, I18n::Message::Warning), - m_consoleController(nullptr, snapshot->scriptStore() + m_pythonHeap{}, + m_pythonUser(nullptr), + m_consoleController(nullptr, this, snapshot->scriptStore() #if EPSILON_GETOPT , snapshot->lockOnConsole() #endif ), m_listFooter(&m_codeStackViewController, &m_menuController, &m_menuController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey, ButtonRowController::Size::Large), - m_menuController(&m_listFooter, snapshot->scriptStore(), &m_listFooter), + m_menuController(&m_listFooter, this, snapshot->scriptStore(), &m_listFooter), m_codeStackViewController(&m_modalViewController, &m_listFooter), m_variableBoxController(&m_menuController, snapshot->scriptStore()) { } +App::~App() { + deinitPython(); +} + bool App::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Home && m_consoleController.inputRunLoopActive()) { // We need to return true here because we want to actually exit from the @@ -117,4 +123,17 @@ bool App::textInputDidReceiveEvent(TextInput * textInput, Ion::Events::Event eve return false; } +void App::initPythonWithUser(const void * pythonUser) { + assert(m_pythonUser == nullptr); + m_pythonUser = pythonUser; + MicroPython::init(m_pythonHeap, m_pythonHeap + k_pythonHeapSize); +} + +void App::deinitPython() { + if (m_pythonUser) { + MicroPython::deinit(); + m_pythonUser = nullptr; + } +} + } diff --git a/apps/code/app.h b/apps/code/app.h index 68a7a826d..f3deb7c79 100644 --- a/apps/code/app.h +++ b/apps/code/app.h @@ -36,12 +36,26 @@ public: #endif ScriptStore m_scriptStore; }; + ~App(); StackViewController * stackViewController() { return &m_codeStackViewController; } ConsoleController * consoleController() { return &m_consoleController; } PythonToolbox * pythonToolbox() { return &m_toolbox; } bool handleEvent(Ion::Events::Event event) override; bool textInputDidReceiveEvent(TextInput * textInput, Ion::Events::Event event); + // Python delegate + bool isPythonUser(const void * pythonUser) { return m_pythonUser == pythonUser; } + void initPythonWithUser(const void * pythonUser); + void deinitPython(); private: + /* Python delegate: + * MicroPython requires a heap. To avoid dynamic allocation, we keep a working + * buffer here and we give to controllers that load Python environment. We + * also memoize the last Python user to avoid re-initiating MicroPython when + * unneeded. */ + static constexpr int k_pythonHeapSize = 16384; + char m_pythonHeap[k_pythonHeapSize]; + const void * m_pythonUser; + App(Container * container, Snapshot * snapshot); ConsoleController m_consoleController; ButtonRowController m_listFooter; diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index 6ff68c3c1..814b7f822 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -15,7 +15,7 @@ namespace Code { static const char * sStandardPromptText = ">>> "; -ConsoleController::ConsoleController(Responder * parentResponder, ScriptStore * scriptStore +ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore #if EPSILON_GETOPT , bool lockOnConsole #endif @@ -24,11 +24,11 @@ ConsoleController::ConsoleController(Responder * parentResponder, ScriptStore * SelectableTableViewDataSource(), TextFieldDelegate(), MicroPython::ExecutionEnvironment(), + m_pythonDelegate(pythonDelegate), m_rowHeight(KDText::charSize(k_fontSize).height()), m_importScriptsWhenViewAppears(false), m_selectableTableView(this, this, this, this), m_editCell(this, this), - m_pythonHeap(nullptr), m_scriptStore(scriptStore), m_sandboxController(this), m_inputRunLoopActive(false) @@ -44,22 +44,12 @@ ConsoleController::ConsoleController(Responder * parentResponder, ScriptStore * } } -ConsoleController::~ConsoleController() { - unloadPythonEnvironment(); -} - bool ConsoleController::loadPythonEnvironment(bool autoImportScripts) { if(pythonEnvironmentIsLoaded()) { return true; } emptyOutputAccumulationBuffer(); - m_pythonHeap = (char *)malloc(k_pythonHeapSize); - if (m_pythonHeap == nullptr) { - // In DEBUG mode, the assert at the end of malloc would have already failed - // and the program crashed. - return false; - } - MicroPython::init(m_pythonHeap, m_pythonHeap + k_pythonHeapSize); + m_pythonDelegate->initPythonWithUser(this); MicroPython::registerScriptProvider(m_scriptStore); m_importScriptsWhenViewAppears = autoImportScripts; return true; @@ -68,14 +58,12 @@ bool ConsoleController::loadPythonEnvironment(bool autoImportScripts) { void ConsoleController::unloadPythonEnvironment() { if (pythonEnvironmentIsLoaded()) { m_consoleStore.startNewSession(); - MicroPython::deinit(); - free(m_pythonHeap); - m_pythonHeap = nullptr; + m_pythonDelegate->deinitPython(); } } bool ConsoleController::pythonEnvironmentIsLoaded() { - return (m_pythonHeap != nullptr); + return m_pythonDelegate->isPythonUser(this); } void ConsoleController::autoImport() { diff --git a/apps/code/console_controller.h b/apps/code/console_controller.h index 525ffc038..624fb5e9e 100644 --- a/apps/code/console_controller.h +++ b/apps/code/console_controller.h @@ -12,20 +12,17 @@ namespace Code { +class App; + class ConsoleController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate, public MicroPython::ExecutionEnvironment { public: static constexpr KDText::FontSize k_fontSize = KDText::FontSize::Large; - ConsoleController(Responder * parentResponder, ScriptStore * scriptStore + ConsoleController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore #if EPSILON_GETOPT , bool m_lockOnConsole #endif ); - ~ConsoleController(); - ConsoleController(const ConsoleController& other) = delete; - ConsoleController(ConsoleController&& other) = delete; - ConsoleController operator=(const ConsoleController& other) = delete; - ConsoleController& operator=(ConsoleController&& other) = delete; bool loadPythonEnvironment(bool autoImportScripts = true); void unloadPythonEnvironment(); @@ -81,20 +78,19 @@ private: static constexpr int LineCellType = 0; static constexpr int EditCellType = 1; static constexpr int k_numberOfLineCells = 15; // May change depending on the screen height - static constexpr int k_pythonHeapSize = 16384; static constexpr int k_outputAccumulationBufferSize = 100; void flushOutputAccumulationBufferToStore(); void appendTextToOutputAccumulationBuffer(const char * text, size_t length); void emptyOutputAccumulationBuffer(); size_t firstNewLineCharIndex(const char * text, size_t length); StackViewController * stackViewController(); + App * m_pythonDelegate; int m_rowHeight; bool m_importScriptsWhenViewAppears; ConsoleStore m_consoleStore; SelectableTableView m_selectableTableView; ConsoleLineCell m_cells[k_numberOfLineCells]; ConsoleEditCell m_editCell; - char * m_pythonHeap; char m_outputAccumulationBuffer[k_outputAccumulationBufferSize]; /* The Python machine might call printText several times to print a single * string. We thus use m_outputAccumulationBuffer to store and concatenate the diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index f3e5e8b94..a4efa4219 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -8,9 +8,9 @@ namespace Code { -EditorController::EditorController(MenuController * menuController) : +EditorController::EditorController(MenuController * menuController, App * pythonDelegate) : ViewController(nullptr), - m_editorView(this), + m_editorView(this, pythonDelegate), m_script(Ion::Storage::Record()), m_menuController(menuController) { diff --git a/apps/code/editor_controller.h b/apps/code/editor_controller.h index 1fea76efe..8161b7f3a 100644 --- a/apps/code/editor_controller.h +++ b/apps/code/editor_controller.h @@ -12,7 +12,7 @@ class ScriptParameterController; class EditorController : public ViewController, public TextAreaDelegate { public: - EditorController(MenuController * menuController); + EditorController(MenuController * menuController, App * pythonDelegate); void setScript(Script script); /* ViewController */ diff --git a/apps/code/editor_view.cpp b/apps/code/editor_view.cpp index 2253d9cbb..fdc3ec91f 100644 --- a/apps/code/editor_view.cpp +++ b/apps/code/editor_view.cpp @@ -8,10 +8,10 @@ namespace Code { constexpr KDText::FontSize editorFontSize = KDText::FontSize::Large; -EditorView::EditorView(Responder * parentResponder) : +EditorView::EditorView(Responder * parentResponder, App * pythonDelegate) : Responder(parentResponder), View(), - m_textArea(parentResponder, editorFontSize), + m_textArea(parentResponder, pythonDelegate, editorFontSize), m_gutterView(editorFontSize) { m_textArea.setScrollViewDelegate(this); diff --git a/apps/code/editor_view.h b/apps/code/editor_view.h index be4bcd907..520efb376 100644 --- a/apps/code/editor_view.h +++ b/apps/code/editor_view.h @@ -8,7 +8,7 @@ namespace Code { class EditorView : public Responder, public View, public ScrollViewDelegate { public: - EditorView(Responder * parentResponder); + EditorView(Responder * parentResponder, App * pythonDelegate); void setTextAreaDelegate(TextAreaDelegate * delegate) { m_textArea.setDelegate(delegate); } diff --git a/apps/code/menu_controller.cpp b/apps/code/menu_controller.cpp index 900258ebb..c55d39981 100644 --- a/apps/code/menu_controller.cpp +++ b/apps/code/menu_controller.cpp @@ -8,7 +8,7 @@ namespace Code { -MenuController::MenuController(Responder * parentResponder, ScriptStore * scriptStore, ButtonRowController * footer) : +MenuController::MenuController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore, ButtonRowController * footer) : ViewController(parentResponder), ButtonRowDelegate(nullptr, footer), m_scriptStore(scriptStore), @@ -23,7 +23,7 @@ MenuController::MenuController(Responder * parentResponder, ScriptStore * script }, this), KDText::FontSize::Large), m_selectableTableView(this, this, this, this), m_scriptParameterController(nullptr, I18n::Message::ScriptOptions, this), - m_editorController(this), + m_editorController(this, pythonDelegate), m_reloadConsoleWhenBecomingFirstResponder(false), m_shouldDisplayAddScriptRow(true) { diff --git a/apps/code/menu_controller.h b/apps/code/menu_controller.h index 61b5f565a..490c43935 100644 --- a/apps/code/menu_controller.h +++ b/apps/code/menu_controller.h @@ -13,7 +13,7 @@ class ScriptParameterController; class MenuController : public ViewController, public TableViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public TextFieldDelegate, public ButtonRowDelegate { public: - MenuController(Responder * parentResponder, ScriptStore * scriptStore, ButtonRowController * footer); + MenuController(Responder * parentResponder, App * pythonDelegate, ScriptStore * scriptStore, ButtonRowController * footer); ConsoleController * consoleController(); StackViewController * stackViewController(); void willExitResponderChain(Responder * nextFirstResponder) override; diff --git a/apps/code/python_text_area.cpp b/apps/code/python_text_area.cpp index 8f7f3b6d7..e8764be2d 100644 --- a/apps/code/python_text_area.cpp +++ b/apps/code/python_text_area.cpp @@ -1,4 +1,5 @@ #include "python_text_area.h" +#include "app.h" extern "C" { #include "py/nlr.h" @@ -75,19 +76,11 @@ static inline size_t TokenLength(mp_lexer_t * lex) { } void PythonTextArea::ContentView::loadSyntaxHighlighter() { - assert(m_pythonHeap == nullptr); - m_pythonHeap = static_cast(malloc(k_pythonHeapSize)); - if (m_pythonHeap != nullptr) { - MicroPython::init(m_pythonHeap, m_pythonHeap + k_pythonHeapSize); - } + m_pythonDelegate->initPythonWithUser(this); } void PythonTextArea::ContentView::unloadSyntaxHighlighter() { - if (m_pythonHeap != nullptr) { - MicroPython::deinit(); - free(m_pythonHeap); - m_pythonHeap = nullptr; - } + m_pythonDelegate->deinitPython(); } void PythonTextArea::ContentView::clearRect(KDContext * ctx, KDRect rect) const { @@ -105,7 +98,7 @@ void PythonTextArea::ContentView::clearRect(KDContext * ctx, KDRect rect) const void PythonTextArea::ContentView::drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn) const { LOG_DRAW("Drawing \"%.*s\"\n", length, text); - if (m_pythonHeap == nullptr) { + if (!m_pythonDelegate->isPythonUser(this)) { drawStringAt( ctx, line, diff --git a/apps/code/python_text_area.h b/apps/code/python_text_area.h index 6d988e617..bb04c07fc 100644 --- a/apps/code/python_text_area.h +++ b/apps/code/python_text_area.h @@ -5,11 +5,13 @@ namespace Code { +class App; + class PythonTextArea : public TextArea { public: - PythonTextArea(Responder * parentResponder, KDText::FontSize fontSize) : + PythonTextArea(Responder * parentResponder, App * pythonDelegate, KDText::FontSize fontSize) : TextArea(parentResponder, &m_contentView, fontSize), - m_contentView(fontSize) + m_contentView(pythonDelegate, fontSize) { } void loadSyntaxHighlighter() { m_contentView.loadSyntaxHighlighter(); } @@ -17,9 +19,9 @@ public: protected: class ContentView : public TextArea::ContentView { public: - ContentView(KDText::FontSize fontSize) : + ContentView(App * pythonDelegate, KDText::FontSize fontSize) : TextArea::ContentView(fontSize), - m_pythonHeap(nullptr) + m_pythonDelegate(pythonDelegate) { } void loadSyntaxHighlighter(); @@ -28,8 +30,7 @@ protected: void drawLine(KDContext * ctx, int line, const char * text, size_t length, int fromColumn, int toColumn) const override; KDRect dirtyRectFromCursorPosition(size_t index, bool lineBreak) const override; private: - static constexpr size_t k_pythonHeapSize = 4096; - char * m_pythonHeap; + App * m_pythonDelegate; }; private: const ContentView * nonEditableContentView() const override { return &m_contentView; }