#include "config.h"
#include "JSCOptions.h"
#include "Options.h"
#include <glib/gi18n-lib.h>
#include <wtf/Vector.h>
#include <wtf/glib/GUniquePtr.h>
using namespace JSC;
using int32 = int32_t;
using size = size_t;
static bool valueFromGValue(const GValue* gValue, bool& value)
{
value = g_value_get_boolean(gValue);
return true;
}
static void valueToGValue(bool value, GValue* gValue)
{
g_value_set_boolean(gValue, value);
}
static bool valueFromGValue(const GValue* gValue, int32_t& value)
{
value = g_value_get_int(gValue);
return true;
}
static void valueToGValue(int32_t value, GValue* gValue)
{
g_value_set_int(gValue, value);
}
#if CPU(ADDRESS64)
static bool valueFromGValue(const GValue* gValue, unsigned& value)
{
value = g_value_get_uint(gValue);
return true;
}
static void valueToGValue(unsigned value, GValue* gValue)
{
g_value_set_uint(gValue, value);
}
#endif
static bool valueFromGValue(const GValue* gValue, size_t& value)
{
value = GPOINTER_TO_SIZE(g_value_get_pointer(gValue));
return true;
}
static void valueToGValue(size_t value, GValue* gValue)
{
g_value_set_pointer(gValue, GSIZE_TO_POINTER(value));
}
static bool valueFromGValue(const GValue* gValue, const char*& value)
{
value = g_value_dup_string(gValue);
return true;
}
static void valueToGValue(const char* value, GValue* gValue)
{
g_value_set_string(gValue, value);
}
static bool valueFromGValue(const GValue* gValue, double& value)
{
value = g_value_get_double(gValue);
return true;
}
static void valueToGValue(double value, GValue* gValue)
{
g_value_set_double(gValue, value);
}
static bool valueFromGValue(const GValue* gValue, OptionRange& value)
{
return value.init(g_value_get_string(gValue) ? g_value_get_string(gValue) : "<null>");
}
static void valueToGValue(const OptionRange& value, GValue* gValue)
{
const char* rangeString = value.rangeString();
g_value_set_string(gValue, !g_strcmp0(rangeString, "<null>") ? nullptr : rangeString);
}
static bool valueFromGValue(const GValue* gValue, GCLogging::Level& value)
{
switch (g_value_get_uint(gValue)) {
case 0:
value = GCLogging::Level::None;
return true;
case 1:
value = GCLogging::Level::Basic;
return true;
case 2:
value = GCLogging::Level::Verbose;
return true;
default:
break;
}
return false;
}
static void valueToGValue(GCLogging::Level value, GValue* gValue)
{
switch (value) {
case GCLogging::Level::None:
g_value_set_uint(gValue, 0);
break;
case GCLogging::Level::Basic:
g_value_set_uint(gValue, 1);
break;
case GCLogging::Level::Verbose:
g_value_set_uint(gValue, 2);
break;
}
}
static gboolean jscOptionsSetValue(const char* option, const GValue* value)
{
#define SET_OPTION_VALUE(type_, name_, defaultValue_, availability_, description_) \
if (!g_strcmp0(#name_, option)) { \
OptionsStorage::type_ valueToSet; \
if (!valueFromGValue(value, valueToSet)) \
return FALSE; \
Options::name_() = valueToSet; \
return TRUE; \
}
Options::initialize();
FOR_EACH_JSC_OPTION(SET_OPTION_VALUE)
#undef SET_OPTION_VALUE
return FALSE;
}
static gboolean jscOptionsGetValue(const char* option, GValue* value)
{
#define GET_OPTION_VALUE(type_, name_, defaultValue_, availability_, description_) \
if (!g_strcmp0(#name_, option)) { \
OptionsStorage::type_ valueToGet = Options::name_(); \
valueToGValue(valueToGet, value); \
return TRUE; \
}
Options::initialize();
FOR_EACH_JSC_OPTION(GET_OPTION_VALUE)
#undef GET_OPTION_VALUE
return FALSE;
}
gboolean jsc_options_set_boolean(const char* option, gboolean value)
{
g_return_val_if_fail(option, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_BOOLEAN);
g_value_set_boolean(&gValue, value);
return jscOptionsSetValue(option, &gValue);
}
gboolean jsc_options_get_boolean(const char* option, gboolean* value)
{
g_return_val_if_fail(option, FALSE);
g_return_val_if_fail(value, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_BOOLEAN);
if (!jscOptionsGetValue(option, &gValue))
return FALSE;
*value = g_value_get_boolean(&gValue);
return TRUE;
}
gboolean jsc_options_set_int(const char* option, gint value)
{
g_return_val_if_fail(option, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_INT);
g_value_set_int(&gValue, value);
return jscOptionsSetValue(option, &gValue);
}
gboolean jsc_options_get_int(const char* option, gint* value)
{
g_return_val_if_fail(option, FALSE);
g_return_val_if_fail(value, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_INT);
if (!jscOptionsGetValue(option, &gValue))
return FALSE;
*value = g_value_get_int(&gValue);
return TRUE;
}
gboolean jsc_options_set_uint(const char* option, guint value)
{
g_return_val_if_fail(option, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_UINT);
g_value_set_uint(&gValue, value);
return jscOptionsSetValue(option, &gValue);
}
gboolean jsc_options_get_uint(const char* option, guint* value)
{
g_return_val_if_fail(option, FALSE);
g_return_val_if_fail(value, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_UINT);
if (!jscOptionsGetValue(option, &gValue))
return FALSE;
*value = g_value_get_uint(&gValue);
return TRUE;
}
gboolean jsc_options_set_size(const char* option, gsize value)
{
g_return_val_if_fail(option, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_POINTER);
g_value_set_pointer(&gValue, GSIZE_TO_POINTER(value));
return jscOptionsSetValue(option, &gValue);
}
gboolean jsc_options_get_size(const char* option, gsize* value)
{
g_return_val_if_fail(option, FALSE);
g_return_val_if_fail(value, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_POINTER);
if (!jscOptionsGetValue(option, &gValue))
return FALSE;
*value = GPOINTER_TO_SIZE(g_value_get_pointer(&gValue));
return TRUE;
}
gboolean jsc_options_set_double(const char* option, gdouble value)
{
g_return_val_if_fail(option, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_DOUBLE);
g_value_set_double(&gValue, value);
return jscOptionsSetValue(option, &gValue);
}
gboolean jsc_options_get_double(const char* option, gdouble* value)
{
g_return_val_if_fail(option, FALSE);
g_return_val_if_fail(value, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_DOUBLE);
if (!jscOptionsGetValue(option, &gValue))
return FALSE;
*value = g_value_get_double(&gValue);
return TRUE;
}
gboolean jsc_options_set_string(const char* option, const char* value)
{
g_return_val_if_fail(option, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_STRING);
g_value_set_string(&gValue, value);
bool success = jscOptionsSetValue(option, &gValue);
g_value_unset(&gValue);
return success;
}
gboolean jsc_options_get_string(const char* option, char** value)
{
g_return_val_if_fail(option, FALSE);
g_return_val_if_fail(value, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_STRING);
if (!jscOptionsGetValue(option, &gValue))
return FALSE;
*value = g_value_dup_string(&gValue);
g_value_unset(&gValue);
return TRUE;
}
gboolean jsc_options_set_range_string(const char* option, const char* value)
{
g_return_val_if_fail(option, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_STRING);
g_value_set_string(&gValue, value);
bool success = jscOptionsSetValue(option, &gValue);
g_value_unset(&gValue);
return success;
}
gboolean jsc_options_get_range_string(const char* option, char** value)
{
g_return_val_if_fail(option, FALSE);
g_return_val_if_fail(value, FALSE);
GValue gValue = G_VALUE_INIT;
g_value_init(&gValue, G_TYPE_STRING);
if (!jscOptionsGetValue(option, &gValue))
return FALSE;
*value = g_value_dup_string(&gValue);
g_value_unset(&gValue);
return TRUE;
}
static JSCOptionType jscOptionsType(bool)
{
return JSC_OPTION_BOOLEAN;
}
static JSCOptionType jscOptionsType(int)
{
return JSC_OPTION_INT;
}
#if CPU(ADDRESS64)
static JSCOptionType jscOptionsType(unsigned)
{
return JSC_OPTION_UINT;
}
#endif
static JSCOptionType jscOptionsType(size_t)
{
return JSC_OPTION_SIZE;
}
static JSCOptionType jscOptionsType(double)
{
return JSC_OPTION_DOUBLE;
}
static JSCOptionType jscOptionsType(const char*)
{
return JSC_OPTION_STRING;
}
static JSCOptionType jscOptionsType(const OptionRange&)
{
return JSC_OPTION_RANGE_STRING;
}
void jsc_options_foreach(JSCOptionsFunc function, gpointer userData)
{
g_return_if_fail(function);
#define VISIT_OPTION(type_, name_, defaultValue_, availability_, description_) \
if (Options::Availability::availability_ == Options::Availability::Normal \
|| Options::isAvailable(Options::name_##ID, Options::Availability::availability_)) { \
OptionsStorage::type_ defaultValue { }; \
auto optionType = jscOptionsType(defaultValue); \
if (function (#name_, optionType, description_, userData)) \
return; \
}
Options::initialize();
FOR_EACH_JSC_OPTION(VISIT_OPTION)
#undef VISIT_OPTION
}
static gboolean setOptionEntry(const char* optionNameFull, const char* value, gpointer, GError** error)
{
const char* optionName = optionNameFull + 6; GUniquePtr<char> option(g_strdup_printf("%s=%s", optionName, value));
if (!Options::setOption(option.get())) {
g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed parse value '%s' for %s", value, optionNameFull);
return FALSE;
}
return TRUE;
}
GOptionGroup* jsc_options_get_option_group(void)
{
auto* names = new Vector<GUniquePtr<char>>;
GOptionGroup* group = g_option_group_new("jsc", _("JSC Options"), _("Show JSC Options"), names, [] (gpointer data) {
delete static_cast<Vector<GUniquePtr<char>>*>(data);
});
g_option_group_set_translation_domain(group, GETTEXT_PACKAGE);
GArray* entries = g_array_new(TRUE, TRUE, sizeof(GOptionEntry));
#define REGISTER_OPTION(type_, name_, defaultValue_, availability_, description_) \
if (Options::Availability::availability_ == Options::Availability::Normal \
|| Options::isAvailable(Options::name_##ID, Options::Availability::availability_)) { \
GUniquePtr<char> name(g_strdup_printf("jsc-%s", #name_)); \
entries = g_array_set_size(entries, entries->len + 1); \
GOptionEntry* entry = &g_array_index(entries, GOptionEntry, entries->len - 1); \
entry->long_name = name.get(); \
entry->arg = G_OPTION_ARG_CALLBACK; \
entry->arg_data = reinterpret_cast<gpointer>(setOptionEntry); \
entry->description = description_; \
names->append(WTFMove(name)); \
}
Options::initialize();
FOR_EACH_JSC_OPTION(REGISTER_OPTION)
#undef REGISTER_OPTION
g_option_group_add_entries(group, reinterpret_cast<GOptionEntry*>(entries->data));
return group;
}