diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index 0a90ef9d2..16a5e4400 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -52,6 +52,7 @@ bool ConsoleController::loadPythonEnvironment() { m_pythonDelegate->initPythonWithUser(this); MicroPython::registerScriptProvider(m_scriptStore); m_importScriptsWhenViewAppears = m_autoImportScripts; + m_scriptStore->clearFetchInformation(); } return true; } @@ -60,7 +61,6 @@ void ConsoleController::unloadPythonEnvironment() { if (!m_pythonDelegate->isPythonUser(nullptr)) { m_consoleStore.startNewSession(); m_pythonDelegate->deinitPython(); - m_scriptStore->clearFetchInformation(); } } diff --git a/apps/code/script.cpp b/apps/code/script.cpp index f74e4e367..6615eed4a 100644 --- a/apps/code/script.cpp +++ b/apps/code/script.cpp @@ -84,16 +84,41 @@ const char * Script::content() const { } bool Script::contentFetchedFromConsole() const { - assert(!isNull()); - Data d = value(); - return (((char *)d.buffer)[k_autoImportationStatusSize] == 1); + return fetchedStatus() == FetchedStatus::FromConsole; } -void Script::setContentFetchedFromConsole(bool fetch) { +bool Script::contentFetchedForVariableBox() const { + return fetchedStatus() == FetchedStatus::ForVariableBox; +} + +void Script::setContentFetchedFromConsole() { + setFetchedStatus(FetchedStatus::FromConsole); +} + +void Script::setContentFetchedForVariableBox() { + setFetchedStatus(FetchedStatus::ForVariableBox); +} + +void Script::resetContentFetchedStatus() { + setFetchedStatus(FetchedStatus::None); +} + +Script::FetchedStatus Script::fetchedStatus() const { assert(!isNull()); Data d = value(); - ((char *)d.buffer)[k_autoImportationStatusSize] = fetch; + uint8_t status = const_cast(static_cast(d.buffer))[k_autoImportationStatusSize]; + assert(status == static_cast(FetchedStatus::None) + || status == static_cast(FetchedStatus::FromConsole) + || status == static_cast(FetchedStatus::ForVariableBox)); + return static_cast(status); +} + +void Script::setFetchedStatus(FetchedStatus status) { + assert(!isNull()); + Data d = value(); + const_cast(static_cast(d.buffer))[k_autoImportationStatusSize] = static_cast(status); setValue(d); } + } diff --git a/apps/code/script.h b/apps/code/script.h index faa408877..b5b1cadf6 100644 --- a/apps/code/script.h +++ b/apps/code/script.h @@ -5,14 +5,21 @@ namespace Code { -/* Record: | Size | Name | Body | - * Script: | | | AutoImportationStatus | ContentFetchedFromConsoleStatus | Content | +/* Record: | Size | Name | Body | + * Script: | | | AutoImportationStatus | FetchedStatus | Content | * * AutoImportationStatus is 1 if the script should be auto imported when the * console opens. * - * ContentFetchedFromConsoleStatus is 1 if hte console has currently imported - * this script. This is used to import the right variables in the variable box. */ + * FetchedStatus has two purposes: + * - It is used to detect which scripts are imported in the console, so we can + * retrieve the correct variables afterwards in the variable box. When a + * script has been imported, its fetchedStatus value is + * FetchedStatus::FromConsole. + * - It is used to prevent circular importation problems, such as scriptA + * importing scriptB, which imports scriptA. Once we get the variables from a + * script to put them in the variable box, we switch the status to + * FetchedStatus::ForVariableBox and won't reload it afterwards. */ class Script : public Ion::Storage::Record { private: @@ -22,6 +29,7 @@ private: static constexpr size_t k_autoImportationStatusSize = 1; // TODO use only 1 byte for both status flags static constexpr size_t k_currentImportationStatusSize = 1; + public: static constexpr int k_defaultScriptNameMaxSize = 6 + k_defaultScriptNameNumberMaxSize + 1; /* 6 = strlen("script") @@ -32,12 +40,25 @@ public: static bool nameCompliant(const char * name); static constexpr size_t InformationSize() { return k_autoImportationStatusSize + k_currentImportationStatusSize; } + Script(Ion::Storage::Record r = Ion::Storage::Record()) : Record(r) {} bool autoImportationStatus() const; void toggleAutoimportationStatus(); const char * content() const; bool contentFetchedFromConsole() const; - void setContentFetchedFromConsole(bool fetch); + bool contentFetchedForVariableBox() const; + void setContentFetchedFromConsole(); + void setContentFetchedForVariableBox(); + void resetContentFetchedStatus(); +private: + /* Fetched status */ + enum class FetchedStatus : uint8_t { + None = 0, + FromConsole = 1, + ForVariableBox = 2 + }; + FetchedStatus fetchedStatus() const; + void setFetchedStatus(FetchedStatus status); }; } diff --git a/apps/code/script_store.cpp b/apps/code/script_store.cpp index 80fdb523d..c6b232f13 100644 --- a/apps/code/script_store.cpp +++ b/apps/code/script_store.cpp @@ -31,7 +31,7 @@ const char * ScriptStore::contentOfScript(const char * name, bool markAsFetched) return nullptr; } if (markAsFetched) { - script.setContentFetchedFromConsole(true); + script.setContentFetchedFromConsole(); } return script.content(); } @@ -40,7 +40,7 @@ void ScriptStore::clearFetchInformation() { // TODO optimize fetches const int scriptsCount = numberOfScripts(); for (int i = 0; i < scriptsCount; i++) { - scriptAtIndex(i).setContentFetchedFromConsole(false); + scriptAtIndex(i).resetContentFetchedStatus(); } } diff --git a/apps/code/variable_box_controller.cpp b/apps/code/variable_box_controller.cpp index a6d372764..d884abe32 100644 --- a/apps/code/variable_box_controller.cpp +++ b/apps/code/variable_box_controller.cpp @@ -158,7 +158,7 @@ void VariableBoxController::loadFunctionsAndVariables(int scriptIndex, const cha // Reset the node counts empty(); - + m_scriptStore->clearFetchInformation(); if (textToAutocomplete != nullptr && textToAutocompleteLength < 0) { textToAutocompleteLength = strlen(textToAutocomplete); } @@ -176,6 +176,14 @@ void VariableBoxController::loadFunctionsAndVariables(int scriptIndex, const cha loadBuiltinNodes(textToAutocomplete, textToAutocompleteLength); Script script = m_scriptStore->scriptAtIndex(scriptIndex); assert(!script.isNull()); + + /* Handle the FetchedStatus: we will import the current script variables in + * loadCurrentVariablesInScript, so we do not want to import those variables + * before, if any imported script also imported the current script. */ + assert(!script.contentFetchedFromConsole() && !script.contentFetchedForVariableBox()); + script.setContentFetchedForVariableBox(); + + // Load the imported and current variables const char * scriptContent = script.content(); assert(scriptContent != nullptr); loadImportedVariablesInScript(scriptContent, textToAutocomplete, textToAutocompleteLength); @@ -222,7 +230,7 @@ void VariableBoxController::loadVariablesImportedFromScripts() { for (int i = 0; i < scriptsCount; i++) { Script script = m_scriptStore->scriptAtIndex(i); if (script.contentFetchedFromConsole()) { - loadGlobalAndImportedVariablesInScriptAsImported(script.fullName(), script.content(), nullptr, -1, false); + loadGlobalAndImportedVariablesInScriptAsImported(script, nullptr, -1, false); } } } @@ -619,10 +627,15 @@ void VariableBoxController::loadCurrentVariablesInScript(const char * scriptCont } } -void VariableBoxController::loadGlobalAndImportedVariablesInScriptAsImported(const char * scriptName, const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules) { +void VariableBoxController::loadGlobalAndImportedVariablesInScriptAsImported(Script script, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules) { + if (script.contentFetchedForVariableBox()) { + // We already fetched these script variables + return; + } nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - + const char * scriptName = script.fullName(); + const char * scriptContent = script.content(); mp_lexer_t *lex = mp_lexer_new_from_str_len(0, scriptContent, strlen(scriptContent), false); mp_parse_tree_t parseTree = mp_parse(lex, MP_PARSE_FILE_INPUT); mp_parse_node_t pn = parseTree.root; @@ -660,6 +673,8 @@ void VariableBoxController::loadGlobalAndImportedVariablesInScriptAsImported(con mp_parse_tree_clear(&parseTree); nlr_pop(); } + // Mark that we already fetched these script variables + script.setContentFetchedForVariableBox(); } bool VariableBoxController::addNodesFromImportMaybe(mp_parse_node_struct_t * parseNode, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules) { @@ -758,9 +773,7 @@ bool VariableBoxController::addNodesFromImportMaybe(mp_parse_node_struct_t * par Script importedScript; const char * scriptFullName; if (importationSourceIsScript(importationSourceName, &scriptFullName, &importedScript)) { - const char * scriptContent = importedScript.content(); - assert(scriptContent != nullptr); - loadGlobalAndImportedVariablesInScriptAsImported(scriptFullName, scriptContent, textToAutocomplete, textToAutocompleteLength); + loadGlobalAndImportedVariablesInScriptAsImported(importedScript, textToAutocomplete, textToAutocompleteLength); } } } diff --git a/apps/code/variable_box_controller.h b/apps/code/variable_box_controller.h index d9c681c24..f8968b41b 100644 --- a/apps/code/variable_box_controller.h +++ b/apps/code/variable_box_controller.h @@ -87,7 +87,7 @@ private: void loadBuiltinNodes(const char * textToAutocomplete, int textToAutocompleteLength); void loadImportedVariablesInScript(const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength); void loadCurrentVariablesInScript(const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength); - void loadGlobalAndImportedVariablesInScriptAsImported(const char * scriptName, const char * scriptContent, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules = true); + void loadGlobalAndImportedVariablesInScriptAsImported(Script script, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules = true); // Returns true if this was an import structure bool addNodesFromImportMaybe(mp_parse_node_struct_t * parseNode, const char * textToAutocomplete, int textToAutocompleteLength, bool importFromModules = true); const char * importationSourceNameFromNode(mp_parse_node_t & node);