avm2: Add SetSlotNoCoerce and ReturnValueNoCoerce ops for SetSlot and ReturnValue without coercion when type is guaranteed to be not coerced
This commit is contained in:
parent
daaf274245
commit
1f4eebfd8d
|
@ -933,6 +933,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
num_args,
|
num_args,
|
||||||
} => self.op_call_super_void(*multiname, *num_args),
|
} => self.op_call_super_void(*multiname, *num_args),
|
||||||
Op::ReturnValue => self.op_return_value(method),
|
Op::ReturnValue => self.op_return_value(method),
|
||||||
|
Op::ReturnValueNoCoerce => self.op_return_value_no_coerce(),
|
||||||
Op::ReturnVoid => self.op_return_void(),
|
Op::ReturnVoid => self.op_return_void(),
|
||||||
Op::GetProperty { multiname } => self.op_get_property(*multiname),
|
Op::GetProperty { multiname } => self.op_get_property(*multiname),
|
||||||
Op::SetProperty { multiname } => self.op_set_property(*multiname),
|
Op::SetProperty { multiname } => self.op_set_property(*multiname),
|
||||||
|
@ -955,6 +956,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
Op::GetDescendants { multiname } => self.op_get_descendants(*multiname),
|
Op::GetDescendants { multiname } => self.op_get_descendants(*multiname),
|
||||||
Op::GetSlot { index } => self.op_get_slot(*index),
|
Op::GetSlot { index } => self.op_get_slot(*index),
|
||||||
Op::SetSlot { index } => self.op_set_slot(*index),
|
Op::SetSlot { index } => self.op_set_slot(*index),
|
||||||
|
Op::SetSlotNoCoerce { index } => self.op_set_slot_no_coerce(*index),
|
||||||
Op::GetGlobalSlot { index } => self.op_get_global_slot(*index),
|
Op::GetGlobalSlot { index } => self.op_get_global_slot(*index),
|
||||||
Op::SetGlobalSlot { index } => self.op_set_global_slot(*index),
|
Op::SetGlobalSlot { index } => self.op_set_global_slot(*index),
|
||||||
Op::Construct { num_args } => self.op_construct(*num_args),
|
Op::Construct { num_args } => self.op_construct(*num_args),
|
||||||
|
@ -1358,6 +1360,12 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
Ok(FrameControl::Return(coerced))
|
Ok(FrameControl::Return(coerced))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn op_return_value_no_coerce(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||||
|
let return_value = self.pop_stack();
|
||||||
|
|
||||||
|
Ok(FrameControl::Return(return_value))
|
||||||
|
}
|
||||||
|
|
||||||
fn op_return_void(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
|
fn op_return_void(&mut self) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||||
Ok(FrameControl::Return(Value::Undefined))
|
Ok(FrameControl::Return(Value::Undefined))
|
||||||
}
|
}
|
||||||
|
@ -1777,6 +1785,15 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
||||||
Ok(FrameControl::Continue)
|
Ok(FrameControl::Continue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn op_set_slot_no_coerce(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||||
|
let value = self.pop_stack();
|
||||||
|
let object = self.pop_stack().coerce_to_object_or_typeerror(self, None)?;
|
||||||
|
|
||||||
|
object.set_slot_no_coerce(index, value, self.context.gc_context)?;
|
||||||
|
|
||||||
|
Ok(FrameControl::Continue)
|
||||||
|
}
|
||||||
|
|
||||||
fn op_get_global_slot(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
|
fn op_get_global_slot(&mut self, index: u32) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||||
let value = self
|
let value = self
|
||||||
.global_scope()
|
.global_scope()
|
||||||
|
|
|
@ -597,6 +597,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
base.set_slot(id, value, activation.gc())
|
base.set_slot(id, value, activation.gc())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_slot_no_coerce(
|
||||||
|
self,
|
||||||
|
id: u32,
|
||||||
|
value: Value<'gc>,
|
||||||
|
mc: &Mutation<'gc>,
|
||||||
|
) -> Result<(), Error<'gc>> {
|
||||||
|
let mut base = self.base_mut(mc);
|
||||||
|
|
||||||
|
base.set_slot(id, value, mc)
|
||||||
|
}
|
||||||
|
|
||||||
/// Call a method by its index.
|
/// Call a method by its index.
|
||||||
///
|
///
|
||||||
/// This directly corresponds with the AVM2 operation `callmethod`.
|
/// This directly corresponds with the AVM2 operation `callmethod`.
|
||||||
|
|
|
@ -296,6 +296,7 @@ pub enum Op<'gc> {
|
||||||
PushUndefined,
|
PushUndefined,
|
||||||
PushWith,
|
PushWith,
|
||||||
ReturnValue,
|
ReturnValue,
|
||||||
|
ReturnValueNoCoerce,
|
||||||
ReturnVoid,
|
ReturnVoid,
|
||||||
RShift,
|
RShift,
|
||||||
SetGlobalSlot {
|
SetGlobalSlot {
|
||||||
|
@ -310,6 +311,9 @@ pub enum Op<'gc> {
|
||||||
SetSlot {
|
SetSlot {
|
||||||
index: u32,
|
index: u32,
|
||||||
},
|
},
|
||||||
|
SetSlotNoCoerce {
|
||||||
|
index: u32,
|
||||||
|
},
|
||||||
SetSuper {
|
SetSuper {
|
||||||
multiname: Gc<'gc, Multiname<'gc>>,
|
multiname: Gc<'gc, Multiname<'gc>>,
|
||||||
},
|
},
|
||||||
|
|
|
@ -152,6 +152,7 @@ pub fn optimize<'gc>(
|
||||||
method: &BytecodeMethod<'gc>,
|
method: &BytecodeMethod<'gc>,
|
||||||
code: &mut Vec<Op<'gc>>,
|
code: &mut Vec<Op<'gc>>,
|
||||||
resolved_parameters: &[ResolvedParamConfig<'gc>],
|
resolved_parameters: &[ResolvedParamConfig<'gc>],
|
||||||
|
return_type: Option<GcCell<'gc, Class<'gc>>>,
|
||||||
jump_targets: HashMap<i32, JumpSources>,
|
jump_targets: HashMap<i32, JumpSources>,
|
||||||
) {
|
) {
|
||||||
// These make the code less readable
|
// These make the code less readable
|
||||||
|
@ -839,7 +840,8 @@ pub fn optimize<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Op::InitProperty { multiname } => {
|
Op::InitProperty { multiname } => {
|
||||||
stack.pop();
|
let set_value = stack.pop_or_any();
|
||||||
|
|
||||||
stack.pop_for_multiname(*multiname);
|
stack.pop_for_multiname(*multiname);
|
||||||
let stack_value = stack.pop_or_any();
|
let stack_value = stack.pop_or_any();
|
||||||
if !multiname.has_lazy_component() {
|
if !multiname.has_lazy_component() {
|
||||||
|
@ -849,6 +851,28 @@ pub fn optimize<'gc>(
|
||||||
Some(Property::Slot { slot_id })
|
Some(Property::Slot { slot_id })
|
||||||
| Some(Property::ConstSlot { slot_id }) => {
|
| Some(Property::ConstSlot { slot_id }) => {
|
||||||
*op = Op::SetSlot { index: slot_id };
|
*op = Op::SetSlot { index: slot_id };
|
||||||
|
|
||||||
|
// If the set value's type is the same as the type of the slot,
|
||||||
|
// a SetSlotNoCoerce can be emitted.
|
||||||
|
let mut value_class =
|
||||||
|
class.instance_vtable().slot_classes()[slot_id as usize];
|
||||||
|
let resolved_value_class = value_class.get_class(activation);
|
||||||
|
|
||||||
|
if let Ok(slot_class) = resolved_value_class {
|
||||||
|
if let Some(slot_class) = slot_class {
|
||||||
|
if let Some(set_value_class) = set_value.class {
|
||||||
|
if GcCell::ptr_eq(
|
||||||
|
set_value_class.inner_class_definition(),
|
||||||
|
slot_class,
|
||||||
|
) {
|
||||||
|
*op = Op::SetSlotNoCoerce { index: slot_id };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Slot type was Any, no coercion will be done anyways
|
||||||
|
*op = Op::SetSlotNoCoerce { index: slot_id };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(Property::Virtual {
|
Some(Property::Virtual {
|
||||||
set: Some(disp_id), ..
|
set: Some(disp_id), ..
|
||||||
|
@ -867,7 +891,8 @@ pub fn optimize<'gc>(
|
||||||
// `stack_pop_multiname` handled lazy
|
// `stack_pop_multiname` handled lazy
|
||||||
}
|
}
|
||||||
Op::SetProperty { multiname } => {
|
Op::SetProperty { multiname } => {
|
||||||
stack.pop();
|
let set_value = stack.pop_or_any();
|
||||||
|
|
||||||
stack.pop_for_multiname(*multiname);
|
stack.pop_for_multiname(*multiname);
|
||||||
let stack_value = stack.pop_or_any();
|
let stack_value = stack.pop_or_any();
|
||||||
if !multiname.has_lazy_component() {
|
if !multiname.has_lazy_component() {
|
||||||
|
@ -876,6 +901,28 @@ pub fn optimize<'gc>(
|
||||||
match class.instance_vtable().get_trait(multiname) {
|
match class.instance_vtable().get_trait(multiname) {
|
||||||
Some(Property::Slot { slot_id }) => {
|
Some(Property::Slot { slot_id }) => {
|
||||||
*op = Op::SetSlot { index: slot_id };
|
*op = Op::SetSlot { index: slot_id };
|
||||||
|
|
||||||
|
// If the set value's type is the same as the type of the slot,
|
||||||
|
// a SetSlotNoCoerce can be emitted.
|
||||||
|
let mut value_class =
|
||||||
|
class.instance_vtable().slot_classes()[slot_id as usize];
|
||||||
|
let resolved_value_class = value_class.get_class(activation);
|
||||||
|
|
||||||
|
if let Ok(slot_class) = resolved_value_class {
|
||||||
|
if let Some(slot_class) = slot_class {
|
||||||
|
if let Some(set_value_class) = set_value.class {
|
||||||
|
if GcCell::ptr_eq(
|
||||||
|
set_value_class.inner_class_definition(),
|
||||||
|
slot_class,
|
||||||
|
) {
|
||||||
|
*op = Op::SetSlotNoCoerce { index: slot_id };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Slot type was Any, no coercion will be done anyways
|
||||||
|
*op = Op::SetSlotNoCoerce { index: slot_id };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(Property::Virtual {
|
Some(Property::Virtual {
|
||||||
set: Some(disp_id), ..
|
set: Some(disp_id), ..
|
||||||
|
@ -940,7 +987,6 @@ pub fn optimize<'gc>(
|
||||||
// Avoid checking return value for now
|
// Avoid checking return value for now
|
||||||
stack.push_any();
|
stack.push_any();
|
||||||
}
|
}
|
||||||
Op::CallMethod { .. } => unreachable!("CallMethod cannot be emitted by verifier"),
|
|
||||||
Op::CallPropLex {
|
Op::CallPropLex {
|
||||||
multiname,
|
multiname,
|
||||||
num_args,
|
num_args,
|
||||||
|
@ -1141,7 +1187,27 @@ pub fn optimize<'gc>(
|
||||||
stack.pop();
|
stack.pop();
|
||||||
stack.push_class_object(types.number);
|
stack.push_class_object(types.number);
|
||||||
}
|
}
|
||||||
Op::ReturnVoid | Op::ReturnValue | Op::Throw | Op::LookupSwitch(_) => {
|
Op::ReturnVoid | Op::Throw | Op::LookupSwitch(_) => {
|
||||||
|
// End of block
|
||||||
|
stack.clear();
|
||||||
|
scope_stack.clear();
|
||||||
|
local_types = initial_local_types.clone();
|
||||||
|
last_op_was_block_terminating = true;
|
||||||
|
}
|
||||||
|
Op::ReturnValue => {
|
||||||
|
let stack_value = stack.pop_or_any();
|
||||||
|
|
||||||
|
if let Some(return_type) = return_type {
|
||||||
|
if let Some(stack_value_class) = stack_value.class {
|
||||||
|
if GcCell::ptr_eq(stack_value_class.inner_class_definition(), return_type) {
|
||||||
|
*op = Op::ReturnValueNoCoerce;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Return type was Any, no coercion will be done anyways
|
||||||
|
*op = Op::ReturnValueNoCoerce;
|
||||||
|
}
|
||||||
|
|
||||||
// End of block
|
// End of block
|
||||||
stack.clear();
|
stack.clear();
|
||||||
scope_stack.clear();
|
scope_stack.clear();
|
||||||
|
@ -1157,6 +1223,10 @@ pub fn optimize<'gc>(
|
||||||
local_types = initial_local_types.clone();
|
local_types = initial_local_types.clone();
|
||||||
last_op_was_block_terminating = true;
|
last_op_was_block_terminating = true;
|
||||||
}
|
}
|
||||||
|
other => unreachable!(
|
||||||
|
"Optimizer hit op {:?}, which cannot appear after the verifier step",
|
||||||
|
other
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -581,6 +581,7 @@ pub fn verify_method<'gc>(
|
||||||
method,
|
method,
|
||||||
&mut verified_code,
|
&mut verified_code,
|
||||||
&resolved_param_config,
|
&resolved_param_config,
|
||||||
|
resolved_return_type,
|
||||||
potential_jump_targets,
|
potential_jump_targets,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue