-
Notifications
You must be signed in to change notification settings - Fork 443
feat: JSON implementation v4 #4972
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
🛠 PR Checks Summary🔴 Changes related to gnoweb must be reviewed by its codeowners Manual Checks (for Reviewers):
Read More🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers. ✅ Automated Checks (for Contributors):🟢 Maintainers must be able to edit this pull request (more info) ☑️ Contributor Actions:
☑️ Reviewer Actions:
📚 Resources:Debug
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
STILL WIP - need testing
Following: #2949
This implementation is the most advanced JSON implementation I have made so far. It tries to follow @jaekwon's specs #2949 (comment) while taking some liberties with the structure format to make it more usable.
What Changed from #2949
The previous PR (#2949) was a simpler approach that:
<obj:TYPE:OID>Error()/String()methods inlineThis new implementation takes a different approach:
{T: <type>, V: <value>}format@typetags)RefValuewith queryableObjectIDvm/qobjectendpoint to fetch any object by its IDJSONStructValue)Key Features
vm/qeval: Every result includes type informationvm/qobjectendpoint: Traverse the object graph by fetching objects via ObjectID@errorfield when functions return errors (per spec):1,:2)Usage
Query function results (vm/qeval)
{"results":[{"T":"string","V":"Hello world"}]}{"results":[{"T":"string","V":""},{"T":"*RefType{errors.errorString}","V":{...}}],"@error":"something went wrong"}{"results":[{"T":"*RefType{avl.Tree}","V":{"@type":"/gno.PointerValue","Base":{"@type":"/gno.RefValue","ObjectID":"abc123:42"},"Index":"0"}}]}Fetch object by ID (vm/qobject)
{ "objectid": "abc123:42", "value": { "@type": "/gno.JSONStructValue", "ObjectInfo": {"ID": "...", "RefCount": "1"}, "Fields": [{"N": "node", "T": {...}, "V": {...}}] } }Spec Implementation
Original Specification from @jaekwon
What Was Implemented
ExportValues()invalues_export.go- cycles becomeRefValue{ObjectID:":N"}@errorfield extracted when function signature returnserror{T: <type>, V: <value>}format, Amino for complex types:Ninstead of000...000:NAdditions Beyond Spec
vm/qobjectendpoint: Fetch any persisted object by ObjectID - enables traversing the full object graphJSONStructValuewith field names: Struct fields include their names for better readability:{"@type":"/gno.JSONStructValue","Fields":[{"N":"Data","T":{...},"V":{...}}]}JSONExporterOptions: Configurable export behavior:ExportUnexported: Include unexported (lowercase) fields (used by qobject)MaxDepth: Limit nested object expansionSignature-based error detection: Per spec,
@erroris only extracted when the function signature declares an error return typeDifferences from Spec
@erroras top-level field: Instead of injecting into the value object,@erroris at the response root:{"results":[...],"@error":"error message"}This avoids modifying value serialization and is cleaner to parse.
Persisted objects as RefValue: Real objects return
RefValuewith ObjectID rather than being expanded inline. Usevm/qobjectto fetch the full object. This prevents unbounded response sizes..Error()call without cache-wrapped store: The spec wanted.Error()called with a cache-wrapped store to discard any mutations, plus graceful fallback if gas runs out (return raw value instead of error string). Current implementation uses the machine directly, which naturally respects gas limits but does not discard mutations or handle gas exhaustion gracefully.Files Changed
gnovm/pkg/gnolang/values_export.goJSONExporterOptionsgnovm/pkg/gnolang/package.goJSONField,JSONObjectInfo,JSONStructValuegno.land/pkg/sdk/vm/convert.gostringifyJSONResults()for qeval JSON formatgno.land/pkg/sdk/vm/keeper.goQueryObject()for vm/qobject endpointgno.land/pkg/sdk/vm/handler.govm/qobjectqueriesExample Outputs
String result
{"results":[{"T":"string","V":"Hello juliette"},{"T":null,"V":null}]}Error result
{ "results": [ {"T": "string", "V": ""}, { "T": "*RefType{errors.errorString}", "V": { "@type": "/gno.PointerValue", "Base": { "@type": "/gno.StructValue", "ObjectInfo": {"ID": ":0", "ModTime": "0", "RefCount": "0"}, "Fields": [{"T": {"@type": "/gno.PrimitiveType", "value": "16"}, "V": {"@type": "/gno.StringValue", "value": "not for you bernard!"}}] }, "Index": "0" } } ], "@error": "not for you bernard!" }Persisted object (RefValue with queryable ObjectID)
{ "results": [ { "T": "*RefType{gno.land/r/dev/silo.Silo}", "V": { "@type": "/gno.PointerValue", "Base": { "@type": "/gno.RefValue", "ObjectID": "6aed2eda72c79e411eec1ea7f661c5e30383fb59:3", "Hash": "efbf31b561d0858742bb7c5004509cbade105909" }, "Index": "0" } } ] }qobject response (with field names)
{ "objectid": "6aed2eda72c79e411eec1ea7f661c5e30383fb59:3", "value": { "@type": "/gno.JSONStructValue", "ObjectInfo": { "ID": "6aed2eda72c79e411eec1ea7f661c5e30383fb59:4", "Hash": "877a0487f47efa309c26e43d8781773d9d27145f", "OwnerID": "6aed2eda72c79e411eec1ea7f661c5e30383fb59:3", "RefCount": "1" }, "Fields": [ { "N": "Data", "T": {"@type": "/gno.PrimitiveType", "value": "16"}, "V": {"@type": "/gno.StringValue", "value": "secret message"} } ] } }