Refactor LuaParser and make it abort running Lua if taking too long

pull/229/head
James Ball 2024-03-18 20:27:31 +00:00 zatwierdzone przez James H Ball
rodzic faaa049b9d
commit 2e23c485b6
6 zmienionych plików z 145 dodań i 89 usunięć

Wyświetl plik

@ -30,7 +30,7 @@ OscirenderLookAndFeel::OscirenderLookAndFeel() {
// combo box
setColour(juce::ComboBox::backgroundColourId, Colours::veryDark);
setColour(juce::ComboBox::outlineColourId, juce::Colours::white);
setColour(juce::ComboBox::outlineColourId, Colours::veryDark);
setColour(juce::ComboBox::arrowColourId, juce::Colours::white);
// text box
@ -92,10 +92,10 @@ void OscirenderLookAndFeel::drawComboBox(juce::Graphics& g, int width, int heigh
juce::Rectangle<int> boxBounds{0, 0, width, height};
g.setColour(box.findColour(juce::ComboBox::backgroundColourId));
g.fillRect(boxBounds.toFloat());
g.fillRoundedRectangle(boxBounds.toFloat(), RECT_RADIUS);
g.setColour(box.findColour(juce::ComboBox::outlineColourId).withAlpha(box.isEnabled() ? 1.0f : 0.5f));
g.drawRect(boxBounds.toFloat(), 1.0f);
g.drawRoundedRectangle(boxBounds.toFloat(), RECT_RADIUS, 1.0f);
juce::Rectangle<int> arrowZone{width - 15, 0, 10, height};
juce::Path path;
@ -164,8 +164,14 @@ void OscirenderLookAndFeel::drawGroupComponentOutline(juce::Graphics& g, int wid
auto alpha = group.isEnabled() ? 1.0f : 0.5f;
juce::Path background;
background.addRoundedRectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), RECT_RADIUS, RECT_RADIUS);
auto ds = juce::DropShadow(juce::Colours::black, 3, juce::Point<int>(0, 0));
ds.drawForPath(g, background);
g.setColour(group.findColour(groupComponentBackgroundColourId).withMultipliedAlpha(alpha));
g.fillRect(bounds);
g.fillPath(background);
auto header = bounds.removeFromTop(2 * textH);
@ -192,8 +198,13 @@ void OscirenderLookAndFeel::drawLinearSlider(juce::Graphics& g, int x, int y, in
auto thumbWidth = getSliderThumbRadius(slider);
juce::Path thumb;
thumb.addEllipse(juce::Rectangle<float>(static_cast<float>(thumbWidth), static_cast<float>(thumbWidth)).withCentre(point));
juce::DropShadow ds(juce::Colours::black, 1, { 0, 1 });
ds.drawForPath(g, thumb);
g.setColour(slider.findColour(sliderThumbOutlineColourId).withAlpha(slider.isEnabled() ? 1.0f : 0.5f));
g.drawEllipse(juce::Rectangle<float>(static_cast<float>(thumbWidth), static_cast<float>(thumbWidth)).withCentre(point), 1.0f);
g.strokePath(thumb, juce::PathStrokeType(1.0f));
}
void OscirenderLookAndFeel::drawButtonBackground(juce::Graphics& g, juce::Button& button, const juce::Colour& backgroundColour, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) {

Wyświetl plik

@ -69,6 +69,8 @@ class OscirenderLookAndFeel : public juce::LookAndFeel_V4 {
public:
OscirenderLookAndFeel();
static const int RECT_RADIUS = 5;
void drawComboBox(juce::Graphics& g, int width, int height, bool, int, int, int, int, juce::ComboBox& box) override;
void positionComboBoxText(juce::ComboBox& box, juce::Label& label) override;
void drawTickBox(juce::Graphics& g, juce::Component& component,

Wyświetl plik

@ -152,24 +152,24 @@ void OscirenderAudioProcessorEditor::paint(juce::Graphics& g) {
auto ds = juce::DropShadow(juce::Colours::black, 5, juce::Point<int>(0, 0));
if (!usingNativeMenuBar) {
// add drop shadow to the menu bar
ds.drawForRectangle(g, menuBar.getBounds());
}
// if (!usingNativeMenuBar) {
// // add drop shadow to the menu bar
// ds.drawForRectangle(g, menuBar.getBounds());
// }
for (int i = 0; i < codeEditors.size(); i++) {
if (codeEditors[i]->isVisible()) {
ds.drawForRectangle(g, codeEditors[i]->getBounds());
}
}
if (lua.isVisible()) {
ds.drawForRectangle(g, lua.getBounds());
}
//for (int i = 0; i < codeEditors.size(); i++) {
// if (codeEditors[i]->isVisible()) {
// ds.drawForRectangle(g, codeEditors[i]->getBounds());
// }
//}
//
//if (lua.isVisible()) {
// ds.drawForRectangle(g, lua.getBounds());
//}
if (console.isVisible()) {
ds.drawForRectangle(g, console.getBounds());
}
// if (console.isVisible()) {
// ds.drawForRectangle(g, console.getBounds());
// }
}
void OscirenderAudioProcessorEditor::resized() {

Wyświetl plik

@ -97,13 +97,13 @@ void SettingsComponent::mouseDown(const juce::MouseEvent& event) {
void SettingsComponent::paint(juce::Graphics& g) {
// add drop shadow to each component
auto dc = juce::DropShadow(juce::Colours::black, 5, juce::Point<int>(0, 0));
dc.drawForRectangle(g, main.getBounds());
dc.drawForRectangle(g, effects.getBounds());
dc.drawForRectangle(g, midi.getBounds());
dc.drawForRectangle(g, perspective.getBounds());
//auto dc = juce::DropShadow(juce::Colours::black, 5, juce::Point<int>(0, 0));
//dc.drawForRectangle(g, main.getBounds());
//dc.drawForRectangle(g, effects.getBounds());
//dc.drawForRectangle(g, midi.getBounds());
//dc.drawForRectangle(g, perspective.getBounds());
if (txt.isVisible()) {
dc.drawForRectangle(g, txt.getBounds());
}
//if (txt.isVisible()) {
// dc.drawForRectangle(g, txt.getBounds());
//}
}

Wyświetl plik

@ -4,6 +4,23 @@
std::function<void(const std::string&)> LuaParser::onPrint;
std::function<void()> LuaParser::onClear;
void LuaParser::maximumInstructionsReached(lua_State* L, lua_Debug* D) {
lua_getstack(L, 1, D);
lua_getinfo(L, "l", D);
std::string msg = std::to_string(D->currentline) + ": Maximum instructions reached! You may have an infinite loop.";
lua_pushstring(L, msg.c_str());
lua_error(L);
}
void LuaParser::setMaximumInstructions(lua_State*& L, int count) {
lua_sethook(L, LuaParser::maximumInstructionsReached, LUA_MASKCOUNT, count);
}
void LuaParser::resetMaximumInstructions(lua_State*& L) {
lua_sethook(L, LuaParser::maximumInstructionsReached, 0, 0);
}
static int luaPrint(lua_State* L) {
int nargs = lua_gettop(L);
@ -81,16 +98,71 @@ void LuaParser::parse(lua_State*& L) {
const char* error = lua_tostring(L, -1);
reportError(error);
lua_pop(L, 1);
functionRef = -1;
usingFallbackScript = true;
if (script != fallbackScript) {
reset(L, fallbackScript);
}
revertToFallback(L);
} else {
functionRef = luaL_ref(L, LUA_REGISTRYINDEX);
}
}
void LuaParser::setGlobalVariable(lua_State*& L, const char* name, double value) {
lua_pushnumber(L, value);
lua_setglobal(L, name);
}
void LuaParser::setGlobalVariable(lua_State*& L, const char* name, int value) {
lua_pushnumber(L, value);
lua_setglobal(L, name);
}
void LuaParser::setGlobalVariables(lua_State*& L, LuaVariables& vars) {
setGlobalVariable(L, "step", vars.step);
setGlobalVariable(L, "sample_rate", vars.sampleRate);
setGlobalVariable(L, "frequency", vars.frequency);
setGlobalVariable(L, "phase", vars.phase);
for (int i = 0; i < NUM_SLIDERS; i++) {
setGlobalVariable(L, SLIDER_NAMES[i], vars.sliders[i]);
}
if (vars.isEffect) {
setGlobalVariable(L, "x", vars.x);
setGlobalVariable(L, "y", vars.y);
setGlobalVariable(L, "z", vars.z);
}
}
void LuaParser::incrementVars(LuaVariables& vars) {
vars.step++;
vars.phase += 2 * std::numbers::pi * vars.frequency / vars.sampleRate;
if (vars.phase > 2 * std::numbers::pi) {
vars.phase -= 2 * std::numbers::pi;
}
}
void LuaParser::clearStack(lua_State*& L) {
lua_settop(L, 0);
}
void LuaParser::revertToFallback(lua_State*& L) {
functionRef = -1;
usingFallbackScript = true;
if (script != fallbackScript) {
reset(L, fallbackScript);
}
}
void LuaParser::readTable(lua_State*& L, std::vector<float>& values) {
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);
}
}
// only the audio thread runs this fuction
std::vector<float> LuaParser::run(lua_State*& L, LuaVariables& vars) {
// if we haven't seen this state before, reset it
@ -102,77 +174,37 @@ std::vector<float> LuaParser::run(lua_State*& L, LuaVariables& vars) {
std::vector<float> values;
lua_pushnumber(L, vars.step);
lua_setglobal(L, "step");
lua_pushnumber(L, vars.sampleRate);
lua_setglobal(L, "sample_rate");
lua_pushnumber(L, vars.frequency);
lua_setglobal(L, "frequency");
lua_pushnumber(L, vars.phase);
lua_setglobal(L, "phase");
for (int i = 0; i < NUM_SLIDERS; i++) {
lua_pushnumber(L, vars.sliders[i]);
lua_setglobal(L, SLIDER_NAMES[i]);
}
if (vars.isEffect) {
lua_pushnumber(L, vars.x);
lua_setglobal(L, "x");
lua_pushnumber(L, vars.y);
lua_setglobal(L, "y");
lua_pushnumber(L, vars.z);
lua_setglobal(L, "z");
}
setGlobalVariables(L, vars);
// Get the function from the registry
lua_geti(L, LUA_REGISTRYINDEX, functionRef);
setMaximumInstructions(L, 1000000);
if (lua_isfunction(L, -1)) {
const int ret = lua_pcall(L, 0, LUA_MULTRET, 0);
if (ret != LUA_OK) {
const char* error = lua_tostring(L, -1);
reportError(error);
functionRef = -1;
usingFallbackScript = true;
if (script != fallbackScript) {
reset(L, fallbackScript);
}
} 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);
revertToFallback(L);
} else {
if (lua_istable(L, -1)) {
readTable(L, values);
}
}
} else {
functionRef = -1;
usingFallbackScript = true;
if (script != fallbackScript) {
reset(L, fallbackScript);
}
revertToFallback(L);
}
resetMaximumInstructions(L);
if (functionRef != -1 && !usingFallbackScript) {
resetErrors();
}
// clear stack
lua_settop(L, 0);
vars.step++;
vars.phase += 2 * std::numbers::pi * vars.frequency / vars.sampleRate;
if (vars.phase > 2 * std::numbers::pi) {
vars.phase -= 2 * std::numbers::pi;
}
clearStack(L);
incrementVars(vars);
return values;
}

Wyświetl plik

@ -59,6 +59,7 @@ struct LuaVariables {
};
struct lua_State;
struct lua_Debug;
class LuaParser {
public:
LuaParser(juce::String fileName, juce::String script, std::function<void(int, juce::String, juce::String)> errorCallback, juce::String fallbackScript = "return { 0.0, 0.0 }");
@ -73,15 +74,25 @@ public:
static std::function<void()> onClear;
private:
static void maximumInstructionsReached(lua_State* L, lua_Debug* D);
void reset(lua_State*& L, juce::String script);
void reportError(const char* error);
void parse(lua_State*& L);
void setGlobalVariable(lua_State*& L, const char* name, double value);
void setGlobalVariable(lua_State*& L, const char* name, int value);
void setGlobalVariables(lua_State*& L, LuaVariables& vars);
void incrementVars(LuaVariables& vars);
void clearStack(lua_State*& L);
void revertToFallback(lua_State*& L);
void readTable(lua_State*& L, std::vector<float>& values);
void setMaximumInstructions(lua_State*& L, int count);
void resetMaximumInstructions(lua_State*& L);
int functionRef = -1;
bool usingFallbackScript = false;
juce::String script;
juce::String fallbackScript;
std::atomic<bool> updateVariables = false;
std::function<void(int, juce::String, juce::String)> errorCallback;
juce::String fileName;
std::vector<lua_State*> seenStates;