HS: wip — parser every-fix, integration boot, test tooling expansion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 18:51:32 +00:00
parent b0c135412a
commit c5d9a8b789
6 changed files with 442 additions and 64 deletions

View File

@@ -169,6 +169,8 @@ class El {
}
return `<${tag}${attrs}>${inner}</${tag}>`;
}
get childElementCount() { return this.children.length; }
toString() { return this.nodeType === 11 ? '[object DocumentFragment]' : '[object Object]'; }
get firstElementChild() { return this.children[0]||null; }
get lastElementChild() { return this.children[this.children.length-1]||null; }
get nextElementSibling() { if(!this.parentElement)return null; const i=this.parentElement.children.indexOf(this); return this.parentElement.children[i+1]||null; }
@@ -679,6 +681,10 @@ K.registerNative('hs-is-map?',a=>a[0] instanceof Map);
// Upstream test fixtures: synchronous stubs matching OCaml run_tests.ml registrations
globalThis.promiseAString = () => 'foo';
globalThis.promiseAnInt = () => 42;
globalThis.promiseAnIntIn = (n) => Promise.resolve(42);
globalThis.promiseValueBackIn = (v, n) => Promise.resolve(v);
globalThis.throwBar = function() { throw "bar"; };
globalThis.identity = x => x;
// ── JS block execution support ─────────────────────────────────
// Track promise states for synchronous introspection in hs-js-exec
@@ -733,6 +739,57 @@ K.registerNative('host-hs-normalize-exc', a => {
return val;
});
// Like host-call-fn but propagates native JS exceptions via sentinel rather than swallowing them.
// Also synchronously unwraps Promise.resolve() results so async tests work in sync env.
K.registerNative('host-call-fn-raising', 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 : []);
function sxToJs(v) {
if (v && v._type === 'list' && v.items) return Array.from(v.items).map(sxToJs);
return v;
}
if (fn && fn.__sx_handle !== undefined) {
try {
const r = K.callFn(fn, callArgs);
if (globalThis._driveAsync) globalThis._driveAsync(r);
return r !== undefined ? r : null;
} catch(e) {
const msg = (e && e.message) || '';
if (String(msg).includes('TIMEOUT')) throw e;
globalThis.__hs_js_throw = String(e != null ? e : '');
return '__hs_js_throw__';
}
}
try {
const v = fn.apply(null, callArgs.map(sxToJs));
if (v === undefined) return null;
if (v instanceof Promise) {
const s = _promiseStates.get(v);
if (s) {
if (!s.ok) {
globalThis.__hs_async_error = (s.value instanceof Error) ? {message: s.value.message} : s.value;
return '__hs_async_error__';
}
return (s.value !== undefined && s.value !== null) ? s.value : null;
}
return null;
}
return v;
} catch(e) {
const msg = (e instanceof Error) ? e.message : String(e != null ? e : '');
globalThis.__hs_js_throw = msg;
return '__hs_js_throw__';
}
});
K.registerNative('host-take-js-throw', a => {
const v = globalThis.__hs_js_throw;
globalThis.__hs_js_throw = null;
return (v != null) ? String(v) : '';
});
let _testDeadline = 0;
// Mock fetch routes
const _fetchRoutes = {
@@ -761,6 +818,8 @@ const _fetchScripts = {
{ "/test": { status: 200, body: "yay", contentType: "text/html" } },
"can do a simple fetch w/ a custom conversion":
{ "/test": { status: 200, body: "1.2" } },
"can do a simple fetch w/ html":
{ "/test": { status: 200, body: "<p>hello</p>", html: "<p>hello</p>", contentType: "text/html" } },
};
function _mockFetch(url) {
const scriptRoutes = _fetchScripts[globalThis.__currentHsTestName];
@@ -780,7 +839,7 @@ globalThis._driveAsync=function driveAsync(r,d){d=d||0;if(_testDeadline && Date.
}
else if(opName==='io-parse-text'){const resp=items&&items[1];doResume(resp&&resp._body?resp._body:typeof resp==='string'?resp:'');}
else if(opName==='io-parse-json'){const resp=items&&items[1];try{doResume(JSON.parse(typeof resp==='string'?resp:resp&&resp._json?resp._json:'{}'));}catch(e){doResume(null);}}
else if(opName==='io-parse-html'){const frag=new El('fragment');frag.nodeType=11;doResume(frag);}
else if(opName==='io-parse-html'){const resp=items&&items[1];const htmlStr=resp&&(resp._html||resp._body)?String(resp._html||resp._body):'';const frag=new El('fragment');frag.nodeType=11;if(htmlStr)frag._setInnerHTML(htmlStr);doResume(frag);}
else if(opName==='io-settle')doResume(null);
else if(opName==='io-wait-event'){
const target=items&&items[1];