diff --git a/core/io/json.cpp b/core/io/json.cpp index e73677be9ca8..b6b1a88479bd 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -31,7 +31,8 @@ #include "json.h" #include "core/config/engine.h" -#include "core/string/print_string.h" +#include "core/object/script_language.h" +#include "core/variant/container_type_validate.h" const char *JSON::tk_name[TK_MAX] = { "'{'", @@ -563,18 +564,18 @@ String JSON::get_parsed_text() const { } String JSON::stringify(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) { - Ref jason; - jason.instantiate(); + Ref json; + json.instantiate(); HashSet markers; - return jason->_stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision); + return json->_stringify(p_var, p_indent, 0, p_sort_keys, markers, p_full_precision); } Variant JSON::parse_string(const String &p_json_string) { - Ref jason; - jason.instantiate(); - Error error = jason->parse(p_json_string); - ERR_FAIL_COND_V_MSG(error != Error::OK, Variant(), vformat("Parse JSON failed. Error at line %d: %s", jason->get_error_line(), jason->get_error_message())); - return jason->get_data(); + Ref json; + json.instantiate(); + Error error = json->parse(p_json_string); + ERR_FAIL_COND_V_MSG(error != Error::OK, Variant(), vformat("Parse JSON failed. Error at line %d: %s", json->get_error_line(), json->get_error_message())); + return json->get_data(); } void JSON::_bind_methods() { @@ -588,756 +589,1015 @@ void JSON::_bind_methods() { ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line); ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message); - ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_classes", "allow_scripts"), &JSON::to_native, DEFVAL(false), DEFVAL(false)); - ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "allow_classes", "allow_scripts"), &JSON::from_native, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_static_method("JSON", D_METHOD("from_native", "variant", "full_objects"), &JSON::from_native, DEFVAL(false)); + ClassDB::bind_static_method("JSON", D_METHOD("to_native", "json", "allow_objects"), &JSON::to_native, DEFVAL(false)); ADD_PROPERTY(PropertyInfo(Variant::NIL, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), "set_data", "get_data"); // Ensures that it can be serialized as binary. } -#define GDTYPE "__gdtype" -#define VALUES "values" -#define PASS_ARG p_allow_classes, p_allow_scripts +#define TYPE "type" +#define ELEM_TYPE "elem_type" +#define KEY_TYPE "key_type" +#define VALUE_TYPE "value_type" +#define ARGS "args" +#define PROPS "props" + +static bool _encode_container_type(Dictionary &r_dict, const String &p_key, const ContainerType &p_type, bool p_full_objects) { + if (p_type.builtin_type != Variant::NIL) { + if (p_type.script.is_valid()) { + ERR_FAIL_COND_V(!p_full_objects, false); + const String path = p_type.script->get_path(); + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), false, "Failed to encode a path to a custom script for a container type."); + r_dict[p_key] = path; + } else if (p_type.class_name != StringName()) { + ERR_FAIL_COND_V(!p_full_objects, false); + r_dict[p_key] = String(p_type.class_name); + } else { + // No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`. + r_dict[p_key] = Variant::get_type_name(p_type.builtin_type); + } + } + return true; +} + +Variant JSON::_from_native(const Variant &p_variant, bool p_full_objects, int p_depth) { +#define RETURN_ARGS \ + Dictionary ret; \ + ret[TYPE] = Variant::get_type_name(p_variant.get_type()); \ + ret[ARGS] = args; \ + return ret -Variant JSON::from_native(const Variant &p_variant, bool p_allow_classes, bool p_allow_scripts) { switch (p_variant.get_type()) { - case Variant::NIL: { - Dictionary nil; - nil[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return nil; - } break; + case Variant::NIL: case Variant::BOOL: { return p_variant; } break; + case Variant::INT: { - return p_variant; + return "i:" + String(p_variant); } break; case Variant::FLOAT: { - return p_variant; + return "f:" + String(p_variant); } break; case Variant::STRING: { - return p_variant; + return "s:" + String(p_variant); + } break; + case Variant::STRING_NAME: { + return "sn:" + String(p_variant); + } break; + case Variant::NODE_PATH: { + return "np:" + String(p_variant); + } break; + + case Variant::RID: + case Variant::CALLABLE: + case Variant::SIGNAL: { + Dictionary ret; + ret[TYPE] = Variant::get_type_name(p_variant.get_type()); + return ret; } break; + case Variant::VECTOR2: { - Dictionary d; - Vector2 v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Vector2 v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + + RETURN_ARGS; } break; case Variant::VECTOR2I: { - Dictionary d; - Vector2i v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Vector2i v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + + RETURN_ARGS; } break; case Variant::RECT2: { - Dictionary d; - Rect2 r = p_variant; - d["position"] = from_native(r.position); - d["size"] = from_native(r.size); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Rect2 r = p_variant; + + Array args; + args.push_back(r.position.x); + args.push_back(r.position.y); + args.push_back(r.size.width); + args.push_back(r.size.height); + + RETURN_ARGS; } break; case Variant::RECT2I: { - Dictionary d; - Rect2i r = p_variant; - d["position"] = from_native(r.position); - d["size"] = from_native(r.size); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Rect2i r = p_variant; + + Array args; + args.push_back(r.position.x); + args.push_back(r.position.y); + args.push_back(r.size.width); + args.push_back(r.size.height); + + RETURN_ARGS; } break; case Variant::VECTOR3: { - Dictionary d; - Vector3 v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Vector3 v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + + RETURN_ARGS; } break; case Variant::VECTOR3I: { - Dictionary d; - Vector3i v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Vector3i v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + + RETURN_ARGS; } break; case Variant::TRANSFORM2D: { - Dictionary d; - Transform2D t = p_variant; - d["x"] = from_native(t[0]); - d["y"] = from_native(t[1]); - d["origin"] = from_native(t[2]); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Transform2D t = p_variant; + + Array args; + args.push_back(t[0].x); + args.push_back(t[0].y); + args.push_back(t[1].x); + args.push_back(t[1].y); + args.push_back(t[2].x); + args.push_back(t[2].y); + + RETURN_ARGS; } break; case Variant::VECTOR4: { - Dictionary d; - Vector4 v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - values.push_back(v.w); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Vector4 v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + args.push_back(v.w); + + RETURN_ARGS; } break; case Variant::VECTOR4I: { - Dictionary d; - Vector4i v = p_variant; - Array values; - values.push_back(v.x); - values.push_back(v.y); - values.push_back(v.z); - values.push_back(v.w); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Vector4i v = p_variant; + + Array args; + args.push_back(v.x); + args.push_back(v.y); + args.push_back(v.z); + args.push_back(v.w); + + RETURN_ARGS; } break; case Variant::PLANE: { - Dictionary d; - Plane p = p_variant; - d["normal"] = from_native(p.normal); - d["d"] = p.d; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Plane p = p_variant; + + Array args; + args.push_back(p.normal.x); + args.push_back(p.normal.y); + args.push_back(p.normal.z); + args.push_back(p.d); + + RETURN_ARGS; } break; case Variant::QUATERNION: { - Dictionary d; - Quaternion q = p_variant; - Array values; - values.push_back(q.x); - values.push_back(q.y); - values.push_back(q.z); - values.push_back(q.w); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Quaternion q = p_variant; + + Array args; + args.push_back(q.x); + args.push_back(q.y); + args.push_back(q.z); + args.push_back(q.w); + + RETURN_ARGS; } break; case Variant::AABB: { - Dictionary d; - AABB aabb = p_variant; - d["position"] = from_native(aabb.position); - d["size"] = from_native(aabb.size); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const AABB aabb = p_variant; + + Array args; + args.push_back(aabb.position.x); + args.push_back(aabb.position.y); + args.push_back(aabb.position.z); + args.push_back(aabb.size.x); + args.push_back(aabb.size.y); + args.push_back(aabb.size.z); + + RETURN_ARGS; } break; case Variant::BASIS: { - Dictionary d; - Basis t = p_variant; - d["x"] = from_native(t.get_column(0)); - d["y"] = from_native(t.get_column(1)); - d["z"] = from_native(t.get_column(2)); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Basis b = p_variant; + + Array args; + args.push_back(b.get_column(0).x); + args.push_back(b.get_column(0).y); + args.push_back(b.get_column(0).z); + args.push_back(b.get_column(1).x); + args.push_back(b.get_column(1).y); + args.push_back(b.get_column(1).z); + args.push_back(b.get_column(2).x); + args.push_back(b.get_column(2).y); + args.push_back(b.get_column(2).z); + + RETURN_ARGS; } break; case Variant::TRANSFORM3D: { - Dictionary d; - Transform3D t = p_variant; - d["basis"] = from_native(t.basis); - d["origin"] = from_native(t.origin); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Transform3D t = p_variant; + + Array args; + args.push_back(t.basis.get_column(0).x); + args.push_back(t.basis.get_column(0).y); + args.push_back(t.basis.get_column(0).z); + args.push_back(t.basis.get_column(1).x); + args.push_back(t.basis.get_column(1).y); + args.push_back(t.basis.get_column(1).z); + args.push_back(t.basis.get_column(2).x); + args.push_back(t.basis.get_column(2).y); + args.push_back(t.basis.get_column(2).z); + args.push_back(t.origin.x); + args.push_back(t.origin.y); + args.push_back(t.origin.z); + + RETURN_ARGS; } break; case Variant::PROJECTION: { - Dictionary d; - Projection t = p_variant; - d["x"] = from_native(t[0]); - d["y"] = from_native(t[1]); - d["z"] = from_native(t[2]); - d["w"] = from_native(t[3]); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Projection p = p_variant; + + Array args; + args.push_back(p[0].x); + args.push_back(p[0].y); + args.push_back(p[0].z); + args.push_back(p[0].w); + args.push_back(p[1].x); + args.push_back(p[1].y); + args.push_back(p[1].z); + args.push_back(p[1].w); + args.push_back(p[2].x); + args.push_back(p[2].y); + args.push_back(p[2].z); + args.push_back(p[2].w); + args.push_back(p[3].x); + args.push_back(p[3].y); + args.push_back(p[3].z); + args.push_back(p[3].w); + + RETURN_ARGS; } break; case Variant::COLOR: { - Dictionary d; - Color c = p_variant; - Array values; - values.push_back(c.r); - values.push_back(c.g); - values.push_back(c.b); - values.push_back(c.a); - d[VALUES] = values; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::STRING_NAME: { - Dictionary d; - d["name"] = String(p_variant); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::NODE_PATH: { - Dictionary d; - d["path"] = String(p_variant); - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; - } break; - case Variant::RID: { - Dictionary d; - d[GDTYPE] = Variant::get_type_name(p_variant.get_type()); - return d; + const Color c = p_variant; + + Array args; + args.push_back(c.r); + args.push_back(c.g); + args.push_back(c.b); + args.push_back(c.a); + + RETURN_ARGS; } break; + case Variant::OBJECT: { - Object *obj = p_variant.get_validated_object(); + ERR_FAIL_COND_V(!p_full_objects, Variant()); - if (p_allow_classes && obj) { - Dictionary d; - List property_list; - obj->get_property_list(&property_list); - - d["type"] = obj->get_class(); - Dictionary p; - for (const PropertyInfo &P : property_list) { - if (P.usage & PROPERTY_USAGE_STORAGE) { - if (P.name == "script" && !p_allow_scripts) { - continue; - } - p[P.name] = from_native(obj->get(P.name), PASS_ARG); + ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, Variant(), "Variant is too deep. Bailing."); + + const Object *obj = p_variant.get_validated_object(); + if (obj == nullptr) { + return Variant(); + } + + ERR_FAIL_COND_V(!ClassDB::can_instantiate(obj->get_class()), Variant()); + + List prop_list; + obj->get_property_list(&prop_list); + + Array props; + for (const PropertyInfo &pi : prop_list) { + if (!(pi.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } + + Variant value; + if (pi.name == CoreStringName(script)) { + const Ref