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:
relrelb 2022-02-26 03:14:37 +02:00 committed by relrelb
parent fa459f7547
commit 0401b3c447
3 changed files with 57 additions and 110 deletions

23
Cargo.lock generated
View File

@ -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",

View File

@ -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 }

View File

@ -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.