Skip to content

Commit

Permalink
breakpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
colin-i committed Mar 15, 2024
1 parent 69ba551 commit 7d67fa2
Show file tree
Hide file tree
Showing 13 changed files with 297 additions and 48 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ ttf-parser = "0.20"
num-bigint = "0.4"
unic-segment = "0.9.0"
id3 = "1.13.1"
web-sys = { version="0.3.69", features=[ "console" ] }

[target.'cfg(not(target_family = "wasm"))'.dependencies.futures]
version = "0.3.30"
Expand Down
33 changes: 33 additions & 0 deletions core/src/avm1/activation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::avm1::callable_value::CallableValue;
use crate::avm1::debug::VariableDumperJson;
use crate::avm1::error::Error;
use crate::avm1::function::{Avm1Function, ExecutionReason, FunctionObject};
use crate::avm1::object::{Object, TObject};
Expand All @@ -24,9 +25,11 @@ use smallvec::SmallVec;
use std::borrow::Cow;
use std::cmp::min;
use std::fmt;
use std::io;
use swf::avm1::read::Reader;
use swf::avm1::types::*;
use url::form_urlencoded;
use web_sys::console;
use web_time::Instant;

use super::object_reference::MovieClipReference;
Expand Down Expand Up @@ -464,6 +467,36 @@ impl<'a, 'gc> Activation<'a, 'gc> {
"({}) Action: {action:?}",
self.id.depth(),
);
if self.context.avm1.output_json != -1 {
if self.context.avm1.output_json_stdin {
println!("{}", serde_json::to_string(&action).unwrap());

let mut buffer = String::new();
match io::stdin().read_line(&mut buffer).unwrap() {
#[cfg(not(windows))]
1 => (), //\n , \r //continue
#[cfg(windows)]
2 => (), //\r\n //continue

_ => {
//breakpoint
let mut dumper = VariableDumperJson::new();
dumper.print_activation(self);
println!("{}", dumper.output());
let _ = io::stdin().read_line(&mut buffer); //block again to aknowledge the breakpoint at the other side
}
}
} else {
if self.context.avm1.output_json == 1 {
console::log_1(&serde_json::to_string(&action).unwrap().into());
}
if action.discriminant() == self.context.avm1.output_json_code {
let mut dumper = VariableDumperJson::new();
dumper.print_activation(self);
console::log_1(&dumper.output().into());
}
}
}

match action {
Action::Add => self.action_add(),
Expand Down
178 changes: 156 additions & 22 deletions core/src/avm1/debug.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
use crate::avm1::activation::Activation;
use crate::avm1::{Object, ObjectPtr, TObject, Value};
use crate::display_object::{TDisplayObject, TDisplayObjectContainer};
use crate::string::AvmString;
use std::fmt::Write;

macro_rules! print_string {
($self:expr,$string:expr) => {
$self.output.push('\"');
for c in $string.chars() {
let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
let escape = match u8::try_from(c as u32) {
Ok(b'"') => "\\\"",
Ok(b'\\') => "\\\\",
Ok(b'\n') => "\\n",
Ok(b'\r') => "\\r",
Ok(b'\t') => "\\t",
Ok(0x08) => "\\b",
Ok(0x0C) => "\\f",
_ => {
$self.output.push(c);
continue;
}
};
$self.output.push_str(escape);
}
$self.output.push('\"');
};
}

#[allow(dead_code)]
pub struct VariableDumper<'a> {
objects: Vec<*const ObjectPtr>,
Expand Down Expand Up @@ -57,28 +82,7 @@ impl<'a> VariableDumper<'a> {
}

pub fn print_string(&mut self, string: AvmString<'_>) {
self.output.push('\"');

for c in string.chars() {
let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
let escape = match u8::try_from(c as u32) {
Ok(b'"') => "\\\"",
Ok(b'\\') => "\\\\",
Ok(b'\n') => "\\n",
Ok(b'\r') => "\\r",
Ok(b'\t') => "\\t",
Ok(0x08) => "\\b",
Ok(0x0C) => "\\f",
_ => {
self.output.push(c);
continue;
}
};

self.output.push_str(escape);
}

self.output.push('\"');
print_string!(self, string);
}

pub fn print_object<'gc>(
Expand Down Expand Up @@ -189,6 +193,136 @@ impl<'a> VariableDumper<'a> {
}
}

pub struct VariableDumperJson {
output: String,
}

impl<'a> VariableDumperJson {
pub fn new() -> Self {
Self {
output: String::new(),
}
}

pub fn output(&mut self) -> &str {
self.output.push('}');
&self.output
}

fn print_property<'gc>(
&mut self,
object: &Object<'gc>,
key: AvmString<'gc>,
activation: &mut Activation<'_, 'gc>,
) {
match object.get(key, activation) {
Ok(value) => {
self.print_value(&value, activation);
}
Err(e) => {
self.output.push_str("\"Error\": \"");
self.output.push_str(&e.to_string());
self.output.push('\"');
}
}
}

fn print_properties<'gc>(
&mut self,
object: &Object<'gc>,
activation: &mut Activation<'_, 'gc>,
) {
let keys = object.get_keys(activation, false);
if keys.is_empty() {
self.output.push_str("{}");
} else {
self.output.push('{');

let mut b = false;
for key in keys.into_iter() {
if b {
self.output.push(',');
} else {
b = true;
}
self.output.push('\"');
self.output.push_str(&key.to_utf8_lossy());
self.output.push_str("\":");
self.print_property(object, key, activation);
}

self.output.push('}');
}
}

fn print_value<'gc>(&mut self, value: &Value<'gc>, activation: &mut Activation<'_, 'gc>) {
match value {
Value::Undefined => self.output.push_str("[]"), // "undefined" is not valid json, {} is for empty objects, [] ? (is not conflicting with "NewObject" for "Array")
Value::Null => self.output.push_str("null"),
Value::Bool(value) => self.output.push_str(&value.to_string()),
Value::Number(value) => self.output.push_str(&value.to_string()),
Value::String(value) => {
print_string!(self, *value);
}
Value::Object(object) => {
self.print_properties(object, activation);
}
Value::MovieClip(_) => {
let obj = value.coerce_to_object(activation);
self.print_properties(&obj, activation); //object's properties
}
}
}

fn print_variables<'gc>(
&mut self,
name: &str,
object: &Object<'gc>,
activation: &mut Activation<'_, 'gc>,
) {
let keys = object.get_keys(activation, false);
if keys.is_empty() {
return;
}

if self.output != "" {
self.output.push(',');
} else {
self.output.push('{');
}
self.output.push('\"');
self.output.push_str(name);
self.output.push_str("\":{");

let mut b = false;
for key in keys.into_iter() {
if b {
self.output.push(',');
} else {
b = true;
}

let _ = write!(self.output, "\"{key}\":");
self.print_property(object, key, activation);
}

self.output.push('}');
}

pub fn print_activation<'gc>(&mut self, activation: &mut Activation<'_, 'gc>) {
self.print_variables(
"_global",
&activation.context.avm1.global_object(),
activation,
);
for display_object in activation.context.stage.iter_render_list() {
let level = display_object.depth();
let object = display_object.object().coerce_to_object(activation);
self.print_variables(&format!("_level{level}"), &object, activation);
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
21 changes: 21 additions & 0 deletions core/src/avm1/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use gc_arena::{Collect, Gc, Mutation};
use std::borrow::Cow;
use swf::avm1::read::Reader;
use tracing::instrument;
use web_sys::console;

#[derive(Collect)]
#[collect(no_drop)]
Expand Down Expand Up @@ -87,6 +88,10 @@ pub struct Avm1<'gc> {

#[cfg(feature = "avm_debug")]
pub debug_output: bool,

pub output_json: i8,
pub output_json_stdin: bool,
pub output_json_code: u8,
}

impl<'gc> Avm1<'gc> {
Expand Down Expand Up @@ -118,6 +123,10 @@ impl<'gc> Avm1<'gc> {
#[cfg(feature = "avm_debug")]
debug_output: false,
use_new_invalid_bounds_value: false,

output_json: -1,
output_json_stdin: false,
output_json_code: 0xFF,
}
}

Expand Down Expand Up @@ -350,6 +359,18 @@ impl<'gc> Avm1<'gc> {
});

avm_debug!(self, "Stack pop {}: {value:?}", self.stack.len());
if self.output_json == 1 {
match value {
Value::Bool(b) => {
if self.output_json_stdin {
println!("{}", b);
} else {
console::log_1(&b.into());
}
}
_ => (),
}
}

value
}
Expand Down
12 changes: 12 additions & 0 deletions core/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,18 @@ impl Player {
}
}

pub fn avm_output_json(&mut self, switch: i8) {
self.mutate_with_update_context(|context| {
context.avm1.output_json = switch;
});
}

pub fn avm_output_json_code(&mut self, opcode: u8) {
self.mutate_with_update_context(|context| {
context.avm1.output_json_code = opcode;
});
}

/// Updates the hover state of buttons.
fn update_mouse_state(&mut self, is_mouse_button_changed: bool, is_mouse_moved: bool) -> bool {
let mut new_cursor = self.mouse_cursor;
Expand Down
3 changes: 3 additions & 0 deletions desktop/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ pub struct Opt {
value_name = "GAMEPAD BUTTON>=<KEY NAME"
)]
pub gamepad_button: Vec<(GamepadButton, KeyCode)>,

#[clap(long, action)]
pub avm_output_json: bool,
}

fn parse_movie_file_or_url(path: &str) -> Result<Url, Error> {
Expand Down
9 changes: 9 additions & 0 deletions desktop/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub struct PlayerOptions {
pub open_url_mode: OpenURLMode,
pub dummy_external_interface: bool,
pub gamepad_button_mapping: HashMap<GamepadButton, KeyCode>,
pub avm_output_json: bool,
}

impl From<&GlobalPreferences> for PlayerOptions {
Expand Down Expand Up @@ -85,6 +86,7 @@ impl From<&GlobalPreferences> for PlayerOptions {
socket_allowed: HashSet::from_iter(value.cli.socket_allow.iter().cloned()),
tcp_connections: value.cli.tcp_connections,
gamepad_button_mapping: HashMap::from_iter(value.cli.gamepad_button.iter().cloned()),
avm_output_json: value.cli.avm_output_json,
}
}
}
Expand Down Expand Up @@ -269,6 +271,13 @@ impl ActivePlayer {
"Arial Unicode MS".into(), // Mac fallback
],
);

player_lock.mutate_with_update_context(|context| {
if opt.avm_output_json {
context.avm1.output_json = 1;
context.avm1.output_json_stdin = true;
}
});
}

Self { player, executor }
Expand Down
Loading

0 comments on commit 7d67fa2

Please sign in to comment.