Allow levels to be read as scope variables, and add a test for this.
This commit is contained in:
parent
5a7e530c91
commit
9adf0f43d7
|
@ -1450,13 +1450,17 @@ impl<'gc> Avm1<'gc> {
|
||||||
|
|
||||||
//Fun fact: This isn't in the Adobe SWF19 spec, but this opcode returns
|
//Fun fact: This isn't in the Adobe SWF19 spec, but this opcode returns
|
||||||
//a boolean based on if the delete actually deleted something.
|
//a boolean based on if the delete actually deleted something.
|
||||||
let did_exist = self.current_stack_frame().unwrap().read().is_defined(name);
|
let did_exist = self
|
||||||
|
.current_stack_frame()
|
||||||
self.current_stack_frame()
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read()
|
.read()
|
||||||
.scope()
|
.is_defined(context, name);
|
||||||
.delete(name, context.gc_context);
|
|
||||||
|
self.current_stack_frame().unwrap().read().scope().delete(
|
||||||
|
context,
|
||||||
|
name,
|
||||||
|
context.gc_context,
|
||||||
|
);
|
||||||
self.push(did_exist);
|
self.push(did_exist);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -331,7 +331,7 @@ impl<'gc> Activation<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a particular property in the scope chain is defined.
|
/// Check if a particular property in the scope chain is defined.
|
||||||
pub fn is_defined(&self, name: &str) -> bool {
|
pub fn is_defined(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
if name == "this" {
|
if name == "this" {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -340,7 +340,7 @@ impl<'gc> Activation<'gc> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scope().is_defined(name)
|
self.scope().is_defined(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a named local variable within this activation.
|
/// Define a named local variable within this activation.
|
||||||
|
|
|
@ -544,12 +544,12 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
.add_property(gc_context, name, get, set, attributes)
|
.add_property(gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base.has_property(name)
|
self.base.has_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base.has_own_property(name)
|
self.base.has_own_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, name: &str) -> bool {
|
fn is_property_overwritable(&self, name: &str) -> bool {
|
||||||
|
|
|
@ -66,12 +66,12 @@ pub fn add_property<'gc>(
|
||||||
/// Implements `Object.prototype.hasOwnProperty`
|
/// Implements `Object.prototype.hasOwnProperty`
|
||||||
pub fn has_own_property<'gc>(
|
pub fn has_own_property<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
_avm: &mut Avm1<'gc>,
|
||||||
_action_context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
match args.get(0) {
|
match args.get(0) {
|
||||||
Some(Value::String(name)) => Ok(Value::Bool(this.has_own_property(name)).into()),
|
Some(Value::String(name)) => Ok(Value::Bool(this.has_own_property(context, name)).into()),
|
||||||
_ => Ok(Value::Bool(false).into()),
|
_ => Ok(Value::Bool(false).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
avm: &mut Avm1<'gc>,
|
avm: &mut Avm1<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
if self.has_own_property(name) {
|
if self.has_own_property(context, name) {
|
||||||
self.get_local(name, avm, context, (*self).into())
|
self.get_local(name, avm, context, (*self).into())
|
||||||
} else {
|
} else {
|
||||||
search_prototype(self.proto(), name, avm, context, (*self).into())
|
search_prototype(self.proto(), name, avm, context, (*self).into())
|
||||||
|
@ -172,11 +172,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Checks if the object has a given named property.
|
/// Checks if the object has a given named property.
|
||||||
fn has_property(&self, name: &str) -> bool;
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool;
|
||||||
|
|
||||||
/// Checks if the object has a given named property on itself (and not,
|
/// Checks if the object has a given named property on itself (and not,
|
||||||
/// say, the object's prototype or superclass)
|
/// say, the object's prototype or superclass)
|
||||||
fn has_own_property(&self, name: &str) -> bool;
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool;
|
||||||
|
|
||||||
/// Checks if a named property can be overwritten.
|
/// Checks if a named property can be overwritten.
|
||||||
fn is_property_overwritable(&self, name: &str) -> bool;
|
fn is_property_overwritable(&self, name: &str) -> bool;
|
||||||
|
@ -362,7 +362,7 @@ pub fn search_prototype<'gc>(
|
||||||
return Err("Encountered an excessively deep prototype chain.".into());
|
return Err("Encountered an excessively deep prototype chain.".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if proto.unwrap().has_own_property(name) {
|
if proto.unwrap().has_own_property(context, name) {
|
||||||
return proto.unwrap().get_local(name, avm, context, this);
|
return proto.unwrap().get_local(name, avm, context, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,7 @@ impl<'gc> Scope<'gc> {
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
if self.locals().has_property(name) {
|
if self.locals().has_property(context, name) {
|
||||||
return self.locals().get(name, avm, context);
|
return self.locals().get(name, avm, context);
|
||||||
}
|
}
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
|
@ -244,13 +244,13 @@ impl<'gc> Scope<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a particular property in the scope chain is defined.
|
/// Check if a particular property in the scope chain is defined.
|
||||||
pub fn is_defined(&self, name: &str) -> bool {
|
pub fn is_defined(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
if self.locals().has_property(name) {
|
if self.locals().has_property(context, name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
return scope.is_defined(name);
|
return scope.is_defined(context, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
@ -271,7 +271,8 @@ impl<'gc> Scope<'gc> {
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if self.class == ScopeClass::Target
|
if self.class == ScopeClass::Target
|
||||||
|| (self.locals().has_property(name) && self.locals().is_property_overwritable(name))
|
|| (self.locals().has_property(context, name)
|
||||||
|
&& self.locals().is_property_overwritable(name))
|
||||||
{
|
{
|
||||||
// Value found on this object, so overwrite it.
|
// Value found on this object, so overwrite it.
|
||||||
// Or we've hit the executing movie clip, so create it here.
|
// Or we've hit the executing movie clip, so create it here.
|
||||||
|
@ -300,13 +301,18 @@ impl<'gc> Scope<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a value from scope
|
/// Delete a value from scope
|
||||||
pub fn delete(&self, name: &str, mc: MutationContext<'gc, '_>) -> bool {
|
pub fn delete(
|
||||||
if self.locals().has_property(name) {
|
&self,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
name: &str,
|
||||||
|
mc: MutationContext<'gc, '_>,
|
||||||
|
) -> bool {
|
||||||
|
if self.locals().has_property(context, name) {
|
||||||
return self.locals().delete(mc, name);
|
return self.locals().delete(mc, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scope) = self.parent() {
|
if let Some(scope) = self.parent() {
|
||||||
return scope.delete(name, mc);
|
return scope.delete(context, name, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
|
|
|
@ -379,17 +379,17 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the object has a given named property.
|
/// Checks if the object has a given named property.
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.has_own_property(name)
|
self.has_own_property(context, name)
|
||||||
|| self
|
|| self
|
||||||
.proto()
|
.proto()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |p| p.has_property(name))
|
.map_or(false, |p| p.has_property(context, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the object has a given named property on itself (and not,
|
/// Checks if the object has a given named property on itself (and not,
|
||||||
/// say, the object's prototype or superclass)
|
/// say, the object's prototype or superclass)
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, _context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
if name == "__proto__" {
|
if name == "__proto__" {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,12 +213,12 @@ impl<'gc> TObject<'gc> for SoundObject<'gc> {
|
||||||
.add_property(gc_context, name, get, set, attributes)
|
.add_property(gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base().has_property(name)
|
self.base().has_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base().has_own_property(name)
|
self.base().has_own_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, name: &str) -> bool {
|
fn is_property_overwritable(&self, name: &str) -> bool {
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
let props = avm.display_properties;
|
let props = avm.display_properties;
|
||||||
// Property search order for DisplayObjects:
|
// Property search order for DisplayObjects:
|
||||||
if self.has_own_property(name) {
|
if self.has_own_property(context, name) {
|
||||||
// 1) Actual properties on the underlying object
|
// 1) Actual properties on the underlying object
|
||||||
self.get_local(name, avm, context, (*self).into())
|
self.get_local(name, avm, context, (*self).into())
|
||||||
} else if let Some(property) = props.read().get_by_name(&name) {
|
} else if let Some(property) = props.read().get_by_name(&name) {
|
||||||
|
@ -75,8 +75,11 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
} else if let Some(child) = self.display_object.get_child_by_name(name) {
|
} else if let Some(child) = self.display_object.get_child_by_name(name) {
|
||||||
// 3) Child display objects with the given instance name
|
// 3) Child display objects with the given instance name
|
||||||
Ok(child.object().into())
|
Ok(child.object().into())
|
||||||
|
} else if let Some(layer) = self.display_object.get_layer_by_name(name, context) {
|
||||||
|
// 4) Top-level layers
|
||||||
|
Ok(layer.object().into())
|
||||||
} else {
|
} else {
|
||||||
// 4) Prototype
|
// 5) Prototype
|
||||||
crate::avm1::object::search_prototype(self.proto(), name, avm, context, (*self).into())
|
crate::avm1::object::search_prototype(self.proto(), name, avm, context, (*self).into())
|
||||||
}
|
}
|
||||||
// 4) TODO: __resolve?
|
// 4) TODO: __resolve?
|
||||||
|
@ -100,7 +103,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let props = avm.display_properties;
|
let props = avm.display_properties;
|
||||||
if self.base.has_own_property(name) {
|
if self.base.has_own_property(context, name) {
|
||||||
// 1) Actual proeprties on the underlying object
|
// 1) Actual proeprties on the underlying object
|
||||||
self.base
|
self.base
|
||||||
.internal_set(name, value, avm, context, (*self).into())
|
.internal_set(name, value, avm, context, (*self).into())
|
||||||
|
@ -175,8 +178,8 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
.add_property(gc_context, name, get, set, attributes)
|
.add_property(gc_context, name, get, set, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
if self.base.has_property(name) {
|
if self.base.has_property(context, name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,12 +187,20 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.display_object
|
||||||
|
.get_layer_by_name(name, context)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
// Note that `hasOwnProperty` does NOT return true for child display objects.
|
// Note that `hasOwnProperty` does NOT return true for child display objects.
|
||||||
self.base.has_own_property(name)
|
self.base.has_own_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, name: &str) -> bool {
|
fn is_property_enumerable(&self, name: &str) -> bool {
|
||||||
|
|
|
@ -161,12 +161,12 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
//`super` cannot have properties defined on it
|
//`super` cannot have properties defined on it
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.0.read().child.has_property(name)
|
self.0.read().child.has_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.0.read().child.has_own_property(name)
|
self.0.read().child.has_own_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_enumerable(&self, name: &str) -> bool {
|
fn is_property_enumerable(&self, name: &str) -> bool {
|
||||||
|
|
|
@ -210,12 +210,12 @@ impl<'gc> TObject<'gc> for ValueObject<'gc> {
|
||||||
self.0.read().base.proto()
|
self.0.read().base.proto()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.0.read().base.has_property(name)
|
self.0.read().base.has_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.0.read().base.has_own_property(name)
|
self.0.read().base.has_own_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, name: &str) -> bool {
|
fn is_property_overwritable(&self, name: &str) -> bool {
|
||||||
|
|
|
@ -152,11 +152,11 @@ impl<'gc> TObject<'gc> for XMLAttributesObject<'gc> {
|
||||||
self.base().proto()
|
self.base().proto()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base().has_property(name)
|
self.base().has_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, _context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.node()
|
self.node()
|
||||||
.attribute_value(&XMLName::from_str(name))
|
.attribute_value(&XMLName::from_str(name))
|
||||||
.is_some()
|
.is_some()
|
||||||
|
|
|
@ -146,12 +146,13 @@ impl<'gc> TObject<'gc> for XMLIDMapObject<'gc> {
|
||||||
self.base().proto()
|
self.base().proto()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base().has_property(name)
|
self.base().has_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.document().get_node_by_id(name).is_some() || self.base().has_own_property(name)
|
self.document().get_node_by_id(name).is_some()
|
||||||
|
|| self.base().has_own_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, name: &str) -> bool {
|
fn is_property_overwritable(&self, name: &str) -> bool {
|
||||||
|
|
|
@ -140,12 +140,12 @@ impl<'gc> TObject<'gc> for XMLObject<'gc> {
|
||||||
self.base().proto()
|
self.base().proto()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_property(&self, name: &str) -> bool {
|
fn has_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base().has_property(name)
|
self.base().has_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_own_property(&self, name: &str) -> bool {
|
fn has_own_property(&self, context: &mut UpdateContext<'_, 'gc, '_>, name: &str) -> bool {
|
||||||
self.base().has_own_property(name)
|
self.base().has_own_property(context, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_overwritable(&self, name: &str) -> bool {
|
fn is_property_overwritable(&self, name: &str) -> bool {
|
||||||
|
|
|
@ -634,6 +634,18 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
|
||||||
// TODO: Make a HashMap from name -> child?
|
// TODO: Make a HashMap from name -> child?
|
||||||
self.children().find(|child| &*child.name() == name)
|
self.children().find(|child| &*child.name() == name)
|
||||||
}
|
}
|
||||||
|
/// Get another layer by layer name.
|
||||||
|
fn get_layer_by_name(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
) -> Option<DisplayObject<'gc>> {
|
||||||
|
context
|
||||||
|
.layers
|
||||||
|
.values()
|
||||||
|
.find(|layer| &*layer.name() == name)
|
||||||
|
.copied()
|
||||||
|
}
|
||||||
fn removed(&self) -> bool;
|
fn removed(&self) -> bool;
|
||||||
fn set_removed(&mut self, context: MutationContext<'gc, '_>, value: bool);
|
fn set_removed(&mut self, context: MutationContext<'gc, '_>, value: bool);
|
||||||
|
|
||||||
|
|
|
@ -174,6 +174,7 @@ swf_tests! {
|
||||||
(loadvariables_method, "avm1/loadvariables_method", 3),
|
(loadvariables_method, "avm1/loadvariables_method", 3),
|
||||||
(xml_load, "avm1/xml_load", 1),
|
(xml_load, "avm1/xml_load", 1),
|
||||||
(cross_movie_root, "avm1/cross_movie_root", 5),
|
(cross_movie_root, "avm1/cross_movie_root", 5),
|
||||||
|
(roots_and_levels, "avm1/roots_and_levels", 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
_level0
|
||||||
|
_level0
|
||||||
|
_level0
|
||||||
|
_level0
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue