From 337c8265cd72fe756a9a238b7c95bf22cf7d671f Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 25 Apr 2026 12:53:12 +0000 Subject: [PATCH] HS cluster 22: host-call-fn FFI + hs-win-call + def hoisting - Add host-call-fn FFI primitive to test runner (calls SX lambdas or JS fns) - Add hs-win-call runtime helper: looks up fn by name in window globals - Compiler call case: emit guard-wrapped hs-win-call for bare (ref ...) calls - Compiler method-call else: same guard pattern for non-dot method calls - Compiler do case: hoist define forms before init/other forms (def hoisting) Co-Authored-By: Claude Sonnet 4.6 --- lib/hyperscript/compiler.sx | 26 +++++++++++++++++++++----- shared/static/wasm/sx/hs-compiler.sx | 26 +++++++++++++++++++++----- tests/hs-run-filtered.js | 1 + 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/lib/hyperscript/compiler.sx b/lib/hyperscript/compiler.sx index 3374d412..1d17c214 100644 --- a/lib/hyperscript/compiler.sx +++ b/lib/hyperscript/compiler.sx @@ -1058,9 +1058,15 @@ (cons (quote hs-method-call) (cons obj (cons method args)))) - (cons - (quote hs-method-call) - (cons (hs-to-sx dot-node) args))))) + (if + (and (list? dot-node) (= (first dot-node) (quote ref))) + (list + (quote guard) + (list (quote _hs-win-e) + (list (quote true) + (list (quote hs-win-call) (nth dot-node 1) (cons (quote list) args)))) + (cons (hs-to-sx dot-node) args)) + (cons (quote hs-method-call) (cons (hs-to-sx dot-node) args)))))) ((= head (quote string-postfix)) (list (quote str) (hs-to-sx (nth ast 1)) (nth ast 2))) ((= head (quote block-literal)) @@ -1714,7 +1720,10 @@ body))) (nth compiled (- (len compiled) 1)) (rest (reverse compiled))) - (cons (quote do) compiled))))) + (let + ((defs (filter (fn (c) (and (list? c) (> (len c) 0) (= (first c) (quote define)))) compiled)) + (non-defs (filter (fn (c) (not (and (list? c) (> (len c) 0) (= (first c) (quote define))))) compiled))) + (cons (quote do) (append defs non-defs))))))) ((= head (quote wait)) (list (quote hs-wait) (nth ast 1))) ((= head (quote wait-for)) (emit-wait-for ast)) ((= head (quote log)) @@ -1822,7 +1831,14 @@ (make-symbol raw-fn) (hs-to-sx raw-fn))) (args (map hs-to-sx (rest (rest ast))))) - (cons fn-expr args))) + (if (and (list? raw-fn) (= (first raw-fn) (quote ref))) + (list + (quote guard) + (list (quote _hs-win-e) + (list (quote true) + (list (quote hs-win-call) (nth raw-fn 1) (cons (quote list) args)))) + (cons fn-expr args)) + (cons fn-expr args)))) ((= head (quote return)) (let ((val (nth ast 1))) diff --git a/shared/static/wasm/sx/hs-compiler.sx b/shared/static/wasm/sx/hs-compiler.sx index 3374d412..1d17c214 100644 --- a/shared/static/wasm/sx/hs-compiler.sx +++ b/shared/static/wasm/sx/hs-compiler.sx @@ -1058,9 +1058,15 @@ (cons (quote hs-method-call) (cons obj (cons method args)))) - (cons - (quote hs-method-call) - (cons (hs-to-sx dot-node) args))))) + (if + (and (list? dot-node) (= (first dot-node) (quote ref))) + (list + (quote guard) + (list (quote _hs-win-e) + (list (quote true) + (list (quote hs-win-call) (nth dot-node 1) (cons (quote list) args)))) + (cons (hs-to-sx dot-node) args)) + (cons (quote hs-method-call) (cons (hs-to-sx dot-node) args)))))) ((= head (quote string-postfix)) (list (quote str) (hs-to-sx (nth ast 1)) (nth ast 2))) ((= head (quote block-literal)) @@ -1714,7 +1720,10 @@ body))) (nth compiled (- (len compiled) 1)) (rest (reverse compiled))) - (cons (quote do) compiled))))) + (let + ((defs (filter (fn (c) (and (list? c) (> (len c) 0) (= (first c) (quote define)))) compiled)) + (non-defs (filter (fn (c) (not (and (list? c) (> (len c) 0) (= (first c) (quote define))))) compiled))) + (cons (quote do) (append defs non-defs))))))) ((= head (quote wait)) (list (quote hs-wait) (nth ast 1))) ((= head (quote wait-for)) (emit-wait-for ast)) ((= head (quote log)) @@ -1822,7 +1831,14 @@ (make-symbol raw-fn) (hs-to-sx raw-fn))) (args (map hs-to-sx (rest (rest ast))))) - (cons fn-expr args))) + (if (and (list? raw-fn) (= (first raw-fn) (quote ref))) + (list + (quote guard) + (list (quote _hs-win-e) + (list (quote true) + (list (quote hs-win-call) (nth raw-fn 1) (cons (quote list) args)))) + (cons fn-expr args)) + (cons fn-expr args)))) ((= head (quote return)) (let ((val (nth ast 1))) diff --git a/tests/hs-run-filtered.js b/tests/hs-run-filtered.js index 9a5f02db..8a1406a0 100755 --- a/tests/hs-run-filtered.js +++ b/tests/hs-run-filtered.js @@ -553,6 +553,7 @@ K.registerNative('host-get',a=>{ }); K.registerNative('host-set!',a=>{if(a[0]!=null){const v=a[2]; if(a[1]==='innerHTML'&&a[0] instanceof El){const s=v===null?'null':v===undefined?'':String(v);a[0]._setInnerHTML(s);a[0][a[1]]=a[0].innerHTML;} else if(a[1]==='textContent'&&a[0] instanceof El){const s=v===null?'null':v===undefined?'':String(v);a[0].textContent=s;a[0].innerHTML=s;for(const c of a[0].children){c.parentElement=null;c.parentNode=null;}a[0].children=[];a[0].childNodes=[];} else{a[0][a[1]]=v;}} return a[2];}); K.registerNative('host-call',a=>{if(_testDeadline&&Date.now()>_testDeadline)throw new Error('TIMEOUT: wall clock exceeded');const[o,m,...r]=a;if(o==null){const f=globalThis[m];return typeof f==='function'?f.apply(null,r):null;}if(o&&typeof o[m]==='function'){try{const v=o[m].apply(o,r);return v===undefined?null:v;}catch(e){return null;}}return null;}); +K.registerNative('host-call-fn',a=>{const[fn,argList]=a;if(typeof fn!=='function'&&!(fn&&fn.__sx_handle!==undefined))return null;const callArgs=(argList&&argList._type==='list'&&argList.items)?Array.from(argList.items):(Array.isArray(argList)?argList:[]);if(fn&&fn.__sx_handle!==undefined)return K.callFn(fn,callArgs);try{const v=fn.apply(null,callArgs);return v===undefined?null:v;}catch(e){return null;}}); K.registerNative('host-new',a=>{const C=typeof a[0]==='string'?globalThis[a[0]]:a[0];return typeof C==='function'?new C(...a.slice(1)):null;}); K.registerNative('host-callback',a=>{const fn=a[0];if(typeof fn==='function'&&fn.__sx_handle===undefined)return fn;if(fn&&fn.__sx_handle!==undefined)return function(){const r=K.callFn(fn,Array.from(arguments));if(globalThis._driveAsync)globalThis._driveAsync(r);return r;};return function(){};}); K.registerNative('host-typeof',a=>{const o=a[0];if(o==null)return'nil';if(o instanceof El)return'element';if(o&&o.nodeType===3)return'text';if(o instanceof Ev)return'event';if(o instanceof Promise)return'promise';return typeof o;});