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,
|
||||
} => self.op_call_super_void(*multiname, *num_args),
|
||||
Op::ReturnValue => self.op_return_value(method),
|
||||
Op::ReturnValueNoCoerce => self.op_return_value_no_coerce(),
|
||||
Op::ReturnVoid => self.op_return_void(),
|
||||
Op::GetProperty { multiname } => self.op_get_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::GetSlot { index } => self.op_get_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::SetGlobalSlot { index } => self.op_set_global_slot(*index),
|
||||
Op::Construct { num_args } => self.op_construct(*num_args),
|
||||
|
@ -1358,6 +1360,12 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
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>> {
|
||||
Ok(FrameControl::Return(Value::Undefined))
|
||||
}
|
||||
|
@ -1777,6 +1785,15 @@ impl<'a, 'gc> Activation<'a, 'gc> {
|
|||
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>> {
|
||||
let value = self
|
||||
.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())
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// This directly corresponds with the AVM2 operation `callmethod`.
|
||||
|
|
|
@ -296,6 +296,7 @@ pub enum Op<'gc> {
|
|||
PushUndefined,
|
||||
PushWith,
|
||||
ReturnValue,
|
||||
ReturnValueNoCoerce,
|
||||
ReturnVoid,
|
||||
RShift,
|
||||
SetGlobalSlot {
|
||||
|
@ -310,6 +311,9 @@ pub enum Op<'gc> {
|
|||
SetSlot {
|
||||
index: u32,
|
||||
},
|
||||
SetSlotNoCoerce {
|
||||
index: u32,
|
||||
},
|
||||
SetSuper {
|
||||
multiname: Gc<'gc, Multiname<'gc>>,
|
||||
},
|
||||
|
|
|
@ -152,6 +152,7 @@ pub fn optimize<'gc>(
|
|||
method: &BytecodeMethod<'gc>,
|
||||
code: &mut Vec<Op<'gc>>,
|
||||
resolved_parameters: &[ResolvedParamConfig<'gc>],
|
||||
return_type: Option<GcCell<'gc, Class<'gc>>>,
|
||||
jump_targets: HashMap<i32, JumpSources>,
|
||||
) {
|
||||
// These make the code less readable
|
||||
|
@ -839,7 +840,8 @@ pub fn optimize<'gc>(
|
|||
}
|
||||
}
|
||||
Op::InitProperty { multiname } => {
|
||||
stack.pop();
|
||||
let set_value = stack.pop_or_any();
|
||||
|
||||
stack.pop_for_multiname(*multiname);
|
||||
let stack_value = stack.pop_or_any();
|
||||
if !multiname.has_lazy_component() {
|
||||
|
@ -849,6 +851,28 @@ pub fn optimize<'gc>(
|
|||
Some(Property::Slot { slot_id })
|
||||
| Some(Property::ConstSlot { 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 {
|
||||
set: Some(disp_id), ..
|
||||
|
@ -867,7 +891,8 @@ pub fn optimize<'gc>(
|
|||
// `stack_pop_multiname` handled lazy
|
||||
}
|
||||
Op::SetProperty { multiname } => {
|
||||
stack.pop();
|
||||
let set_value = stack.pop_or_any();
|
||||
|
||||
stack.pop_for_multiname(*multiname);
|
||||
let stack_value = stack.pop_or_any();
|
||||
if !multiname.has_lazy_component() {
|
||||
|
@ -876,6 +901,28 @@ pub fn optimize<'gc>(
|
|||
match class.instance_vtable().get_trait(multiname) {
|
||||
Some(Property::Slot { 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 {
|
||||
set: Some(disp_id), ..
|
||||
|
@ -940,7 +987,6 @@ pub fn optimize<'gc>(
|
|||
// Avoid checking return value for now
|
||||
stack.push_any();
|
||||
}
|
||||
Op::CallMethod { .. } => unreachable!("CallMethod cannot be emitted by verifier"),
|
||||
Op::CallPropLex {
|
||||
multiname,
|
||||
num_args,
|
||||
|
@ -1141,7 +1187,27 @@ pub fn optimize<'gc>(
|
|||
stack.pop();
|
||||
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
|
||||
stack.clear();
|
||||
scope_stack.clear();
|
||||
|
@ -1157,6 +1223,10 @@ pub fn optimize<'gc>(
|
|||
local_types = initial_local_types.clone();
|
||||
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,
|
||||
&mut verified_code,
|
||||
&resolved_param_config,
|
||||
resolved_return_type,
|
||||
potential_jump_targets,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue