another round of optimizations

This commit is contained in:
CoolElectronics 2024-03-11 17:41:34 -04:00
parent c09027d0c3
commit 00995d4819
No known key found for this signature in database
GPG key ID: F63593D168636C50

View file

@ -1,9 +1,12 @@
import { assert } from "./asserts"; import { assert } from "./asserts";
const Fragment = Symbol(); // enables a small terser optimization
let document = self.document;
let Fragment = Symbol();
// We add some extra properties into various objects throughout, better to use symbols and not interfere. this is just a tiny optimization // We add some extra properties into various objects throughout, better to use symbols and not interfere. this is just a tiny optimization
const [USE_MAPFN, TARGET, PROXY, STEPS, LISTENERS, IF] = [, , , , , ,].fill().map(Symbol); let [USE_MAPFN, TARGET, PROXY, STEPS, LISTENERS, IF] = [, , , , , ,].fill().map(Symbol);
// whether to return the true value from a stateful object or a "trap" containing the pointer // whether to return the true value from a stateful object or a "trap" containing the pointer
@ -39,7 +42,7 @@ Object.defineProperty(window, "use", {
}); });
Object.assign(window, { isDLPtr, h, stateful, handle, $if, Fragment }); Object.assign(window, { isDLPtr, h, stateful, handle, $if, Fragment });
const TRAPS = new Map; let TRAPS = new Map;
// This wraps the target in a proxy, doing 2 things: // This wraps the target in a proxy, doing 2 things:
// - whenever a property is accessed, return a "trap" that catches and records accessors // - whenever a property is accessed, return a "trap" that catches and records accessors
// - whenever a property is set, notify the subscribed listeners // - whenever a property is set, notify the subscribed listeners
@ -48,8 +51,9 @@ function stateful(target, hook) {
assert(target instanceof Object, "stateful() requires an object"); assert(target instanceof Object, "stateful() requires an object");
target[LISTENERS] = []; target[LISTENERS] = [];
target[TARGET] = target; target[TARGET] = target;
let TOPRIMITIVE = Symbol.toPrimitive;
const proxy = new Proxy(target, { let proxy = new Proxy(target, {
get(target, property, proxy) { get(target, property, proxy) {
if (__use_trap) { if (__use_trap) {
let sym = Symbol(); let sym = Symbol();
@ -57,10 +61,10 @@ function stateful(target, hook) {
[TARGET]: target, [TARGET]: target,
[PROXY]: proxy, [PROXY]: proxy,
[STEPS]: [property], [STEPS]: [property],
[Symbol.toPrimitive]: () => sym, [TOPRIMITIVE]: _ => sym,
}, { }, {
get(target, property) { get(target, property) {
if ([TARGET, PROXY, STEPS, USE_MAPFN, Symbol.toPrimitive].includes(property)) return target[property]; if ([TARGET, PROXY, STEPS, USE_MAPFN, TOPRIMITIVE].includes(property)) return target[property];
property = TRAPS.get(property) || property; property = TRAPS.get(property) || property;
target[STEPS].push(property); target[STEPS].push(property);
return trap; return trap;
@ -75,7 +79,7 @@ function stateful(target, hook) {
set(target, property, val) { set(target, property, val) {
if (hook) hook(target, property, val); if (hook) hook(target, property, val);
let trap = Reflect.set(target, property, val); let trap = Reflect.set(target, property, val);
for (const listener of target[LISTENERS]) { for (let listener of target[LISTENERS]) {
listener(target, property, val); listener(target, property, val);
} }
return trap; return trap;
@ -86,6 +90,7 @@ function stateful(target, hook) {
} }
let isobj = (o) => o instanceof Object; let isobj = (o) => o instanceof Object;
let isfn = (o) => typeof o === "function";
function isDLPtr(arr) { function isDLPtr(arr) {
return isobj(arr) && TARGET in arr return isobj(arr) && TARGET in arr
} }
@ -100,7 +105,7 @@ function $if(condition, then, otherwise) {
// This lets you subscribe to a stateful object // This lets you subscribe to a stateful object
function handle(ptr, callback) { function handle(ptr, callback) {
assert(isDLPtr(ptr), "handle() requires a stateful object"); assert(isDLPtr(ptr), "handle() requires a stateful object");
assert(typeof callback === "function", "handle() requires a callback function"); assert(isfn(callback), "handle() requires a callback function");
let step, resolvedSteps = []; let step, resolvedSteps = [];
function update() { function update() {
@ -116,7 +121,7 @@ function handle(ptr, callback) {
} }
// inject ourselves into nested objects // inject ourselves into nested objects
const curry = (target, i) => function subscription(tgt, prop, val) { let curry = (target, i) => function subscription(tgt, prop, val) {
if (prop === resolvedSteps[i] && target === tgt) { if (prop === resolvedSteps[i] && target === tgt) {
update(); update();
@ -177,11 +182,11 @@ function JSXAddFixedWrapper(ptr, cb, $if) {
// returns a function that sets a reference // returns a function that sets a reference
// the currying is a small optimization // the currying is a small optimization
const curryset = ptr => val =>{ let curryset = ptr => val => {
let next = ptr[PROXY]; let next = ptr[PROXY];
let steps = ptr[STEPS]; let steps = ptr[STEPS];
let i =0; let i = 0;
for (;i<steps.length-1;i++) { for (; i < steps.length - 1; i++) {
next = next[steps[i]]; next = next[steps[i]];
if (!isobj(next)) return; if (!isobj(next)) return;
} }
@ -195,13 +200,13 @@ function h(type, props, ...children) {
// functional components. create the stateful object // functional components. create the stateful object
let newthis = stateful(Object.create(type.prototype)); let newthis = stateful(Object.create(type.prototype));
for (const name in props) { for (let name in props) {
const ptr = props[name]; let ptr = props[name];
if (name.startsWith("bind:")) { if (name.startsWith("bind:")) {
assert(isDLPtr(ptr), "bind: requires a reference pointer from use"); assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
const set = curryset(ptr); let set = curryset(ptr);
const propname = name.substring(5); let propname = name.substring(5);
if (propname == "this") { if (propname == "this") {
set(newthis); set(newthis);
} else { } else {
@ -231,7 +236,7 @@ function h(type, props, ...children) {
Object.assign(newthis, props); Object.assign(newthis, props);
newthis.children = []; newthis.children = [];
for (const child of children) { for (let child of children) {
JSXAddChild(child, newthis.children.push.bind(newthis.children)); JSXAddChild(child, newthis.children.push.bind(newthis.children));
} }
@ -239,8 +244,9 @@ function h(type, props, ...children) {
elm.$ = newthis; elm.$ = newthis;
newthis.root = elm; newthis.root = elm;
if (newthis.css) { if (newthis.css) {
elm.classList.add(newthis.css); let cl = elm.classList
elm.classList.add("self"); cl.add(newthis.css);
cl.add("self");
} }
elm.setAttribute("data-component", type.name); elm.setAttribute("data-component", type.name);
if (typeof newthis.mount === "function") if (typeof newthis.mount === "function")
@ -250,10 +256,10 @@ function h(type, props, ...children) {
let xmlns = props?.xmlns; let xmlns = props?.xmlns;
const elm = xmlns ? document.createElementNS(xmlns, type) : document.createElement(type); let elm = xmlns ? document.createElementNS(xmlns, type) : document.createElement(type);
for (const child of children) { for (let child of children) {
let cond = child && !isDLPtr(child) && child[IF]; let cond = child && !isDLPtr(child) && child[IF];
let bappend = elm.append.bind(elm); let bappend = elm.append.bind(elm);
if (cond) { if (cond) {
@ -264,18 +270,18 @@ function h(type, props, ...children) {
if (!props) return elm; if (!props) return elm;
function useProp(name, callback) { let useProp = (name, callback) => {
if (!(name in props)) return; if (!(name in props)) return;
let prop = props[name]; let prop = props[name];
callback(prop); callback(prop);
delete props[name]; delete props[name];
} }
for (const name in props) { for (let name in props) {
const ptr = props[name]; let ptr = props[name];
if (name.startsWith("bind:")) { if (name.startsWith("bind:")) {
assert(isDLPtr(ptr), "bind: requires a reference pointer from use"); assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
const propname = name.substring(5); let propname = name.substring(5);
// create the function to set the value of the pointer // create the function to set the value of the pointer
let set = curryset(ptr); let set = curryset(ptr);
@ -304,7 +310,7 @@ function h(type, props, ...children) {
return; return;
} }
for (const name of classlist) { for (let name of classlist) {
if (isDLPtr(name)) { if (isDLPtr(name)) {
let oldvalue = null; let oldvalue = null;
handle(name, value => { handle(name, value => {
@ -321,8 +327,8 @@ function h(type, props, ...children) {
}); });
// apply the non-reactive properties // apply the non-reactive properties
for (const name in props) { for (let name in props) {
const prop = props[name]; let prop = props[name];
if (isDLPtr(prop)) { if (isDLPtr(prop)) {
handle(prop, (val) => { handle(prop, (val) => {
JSXAddAttributes(elm, name, val); JSXAddAttributes(elm, name, val);
@ -365,10 +371,10 @@ function JSXAddChild(child, cb) {
function JSXAddAttributes(elm, name, prop) { function JSXAddAttributes(elm, name, prop) {
if (name.startsWith("on:")) { if (name.startsWith("on:")) {
assert(typeof prop === "function", "on: requires a function"); assert(typeof prop === "function", "on: requires a function");
const names = name.substring(3); let names = name.substring(3);
for (const name of names.split("$")) { for (let name of names.split("$")) {
elm.addEventListener(name, (...args) => { elm.addEventListener(name, (...args) => {
window.$el = elm; self.$el = elm;
prop(...args); prop(...args);
}); });
} }