Skip to content

Downstream fork with several upstreamable slices — worth proposing any of them? #185

Description

@AkisArou

Hi — I maintain a fork of scriptc that serves as the compiler for a native
TypeScript toolchain, and before opening any PRs I'd rather ask what you'd
actually want. Some of this is large, and none of it is urgent for me.

What the fork is for. TypeScript programs that call C and Java APIs
directly — GTK 4 desktop applications and Android/JNI — with the binding
surface generated from GObject-introspection and Android SDK metadata rather
than written by hand. Everything crosses the boundary through a declarative
native manifest that the compiler lowers, instead of through per-target glue.
The GTK applications compile through both the C and LLVM backends and open
real windows under test.

Honest size. Measured against d7b4480e (v0.0.34), which the fork is
merged up to as of now: 152 commits ahead, +32.4k/−2.5k lines across 174
files. That number overstates it in a way worth spelling out: 20.3k of the
insertions are in 118 files that didn't exist here, 13.6k of the total is
tests and fixtures, and only ~2.5k lines of your existing code are replaced.

The divergence is additive — new paths beside yours rather than rewrites of
them — and the merges are the evidence rather than the claim. Your six library
caching commits cost 16 conflicted files and 31 hunks. #178's reachability
rework, which restructures the lowering entry point this fork also threads
through, cost exactly two conflicts: one real, one an artifact of my own
earlier edit. Your library caching is merged in and running here; the semantic
tier is a nice piece of work.

What might be worth upstreaming

Every slice below is a generic compiler or runtime capability. None of them
contains GTK, JNI, Android, or Cocoa knowledge — not a symbol, not a type, not
a conditional. Every target-specific line lives in the other repo, above the
compiler, which is the constraint the whole fork is organised around: if a
target needs something, it belongs to the boundary, not to the target. That is
also what makes these separable at all.

Commit counts come from classifying all 151, so they're real rather than
estimated:

Slice Commits What it is
Embedder plans + external build execution 16 Serializable, path-free compilation plans, and a seam that hands an embedder your exact driver commands instead of running them
Exact native scalars and arithmetic 29 i8…u64, target-sized integers, f32 as an ABI carrier, bigint, and the operations an operator cannot carry
Native aggregates 4 Structs by value, nested aggregates, exact aggregate ABI signatures
Strings and byte views 18 Borrowed UTF-8, checked C strings, C-string vectors, byte spans with explicit element and unit denomination
Native handles and ownership 16 Opaque and nullable handles, identity upcasts, pointer-keyed interning, transfer, use-after-dispose
Outcome contracts 6 errno and error-object failure; eleven C conventions reduced to three questions
Callbacks 28 Call-scoped and retained registrations, owner gateway, transport tokens, answered and queued delivery, foreign-thread ingress
Shared boundary decisions 4 One module deciding what a foreign call's arguments, result, failure and shape become, so both backends stop deciding it twice
Native manifest contract 3 The declaration file an embedder writes against, with no imports and no runtime value so it costs a consumer nothing

They're dependent roughly in that order — handles need scalars, callbacks need
handles and outcomes — so they'd arrive as a short stack rather than all at
once, and I'd keep it two or three deep rather than opening the whole thing.

What I'm not proposing. The fork deletes the outbound FFI subsystem,
because everything here routes through the manifest instead. That's a
fork-only simplification and you'd be right to refuse it. The remaining ~22
commits are housekeeping, fixture scoping, and fixes to my own code.

The question

Is any of this interesting to you, and if so where would you want me to start?

I was going to lead with embedder plans — it's contained, it has no semantic
risk, and it's the one that makes the rest legible — but I notice you've spent
this week in library build performance, and that slice touches
compileLibrary, so I'd rather not surprise you there. Exact scalars is the
obvious alternative: it's the dependency floor for everything else and it
touches nothing you're currently working in.

"No thanks" or "not right now" are completely fine answers, and I'd rather have
either one now than build seven PRs nobody asked for. I'll keep the fork in
sync either way.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions