TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false); if (!m_property->is(Expression::KindOfScalarExpression)) { m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(getOriginalClass(), false, true); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); const string &name = exp->getLiteralString(); if (name.empty()) { m_property->inferAndCheck(ar, Type::String, false); if (m_context & (LValue | RefValue)) { ar->forceClassVariants(getOriginalClass(), false, true); } return Type::Variant; // we have to use a variant to hold dynamic value } m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(shared_from_this(), objectType->getName()); } else { if ((m_context & LValue) && objectType && !objectType->is(Type::KindOfObject) && !objectType->is(Type::KindOfVariant) && !objectType->is(Type::KindOfSome) && !objectType->is(Type::KindOfAny)) { m_object->inferAndCheck(ar, Type::Object, true); } } if (!cls) { if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) { ar->forceClassVariants(name, getOriginalClass(), false, true); } return Type::Variant; } // resolved to this class if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = m_object->getOriginalFunction(); if (!func || func->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } assert(cls); if (!m_propSym || cls != m_objectClass.lock()) { m_objectClass = cls; ClassScopePtr parent; m_propSym = cls->findProperty(parent, name, ar); if (m_propSym) { if (!parent) { parent = cls; } m_symOwner = parent; always_assert(m_propSym->isPresent()); m_propSymValid = (!m_propSym->isPrivate() || getOriginalClass() == parent) && !m_propSym->isStatic(); if (m_propSymValid) { m_symOwner->addUse(getScope(), BlockScope::GetNonStaticRefUseKind( m_propSym->getHash())); } } } TypePtr ret; if (m_propSymValid && (!cls->derivesFromRedeclaring() || m_propSym->isPrivate())) { always_assert(m_symOwner); TypePtr t(m_propSym->getType()); if (t && t->is(Type::KindOfVariant)) { // only check property if we could possibly do some work ret = t; } else { if (coerce && type->is(Type::KindOfAutoSequence) && (!t || t->is(Type::KindOfVoid) || t->is(Type::KindOfSome) || t->is(Type::KindOfArray))) { type = Type::Array; } assert(getScope()->is(BlockScope::FunctionScope)); GET_LOCK(m_symOwner); ret = m_symOwner->checkProperty(getScope(), m_propSym, type, coerce, ar); } always_assert(m_object->getActualType() && m_object->getActualType()->isSpecificObject()); m_valid = true; return ret; } else { m_actualType = Type::Variant; return m_actualType; } }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false); if (!m_property->is(Expression::KindOfScalarExpression)) { m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(getOriginalClass(), false); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); string name = exp->getString(); ASSERT(!name.empty()); m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(shared_from_this(), objectType->getName()); } else { if ((m_context & LValue) && objectType && !objectType->is(Type::KindOfObject) && !objectType->is(Type::KindOfVariant) && !objectType->is(Type::KindOfSome) && !objectType->is(Type::KindOfAny)) { m_object->inferAndCheck(ar, Type::Object, true); } } if (!cls) { if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) { ar->forceClassVariants(name, getOriginalClass(), false); } return Type::Variant; } int prop = hasContext(AssignmentLHS) ? ClassScope::MayHaveUnknownPropSetter : hasContext(ExistContext) ? ClassScope::MayHaveUnknownPropTester : hasContext(UnsetContext) && hasContext(LValue) ? ClassScope::MayHavePropUnsetter : ClassScope::MayHaveUnknownPropGetter; if ((m_context & (AssignmentLHS|OprLValue)) || !cls->implementsAccessor(prop)) { clearEffect(AccessorEffect); } // resolved to this class if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = m_object->getOriginalFunction(); if (!func || func->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } if (!m_propSym || cls != m_objectClass.lock()) { m_objectClass = cls; ClassScopePtr parent; m_propSym = cls->findProperty(parent, name, ar, self); assert(m_propSym); if (!parent) { parent = cls; } m_propSymValid = m_propSym->isPresent() && (!m_propSym->isPrivate() || getOriginalClass() == parent) && !m_propSym->isStatic(); if (m_propSymValid) { parent->addUse(getScope(), BlockScope::UseKindNonStaticRef); } } TypePtr ret; if (m_propSymValid && (!cls->derivesFromRedeclaring() || m_propSym->isPrivate())) { ret = cls->checkProperty(m_propSym, type, coerce, ar); assert(m_object->getType()->isSpecificObject()); m_valid = true; clearEffect(AccessorEffect); clearEffect(CreateEffect); return ret; } else { m_actualType = Type::Variant; return m_actualType; } }