diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index df319f5..d8201f5 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -187,11 +187,17 @@ void OscirenderAudioProcessorEditor::updateCodeEditor() { codeEditors[i]->setVisible(false); } codeEditors[index]->setVisible(true); + // used so that codeDocumentTextInserted and codeDocumentTextDeleted know whether the parserLock + // is held by the message thread or not. We hold the lock in this function, but not when the + // code document is updated by the user editing text. Since both functions are called by the + // message thread, this is safe. + updatingDocumentsWithParserLock = true; if (index == 0) { codeEditors[index]->loadContent(audioProcessor.perspectiveEffect->getCode()); } else { codeEditors[index]->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(originalIndex), false).readEntireStreamAsString()); } + updatingDocumentsWithParserLock = false; } triggerAsyncUpdate(); } @@ -227,12 +233,22 @@ void OscirenderAudioProcessorEditor::editPerspectiveFunction(bool enable) { // parsersLock AND effectsLock must be locked before calling this function void OscirenderAudioProcessorEditor::codeDocumentTextInserted(const juce::String& newText, int insertIndex) { - updateCodeDocument(); + if (updatingDocumentsWithParserLock) { + updateCodeDocument(); + } else { + juce::SpinLock::ScopedLockType parserLock(audioProcessor.parsersLock); + updateCodeDocument(); + } } // parsersLock AND effectsLock must be locked before calling this function void OscirenderAudioProcessorEditor::codeDocumentTextDeleted(int startIndex, int endIndex) { - updateCodeDocument(); + if (updatingDocumentsWithParserLock) { + updateCodeDocument(); + } else { + juce::SpinLock::ScopedLockType parserLock(audioProcessor.parsersLock); + updateCodeDocument(); + } } // parsersLock AND effectsLock must be locked before calling this function diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index eafa853..011f3d1 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -55,6 +55,8 @@ private: MainMenuBarModel menuBarModel{*this}; juce::MenuBarComponent menuBar; + std::atomic updatingDocumentsWithParserLock = false; + void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override; void codeDocumentTextDeleted(int startIndex, int endIndex) override; void updateCodeDocument(); diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index fc9dc84..63afcd0 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -381,10 +381,10 @@ void OscirenderAudioProcessor::changeCurrentFile(int index) { if (index < 0 || index >= fileBlocks.size()) { return; } - changeSound(sounds[index]); currentFile = index; updateLuaValues(); updateObjValues(); + changeSound(sounds[index]); } void OscirenderAudioProcessor::changeSound(ShapeSound::Ptr sound) { diff --git a/Source/lua/LuaParser.cpp b/Source/lua/LuaParser.cpp index 052a698..9e77698 100644 --- a/Source/lua/LuaParser.cpp +++ b/Source/lua/LuaParser.cpp @@ -5,6 +5,7 @@ LuaParser::LuaParser(juce::String script) { // initialization L = luaL_newstate(); + lua_atpanic(L, panic); luaL_openlibs(L); this->script = script; @@ -54,24 +55,30 @@ std::vector LuaParser::run() { lua_geti(L, LUA_REGISTRYINDEX, functionRef); - const int ret = lua_pcall(L, 0, LUA_MULTRET, 0); - if (ret != 0) { - const char* error = lua_tostring(L, -1); - DBG(error); - functionRef = -1; - } else if (lua_istable(L, -1)) { - auto length = lua_rawlen(L, -1); + if (lua_isfunction(L, -1)) { + const int ret = lua_pcall(L, 0, LUA_MULTRET, 0); + if (ret != 0) { + const char* error = lua_tostring(L, -1); + DBG(error); + functionRef = -1; + } else if (lua_istable(L, -1)) { + auto length = lua_rawlen(L, -1); - for (int i = 1; i <= length; i++) { - lua_pushinteger(L, i); - lua_gettable(L, -2); - float value = lua_tonumber(L, -1); - lua_pop(L, 1); - values.push_back(value); + for (int i = 1; i <= length; i++) { + lua_pushinteger(L, i); + lua_gettable(L, -2); + float value = lua_tonumber(L, -1); + lua_pop(L, 1); + values.push_back(value); + } } + } else { + DBG("functionRef is not a function"); + functionRef = -1; } - lua_pop(L, 1); + // clear stack + lua_settop(L, 0); step++; @@ -86,3 +93,11 @@ void LuaParser::setVariable(juce::String variableName, double value) { variables.push_back(value); updateVariables = true; } + + +int LuaParser::panic(lua_State *L) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "error object is not a string"; + DBG("PANIC: unprotected error in call to Lua API (%s)\n" << msg); + return 0; /* return to Lua to abort */ +} \ No newline at end of file diff --git a/Source/lua/LuaParser.h b/Source/lua/LuaParser.h index 8159fae..c7f23b8 100644 --- a/Source/lua/LuaParser.h +++ b/Source/lua/LuaParser.h @@ -14,6 +14,8 @@ public: private: void parse(); + static int panic(lua_State* L); + int functionRef = -1; long step = 1; lua_State* L;