efl/src/bin/eolian_js/main.cc

1197 lines
46 KiB
C++

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Eolian.h>
#include <Eina.hh>
#include <eolian/js/domain.hh>
#include <eolian/js/format.hh>
#include <eolian/class.hh>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include <sstream>
#include <stdexcept>
#include <libgen.h>
#include <getopt.h>
#include <cstdlib>
#include <vector>
#include <set>
namespace eolian { namespace js {
efl::eina::log_domain domain("eolian_js");
struct incomplete_complex_type_error : public std::exception
{
explicit incomplete_complex_type_error(std::string const& msg_arg)
: msg(msg_arg)
{}
virtual ~incomplete_complex_type_error() {}
virtual const char* what() const noexcept { return msg.c_str(); }
std::string msg;
};
} }
std::string
_lowercase(std::string str)
{
transform(begin(str), end(str), begin(str), tolower);
return str;
}
std::string
_uppercase(std::string str)
{
transform(begin(str), end(str), begin(str), toupper);
return str;
}
std::string
_class_name_getter(std::string const& caller_class_prefix, std::string class_name)
{
std::replace(class_name.begin(), class_name.end(), '.', '_');
return caller_class_prefix + "_" + class_name + "_cls_name_getter";
}
void
_final_type_and_type_type_get(Eolian_Type const* tp_in, Eolian_Type const*& tp_out, Eolian_Type_Type& tpt_out)
{
tp_out = tp_in;
tpt_out = eolian_type_type_get(tp_in);
if (tpt_out == EOLIAN_TYPE_REGULAR)
{
auto tpd = eolian_type_typedecl_get(tp_out);
if (tpd && eolian_typedecl_type_get(tpd) == EOLIAN_TYPEDECL_ALIAS &&
!eolian_typedecl_is_extern(tpd))
{
auto btp = eolian_typedecl_aliased_base_get(tpd);
if (btp && eolian_type_name_get(btp) &&
strcmp(eolian_type_name_get(btp), "__undefined_type") != 0)
{
_final_type_and_type_type_get(btp, tp_out, tpt_out);
}
}
}
}
std::string
_eolian_type_cpp_type_named_get(const Eolian_Type *tp, std::string const& caller_class_prefix, std::set<std::string>& need_name_getter, bool in_pointer = false)
{
const auto is_const = eolian_type_is_const(tp);
std::string result;
if(!in_pointer && (eolian_type_is_ptr(tp) || eolian_type_type_get(tp) == EOLIAN_TYPE_TERMINATED_ARRAY
|| eolian_type_type_get(tp) == EOLIAN_TYPE_STATIC_ARRAY))
// else if (tpt == EOLIAN_TYPE_POINTER)
{
// auto btp = eolian_type_base_type_get(tp);
auto btp = eolian_type_type_get(tp) == EOLIAN_TYPE_TERMINATED_ARRAY
|| eolian_type_type_get(tp) == EOLIAN_TYPE_STATIC_ARRAY
? eolian_type_base_type_get(tp)
: tp;
result += _eolian_type_cpp_type_named_get(btp, caller_class_prefix, need_name_getter
, eolian_type_is_ptr(tp));
const auto base_is_const = eolian_type_is_const(btp);
Eolian_Type_Type btpt = EOLIAN_TYPE_UNKNOWN_TYPE;
_final_type_and_type_type_get(btp, btp, btpt);
auto btpd = eolian_type_typedecl_get(btp);
if (btpd && eolian_typedecl_type_get(btpd) == EOLIAN_TYPEDECL_STRUCT)
{
std::string f = "::make_struct_tag";
auto p = result.find(f);
if (p == std::string::npos)
throw std::runtime_error("missing struct type tag");
result.replace(p, f.size(), "::make_struct_ptr_tag");
result.pop_back();
result += " *";
if (is_const) result += " const";
result += ">";
}
else
{
// if (btpt != EOLIAN_TYPE_POINTER || base_is_const)
// result += ' ';
result += '*';
if (is_const) result += " const";
}
}
else
{
Eolian_Type_Type tpt = EOLIAN_TYPE_UNKNOWN_TYPE;
_final_type_and_type_type_get(tp, tp, tpt);
if (tpt == EOLIAN_TYPE_UNKNOWN_TYPE || tpt == EOLIAN_TYPE_UNDEFINED)
return "error";
if ((tpt == EOLIAN_TYPE_VOID
|| tpt == EOLIAN_TYPE_REGULAR
|| tpt == EOLIAN_TYPE_COMPLEX
|| tpt == EOLIAN_TYPE_CLASS)
&& is_const)
{
result += "const ";
}
if (tpt == EOLIAN_TYPE_REGULAR
|| tpt == EOLIAN_TYPE_COMPLEX
|| tpt == EOLIAN_TYPE_CLASS)
{
for (efl::eina::iterator<const char> first(::eolian_type_namespaces_get(tp)), last; first != last; ++first)
{
std::string np(&*first);
result += np + "_"; // TODO: transform it to the C++ equivalent?
}
// this comes from ctypes at eo_lexer.c and KEYWORDS at eo_lexer.h
const static std::unordered_map<std::string, std::string> type_map = {
{"byte", "signed char"},
{"ubyte", "unsigned char"},
{"char", "char"},
{"short", "short"},
{"ushort", "unsigned short"},
{"int", "int"},
{"uint", "unsigned int"},
{"long", "long"},
{"ulong", "unsigned long"},
{"llong", "long long"},
{"ullong", "unsigned long long"},
{"int8", "int8_t"},
{"uint8", "uint8_t"},
{"int16", "int16_t"},
{"uint16", "uint16_t"},
{"int32", "int32_t"},
{"uint32", "uint32_t"},
{"int64", "int64_t"},
{"uint64", "uint64_t"},
{"int128", "int128_t"},
{"uint128", "uint128_t"},
{"size", "size_t"},
{"ssize", "ssize_t"},
{"intptr", "intptr_t"},
{"uintptr", "uintptr_t"},
{"ptrdiff", "ptrdiff_t"},
{"time", "time_t"},
{"float", "float"},
{"double", "double"},
{"bool", "Eina_Bool"},
{"void", "void"},
{"generic_value", "Eina_Value"},
{"accessor", "Eina_Accessor"},
{"array", "Eina_Array"},
{"iterator", "Eina_Iterator"},
{"hash", "Eina_Hash"},
{"list", "Eina_List"},
{"string", "const char*"},
{"void_ptr", "void *"},
{"stringshare", "Eina_Stringshare*"}
};
std::string type_name = eolian_type_short_name_get(tp);
auto it = type_map.find(type_name);
if (it != end(type_map))
type_name = it->second;
result += type_name;
if (tpt == EOLIAN_TYPE_CLASS || tpt == EOLIAN_TYPE_COMPLEX)
result += "*"; // Implied pointer
auto tpd = eolian_type_typedecl_get(tp);
if (tpd && eolian_typedecl_type_get(tpd) == EOLIAN_TYPEDECL_STRUCT)
{
result = "efl::eina::js::make_struct_tag<" + result + ">";
}
if (tpt == EOLIAN_TYPE_COMPLEX)
{
result = "efl::eina::js::make_complex_tag<" + result;
bool has_subtypes = false;
const Eolian_Type *subtype = eolian_type_base_type_get(tp);
while (subtype)
{
auto t = _eolian_type_cpp_type_named_get(subtype, caller_class_prefix, need_name_getter);
auto k = type_class_name(subtype);
if (!k.empty())
{
result += ", " + t + ", " + _class_name_getter(caller_class_prefix, k);
need_name_getter.insert(k);
}
else
{
result += ", " + t + ", ::efl::eina::js::nonclass_cls_name_getter";
}
has_subtypes = true;
subtype = eolian_type_next_type_get(subtype);
}
if (!has_subtypes)
throw eolian::js::incomplete_complex_type_error("Incomplete complex type");
result += ">";
}
}
else if (tpt == EOLIAN_TYPE_VOID)
result += "void";
// else if(tpt == EOLIAN_TYPE_STATIC_ARRAY)
// {
// result += "void";
// }
// else if(tpt == EOLIAN_TYPE_TERMINATED_ARRAY)
// {
// result += "void";
// }
}
// else
// {
// throw std::runtime_error("unhandled Eolian_Type_Type value");
// }
/*if (!name.empty())
{
if (tpt != EOLIAN_TYPE_POINTER)
result += ' ';
result += name;
}*/
return result;
}
using ParametersIterator = efl::eina::iterator<const ::Eolian_Function_Parameter>;
std::vector<const ::Eolian_Function_Parameter*>
_eolian_function_keys_get(const Eolian_Function *function_id, Eolian_Function_Type ftype)
{
std::vector<const ::Eolian_Function_Parameter*> keys;
for(ParametersIterator it(::eolian_property_keys_get(function_id, ftype)), last; it != last; ++it)
keys.push_back(&*it);
return keys;
}
std::vector<const ::Eolian_Function_Parameter*>
_eolian_function_parameters_get(const Eolian_Function *function_id, Eolian_Function_Type function_type)
{
std::vector<const ::Eolian_Function_Parameter*> parameters;
ParametersIterator it { (function_type == EOLIAN_METHOD) ?
::eolian_function_parameters_get(function_id) :
::eolian_property_values_get(function_id, function_type)
}, last;
for(; it != last; ++it)
parameters.push_back(&*it);
return parameters;
}
bool
_function_return_is_missing(Eolian_Function const* func, Eolian_Function_Type func_type)
{
// XXX This function shouldn't exist. Eolian should
// forge functions a priori. Bindings generators
// shouldn't be required to convert such thing.
Eolian_Type const* type =
::eolian_function_return_type_get(func, func_type);
return !type;
}
bool
_type_is_generatable(const Eolian_Type *tp, bool add_pointer, Eolian_C_Type_Type c_type_type)
{
std::string c_type = eolian_type_c_type_get(tp, c_type_type);
if (add_pointer)
c_type += " *";
return c_type.find("void *") == std::string::npos;
}
bool
_function_belongs_to(const Eolian_Function *function, std::string klass)
{
const Eolian_Class *cl = eolian_function_class_get(function);
const std::string name = cl ? eolian_class_name_get(cl) : "";
return name.find(klass) == 0;
}
bool
_function_is_generatable(const Eolian_Function *function, Eolian_Function_Type ftp)
{
const auto key_params = _eolian_function_keys_get(function, ftp);
const auto parameters = _eolian_function_parameters_get(function, ftp);
std::vector<const ::Eolian_Function_Parameter*> full_params;
full_params.insert(std::end(full_params), std::begin(key_params), std::end(key_params));
full_params.insert(std::end(full_params), std::begin(parameters), std::end(parameters));
for (auto parameter : full_params)
{
auto tp = ::eolian_parameter_type_get(parameter);
bool add_pointer = eolian_parameter_direction_get(parameter) != EOLIAN_PARAMETER_IN;
if (!_type_is_generatable(tp, add_pointer, EOLIAN_C_TYPE_PARAM))
return false;
if (eolian_type_is_ptr(tp) && _function_belongs_to(function, "Efl.Object"))
return false;
}
auto rtp = ::eolian_function_return_type_get(function, ftp);
return rtp ? _type_is_generatable(rtp, false, EOLIAN_C_TYPE_RETURN) : true;
}
bool
_function_is_public(const Eolian_Function *function, Eolian_Function_Type t)
{
if (t == EOLIAN_PROPERTY)
return _function_is_public(function, EOLIAN_PROP_GET) || _function_is_public(function, EOLIAN_PROP_SET);
else
return eolian_function_scope_get(function, t) == EOLIAN_SCOPE_PUBLIC;
}
void separate_functions(Eolian_Class const* klass, Eolian_Function_Type t, bool ignore_constructors,
std::vector<Eolian_Function const*>& constructor_functions,
std::vector<Eolian_Function const*>& normal_functions)
{
efl::eina::iterator<Eolian_Function> first ( ::eolian_class_functions_get(klass, t) )
, last;
for(; first != last; ++first)
{
Eolian_Function const* function = &*first;
if (_function_is_public(function, t))
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << ::eolian_function_full_c_name_get(function, t, EINA_FALSE);
if(strcmp("elm_obj_entry_input_panel_imdata_get", ::eolian_function_full_c_name_get(function, t, EINA_FALSE)) != 0 &&
!eolian_function_is_beta(function) &&
// strcmp("data_callback", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("property", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("part_text_anchor_geometry_get", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("children_iterator_new", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("inputs_get", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("constructor", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("render_updates", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("efl_canvas_surface_x11_pixmap_set", ::eolian_function_full_c_name_get(function, t, EINA_FALSE)) != 0 && // TODO: remove this
strcmp("efl_canvas_surface_x11_pixmap_get", ::eolian_function_full_c_name_get(function, t, EINA_FALSE)) != 0 && // TODO: remove this
strcmp("efl_canvas_surface_native_buffer_set", ::eolian_function_full_c_name_get(function, t, EINA_FALSE)) != 0 && // TODO: remove this
strcmp("efl_canvas_surface_native_buffer_get", ::eolian_function_full_c_name_get(function, t, EINA_FALSE)) != 0 && // TODO: remove this
strcmp("event_callback_priority_add", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("event_callback_array_priority_add", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("event_callback_array_del", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("event_callback_call", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("event_callback_forwarder_add", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("event_callback_forwarder_del", ::eolian_function_name_get(function)) != 0 && // TODO: remove this
strcmp("event_callback_del", ::eolian_function_name_get(function)) != 0)
{
if( ::eolian_function_is_constructor(function, klass))
{
if(!ignore_constructors)
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "is a constructor";
constructor_functions.push_back(function);
}
else
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "ignoring parent's constructors";
}
}
else /*if( std::strcmp( ::eolian_function_full_c_name_get(function, t, EINA_FALSE)
, "eo_parent") != 0)*/
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "is a NOT constructor "
<< ::eolian_function_full_c_name_get(function, t, EINA_FALSE);
normal_functions.push_back(function);
}
// else
// {
// EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "parent_set as first constructor";
// constructor_functions.insert(constructor_functions.begin(), function);
// normal_functions.push_back(function);
// }
}
}
}
}
int main(int argc, char** argv)
{
namespace format = eolian::js::format;
std::vector<std::string> include_paths;
std::string out_file, in_file;
efl::eina::eina_init eina_init;
struct eolian_init
{
eolian_init() { ::eolian_init(); }
~eolian_init() { ::eolian_shutdown(); }
} eolian_init;
const struct option long_options[] =
{
{ "in", required_argument, 0, 'I' },
{ "out-file", required_argument, 0, 'o' },
{ "version", no_argument, 0, 'v' },
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
const char* options = "I:D:o:c:arvh";
// get command line options
int c, idx;
while ( (c = getopt_long(argc, argv, options, long_options, &idx)) != -1)
{
if (c == 'I')
{
include_paths.push_back(optarg);
}
else if (c == 'o')
{
if(!out_file.empty())
{
// _usage(argv[0]);
return 1;
}
out_file = optarg;
}
else if (c == 'h')
{
// _usage(argv[0]);
return 1;
}
else if (c == 'v')
{
// _print_version();
// if (argc == 2) exit(EXIT_SUCCESS);
}
}
if (optind == argc-1)
{
in_file = argv[optind];
}
// Add include paths to eolian library
for(auto src : include_paths)
if (!::eolian_state_directory_add(src.c_str()))
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain)
<< "Couldn't load eolian from '" << src << "'.";
}
if (!::eolian_state_all_eot_files_parse())
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain)
<< "Eolian failed parsing eot files";
assert(false && "Error parsing eot files");
}
if (!::eolian_state_file_parse(in_file.c_str()))
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain)
<< "Failed parsing: " << in_file << ".";
assert(false && "Error parsing input file");
}
// Create filename path for output
std::string file_basename;
const Eolian_Class *klass = NULL;
{
char* dup = strdup(in_file.c_str());
char *bn = basename(dup);
klass = ::eolian_class_get_by_file(NULL, bn);
file_basename = bn;
free(dup);
}
if(!klass)
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "could not find any class defined in this eo file";
return -1;
}
std::vector<Eolian_Function const*> constructor_functions;
std::vector<Eolian_Function const*> normal_functions;
std::set<Eolian_Class const*> classes;
// separate normal functions from constructors for all methods and properties
separate_functions(klass, EOLIAN_METHOD, false, constructor_functions, normal_functions);
separate_functions(klass, EOLIAN_PROPERTY, false, constructor_functions, normal_functions);
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "functions were separated";
// function to iterate through all inheritance class
std::function<void(Eolian_Class const*, std::function<void(Eolian_Class const*)>)>
recurse_inherits
= [&] (Eolian_Class const* klass, std::function<void(Eolian_Class const*)> function)
{
for(efl::eina::iterator<const char> first ( ::eolian_class_inherits_get(klass))
, last; first != last; ++first)
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << &*first << std::endl;
Eolian_Class const* base = ::eolian_class_get_by_name(NULL, &*first);
function(base);
recurse_inherits(base, function);
}
};
// save functions from all inhehritance
auto save_functions = [&](Eolian_Class const* klass)
{
if(classes.find(klass) == classes.end())
{
classes.insert(klass);
separate_functions(klass, EOLIAN_METHOD, true, constructor_functions, normal_functions);
separate_functions(klass, EOLIAN_PROPERTY, true, constructor_functions, normal_functions);
}
};
// save functions from all inheritance class without constructors
recurse_inherits(klass, save_functions);
EINA_CXX_DOM_LOG_DBG(eolian::js::domain) << "inherits were recursed";
std::ofstream os (out_file.c_str());
if(!os.is_open())
{
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "Couldn't open output file " << out_file;
return -1;
}
EINA_CXX_DOM_LOG_DBG(eolian::js::domain) << "output was opened";
std::string class_name(name(klass)),
class_full_name(full_name(klass)),
upper_case_class_name(_uppercase(class_name)),
lower_case_class_name(_lowercase(class_name));
// Start preamble generation
if (getenv("EFL_RUN_IN_TREE"))
{
os << "#ifdef HAVE_CONFIG_H\n";
os << "#include \"config.h\"\n";
os << "#endif\n";
os << "#include <Efl.h>\n";
os << "#include <Ecore.h>\n";
os << "#include <Eo.h>\n\n";
}
else
{
os << "#ifdef HAVE_CONFIG_H\n";
os << "#include \"elementary_config.h\"\n";
os << "#endif\n";
os << "#include <Efl.h>\n";
os << "#include <Ecore.h>\n";
os << "#include <Eo.h>\n";
os << "#include <Evas.h>\n";
os << "#include <Edje.h>\n";
os << "#include <Elementary.h>\n\n";
os << "extern \"C\" {\n";
os << "#include <elm_widget.h>\n";
os << "}\n\n";
}
os << "#include <Eina_Js.hh>\n\n";
os << "#include <Eo_Js.hh>\n\n";
os << "#ifdef EAPI\n";
os << "# undef EAPI\n";
os << "#endif\n";
os << "#ifdef _WIN32\n";
os << "# define EAPI __declspec(dllimport)\n";
os << "#else\n";
os << "# ifdef __GNUC__\n";
os << "# if __GNUC__ >= 4\n";
os << "# define EAPI __attribute__ ((visibility(\"default\")))\n";
os << "# else\n";
os << "# define EAPI\n";
os << "# endif\n";
os << "# else\n";
os << "# define EAPI\n";
os << "# endif\n";
os << "#endif /* ! _WIN32 */\n\n";
os << "extern \"C\" {\n";
// generate include for necessary headers
if(is_evas(klass))
os << "#include <Evas.h>\n";
auto includes_fun = [&os] (Eolian_Class const* klass)
{
os << "#include <" << eolian_class_file_get(klass) << ".h>\n\n";
};
// generate include for all inheritance
recurse_inherits(klass, includes_fun);
os << "#include <" << eolian_class_file_get(klass) << ".h>\n\n";
os << "}\n\n";
os << "#ifdef _WIN32\n";
os << "# undef EAPI\n";
os << "# define EAPI __declspec(dllexport)\n";
os << "#endif /* ! _WIN32 */\n\n";
os << "#include <array>\n\n";
EINA_CXX_DOM_LOG_DBG(eolian::js::domain) << "includes added";
// generate open namespaces
if(namespace_size(klass))
{
std::string space = "";
for(efl::eina::iterator<const char> first(::eolian_class_namespaces_get(klass)), last; first != last; ++first)
{
std::string lower(_lowercase(&*first));
os << "namespace " << lower << " {" << space;
space = " ";
}
os << "\n";
}
// generate event map
std::string event_map = class_name;
event_map += "_ev_info_map";
os << "namespace {\n";
os << "::efl::eo::js::event_information_map " << event_map << ";\n";
os << "}\n";
EINA_CXX_DOM_LOG_DBG(eolian::js::domain) << "namespace";
// save functions that need a name getter for structs
std::set<std::string> need_name_getter;
// generate all structs parsed in this file
std::stringstream structs_ss;
for (efl::eina::iterator<Eolian_Typedecl> first(::eolian_typedecl_structs_get_by_file(NULL, file_basename.c_str()))
, last; first != last; ++first)
{
std::stringstream ss;
bool should_reject_ref = file_basename == "efl_object.eo";
bool has_ref_field = false;
auto tpd = &*first;
if (!tpd || ::eolian_typedecl_type_get(tpd) == EOLIAN_TYPEDECL_STRUCT_OPAQUE)
continue;
auto struct_name = ::eolian_typedecl_short_name_get(tpd);
auto struct_type_full_name = ::eolian_typedecl_name_get(tpd);
if (!struct_name || !struct_type_full_name)
{
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Could not get struct type name";
continue;
}
else if(strcmp(struct_type_full_name, "Efl.Callback_Array_Item") == 0)
continue;
std::string struct_c_name = struct_type_full_name;
std::replace(struct_c_name.begin(), struct_c_name.end(), '.', '_');
ss << " {\n";
ss << " auto fields_func = [](v8::Isolate* isolate_, v8::Local<v8::ObjectTemplate> prototype_)\n";
ss << " {\n";
for (efl::eina::iterator<Eolian_Struct_Type_Field> sf(::eolian_typedecl_struct_fields_get(tpd))
, sf_end; sf != sf_end; ++sf)
{
auto field_type = ::eolian_typedecl_struct_field_type_get(&*sf);
auto field_name = ::eolian_typedecl_struct_field_name_get(&*sf);
if (!field_name)
{
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Could not get struct field name";
continue;
}
if (should_reject_ref && eolian_type_is_ptr(field_type))
{
has_ref_field = true;
break;
}
std::string field_type_tag_name;
try
{
field_type_tag_name = _eolian_type_cpp_type_named_get(field_type, class_name, need_name_getter);
}
catch(eolian::js::incomplete_complex_type_error const& e)
{
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Exception while generating '" << field_name << "' fielf of '" << struct_type_full_name << "' struct: " << e.what();
continue;
}
std::string member_ref = struct_c_name;
member_ref += "::";
member_ref += field_name;
auto k = type_class_name(field_type);
if (!k.empty())
{
need_name_getter.insert(k);
k = _class_name_getter(class_name, k);
}
else
{
k = "::efl::eina::js::nonclass_cls_name_getter";
}
ss << " prototype_->SetAccessor(::efl::eina::js::compatibility_new<v8::String>(isolate_, \"" << format::format_field(field_name) << "\"),\n";
ss << " static_cast<v8::AccessorGetterCallback>(&::efl::eo::js::get_struct_member<" << struct_c_name << ", decltype(" << member_ref << "), &" << member_ref << ", " << k << ">),\n";
ss << " static_cast<v8::AccessorSetterCallback>(&::efl::eo::js::set_struct_member<" << struct_c_name << ", " << field_type_tag_name << ", decltype(" << member_ref << "), &" << member_ref << ", " << k << ">));\n";
}
if (should_reject_ref && has_ref_field)
continue;
ss << " };\n";
ss << " auto to_export = ::efl::eo::js::get_namespace({";
bool comma = false;
for (efl::eina::iterator<const char> ns_it(::eolian_typedecl_namespaces_get(tpd)), ns_end; ns_it != ns_end; ++ns_it)
{
if (comma)
ss << ", ";
comma = true;
ss << '"' << format::format_namespace(&*ns_it) << '"';
}
ss << "}, isolate, global);\n";
ss << " ::efl::eo::js::register_struct<" << struct_c_name << ">(isolate, \""
<< format::format_struct(struct_name) << "\", \"" << struct_type_full_name << "\", to_export, fields_func);\n";
ss << " }\n";
structs_ss << ss.str();
}
// generate register function for V8
std::stringstream register_from_constructor_begin_ss;
register_from_constructor_begin_ss
<< "EAPI v8::Local<v8::ObjectTemplate>\n"
<< "register_" << lower_case_class_name << "_from_constructor\n"
<< "(v8::Isolate* isolate, v8::Handle<v8::FunctionTemplate> constructor, ::efl::eina::js::global_ref<v8::Function>* constructor_from_eo)\n"
<< "{\n"
<< " v8::Local<v8::ObjectTemplate> instance = constructor->InstanceTemplate();\n"
<< " instance->SetInternalFieldCount(1);\n"
<< " v8::Handle<v8::ObjectTemplate> prototype = constructor->PrototypeTemplate();\n";
std::stringstream functions_ss;
std::set<std::string> member_names;
std::set<std::string> event_member_names;
for(auto function : normal_functions)
{
std::vector<Eolian_Function_Type> function_types;
switch (eolian_function_type_get(function))
{
case EOLIAN_METHOD:
function_types = {EOLIAN_METHOD};
break;
case EOLIAN_PROPERTY:
function_types = {EOLIAN_PROP_GET, EOLIAN_PROP_SET};
break;
case EOLIAN_PROP_GET:
function_types = {EOLIAN_PROP_GET};
break;
case EOLIAN_PROP_SET:
function_types = {EOLIAN_PROP_SET};
break;
default:
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Unresolved function type";
continue;
}
// generate function registration
for (const auto function_type : function_types)
{
if (eolian_function_scope_get(function, function_type) != EOLIAN_SCOPE_PUBLIC || !_function_is_generatable(function, function_type))
continue; // Some properties may have public 'get' but protected 'set'.
try
{
std::string member_name;
switch (function_type)
{
case EOLIAN_METHOD:
member_name = eolian_function_name_get(function);
break;
case EOLIAN_PROP_SET:
member_name = eolian_function_name_get(function) + std::string("_set");
break;
case EOLIAN_PROP_GET:
member_name = eolian_function_name_get(function) + std::string("_get");
break;
case EOLIAN_PROPERTY:
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "EOLIAN_PROPERTY function type is invalid at this point";
return -1;
case EOLIAN_UNRESOLVED:
default:
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Unresolved function type";
return -1;
}
if(member_names.find(member_name) == member_names.end())
{
member_names.insert(member_name);
std::stringstream ss;
auto output_begin = [&] (std::string name)
{
if(! ::eolian_function_is_constructor(function, klass))
ss << " prototype->Set( ::efl::eina::js::compatibility_new<v8::String>(isolate, \""
<< format::format_method(name) << "\")\n"
<< " , ::efl::eina::js::compatibility_new<v8::FunctionTemplate>(isolate, &efl::eo::js::call_function\n"
<< " , efl::eo::js::call_function_data<\n"
<< " ::efl::eina::_mpl::tuple_c<std::size_t";
};
output_begin(member_name);
const auto key_params = _eolian_function_keys_get(function, function_type);
const auto parameters = _eolian_function_parameters_get(function, function_type);
std::vector<const ::Eolian_Function_Parameter*> full_params;
full_params.insert(end(full_params), begin(key_params), end(key_params));
// only one property_get parameter is translated as the function return in C
const auto param_as_return = (EOLIAN_PROP_GET == function_type) && (parameters.size() == 1)
&& _function_return_is_missing(function, function_type);
if (!param_as_return)
full_params.insert(end(full_params), begin(parameters), end(parameters));
// call_function_data Ins
std::size_t i = 0;
for (auto parameter : full_params)
{
if (EOLIAN_PROP_SET == function_type)
ss << ", " << i;
else
if (EOLIAN_METHOD == function_type)
{
switch (eolian_parameter_direction_get(parameter))
{
case EOLIAN_PARAMETER_IN:
case EOLIAN_PARAMETER_INOUT:
ss << ", " << i;
default: break;
}
}
++i;
}
// call_function_data Outs
ss << ">\n , ::efl::eina::_mpl::tuple_c<std::size_t";
auto key_count = key_params.size();
i = 0;
for (auto parameter : full_params)
{
// ignore keys
if (key_count > 0)
{
--key_count;
++i;
continue;
}
// properties doesn't support in/out/inout
if (EOLIAN_PROP_GET == function_type)
ss << ", " << i;
else
if (EOLIAN_METHOD == function_type)
{
switch (eolian_parameter_direction_get(parameter))
{
case EOLIAN_PARAMETER_OUT:
case EOLIAN_PARAMETER_INOUT:
ss << ", " << i;
default: break;
}
}
++i;
}
// call_function_data Ownership
ss << ">\n , std::tuple<\n";
auto sep = "";
for (auto parameter : full_params)
{
auto type = eolian_parameter_type_get(parameter);
if(eolian_type_is_own(type))
ss << sep << " ::std::true_type";
else
ss << sep << " ::std::false_type";
sep = ",\n";
}
// call_function_data Return
ss << ">\n , ";
const Eolian_Type *return_type = nullptr;
if (param_as_return)
{
return_type = eolian_parameter_type_get(parameters[0]);
}
else
{
return_type = ::eolian_function_return_type_get(function, function_type);
}
std::string param = "void";
if (nullptr != return_type)
{
param = _eolian_type_cpp_type_named_get(return_type, class_name, need_name_getter);
}
ss << param;
// call_function_data Parameters
ss << "\n , std::tuple<\n";
sep = " ";
key_count = key_params.size();
for (auto parameter : full_params)
{
// TODO: REVIEW ALL THIS TOO!!!
auto type = eolian_parameter_type_get(parameter);
auto param = _eolian_type_cpp_type_named_get(type, class_name, need_name_getter);
if (!key_count && EOLIAN_PROP_GET == function_type)
param += "*";
else
{
switch(eolian_parameter_direction_get(parameter))
{
case EOLIAN_PARAMETER_OUT:
case EOLIAN_PARAMETER_INOUT:
param += "*";
default: break;
}
}
ss << sep << param;
sep = ",\n ";
if (key_count > 0) --key_count;
}
std::string param_class_names;
for (auto parameter : full_params)
{
param_class_names += '"' + type_class_name(::eolian_parameter_type_get(parameter)) + "\", ";
}
param_class_names += '"' + type_class_name(return_type) + '"';
std::string param_class_names_array = "std::array<const char*, ";
param_class_names_array += std::to_string(full_params.size() + 1);
param_class_names_array += ">{{" + param_class_names + "}}";
auto output_end = [&] (std::string const& name)
{
ss << "> >(isolate, " << param_class_names_array << ", & ::" << name << ")));\n";
};
switch (function_type)
{
case EOLIAN_METHOD:
output_end(eolian_function_full_c_name_get(function, function_type, EINA_FALSE));
break;
case EOLIAN_PROP_SET:
output_end(eolian_function_full_c_name_get(function, function_type, EINA_FALSE) /*+ std::string("_set")*/);
break;
case EOLIAN_PROP_GET:
output_end(eolian_function_full_c_name_get(function, function_type, EINA_FALSE) /*+ std::string("_get")*/);
break;
case EOLIAN_PROPERTY:
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "EOLIAN_PROPERTY function type is invalid at this point";
return -1;
case EOLIAN_UNRESOLVED:
default:
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Unresolved function type";
return -1;
}
// Write function to functions stream
functions_ss << ss.str();
}
else
{
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Duplicate member function found in class: " <<
eolian_class_name_get(klass) << ": '" << member_name << "'";
}
}
catch(eolian::js::incomplete_complex_type_error const& e)
{
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Exception while generating '" << eolian_function_name_get(function) << "': " << e.what();
}
}
}
functions_ss << "\n prototype->Set(::efl::eina::js::compatibility_new<v8::String>(isolate, \"cast\"),\n"
" ::efl::eina::js::compatibility_new<v8::FunctionTemplate>(isolate, &efl::eina::js::cast_function));\n\n";
// generate all events
std::stringstream events_ss;
auto generate_events = [&] (Eolian_Class const* klass)
{
std::stringstream ss;
for(efl::eina::iterator< ::Eolian_Event> first ( ::eolian_class_events_get(klass))
, last; first != last; ++first)
{
std::string event_name (::eolian_event_name_get(&*first));
if (!eolian_event_is_beta(&*first) &&
event_member_names.find(event_name) == event_member_names.end())
{
auto tp = eolian_event_type_get(&*first);
ss << " {\n";
ss << " static efl::eo::js::event_information ev_info{&constructor_from_eo, " << eolian_event_c_macro_get(&*first);
ss << ", &efl::eo::js::event_callback<";
ss << (tp ? _eolian_type_cpp_type_named_get(tp, class_name, need_name_getter) : "void");
ss << ">, \"" << type_class_name(tp) << "\"};\n";
ss << " " << event_map << "[\"" << event_name << "\"] = &ev_info;\n";
ss << " }\n";
event_member_names.insert(event_name);
}
}
events_ss << ss.str();
};
generate_events(klass);
recurse_inherits(klass, generate_events);
std::stringstream register_from_constructor_end_ss;
register_from_constructor_end_ss
<< " prototype->Set(::efl::eina::js::compatibility_new<v8::String>(isolate, \"on\")\n"
<< " , ::efl::eina::js::compatibility_new<v8::FunctionTemplate>(isolate, &efl::eo::js::on_event\n"
<< " , ::efl::eina::js::compatibility_new<v8::External>(isolate, &" << event_map << ")));\n"
<< " static_cast<void>(prototype); /* avoid warnings */\n"
<< " static_cast<void>(isolate); /* avoid warnings */\n"
<< " static_cast<void>(constructor_from_eo); /* avoid warnings */\n"
<< " return instance;\n"
<< "}\n\n";
std::stringstream name_getters_ss;
for (auto const& k : need_name_getter)
{
name_getters_ss << " struct " << _class_name_getter(class_name, k) << " { static char const* class_name() { return \"" << k << "\"; } };\n";
}
os << "namespace {\n";
os << name_getters_ss.str();
os << "}\n\n";
os << register_from_constructor_begin_ss.str();
os << functions_ss.str();
os << register_from_constructor_end_ss.str();
// generate main entry-point for generation
os << "EAPI void register_" << lower_case_class_name
<< "(v8::Handle<v8::Object> global, v8::Isolate* isolate)\n";
os << "{\n";
os << " v8::Handle<v8::FunctionTemplate> constructor = ::efl::eina::js::compatibility_new<v8::FunctionTemplate>\n";
os << " (isolate, efl::eo::js::constructor\n"
<< " , efl::eo::js::constructor_data(isolate\n"
" , ";
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "before print eo_class";
print_eo_class(klass, os);
EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "print eo_class";
for(auto function : constructor_functions)
{
auto ftype = eolian_function_type_get(function);
if(ftype == EOLIAN_PROPERTY)
ftype = EOLIAN_PROP_SET;
os << "\n , & ::"
<< eolian_function_full_c_name_get(function, ftype, EINA_FALSE);
}
os << "));\n";
os << " static ::efl::eina::js::global_ref<v8::Function> constructor_from_eo;\n";
os << events_ss.str();
os << " register_" << lower_case_class_name << "_from_constructor(isolate, constructor, &constructor_from_eo);\n";
os << " constructor->SetClassName( ::efl::eina::js::compatibility_new<v8::String>(isolate, \""
<< format::format_class(class_name)
<< "\"));\n";
os << " auto to_export = ::efl::eo::js::get_namespace({";
if (namespace_size(klass))
{
bool comma = false;
for (efl::eina::iterator<const char> ns_it(::eolian_class_namespaces_get(klass)), ns_end; ns_it != ns_end; ++ns_it)
{
if (comma)
os << ", ";
comma = true;
os << '"' << format::format_namespace(&*ns_it) << '"';
}
}
os << "}, isolate, global);\n";
os << " to_export->Set( ::efl::eina::js::compatibility_new<v8::String>(isolate, \""
<< format::format_class(class_name) << "\")"
<< ", constructor->GetFunction());\n";
os << " {\n";
os << " v8::Handle<v8::FunctionTemplate> constructor = ::efl::eina::js::compatibility_new<v8::FunctionTemplate>\n";
os << " (isolate, &efl::eo::js::construct_from_eo);\n";
os << " constructor->SetClassName( ::efl::eina::js::compatibility_new<v8::String>(isolate, \""
<< format::format_class(class_name)
<< "\"));\n";
os << " v8::Local<v8::ObjectTemplate> instance = "
<< "register_" << lower_case_class_name << "_from_constructor(isolate, constructor, &constructor_from_eo);\n";
os << " ::efl::eina::js::make_persistent(isolate, instance);\n";
os << " constructor_from_eo = {isolate, constructor->GetFunction()};\n";
os << " ::efl::eina::js::register_class_constructor(\"" << class_full_name << "\", constructor_from_eo.handle());\n";
os << " }\n";
os << structs_ss.str();
// generate enumerations
for (efl::eina::iterator<Eolian_Typedecl> first(::eolian_typedecl_enums_get_by_file(NULL, file_basename.c_str()))
, last; first != last; ++first)
{
auto tpd = &*first;
if (::eolian_typedecl_is_extern(tpd))
continue;
std::string enum_name = ::eolian_typedecl_short_name_get(tpd);
os << " {\n";
os << " auto to_export = ::efl::eo::js::get_namespace({";
bool comma = false;
for (efl::eina::iterator<const char> ns_it(::eolian_typedecl_namespaces_get(tpd)), ns_end; ns_it != ns_end; ++ns_it)
{
if (comma)
os << ", ";
comma = true;
os << '"' << format::format_namespace(&*ns_it) << '"';
}
os << "}, isolate, global);\n";
os << " v8::Handle<v8::Object> enum_obj = efl::eina::js::compatibility_new<v8::Object>(isolate);\n";
os << " to_export->Set(efl::eina::js::compatibility_new<v8::String>(isolate, \""
<< format::format_enum(enum_name) << "\"), enum_obj);\n";
for (efl::eina::iterator<Eolian_Enum_Type_Field> ef(::eolian_typedecl_enum_fields_get(tpd))
, ef_end; ef != ef_end; ++ef)
{
auto field_name = ::eolian_typedecl_enum_field_name_get(&*ef);
auto field_c_name = ::eolian_typedecl_enum_field_c_constant_get(&*ef);
if (!field_name || !field_c_name)
{
EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Could not get enum field name";
continue;
}
os << " enum_obj->Set(efl::eina::js::compatibility_new<v8::String>(isolate, \"" << format::constant(field_name) << "\"),\n";
os << " efl::eina::js::compatibility_new<v8::Int32>(isolate, static_cast<int32_t>(::" << field_c_name << ")));\n";
}
os << " }\n";
}
os << "}\n\n";
for(std::size_t i = 0, j = namespace_size(klass); i != j; ++i)
os << "}";
os << "\n";
}