avm2: Implement AIR playerglobal versioning
This builds on our existing playerglobal versioning support to add in AIR versioning. We closely follow the avmplus implementation: * When an SWF is loaded, we chose either a FlashPlayer or AIR APIVersion for its SWF version, based on our configured player runtime. * When loading playerglobals, we look at the player runtime. In AIR mode, we map FlashPlayer-versioned definitions to the closest AIR version. This ensures that all runtime APIVersions are in the same series (either AIR or FlashPlayer). In FlashPlayer mode, all AIR-versioned definitions get mapped to VM_INTERNAL, hiding them from user code. Part of our existing api versioning code was implemented incorrectly. Within playerglobals, we need to treat all unmarked namespaces as VM_INTERNAL - this allows things like playerglobal script initializer "initproperty" opcodes to see any VM_INTERNAL AIR definitions (when we run under FlashPlayer mode). Previously, we were using AllVersions, which would result in those VM_INTERNAL definitions being hidden from other playerglobal code, which is not correct. Using this support, I've added a stub for the AIR-only 'flash.net.DatagramSocket'. I've also extended the test framework with a new 'player_options.runtime' config option, which can be set to "AIR" or "FlashPlayer" to configure the test runtime mode. I've also added two new tests: * 'air_hidden_lookup' runs under the FlashPlayer runtime, and verifies that a list of classes (currently just "DatagramSocket" are inacessible). * 'air_datagram_socket', which uses `player_options.runtime = "AIR"` to construct an instance of `flash.net.DatagramSocket`. We can extend this test once we implement more of `DatagramSocket` With this commit, we have all of the needed infrastructure to start implementing and testing AIR-only classes and methods.
This commit is contained in:
parent
375c99e601
commit
287ca8801a
|
@ -4263,6 +4263,7 @@ dependencies = [
|
|||
"egui",
|
||||
"egui_extras",
|
||||
"encoding_rs",
|
||||
"enum-map",
|
||||
"enumset",
|
||||
"flash-lso",
|
||||
"flate2",
|
||||
|
|
|
@ -58,6 +58,7 @@ flv-rs = { path = "../flv" }
|
|||
async-channel = "2.1.1"
|
||||
jpegxr = { git = "https://github.com/ruffle-rs/jpegxr", branch = "ruffle", optional = true }
|
||||
image = { version = "0.24.7", default-features = false, features = ["tiff", "dxt"] }
|
||||
enum-map = "2.7.3"
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies.futures]
|
||||
version = "0.3.29"
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::context::{GcContext, UpdateContext};
|
|||
use crate::display_object::{DisplayObject, DisplayObjectWeak, TDisplayObject};
|
||||
use crate::string::AvmString;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use crate::PlayerRuntime;
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use gc_arena::{Collect, GcCell, Mutation};
|
||||
|
@ -92,6 +93,10 @@ pub struct Avm2<'gc> {
|
|||
/// The Flash Player version we're emulating.
|
||||
player_version: u8,
|
||||
|
||||
/// The player runtime we're emulating
|
||||
#[collect(require_static)]
|
||||
pub player_runtime: PlayerRuntime,
|
||||
|
||||
/// Values currently present on the operand stack.
|
||||
stack: Vec<Value<'gc>>,
|
||||
|
||||
|
@ -180,7 +185,11 @@ pub struct Avm2<'gc> {
|
|||
|
||||
impl<'gc> Avm2<'gc> {
|
||||
/// Construct a new AVM interpreter.
|
||||
pub fn new(context: &mut GcContext<'_, 'gc>, player_version: u8) -> Self {
|
||||
pub fn new(
|
||||
context: &mut GcContext<'_, 'gc>,
|
||||
player_version: u8,
|
||||
player_runtime: PlayerRuntime,
|
||||
) -> Self {
|
||||
let playerglobals_domain = Domain::uninitialized_domain(context.gc_context, None);
|
||||
let stage_domain =
|
||||
Domain::uninitialized_domain(context.gc_context, Some(playerglobals_domain));
|
||||
|
@ -191,6 +200,7 @@ impl<'gc> Avm2<'gc> {
|
|||
|
||||
Self {
|
||||
player_version,
|
||||
player_runtime,
|
||||
stack: Vec::new(),
|
||||
scope_stack: Vec::new(),
|
||||
call_stack: GcCell::new(context.gc_context, CallStack::new()),
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use std::sync::OnceLock;
|
||||
|
||||
use crate::PlayerRuntime;
|
||||
use enum_map::{enum_map, Enum, EnumMap};
|
||||
|
||||
// Based on https://github.com/adobe/avmplus/blob/master/core/api-versions.h
|
||||
// FIXME - if we ever add in AIR emulation, then implement the 'version series' logic.
|
||||
// For now, it's fine to just compare Flash player version against air versions directly.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, FromPrimitive)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, FromPrimitive, Enum)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum ApiVersion {
|
||||
AllVersions = 0,
|
||||
|
@ -59,37 +62,142 @@ pub enum ApiVersion {
|
|||
VM_INTERNAL = 52,
|
||||
}
|
||||
|
||||
static TRANSFER_TABLE: OnceLock<EnumMap<ApiVersion, (ApiVersion, ApiVersion)>> = OnceLock::new();
|
||||
|
||||
impl ApiVersion {
|
||||
pub fn from_swf_version(val: u8) -> Option<ApiVersion> {
|
||||
match val {
|
||||
pub fn to_valid_playerglobals_version(self, runtime: PlayerRuntime) -> ApiVersion {
|
||||
// This maps an ApiVersion from our playerglobals SWF to the closest valid version,
|
||||
// based on the active runtime.
|
||||
//
|
||||
// If our runtime is AIR, then we leave ApiVersion::AIR_* unchanged, and map
|
||||
// ApiVersion::FP_* to the closest AIR version. This has the effect of exposing
|
||||
// API versioned with an AIR-specific version, and also exposing all of the normal FP
|
||||
// APIs that were included in that AIR release.
|
||||
//
|
||||
// If our runtime is FlashPlayer, then we leave ApiVersion::FP_* unchanged, and
|
||||
// map ApiVersion::AIR_* to VM_INTERNAL. This hides all AIR-specific APIs when
|
||||
// running in FlashPlayer.
|
||||
//
|
||||
// See https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/api-versions.cpp#L63
|
||||
let active_series = TRANSFER_TABLE.get_or_init(|| {
|
||||
enum_map! {
|
||||
ApiVersion::AllVersions => (ApiVersion::AllVersions, ApiVersion::AllVersions),
|
||||
ApiVersion::AIR_1_0 => (ApiVersion::AIR_1_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::FP_10_0 => (ApiVersion::AIR_1_5, ApiVersion::FP_10_0),
|
||||
ApiVersion::AIR_1_5 => (ApiVersion::AIR_1_5, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::AIR_1_5_1 => (ApiVersion::AIR_1_5_1, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::FP_10_0_32 => (ApiVersion::AIR_1_5_2, ApiVersion::FP_10_0_32),
|
||||
ApiVersion::AIR_1_5_2 => (ApiVersion::AIR_1_5_2, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::FP_10_1 => (ApiVersion::AIR_2_0, ApiVersion::FP_10_1),
|
||||
ApiVersion::AIR_2_0 => (ApiVersion::AIR_2_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::AIR_2_5 => (ApiVersion::AIR_2_5, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::FP_10_2 => (ApiVersion::AIR_2_6, ApiVersion::FP_10_2),
|
||||
ApiVersion::AIR_2_6 => (ApiVersion::AIR_2_6, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_12 => (ApiVersion::SWF_12, ApiVersion::SWF_12),
|
||||
ApiVersion::AIR_2_7 => (ApiVersion::AIR_2_7, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_13 => (ApiVersion::AIR_3_0, ApiVersion::SWF_13),
|
||||
ApiVersion::AIR_3_0 => (ApiVersion::AIR_3_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_14 => (ApiVersion::AIR_3_1, ApiVersion::SWF_14),
|
||||
ApiVersion::AIR_3_1 => (ApiVersion::AIR_3_1, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_15 => (ApiVersion::AIR_3_2, ApiVersion::SWF_15),
|
||||
ApiVersion::AIR_3_2 => (ApiVersion::AIR_3_2, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_16 => (ApiVersion::AIR_3_3, ApiVersion::SWF_16),
|
||||
ApiVersion::AIR_3_3 => (ApiVersion::AIR_3_3, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_17 => (ApiVersion::AIR_3_4, ApiVersion::SWF_17),
|
||||
ApiVersion::AIR_3_4 => (ApiVersion::AIR_3_4, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_18 => (ApiVersion::AIR_3_5, ApiVersion::SWF_18),
|
||||
ApiVersion::AIR_3_5 => (ApiVersion::AIR_3_5, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_19 => (ApiVersion::AIR_3_6, ApiVersion::SWF_19),
|
||||
ApiVersion::AIR_3_6 => (ApiVersion::AIR_3_6, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_20 => (ApiVersion::AIR_3_7, ApiVersion::SWF_20),
|
||||
ApiVersion::AIR_3_7 => (ApiVersion::AIR_3_7, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_21 => (ApiVersion::AIR_3_8, ApiVersion::SWF_21),
|
||||
ApiVersion::AIR_3_8 => (ApiVersion::AIR_3_8, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_22 => (ApiVersion::AIR_3_9, ApiVersion::SWF_22),
|
||||
ApiVersion::AIR_3_9 => (ApiVersion::AIR_3_9, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_23 => (ApiVersion::AIR_4_0, ApiVersion::SWF_23),
|
||||
ApiVersion::AIR_4_0 => (ApiVersion::AIR_4_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_24 => (ApiVersion::AIR_13_0, ApiVersion::SWF_24),
|
||||
ApiVersion::AIR_13_0 => (ApiVersion::AIR_13_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_25 => (ApiVersion::AIR_14_0, ApiVersion::SWF_25),
|
||||
ApiVersion::AIR_14_0 => (ApiVersion::AIR_14_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_26 => (ApiVersion::AIR_15_0, ApiVersion::SWF_26),
|
||||
ApiVersion::AIR_15_0 => (ApiVersion::AIR_15_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_27 => (ApiVersion::AIR_16_0, ApiVersion::SWF_27),
|
||||
ApiVersion::AIR_16_0 => (ApiVersion::AIR_16_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_28 => (ApiVersion::AIR_17_0, ApiVersion::SWF_28),
|
||||
ApiVersion::AIR_17_0 => (ApiVersion::AIR_17_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_29 => (ApiVersion::AIR_18_0, ApiVersion::SWF_29),
|
||||
ApiVersion::AIR_18_0 => (ApiVersion::AIR_18_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_30 => (ApiVersion::AIR_19_0, ApiVersion::SWF_30),
|
||||
ApiVersion::AIR_19_0 => (ApiVersion::AIR_19_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::SWF_31 => (ApiVersion::AIR_20_0, ApiVersion::SWF_31),
|
||||
ApiVersion::AIR_20_0 => (ApiVersion::AIR_20_0, ApiVersion::VM_INTERNAL),
|
||||
ApiVersion::VM_INTERNAL => (ApiVersion::VM_INTERNAL, ApiVersion::VM_INTERNAL),
|
||||
}
|
||||
})[self];
|
||||
|
||||
match runtime {
|
||||
PlayerRuntime::AIR => active_series.0,
|
||||
PlayerRuntime::FlashPlayer => active_series.1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_swf_version(val: u8, runtime: PlayerRuntime) -> Option<ApiVersion> {
|
||||
// Based on this table: https://github.com/ruffle-rs/ruffle/wiki/SWF-version-chart
|
||||
match (val, runtime) {
|
||||
// There's no specific entry for SWF 9 in avmplus,
|
||||
// so map it to the lowest entry.
|
||||
9 => Some(ApiVersion::AllVersions),
|
||||
10 => Some(ApiVersion::FP_10_1),
|
||||
11 => Some(ApiVersion::FP_10_2),
|
||||
12 => Some(ApiVersion::SWF_12),
|
||||
13 => Some(ApiVersion::SWF_13),
|
||||
14 => Some(ApiVersion::SWF_14),
|
||||
15 => Some(ApiVersion::SWF_15),
|
||||
16 => Some(ApiVersion::SWF_16),
|
||||
17 => Some(ApiVersion::SWF_17),
|
||||
18 => Some(ApiVersion::SWF_18),
|
||||
19 => Some(ApiVersion::SWF_19),
|
||||
20 => Some(ApiVersion::SWF_20),
|
||||
21 => Some(ApiVersion::SWF_21),
|
||||
22 => Some(ApiVersion::SWF_22),
|
||||
23 => Some(ApiVersion::SWF_23),
|
||||
24 => Some(ApiVersion::SWF_24),
|
||||
25 => Some(ApiVersion::SWF_25),
|
||||
26 => Some(ApiVersion::SWF_26),
|
||||
27 => Some(ApiVersion::SWF_27),
|
||||
28 => Some(ApiVersion::SWF_28),
|
||||
29 => Some(ApiVersion::SWF_29),
|
||||
30 => Some(ApiVersion::SWF_30),
|
||||
31 => Some(ApiVersion::SWF_31),
|
||||
(9, _) => Some(ApiVersion::AllVersions),
|
||||
(10, PlayerRuntime::FlashPlayer) => Some(ApiVersion::FP_10_1),
|
||||
(10, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_0),
|
||||
(11, PlayerRuntime::FlashPlayer) => Some(ApiVersion::FP_10_2),
|
||||
(11, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_6),
|
||||
(12, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_12),
|
||||
(12, PlayerRuntime::AIR) => Some(ApiVersion::AIR_2_7),
|
||||
(13, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_13),
|
||||
(13, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_0),
|
||||
(14, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_14),
|
||||
(14, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_1),
|
||||
(15, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_15),
|
||||
(15, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_2),
|
||||
(16, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_16),
|
||||
(16, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_3),
|
||||
(17, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_17),
|
||||
(17, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_4),
|
||||
(18, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_18),
|
||||
(18, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_5),
|
||||
(19, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_19),
|
||||
(19, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_6),
|
||||
(20, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_20),
|
||||
(20, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_7),
|
||||
(21, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_21),
|
||||
(21, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_8),
|
||||
(22, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_22),
|
||||
(22, PlayerRuntime::AIR) => Some(ApiVersion::AIR_3_9),
|
||||
(23, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_23),
|
||||
(23, PlayerRuntime::AIR) => Some(ApiVersion::AIR_4_0),
|
||||
(24, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_24),
|
||||
(24, PlayerRuntime::AIR) => Some(ApiVersion::AIR_13_0),
|
||||
(25, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_25),
|
||||
(25, PlayerRuntime::AIR) => Some(ApiVersion::AIR_14_0),
|
||||
(26, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_26),
|
||||
(26, PlayerRuntime::AIR) => Some(ApiVersion::AIR_15_0),
|
||||
(27, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_27),
|
||||
(27, PlayerRuntime::AIR) => Some(ApiVersion::AIR_16_0),
|
||||
(28, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_28),
|
||||
(28, PlayerRuntime::AIR) => Some(ApiVersion::AIR_17_0),
|
||||
(29, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_29),
|
||||
(29, PlayerRuntime::AIR) => Some(ApiVersion::AIR_18_0),
|
||||
(30, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_30),
|
||||
(30, PlayerRuntime::AIR) => Some(ApiVersion::AIR_19_0),
|
||||
(31, PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_31),
|
||||
(31, PlayerRuntime::AIR) => Some(ApiVersion::AIR_20_0),
|
||||
|
||||
// We haven't yet created entries from higher versions - just map them
|
||||
// to the highest non-VM_INTERNAL version.
|
||||
_ if val > 31 => Some(ApiVersion::SWF_31),
|
||||
(32.., PlayerRuntime::FlashPlayer) => Some(ApiVersion::SWF_31),
|
||||
(32.., PlayerRuntime::AIR) => Some(ApiVersion::AIR_20_0),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -702,7 +702,7 @@ fn load_playerglobal<'gc>(
|
|||
// Lookup with the highest version, so we we see all defined classes here
|
||||
let ns = Namespace::package($package, ApiVersion::VM_INTERNAL, &mut activation.borrow_gc());
|
||||
let name = QName::new(ns, $class_name);
|
||||
let class_object = activation.domain().get_defined_value(activation, name)?;
|
||||
let class_object = activation.domain().get_defined_value(activation, name).unwrap_or_else(|e| panic!("Failed to lookup {name:?}: {e:?}"));
|
||||
let class_object = class_object.as_object().unwrap().as_class_object().unwrap();
|
||||
let sc = activation.avm2().system_classes.as_mut().unwrap();
|
||||
sc.$field = class_object;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package flash.net {
|
||||
[API("668")] // AIR 2.0
|
||||
public class DatagramSocket {
|
||||
|
||||
}
|
||||
}
|
|
@ -254,6 +254,7 @@ include "flash/media/VideoStreamSettings.as"
|
|||
include "flash/external/ExternalInterface.as"
|
||||
|
||||
include "flash/net.as"
|
||||
include "flash/net/DatagramSocket.as"
|
||||
include "flash/net/FileFilter.as"
|
||||
include "flash/net/FileReference.as"
|
||||
include "flash/net/FileReferenceList.as"
|
||||
|
|
|
@ -107,9 +107,38 @@ impl<'gc> Namespace<'gc> {
|
|||
)));
|
||||
}
|
||||
|
||||
// FIXME - what other versioned urls are there?
|
||||
let is_versioned_url = |url: AvmAtom<'gc>| url.as_wstr().is_empty();
|
||||
|
||||
// FIXME - AvmCore gets this from an external source. I'm not exactly sure
|
||||
// what the contents it, but it's probably all 'flash.*', 'air.*', etc. namespaces
|
||||
// This is only ever used when parsing our playerglobals, so we just treat everything
|
||||
// as versioned for now. As a result, any intra-playerglobal *references* that lack
|
||||
// an explicit version marker will be treated as ApiVersion::VM_INTERNAL.
|
||||
// The only exceptions are the 'AS3' ("http://adobe.com/AS3/2006/builtin")
|
||||
// and "flash_proxy" (b"http://www.adobe.com/2006/actionscript/flash/proxy") namespaces.
|
||||
// These are used by user code, and are not given version markers in playerglobals
|
||||
// by the ASC compiler. As a result, we do not treat them as versioned, so that
|
||||
// references from within playerglobals will use ApiVersion::AllVersions;
|
||||
//
|
||||
// For example, consider the AIR-only class `flash.net.DatagramSocket`. The class
|
||||
// definition has version marker corresponding to an AIR-only version - when running
|
||||
// the Flash Player runtime, we will map this to VM_INTERNAL in `ApiVersion::to_valid_playerglobals_version`
|
||||
// (which hides it from user code). However, the playerglobal will still try to initialize this class via:
|
||||
//
|
||||
// ```
|
||||
// initproperty QName(PackageNamespace("flash.net"),"DatagramSocket")
|
||||
// ```
|
||||
//
|
||||
// This is a namespace without a version marker (the compiler only ever generates version
|
||||
// markers in definitions, not references). As a result, we will treat this as a VM_INTERNAL
|
||||
// which will allow `initproperty` to see the `flash.net.DatagramSocket` class definition,
|
||||
// even when running as the FlashPlayer (not AIR) runtime.
|
||||
//
|
||||
// Outside of playerglobals, we'll tag all namespaces with a version based on the SWF version.
|
||||
// This is always less than VM_INTERNAL, so AIR-only classes will be correctly hidden outside
|
||||
// of playerglobals when using the FlashPlayer runtime.
|
||||
let is_versioned_url = |url: AvmAtom<'gc>| {
|
||||
url.as_wstr() != b"http://adobe.com/AS3/2006/builtin"
|
||||
&& url.as_wstr() != b"http://www.adobe.com/2006/actionscript/flash/proxy"
|
||||
};
|
||||
let is_public = matches!(
|
||||
abc_namespace,
|
||||
AbcNamespace::Namespace(_) | AbcNamespace::Package(_)
|
||||
|
@ -137,6 +166,11 @@ impl<'gc> Namespace<'gc> {
|
|||
if !has_version_mark && is_public && is_versioned_url(namespace_name) {
|
||||
api_version = ApiVersion::VM_INTERNAL;
|
||||
}
|
||||
// In avmplus, this conversion is done later in in 'getValidApiVersion'
|
||||
// However, there's no reason to hold on to invalid API versions for the
|
||||
// current active series (player runtime), so let's just do the conversion immediately.
|
||||
api_version =
|
||||
api_version.to_valid_playerglobals_version(context.avm2.player_runtime);
|
||||
} else if is_public {
|
||||
api_version = translation_unit.api_version(context.avm2);
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::avm2::{Avm2, Error};
|
|||
use crate::context::{GcContext, UpdateContext};
|
||||
use crate::string::{AvmAtom, AvmString};
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use crate::PlayerRuntime;
|
||||
use gc_arena::{Collect, Gc, GcCell, Mutation};
|
||||
use std::cell::Ref;
|
||||
use std::mem::drop;
|
||||
|
@ -135,7 +136,10 @@ impl<'gc> TranslationUnit<'gc> {
|
|||
pub fn api_version(self, avm2: &Avm2<'gc>) -> ApiVersion {
|
||||
if self.domain().is_playerglobals_domain(avm2) {
|
||||
// FIXME: get this from the player version we're emulating
|
||||
ApiVersion::SWF_31
|
||||
match avm2.player_runtime {
|
||||
PlayerRuntime::FlashPlayer => ApiVersion::SWF_31,
|
||||
PlayerRuntime::AIR => ApiVersion::AIR_20_0,
|
||||
}
|
||||
} else {
|
||||
avm2.root_api_version
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ use ruffle_render::commands::CommandList;
|
|||
use ruffle_render::quality::StageQuality;
|
||||
use ruffle_render::transform::TransformStack;
|
||||
use ruffle_video::backend::VideoBackend;
|
||||
use serde::Deserialize;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::ops::DerefMut;
|
||||
|
@ -392,7 +393,10 @@ impl Player {
|
|||
|
||||
self.mutate_with_update_context(|context| {
|
||||
if context.swf.is_action_script_3() {
|
||||
context.avm2.root_api_version = ApiVersion::from_swf_version(context.swf.version())
|
||||
context.avm2.root_api_version = ApiVersion::from_swf_version(
|
||||
context.swf.version(),
|
||||
context.avm2.player_runtime,
|
||||
)
|
||||
.unwrap_or_else(|| panic!("Unknown SWF version {}", context.swf.version()));
|
||||
}
|
||||
|
||||
|
@ -2432,6 +2436,7 @@ impl PlayerBuilder {
|
|||
fn create_gc_root<'gc>(
|
||||
gc_context: &'gc gc_arena::Mutation<'gc>,
|
||||
player_version: u8,
|
||||
player_runtime: PlayerRuntime,
|
||||
fullscreen: bool,
|
||||
fake_movie: Arc<SwfMovie>,
|
||||
external_interface_providers: Vec<Box<dyn ExternalInterfaceProvider>>,
|
||||
|
@ -2452,7 +2457,7 @@ impl PlayerBuilder {
|
|||
audio_manager: AudioManager::new(),
|
||||
action_queue: ActionQueue::new(),
|
||||
avm1: Avm1::new(&mut init, player_version),
|
||||
avm2: Avm2::new(&mut init, player_version),
|
||||
avm2: Avm2::new(&mut init, player_version, player_runtime),
|
||||
interner,
|
||||
current_context_menu: None,
|
||||
drag_object: None,
|
||||
|
@ -2572,6 +2577,7 @@ impl PlayerBuilder {
|
|||
Self::create_gc_root(
|
||||
gc_context,
|
||||
player_version,
|
||||
self.player_runtime,
|
||||
self.fullscreen,
|
||||
fake_movie.clone(),
|
||||
self.external_interface_providers,
|
||||
|
@ -2682,7 +2688,7 @@ fn run_mouse_pick<'gc>(
|
|||
}
|
||||
|
||||
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
|
||||
pub enum PlayerRuntime {
|
||||
#[default]
|
||||
FlashPlayer,
|
||||
|
|
|
@ -166,6 +166,7 @@ impl ActivePlayer {
|
|||
.with_spoofed_url(opt.spoof_url.clone().map(|url| url.to_string()))
|
||||
.with_page_url(opt.spoof_url.clone().map(|url| url.to_string()))
|
||||
.with_player_version(Some(opt.player_version))
|
||||
.with_player_runtime(opt.player_runtime)
|
||||
.with_frame_rate(opt.frame_rate);
|
||||
let player = builder.build();
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ viewport_dimensions = { width = 100, height = 100, scale_factor = 1 } # The size
|
|||
with_renderer = { optional = false, sample_count = 4, exclude_warp = false } # If this test requires a renderer to run. Optional will enable the renderer where available.
|
||||
with_audio = false # If this test requires an audio backend to run.
|
||||
with_video = false # If this test requires a video decoder backend to run.
|
||||
runtime = "AIR" # The runtime to emulate ("FlashPlayer" or "AIR"). Defaults to "FlashPlayer"
|
||||
|
||||
# A list of image comparisons to perform during the test. This block is repeatable infinitely, as long as each name is unique.
|
||||
# The comparison part of a test is optional and only runs when `imgtests` feature is enabled
|
||||
|
|
|
@ -7,7 +7,7 @@ use approx::assert_relative_eq;
|
|||
use image::ImageOutputFormat;
|
||||
use regex::Regex;
|
||||
use ruffle_core::tag_utils::SwfMovie;
|
||||
use ruffle_core::{PlayerBuilder, ViewportDimensions};
|
||||
use ruffle_core::{PlayerBuilder, PlayerRuntime, ViewportDimensions};
|
||||
use ruffle_render::backend::RenderBackend;
|
||||
use ruffle_render::quality::StageQuality;
|
||||
use serde::Deserialize;
|
||||
|
@ -135,6 +135,7 @@ pub struct PlayerOptions {
|
|||
with_renderer: Option<RenderOptions>,
|
||||
with_audio: bool,
|
||||
with_video: bool,
|
||||
runtime: PlayerRuntime,
|
||||
}
|
||||
|
||||
impl PlayerOptions {
|
||||
|
@ -157,6 +158,8 @@ impl PlayerOptions {
|
|||
player_builder = player_builder.with_audio(TestAudioBackend::default());
|
||||
}
|
||||
|
||||
player_builder = player_builder.with_player_runtime(self.runtime);
|
||||
|
||||
#[cfg(feature = "ruffle_video_software")]
|
||||
if self.with_video {
|
||||
use ruffle_video_software::backend::SoftwareVideoBackend;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package {
|
||||
|
||||
import flash.display.MovieClip;
|
||||
|
||||
|
||||
public class Test extends MovieClip {
|
||||
|
||||
|
||||
public function Test() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
import flash.net.DatagramSocket;
|
||||
var socket = new DatagramSocket();
|
||||
// TODO - test functionally once we implement it in Ruffle
|
||||
trace("Socket: " + socket);
|
|
@ -0,0 +1 @@
|
|||
Socket: [object DatagramSocket]
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
num_ticks = 1
|
||||
|
||||
[player_options]
|
||||
runtime = "AIR"
|
|
@ -0,0 +1,26 @@
|
|||
package {
|
||||
|
||||
import flash.display.MovieClip;
|
||||
|
||||
|
||||
public class Test extends MovieClip {
|
||||
|
||||
|
||||
public function Test() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
import flash.utils.getDefinitionByName;
|
||||
|
||||
const HIDDEN_CLASSES: Array = ["flash.net.DatagramSocket"];
|
||||
|
||||
for each (var klass in HIDDEN_CLASSES) {
|
||||
try {
|
||||
var obj = getDefinitionByName(klass);
|
||||
trace("ERROR: " + klass + " is accessible from Flash Player");
|
||||
} catch (e) {
|
||||
trace(klass + " is inaccessible from Flash Player");
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
flash.net.DatagramSocket is inaccessible from Flash Player
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
num_ticks = 1
|
Loading…
Reference in New Issue