Initial commit

master
garrettmills 4 years ago
commit cdd8b4dc4f
No known key found for this signature in database
GPG Key ID: 6ACD58D6ADACFC6E

@ -0,0 +1 @@
export PATH=/home/garrettmills/.clone/emsdk:/home/garrettmills/.clone/emsdk/node/12.9.1_64bit/bin:/home/garrettmills/.clone/emsdk/upstream/emscripten:$PATH

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/cppjs.iml" filepath="$PROJECT_DIR$/.idea/cppjs.iml" />
</modules>
</component>
</project>

@ -0,0 +1,12 @@
#!/bin/bash
emcc main.cpp --std=c++14 \
--shell-file shell_minimal.html \
--emrun \
-o hello.html \
-s NO_EXIT_RUNTIME=1 \
-s EXPORTED_FUNCTIONS="['_main', '_itest', '_str_pass_size', '_fire_event_bus']" \
-s EXTRA_EXPORTED_RUNTIME_METHODS="['cwrap', 'ccall', 'stringToUTF8', 'setValue', 'getValue', 'UTF8ToString']" \
-s WASM=1 \
-s DISABLE_EXCEPTION_CATCHING=0 \
--bind

@ -0,0 +1,61 @@
var Module = {
preRun: [],
postRun: [],
print: (function() {
var element = document.getElementById('output');
if (element) element.value = ''; // clear browser cache
return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
console.log(text);
if (element) {
element.value += text + "\n";
element.scrollTop = element.scrollHeight; // focus on bottom
}
};
})(),
printErr: function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
},
/*canvas: (function() {
var canvas = document.getElementById('canvas');
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
return canvas;
})(),*/
/*setStatus: function(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
Module.setStatus.last.time = now;
Module.setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if (!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
},*/
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
}
};

@ -0,0 +1,22 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Emscripten-Generated Code</title>
</head>
<body>
<!-- <div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>
-->
<textarea class="emscripten" id="output" rows="8"></textarea>
<br><br>
<p id="foo" class="bar baz" data-test="3.141">Hello, world!</p>
<script type='text/javascript' src="ems-core.js"></script>
<script async type="text/javascript" src="hello.js"></script>
<script type="text/javascript" src="init.js"></script>
</body>
</html>

7005
hello.js

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -0,0 +1,57 @@
var itest;
function InitWrappers() {
// itest = Module.cwrap('itest', 'string');
itest = (str) => {
const buffer = Module._malloc(str.length + 1)
console.log({buffer})
Module.stringToUTF8(str, buffer, str.length + 1)
return Module.ccall('itest', 'string', ['number'], [buffer])
}
window.read_str_pass = Module.cwrap('read_str_pass', 'void', ['number'])
}
window.cjs = new (class {
str_pass_size() {
return Module.ccall('str_pass_size', 'number')
}
write_string(string) {
const buffer = Module._malloc(string.length + 1)
Module.stringToUTF8(string, buffer, string.length + 1)
return buffer
}
set_string_pass(str_address, size, other = 0) {
const offset = Module._malloc(this.str_pass_size())
Module.setValue(offset, str_address, 'i32')
Module.setValue(offset + 4, size, 'i32')
Module.setValue(offset + 8, other, 'i32')
return offset
} // [START, SIZE, 0]
string_pass(string) {
const str_address = this.write_string(string)
return this.set_string_pass(str_address, string.length)
}
receive(offset) {
const { str_address, str_length, other } = this.get_string_pass(offset)
const string = Module.UTF8ToString(str_address, str_length)
Module._free(offset, this.str_pass_size())
return string
}
get_string_pass(offset) {
const str_address = Module.getValue(offset, 'i32')
const str_length = Module.getValue(offset + 4, 'i32')
const other = Module.getValue(offset + 8, 'i32')
return { str_address, str_length, other }
}
fire_event_bus(key) {
const key_pass = this.string_pass(key)
Module.ccall('fire_event_bus', null, ['number'], [key_pass])
}
})

@ -0,0 +1,41 @@
#include <iostream>
#include <emscripten.h>
#include <exception>
#include "src/class/Element.h"
#include "src/proc/string.cpp"
#include "src/class/EventBus.h"
#include "src/proc/event.cpp"
#include "src/proc/uuid.cpp"
extern "C" {
char* itest();
int str_pass_size();
void fire_event_bus(int key_pass);
}
void say_aye() {
printf("Arrgh mates!\n");
}
char* itest() {
Element p_tag("#foo");
p_tag.addEventListener("click", say_aye);
return "Hi, there";
}
// These are REQUIRED.
int str_pass_size() {
return cjs::str::pass_size();
}
void fire_event_bus(int string_pass) {
return cjs::event::handle_bus_fire(string_pass);
}
int main(int argc, char** argv) {
EM_ASM(InitWrappers());
printf("C++.js has initialized!\n");
return 0;
}

@ -0,0 +1,20 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Emscripten-Generated Code</title>
</head>
<body>
<!-- <div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>
-->
<textarea class="emscripten" id="output" rows="8"></textarea>
<br><br>
<p id="foo" class="bar baz" data-test="3.141">Hello, world!</p>
<script type='text/javascript' src="ems-core.js"></script>
{{{ SCRIPT }}}
<script type="text/javascript" src="init.js"></script>
</body>
</html>

@ -0,0 +1,5 @@
//
// Created by garrettmills on 5/6/20.
//
#include "Component.h"

@ -0,0 +1,19 @@
#ifndef CPPJS_COMPONENT_H
#define CPPJS_COMPONENT_H
class Component {
private:
// =================== HTML TEMPLATE =================== //
char* tmpl = R"V0G0N(
<h1>Hello, world!</h1>
<p>This is a test hello message!</p>
)V0G0N";
// ================= / HTML TEMPLATE =================== //
};
#endif //CPPJS_COMPONENT_H

@ -0,0 +1,161 @@
#include <stdexcept>
#include "Element.h"
#include "../proc/document.cpp"
#include "../proc/string.cpp"
#include "../proc/uuid.cpp"
#include "../class/EventBus.h"
using namespace cjs;
void Element::_identify_and_rebase() {
try {
std::string uuid = this->getUUID();
this->rebase("[data-cjs-uuid='"+uuid+"']");
} catch (std::runtime_error& e) {
std::string new_uuid = uuid::v4();
this->setData("cjs-uuid", new_uuid);
this->rebase("[data-cjs-uuid='"+new_uuid+"']");
}
}
Element::Element(std::string selector, int nth_elem) {
this->_selector_ref = selector;
this->_selector_nth_elem = nth_elem;
this->_identify_and_rebase();
}
Element::Element(Element& parent, std::string innerHTML, std::string selector, int nth_elem) {
this->_selector_ref = selector;
this->_selector_nth_elem = nth_elem;
parent.append(innerHTML);
this->_identify_and_rebase();
}
std::string Element::getInnerHTML() {
cjs_str* html = doc::get_inner_html_of(this->_selector_ref, this->_selector_nth_elem);
std::string return_str = html->to_str();
delete html;
return return_str;
}
std::string Element::getInnerText() {
cjs_str* text = doc::get_inner_text_of(this->_selector_ref, this->_selector_nth_elem);
std::string return_str = text->to_str();
delete text;
return return_str;
}
std::string Element::getID() {
cjs_str* html = doc::get_id_of(this->_selector_ref, this->_selector_nth_elem);
std::string return_str = html->to_str();
delete html;
return return_str;
}
std::string Element::getUUID() {
return this->getData("cjs-uuid");
}
std::string Element::getClassRaw() {
cjs_str* html = doc::get_class_of(this->_selector_ref, this->_selector_nth_elem);
std::string return_str = html->to_str();
delete html;
return return_str;
}
std::vector<std::string> Element::getClasses() {
cjs_str* html = doc::get_class_of(this->_selector_ref, this->_selector_nth_elem);
std::vector<std::string> result = html->split();
delete html;
return result;
}
bool Element::hasClass(std::string cls) {
std::vector<std::string> classes = this->getClasses();
for ( std::string s : classes )
if ( s == cls ) return true;
return false;
}
std::string Element::getAttribute(std::string attr) {
cjs_str* value = doc::_query_select_attribute(attr, this->_selector_ref, this->_selector_nth_elem);
std::string return_str = value->to_str();
delete value;
return return_str;
}
std::string Element::getData(std::string key) {
return this->getAttribute("data-"+key);
}
void Element::setInnerHTML(std::string html) {
doc::set_inner_html_of(this->_selector_ref, html, this->_selector_nth_elem);
}
void Element::setInnerText(std::string text) {
doc::set_inner_text_of(this->_selector_ref, text, this->_selector_nth_elem);
}
void Element::setID(std::string id) {
doc::set_id_of(this->_selector_ref, id, this->_selector_nth_elem);
}
void Element::setClassRaw(std::string cls) {
doc::set_class_of(this->_selector_ref, cls, this->_selector_nth_elem);
}
void Element::addClass(std::string cls) {
if ( !this->hasClass(cls) ) {
std::vector<std::string> classes = this->getClasses();
classes.push_back(cls);
this->setClassRaw(str::join(classes, " "));
}
}
void Element::removeClass(std::string cls) {
if ( this->hasClass(cls) ) {
std::vector<std::string> old_classes = this->getClasses();
std::vector<std::string> new_classes;
for ( std::string maybe_cls : old_classes ) {
if ( maybe_cls != cls )
new_classes.push_back(maybe_cls);
}
this->setClassRaw(str::join(new_classes, " "));
}
}
void Element::toggleClass(std::string cls) {
if ( this->hasClass(cls) ) this->removeClass(cls);
else this->addClass(cls);
}
void Element::setAttribute(std::string attr, std::string value) {
doc::_query_set_attribute(attr, value, this->_selector_ref, this->_selector_nth_elem);
}
void Element::setData(std::string key, std::string value) {
this->setAttribute("data-"+key, value);
}
void Element::append(std::string html) {
this->setInnerHTML(this->getInnerHTML()+html);
}
void Element::addEventListener(std::string dom_event, void(*func)()) {
// Generate the unique function key using the dom_event and uuid
std::string event_key = this->getUUID() + "-" + dom_event;
// Add the function and the key to the event bus
g_event_bus.addHandler(event_key, func);
// Add the event bus link to the element's dom event
doc::link_event_bus(dom_event, event_key, this->_selector_ref, this->_selector_nth_elem);
}
void Element::rebase(std::string query_selector, int nth_elem) {
this->_selector_ref = query_selector;
this->_selector_nth_elem = nth_elem;
}

@ -0,0 +1,44 @@
#ifndef CPPJS_ELEMENT_H
#define CPPJS_ELEMENT_H
class Element {
private:
std::string _selector_ref = "";
int _selector_nth_elem = 0;
void _identify_and_rebase();
public:
Element(std::string selector, int nth_elem = 0);
Element(Element& parent, std::string innerHTML, std::string selector, int nth_elem = 0);
std::string getInnerHTML();
std::string getInnerText();
std::string getID();
std::string getUUID();
std::string getClassRaw();
std::vector<std::string> getClasses();
bool hasClass(std::string cls);
std::string getAttribute(std::string attr);
std::string getData(std::string key);
void setInnerHTML(std::string html);
void setInnerText(std::string text);
void setID(std::string id);
void setClassRaw(std::string cls);
void addClass(std::string cls);
void removeClass(std::string cls);
void toggleClass(std::string cls);
void setAttribute(std::string attr, std::string value);
void setData(std::string key, std::string value);
void append(std::string html);
void addEventListener(std::string dom_event, void(*func)());
void rebase(std::string query_selector, int nth_elem = 0);
};
#include "Element.cpp"
#endif //CPPJS_ELEMENT_H

@ -0,0 +1,25 @@
#include "EventBus.h"
void EventBus::addHandler(std::string key, void (*func)()) {
// If the bus already has this key, add a handler
auto search = this->_bus.find(key);
if ( search != this->_bus.end() ) {
std::vector<void (*)()> func_arr = this->_bus[key];
func_arr.push_back(func);
this->_bus[key] = func_arr;
} else {
std::vector<void (*)()> func_arr;
func_arr.push_back(func);
this->_bus[key] = func_arr;
}
}
void EventBus::fire(std::string key) {
auto search = this->_bus.find(key);
if ( search != this->_bus.end() ) {
std::vector<void (*)()> func_arr = this->_bus[key];
for ( void(*func)() : func_arr ) {
(*func)();
}
}
}

@ -0,0 +1,21 @@
#ifndef CPPJS_EVENTBUS_H
#define CPPJS_EVENTBUS_H
#include <map>
#include <vector>
class EventBus {
private:
std::map<std::string, std::vector<void (*)()>> _bus;
public:
void addHandler(std::string key, void (*func)());
void fire(std::string key);
};
extern EventBus g_event_bus;
EventBus g_event_bus;
#include "EventBus.cpp"
#endif //CPPJS_EVENTBUS_H

@ -0,0 +1,116 @@
#ifndef CJS_PROC_DOCUMENT
#define CJS_PROC_DOCUMENT
#include <string>
#include <stdexcept>
#include "string.cpp"
namespace cjs {
namespace doc {
#include <emscripten.h>
cjs_str* _query_select_property(std::string method, std::string query_selector, int nth_elem = 0) {
int method_pass = cjs::str::pass(cjs::str::cpp_to_c(method), method.length());
int query_pass = cjs::str::pass(cjs::str::cpp_to_c(query_selector), query_selector.length());
int return_str_pass = EM_ASM_INT({
try {
return cjs.string_pass(document.querySelectorAll(cjs.receive($1))[$2][cjs.receive($0)]);
} catch (e) {
return 0;
}
}, method_pass, query_pass, nth_elem);
if ( return_str_pass == 0 ) {
throw std::runtime_error("The property or element is undefined.");
}
cjs_str* return_str = cjs::str::receive(return_str_pass);
return return_str;
}
cjs_str* _query_select_attribute(std::string attribute, std::string query_selector, int nth_elem = 0) {
int attribute_pass = cjs::str::pass(cjs::str::cpp_to_c(attribute), attribute.length());
int query_pass = cjs::str::pass(cjs::str::cpp_to_c(query_selector), query_selector.length());
int return_str_pass = EM_ASM_INT({
try {
return cjs.string_pass(document.querySelectorAll(cjs.receive($1))[$2].getAttribute(cjs.receive($0)));
} catch (e) {
return 0;
}
}, attribute_pass, query_pass, nth_elem);
if ( return_str_pass == 0 ) {
throw std::runtime_error("The attribute or element is undefined.");
}
cjs_str* return_str = cjs::str::receive(return_str_pass);
return return_str;
}
void _query_set_property(std::string property, std::string value, std::string query_selector, int nth_elem = 0) {
int property_pass = cjs::str::pass(cjs::str::cpp_to_c(property), property.length());
int value_pass = cjs::str::pass(cjs::str::cpp_to_c(value), value.length());
int query_pass = cjs::str::pass(cjs::str::cpp_to_c(query_selector), query_selector.length());
EM_ASM({
document.querySelectorAll(cjs.receive($2))[$3][cjs.receive($0)] = cjs.receive($1);
}, property_pass, value_pass, query_pass, nth_elem);
}
void _query_set_attribute(std::string attribute, std::string value, std::string query_selector, int nth_elem = 0) {
int attribute_pass = cjs::str::pass(cjs::str::cpp_to_c(attribute), attribute.length());
int value_pass = cjs::str::pass(cjs::str::cpp_to_c(value), value.length());
int query_pass = cjs::str::pass(cjs::str::cpp_to_c(query_selector), query_selector.length());
EM_ASM({
document.querySelectorAll(cjs.receive($2))[$3].setAttribute(cjs.receive($0), cjs.receive($1));
}, attribute_pass, value_pass, query_pass, nth_elem);
}
void link_event_bus(std::string dom_event, std::string event_bus_key, std::string query_selector, int nth_elem = 0) {
int dom_event_pass = cjs::str::pass(cjs::str::cpp_to_c(dom_event), dom_event.length());
int event_bus_key_pass = cjs::str::pass(cjs::str::cpp_to_c(event_bus_key), event_bus_key.length());
int query_pass = cjs::str::pass(cjs::str::cpp_to_c(query_selector), query_selector.length());
EM_ASM({
document.querySelectorAll(cjs.receive($2))[$3].addEventListener(cjs.receive($0), () => cjs.fire_event_bus(cjs.receive($1)));
}, dom_event_pass, event_bus_key_pass, query_pass, nth_elem);
}
cjs_str* get_inner_html_of(std::string query_selector, int nth_elem = 0) {
return _query_select_property("innerHTML", query_selector, nth_elem);
}
void set_inner_html_of(std::string query_selector, std::string value, int nth_elem = 0) {
_query_set_property("innerHTML", value, query_selector, nth_elem);
}
cjs_str* get_inner_text_of(std::string query_selector, int nth_elem = 0) {
return _query_select_property("innerText", query_selector, nth_elem);
}
void set_inner_text_of(std::string query_selector, std::string value, int nth_elem = 0) {
_query_set_property("innerText", value, query_selector, nth_elem);
}
cjs_str* get_id_of(std::string query_selector, int nth_elem = 0) {
return _query_select_property("id", query_selector, nth_elem);
}
void set_id_of(std::string query_selector, std::string value, int nth_elem = 0) {
_query_set_property("id", value, query_selector, nth_elem);
}
cjs_str* get_class_of(std::string query_selector, int nth_elem = 0) {
return _query_select_attribute("class", query_selector, nth_elem);
}
void set_class_of(std::string query_selector, std::string cls, int nth_elem = 0) {
return _query_set_attribute("class", cls, query_selector, nth_elem);
}
}
}
#endif //CJS_PROC_DOCUMENT

@ -0,0 +1,18 @@
#ifndef CJS_PROC_EVENT
#define CJS_PROC_EVENT
#include <string>
#include "string.cpp"
namespace cjs {
namespace event {
void handle_bus_fire(int key_pass) {
cjs_str* key_str = cjs::str::receive(key_pass);
std::string key = key_str->to_str();
delete key_str;
g_event_bus.fire(key);
}
}
}
#endif //CJS_PROC_EVENT

@ -0,0 +1,102 @@
#ifndef CJS_PROC_STRING
#define CJS_PROC_STRING
#include <vector>
#include <sstream>
#include <ostream>
struct cjs_str {
int length;
char* string;
std::string to_str() {
std::string str(this->string);
return str;
}
void from_str(std::string str) {
this->length = str.length();
char* cstr = new char[str.length() + 1];
strcpy(cstr, str.c_str());
this->string = cstr;
}
std::string c_str() {
return this->to_str().c_str();
}
std::vector<std::string> split() {
std::string s = this->to_str();
std::vector<std::string> result;
std::istringstream iss(s);
for(std::string s; iss >> s; )
result.push_back(s);
return result;
}
};
namespace cjs {
namespace str {
int pass_size() {
return 3 * sizeof(int);
}
char* cpp_to_c(std::string str) {
char* cstr = new char[str.length() + 1];
strcpy(cstr, str.c_str());
return cstr;
}
std::string join(const std::vector<std::string> strings, const char* by) {
if ( strings.size() < 1 ) return "";
else if ( strings.size() == 1 ) return strings.front();
else {
std::stringstream res;
copy(strings.begin(), strings.end() - 1, std::ostream_iterator<std::string>(res, by));
res << strings.back();
return res.str();
}
}
cjs_str* cpp_to_cjs(std::string str) {
return new cjs_str{ (int) str.length(), cpp_to_c(str) };
}
cjs_str* c_to_cjs(char* string, int len) {
return new cjs_str{ len, string };
}
std::string c_to_cpp(char* string) {
std::string str(string);
return str;
}
std::string cjs_to_cpp(cjs_str* cjs) {
return c_to_cpp(cjs->string);
}
cjs_str* receive(int offset) {
// Get the string pass information
// Format: [ string offset, string length, (unused) ]
int* str_pass = reinterpret_cast<int*>(offset);
int length = str_pass[1];
char* string = reinterpret_cast<char*>(str_pass[0]);
free(str_pass);
return new cjs_str{ length, string };
}
int set_pass(int address, int size, int other = 0) {
int* pass = new int[3];
pass[0] = address;
pass[1] = size;
pass[2] = other;
return (int) pass;
}
int pass(char* string, int length) {
return set_pass((int) string, length);
}
}
}
#endif //CJS_PROC_STRING

@ -0,0 +1,44 @@
#ifndef CJS_PROC_UUID
#define CJS_PROC_UUID
#include <random>
#include <sstream>
namespace cjs {
namespace uuid {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> dis(0, 15);
static std::uniform_int_distribution<> dis2(8, 11);
std::string v4() {
std::stringstream ss;
int i;
ss << std::hex;
for (i = 0; i < 8; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 4; i++) {
ss << dis(gen);
}
ss << "-4";
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
ss << dis2(gen);
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 12; i++) {
ss << dis(gen);
};
return ss.str();
}
}
}
// https://stackoverflow.com/questions/24365331/how-can-i-generate-uuid-in-c-without-using-boost-library
#endif // CJS_PROC_UUID
Loading…
Cancel
Save