mysqlshdk/shellcore/shell_polyglot.cc (109 lines of code) (raw):

/* * Copyright (c) 2024, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysqlshdk/include/shellcore/shell_polyglot.h" #include <stdexcept> #include "mysqlshdk/include/shellcore/base_shell.h" #include "mysqlshdk/libs/utils/logger.h" #include "mysqlshdk/scripting/polyglot/polyglot_context.h" #include "shellcore/base_session.h" #include "shellcore/interrupt_handler.h" namespace shcore { namespace { const char *to_string(polyglot::Language language) { switch (language) { case polyglot::Language::JAVASCRIPT: return "JavaScript"; } throw std::logic_error{"to_string(polyglot::Language): should not happen"}; } } // namespace Shell_polyglot::Shell_polyglot(Shell_core *shcore, polyglot::Language type) : Shell_language(shcore), m_polyglot{std::make_shared<polyglot::Polyglot_context>( shcore->registry(), type)} { // Starts and sleeps the polyglot interruption handler thread m_interrupt_handler_thread = std::thread([this, type]() { std::unique_lock<std::mutex> lock(m_interrupt_handler_mutex); while (!m_tear_down) { m_interrupt_handler_condition.wait( lock, [this]() { return m_tear_down || m_interrupt; }); if (m_interrupt) { lock.unlock(); try { abort(); } catch (const std::exception &e) { log_error("Exception while trying to interrupt the %s execution: %s", to_string(type), e.what()); } lock.lock(); m_interrupt = false; } } }); } Shell_polyglot::~Shell_polyglot() { // Shutdowns the polyglot interruption handler thread { std::lock_guard<std::mutex> lock(m_interrupt_handler_mutex); m_tear_down = true; } m_interrupt_handler_condition.notify_one(); m_interrupt_handler_thread.join(); } void Shell_polyglot::set_result_processor( std::function<void(shcore::Value, bool)> result_processor) { m_result_processor = std::move(result_processor); } void Shell_polyglot::handle_input(std::string &code, Input_state &state) { handle_input(code, false); state = m_input_state; } void Shell_polyglot::flush_input(const std::string &code) { std::string code_copy{code}; handle_input(code_copy, true); } void Shell_polyglot::handle_input(std::string &code, bool flush) { // Undefined to be returned in case of errors Value result; shcore::Interrupt_handler handler([this]() { // NOTE: this code is not signal-safe bool notify = false; { std::lock_guard<std::mutex> lock(m_interrupt_handler_mutex); if (!m_interrupt) { m_interrupt = notify = true; } } if (notify) { m_interrupt_handler_condition.notify_one(); } return true; }); bool got_error = true; if (_owner->interactive()) { std::tie(result, got_error) = m_polyglot->execute_interactive(code, &m_input_state, flush); } else { try { std::tie(result, got_error) = m_polyglot->execute(code, _owner->get_input_source()); } catch (const std::exception &exc) { mysqlsh::current_console()->print_diag(exc.what()); } } if (!code.empty()) { _last_handled = code; } m_result_processor(result, got_error); } void Shell_polyglot::set_global(const std::string &name, const Value &value) { m_polyglot->set_global(name, value); } void Shell_polyglot::set_argv(const std::vector<std::string> &argv) { m_polyglot->set_argv(argv); } void Shell_polyglot::abort() noexcept { // Abort execution of JS code // To abort execution of MySQL query called during JS code, a separate // handler should be pushed into the stack in the code that performs the query log_info("User aborted JavaScript execution (^C)"); m_polyglot->terminate(); } std::string Shell_polyglot::get_continued_input_context() { return m_input_state == Input_state::Ok ? "" : "-"; } bool Shell_polyglot::load_plugin(const Plugin_definition &plugin) { return m_polyglot->load_plugin(plugin); } } // namespace shcore