cgen: json sumtype inlining (#11961)
							parent
							
								
									430677a0c0
								
							
						
					
					
						commit
						c75271fcb7
					
				|  | @ -10,7 +10,7 @@ module json | |||
| 
 | ||||
| struct C.cJSON { | ||||
| 	valueint    int | ||||
| 	valuedouble f32 | ||||
| 	valuedouble f64 | ||||
| 	valuestring &char | ||||
| } | ||||
| 
 | ||||
|  | @ -78,6 +78,13 @@ fn decode_byte(root &C.cJSON) byte { | |||
| 	return byte(root.valueint) | ||||
| } | ||||
| 
 | ||||
| fn decode_u8(root &C.cJSON) u8 { | ||||
| 	if isnil(root) { | ||||
| 		return byte(0) | ||||
| 	} | ||||
| 	return byte(root.valueint) | ||||
| } | ||||
| 
 | ||||
| fn decode_u16(root &C.cJSON) u16 { | ||||
| 	if isnil(root) { | ||||
| 		return u16(0) | ||||
|  | @ -103,14 +110,26 @@ fn decode_f32(root &C.cJSON) f32 { | |||
| 	if isnil(root) { | ||||
| 		return f32(0) | ||||
| 	} | ||||
| 	return root.valuedouble | ||||
| 	return f32(root.valuedouble) | ||||
| } | ||||
| 
 | ||||
| fn decode_f64(root &C.cJSON) f64 { | ||||
| 	if isnil(root) { | ||||
| 		return f64(0) | ||||
| 	} | ||||
| 	return f64(root.valuedouble) | ||||
| 	return root.valuedouble | ||||
| } | ||||
| 
 | ||||
| fn decode_rune(root &C.cJSON) rune { | ||||
| 	if isnil(root) { | ||||
| 		return rune(0) | ||||
| 	} | ||||
| 	if isnil(root.valuestring) { | ||||
| 		return rune(0) | ||||
| 	} | ||||
| 
 | ||||
| 	// TODO: Parse as runes, bypassing string casting...?
 | ||||
| 	return unsafe { tos_clone(&byte(root.valuestring)).runes().first() } | ||||
| } | ||||
| 
 | ||||
| fn decode_string(root &C.cJSON) string { | ||||
|  | @ -153,6 +172,10 @@ fn encode_byte(val byte) &C.cJSON { | |||
| 	return C.cJSON_CreateNumber(val) | ||||
| } | ||||
| 
 | ||||
| fn encode_u8(val u8) &C.cJSON { | ||||
| 	return C.cJSON_CreateNumber(val) | ||||
| } | ||||
| 
 | ||||
| fn encode_u16(val u16) &C.cJSON { | ||||
| 	return C.cJSON_CreateNumber(val) | ||||
| } | ||||
|  | @ -177,6 +200,10 @@ fn encode_bool(val bool) &C.cJSON { | |||
| 	return C.cJSON_CreateBool(val) | ||||
| } | ||||
| 
 | ||||
| fn encode_rune(val rune) &C.cJSON { | ||||
| 	return C.cJSON_CreateString(&char(val.str().str)) | ||||
| } | ||||
| 
 | ||||
| fn encode_string(val string) &C.cJSON { | ||||
| 	return C.cJSON_CreateString(&char(val.str)) | ||||
| } | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ fn test_encode_decode_sumtype() ? { | |||
| 		other: [ | ||||
| 			Entity(Item{'Pen'}), | ||||
| 			Item{'Cookie'}, | ||||
| 			Animal.dog, | ||||
| 			Animal.cat, | ||||
| 			'Stool', | ||||
| 			time.now(), | ||||
| 		] | ||||
|  | @ -81,6 +81,7 @@ fn test_encode_decode_sumtype() ? { | |||
| 
 | ||||
| 	assert game.title == dec.title | ||||
| 	assert game.player == dec.player | ||||
| 	assert (game.other[2] as Animal) == (dec.other[2] as Animal) | ||||
| 	assert (game.other[4] as time.Time).unix_time() == (dec.other[4] as time.Time).unix_time() | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -86,10 +86,13 @@ $enc_fn_dec { | |||
| 			return | ||||
| 		} | ||||
| 		enc.writeln('\to = cJSON_CreateObject();') | ||||
| 		if psym.info !is ast.Struct { | ||||
| 		if psym.info is ast.Struct { | ||||
| 			g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec) | ||||
| 		} else if psym.kind == .sum_type { | ||||
| 			verror('json: $sym.name aliased sumtypes does not work at the moment') | ||||
| 		} else { | ||||
| 			verror('json: $sym.name is not struct') | ||||
| 		} | ||||
| 		g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec) | ||||
| 	} else if sym.kind == .sum_type { | ||||
| 		enc.writeln('\to = cJSON_CreateObject();') | ||||
| 		// Sumtypes. Range through variants of sumtype
 | ||||
|  | @ -118,46 +121,164 @@ $enc_fn_dec { | |||
| [inline] | ||||
| fn (mut g Gen) gen_sumtype_enc_dec(sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder) { | ||||
| 	info := sym.info as ast.SumType | ||||
| 	type_var := g.new_tmp_var() | ||||
| 	typ := sym.idx | ||||
| 
 | ||||
| 	// DECODING (inline)
 | ||||
| 	$if !json_no_inline_sumtypes ? { | ||||
| 		type_tmp := g.new_tmp_var() | ||||
| 		dec.writeln('\tif (cJSON_IsObject(root)) {') | ||||
| 		dec.writeln('\t\tcJSON* $type_tmp = js_get(root, "_type");') | ||||
| 		dec.writeln('\t\tif ($type_tmp != 0) {') | ||||
| 		dec.writeln('\t\t\tchar* $type_var = cJSON_GetStringValue($type_tmp);') | ||||
| 		// dec.writeln('\t\t\tcJSON_DeleteItemFromObjectCaseSensitive(root, "_type");')
 | ||||
| 	} | ||||
| 
 | ||||
| 	mut variant_types := []string{} | ||||
| 	mut variant_symbols := []ast.TypeSymbol{} | ||||
| 	mut at_least_one_prim := false | ||||
| 	for variant in info.variants { | ||||
| 		variant_typ := g.typ(variant) | ||||
| 		variant_types << variant_typ | ||||
| 		variant_sym := g.table.get_type_symbol(variant) | ||||
| 		variant_symbols << variant_sym | ||||
| 		at_least_one_prim = at_least_one_prim || is_js_prim(variant_typ) | ||||
| 			|| variant_sym.kind == .enum_ || variant_sym.name == 'time.Time' | ||||
| 		unmangled_variant_name := variant_sym.name.split('.').last() | ||||
| 
 | ||||
| 		// TODO: Do not generate dec/enc for 'time.Time', because we handle it by saving it as u64
 | ||||
| 		g.gen_json_for_type(variant) | ||||
| 
 | ||||
| 		// Helpers for decoding
 | ||||
| 		g.write_sumtype_casting_fn(variant, typ) | ||||
| 		g.definitions.writeln('static inline $sym.cname ${variant_typ}_to_sumtype_${sym.cname}($variant_typ* x);') | ||||
| 
 | ||||
| 		// ENCODING
 | ||||
| 		enc.writeln('\tif (val._typ == $variant) {') | ||||
| 		if variant_sym.kind == .enum_ { | ||||
| 			enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", json__encode_u64(*val._$variant_typ));') | ||||
| 		} else if variant_sym.name == 'time.Time' { | ||||
| 			enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", json__encode_i64(val._$variant_typ->_v_unix));') | ||||
| 		} else { | ||||
| 			enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", json__encode_${variant_typ}(*val._$variant_typ));') | ||||
| 		$if json_no_inline_sumtypes ? { | ||||
| 			if variant_sym.kind == .enum_ { | ||||
| 				enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", ${js_enc_name('u64')}(*val._$variant_typ));') | ||||
| 			} else if variant_sym.name == 'time.Time' { | ||||
| 				enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", ${js_enc_name('i64')}(val._$variant_typ->_v_unix));') | ||||
| 			} else { | ||||
| 				enc.writeln('\t\tcJSON_AddItemToObject(o, "$unmangled_variant_name", ${js_enc_name(variant_typ)}(*val._$variant_typ));') | ||||
| 			} | ||||
| 		} $else { | ||||
| 			if is_js_prim(variant_typ) { | ||||
| 				enc.writeln('\t\to = ${js_enc_name(variant_typ)}(*val._$variant_typ);') | ||||
| 			} else if variant_sym.kind == .enum_ { | ||||
| 				enc.writeln('\t\to = ${js_enc_name('u64')}(*val._$variant_typ);') | ||||
| 			} else if variant_sym.name == 'time.Time' { | ||||
| 				enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("$unmangled_variant_name"));') | ||||
| 				enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(val._$variant_typ->_v_unix));') | ||||
| 			} else { | ||||
| 				enc.writeln('\t\to = ${js_enc_name(variant_typ)}(*val._$variant_typ);') | ||||
| 				enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("$unmangled_variant_name"));') | ||||
| 			} | ||||
| 		} | ||||
| 		enc.writeln('\t}') | ||||
| 
 | ||||
| 		// DECODING
 | ||||
| 		dec.writeln('\tif (strcmp("$unmangled_variant_name", root->child->string) == 0) {') | ||||
| 		tmp := g.new_tmp_var() | ||||
| 		if is_js_prim(variant_typ) { | ||||
| 			gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, false) | ||||
| 			dec.writeln('\t\t$variant_typ value = (${js_dec_name(variant_typ)})(jsonroot_$tmp);') | ||||
| 		} else if variant_sym.kind == .enum_ { | ||||
| 			gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, false) | ||||
| 			dec.writeln('\t\t$variant_typ value = json__decode_u64(jsonroot_$tmp);') | ||||
| 		} else if variant_sym.name == 'time.Time' { | ||||
| 			gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, false) | ||||
| 			dec.writeln('\t\t$variant_typ value = time__unix(json__decode_i64(jsonroot_$tmp));') | ||||
| 		} else { | ||||
| 			gen_js_get_opt(js_dec_name(variant_typ), variant_typ, sym.cname, tmp, unmangled_variant_name, mut | ||||
| 				dec, false) | ||||
| 			dec.writeln('\t\t$variant_typ value = *($variant_typ*)(${tmp}.data);') | ||||
| 		$if json_no_inline_sumtypes ? { | ||||
| 			dec.writeln('\tif (strcmp("$unmangled_variant_name", root->child->string) == 0) {') | ||||
| 			if is_js_prim(variant_typ) { | ||||
| 				gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true) | ||||
| 				dec.writeln('\t\t$variant_typ value = ${js_dec_name(variant_typ)}(jsonroot_$tmp);') | ||||
| 			} else if variant_sym.kind == .enum_ { | ||||
| 				gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true) | ||||
| 				dec.writeln('\t\t$variant_typ value = ${js_dec_name('u64')}(jsonroot_$tmp);') | ||||
| 			} else if variant_sym.name == 'time.Time' { | ||||
| 				gen_js_get(variant_typ, tmp, unmangled_variant_name, mut dec, true) | ||||
| 				dec.writeln('\t\t$variant_typ value = time__unix(${js_dec_name('i64')}(jsonroot_$tmp));') | ||||
| 			} else { | ||||
| 				gen_js_get_opt(js_dec_name(variant_typ), variant_typ, sym.cname, tmp, | ||||
| 					unmangled_variant_name, mut dec, true) | ||||
| 				dec.writeln('\t\t$variant_typ value = *($variant_typ*)(${tmp}.data);') | ||||
| 			} | ||||
| 			dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&value);') | ||||
| 			dec.writeln('\t}') | ||||
| 		} $else { | ||||
| 			if variant_sym.name == 'time.Time' { | ||||
| 				dec.writeln('\t\t\tif (strcmp("Time", $type_var) == 0) {') | ||||
| 				gen_js_get(sym.cname, tmp, 'value', mut dec, true) | ||||
| 				dec.writeln('\t\t\t\t$variant_typ $tmp = time__unix(${js_dec_name('i64')}(jsonroot_$tmp));') | ||||
| 				dec.writeln('\t\t\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&$tmp);') | ||||
| 				dec.writeln('\t\t\t}') | ||||
| 			} else if !is_js_prim(variant_typ) && variant_sym.kind != .enum_ { | ||||
| 				dec.writeln('\t\t\tif (strcmp("$unmangled_variant_name", $type_var) == 0) {') | ||||
| 				dec.writeln('\t\t\t\tOption_$variant_typ $tmp = ${js_dec_name(variant_typ)}(root);') | ||||
| 				dec.writeln('\t\t\t\tif (${tmp}.state != 0) {') | ||||
| 				dec.writeln('\t\t\t\t\treturn (Option_$sym.cname){ .state = ${tmp}.state, .err = ${tmp}.err, .data = {0} };') | ||||
| 				dec.writeln('\t\t\t\t}') | ||||
| 				dec.writeln('\t\t\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(($variant_typ*)${tmp}.data);') | ||||
| 				dec.writeln('\t\t\t}') | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// DECODING (inline)
 | ||||
| 	$if !json_no_inline_sumtypes ? { | ||||
| 		dec.writeln('\t\t}') | ||||
| 
 | ||||
| 		mut number_is_met := false | ||||
| 		mut string_is_met := false | ||||
| 		mut last_number_type := '' | ||||
| 
 | ||||
| 		if at_least_one_prim { | ||||
| 			dec.writeln('\t} else {') | ||||
| 
 | ||||
| 			if 'bool' in variant_types { | ||||
| 				var_t := 'bool' | ||||
| 				dec.writeln('\t\tif (cJSON_IsBool(root)) {') | ||||
| 				dec.writeln('\t\t\t$var_t value = ${js_dec_name(var_t)}(root);') | ||||
| 				dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);') | ||||
| 				dec.writeln('\t\t}') | ||||
| 			} | ||||
| 
 | ||||
| 			for i, var_t in variant_types { | ||||
| 				if variant_symbols[i].kind == .enum_ { | ||||
| 					if number_is_met { | ||||
| 						var_num := var_t.replace('__', '.') | ||||
| 						last_num := last_number_type.replace('__', '.') | ||||
| 						verror('json: can not decode `$sym.name` sumtype, too many numeric types (conflict of `$last_num` and `$var_num`), you can try to use alias for `$var_num` or compile v with `json_no_inline_sumtypes` flag') | ||||
| 					} | ||||
| 					number_is_met = true | ||||
| 					last_number_type = var_t | ||||
| 					dec.writeln('\t\tif (cJSON_IsNumber(root)) {') | ||||
| 					dec.writeln('\t\t\t$var_t value = ${js_dec_name('u64')}(root);') | ||||
| 					dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);') | ||||
| 					dec.writeln('\t\t}') | ||||
| 				} | ||||
| 
 | ||||
| 				if var_t in ['string', 'rune'] { | ||||
| 					if string_is_met { | ||||
| 						var_num := var_t.replace('__', '.') | ||||
| 						verror('json: can not decode `$sym.name` sumtype, too many string types (conflict of `string` and `rune`), you can try to use alias for `$var_num` or compile v with `json_no_inline_sumtypes` flag') | ||||
| 					} | ||||
| 					string_is_met = true | ||||
| 					dec.writeln('\t\tif (cJSON_IsString(root)) {') | ||||
| 					dec.writeln('\t\t\t$var_t value = ${js_dec_name(var_t)}(root);') | ||||
| 					dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);') | ||||
| 					dec.writeln('\t\t}') | ||||
| 				} | ||||
| 
 | ||||
| 				if var_t in ['i64', 'int', 'i8', 'u64', 'u32', 'u16', 'byte', 'u8', 'rune', 'f64', | ||||
| 					'f32'] { | ||||
| 					if number_is_met { | ||||
| 						var_num := var_t.replace('__', '.') | ||||
| 						last_num := last_number_type.replace('__', '.') | ||||
| 						verror('json: can not decode `$sym.name` sumtype, too many numeric types (conflict of `$last_num` and `$var_num`), you can try to use alias for `$var_num` or compile v with `json_no_inline_sumtypes` flag') | ||||
| 					} | ||||
| 					number_is_met = true | ||||
| 					last_number_type = var_t | ||||
| 					dec.writeln('\t\tif (cJSON_IsNumber(root)) {') | ||||
| 					dec.writeln('\t\t\t$var_t value = ${js_dec_name(var_t)}(root);') | ||||
| 					dec.writeln('\t\t\tres = ${var_t}_to_sumtype_${sym.cname}(&value);') | ||||
| 					dec.writeln('\t\t}') | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${sym.cname}(&value);') | ||||
| 		dec.writeln('\t}') | ||||
| 	} | ||||
| } | ||||
|  | @ -294,8 +415,8 @@ fn js_dec_name(typ string) string { | |||
| } | ||||
| 
 | ||||
| fn is_js_prim(typ string) bool { | ||||
| 	return typ in ['int', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u16', 'u32', 'u64', | ||||
| 		'byte'] | ||||
| 	return typ in ['int', 'rune', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u8', 'u16', | ||||
| 		'u32', 'u64', 'byte'] | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) decode_array(value_type ast.Type) string { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue