aboutsummaryrefslogtreecommitdiffstats
path: root/src/bin
diff options
context:
space:
mode:
authorFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2014-09-01 15:08:49 -0300
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2015-12-19 17:05:28 -0200
commit844228b4060c77a41db6232103d8a5a20cd2bf95 (patch)
tree50aa3427c700318e3932b92702aac91a3dec586d /src/bin
parentEvas textblock: Fix some indentation and formatting. (diff)
downloadefl-844228b4060c77a41db6232103d8a5a20cd2bf95.tar.gz
efl-js: JavaScript Eolian bindingdevs/felipealmeida/js-20151219
To configure efl sources with bindings to use in nodejs add ––with-js=nodejs in configure flags to generate node files $ configure --with-js=nodejs and compile normally with: $ make $ make install To use, you have to require efl: efl = require('efl') The bindings is divided in two parts: generated and manually written. The generation uses the Eolian library for parsing Eo files and generate C++ code that is compiled against V8 interpreter library to create a efl.node file that can be required in a node.js instance. @feature
Diffstat (limited to 'src/bin')
-rwxr-xr-xsrc/bin/efl_js/efljslaunch139
-rw-r--r--src/bin/efl_js/efljslaunch.desktop7
-rw-r--r--src/bin/efl_js/efljslaunch.xml7
-rwxr-xr-xsrc/bin/efl_js/efljspack251
-rw-r--r--src/bin/efl_js/launcher_main.cc156
-rw-r--r--src/bin/eolian_js/.gitignore1
-rw-r--r--src/bin/eolian_js/eolian/class.hh139
-rw-r--r--src/bin/eolian_js/eolian/js/domain.hh8
-rw-r--r--src/bin/eolian_js/eolian/js/format.hh44
-rw-r--r--src/bin/eolian_js/main.cc1076
10 files changed, 1828 insertions, 0 deletions
diff --git a/src/bin/efl_js/efljslaunch b/src/bin/efl_js/efljslaunch
new file mode 100755
index 0000000000..785c30e3e3
--- /dev/null
+++ b/src/bin/efl_js/efljslaunch
@@ -0,0 +1,139 @@
+#!/bin/sh
+':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"
+
+// Core node modules
+var path = require('path');
+var os = require('os');
+var zlib = require('zlib');
+var child_process = require('child_process');
+
+// 3rd party modules
+var fs = require('fs-extra');
+var getopt = require('node-getopt');
+var tar = require('tar');
+
+function make_error_cb(message)
+{
+ return function(e) {
+ console.error("Error %s: %s", message, e);
+ process.exit(1);
+ };
+}
+
+function remove_files(options)
+{
+ if (options.verbose)
+ console.log("Removing temporary files");
+
+ fs.remove(options.project_folder);
+}
+
+function run_project(options)
+{
+ if (options.verbose)
+ console.log("Running the project");
+
+ var current_dir = process.cwd();
+ process.chdir(options.project_root);
+
+ var proc = child_process.fork(options.metadata.Entry);
+ proc.on('exit', function(code){
+ if (options.verbose)
+ console.log('Child exited with code %s', code);
+ process.chdir(current_dir);
+ if (!options.keep)
+ remove_files(options);
+ });
+
+}
+
+function unpack_project_data(options)
+{
+ if (options.verbose)
+ console.log("Unpacking project sources and assets");
+
+ var datafile = path.join(options.project_folder, "data.tar.gz");
+ var project_root = path.join(options.project_folder, "root");
+
+ options.project_root = project_root;
+
+ var input = fs.createReadStream(datafile);
+ var unzipper = zlib.createGunzip();
+ var extractor = tar.Extract({path: project_root, strip: 0});
+
+ input.on('error', make_error_cb("reading package data file."));
+ extractor.on('error', make_error_cb("unpacking package data file."));
+ if (!("only-extract" in options))
+ extractor.on('end', function(){ run_project(options); });
+
+ input.pipe(unzipper)
+ unzipper.pipe(extractor);
+}
+
+function read_metadata(options)
+{
+ if (options.verbose)
+ console.log("Reading project metadata");
+
+ var project_folder = options.project_folder;
+ var metadata = JSON.parse(fs.readFileSync(path.join(project_folder, "meta.json")));
+
+ if (options.verbose)
+ console.log("Project: %s\nVersion: %s\nEntry point: %s", metadata.Name, metadata.Version, metadata.Entry);
+ if ("only-dump" in options)
+ process.exit(0);
+
+ options.metadata = metadata;
+
+ unpack_project_data(options);
+}
+
+function extract(filename, options)
+{
+ if (options.verbose)
+ console.log("Extracting ", filename, "with options ", options);
+
+ var project_id = path.basename(filename, ".epk");
+ var project_folder = path.join(options['temp-dir'], project_id);
+
+ options.project_folder = project_folder;
+ options.project_id = project_id;
+
+ var input = fs.createReadStream(filename);
+ var extractor = tar.Extract({path: options['temp-dir'], strip: 0});
+
+ input.on('error', make_error_cb("reading package file."));
+ extractor.on('error', make_error_cb("unpacking package file."));
+ extractor.on('end', function(){ read_metadata(options); });
+
+ input.pipe(extractor);
+}
+
+function main() {
+ var options = getopt.create([
+ ['d', 'only-dump', 'Only dump information about the package'],
+ ['e', 'only-extract', 'Only extract the package, do not run'],
+ ['h', 'help', 'Display this help'],
+ ['k', 'keep', 'Do not remove the files after exiting'],
+ ['t', 'temp-dir=ARG', 'Temporary dir to extract files'],
+ ['v', 'verbose', 'Print information messages'],
+ ]).bindHelp().parseSystem();
+
+ var filename = options.argv[0];
+ if (filename === undefined)
+ {
+ console.error("Must provide a package file.");
+ process.exit(1);
+ }
+
+ if (!('temp-dir' in options.options))
+ {
+ options.options["temp-dir"] = path.join(os.tmpdir(), "efljs_apps");
+ if (options.verbose)
+ console.log("Defaulting temp dir to ", options.options["temp-dir"]);
+ }
+
+ extract(filename, options.options);
+}
+
+main();
diff --git a/src/bin/efl_js/efljslaunch.desktop b/src/bin/efl_js/efljslaunch.desktop
new file mode 100644
index 0000000000..53371cba97
--- /dev/null
+++ b/src/bin/efl_js/efljslaunch.desktop
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Name=EFL JS package launcher
+Exec=efljslaunch %f
+Type=Application
+Categories=EFL
+Terminal=true
+MimeType=application/x-efljspackage;
diff --git a/src/bin/efl_js/efljslaunch.xml b/src/bin/efl_js/efljslaunch.xml
new file mode 100644
index 0000000000..b1db6841b2
--- /dev/null
+++ b/src/bin/efl_js/efljslaunch.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/x-efljspackage">
+ <comment xml:lang="en">EFL JS package</comment>
+ <glob pattern="*.epk"/>
+ </mime-type>
+</mime-info> \ No newline at end of file
diff --git a/src/bin/efl_js/efljspack b/src/bin/efl_js/efljspack
new file mode 100755
index 0000000000..50e27b6ac4
--- /dev/null
+++ b/src/bin/efl_js/efljspack
@@ -0,0 +1,251 @@
+#!/bin/sh
+':' //; exec "$(command -v nodejs || command -v node)" "$0" "$@"
+
+var zlib = require('zlib');
+var path = require('path');
+
+// external dependencies
+var fs = require('fs-extra');
+var tar = require('tar');
+var fstream = require('fstream');
+var getopt = require('node-getopt');
+
+/*
+ * Packing a project.
+ * The efljs package has a similar format to debian packages. It is a
+ * tar package containing two files:
+ *
+ * meta.txt: Metadata information about this package.
+ * data.tar.gz: Gzipped data, with the project tree ready to be decompressed
+ * and run by the package launcher.
+ *
+ * During the build, a out/ directory is created in the project root to
+ * store the package and temporary files.
+ */
+
+// Creates a stub .project file and packs it.
+function pack_single(sourcepath, options)
+{
+ if (options.verbose)
+ console.log("Creating project file for single file app", sourcepath);
+
+ var dir_name = path.dirname(fs.realpathSync(sourcepath));
+ var filename = path.basename(sourcepath);
+ var projectRegex = /^(.*).js$/g;
+ var project_name = projectRegex.exec(filename)[1];
+
+ if (!validade_project_name(project_name))
+ {
+ console.error("Invalid project name. Must start with a letter.");
+ process.exit(0);
+ }
+
+ var project_filename = path.join(dir_name, project_name + ".project");
+
+ var fd = fs.openSync(project_filename, 'w');
+
+ var jsonData = {};
+
+ jsonData["Name"] = project_name;
+ jsonData["Entry"] = filename;
+ jsonData["Sources"] = [[filename, '.']];
+ jsonData["Version"] = "0.1";
+
+ fs.writeSync(fd, JSON.stringify(jsonData, null, 2));
+
+ fs.closeSync(fd);
+
+ pack_project(project_filename, options);
+
+}
+
+function generate_build_info(configuration, project_file, options)
+{
+ build_info = {};
+
+ // project == project_dir
+ // /out == build_dir
+ // /data == data_dir
+ // /name-version == package_dir
+
+ build_info.package_id = configuration.Name + "-" + configuration.Version;
+ build_info.project_dir = path.dirname(project_file);
+ build_info.build_dir = path.join(build_info.project_dir, "out");
+ build_info.data_dir = path.join(build_info.build_dir, "data");
+ build_info.package_dir = path.join(build_info.build_dir, build_info.package_id);
+ build_info.data_file = path.join(build_info.package_dir, "data.tar.gz");
+ build_info.package_file = path.join(build_info.build_dir, build_info.package_id + ".epk")
+ build_info.metadata_file = path.join(build_info.package_dir, "meta.json");
+
+ if (options.verbose)
+ {
+ console.log("Project id: ", build_info.package_id);
+ console.log("Project source dir: ", build_info.project_dir);
+ console.log("Project build dir: ", build_info.build_dir);
+ console.log("Project data dir:", build_info.data_dir);
+ console.log("Project package dir:", build_info.package_dir);
+ }
+
+ return build_info;
+
+}
+
+// Project names must start with a letter and contain only
+// letters, digits and underscores.
+function validade_project_name(name)
+{
+ return (/^[a-zA-Z][\w-]*$/).test(name)
+}
+
+function pack_project(project_file, options)
+{
+ if (options.verbose)
+ console.log("Packing project from project file ", project_file);
+
+ var configuration = JSON.parse(fs.readFileSync(project_file));
+
+ if (!validade_project_name(configuration.Name))
+ {
+ console.error("Invalid project name. Must start with a letter.");
+ process.exit(0);
+ }
+
+ var build_info = generate_build_info(configuration, project_file, options);
+
+ try
+ {
+ fs.mkdirSync(build_info.build_dir);
+ fs.mkdirSync(build_info.data_dir);
+ fs.mkdirSync(build_info.package_dir);
+ }
+ catch (e)
+ {
+ console.warn("Warning: Project output directories not empty.");
+ }
+
+ create_metadata_file(configuration, build_info, options);
+
+ // If not explicitly named on configuration, add the entire directory
+ if (!('Sources' in configuration))
+ {
+ generate_source_list(configuration, build_info.project_dir, options);
+ }
+
+ create_project_tree(configuration.Sources, build_info, options);
+
+ pack_data_dir(build_info, options);
+}
+
+function create_project_tree(sources, build_info, options)
+{
+ for (var i = sources.length - 1; i >= 0; i--) {
+ if (options.verbose)
+ console.log("Adding file ", sources[i], "to package.");
+ var source_file = path.join(build_info.project_dir, sources[i][0]);
+ var destination_dir = path.join(build_info.data_dir, sources[i][1]);
+ var destination_filename = path.basename(source_file);
+ var destination_file = path.join(destination_dir, destination_filename);
+
+ fs.copySync(source_file, destination_file);
+ };
+}
+
+function generate_source_list(configuration, project_dir, options)
+{
+ console.log("Generating source list for project dir", build_info.project_dir);
+ var dir_entries = fs.readdirSync(project_dir);
+ var sources = [];
+
+ dir_entries.forEach(function(entry){
+ if (entry == "out")
+ return;
+ sources.push([entry, "."]);
+ });
+ configuration.Sources = sources;
+}
+
+function create_metadata_file(configuration, build_info, options)
+{
+ if (options.verbose)
+ console.log("Creating metadata file", build_info.metadata_file);
+
+ var metadata = {};
+
+ metadata.Name = configuration.Name;
+ metadata.Entry = configuration.Entry;
+ metadata.Version = configuration.Version;
+
+ var output = fs.createWriteStream(build_info.metadata_file);
+ output.write(JSON.stringify(metadata, null, 2));
+ output.close();
+}
+
+function pack_data_dir(build_info, options)
+{
+ if (options.verbose)
+ console.log("Packing data...");
+
+ pack_directory(build_info.data_dir, build_info.data_file, true, true, function(){
+ if (options.verbose)
+ console.log("Packed data");
+ pack_final_package(build_info, options);
+ });
+}
+
+function pack_final_package(build_info, options)
+{
+ if (options.verbose)
+ console.log("Creating package ", build_info.package_file);
+ pack_directory(build_info.package_dir, build_info.package_file, false, false, function(){
+ if (options.verbose)
+ console.log("Created project package.");
+ });
+}
+
+function pack_directory(source_dir, target_file, strip_base_dir, should_gzip, callback)
+{
+ var output = fs.createWriteStream(target_file);
+ var packer = tar.Pack({fromBase: strip_base_dir == true});
+ if (callback != undefined)
+ output.on('close', callback);
+
+ var reader = fstream.Reader({path: source_dir, type: "Directory"});
+ var destStr = reader.pipe(packer);
+ if(should_gzip)
+ destStr = destStr.pipe(zlib.createGzip());
+ destStr.pipe(output);
+}
+
+function main()
+{
+
+ var options = getopt.create([
+ ['v', 'verbose', 'Explain what is being done'],
+ ['h', 'help', 'Display this help']
+ ]).bindHelp().parseSystem();
+
+ filename = options.argv[0];
+
+ if (typeof filename === 'undefined')
+ {
+ console.error('Must provide a valid js or project file.');
+ process.exit(1);
+ }
+
+ if (endsWith(filename, ".js"))
+ {
+ pack_single(filename, options.options);
+ }
+ else if (endsWith(filename, ".project"))
+ {
+ pack_project(filename, options.options);
+ }
+}
+
+main();
+
+//// Helper functions
+function endsWith(str, suffix)
+{
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
+}
diff --git a/src/bin/efl_js/launcher_main.cc b/src/bin/efl_js/launcher_main.cc
new file mode 100644
index 0000000000..680f16ca52
--- /dev/null
+++ b/src/bin/efl_js/launcher_main.cc
@@ -0,0 +1,156 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <cerrno>
+
+#include <Eo_Js.hh>
+#include <Eina.hh>
+#include <Eo.hh>
+// #include <efl_js.hh>
+
+using namespace std;
+using namespace v8;
+
+const char PATH_SEPARATOR =
+#ifdef _WIN32
+ '\\';
+#else
+ '/';
+#endif
+
+static std::string get_file_contents(const char *filename) {
+ std::ifstream in(filename, std::ios::in);
+ if (in) {
+ std::ostringstream contents;
+ contents << in.rdbuf();
+ in.close();
+ return contents.str();
+ } else {
+ throw(errno);
+ }
+}
+
+static std::string get_filename(std::string path)
+{
+ int beginIdx = path.rfind(PATH_SEPARATOR);
+ return path.substr(beginIdx + 1);
+}
+
+static void show_usage(std::string name)
+{
+ std::cerr << "Usage: " << get_filename(name) << " <option(s)> [SOURCE]\n" << std::endl
+ << "Options:" << std::endl
+ << "\t-h, --help\t\t Show this help message" << std::endl;
+}
+
+/*
+ * Basic console.log implementation with space-separated values,
+ * no substitution
+ */
+void Log(const FunctionCallbackInfo<Value>& args)
+{
+ Isolate* isolate = Isolate::GetCurrent();
+ HandleScope scope(isolate);
+
+ for (int i=0; i < args.Length(); i++)
+ {
+ if (i != 0)
+ std::cout << " ";
+ String::Utf8Value string(args[i]);
+ std::cout << *string;
+ }
+
+ std::cout << std::endl;
+
+ args.GetReturnValue().Set(v8::Null(isolate));
+}
+
+
+int main(int argc, char* argv[])
+{
+
+ std::string script_source;
+ char *filename = 0;
+
+ for (int i=1; i < argc; i++)
+ {
+ if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0))
+ {
+ show_usage(argv[0]);
+ return 0;
+ }
+ else
+ {
+ filename = argv[i];
+ }
+ }
+
+ if (!filename)
+ {
+ std::cerr << "Error: No source provided." << std::endl;
+ show_usage(argv[0]);
+ return 1;
+ }
+
+ try
+ {
+ script_source = get_file_contents(filename);
+ } catch (int errno)
+ {
+ perror("Error: ");
+ return 1;
+ }
+
+
+ efl::eina::js::compatibility_initialize();
+ v8::V8::SetFlagsFromCommandLine(&argc, const_cast<char**>(argv), true);
+
+ v8::Isolate* isolate = efl::eina::js::compatibility_isolate_new();
+ {
+ Isolate::Scope isolate_scope(isolate);
+ HandleScope handleScope(isolate);
+
+ Local<Context> context = Context::New(isolate, NULL);
+ Context::Scope context_scope(context);
+ context->Enter();
+
+ // Setup the console and log
+ Local<Object> console = Object::New(isolate);
+ Local<FunctionTemplate> log = FunctionTemplate::New(isolate, Log);
+ console->Set(String::NewFromUtf8(isolate, "log"), log->GetFunction());
+
+ Local<Object> global = context->Global();
+ global->Set(String::NewFromUtf8(isolate, "console"), console);
+
+ // Set up the efl exports; Needed to enter the context before this
+ // due to creating Objects instead of Objects Templates
+ // WIP: Commented out due to potential missing v8 platform implementation issues
+ // Local<Object> efl_exports = Object::New(isolate);
+ // global->Set(String::NewFromUtf8(isolate, "efl"), efl_exports);
+ // efl_js::init(efl_exports);
+
+ // And now the user's script
+ Local<String> source = String::NewFromUtf8(isolate, script_source.c_str());
+
+ Local<Script> script = Script::Compile(source);
+
+ TryCatch tryCatch(isolate);
+ Local<Value> result = script->Run();
+
+ if (result.IsEmpty())
+ {
+ Local<Value> exception = tryCatch.Exception();
+ String::Utf8Value exception_str(exception);
+ printf("Exception: %s\n", *exception_str);
+ }
+
+ }
+
+ V8::Dispose();
+ return 0;
+}
diff --git a/src/bin/eolian_js/.gitignore b/src/bin/eolian_js/.gitignore
new file mode 100644
index 0000000000..631f68aa1f
--- /dev/null
+++ b/src/bin/eolian_js/.gitignore
@@ -0,0 +1 @@
+/eolian_js
diff --git a/src/bin/eolian_js/eolian/class.hh b/src/bin/eolian_js/eolian/class.hh
new file mode 100644
index 0000000000..bd04d5fedd
--- /dev/null
+++ b/src/bin/eolian_js/eolian/class.hh
@@ -0,0 +1,139 @@
+#ifndef EOLIAN_KLASS_HH
+#define EOLIAN_KLASS_HH
+
+#include <Eina.hh>
+
+#include <eolian/js/domain.hh>
+
+#include <ostream>
+
+inline std::string name(Eolian_Class const* klass)
+{
+ return ::eolian_class_name_get(klass);
+}
+
+inline std::string full_name(Eolian_Class const* klass)
+{
+ return ::eolian_class_full_name_get(klass);
+}
+
+inline std::string full_name_transformed(Eolian_Class const* klass)
+{
+ auto r = full_name(klass);
+ std::replace(r.begin(), r.end(), '.', '_');
+ return r;
+}
+
+inline std::size_t namespace_size(Eolian_Class const* klass)
+{
+ std::size_t size = 0;
+ for(efl::eina::iterator<const char> first (::eolian_class_namespaces_get(klass))
+ , last; first != last; ++first)
+ ++size;
+ return size;
+}
+
+inline std::string type_class_name(Eolian_Type const* tp)
+{
+ if (tp)
+ {
+ Eolian_Type_Type tpt = ::eolian_type_type_get(tp);
+ if (tpt == EOLIAN_TYPE_POINTER || tpt == EOLIAN_TYPE_ALIAS || tpt == EOLIAN_TYPE_REGULAR)
+ {
+ return type_class_name(::eolian_type_base_type_get(tp));
+ }
+ else if(tpt == EOLIAN_TYPE_CLASS)
+ {
+ Eolian_Class const* klass = ::eolian_type_class_get(tp);
+ if (klass)
+ {
+ Eina_Stringshare* klass_name = ::eolian_class_full_name_get(klass);
+ if (!klass_name)
+ throw std::runtime_error("Could not get Eo class name");
+
+ return klass_name;
+ } // TODO: else should throw std::runtime_error("Could not get Eo class");
+ }
+ else if(tpt == EOLIAN_TYPE_STRUCT)
+ {
+ auto struct_type_full_name = ::eolian_type_full_name_get(tp);
+ if (!struct_type_full_name)
+ throw std::runtime_error("Could not get struct name");
+ return struct_type_full_name;
+ }
+ }
+ return "";
+}
+
+inline void print_lower_case_namespace(Eolian_Class const* klass, std::ostream& os)
+{
+ std::vector<std::string> namespace_;
+ for(efl::eina::iterator<const char> first (::eolian_class_namespaces_get(klass))
+ , last; first != last; ++first)
+ namespace_.push_back(&*first);
+ for(auto first = namespace_.begin(), last = namespace_.end()
+ ; first != last; ++first)
+ {
+ std::string lower(*first);
+ std::transform(lower.begin(), lower.end(), lower.begin(), tolower);
+ os << lower;
+ if(std::next(first) != last) os << "::";
+ }
+}
+
+inline void print_eo_class(Eolian_Class const* klass, std::ostream& os)
+{
+ assert(klass != 0);
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "print_eo_class";
+
+ auto toupper = [] (unsigned char c) { return std::toupper(c); };
+
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "print_eo_class";
+ std::vector<std::string> namespace_;
+ for(efl::eina::iterator<const char> first (::eolian_class_namespaces_get(klass))
+ , last; first != last; ++first)
+ namespace_.push_back(&*first);
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "namespace";
+ namespace_.push_back(name(klass));
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "class";
+ switch(eolian_class_type_get(klass))
+ {
+ case EOLIAN_CLASS_REGULAR:
+ case EOLIAN_CLASS_ABSTRACT:
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "";
+ namespace_.push_back("CLASS");
+ break;
+ case EOLIAN_CLASS_INTERFACE:
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "";
+ namespace_.push_back("INTERFACE");
+ break;
+ case EOLIAN_CLASS_MIXIN:
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "";
+ namespace_.push_back("MIXIN");
+ break;
+ default:
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "default ?";
+ std::abort();
+ }
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "";
+ for(auto first = namespace_.begin(), last = namespace_.end()
+ ; first != last; ++first)
+ {
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "";
+ std::string upper(*first);
+ std::transform(upper.begin(), upper.end(), upper.begin(), toupper);
+ os << upper;
+ if(std::next(first) != last) os << "_";
+ }
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "";
+}
+
+inline bool is_evas(Eolian_Class const* klass)
+{
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "is_evas";
+ efl::eina::iterator<const char> first (::eolian_class_namespaces_get(klass));
+ return first != efl::eina::iterator<const char>()
+ && std::strcmp(&*first, "Evas") == 0;
+}
+
+#endif
diff --git a/src/bin/eolian_js/eolian/js/domain.hh b/src/bin/eolian_js/eolian/js/domain.hh
new file mode 100644
index 0000000000..38cf542873
--- /dev/null
+++ b/src/bin/eolian_js/eolian/js/domain.hh
@@ -0,0 +1,8 @@
+
+#include <Eina.hh>
+
+namespace eolian { namespace js {
+
+extern efl::eina::log_domain domain;
+
+} }
diff --git a/src/bin/eolian_js/eolian/js/format.hh b/src/bin/eolian_js/eolian/js/format.hh
new file mode 100644
index 0000000000..a07d541e14
--- /dev/null
+++ b/src/bin/eolian_js/eolian/js/format.hh
@@ -0,0 +1,44 @@
+#ifndef EOLIAN_JS_FORMAT_HH
+#define EOLIAN_JS_FORMAT_HH
+
+#include <eolian/js/domain.hh>
+
+#include <algorithm>
+#include <string>
+#include <cctype>
+
+namespace eolian { namespace js {
+
+namespace format {
+
+std::string generic(std::string const& in)
+{
+ std::string s = in;
+ auto i = s.find('_');
+ while (i != std::string::npos)
+ {
+ if (i <= 0 || i+1 >= s.size() ||
+ !::isalnum(s[i-1]) || !::isalnum(s[i+1]))
+ {
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "Entity '" << in
+ << "' can't be conveniently converted to a JavaScript name.";
+ return in;
+ }
+ s[i+1] = static_cast<char>(::toupper(s[i+1]));
+ s.erase(i, 1);
+ i = s.find('_', i);
+ }
+ return s;
+}
+
+std::string constant(std::string in)
+{
+ std::transform(in.begin(), in.end(), in.begin(), ::toupper);
+ return in;
+}
+
+}
+
+} }
+
+#endif
diff --git a/src/bin/eolian_js/main.cc b/src/bin/eolian_js/main.cc
new file mode 100644
index 0000000000..7f3cfd2e59
--- /dev/null
+++ b/src/bin/eolian_js/main.cc
@@ -0,0 +1,1076 @@
+
+#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);
+ while ((tpt_out == EOLIAN_TYPE_REGULAR || tpt_out == EOLIAN_TYPE_ALIAS) && !eolian_type_is_extern(tp_out))
+ {
+ auto t = eolian_type_base_type_get(tp_out);
+ // TODO: shouldn't __undefined_type be flagged as external???
+ if (!t || !eolian_type_full_name_get(t) || strcmp(eolian_type_full_name_get(t), "__undefined_type") == 0) break;
+ tp_out = t;
+ tpt_out = eolian_type_type_get(t);
+ }
+}
+
+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)
+{
+ const auto is_const = eolian_type_is_const(tp);
+
+ Eolian_Type_Type tpt = EOLIAN_TYPE_UNKNOWN_TYPE;
+ _final_type_and_type_type_get(tp, tp, tpt);
+
+ if (tpt == EOLIAN_TYPE_UNKNOWN_TYPE)
+ return "error";
+
+ std::string result;
+
+ if ((tpt == EOLIAN_TYPE_VOID
+ || tpt == EOLIAN_TYPE_REGULAR
+ || tpt == EOLIAN_TYPE_COMPLEX
+ || tpt == EOLIAN_TYPE_STRUCT
+ || tpt == EOLIAN_TYPE_STRUCT_OPAQUE
+ || tpt == EOLIAN_TYPE_ENUM
+ || tpt == EOLIAN_TYPE_ALIAS
+ || tpt == EOLIAN_TYPE_CLASS)
+ && is_const)
+ {
+ result += "const ";
+ }
+
+
+ if (tpt == EOLIAN_TYPE_REGULAR
+ || tpt == EOLIAN_TYPE_COMPLEX
+ || tpt == EOLIAN_TYPE_STRUCT
+ || tpt == EOLIAN_TYPE_STRUCT_OPAQUE
+ || tpt == EOLIAN_TYPE_ENUM
+ || tpt == EOLIAN_TYPE_ALIAS
+ || 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"}
+ };
+
+ std::string type_name = eolian_type_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_STRUCT)
+ {
+ result = "efl::eina::js::make_struct_tag<" + result + ">";
+ }
+ }
+ else if (tpt == EOLIAN_TYPE_VOID)
+ result += "void";
+ else // tpt == EOLIAN_TYPE_POINTER
+ {
+ auto btp = eolian_type_base_type_get(tp);
+ result += _eolian_type_cpp_type_named_get(btp, caller_class_prefix, need_name_getter);
+
+ 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);
+
+ if (btpt == EOLIAN_TYPE_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";
+ }
+
+ if (btpt == EOLIAN_TYPE_COMPLEX)
+ {
+ result = "efl::eina::js::make_complex_tag<" + result;
+
+ bool has_subtypes = false;
+ auto subtypes = eolian_type_subtypes_get(btp);
+ const Eolian_Type *subtype;
+ EINA_ITERATOR_FOREACH(subtypes, 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;
+ }
+
+ if (!has_subtypes)
+ throw eolian::js::incomplete_complex_type_error("Incomplete complex type");
+
+ result += ">";
+ }
+ }
+
+ /*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;
+}
+
+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";
+
+ 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];
+ }
+
+
+ for(auto src : include_paths)
+ if (!::eolian_directory_scan(src.c_str()))
+ {
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain)
+ << "Couldn't load eolian from '" << src << "'.";
+ }
+ if (!::eolian_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_file_parse(in_file.c_str()))
+ {
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain)
+ << "Failed parsing: " << in_file << ".";
+ assert(false && "Error parsing input file");
+ }
+
+ 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(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;
+
+ auto separate_functions = [&] (Eolian_Class const* klass, Eolian_Function_Type t
+ , bool ignore_constructors)
+ {
+ efl::eina::iterator<Eolian_Function> first ( ::eolian_class_functions_get(klass, t) )
+ , last;
+ for(; first != last; ++first)
+ {
+ Eolian_Function const* function = &*first;
+ if(eolian_function_scope_get(function) == EOLIAN_SCOPE_PUBLIC)
+ {
+ 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("render2_updates", ::eolian_function_name_get(function)) != 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);
+ // }
+ }
+ }
+ }
+ };
+ separate_functions(klass, EOLIAN_METHOD, false);
+ separate_functions(klass, EOLIAN_PROPERTY, false);
+
+ EINA_CXX_DOM_LOG_WARN(eolian::js::domain) << "functions were separated";
+
+ 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(&*first);
+ function(base);
+ recurse_inherits(base, function);
+ }
+ };
+
+ auto save_functions = [&](Eolian_Class const* klass)
+ {
+ if(classes.find(klass) == classes.end())
+ {
+ classes.insert(klass);
+ separate_functions(klass, EOLIAN_METHOD, true);
+ separate_functions(klass, EOLIAN_PROPERTY, true);
+ }
+ };
+ recurse_inherits(klass, save_functions);
+
+ EINA_CXX_DOM_LOG_WARN(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_WARN(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));
+
+ 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";
+
+ 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";
+ };
+ 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_WARN(eolian::js::domain) << "includes added";
+
+ 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";
+ }
+
+ 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_WARN(eolian::js::domain) << "namespace";
+
+ std::set<std::string> need_name_getter;
+
+ std::stringstream structs_ss;
+ for (efl::eina::iterator<Eolian_Type> first(::eolian_type_structs_get_by_file(file_basename.c_str()))
+ , last; first != last; ++first)
+ {
+ std::stringstream ss;
+ auto tp = &*first;
+ if (::eolian_type_type_get(tp) == EOLIAN_TYPE_STRUCT_OPAQUE)
+ continue;
+
+ auto struct_name = ::eolian_type_name_get(tp);
+ auto struct_type_full_name = ::eolian_type_full_name_get(tp);
+ 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, "Eo.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_type_struct_fields_get(tp))
+ , sf_end; sf != sf_end; ++sf)
+ {
+ auto field_type = ::eolian_type_struct_field_type_get(&*sf);
+ auto field_name = ::eolian_type_struct_field_name_get(&*sf);
+ if (!field_name)
+ {
+ EINA_CXX_DOM_LOG_ERR(eolian::js::domain) << "Could not get struct field name";
+ continue;
+ }
+ 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::generic(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";
+ }
+ ss << " };\n";
+ ss << " auto to_export = ::efl::eo::js::get_namespace({";
+ bool comma = false;
+ for (efl::eina::iterator<const char> ns_it(::eolian_type_namespaces_get(tp)), ns_end; ns_it != ns_end; ++ns_it)
+ {
+ if (comma)
+ ss << ", ";
+ comma = true;
+ ss << '"' << format::generic(&*ns_it) << '"';
+ }
+ ss << "}, isolate, global);\n";
+ ss << " ::efl::eo::js::register_struct<" << struct_c_name << ">(isolate, \""
+ << format::generic(struct_name) << "\", \"" << struct_type_full_name << "\", to_export, fields_func);\n";
+ ss << " }\n";
+
+ structs_ss << ss.str();
+ }
+
+
+ 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;
+ }
+
+ for (const auto function_type : function_types)
+ {
+ 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 = std::string("set_") + eolian_function_name_get(function);
+ break;
+ case EOLIAN_PROP_GET:
+ member_name = std::string("get_") + eolian_function_name_get(function);
+ 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::generic(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)
+ {
+ // TODO: REVIEW THIS!!! IT IS TOO STRANGE...
+ // properties doesn't support in/out/inout
+ if (EOLIAN_PROP_SET == function_type)
+ ss << ", " << i;
+ else
+ if (EOLIAN_METHOD == function_type)
+ {
+ switch (eolian_parameter_direction_get(parameter))
+ {
+ case EOLIAN_IN_PARAM:
+ case EOLIAN_INOUT_PARAM:
+ 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)
+ {
+ // TODO: THIS TOO!!!
+ // 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_OUT_PARAM:
+ case EOLIAN_INOUT_PARAM:
+ 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_OUT_PARAM:
+ case EOLIAN_INOUT_PARAM:
+ 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 + "}}";
+
+ // // ss << __func__ << ":" << __LINE__;
+ 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();
+ }
+ }
+ 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();
+ }
+ }
+ }
+
+ 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));
+ std::replace(event_name.begin(), event_name.end(), ',', '_');
+
+ 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_name_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();
+
+
+ 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::generic(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::generic(&*ns_it) << '"';
+ }
+ }
+ os << "}, isolate, global);\n";
+
+ os << " to_export->Set( ::efl::eina::js::compatibility_new<v8::String>(isolate, \""
+ << format::generic(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::generic(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();
+
+ for (efl::eina::iterator<Eolian_Type> first(::eolian_type_enums_get_by_file(file_basename.c_str()))
+ , last; first != last; ++first)
+ {
+ auto tp = &*first;
+ if (::eolian_type_is_extern(tp))
+ continue;
+ std::string enum_name = ::eolian_type_name_get(tp);
+ os << " {\n";
+ os << " auto to_export = ::efl::eo::js::get_namespace({";
+ bool comma = false;
+ for (efl::eina::iterator<const char> ns_it(::eolian_type_namespaces_get(tp)), ns_end; ns_it != ns_end; ++ns_it)
+ {
+ if (comma)
+ os << ", ";
+ comma = true;
+ os << '"' << format::generic(&*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::generic(enum_name) << "\"), enum_obj);\n";
+ for (efl::eina::iterator<Eolian_Enum_Type_Field> ef(::eolian_type_enum_fields_get(tp))
+ , ef_end; ef != ef_end; ++ef)
+ {
+ auto field_name = ::eolian_type_enum_field_name_get(&*ef);
+ auto field_c_name = ::eolian_type_enum_field_c_name_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";
+
+
+}