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())
|
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.
|
/// Implements `Boolean`'s class initializer.
|
||||||
pub fn class_init<'gc>(
|
pub fn class_init<'gc>(
|
||||||
_activation: &mut Activation<'_, '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);
|
let mut write = class.write(mc);
|
||||||
write.set_instance_deriver(primitive_deriver);
|
write.set_instance_deriver(primitive_deriver);
|
||||||
|
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||||
|
|
||||||
class
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,19 @@ pub fn instance_init<'gc>(
|
||||||
Err("int constructor is a stub.".into())
|
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.
|
/// Implements `int`'s class initializer.
|
||||||
pub fn class_init<'gc>(
|
pub fn class_init<'gc>(
|
||||||
_activation: &mut Activation<'_, '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);
|
let mut write = class.write(mc);
|
||||||
write.set_instance_deriver(primitive_deriver);
|
write.set_instance_deriver(primitive_deriver);
|
||||||
|
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||||
|
|
||||||
class
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,19 @@ pub fn instance_init<'gc>(
|
||||||
Err("Number constructor is a stub.".into())
|
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.
|
/// Implements `Number`'s class initializer.
|
||||||
pub fn class_init<'gc>(
|
pub fn class_init<'gc>(
|
||||||
_activation: &mut Activation<'_, '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);
|
let mut write = class.write(mc);
|
||||||
write.set_instance_deriver(primitive_deriver);
|
write.set_instance_deriver(primitive_deriver);
|
||||||
|
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||||
|
|
||||||
class
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub fn instance_init<'gc>(
|
||||||
activation.super_init(this, &[])?;
|
activation.super_init(this, &[])?;
|
||||||
|
|
||||||
if let Some(mut value) = this.as_primitive_mut(activation.context.gc_context) {
|
if let Some(mut value) = this.as_primitive_mut(activation.context.gc_context) {
|
||||||
|
if !matches!(*value, Value::String(_)) {
|
||||||
*value = args
|
*value = args
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::String("".into()))
|
.unwrap_or(&Value::String("".into()))
|
||||||
|
@ -28,6 +29,7 @@ pub fn instance_init<'gc>(
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,19 @@ pub fn instance_init<'gc>(
|
||||||
Err("uint constructor is a stub.".into())
|
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.
|
/// Implements `uint`'s class initializer.
|
||||||
pub fn class_init<'gc>(
|
pub fn class_init<'gc>(
|
||||||
_activation: &mut Activation<'_, '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);
|
let mut write = class.write(mc);
|
||||||
write.set_instance_deriver(primitive_deriver);
|
write.set_instance_deriver(primitive_deriver);
|
||||||
|
write.set_native_instance_init(Method::from_builtin(native_instance_init));
|
||||||
|
|
||||||
class
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,16 @@ pub fn primitive_deriver<'gc>(
|
||||||
proto: Object<'gc>,
|
proto: Object<'gc>,
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
) -> Result<Object<'gc>, Error> {
|
) -> 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.
|
/// An Object which represents a primitive value of some other kind.
|
||||||
|
@ -41,43 +50,51 @@ pub struct PrimitiveObjectData<'gc> {
|
||||||
|
|
||||||
impl<'gc> PrimitiveObject<'gc> {
|
impl<'gc> PrimitiveObject<'gc> {
|
||||||
/// Box a primitive into an object.
|
/// 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(
|
pub fn from_primitive(
|
||||||
primitive: Value<'gc>,
|
primitive: Value<'gc>,
|
||||||
constr: Object<'gc>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
base_proto: Object<'gc>,
|
|
||||||
mc: MutationContext<'gc, '_>,
|
|
||||||
) -> Result<Object<'gc>, Error> {
|
) -> Result<Object<'gc>, Error> {
|
||||||
if !primitive.is_primitive() {
|
if !primitive.is_primitive() {
|
||||||
return Err("Attempted to box an object as a primitive".into());
|
return Err("Attempted to box an object as a primitive".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let base =
|
if matches!(primitive, Value::Undefined) {
|
||||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
return Err("Cannot box an undefined value".into());
|
||||||
|
} else if matches!(primitive, Value::Null) {
|
||||||
Ok(PrimitiveObject(GcCell::allocate(
|
return Err("Cannot box a null value".into());
|
||||||
mc,
|
|
||||||
PrimitiveObjectData { base, primitive },
|
|
||||||
))
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a primitive subclass.
|
let proto = match primitive {
|
||||||
pub fn derive(
|
Value::Bool(_) => activation.avm2().prototypes().boolean,
|
||||||
constr: Object<'gc>,
|
Value::Number(_) => activation.avm2().prototypes().number,
|
||||||
base_proto: Object<'gc>,
|
Value::Unsigned(_) => activation.avm2().prototypes().uint,
|
||||||
mc: MutationContext<'gc, '_>,
|
Value::Integer(_) => activation.avm2().prototypes().int,
|
||||||
) -> Result<Object<'gc>, Error> {
|
Value::String(_) => activation.avm2().prototypes().string,
|
||||||
let base =
|
_ => unreachable!(),
|
||||||
ScriptObjectData::base_new(Some(base_proto), ScriptObjectClass::ClassInstance(constr));
|
};
|
||||||
|
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(
|
let base =
|
||||||
mc,
|
ScriptObjectData::base_new(Some(proto), ScriptObjectClass::ClassInstance(constr));
|
||||||
PrimitiveObjectData {
|
let this = PrimitiveObject(GcCell::allocate(
|
||||||
base,
|
activation.context.gc_context,
|
||||||
primitive: Value::Undefined,
|
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 {
|
PrimitiveObject::from_primitive(self.clone(), activation)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine if two values are abstractly equal to each other.
|
/// Determine if two values are abstractly equal to each other.
|
||||||
|
|
Loading…
Reference in New Issue