avm2: Replace `json` with `serde_json`
The `json` crate seems unmaintained, and recently also causes compile errors with stable Rust 1.59.0. On the other hand, `serde_json` is very maintained and more popular. However, from some reason a cyclic package dependency has introduced by this change. For now use a workaround from: https://github.com/tkaitchuck/aHash/issues/95#issuecomment-903560879
This commit is contained in:
parent
fa459f7547
commit
0401b3c447
|
@ -1683,7 +1683,7 @@ checksum = "a538f217be4d405ff4719a283ca68323cc2384003eca5baaa87501e821c81dda"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"gpu-descriptor-types",
|
||||
"hashbrown",
|
||||
"hashbrown 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1733,6 +1733,12 @@ dependencies = [
|
|||
"wide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
|
@ -1824,12 +1830,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.0"
|
||||
version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||
checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.9.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -1955,12 +1961,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||
|
||||
[[package]]
|
||||
name = "khronos-egl"
|
||||
version = "4.1.0"
|
||||
|
@ -3133,7 +3133,6 @@ dependencies = [
|
|||
"indexmap",
|
||||
"instant",
|
||||
"jpeg-decoder 0.2.2",
|
||||
"json",
|
||||
"log",
|
||||
"lzma-rs",
|
||||
"minimp3",
|
||||
|
@ -3150,6 +3149,7 @@ dependencies = [
|
|||
"regress",
|
||||
"ruffle_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"swf",
|
||||
"symphonia",
|
||||
|
@ -3409,6 +3409,7 @@ version = "1.0.79"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa 1.0.1",
|
||||
"ryu",
|
||||
"serde",
|
||||
|
|
|
@ -13,7 +13,9 @@ fnv = "1.0.7"
|
|||
gc-arena = { git = "https://github.com/ruffle-rs/gc-arena" }
|
||||
generational-arena = "0.2.8"
|
||||
gif = "0.11.3"
|
||||
indexmap = "1.8.0"
|
||||
# TODO: From some reason newer indexmap versions cause a cyclic package dependency.
|
||||
# This is a workaround from: https://github.com/tkaitchuck/aHash/issues/95#issuecomment-903560879
|
||||
indexmap = "~1.6.2"
|
||||
log = "0.4"
|
||||
minimp3 = { version = "0.5.1", optional = true }
|
||||
png = { version = "0.17.3" }
|
||||
|
@ -34,12 +36,12 @@ instant = "0.1"
|
|||
encoding_rs = "0.8.30"
|
||||
rand = { version = "0.8.5", features = ["std", "small_rng"], default-features = false }
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||
nellymoser-rs = { git = "https://github.com/ruffle-rs/nellymoser" }
|
||||
h263-rs = { git = "https://github.com/ruffle-rs/h263-rs", rev = "023e14c73e565c4c778d41f66cfbac5ece6419b2", optional = true }
|
||||
h263-rs-yuv = { git = "https://github.com/ruffle-rs/h263-rs", rev = "023e14c73e565c4c778d41f66cfbac5ece6419b2", optional = true }
|
||||
regress = "0.4"
|
||||
flash-lso = { git = "https://github.com/ruffle-rs/rust-flash-lso", rev = "19fecd07b9888c4bdaa66771c468095783b52bed" }
|
||||
json = "0.12.4"
|
||||
lzma-rs = {version = "0.2.0", optional = true }
|
||||
dasp = { git = "https://github.com/RustAudio/dasp", rev = "f05a703", features = ["interpolate", "interpolate-linear", "signal"] }
|
||||
symphonia = { version = "0.5.0", default-features = false, features = ["mp3"], optional = true }
|
||||
|
|
|
@ -10,13 +10,11 @@ use crate::avm2::object::{ArrayObject, FunctionObject, Object, TObject};
|
|||
use crate::avm2::value::Value;
|
||||
use crate::avm2::Error;
|
||||
use crate::ecma_conversions::f64_to_wrapping_i32;
|
||||
use crate::string::AvmString;
|
||||
use crate::string::{AvmString, Units};
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use json::{
|
||||
codegen::Generator as JsonGenerator, object::Object as JsonObject, parse as parse_json,
|
||||
JsonValue,
|
||||
};
|
||||
use std::cmp;
|
||||
use serde::Serialize;
|
||||
use serde_json::{Map as JsonObject, Value as JsonValue};
|
||||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
|
||||
fn deserialize_json_inner<'gc>(
|
||||
|
@ -26,17 +24,14 @@ fn deserialize_json_inner<'gc>(
|
|||
) -> Result<Value<'gc>, Error> {
|
||||
Ok(match json {
|
||||
JsonValue::Null => Value::Null,
|
||||
JsonValue::Short(s) => {
|
||||
AvmString::new_utf8(activation.context.gc_context, s.to_string()).into()
|
||||
}
|
||||
JsonValue::String(s) => AvmString::new_utf8(activation.context.gc_context, s).into(),
|
||||
JsonValue::Boolean(b) => b.into(),
|
||||
JsonValue::Number(num) => {
|
||||
let num: f64 = num.into();
|
||||
if num.fract() == 0.0 {
|
||||
f64_to_wrapping_i32(num).into()
|
||||
JsonValue::Bool(b) => b.into(),
|
||||
JsonValue::Number(number) => {
|
||||
let number = number.as_f64().unwrap();
|
||||
if number.fract() == 0.0 {
|
||||
f64_to_wrapping_i32(number).into()
|
||||
} else {
|
||||
num.into()
|
||||
number.into()
|
||||
}
|
||||
}
|
||||
JsonValue::Object(js_obj) => {
|
||||
|
@ -86,72 +81,6 @@ fn deserialize_json<'gc>(
|
|||
}
|
||||
}
|
||||
|
||||
/// This is a custom generator backend for the json crate that allows modifying the indentation character.
|
||||
/// This generator is used in `JSON.stringify` when a string is passed to the `space` parameter.
|
||||
struct FlashStrGenerator<'a> {
|
||||
code: Vec<u8>,
|
||||
indent_str: &'a str,
|
||||
indent_size: u16,
|
||||
}
|
||||
|
||||
impl<'a> FlashStrGenerator<'a> {
|
||||
fn new(indent_str: &'a str) -> Self {
|
||||
Self {
|
||||
code: Vec::with_capacity(1024),
|
||||
indent_str,
|
||||
indent_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume(self) -> String {
|
||||
// SAFETY: JSON crate should never generate invalid UTF-8
|
||||
unsafe { String::from_utf8_unchecked(self.code) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> JsonGenerator for FlashStrGenerator<'a> {
|
||||
type T = Vec<u8>;
|
||||
|
||||
#[inline(always)]
|
||||
fn write(&mut self, slice: &[u8]) -> std::io::Result<()> {
|
||||
self.code.extend_from_slice(slice);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_char(&mut self, ch: u8) -> std::io::Result<()> {
|
||||
self.code.push(ch);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_writer(&mut self) -> &mut Vec<u8> {
|
||||
&mut self.code
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_min(&mut self, slice: &[u8], _: u8) -> std::io::Result<()> {
|
||||
self.code.extend_from_slice(slice);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_line(&mut self) -> std::io::Result<()> {
|
||||
self.code.push(b'\n');
|
||||
for _ in 0..self.indent_size {
|
||||
self.code.extend_from_slice(self.indent_str.as_bytes());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn indent(&mut self) {
|
||||
self.indent_size += 1;
|
||||
}
|
||||
|
||||
fn dedent(&mut self) {
|
||||
self.indent_size -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
enum Replacer<'gc> {
|
||||
Function(FunctionObject<'gc>),
|
||||
PropList(ArrayObject<'gc>),
|
||||
|
@ -235,7 +164,7 @@ impl<'gc> AvmSerializer<'gc> {
|
|||
let mapped = self.map_value(activation, || key, value)?;
|
||||
if !matches!(mapped, Value::Undefined) {
|
||||
js_obj.insert(
|
||||
&key.to_utf8_lossy(),
|
||||
key.to_utf8_lossy().into_owned(),
|
||||
self.serialize_value(activation, mapped)?,
|
||||
);
|
||||
}
|
||||
|
@ -252,7 +181,7 @@ impl<'gc> AvmSerializer<'gc> {
|
|||
let mapped = self.map_value(activation, || name, value)?;
|
||||
if !matches!(mapped, Value::Undefined) {
|
||||
js_obj.insert(
|
||||
&name.to_utf8_lossy(),
|
||||
name.to_utf8_lossy().into_owned(),
|
||||
self.serialize_value(activation, mapped)?,
|
||||
);
|
||||
}
|
||||
|
@ -362,7 +291,7 @@ pub fn parse<'gc>(
|
|||
.unwrap_or(&Value::Undefined)
|
||||
.coerce_to_object(activation)
|
||||
.ok();
|
||||
let parsed = parse_json(&input.to_utf8_lossy())
|
||||
let parsed = serde_json::from_str(&input.to_utf8_lossy())
|
||||
.map_err(|_| "SyntaxError: Error #1132: Invalid JSON parse input.")?;
|
||||
deserialize_json(activation, parsed, reviver)
|
||||
}
|
||||
|
@ -390,19 +319,18 @@ pub fn stringify<'gc>(
|
|||
}
|
||||
}).transpose()?;
|
||||
|
||||
let mut serializer = AvmSerializer::new(replacer);
|
||||
let result = serializer.serialize(activation, *val)?;
|
||||
// NOTE: We do not coerce to a string or to a number, the value must already be a string or number.
|
||||
let output = if let Value::String(s) = spaces {
|
||||
// If the string is empty, just use the normal dump generator.
|
||||
let indent = if let Value::String(s) = spaces {
|
||||
if s.is_empty() {
|
||||
result.dump()
|
||||
None
|
||||
} else {
|
||||
// we can only use the first 10 characters
|
||||
let indent = s.slice(..cmp::min(s.len(), 10)).unwrap().to_utf8_lossy();
|
||||
let mut gen = FlashStrGenerator::new(&indent);
|
||||
gen.write_json(&result).expect("Can't fail");
|
||||
gen.consume()
|
||||
// We can only use the first 10 characters.
|
||||
let indent = &s[..s.len().min(10)];
|
||||
let indent_bytes = match indent.units() {
|
||||
Units::Bytes(units) => Cow::Borrowed(units),
|
||||
Units::Wide(_) => Cow::Owned(indent.to_utf8_lossy().into_owned().into_bytes()),
|
||||
};
|
||||
Some(indent_bytes)
|
||||
}
|
||||
} else {
|
||||
let indent_size = spaces
|
||||
|
@ -410,12 +338,28 @@ pub fn stringify<'gc>(
|
|||
.unwrap_or(0.0)
|
||||
.clamp(0.0, 10.0) as u16;
|
||||
if indent_size == 0 {
|
||||
result.dump()
|
||||
None
|
||||
} else {
|
||||
result.pretty(indent_size)
|
||||
Some(Cow::Owned(b" ".repeat(indent_size.into())))
|
||||
}
|
||||
};
|
||||
Ok(AvmString::new_utf8(activation.context.gc_context, output).into())
|
||||
|
||||
let mut serializer = AvmSerializer::new(replacer);
|
||||
let json = serializer.serialize(activation, *val)?;
|
||||
let result = match indent {
|
||||
Some(indent) => {
|
||||
let mut vec = Vec::with_capacity(128);
|
||||
let formatter = serde_json::ser::PrettyFormatter::with_indent(&indent);
|
||||
let mut serializer = serde_json::Serializer::with_formatter(&mut vec, formatter);
|
||||
json.serialize(&mut serializer)?;
|
||||
unsafe {
|
||||
// `serde_json` never emits invalid UTF-8.
|
||||
String::from_utf8_unchecked(vec)
|
||||
}
|
||||
}
|
||||
None => serde_json::to_string(&json)?,
|
||||
};
|
||||
Ok(AvmString::new_utf8(activation.context.gc_context, result).into())
|
||||
}
|
||||
|
||||
/// Construct `JSON`'s class.
|
||||
|
|
Loading…
Reference in New Issue