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