Skip to content

go/java/cs/rust: native emission instead of runtime helpers where types are proven (ccxt#30919) - #104

Merged
frosty00 merged 75 commits into
ccxt:masterfrom
carlotestor:hx4-native-helpers
Oct 22, 2026
Merged

frosty00 merged 75 commits into
ccxt:masterfrom
carlotestor:hx4-native-helpers

Conversation

@carlotestor

@carlotestor carlotestor commented Oct 19, 2026 •

Copy link
Copy Markdown

Generator side of ccxt/ccxt#30919 (pinned there as 7443dc0a77a93722a46d27860980ffc853733d3a, the head of this PR; please don't rebase).

Emits native code instead of runtime helper calls across the Go, Java, C# and Rust printers when operand or receiver types are proven, and provides the printer hooks used by ccxt/ccxt#30919 to drive all Math, Ordered-Comparison, String/Slice/Parse, and Equality/Truthiness/inOp/Type-Check helper families to 0 in generated code. Every rule preserves the helper's null, pointer-unwrapping, numeric-widening and NaN semantics.

Go (src/goTranspiler.ts)

  • GetValue(x, "k") → x["k"] on GetArgMap-bound params, declared map locals, and market/currency rows at deref-consumer positions
  • IsEqual(x, nil) → x == nil on locals whose writes cannot hold a typed-nil pointer, plus IsEqual on proven scalar/pointer types
  • Add / Subtract / Multiply / Divide / Mod / OpNeg with int/int64/float64 widening and literal-union *string concatenation
  • Ordered comparisons (IsLessThan, IsGreaterThan, IsLessThanOrEqual, IsGreaterThanOrEqual) across int, int64, and float64
  • GetLength / GetIndexOf / Slice / ToUpper / ToLower / Split / Replace → native len / slice / strings.* on proven strings
  • Required exchange-local parameters and tuple returns typed from call sites (string, *string, map[string]any, []any)

Java (src/javaTranspiler.ts)

  • addElementToObject → put on declared, non-shared map locals; GetValue → get on base map fields
  • Native arithmetic (add, subtract, multiply, divide, mod, opNeg) and ordered comparisons (<, >, <=, >=) on proven numeric types with Integer → Long widening
  • isTrue, isEqual, inOp, isInstance, getIndexOf, slice, replace, split, toUpper, toLower native emission when types are proven

C# (src/csharpTranspiler.ts)

  • GetValue / getValue → guarded indexer on declared collections
  • Native arithmetic (+, -, *, /, %, unary -), ordered comparisons, isEqual, inOp, getArrayLength, getIndexOf, and string methods on proven types

Rust (src/rustTranspiler.ts)

  • Complete removal of multiply, subtract, divide, mod_val, negate, add, is_less_than, is_greater_than, is_less_than_or_equal, is_greater_than_or_equal, math_floor, math_ceil, math_round, math_abs
  • Complete removal of is_true, is_equal, in_op, get_array_length, object_keys, remove, slice, get_index_of, to_upper, to_lower, replace_all_str, pad_start, trim
  • Native IndexMap / Vec operations (insert, push, get) on proven dict/list locals and struct fields

Verification

  • jest: 14/14 suites, 2,046/2,046 tests pass, eslint 0 errors
  • Certified end-to-end on the ccxt build farm in job 6003 across all 7 languages (go,java,cs,rust,python,php,js --force --prediction --unit-tests), exit code 0
  • dist/ rebuilt cleanly from src/

…safeDict of non-shared data (hx4 J01) [WIP, gate pending]
…-consumer positions and market rows (hx4 G01) WIP3
…only; gate 3118 passed on 579c0b6, same src/dist) (hx4 J01)
@frosty00
frosty00 merged commit 38e725e into ccxt:master Oct 22, 2026
1 check passed
frosty00 pushed a commit to ccxt/ccxt that referenced this pull request Oct 22, 2026
… (-67% Rust, -60% Java, -53% C#, -38% Go non-plumbing) (#30919)

* generators: emit native code instead of runtime helpers (go/java/cs/rust)

Generator rules (ast-transpiler pin 32de8a2c235 + build/ passes) that print native
operators, indexers, len/size, map writes and comparisons where the operand types are
proven, plus behaviour-preserving ts/src typing that lets those rules fire.
checkOptionBool now coerces true/false/1/0 and throws BadRequest on other non-booleans
in every port. No generated files.

* hx4 wave 4: upstream ts/src method/param/local retyping + generator native emission (B01, G13-14, J12-14, C09-11, R10-12, U01-19)

* build: pin ast-transpiler lint fix (c4ae90bd59ccc9e75447f50982c1e7575a047a02)

* feat(transpiler): hx4 wave 5 native helper emission + upstream TS typing

* feat(transpiler): hx4 wave 6 math/comparison + collection native helper emission

* feat(transpiler): hx4 wave 7 math/comparison + collection native helper emission

* feat(transpiler): hx4 wave 8 math/comparison + collection native helper emission

* feat(transpiler): hx4 wave 9 native math/comparison + helper emission

* feat(transpiler): hx4 wave 10 native math/comparison + helper emission

* feat(transpiler): hx4 wave 11 native string/slice/parse + equality/truthiness/inOp/type-check emission

* build: bump ast-transpiler pin to merged 38e725e4b66b (ccxt/ast-transpiler#104)

* fix(coinbaseinternational): read sign()'s base url via safeString

Master's sign() built url from the raw urls['api']['rest'] read, which
the C# generator deliberately keeps as object (a cast would throw on a
null or numeric box), so it CS1503'd against createAuthToken's Str
parameter. The house safeString read with a '' default makes the local
provably a string, which also drops two C# add() helper calls here.

* fix(go): accept MapTyped-wrapped balance merge writes in the retype guard

Master's binance parseBalanceCustom now reassigns result via
mergeBalanceAccount, which the printer emits as
`result = MapTyped(this.MergeBalanceAccount(result, ..))`. The
goBalanceLocalIsMap allow-list only matched the bare call, so the
map[string]any retype rejected the method and transpileGO failed.

* fix(extended,whitebit): return the proven expression instead of an any local

Master's new Promise<Dict> / Order[] return types make the Go async
channel rule require a proven element type, and a local initialised from
safeDict without a default (or from filterBySymbolSinceLimit) is printed
as any, so GOCHAN3 refused the AsyncResult. Returning the safeDict /
filterBySymbolSinceLimit expression directly, as master does, keeps the
type proven.

* fix(go): accept MapTyped-wrapped balance merge writes in the chan3 send rule

Same gap as goBalanceLocalIsMap, in the async-channel copy: gate's
fetchBalance reassigns result through mergeBalanceAccount, printed as
`result = MapTyped(this.MergeBalanceAccount(result, ..))`, so the
balance-send rule could not prove the map and GOCHAN3 refused
AsyncResult[map[string]any].

* fix(go): emit gofmt-clean layout for for-clauses and called func literals

The `--check-gofmt` CI gate failed on 6 emitted files. Two printer gaps:

- a three-clause `for` kept the parens the native comparison passes wrap its
  condition in; goControlClauseClose only matched a whole control clause.
- a line closing a called multi-line func literal (`}(args) + x`) was treated
  as a continuation and never laid out, so its operators kept blanks gofmt
  strips. It is now laid out as the expression it is, at the depth of the
  brackets still open around it plus the literal's own frame.

Composite literal elements are no longer marked as expression continuations,
and call-paren depth now counts multi-line argument lists.
frosty00 pushed a commit to ccxt/ccxt that referenced this pull request Oct 22, 2026
…ined helper bodies (#30966)

* generators: emit native code instead of runtime helpers (go/java/cs/rust)

Generator rules (ast-transpiler pin 32de8a2c235 + build/ passes) that print native
operators, indexers, len/size, map writes and comparisons where the operand types are
proven, plus behaviour-preserving ts/src typing that lets those rules fire.
checkOptionBool now coerces true/false/1/0 and throws BadRequest on other non-booleans
in every port. No generated files.

* hx4 wave 4: upstream ts/src method/param/local retyping + generator native emission (B01, G13-14, J12-14, C09-11, R10-12, U01-19)

* build: pin ast-transpiler lint fix (c4ae90bd59ccc9e75447f50982c1e7575a047a02)

* feat(transpiler): hx4 wave 5 native helper emission + upstream TS typing

* feat(transpiler): hx4 wave 6 math/comparison + collection native helper emission

* feat(transpiler): hx4 wave 7 math/comparison + collection native helper emission

* feat(transpiler): hx4 wave 8 math/comparison + collection native helper emission

* feat(transpiler): hx4 wave 9 native math/comparison + helper emission

* feat(transpiler): hx4 wave 10 native math/comparison + helper emission

* feat(transpiler): hx4 wave 11 native string/slice/parse + equality/truthiness/inOp/type-check emission

* build: bump ast-transpiler pin to merged 38e725e4b66b (ccxt/ast-transpiler#104)

* fix(coinbaseinternational): read sign()'s base url via safeString

Master's sign() built url from the raw urls['api']['rest'] read, which
the C# generator deliberately keeps as object (a cast would throw on a
null or numeric box), so it CS1503'd against createAuthToken's Str
parameter. The house safeString read with a '' default makes the local
provably a string, which also drops two C# add() helper calls here.

* fix(go): accept MapTyped-wrapped balance merge writes in the retype guard

Master's binance parseBalanceCustom now reassigns result via
mergeBalanceAccount, which the printer emits as
`result = MapTyped(this.MergeBalanceAccount(result, ..))`. The
goBalanceLocalIsMap allow-list only matched the bare call, so the
map[string]any retype rejected the method and transpileGO failed.

* fix(extended,whitebit): return the proven expression instead of an any local

Master's new Promise<Dict> / Order[] return types make the Go async
channel rule require a proven element type, and a local initialised from
safeDict without a default (or from filterBySymbolSinceLimit) is printed
as any, so GOCHAN3 refused the AsyncResult. Returning the safeDict /
filterBySymbolSinceLimit expression directly, as master does, keeps the
type proven.

* fix(go): accept MapTyped-wrapped balance merge writes in the chan3 send rule

Same gap as goBalanceLocalIsMap, in the async-channel copy: gate's
fetchBalance reassigns result through mergeBalanceAccount, printed as
`result = MapTyped(this.MergeBalanceAccount(result, ..))`, so the
balance-send rule could not prove the map and GOCHAN3 refused
AsyncResult[map[string]any].

* fix(go): emit gofmt-clean layout for for-clauses and called func literals

The `--check-gofmt` CI gate failed on 6 emitted files. Two printer gaps:

- a three-clause `for` kept the parens the native comparison passes wrap its
  condition in; goControlClauseClose only matched a whole control clause.
- a line closing a called multi-line func literal (`}(args) + x`) was treated
  as a continuation and never laid out, so its operators kept blanks gofmt
  strips. It is now laid out as the expression it is, at the depth of the
  brackets still open around it plus the literal's own frame.

Composite literal elements are no longer marked as expression continuations,
and call-paren depth now counts multi-line argument lists.

* hx5 X09: drop C35 inlined parseInt/parseFloat/toFixed/divide bodies, keep helper calls

* build(cs): hx5 X10 drop inlined Slice lambda; native Substring only on proven literal-length strings

* hx5 X05: C33 isEqual int literal -> plain == on proven int/Int64, else isEqual helper; drop numeric switch

* hx5 X24: drop the G41 inline InOp closure; unproven InOp stays the helper call, remove exported AddElementMu

* hx5 X04: drop the inline getIndexOf switch from hx4 C33; unproven receivers keep getIndexOf(x, k)

* hx5 X03: C# inOp — drop the C33 inline switch; unproven sites keep inOp(x, k)

* hx5 X31: drop the Go string-shadow parameter renames (G41/G45) now that no pasted body needs them

* hx5 X16: J41 drop slice/getIndexOf inline lambdas; native substring/indexOf only on proven String

* hx5 X20: BaseExchange hand-written region: drop J40/J44 inlined blobs (isSigned -> Helpers.isTrue, isDictionary -> instanceof Map), wrap crc32 table literal

* hx5 X01: drop the C33 getArrayLength inline switch; unproven sites keep getArrayLength(x)

* hx5 X23: drop the G40 IsEqual inline closure; native == nil only on typed map/slice/scalar-pointer operands

* hx5 X18: drop J43 curried lambdas; native String.replace only for proven replaceAll, else helper

* hx5 X13: J36 multiply/subtract — native only on proven integral primitives, helper call otherwise (drop inline lambda)

* hx5 X23: anchor the duplicate-nil collapse

* fix(java): J40 isTrue emits native only on proven boolean/Boolean, else Helpers.isTrue (hx5 X14)

* hx5 X08: C32 keeps native ordered compares on proven integers, drops the inlined helper-body fallback

* hx5 X15: J40 isEqual — native ==/.equals only on proven primitives/Strings, else Helpers.isEqual; drop inline switch/lambda

* fix(cs): hx5 X02 — stop inlining isTrue; unproven operands keep the isTrue helper call

* hx5 X07: C31 native math only on proven numerics, helper call otherwise (no inlined bodies)

* hx5 X06(cs): drop C34 inlined isEqual/inOp lambdas; proven scalar pairs -> ==, rest keep the helper call

* hx5 X27: Go Split/Join/Replace — delete hx4 G44 inline closures; native strings.* only on proven string/[]string, else the helper call

* hx5 X17: J42 parseInt/parseFloat — drop inline pattern switches; native only on proven numeric/literal operands, else Helpers call

* hx5 X30: drop G35/G36/G37 inline closure tiers for Go math and ordered comparisons; unproven sites keep the helper call

* hx5 X26: G43 case/affix — native strings.* only on proven string, else the helper; drop inline closures and the (strings.X)( census respelling

* hx5 X07: keep Int64 mathMin/mathMax on the helper (double compare above 2^53)

* hx5 X29(go): drop G46 inline parse/coerce closures and the strconv/precise respelling; native only on a proven scalar operand, else the helper

* hx5 X12: type toArray/unique/list-producer locals in C# (widen typed-local inference)

* hx5 X25: drop G42 inline type-check closures; native test only on proven operands, else the helper

* fix(go): hx5 X28 drop the G45 Slice/GetIndexOf/GetLength inline closures; native only on proven string/slice operands

* phemex: read market settle once via safeString in 12 methods (hx5 X63)

* hx5 X19: J44 emits native instanceof/containsKey only on proven operands, else the Helpers call

* fix(java): hx5 X22 GetValue/addElementToObject emit plain get/put on proven maps, helper otherwise (drop J39 inline blobs)

* fix(rust): hx5 X36 — r29 emits the remove helper call instead of an inline match

* hx5 X56: binance fold oneWeek constant, reuse newNumSubscriptions in pro stream()

* build(rust): hx5 X43 drop r32 inline bodies; keep is_instance/is_function/object_values/shift helper calls

* hx5 X55: type postOnly/first locals in base Exchange.ts for native emission; wrap incrementingNonce JSDoc

* fix(rust): r28 is_true keeps native form only on proven bools, else runtime::is_true (hx5 X38)

* fix(rust): hx5 X35 — keep get_array_length as the helper call instead of an inlined match

* fix(java): hx5 X22 keep GetValue helper on volatile markets_by_id

* hx5 X61: kalshi single-assignment eventParts, drop dead parsedMarkets fallback

* hx5 X42: rust r31 to_string_val emits the helper call unless the operand is a Value::Str box

* hx5 X64: type bitfinex networks/deltas, bingx ws inverse, gemini ws base url in ts/src

* hx5 X62: type pro/gate unWatchOrderBook limit and boolean default params

* hx5 X41: r31 stops inlining pad_start/pad_end/to_fixed bodies; unproven sites keep the runtime helper call

* fix(rust): emit runtime split/join/replace_str calls instead of inlined bodies (hx5 X40)

* fix(java): BaseExchange head drops hx4 isTrue inline switches (crc32 -> Helpers.isTrue, isDictionary booleans native) (hx5 X14)

* hx5 X57: fold bybit constant products so ports emit integer literals

* build(rust): hx5 X46 native ±1 for-loop increment on proven Value::Int counters

* hx5 X60: hoist duplicated element reads in polymarket/sxbet into single-assignment consts

* hx5 X62: type kraken parseTransactionsByType transactions as List

* fix(java): J40 isTrue native on top-level && / || operands (hx5 X14)

* rust: hx5 X39 — drop r31 starts_with/ends_with inline bodies; native only on proven Str boxes, else helper call

* hx5 X62: pro/kraken formatNumber leading-zero loop via startsWith

* fix(rust): emit runtime::object_keys instead of an inline match (hx5 X34)

* hx5 X62: seed gate fetchLeverageTiers limit via parseToInt

* hx5 X33: rust r29 in_op — drop inline match; native contains_key on proven IndexMap locals, else runtime in_op helper

* kucoin: read sign() apiName with safeString so ports type it as a string

* hx5 X58: type htx parseOrder side/type, fetchPosition marketId, ws orderbook ch as strings

* hx5 X37(rust): r28 is_equal keeps the helper call unless both operands are proven one scalar variant or Null

* hx5 X58: htx ws handleMyTrade iterate a typed list

* hyperliquid: read ledger delta vault with safeString so the compare is typed

* build(rust): hx5 X46 fix pass name mismatch (x46Incr)

* hx5 X57: revert bybit fundingInterval literal (de-typed C# local)

* hx5 X61: revert kalshi eventParts ternary (Go IIFE regression)

* build(go): hx5 X32 — type Str params *string when callers pass *string or a proven string (SafeStringPtr-boxed)

* hx5 X62: revert no-op typings (pro/gate limit/boolean params, kraken transactions: List) - moved nothing in cs/go

* java(hx5 X21): type Object locals from base Str producers and venue Map-returning callees

- J14 cast accessors: currencyToPrecision/forceString/parsePrecision/convertExpireDate/remove0xPrefix
  (String|null|throw on every Java path) retype single-declaration locals to String with a checkcast,
  gated on no venue override reachable by dispatch
- J11 map value: a sync single-dispatch venue callee whose every return is a fresh map, an omit of a
  never-written Map-printed parameter, an omit of an audited handle* element-1 const binding, or a const
  local of those, proves a Map<String, Object> local (checkcast when the callee prints Object)
- self-test x21SelfTest: positives a/b/d/e, negatives c (mixed write) and f (param passthrough)

* build(go): hx5 X32 — never probe parameter types from call-arg printing inside a parameter proof (fixes handler map param regressions)

* feat(rust): hx5 X45 — proven-variant Value locals emit plain native ops instead of the helper

* build(go): hx5 X32 — box string args at every print; scratch param probes inside a proof drop their cache entries

* hx5 P04: type ws handler client/message params (coinbaseinternational, polymarket)

* build(go): hx5 X32 — string pre-filter accepts plain string (farm program types Str as string)

* types(base): declare liquidations/myLiquidations as ArrayCache | undefined (hx5 P27)

* hx5 P13: binance calculateRateLimiterCost byLimit safeValue -> safeList

* types(ts): annotate exchange-local params as Dict in polymarket, bitget, hyperliquid, xt (hx5 P02)

* hx5 P12: type untyped prediction-tier params (name/isTestnet/marketSymbol/client/msg) and limitless string returns

* refactor(htx pro): read order 'trade' fills with safeList

* go: native this.<F>[k] reads on proven plain base map fields (hx5 P28)

* types: parseOpenInterest interest: NullableDict (base + overrides)

* types: handleDeltas deltas: any[] (base + 21 pro overrides)

* types: sign() method: string = 'GET' in base and overrides

* types: parseDepositWithdrawFee fee: NullableDict

---------

Co-authored-by: x54 <x54@local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants