mysqlshdk/scripting/polyglot/languages/polyglot_language.h (161 lines of code) (raw):

/* * Copyright (c) 2024, 2025, 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 */ #ifndef MYSQLSHDK_SCRIPTING_POLYGLOT_LANGUAGES_POLYGLOT_LANGUAGE_H_ #define MYSQLSHDK_SCRIPTING_POLYGLOT_LANGUAGES_POLYGLOT_LANGUAGE_H_ #include <cassert> #include <cstdint> #include <list> #include <memory> #include <stack> #include <string> #include <vector> #include "mysqlshdk/scripting/polyglot/utils/polyglot_api_clean.h" #include "mysqlshdk/include/scripting/naming_style.h" #include "mysqlshdk/include/scripting/types.h" #include "mysqlshdk/scripting/polyglot/file_system/polyglot_file_system.h" #include "mysqlshdk/scripting/polyglot/languages/polyglot_common_context.h" #include "mysqlshdk/scripting/polyglot/native_wrappers/polyglot_collectable.h" #include "mysqlshdk/scripting/polyglot/polyglot_wrappers/types_polyglot.h" #include "mysqlshdk/scripting/polyglot/utils/graalvm_exceptions.h" #include "mysqlshdk/scripting/polyglot/utils/polyglot_error.h" #include "mysqlshdk/scripting/polyglot/utils/polyglot_store.h" namespace shcore { namespace polyglot { class ICollectable; class Polyglot_language; /** * Allows creating a temporary global variable with a random name which will * only live until the execution context of the instance of this class ends. * * The reason this is needed is because in graal, non primitive values * originating from guest languages (like JS) are not sharable, this means: * * - A guest language specific object is not mappable to C++ equivalent, i.e. * (i.e. undefined, ArrayBuffer) * - There's no way we can generate a guest language specific object from C++ * (i.e. undefined, ArrayBuffer or Exception Objects) * * The way this problem is addressed, is by creating a temporary object in the * global context, to create the object in the guest language and then if * needed, storing it to create a reference so the object can be deleted from * the main context (to avoid pollution) without actually losing the object, * then the reference is provided to the guest language. * * Additional Information: * https://github.com/oracle/graaljs/issues/70#issuecomment-436257058 * https://github.com/oracle/graal/blob/master/sdk/CHANGELOG.md#version-10-rc9 */ class Scoped_global final { public: Scoped_global() = delete; // Generates a random name for the scoped value, if value is provided, it will // be set as the global value, otherwise just a random name is created to be // used in the execute function. Scoped_global(const Polyglot_language *language, poly_value value = nullptr); Scoped_global(const Scoped_global &) = delete; Scoped_global &operator=(const Scoped_global &) = delete; Scoped_global(Scoped_global &&) = delete; Scoped_global &operator=(Scoped_global &&) = delete; // Executes some code referencing the scoped value, the string <<global>> // should be used in the text to refer to the created global (internally // <<global>> will be replaced with the generated value), returns the result // of the code execution poly_value execute(const std::string &code); ~Scoped_global(); private: const Polyglot_language *m_language; std::string m_name; }; /** * The polyglot library may support several languages (guest languages), and * provides out of the box C++ interfaces to handle common language elements, * such as Objects, Maps, Arrays, Functions and primitive data types. * * However, it does not provide out of the box C++ interface for guest language * non primitive data types or specific objects. * * This class is to be used as the base class to handle the common language * elements supported in the polyglot library, any guest language supported by * the shell is meant to extend this class and add handling of the elements that * are specific to it. */ class Polyglot_language : public std::enable_shared_from_this<Polyglot_language> { public: class Script_scope final { public: Script_scope() = delete; Script_scope(Polyglot_language *owner, const std::string &s) : m_owner(owner) { assert(m_owner); m_owner->m_current_script.push(s); } Script_scope(const Script_scope &) = delete; Script_scope(Script_scope &&) = default; Script_scope &operator=(const Script_scope &) = delete; Script_scope &operator=(Script_scope &&) = default; ~Script_scope() { m_owner->m_current_script.pop(); } private: Polyglot_language *m_owner = nullptr; }; class Current_script { public: Current_script(); Current_script(const Current_script &) = delete; Current_script &operator=(const Current_script &) = delete; Current_script(Current_script &&) = delete; Current_script &operator=(Current_script &&) = delete; void push(const std::string &script); void pop() { m_scripts.pop(); } std::string current_script() const { return m_scripts.top(); } std::string current_folder() const; private: std::string m_root; std::stack<std::string> m_scripts; }; explicit Polyglot_language(Polyglot_common_context *common_context, const std::string &debug_port = ""); Polyglot_language(const Polyglot_language &) = delete; Polyglot_language &operator=(const Polyglot_language &) = delete; Polyglot_language(Polyglot_language &&) = delete; Polyglot_language &operator=(Polyglot_language &&) = delete; virtual ~Polyglot_language() = default; virtual const char *get_language_id() const = 0; virtual NamingStyle naming_style() const = 0; virtual void init_context_builder(); virtual void initialize(const std::shared_ptr<IFile_system> &fs = {}); virtual void finalize(); /** * The polyglot_utils file has the following functions that can be used to * export C++ functions as polyglot global functions: * * - polyglot_handler* * - native_handler* * * These wrappers allow exposing class functions as long as the class * containing them contains a language() function that returns a pointer to a * Polyglot language. * * This function, basically enables subclasses to expose functions as polyglot * globals. */ Polyglot_language *language() { return this; } /** * Throws a specific exception type in the GraalVM environment, with a * specific message */ void throw_graalvm_exception(const Graalvm_exception &exception); // These functions represent operations than have specific handling depending // on the guest language, for that reason only the interface is defined here // and the subclasses provide specific language handling. /** * Return the a guest language interpretation of the Undefined C++ value. */ virtual poly_value undefined() const = 0; /** * Returns true if the provided value is undefined in the guest language. */ virtual bool is_undefined(poly_value value) const = 0; /** * Return the guest language interpretation of a binary string. */ virtual poly_value array_buffer(const std::string &data) const = 0; /** * Return true if the guest language identifies the value as an object */ virtual bool is_object(poly_value object, std::string *class_name = nullptr) const = 0; /** * Converts a C++ object into a representation of the object in the guest * language */ virtual poly_value from_native_object( const Object_bridge_ref &object) const = 0; /** * Converts a guest language object into its C++ representation */ virtual shcore::Value to_native_object(poly_value object, const std::string &class_name); /** * Translates a dictionary object into an exception in the guest language. */ void throw_exception_object(const shcore::Dictionary_t &data) const; /** * Translates a Polyglot_error into an exception in the guest language. */ void throw_exception_object(const Polyglot_error &data) const; /** * Retrieves the list of keywords in the guest language */ virtual const std::vector<std::string> &keywords() const = 0; /** * Wraps a call to poly_context_eval */ int64_t eval(const std::string &source, const std::string &code_str, poly_value *result) const; poly_value create_source(const std::string &path) const; /** * Debugs the given script */ std::pair<Value, bool> debug(const std::string &path); /** * Executes the given script */ std::pair<Value, bool> execute(const std::string &code_str, const std::string &source); /** * This function should be implemented to provide plugin load support */ virtual bool load_plugin([[maybe_unused]] const std::string &path) { throw std::runtime_error("Plugin support is not available."); } /** * Creates a copy of the global context */ poly_context copy_global_context() const; poly_value poly_string(const std::string &data) const; std::string to_string(poly_value obj) const; poly_context context() const; poly_thread thread() const; const std::shared_ptr<Polyglot_object> &globals() const; poly_reference store(poly_value value); void erase(poly_reference value); void set_global(const std::string &name, const Value &value) const; void set_global(const std::string &name, poly_value value) const; void set_global_function(const std::string &name, poly_callback callback, void *data = nullptr); Store get_global(const std::string &name) const; void terminate(); bool is_terminating() const; void clear_is_terminating(); Script_scope enter_script(const std::string &s); Value convert(poly_value value) const; poly_value convert(const Value &value) const; Argument_list convert_args(const std::vector<poly_value> &args) const; poly_value type_info(poly_value value) const; bool get_member(poly_value object, const char *name, poly_value *member) const; std::string current_script_folder() const; Polyglot_common_context *common_context() { return m_common_context; } protected: poly_value wrap_callback(poly_callback callback, void *data) const; Polyglot_common_context *m_common_context; poly_thread m_thread = nullptr; poly_context_builder m_context_builder = nullptr; Store m_context; std::unique_ptr<Polyglot_type_bridger> m_types; std::shared_ptr<Polyglot_object> m_globals; Current_script m_current_script; std::unique_ptr<Polyglot_storage> m_storage; std::shared_ptr<IFile_system> m_file_system; protected: std::string m_debug_port; private: std::unique_ptr<Polyglot_scope> m_scope; bool m_terminating = false; void set_file_system(); void throw_exception_object(poly_value exception) const; static void output_callback(const char *bytes, size_t length, void *data); static void error_callback(const char *bytes, size_t length, void *data); virtual poly_value create_exception_object( const std::string &error, poly_value exception_object) const = 0; virtual void output_handler(const char *bytes, size_t length) = 0; virtual void error_handler(const char *bytes, size_t length) = 0; void enable_debug(); virtual std::optional<std::string> get_debug_port() const { return {}; } }; } // namespace polyglot } // namespace shcore #endif // MYSQLSHDK_SCRIPTING_POLYGLOT_LANGUAGES_POLYGLOT_LANGUAGE_H_