TypePtr VariableTable::checkProperty(BlockScopeRawPtr context, Symbol *sym, TypePtr type, bool coerce, AnalysisResultConstPtr ar) { always_assert(sym->isPresent()); if (sym->isOverride()) { Symbol *base; ClassScopePtr parent = findParent(ar, sym->getName(), base); assert(parent); assert(parent.get() != &m_blockScope); assert(base && !base->isPrivate()); if (context->is(BlockScope::FunctionScope)) { GET_LOCK(parent); type = parent->getVariables()->setType(ar, base, type, coerce); } else { TRY_LOCK(parent); type = parent->getVariables()->setType(ar, base, type, coerce); } } return setType(ar, sym, type, coerce); }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), true); if (!m_property->is(Expression::KindOfScalarExpression)) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicProperty, self); } 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(); } 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()); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findClass(objectType->getName()); ASSERT(cls); } else { // what ->property has told us cls = ar->findClass(name, AnalysisResult::PropertyName); if (!cls) { if (m_context & (LValue | RefValue)) { ar->forceClassVariants(name); } return Type::Variant; } m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()), true); } const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" : hasContext(ExistContext) ? "__isset" : hasContext(UnsetContext) ? "__unset" : "__get"; if (!cls->implementsAccessor(ar, accessorName)) { if (m_localEffects & AccessorEffect) { recomputeEffects(); } m_localEffects &= ~AccessorEffect; } // resolved to this class int present = 0; if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = ar->getFunctionScope(); if (func->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } TypePtr ret = cls->checkProperty(name, type, coerce, ar, self, present); if (!cls->derivesFromRedeclaring()) { // Have to use dynamic. // Private only valid if in the defining class if (present && (ar->getClassScope().get() == cls.get() || !(present & VariableTable::VariablePrivate))) { m_valid = true; m_static = present & VariableTable::VariableStatic; if (m_static) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_class = cls; } } // get() will return Variant if (!m_valid || !m_object->getType()->isSpecificObject()) { m_actualType = Type::Variant; return m_actualType; } if (m_localEffects & AccessorEffect) { recomputeEffects(); m_localEffects &= ~AccessorEffect; } if (ar->getPhase() == AnalysisResult::LastInference) { if (!(m_context & ObjectContext)) { m_object->clearContext(Expression::LValue); } setContext(Expression::NoLValueWrapper); } return ret; }