Reduce compiler runtime code and unify theme paths.

This commit is contained in:
root committed 2026-08-04 03:27:15 +00:00
1 parent 54619748f4
commit 51558716af
20 files changed
+175 -509

No files matched your search

+1 -1
View File
@@ -22,4 +22,4 @@ int main()
EOF
"${CXX:-c++}" -std=c++20 -fpermissive -I. "$source" -lpcre2-8 -o "$binary"
"$binary"
echo "exact decimal SQLite/MySQL request-handle protocol passed"
echo "exact BRRB u64 decimal round-trip passed"
@@ -2,10 +2,7 @@
COMPONENT(Request& context)
{
DValue asset_props;
asset_props["css"]["0"] = "themes/common/css/gauges.css";
asset_props["js"]["0"] = "components/gauges/common.js";
print(component("../theme/assets", asset_props, context));
gauge_assets(context);
String gauge_id = first(context.props["id"].to_string(), "arcgauge-" + std::to_string((u64)time()));
String title = context.props["title"].to_string();
@@ -1,6 +1,14 @@
#load "../../lib/app.uce"
#include <math.h>
void gauge_assets(Request& context)
{
DValue props;
props["css"]["0"] = "themes/common/css/gauges.css";
props["js"]["0"] = "components/gauges/common.js";
print(component("../theme/assets", props, context));
}
f64 gauge_clamp_value(f64 value, f64 min_value, f64 max_value)
{
return(std::max(min_value, std::min(max_value, value)));
@@ -2,10 +2,7 @@
COMPONENT(Request& context)
{
DValue asset_props;
asset_props["css"]["0"] = "themes/common/css/gauges.css";
asset_props["js"]["0"] = "components/gauges/common.js";
print(component("../theme/assets", asset_props, context));
gauge_assets(context);
String gauge_id = first(context.props["id"].to_string(), "needlegauge-" + std::to_string((u64)time()));
String style = context.props["style"].to_string();
@@ -2,10 +2,7 @@
COMPONENT(Request& context)
{
DValue asset_props;
asset_props["css"]["0"] = "themes/common/css/gauges.css";
asset_props["js"]["0"] = "components/gauges/common.js";
print(component("../theme/assets", asset_props, context));
gauge_assets(context);
String gauge_id = first(context.props["id"].to_string(), "progressbar-" + std::to_string((u64)time()));
String style = context.props["style"].to_string();
@@ -1,3 +1,16 @@
void config_theme(DValue& options, String key, String label, String path, String mode, String description, String footer_text, String meta_description, String theme_color)
{
DValue& theme = options[key];
theme["label"] = label;
theme["path"] = path;
theme["mode"] = mode;
theme["mode-class"] = mode == "dark" ? "dark-theme" : "";
theme["description"] = description;
theme["footer_text"] = footer_text;
theme["meta_description"] = meta_description;
theme["theme_color"] = theme_color;
}
DValue get_config()
{
DValue config;
@@ -10,59 +23,13 @@ DValue get_config()
config["filebase"]["path"] = "/tmp/bearer-starter-data";
config["theme"]["key"] = "portal-dark";
config["theme"]["options"]["light"]["label"] = "Starter Light";
config["theme"]["options"]["light"]["path"] = "themes/light/";
config["theme"]["options"]["light"]["mode"] = "light";
config["theme"]["options"]["light"]["mode-class"] = "";
config["theme"]["options"]["light"]["description"] = "Original starter light theme kept for backward compatibility.";
config["theme"]["options"]["light"]["footer_text"] = "BEARER Starter running with the Starter Light theme.";
config["theme"]["options"]["light"]["meta_description"] = "Starter Light theme for the BEARER starter";
config["theme"]["options"]["light"]["theme_color"] = "#3b82f6";
config["theme"]["options"]["dark"]["label"] = "Starter Dark";
config["theme"]["options"]["dark"]["path"] = "themes/dark/";
config["theme"]["options"]["dark"]["mode"] = "dark";
config["theme"]["options"]["dark"]["mode-class"] = "dark-theme";
config["theme"]["options"]["dark"]["description"] = "Original starter dark theme kept for backward compatibility.";
config["theme"]["options"]["dark"]["footer_text"] = "BEARER Starter running with the Starter Dark theme.";
config["theme"]["options"]["dark"]["meta_description"] = "Starter Dark theme for the BEARER starter";
config["theme"]["options"]["dark"]["theme_color"] = "#0f172a";
config["theme"]["options"]["portal-light"]["label"] = "AI Portal Light";
config["theme"]["options"]["portal-light"]["path"] = "themes/portal-light/";
config["theme"]["options"]["portal-light"]["mode"] = "light";
config["theme"]["options"]["portal-light"]["mode-class"] = "";
config["theme"]["options"]["portal-light"]["description"] = "Dense, corporate portal layout in a light palette.";
config["theme"]["options"]["portal-light"]["footer_text"] = "BEARER Starter running with the AI Portal Light starter theme.";
config["theme"]["options"]["portal-light"]["meta_description"] = "AI Portal Light theme for the BEARER starter";
config["theme"]["options"]["portal-light"]["theme_color"] = "#0f68ad";
config["theme"]["options"]["portal-dark"]["label"] = "AI Portal Dark";
config["theme"]["options"]["portal-dark"]["path"] = "themes/portal-dark/";
config["theme"]["options"]["portal-dark"]["mode"] = "dark";
config["theme"]["options"]["portal-dark"]["mode-class"] = "dark-theme";
config["theme"]["options"]["portal-dark"]["description"] = "Glassy dark portal shell and the current default starter theme.";
config["theme"]["options"]["portal-dark"]["footer_text"] = "BEARER Starter running with the AI Portal Dark starter theme.";
config["theme"]["options"]["portal-dark"]["meta_description"] = "AI Portal Dark theme for the BEARER starter";
config["theme"]["options"]["portal-dark"]["theme_color"] = "#0f172a";
config["theme"]["options"]["localfirst"]["label"] = "Local First";
config["theme"]["options"]["localfirst"]["path"] = "themes/localfirst/";
config["theme"]["options"]["localfirst"]["mode"] = "dark";
config["theme"]["options"]["localfirst"]["mode-class"] = "dark-theme";
config["theme"]["options"]["localfirst"]["description"] = "llm2-derived admin shell with sidebar chrome.";
config["theme"]["options"]["localfirst"]["footer_text"] = "BEARER Starter running with the Local First starter theme.";
config["theme"]["options"]["localfirst"]["meta_description"] = "Local First admin-shell theme for the BEARER starter";
config["theme"]["options"]["localfirst"]["theme_color"] = "#020913";
config["theme"]["options"]["retro-gaming"]["label"] = "Retro Gaming";
config["theme"]["options"]["retro-gaming"]["path"] = "themes/retro-gaming/";
config["theme"]["options"]["retro-gaming"]["mode"] = "dark";
config["theme"]["options"]["retro-gaming"]["mode-class"] = "dark-theme";
config["theme"]["options"]["retro-gaming"]["description"] = "CRT scanlines, pixel font, neon glow.";
config["theme"]["options"]["retro-gaming"]["footer_text"] = "INSERT COIN // BEARER Starter running the Retro Gaming theme.";
config["theme"]["options"]["retro-gaming"]["meta_description"] = "Retro Gaming pixel theme for the BEARER starter";
config["theme"]["options"]["retro-gaming"]["theme_color"] = "#0a0a1a";
DValue& themes = config["theme"]["options"];
config_theme(themes, "light", "Starter Light", "themes/light/", "light", "Original starter light theme kept for backward compatibility.", "BEARER Starter running with the Starter Light theme.", "Starter Light theme for the BEARER starter", "#3b82f6");
config_theme(themes, "dark", "Starter Dark", "themes/dark/", "dark", "Original starter dark theme kept for backward compatibility.", "BEARER Starter running with the Starter Dark theme.", "Starter Dark theme for the BEARER starter", "#0f172a");
config_theme(themes, "portal-light", "AI Portal Light", "themes/portal-light/", "light", "Dense, corporate portal layout in a light palette.", "BEARER Starter running with the AI Portal Light starter theme.", "AI Portal Light theme for the BEARER starter", "#0f68ad");
config_theme(themes, "portal-dark", "AI Portal Dark", "themes/portal-dark/", "dark", "Glassy dark portal shell and the current default starter theme.", "BEARER Starter running with the AI Portal Dark starter theme.", "AI Portal Dark theme for the BEARER starter", "#0f172a");
config_theme(themes, "localfirst", "Local First", "themes/localfirst/", "dark", "llm2-derived admin shell with sidebar chrome.", "BEARER Starter running with the Local First starter theme.", "Local First admin-shell theme for the BEARER starter", "#020913");
config_theme(themes, "retro-gaming", "Retro Gaming", "themes/retro-gaming/", "dark", "CRT scanlines, pixel font, neon glow.", "INSERT COIN // BEARER Starter running the Retro Gaming theme.", "Retro Gaming pixel theme for the BEARER starter", "#0a0a1a");
config["menu"][""]["title"] = "Home";
config["menu"][""]["hidden"].set_bool(true);
@@ -0,0 +1,28 @@
#load "../../lib/app.uce"
inline void render_common_theme_page(Request& context)
{
String main_html = context.call["fragments"]["main"].to_string();
String key = context.cfg.get_by_path("theme/key").to_string();
String body_class;
if(key == "portal-dark") body_class = "portal-theme portal-dark-theme dark-theme";
else if(key == "portal-light") body_class = "portal-theme portal-light-theme";
<>
<!doctype html>
<html class="no-js <?= context.cfg.get_by_path("theme/mode-class").to_string() ?>" lang="en" data-theme-key="<?= key ?>">
<head>
<?: component("../../components/theme/head", context) ?>
</head>
<? if(body_class == "") { ?><body><? } else { ?><body class="<?= body_class ?>"><? } ?>
<? if(key == "retro-gaming") { ?><div class="retro-stars"></div><? } ?>
<?: component("../../components/theme/global_controls", context) ?>
<?: component("../../components/theme/standard_nav", context) ?>
<div id="content">
<?: main_html ?>
</div>
<?: component("../../components/theme/footer", context) ?>
</body>
</html>
</>
}
@@ -1,28 +1,3 @@
#load "../../lib/app.uce"
#load "../common/page.uce"
COMPONENT(Request& context)
{
String main_html = context.call["fragments"]["main"].to_string();
DValue nav_props;
nav_props["account_wrapper_class"] = "nav-account nav-menu";
DValue footer_props;
<>
<!doctype html>
<html class="no-js <?= context.cfg.get_by_path("theme/mode-class").to_string() ?>" lang="en" data-theme-key="<?= context.cfg.get_by_path("theme/key").to_string() ?>">
<head>
<?: component("../../components/theme/head", context) ?>
</head>
<body>
<?: component("../../components/theme/global_controls", context) ?>
<?: component("../../components/theme/standard_nav", nav_props, context) ?>
<div id="content">
<?: main_html ?>
</div>
<?: component("../../components/theme/footer", footer_props, context) ?>
</body>
</html>
</>
}
COMPONENT(Request& context) { render_common_theme_page(context); }
@@ -1,25 +1,3 @@
#load "../../lib/app.uce"
#load "../common/page.uce"
COMPONENT(Request& context)
{
String main_html = context.call["fragments"]["main"].to_string();
DValue footer_props;
<>
<!doctype html>
<html class="no-js <?= context.cfg.get_by_path("theme/mode-class").to_string() ?>" lang="en" data-theme-key="<?= context.cfg.get_by_path("theme/key").to_string() ?>">
<head>
<?: component("../../components/theme/head", context) ?>
</head>
<body>
<?: component("../../components/theme/global_controls", context) ?>
<?: component("../../components/theme/standard_nav", context) ?>
<div id="content">
<?: main_html ?>
</div>
<?: component("../../components/theme/footer", footer_props, context) ?>
</body>
</html>
</>
}
COMPONENT(Request& context) { render_common_theme_page(context); }
@@ -1,28 +1,3 @@
#load "../../lib/app.uce"
#load "../common/page.uce"
COMPONENT(Request& context)
{
String main_html = context.call["fragments"]["main"].to_string();
DValue nav_props;
nav_props["account_wrapper_class"] = "nav-account nav-menu";
DValue footer_props;
<>
<!doctype html>
<html class="no-js <?= context.cfg.get_by_path("theme/mode-class").to_string() ?>" lang="en" data-theme-key="<?= context.cfg.get_by_path("theme/key").to_string() ?>">
<head>
<?: component("../../components/theme/head", context) ?>
</head>
<body class="portal-theme portal-dark-theme dark-theme">
<?: component("../../components/theme/global_controls", context) ?>
<?: component("../../components/theme/standard_nav", nav_props, context) ?>
<div id="content">
<?: main_html ?>
</div>
<?: component("../../components/theme/footer", footer_props, context) ?>
</body>
</html>
</>
}
COMPONENT(Request& context) { render_common_theme_page(context); }
@@ -1,26 +1,3 @@
#load "../../lib/app.uce"
#load "../common/page.uce"
COMPONENT(Request& context)
{
String main_html = context.call["fragments"]["main"].to_string();
DValue footer_props;
footer_props["inner_class"] = "footer-inner";
<>
<!doctype html>
<html class="no-js <?= context.cfg.get_by_path("theme/mode-class").to_string() ?>" lang="en" data-theme-key="<?= context.cfg.get_by_path("theme/key").to_string() ?>">
<head>
<?: component("../../components/theme/head", context) ?>
</head>
<body class="portal-theme portal-light-theme">
<?: component("../../components/theme/global_controls", context) ?>
<?: component("../../components/theme/standard_nav", context) ?>
<div id="content">
<?: main_html ?>
</div>
<?: component("../../components/theme/footer", footer_props, context) ?>
</body>
</html>
</>
}
COMPONENT(Request& context) { render_common_theme_page(context); }
@@ -1,29 +1,3 @@
#load "../../lib/app.uce"
#load "../common/page.uce"
COMPONENT(Request& context)
{
String main_html = context.call["fragments"]["main"].to_string();
DValue nav_props;
nav_props["account_wrapper_class"] = "nav-account nav-menu";
DValue footer_props;
<>
<!doctype html>
<html class="no-js <?= context.cfg.get_by_path("theme/mode-class").to_string() ?>" lang="en" data-theme-key="<?= context.cfg.get_by_path("theme/key").to_string() ?>">
<head>
<?: component("../../components/theme/head", context) ?>
</head>
<body>
<div class="retro-stars"></div>
<?: component("../../components/theme/global_controls", context) ?>
<?: component("../../components/theme/standard_nav", nav_props, context) ?>
<div id="content">
<?: main_html ?>
</div>
<?: component("../../components/theme/footer", footer_props, context) ?>
</body>
</html>
</>
}
COMPONENT(Request& context) { render_common_theme_page(context); }
-77
View File
@@ -1,77 +0,0 @@
var enable_debug = true;
var starterReady = (typeof $ !== 'undefined' && $.ready)
? $.ready.bind($)
: function(callback) {
if (document.readyState !== 'loading') {
callback();
} else {
document.addEventListener('DOMContentLoaded', callback);
}
};
var UI = {
smoothScrollToNamedAnchors: function() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
},
enablePageTransitions: function() {
var style = document.createElement('style');
style.textContent = `
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.25s;
}
::view-transition-old(root) {
animation-name: fade-out;
}
::view-transition-new(root) {
animation-name: fade-in;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}`;
document.body.appendChild(style);
document.addEventListener('click', e => {
const link = e.target.closest('a[href]');
if (!link) return;
e.preventDefault();
document.startViewTransition(() => {
window.location.href = link.href;
});
});
},
init: function() {
//UI.enablePageTransitions();
UI.smoothScrollToNamedAnchors();
document.body.classList.add('loaded');
},
}
starterReady(UI.init);
+1
View File
@@ -0,0 +1 @@
../../examples/bearer-starter/js/site.js
+39 -100
View File
@@ -317,6 +317,15 @@ void store_field(Bytes& code, const std::string& type, unsigned offset)
wasm::append_uleb(code, offset);
}
void store_i32_constant(Bytes& code, unsigned object, std::int32_t value, unsigned offset)
{
code.push_back(0x20);
wasm::append_uleb(code, object);
code.push_back(0x41);
wasm::append_sleb32(code, value);
store_field(code, "s32", offset);
}
void managed_payload_pointer(Bytes& code, unsigned local, const std::string& type)
{
code.push_back(0x20);
@@ -2641,14 +2650,7 @@ std::pair<Bytes, unsigned> FunctionLowerer::allocate_blob(const std::string& typ
append(code, module_.marker(location));
code.insert(code.end(), {0x00, 0x0b});
for (const auto [header, offset] : {std::pair<std::int32_t, unsigned>{1, 0}, {1, 4}, {static_cast<std::int32_t>(type_id), 8}})
{
code.push_back(0x20);
wasm::append_uleb(code, pointer);
code.push_back(0x41);
wasm::append_sleb32(code, header);
code.insert(code.end(), {0x36, 0x02});
wasm::append_uleb(code, offset);
}
store_i32_constant(code, pointer, header, offset);
code.push_back(0x20);
wasm::append_uleb(code, pointer);
code.push_back(0x20);
@@ -3542,14 +3544,7 @@ std::pair<Bytes, std::string> FunctionLowerer::expression(Expr* value, bool valu
{static_cast<std::int32_t>(type_id), 8},
{static_cast<std::int32_t>(size), 12},
{static_cast<std::int32_t>(slot), 16}})
{
code.push_back(0x20);
wasm::append_uleb(code, pointer);
code.push_back(0x41);
wasm::append_sleb32(code, header);
code.insert(code.end(), {0x36, 0x02});
wasm::append_uleb(code, offset);
}
store_i32_constant(code, pointer, header, offset);
code.insert(code.end(), {0x23, 0x01, 0x41, 0x01, 0x6a, 0x24, 0x01});
for (std::size_t i = 0; i < captures.size(); ++i)
{
@@ -3605,14 +3600,7 @@ std::pair<Bytes, std::string> FunctionLowerer::expression(Expr* value, bool valu
{1, 4},
{static_cast<std::int32_t>(module_.tuple_type(type)), 8},
{static_cast<std::int32_t>(layout.size), 12}})
{
code.push_back(0x20);
wasm::append_uleb(code, pointer);
code.push_back(0x41);
wasm::append_sleb32(code, header);
code.insert(code.end(), {0x36, 0x02});
wasm::append_uleb(code, offset);
}
store_i32_constant(code, pointer, header, offset);
code.insert(code.end(), {0x23, 0x01, 0x41, 0x01, 0x6a, 0x24, 0x01});
for (std::size_t i = 0; i < tuple->items.size(); ++i)
{
@@ -4089,14 +4077,7 @@ std::pair<Bytes, std::string> FunctionLowerer::expression(Expr* value, bool valu
code.push_back(0x00);
code.push_back(0x0b);
for (const auto [header, offset] : {std::pair<std::int32_t, unsigned>{1, 0}, {1, 4}, {1, 8}})
{
code.insert(code.end(), {0x20});
wasm::append_uleb(code, pointer);
code.push_back(0x41);
wasm::append_sleb32(code, header);
code.insert(code.end(), {0x36, 0x02});
wasm::append_uleb(code, offset);
}
store_i32_constant(code, pointer, header, offset);
code.insert(code.end(), {0x20});
wasm::append_uleb(code, pointer);
code.insert(code.end(), {0x20});
@@ -4997,14 +4978,7 @@ std::pair<Bytes, std::string> FunctionLowerer::expression(Expr* value, bool valu
{1, 4},
{static_cast<std::int32_t>(aggregate.type_id), 8},
{static_cast<std::int32_t>(layout.size), 12}})
{
code.push_back(0x20);
wasm::append_uleb(code, pointer);
code.push_back(0x41);
wasm::append_sleb32(code, header);
code.insert(code.end(), {0x36, 0x02});
wasm::append_uleb(code, offset);
}
store_i32_constant(code, pointer, header, offset);
code.insert(code.end(), {0x23, 0x01, 0x41, 0x01, 0x6a, 0x24, 0x01});
for (std::size_t i = 0; i < arguments->size(); ++i)
{
@@ -6091,76 +6065,41 @@ std::vector<Bytes> Module::runtime_bodies() const
0x20, 0x00, 0x28, 0x02, 0x18, 0x22, 0x01, 0x45, 0x04, 0x40, 0x05, 0x20, 0x01, 0x10});
wasm::append_uleb(code, import_index("bearer_free"));
code.insert(code.end(), {0x0b, 0x0b});
for (const auto& [type_id, captures] : closure_types_)
auto release_aggregate_fields = [&](unsigned type_id, const std::vector<std::string>& fields, unsigned first_offset)
{
const AggregateLayout layout = aggregate_layout(captures, 24);
std::vector<unsigned> managed;
for (unsigned i = 0; i < captures.size(); ++i)
if (managed_type(captures[i]))
managed.push_back(i);
if (managed.empty())
continue;
code.insert(code.end(), {0x20, 0x00, 0x28, 0x02, 0x08, 0x41});
wasm::append_sleb32(code, static_cast<std::int32_t>(type_id));
code.insert(code.end(), {0x46, 0x04, 0x40});
for (unsigned i : managed)
{
code.push_back(0x20);
wasm::append_uleb(code, 0);
load_field(code, captures[i], layout.offsets[i]);
code.push_back(0x10);
wasm::append_uleb(code, release_index());
}
code.push_back(0x0b);
}
for (const auto& [name, aggregate] : structs_)
{
std::vector<std::string> field_types;
for (const auto& [field_name, field_type] : aggregate.fields)
field_types.push_back(field_type);
const AggregateLayout layout = aggregate_layout(field_types, 16);
std::vector<unsigned> managed;
for (unsigned i = 0; i < aggregate.fields.size(); ++i)
if (managed_type(aggregate.fields[i].second))
managed.push_back(i);
if (managed.empty())
continue;
code.insert(code.end(), {0x20, 0x00, 0x28, 0x02, 0x08, 0x41});
wasm::append_sleb32(code, static_cast<std::int32_t>(aggregate.type_id));
code.insert(code.end(), {0x46, 0x04, 0x40});
for (unsigned i : managed)
{
code.push_back(0x20);
wasm::append_uleb(code, 0);
load_field(code, aggregate.fields[i].second, layout.offsets[i]);
code.push_back(0x10);
wasm::append_uleb(code, release_index());
}
code.push_back(0x0b);
}
for (const auto& [type, id] : tuples_)
{
const auto fields = aggregate_elements(type);
const AggregateLayout layout = aggregate_layout(fields, 16);
std::vector<unsigned> managed;
const AggregateLayout layout = aggregate_layout(fields, first_offset);
bool guarded = false;
for (unsigned i = 0; i < fields.size(); ++i)
if (managed_type(fields[i]))
managed.push_back(i);
if (managed.empty())
continue;
code.insert(code.end(), {0x20, 0x00, 0x28, 0x02, 0x08, 0x41});
wasm::append_sleb32(code, static_cast<std::int32_t>(id));
code.insert(code.end(), {0x46, 0x04, 0x40});
for (unsigned i : managed)
{
if (!managed_type(fields[i]))
continue;
if (!guarded)
{
code.insert(code.end(), {0x20, 0x00, 0x28, 0x02, 0x08, 0x41});
wasm::append_sleb32(code, static_cast<std::int32_t>(type_id));
code.insert(code.end(), {0x46, 0x04, 0x40});
guarded = true;
}
code.push_back(0x20);
wasm::append_uleb(code, 0);
load_field(code, fields[i], layout.offsets[i]);
code.push_back(0x10);
wasm::append_uleb(code, release_index());
}
code.push_back(0x0b);
if (guarded)
code.push_back(0x0b);
};
for (const auto& [type_id, captures] : closure_types_)
release_aggregate_fields(type_id, captures, 24);
for (const auto& [name, aggregate] : structs_)
{
std::vector<std::string> fields;
for (const auto& [field_name, field_type] : aggregate.fields)
fields.push_back(field_type);
release_aggregate_fields(aggregate.type_id, fields, 16);
}
for (const auto& [type, id] : tuples_)
release_aggregate_fields(id, aggregate_elements(type), 16);
code.insert(code.end(), {0x23, 0x01, 0x41, 0x01, 0x6b, 0x24, 0x01, 0x20, 0x00, 0x10});
wasm::append_uleb(code, import_index("bearer_free"));
code.push_back(0x0b);
+20 -1
View File
@@ -34,7 +34,9 @@
#include "fcgicc.h"
#include <atomic>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <errno.h> // E*
@@ -51,6 +53,17 @@
namespace {
std::atomic<u64> websocket_connection_sequence{0};
static u64 next_websocket_connection_sequence()
{
u64 current = websocket_connection_sequence.load(std::memory_order_relaxed);
while(current != std::numeric_limits<u64>::max())
if(websocket_connection_sequence.compare_exchange_weak(current, current + 1, std::memory_order_relaxed))
return(current + 1);
return(0);
}
struct TransportLimits {
u64 max_client_connections = 256;
u64 max_http_header_bytes = 16 * 1024;
@@ -838,8 +851,14 @@ FastCGIServer::validate_websocket_upgrade(FastCGIRequest& request, Connection& c
void
FastCGIServer::begin_websocket_upgrade(FastCGIRequest& request, Connection& connection, String& data)
{
u64 sequence = next_websocket_connection_sequence();
if(sequence == 0)
{
reject_http_connection(connection, "HTTP/1.1 503 Service Unavailable", "WebSocket connection ID space is exhausted");
return;
}
connection.is_websocket = true;
connection.websocket_connection_id = std::to_string(getpid()) + ":" + std::to_string(connection.client_socket);
connection.websocket_connection_id = std::to_string(getpid()) + ":" + std::to_string(sequence);
connection.websocket_scope = first(
request.params["SCRIPT_FILENAME"],
request.params["DOCUMENT_URI"],
-26
View File
@@ -270,8 +270,6 @@ DValue MySQL::get_pending_result()
return(result_data);
}
static bool mysql_has_unquoted_positional_placeholder(String query, bool no_backslash_escapes);
DValue MySQL::query(String q)
{
affected_rows = 0;
@@ -300,30 +298,6 @@ DValue MySQL::query(String q)
return(result);
}
static bool mysql_has_unquoted_positional_placeholder(String query, bool no_backslash_escapes)
{
char quote = 0;
bool line_comment = false, block_comment = false;
for(size_t i = 0; i < query.size(); i++)
{
char c = query[i], next = i + 1 < query.size() ? query[i + 1] : 0;
if(line_comment) { if(c == '\n' || c == '\r') line_comment = false; continue; }
if(block_comment) { if(c == '*' && next == '/') { block_comment = false; i++; } continue; }
if(quote)
{
if(!no_backslash_escapes && c == '\\') { if(next) i++; continue; }
if(c == quote) { if(next == quote) i++; else quote = 0; }
continue;
}
if(c == '\'' || c == '"' || c == '`') { quote = c; continue; }
if(c == '#') { line_comment = true; continue; }
if(c == '-' && next == '-' && (i + 2 >= query.size() || isspace((unsigned char)query[i + 2]))) { line_comment = true; i++; continue; }
if(c == '/' && next == '*') { block_comment = true; i++; continue; }
if(c == '?') return(true);
}
return(false);
}
DValue MySQL::query(String q, StringMap params)
{
// Positional ? placeholders survive named substitution (ordinary values
+24
View File
@@ -1,5 +1,29 @@
#pragma once
inline bool mysql_has_unquoted_positional_placeholder(const String& query, bool no_backslash_escapes)
{
char quote = 0;
bool line_comment = false, block_comment = false;
for(size_t i = 0; i < query.size(); i++)
{
char c = query[i], next = i + 1 < query.size() ? query[i + 1] : 0;
if(line_comment) { if(c == '\n' || c == '\r') line_comment = false; continue; }
if(block_comment) { if(c == '*' && next == '/') { block_comment = false; i++; } continue; }
if(quote)
{
if(!no_backslash_escapes && c == '\\') { if(next) i++; continue; }
if(c == quote) { if(next == quote) i++; else quote = 0; }
continue;
}
if(c == '\'' || c == '"' || c == '`') { quote = c; continue; }
if(c == '#') { line_comment = true; continue; }
if(c == '-' && next == '-' && (i + 2 >= query.size() || std::isspace((unsigned char)query[i + 2]))) { line_comment = true; i++; continue; }
if(c == '/' && next == '*') { block_comment = true; i++; continue; }
if(c == '?') return(true);
}
return(false);
}
struct MySQLFieldInfo {
String name;
String table;
+6 -12
View File
@@ -191,24 +191,18 @@ static DValue wasm_decode_dvalue_result(String encoded)
brb_decode(encoded, out, &error);
return(out);
}
DValue file_stat(String path)
template<typename Hostcall>
static DValue wasm_file_dvalue(String path, Hostcall hostcall)
{
String current = wasm_current_unit_file();
size_t required = bearer_host_file_stat(path.data(), path.size(), current.data(), current.size(), 0, 0);
size_t required = hostcall(path.data(), path.size(), current.data(), current.size(), 0, 0);
String encoded(required, 0);
size_t got = required ? bearer_host_file_stat(path.data(), path.size(), current.data(), current.size(), &encoded[0], required) : 0;
encoded.resize(got <= required ? got : 0);
return(wasm_decode_dvalue_result(encoded));
}
DValue dir_list(String path)
{
String current = wasm_current_unit_file();
size_t required = bearer_host_dir_list(path.data(), path.size(), current.data(), current.size(), 0, 0);
String encoded(required, 0);
size_t got = required ? bearer_host_dir_list(path.data(), path.size(), current.data(), current.size(), &encoded[0], required) : 0;
size_t got = required ? hostcall(path.data(), path.size(), current.data(), current.size(), &encoded[0], required) : 0;
encoded.resize(got <= required ? got : 0);
return(wasm_decode_dvalue_result(encoded));
}
DValue file_stat(String path) { return(wasm_file_dvalue(path, bearer_host_file_stat)); }
DValue dir_list(String path) { return(wasm_file_dvalue(path, bearer_host_dir_list)); }
bool file_rename(String from, String to)
{
String current = wasm_current_unit_file();
+5 -16
View File
@@ -242,25 +242,14 @@ void on_request_fault_signal(int sig)
on_segfault(sig);
}
void install_request_fault_handlers()
void set_request_fault_handlers(void (*handler)(int))
{
signal(SIGSEGV, on_request_fault_signal);
signal(SIGABRT, on_request_fault_signal);
signal(SIGBUS, on_request_fault_signal);
signal(SIGILL, on_request_fault_signal);
signal(SIGFPE, on_request_fault_signal);
signal(SIGALRM, on_request_fault_signal);
for(int sig : {SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE, SIGALRM})
signal(sig, handler);
}
void restore_request_fault_handlers()
{
signal(SIGSEGV, on_segfault);
signal(SIGABRT, on_segfault);
signal(SIGBUS, on_segfault);
signal(SIGILL, on_segfault);
signal(SIGFPE, on_segfault);
signal(SIGALRM, on_segfault);
}
void install_request_fault_handlers() { set_request_fault_handlers(on_request_fault_signal); }
void restore_request_fault_handlers() { set_request_fault_handlers(on_segfault); }
String current_ws_scope()
+10 -80
View File
@@ -22,6 +22,7 @@ static const char* WASM_DB_UNAVAILABLE =
"database connector is not available in the wasm workspace";
extern "C" size_t bearer_host_mysql(const char* in, size_t in_len, char* out, size_t cap);
extern "C" size_t bearer_host_sqlite(const char* in, size_t in_len, char* out, size_t cap);
extern "C" size_t bearer_host_zip(const char* in, size_t in_len, char* out, size_t cap);
extern "C" size_t bearer_host_units(const char* in, size_t in_len, char* out, size_t cap);
extern "C" size_t bearer_host_regex_capy(const char* in, size_t in_len, char* out, size_t cap);
@@ -43,11 +44,10 @@ static DValue wasm_sized_hostcall(DValue request, size_t (*hostcall)(const char*
return(response);
}
static DValue wasm_zip_call(DValue request)
{
DValue response = wasm_sized_hostcall(request, bearer_host_zip);
return(response);
}
static DValue wasm_zip_call(DValue request) { return(wasm_sized_hostcall(request, bearer_host_zip)); }
static DValue wasm_units_call(DValue request) { return(wasm_sized_hostcall(request, bearer_host_units)); }
static DValue wasm_mysql_call(DValue request) { return(wasm_sized_hostcall(request, bearer_host_mysql)); }
static DValue wasm_sqlite_call(DValue request) { return(wasm_sized_hostcall(request, bearer_host_sqlite)); }
static bool wasm_zip_apply(DValue request, DValue& response)
{
@@ -114,11 +114,6 @@ String gz_uncompress(String compressed)
return(response["result"].to_string());
}
static DValue wasm_units_call(DValue request)
{
return(wasm_sized_hostcall(request, bearer_host_units));
}
DValue unit_info(String path)
{
DValue request;
@@ -171,11 +166,6 @@ static size_t bearer_copy_staged(String& staged, char* out, size_t cap);
static bool bearer_decode_brrb_span(const char* value, size_t value_len, DValue& decoded);
static String wasm_unit_call_encoded_result;
static DValue wasm_mysql_call(DValue request)
{
return(wasm_sized_hostcall(request, bearer_host_mysql));
}
bool MySQL::connect(String host, String username, String password, String database)
{
request_host = host;
@@ -317,34 +307,10 @@ String MySQL::parse_query_parameters(String query, StringMap map)
return(result);
}
static bool wasm_mysql_has_unquoted_positional_placeholder(String query)
{
char quote = 0;
bool line_comment = false, block_comment = false;
for(size_t i = 0; i < query.size(); i++)
{
char c = query[i], next = i + 1 < query.size() ? query[i + 1] : 0;
if(line_comment) { if(c == '\n' || c == '\r') line_comment = false; continue; }
if(block_comment) { if(c == '*' && next == '/') { block_comment = false; i++; } continue; }
if(quote)
{
if(c == '\\') { if(next) i++; continue; }
if(c == quote) { if(next == quote) i++; else quote = 0; }
continue;
}
if(c == '\'' || c == '"' || c == '`') { quote = c; continue; }
if(c == '#') { line_comment = true; continue; }
if(c == '-' && next == '-' && (i + 2 >= query.size() || isspace((unsigned char)query[i + 2]))) { line_comment = true; i++; continue; }
if(c == '/' && next == '*') { block_comment = true; i++; continue; }
if(c == '?') return(true);
}
return(false);
}
DValue MySQL::query(String q)
{
affected_rows = 0;
if(!connection && wasm_mysql_has_unquoted_positional_placeholder(q))
if(!connection && mysql_has_unquoted_positional_placeholder(q, false))
{
_preload_next_error_code = 2000;
statement_info = "mysql positional ? placeholders are not supported; use named :name placeholders";
@@ -373,7 +339,7 @@ DValue MySQL::query(String q)
DValue MySQL::query(String q, StringMap params)
{
if(!connection && wasm_mysql_has_unquoted_positional_placeholder(q))
if(!connection && mysql_has_unquoted_positional_placeholder(q, false))
{
_preload_next_error_code = 2000;
statement_info = "mysql positional ? placeholders are not supported; use named :name placeholders";
@@ -405,24 +371,6 @@ DValue MySQL::get_pending_result() { return(DValue()); }
// a per-workspace handle table). One BRRB2-marshalled hostcall carries
// {op,handle,path,query,params} in and {handle,result,insert_id,affected,
// error_code,statement_info} out. `connection` holds the host handle (>0).
extern "C" size_t bearer_host_sqlite(const char* in, size_t in_len, char* out, size_t cap);
static DValue wasm_sqlite_call(DValue request)
{
String encoded = brb_encode(request);
size_t need = bearer_host_sqlite(encoded.data(), encoded.size(), 0, 0);
if(need == 0)
return(DValue());
String buffer(need, 0);
size_t got = bearer_host_sqlite(encoded.data(), encoded.size(), &buffer[0], need);
if(got == 0 || got > need)
return(DValue());
DValue response;
String error;
brb_decode(String(buffer.data(), got), response, &error);
return(response);
}
void SQLite::set_error(s32 code, String info) { error_code = code; statement_info = info; }
bool SQLite::connect(String path)
@@ -1695,13 +1643,7 @@ size_t bearer_file_read(u64 handle, u64 length, char* out, size_t cap)
}
return(wasm_file_read_result.size());
}
if(cap < wasm_file_read_result.size())
return(wasm_file_read_result.size());
if(!wasm_file_read_result.empty())
memcpy(out, wasm_file_read_result.data(), wasm_file_read_result.size());
size_t size = wasm_file_read_result.size();
wasm_file_read_result.clear();
return(size);
return(bearer_copy_staged(wasm_file_read_result, out, cap));
}
u64 bearer_file_write(u64 handle, const char* data, size_t data_len)
@@ -1736,13 +1678,7 @@ size_t bearer_file_temp(const char* prefix, size_t prefix_len, char* out, size_t
wasm_file_temp_result = file_temp(String(prefix ? prefix : "", prefix ? prefix_len : 0));
return(wasm_file_temp_result.size());
}
if(cap < wasm_file_temp_result.size())
return(wasm_file_temp_result.size());
if(!wasm_file_temp_result.empty())
memcpy(out, wasm_file_temp_result.data(), wasm_file_temp_result.size());
size_t size = wasm_file_temp_result.size();
wasm_file_temp_result.clear();
return(size);
return(bearer_copy_staged(wasm_file_temp_result, out, cap));
}
void bearer_file_unlink(const char* path, size_t path_len)
@@ -1867,13 +1803,7 @@ static size_t bearer_component_capture_impl(const char* target, size_t target_le
}
return(wasm_component_capture_result.size());
}
if(cap < wasm_component_capture_result.size())
return(wasm_component_capture_result.size());
if(!wasm_component_capture_result.empty())
memcpy(out, wasm_component_capture_result.data(), wasm_component_capture_result.size());
size_t size = wasm_component_capture_result.size();
wasm_component_capture_result.clear();
return(size);
return(bearer_copy_staged(wasm_component_capture_result, out, cap));
}
size_t bearer_component_capture(const char* target, size_t target_len, char* out, size_t cap)