mysqlshdk/scripting/polyglot/utils/polyglot_utils.h (171 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_UTILS_POLYGLOT_UTILS_H_
#define MYSQLSHDK_SCRIPTING_POLYGLOT_UTILS_POLYGLOT_UTILS_H_
#include <cassert>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include "mysqlshdk/scripting/polyglot/utils/polyglot_api_clean.h"
#include "mysqlshdk/include/scripting/types.h"
#include "mysqlshdk/scripting/polyglot/native_wrappers/polyglot_collectable.h"
#include "mysqlshdk/scripting/polyglot/utils/polyglot_error.h"
namespace shcore {
namespace polyglot {
enum class Language { JAVASCRIPT };
inline const char *k_origin_shell{"(shell)"};
inline const char *k_syntax_error{"SyntaxError"};
inline const char *k_interrupted_error{"InterruptedError"};
/**
* Returns the collectable and arguments sent by Polyglot on a call to a C++
* function.
*
* This function is meant to be used by a C++ function that does not require to
* validate the argument count.
*/
size_t parse_callback_args(poly_thread thread, poly_callback_info args,
std::vector<poly_value> *argv, void **data);
/**
* Returns the collectable sent by Polyglot on a call to a C++ function.
*
* This function is meant to be used in C++ functions that have no arguments.
*
* It also centralizes a validation to ensure no arguments are sent by Polyglot.
*/
bool get_data(poly_thread thread, poly_callback_info args,
std::string_view name, void **data);
/**
* Returns the collectable and arguments sent by Polyglot on a call to a C++
* function.
*
* This function is meant to be used in C++ functions that expect a specific
* number of arguments since it centralizes a validation to ensure the expected
* arguments were received.
*/
bool get_args_and_data(poly_thread thread, poly_callback_info args,
std::string_view name, void **data, size_t expected_argc,
std::vector<poly_value> *argv);
/**
* Identifies if a given poly_value corresponds to a wrapped C++ element.
*/
bool is_native_type(poly_thread thread, poly_value value, Collectable_type type,
void **native_data = nullptr);
/**
* Retrieves the names of the members of the given poly_value.
*/
std::vector<std::string> get_member_keys(poly_thread thread,
poly_context context,
poly_value object);
/**
* Converts a string into a polyglot string
*/
poly_value poly_string(poly_thread thread, poly_context context,
std::string_view data);
/**
* Converts a polyglot string into a C++ string
*/
std::string to_string(poly_thread thread, poly_value obj);
/**
* Converts a polyglot string into a C++ int64_t
*/
int64_t to_int(poly_thread thread, poly_value obj);
/**
* Converts a polyglot value into a C++ double
*/
double to_double(poly_thread thread, poly_value obj);
/**
* Converts a polyglot value into a C++ boolean
*/
bool to_boolean(poly_thread thread, poly_value obj);
/**
* Converts a bool into a polyglot boolean
*/
poly_value poly_bool(poly_thread thread, poly_context context, bool value);
/**
* Converts a bool into a polyglot boolean
*/
poly_value poly_uint(poly_thread thread, poly_context context, uint64_t value);
/**
* Converts a bool into a polyglot boolean
*/
poly_value poly_int(poly_thread thread, poly_context context, int64_t value);
/**
* Converts a double into a polyglot double
*/
poly_value poly_double(poly_thread thread, poly_context context, double value);
/**
* Returns a polyglot null value
*/
poly_value poly_null(poly_thread thread, poly_context context);
/**
* Converts an array of poly_values into a polyglot array
*/
poly_value poly_array(poly_thread thread, poly_context context,
const std::vector<poly_value> &values);
/**
* Retrieves a member identified with name from the given object
*/
poly_value get_member(poly_thread thread, poly_value object,
const std::string &name);
/**
* Returns true if the given value is executable
*/
bool is_executable(poly_thread thread, poly_value object);
/**
* The integration of the PolyglotAPI is mostly centered in the registration of
* C++ callbacks to implement the functionality for the different proxy objects.
* Whenever an exception is reported from any of these callbacks, a
* CallbackException object is created in the PolyglotAPI.
*
* This function is to report a callback exception from C++.
*/
void throw_callback_exception(poly_thread thread, const char *error);
/**
* Generic handler to be used with functions that interact with the polyglot
* library, this is:
*
* - Require the language instance for the execution
* - Receive a vector of polyglot values as arguments
* - Return a polyglot value as result
*/
template <typename Target, typename Config>
static poly_value polyglot_handler_fixed_args(poly_thread thread,
poly_callback_info args) {
std::vector<poly_value> argv;
void *data = nullptr;
poly_value value = nullptr;
try {
if (get_args_and_data(thread, args, Config::name, &data, Config::argc,
&argv)) {
assert(data);
const auto instance = static_cast<Target *>(data);
const auto language = instance->language();
try {
value = (instance->*Config::callback)(argv);
} catch (const Exception &exc) {
language->throw_exception_object(exc.error());
} catch (const Polyglot_error &exc) {
language->throw_exception_object(exc);
}
}
} catch (const std::exception &e) {
throw_callback_exception(thread, e.what());
}
return value;
}
/**
* Generic handler to be used with pure native functions, no interaction with
* the polyglot library is done:
*
* - Receive a vector of Values as arguments
* - Return a Value as result
*/
template <typename Target, typename Config>
static poly_value native_handler_fixed_args(poly_thread thread,
poly_callback_info args) {
std::vector<poly_value> argv;
void *data = nullptr;
poly_value value = nullptr;
try {
if (get_args_and_data(thread, args, Config::name, &data, Config::argc,
&argv)) {
assert(data);
const auto instance = static_cast<Target *>(data);
const auto language = instance->language();
try {
value = language->convert(
(instance->*Config::callback)(language->convert_args(argv)));
} catch (const Exception &exc) {
language->throw_exception_object(exc.error());
} catch (const Polyglot_error &exc) {
language->throw_exception_object(exc);
}
}
} catch (const std::exception &e) {
throw_callback_exception(thread, e.what());
}
return value;
}
/**
* Generic handler to be used with functions that interact with the polyglot
* library:
*
* - Receive no arguments
* - Return a poly_value as result
*/
template <typename Target, typename Config>
static poly_value polyglot_handler_no_args(poly_thread thread,
poly_callback_info args) {
void *data = nullptr;
poly_value value = nullptr;
if (get_data(thread, args, Config::name, &data)) {
assert(data);
const auto instance = static_cast<Target *>(data);
const auto language = instance->language();
try {
value = (instance->*Config::callback)();
} catch (const Exception &exc) {
language->throw_exception_object(exc.error());
} catch (const Polyglot_error &exc) {
language->throw_exception_object(exc);
} catch (const std::exception &e) {
throw_callback_exception(thread, e.what());
}
}
return value;
}
/**
* Generic handler to be used with pure native functions, no interaction with
* the polyglot library is done:
*
* - Receive no arguments
* - Return a Value as result
*/
template <typename Target, typename Config>
static poly_value native_handler_no_args(poly_thread thread,
poly_callback_info args) {
void *data = nullptr;
poly_value value = nullptr;
if (get_data(thread, args, Config::name, &data)) {
assert(data);
const auto instance = static_cast<Target *>(data);
const auto language = instance->language();
try {
value = language->convert((instance->*Config::callback)());
} catch (const Exception &exc) {
language->throw_exception_object(exc.error());
} catch (const Polyglot_error &exc) {
language->throw_exception_object(exc);
} catch (const std::exception &e) {
throw_callback_exception(thread, e.what());
}
}
return value;
}
/**
* Generic handler to be used with pure native functions, no interaction with
* the polyglot library is done:
*
* - Receive a vector of Values as arguments
* - Return a Value as result
*/
template <typename Target, typename Config>
static poly_value native_handler_variable_args(poly_thread thread,
poly_callback_info args) {
std::vector<poly_value> argv;
void *data = nullptr;
poly_value value = nullptr;
try {
parse_callback_args(thread, args, &argv, &data);
assert(data);
const auto instance = static_cast<Target *>(data);
const auto language = instance->language();
try {
value = language->convert(
(instance->*Config::callback)(language->convert_args(argv)));
} catch (const Exception &exc) {
language->throw_exception_object(exc.error());
} catch (const Polyglot_error &exc) {
language->throw_exception_object(exc);
}
} catch (const std::exception &e) {
throw_callback_exception(thread, e.what());
}
return value;
}
bool get_member(poly_thread thread, poly_value object, const char *name,
poly_value *member);
bool is_object(poly_thread thread, poly_value object, std::string *class_name);
} // namespace polyglot
} // namespace shcore
#endif // MYSQLSHDK_SCRIPTING_POLYGLOT_UTILS_POLYGLOT_UTILS_H_