diff --git a/src/Object.cc b/src/Object.cc index 7b1fe84..dc53781 100644 --- a/src/Object.cc +++ b/src/Object.cc @@ -33,6 +33,10 @@ namespace archetype { return attributes_.count(attribute_id) > 0 or (p and p->hasAttribute(attribute_id)); } + bool Object::hasLocalAttribute(int attribute_id) const { + return attributes_.count(attribute_id) > 0; + } + Value Object::getAttributeValue(int attribute_id) const { auto where = attributes_.find(attribute_id); if (where != attributes_.end()) { diff --git a/src/Object.hh b/src/Object.hh index 194754e..0be1ec9 100644 --- a/src/Object.hh +++ b/src/Object.hh @@ -62,6 +62,7 @@ namespace archetype { ObjectPtr parent() const; bool hasAttribute(int attribute_id) const; + bool hasLocalAttribute(int attribute_id) const; Value getAttributeValue(int attribute_id) const; void setAttribute(int attribute_id, Expression expr); void setAttribute(int attribute_id, Value val); diff --git a/src/TestObject.cc b/src/TestObject.cc index f98330f..e731de1 100644 --- a/src/TestObject.cc +++ b/src/TestObject.cc @@ -166,10 +166,47 @@ namespace archetype { ARCHETYPE_TEST_EQUAL(actual2, expected2); } + void TestObject::testReadDoesNotMutate_() { + ObjectPtr subject = Universe::instance().defineNewObject(); + Universe::instance().assignObjectIdentifier(subject, "subject"); + int ghost_id = Universe::instance().Identifiers.index("ghost"); + + ARCHETYPE_TEST(not subject->hasAttribute(ghost_id)); + + Expression expr = make_expr_from_str("subject.ghost"); + Value val = expr->evaluate()->valueConversion(); + ARCHETYPE_TEST(not val->isDefined()); + + ARCHETYPE_TEST(not subject->hasAttribute(ghost_id)); + + expr->evaluate(); + expr->evaluate(); + ARCHETYPE_TEST(not subject->hasAttribute(ghost_id)); + + ObjectPtr animal = Universe::instance().defineNewObject(); + animal->setPrototype(true); + Universe::instance().assignObjectIdentifier(animal, "animal"); + int legs_id = Universe::instance().Identifiers.index("legs"); + animal->setAttribute(legs_id, Value(new NumericValue(4))); + + ObjectPtr dog = Universe::instance().defineNewObject(animal->id()); + Universe::instance().assignObjectIdentifier(dog, "dog"); + + ARCHETYPE_TEST(dog->hasAttribute(legs_id)); + ARCHETYPE_TEST(not dog->hasLocalAttribute(legs_id)); + + Expression inherited = make_expr_from_str("dog.legs"); + Value inherited_val = inherited->evaluate()->numericConversion(); + ARCHETYPE_TEST_EQUAL(inherited_val->getNumber(), 4); + + ARCHETYPE_TEST(not dog->hasLocalAttribute(legs_id)); + } + void TestObject::runTests_() { testObjects_(); testInheritance_(); testMethods_(); testMessagePassing_(); + testReadDoesNotMutate_(); } } diff --git a/src/TestObject.hh b/src/TestObject.hh index 288570c..2e373a0 100644 --- a/src/TestObject.hh +++ b/src/TestObject.hh @@ -19,6 +19,7 @@ namespace archetype { void testInheritance_(); void testMethods_(); void testMessagePassing_(); + void testReadDoesNotMutate_(); protected: virtual void runTests_() override; public: diff --git a/src/Value.cc b/src/Value.cc index 64ea8b2..fe9b250 100644 --- a/src/Value.cc +++ b/src/Value.cc @@ -350,14 +350,10 @@ namespace archetype { Value AttributeValue::dereference_() const { ObjectPtr obj = Universe::instance().getObject(objectId_); - if (not obj) { + if (not obj or not obj->hasAttribute(attributeId_)) { return Value{new UndefinedValue}; } - if (not obj->hasAttribute(attributeId_)) { - obj->setAttribute(attributeId_, Value{new UndefinedValue}); - } - ContextScope c; c->selfObject = obj; return obj->getAttributeValue(attributeId_);