avm2: `PrimitiveObject::from_primitive` should now select it's own class and construct it.
This also includes protections on `String` to prevent it from overwriting itself. All user-constructed primitive objects start out boxing `Value::Undefined` and future constructor implementations should check what's already been boxed before overwriting it with user arguments.
This commit is contained in:
parent
261cb40a22
commit
c167912f52
|
@ -18,6 +18,19 @@ pub fn instance_init<'gc>(
|
|||
Err("Boolean constructor is a stub.".into())
|
||||
}
|
||||
|
||||
/// Implements `Boolean`'s native instance initializer.
|
||||
pub fn native_instance_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
activation.super_init(this, args)?;
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implements `Boolean`'s class initializer.
|
||||
pub fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
|
@ -39,6 +52,7 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
|
||||
let mut write = class.write(mc);
|
||||
write.set_instance_deriver(primitive_deriver);
|
||||
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -18,6 +18,19 @@ pub fn instance_init<'gc>(
|
|||
Err("int constructor is a stub.".into())
|
||||
}
|
||||
|
||||
/// Implements `int`'s native instance initializer.
|
||||
pub fn native_instance_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
activation.super_init(this, args)?;
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implements `int`'s class initializer.
|
||||
pub fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
|
@ -39,6 +52,7 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
|
||||
let mut write = class.write(mc);
|
||||
write.set_instance_deriver(primitive_deriver);
|
||||
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -18,6 +18,19 @@ pub fn instance_init<'gc>(
|
|||
Err("Number constructor is a stub.".into())
|
||||
}
|
||||
|
||||
/// Implements `Number`'s native instance initializer.
|
||||
pub fn native_instance_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
activation.super_init(this, args)?;
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implements `Number`'s class initializer.
|
||||
pub fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
|
@ -39,6 +52,7 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
|
||||
let mut write = class.write(mc);
|
||||
write.set_instance_deriver(primitive_deriver);
|
||||
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ pub fn instance_init<'gc>(
|
|||
activation.super_init(this, &[])?;
|
||||
|
||||
if let Some(mut value) = this.as_primitive_mut(activation.context.gc_context) {
|
||||
if !matches!(*value, Value::String(_)) {
|
||||
*value = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::String("".into()))
|
||||
|
@ -28,6 +29,7 @@ pub fn instance_init<'gc>(
|
|||
.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,19 @@ pub fn instance_init<'gc>(
|
|||
Err("uint constructor is a stub.".into())
|
||||
}
|
||||
|
||||
/// Implements `uint`'s native instance initializer.
|
||||
pub fn native_instance_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = this {
|
||||
activation.super_init(this, args)?;
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implements `uint`'s class initializer.
|
||||
pub fn class_init<'gc>(
|
||||
_activation: &mut Activation<'_, 'gc, '_>,
|
||||
|
@ -39,6 +52,7 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
|
||||
let mut write = class.write(mc);
|
||||
write.set_instance_deriver(primitive_deriver);
|
||||
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -21,7 +21,16 @@ pub fn primitive_deriver<'gc>(
|
|||
proto: Object<'gc>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
PrimitiveObject::derive(constr, proto, activation.context.gc_context)
|
||||
let base = ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(PrimitiveObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
PrimitiveObjectData {
|
||||
base,
|
||||
primitive: Value::Undefined,
|
||||
},
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
/// An Object which represents a primitive value of some other kind.
|
||||
|
@ -41,43 +50,51 @@ pub struct PrimitiveObjectData<'gc> {
|
|||
|
||||
impl<'gc> PrimitiveObject<'gc> {
|
||||
/// Box a primitive into an object.
|
||||
///
|
||||
/// This function will yield an error if `primitive` is `Undefined`, `Null`,
|
||||
/// or an object already.
|
||||
pub fn from_primitive(
|
||||
primitive: Value<'gc>,
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
if !primitive.is_primitive() {
|
||||
return Err("Attempted to box an object as a primitive".into());
|
||||
}
|
||||
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
|
||||
Ok(PrimitiveObject(GcCell::allocate(
|
||||
mc,
|
||||
PrimitiveObjectData { base, primitive },
|
||||
))
|
||||
.into())
|
||||
if matches!(primitive, Value::Undefined) {
|
||||
return Err("Cannot box an undefined value".into());
|
||||
} else if matches!(primitive, Value::Null) {
|
||||
return Err("Cannot box a null value".into());
|
||||
}
|
||||
|
||||
/// Construct a primitive subclass.
|
||||
pub fn derive(
|
||||
constr: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
mc: MutationContext<'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
||||
let proto = match primitive {
|
||||
Value::Bool(_) => activation.avm2().prototypes().boolean,
|
||||
Value::Number(_) => activation.avm2().prototypes().number,
|
||||
Value::Unsigned(_) => activation.avm2().prototypes().uint,
|
||||
Value::Integer(_) => activation.avm2().prototypes().int,
|
||||
Value::String(_) => activation.avm2().prototypes().string,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let constr = match primitive {
|
||||
Value::Bool(_) => activation.avm2().constructors().boolean,
|
||||
Value::Number(_) => activation.avm2().constructors().number,
|
||||
Value::Unsigned(_) => activation.avm2().constructors().uint,
|
||||
Value::Integer(_) => activation.avm2().constructors().int,
|
||||
Value::String(_) => activation.avm2().constructors().string,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(PrimitiveObject(GcCell::allocate(
|
||||
mc,
|
||||
PrimitiveObjectData {
|
||||
base,
|
||||
primitive: Value::Undefined,
|
||||
},
|
||||
let base =
|
||||
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||
let this = PrimitiveObject(GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
PrimitiveObjectData { base, primitive },
|
||||
))
|
||||
.into())
|
||||
.into();
|
||||
|
||||
constr.call_native_initializer(Some(this), &[], activation, Some(constr))?;
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -565,24 +565,7 @@ impl<'gc> Value<'gc> {
|
|||
_ => {}
|
||||
};
|
||||
|
||||
let proto = match self {
|
||||
Value::Bool(_) => activation.avm2().prototypes().boolean,
|
||||
Value::Number(_) => activation.avm2().prototypes().number,
|
||||
Value::Unsigned(_) => activation.avm2().prototypes().uint,
|
||||
Value::Integer(_) => activation.avm2().prototypes().int,
|
||||
Value::String(_) => activation.avm2().prototypes().string,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let constr = match self {
|
||||
Value::Bool(_) => activation.avm2().constructors().boolean,
|
||||
Value::Number(_) => activation.avm2().constructors().number,
|
||||
Value::Unsigned(_) => activation.avm2().constructors().uint,
|
||||
Value::Integer(_) => activation.avm2().constructors().int,
|
||||
Value::String(_) => activation.avm2().constructors().string,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
PrimitiveObject::from_primitive(self.clone(), constr, proto, activation.context.gc_context)
|
||||
PrimitiveObject::from_primitive(self.clone(), activation)
|
||||
}
|
||||
|
||||
/// Determine if two values are abstractly equal to each other.
|
||||
|
|
Loading…
Reference in New Issue