feat(json): hb_jsonDecode 2-arg byref form (Harbour-spec compatible)

Previously hb_jsonDecode took only (cJSON) and returned the value.
That covers most uses but not the Harbour-spec second form

    nBytesParsed := hb_jsonDecode( cJSON, @xOut )

which mod_harbour / fivenode PRG (e.g. bridge_context.prg's
ctx_get / ctx_set) and any other code that wants the parse-length
relies on. The byref output was silently dropped, so a hash lookup
went through the @hOut path that was always NIL and fell back to
the default value — looking like a hash key was missing even
though the JSON parsed fine.

Now PCount() == 1 keeps the legacy return-value form; PCount() >= 2
writes the decoded value into local-2 via SetLocal (which is
already byref-aware) and returns the byte count (0 on parse error).

Verified: hb_jsonDecode('{"x":1,"y":2}', @h) writes the hash and
returns 13; the 1-arg form still returns the value as before;
Compat 56/56 + go test ./compiler/... ./hbrt/... ./hbrtl/... all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 10:44:12 +09:00
parent ad6cc0bcee
commit c7ac4044f7

View File

@@ -58,7 +58,17 @@ func HbJsonEncode(t *hbrt.Thread) {
t.RetString(string(data))
}
// HB_JSONDECODE(cJSON) → xValue
// HB_JSONDECODE(cJSON [, @xOut]) → xValue | nBytesParsed
//
// Two Harbour-spec calling forms:
//
// - 1 arg : returns the decoded value (or NIL on parse error).
// - 2 args: writes the decoded value into the byref @xOut and
// returns the number of bytes parsed (0 on error). This is the
// form mod_harbour / fivenode PRG code uses for ctx_get and the
// like. Five previously implemented only the 1-arg form, so any
// PRG that relied on the byref output saw NIL and silently fell
// through to a default.
func HbJsonDecode(t *hbrt.Thread) {
nParams := t.ParamCount()
t.Frame(nParams, 0)
@@ -66,10 +76,21 @@ func HbJsonDecode(t *hbrt.Thread) {
s := t.Local(1).AsString()
var raw interface{}
if err := json.Unmarshal([]byte(s), &raw); err != nil {
t.RetNil()
if nParams >= 2 {
t.SetLocal(2, hbrt.MakeNil())
t.RetInt(0)
} else {
t.RetNil()
}
return
}
t.RetVal(goToValue(raw))
v := goToValue(raw)
if nParams >= 2 {
t.SetLocal(2, v)
t.RetInt(int64(len(s)))
return
}
t.RetVal(v)
}
// === Five Extensions ===