avm2: Add new 'stub-report' binary crate
This writes the AVM2 stub report to the specified path.
This commit is contained in:
parent
25b5c7b4e2
commit
78873e3670
|
@ -4989,6 +4989,15 @@ version = "0.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "stub-report"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"ruffle_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swf"
|
||||
version = "0.2.0"
|
||||
|
|
|
@ -20,6 +20,8 @@ members = [
|
|||
|
||||
"ruffle_gc_arena",
|
||||
|
||||
"stub-report",
|
||||
|
||||
"video",
|
||||
"video/software",
|
||||
|
||||
|
@ -47,6 +49,8 @@ naga = { version = "0.19.0", features = ["wgsl-out"] }
|
|||
naga_oil = "0.12.0"
|
||||
wgpu = "0.19.1"
|
||||
egui = "0.25.0"
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
anyhow = "1.0"
|
||||
|
||||
[workspace.lints.rust]
|
||||
# Clippy nightly often adds new/buggy lints that we want to ignore.
|
||||
|
|
|
@ -58,6 +58,7 @@ mod qname;
|
|||
mod regexp;
|
||||
mod scope;
|
||||
mod script;
|
||||
#[cfg(feature = "known_stubs")]
|
||||
pub mod specification;
|
||||
mod string;
|
||||
mod stubs;
|
||||
|
|
|
@ -12,7 +12,6 @@ use crate::avm2::Multiname;
|
|||
use crate::avm2::QName;
|
||||
use gc_arena::{Collect, GcCell, GcWeakCell, Mutation};
|
||||
use ruffle_wstr::WStr;
|
||||
use std::cell::Ref;
|
||||
|
||||
use super::class::Class;
|
||||
use super::error::error;
|
||||
|
@ -349,10 +348,6 @@ impl<'gc> Domain<'gc> {
|
|||
Ref::map(self.0.read(), |this| &this.defs)
|
||||
}
|
||||
|
||||
pub fn classes(&self) -> Ref<PropertyMap<'gc, GcCell<'gc, Class<'gc>>>> {
|
||||
Ref::map(self.0.read(), |this| &this.classes)
|
||||
}
|
||||
|
||||
pub fn is_default_domain_memory(&self) -> bool {
|
||||
let read = self.0.read();
|
||||
read.domain_memory.expect("Missing domain memory").as_ptr()
|
||||
|
|
|
@ -148,7 +148,7 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
/// Retrieve the values stored directly on this ScriptObjectData.
|
||||
///
|
||||
/// This should only be used for debugging purposes.
|
||||
pub fn values(&self) -> &DynamicMap<StringOrObject<'gc>, Value<'gc>> {
|
||||
pub fn values(&self) -> &DynamicMap<DynamicKey<'gc>, Value<'gc>> {
|
||||
&self.values
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::avm2::dynamic_map::{DynamicMap, StringOrObject};
|
||||
use crate::avm2::dynamic_map::DynamicKey;
|
||||
use crate::avm2::function::Executable;
|
||||
use crate::avm2::method::{Method, ParamConfig};
|
||||
use crate::avm2::object::TObject;
|
||||
|
@ -11,6 +11,7 @@ use fnv::{FnvHashMap, FnvHashSet};
|
|||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
|
@ -298,12 +299,14 @@ impl Definition {
|
|||
|
||||
let prototype = class_object.prototype();
|
||||
let prototype_base = prototype.base();
|
||||
let prototype_values: &DynamicMap<StringOrObject<'gc>, Value<'gc>> =
|
||||
prototype_base.values();
|
||||
let prototype_values = prototype_base.values();
|
||||
for (key, value) in prototype_values.as_hashmap().iter() {
|
||||
let name = match key {
|
||||
StringOrObject::String(name) => *name,
|
||||
StringOrObject::Object(object) => {
|
||||
DynamicKey::String(name) => *name,
|
||||
DynamicKey::Uint(key) => {
|
||||
AvmString::new_utf8(activation.context.gc_context, key.to_string())
|
||||
}
|
||||
DynamicKey::Object(object) => {
|
||||
Value::Object(*object).coerce_to_string(activation).unwrap()
|
||||
}
|
||||
};
|
||||
|
@ -321,13 +324,13 @@ impl Definition {
|
|||
activation.avm2(),
|
||||
class.read().class_traits(),
|
||||
&mut definition.static_traits,
|
||||
&stubs,
|
||||
stubs,
|
||||
);
|
||||
Self::fill_traits(
|
||||
activation.avm2(),
|
||||
class.read().instance_traits(),
|
||||
&mut definition.instance_traits,
|
||||
&stubs,
|
||||
stubs,
|
||||
);
|
||||
|
||||
definition
|
||||
|
@ -454,10 +457,12 @@ impl Definition {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn capture_specification(context: &mut UpdateContext) {
|
||||
let mut definitions = FnvHashMap::<String, Definition>::default();
|
||||
#[allow(unreachable_code, unused_variables, clippy::diverging_sub_expression)]
|
||||
pub fn capture_specification(context: &mut UpdateContext, output: &Path) {
|
||||
let stubs = crate::stub::get_known_stubs();
|
||||
|
||||
let mut definitions = FnvHashMap::<String, Definition>::default();
|
||||
|
||||
let defs = context.avm2.playerglobals_domain.defs().clone();
|
||||
let mut activation = Activation::from_nothing(context.reborrow());
|
||||
for (name, namespace, _) in defs.iter() {
|
||||
|
@ -510,7 +515,7 @@ pub fn capture_specification(context: &mut UpdateContext) {
|
|||
);
|
||||
}
|
||||
}
|
||||
serde_json::to_writer_pretty(&File::create("implementation.json").unwrap(), &definitions)
|
||||
.unwrap();
|
||||
serde_json::to_writer_pretty(&File::create(output).unwrap(), &definitions).unwrap();
|
||||
tracing::info!("Wrote stub report to {output:?}");
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,7 @@ use crate::avm1::Object;
|
|||
use crate::avm1::SystemProperties;
|
||||
use crate::avm1::VariableDumper;
|
||||
use crate::avm1::{Activation, ActivationIdentifier};
|
||||
use crate::avm1::{ScriptObject, TObject, Value};
|
||||
use crate::avm1::{TObject, Value};
|
||||
use crate::avm2::api_version::ApiVersion;
|
||||
use crate::avm2::specification::capture_specification;
|
||||
use crate::avm2::{
|
||||
object::TObject as _, Activation as Avm2Activation, Avm2, CallStack, Object as Avm2Object,
|
||||
};
|
||||
|
@ -2124,6 +2121,8 @@ pub struct PlayerBuilder {
|
|||
frame_rate: Option<f64>,
|
||||
external_interface_providers: Vec<Box<dyn ExternalInterfaceProvider>>,
|
||||
fs_command_provider: Box<dyn FsCommandProvider>,
|
||||
#[cfg(feature = "known_stubs")]
|
||||
stub_report_output: Option<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
impl PlayerBuilder {
|
||||
|
@ -2172,6 +2171,8 @@ impl PlayerBuilder {
|
|||
frame_rate: None,
|
||||
external_interface_providers: vec![],
|
||||
fs_command_provider: Box::new(NullFsCommandProvider),
|
||||
#[cfg(feature = "known_stubs")]
|
||||
stub_report_output: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2361,6 +2362,14 @@ impl PlayerBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "known_stubs")]
|
||||
/// Sets the output path for the stub report. When set, the player
|
||||
/// will write the report to this path and exit the process.
|
||||
pub fn with_stub_report_output(mut self, output: std::path::PathBuf) -> Self {
|
||||
self.stub_report_output = Some(output);
|
||||
self
|
||||
}
|
||||
|
||||
fn create_gc_root<'gc>(
|
||||
gc_context: &'gc gc_arena::Mutation<'gc>,
|
||||
player_version: u8,
|
||||
|
@ -2551,7 +2560,10 @@ impl PlayerBuilder {
|
|||
stage.set_allow_fullscreen(context, self.allow_fullscreen);
|
||||
stage.post_instantiation(context, None, Instantiator::Movie, false);
|
||||
stage.build_matrices(context);
|
||||
capture_specification(context);
|
||||
#[cfg(feature = "known_stubs")]
|
||||
if let Some(stub_path) = self.stub_report_output {
|
||||
crate::avm2::specification::capture_specification(context, &stub_path);
|
||||
}
|
||||
});
|
||||
player_lock.gc_arena.borrow().mutate(|context, root| {
|
||||
let call_stack = root.data.read().avm2.call_stack();
|
||||
|
|
|
@ -11,7 +11,7 @@ version.workspace = true
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
clap = { workspace = true }
|
||||
futures = "0.3"
|
||||
ruffle_core = { path = "../core", features = ["deterministic", "default_font"] }
|
||||
ruffle_render_wgpu = { path = "../render/wgpu", features = ["clap"] }
|
||||
|
@ -19,8 +19,8 @@ image = { version = "0.24.8", default-features = false, features = ["png"] }
|
|||
log = "0.4"
|
||||
walkdir = "2.4.0"
|
||||
indicatif = "0.17"
|
||||
anyhow = "1.0"
|
||||
rayon = "1.8.1"
|
||||
anyhow = { workspace = true }
|
||||
|
||||
[features]
|
||||
avm_debug = ["ruffle_core/avm_debug"]
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "stub-report"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
ruffle_core = { path = "../core", features = ["known_stubs"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
|
@ -0,0 +1,21 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use ruffle_core::PlayerBuilder;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(name = "Ruffle Stub Report Generator", author, version)]
|
||||
struct Opt {
|
||||
/// The file to store the stub report output
|
||||
#[clap(name = "output")]
|
||||
output_path: PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let opt: Opt = Opt::parse();
|
||||
PlayerBuilder::new()
|
||||
.with_stub_report_output(opt.output_path)
|
||||
.build();
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue