예제 #1
0
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;
}