void ObjectPropertyExpression::outputCPPValidObject(CodeGenerator &cg, AnalysisResultPtr ar, bool guarded) { TypePtr act; bool close = false; if (!m_object->hasCPPTemp() && m_object->getImplementedType() && !Type::SameType(m_object->getImplementedType(), m_object->getActualType())) { act = m_object->getActualType(); m_object->setActualType(m_object->getImplementedType()); if (guarded) { ClassScopePtr cls = ar->findExactClass(shared_from_this(), act->getName()); cg_printf("((%s%s*)", Option::ClassPrefix, cls->getId().c_str()); close = true; } } m_object->outputCPP(cg, ar); if (act) { if (m_object->getImplementedType()->is(Type::KindOfObject)) { cg_printf(".get()"); } else { cg_printf(".getObjectData%s()", guarded ? "" : "OrNull"); } if (close) cg_printf(")"); m_object->setActualType(act); } else { cg_printf(".get()"); } }
bool ObjectPropertyExpression::outputCPPObject(CodeGenerator &cg, AnalysisResultPtr ar, bool noEvalOnError) { if (m_object->isThis()) { if (m_valid) { if (!m_object->getOriginalClass()) { m_valid = false; } else { FunctionScopeRawPtr fs = m_object->getOriginalFunction(); if (!fs || fs->isStatic()) { m_valid = false; } else if (m_object->getOriginalClass() != getClassScope() && m_object->getOriginalClass()->isRedeclaring()) { m_valid = false; } } } if (!m_valid) { cg_printf("GET_THIS_DOT()"); } } else if (m_valid) { TypePtr act; if (!m_object->hasCPPTemp() && m_object->getImplementedType() && !Type::SameType(m_object->getImplementedType(), m_object->getActualType())) { act = m_object->getActualType(); m_object->setActualType(m_object->getImplementedType()); ClassScopePtr cls = ar->findExactClass(shared_from_this(), act->getName()); cg_printf("((%s%s*)", Option::ClassPrefix, cls->getId(cg).c_str()); } m_object->outputCPP(cg, ar); if (act) { if (m_object->getImplementedType()->is(Type::KindOfObject)) { cg_printf(".get())"); } else { cg_printf(".getObjectData())"); } m_object->setActualType(act); } cg_printf("->"); } else { TypePtr t = m_object->getType(); bool ok = t && (t->is(Type::KindOfObject) || t->is(Type::KindOfVariant)); if (noEvalOnError && !ok) { if (!t || !t->is(Type::KindOfArray)) { cg_printf("("); if (m_object->outputCPPUnneeded(cg, ar)) cg_printf(", "); return true; } } ok = ok || !t; if (!ok) cg_printf("Variant("); m_object->outputCPP(cg, ar); if (!ok) cg_printf(")"); cg_printf("."); } return false; }
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), false); 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()); m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(objectType->getName()); } else { // what ->property has told us cls = ar->findClass(name, AnalysisResult::PropertyName); if (cls) { objectType = m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()), false); } 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, NEW_TYPE(Object), true); } } if (!cls) { if (m_context & (LValue | RefValue)) { ar->forceClassVariants(name); } return Type::Variant; } const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" : hasContext(ExistContext) ? "__isset" : hasContext(UnsetContext) ? "__unset" : "__get"; if (!cls->implementsAccessor(ar, accessorName)) clearEffect(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; if (!cls->derivesFromRedeclaring()) { // Have to use dynamic. ret = cls->checkProperty(name, type, coerce, ar, self, present); // Private only valid if in the defining class if (present && (getOriginalScope(ar) == cls || !(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; } clearEffect(AccessorEffect); if (ar->getPhase() == AnalysisResult::LastInference) { if (!(m_context & ObjectContext)) { m_object->clearContext(Expression::LValue); } setContext(Expression::NoLValueWrapper); } return ret; }
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 ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Object, false); m_valid = true; m_bindClass = true; if (m_name.empty()) { m_nameExp->inferAndCheck(ar, Type::String, false); setInvokeParams(ar); // we have to use a variant to hold dynamic value return checkTypesImpl(ar, type, Type::Variant, coerce); } ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { if (m_classScope && !strcasecmp(objectType->getName().c_str(), m_classScope->getName().c_str())) { cls = m_classScope; } else { cls = ar->findExactClass(shared_from_this(), objectType->getName()); } } if (!cls) { if (getScope()->isFirstPass()) { // call resolveClass to mark functions as dynamic // but we cant do anything else with the result. resolveClass(ar, m_name); if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) { Compiler::Error(Compiler::UnknownObjectMethod, self); } } m_classScope.reset(); m_funcScope.reset(); setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } if (m_classScope != cls) { m_classScope = cls; m_funcScope.reset(); } FunctionScopePtr func = m_funcScope; if (!func) { func = cls->findFunction(ar, m_name, true, true); if (!func) { if (!cls->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) { if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) { setDynamicByIdentifier(ar, m_name); } else { Compiler::Error(Compiler::UnknownObjectMethod, self); } } m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } m_funcScope = func; func->addCaller(getScope()); } bool valid = true; m_bindClass = func->isStatic(); // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr localfunc = getFunctionScope(); if (localfunc->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } valid = false; } } // invoke() will return Variant if (!m_object->getType()->isSpecificObject() || (func->isVirtual() && !func->isPerfectVirtual())) { valid = false; } if (!valid) { setInvokeParams(ar); checkTypesImpl(ar, type, Type::Variant, coerce); m_valid = false; // so we use invoke() syntax func->setDynamic(); return m_actualType; } return checkParamsAndReturn(ar, type, coerce, func, false); }
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), true); m_valid = true; m_bindClass = true; if (m_name.empty()) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicMethod, self); } m_nameExp->inferAndCheck(ar, Type::String, false); setInvokeParams(ar); // we have to use a variant to hold dynamic value return checkTypesImpl(ar, type, Type::Variant, coerce); } ClassScopePtr cls = m_classScope; if (objectType && !objectType->getName().empty()) { cls = ar->findExactClass(objectType->getName()); } if (!cls) { if (ar->isFirstPass()) { // call resolveClass to mark functions as dynamic // but we cant do anything else with the result. resolveClass(ar, m_name); if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) { ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self); } } m_classScope.reset(); m_funcScope.reset(); setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } if (m_classScope != cls) { m_classScope = cls; m_funcScope.reset(); } FunctionScopePtr func = m_funcScope; if (!func) { func = cls->findFunction(ar, m_name, true, true); if (!func) { if (!cls->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) { if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) { // TODO: we could try to find out class derivation is present... ar->getCodeError()->record(self, CodeError::DerivedObjectMethod, self); // we have to make sure the method is in invoke list setDynamicByIdentifier(ar, m_name); } else { ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self); } } m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } m_funcScope = func; } bool valid = true; m_bindClass = func->isStatic(); // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr localfunc = ar->getFunctionScope(); if (localfunc->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } valid = false; } } // invoke() will return Variant if (func->isVirtual() || !m_object->getType()->isSpecificObject()) { valid = false; } if (!valid) { setInvokeParams(ar); checkTypesImpl(ar, type, Type::Variant, coerce); m_valid = false; // so we use invoke() syntax func->setDynamic(); return m_actualType; } return checkParamsAndReturn(ar, type, coerce, func); }
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { assert(type); IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope()); resetTypes(); reset(); ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false); m_valid = true; m_bindClass = true; if (m_name.empty()) { m_nameExp->inferAndCheck(ar, Type::Some, false); setInvokeParams(ar); // we have to use a variant to hold dynamic value return checkTypesImpl(ar, type, Type::Variant, coerce); } ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { if (m_classScope && !strcasecmp(objectType->getName().c_str(), m_classScope->getName().c_str())) { cls = m_classScope; } else { cls = ar->findExactClass(shared_from_this(), objectType->getName()); } } if (!cls) { m_classScope.reset(); m_funcScope.reset(); m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } if (m_classScope != cls) { m_classScope = cls; m_funcScope.reset(); } FunctionScopePtr func = m_funcScope; if (!func) { func = cls->findFunction(ar, m_name, true, true); if (!func) { if (!cls->isTrait() && !cls->getAttribute(ClassScope::MayHaveUnknownMethodHandler) && !cls->getAttribute(ClassScope::HasUnknownMethodHandler) && !cls->getAttribute(ClassScope::InheritsUnknownMethodHandler)) { if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) { if (!Option::AllDynamic) { setDynamicByIdentifier(ar, m_name); } } else { Compiler::Error(Compiler::UnknownObjectMethod, self); } } m_valid = false; setInvokeParams(ar); return checkTypesImpl(ar, type, Type::Variant, coerce); } m_funcScope = func; func->addCaller(getScope(), !type->is(Type::KindOfAny)); } bool valid = true; m_bindClass = func->isStatic(); // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr localfunc = getFunctionScope(); if (localfunc->isStatic()) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::MissingObjectContext, self); } valid = false; } } // invoke() will return Variant if (cls->isInterface() || (func->isVirtual() && (!Option::WholeProgram || func->isAbstract() || (func->hasOverride() && cls->getAttribute(ClassScope::NotFinal))) && !func->isPerfectVirtual())) { valid = false; } if (!valid) { setInvokeParams(ar); checkTypesImpl(ar, type, Type::Variant, coerce); m_valid = false; // so we use invoke() syntax if (!Option::AllDynamic) { func->setDynamic(); } assert(m_actualType); return m_actualType; } assert(func); return checkParamsAndReturn(ar, type, coerce, func, false); }
bool ObjectPropertyExpression::outputCPPObject(CodeGenerator &cg, AnalysisResultPtr ar, bool noEvalOnError) { if (m_object->isThis()) { TypePtr thisImplType(m_object->getImplementedType()); TypePtr thisActType (m_object->getActualType()); bool close = false; if (m_valid && thisImplType) { ASSERT(thisActType); ASSERT(!Type::SameType(thisActType, thisImplType)); ClassScopePtr implCls(thisImplType->getClass(ar, getScope())); if (implCls && !implCls->derivesFrom(ar, thisActType->getName(), true, false)) { // This happens in this case: // if ($this instanceof Y) { // ... $this->prop ... // } ClassScopePtr cls(thisActType->getClass(ar, getScope())); ASSERT(cls && !cls->derivedByDynamic()); // since we don't do type // assertions for these cg_printf("static_cast<%s%s*>(", Option::ClassPrefix, cls->getId().c_str()); close = true; } } if (m_valid) { if (!m_object->getOriginalClass()) { m_valid = false; } else { FunctionScopeRawPtr fs = m_object->getOriginalFunction(); if (!fs || fs->isStatic()) { m_valid = false; } else if (m_object->getOriginalClass() != getClassScope()) { if (m_object->getOriginalClass()->isRedeclaring()) { m_valid = false; } else { m_objectClass = getClassScope(); } } } } if (m_valid) { if (close) cg_printf("this"); } else { if (!getClassScope() || getClassScope()->derivedByDynamic() || !static_pointer_cast<SimpleVariable>(m_object)->isGuardedThis()) { if (close) { cg_printf("GET_THIS_VALID()"); } else { cg_printf("GET_THIS_ARROW()"); } } } if (close) { cg_printf(")->"); } } else if (m_valid) { TypePtr act; if (!m_object->hasCPPTemp() && m_object->getImplementedType() && !Type::SameType(m_object->getImplementedType(), m_object->getActualType())) { act = m_object->getActualType(); m_object->setActualType(m_object->getImplementedType()); ClassScopePtr cls = ar->findExactClass(shared_from_this(), act->getName()); cg_printf("((%s%s*)", Option::ClassPrefix, cls->getId().c_str()); } m_object->outputCPP(cg, ar); if (act) { if (m_object->getImplementedType()->is(Type::KindOfObject)) { cg_printf(".get())"); } else { cg_printf(".getObjectData())"); } m_object->setActualType(act); } cg_printf("->"); } else { TypePtr t = m_object->getType(); bool ok = t && (t->is(Type::KindOfObject) || t->is(Type::KindOfVariant)); if (noEvalOnError && !ok) { if (!t || !t->is(Type::KindOfArray)) { cg_printf("("); if (m_object->outputCPPUnneeded(cg, ar)) cg_printf(", "); return true; } } ok = ok || !t; if (!ok) cg_printf("Variant("); m_object->outputCPP(cg, ar); if (!ok) cg_printf(")"); cg_printf("."); } if (m_valid && m_propSym->isPrivate() && m_objectClass != getOriginalClass()) { cg_printf("%s%s::", Option::ClassPrefix, getOriginalClass()->getId().c_str()); } return false; }
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg, AnalysisResultPtr ar, int doExist) { if (m_valid) { TypePtr type = m_object->getActualType(); if (type->isSpecificObject()) { ClassScopePtr cls(type->getClass(ar, getScope())); if (cls) getFileScope()->addUsedClassFullHeader(cls); } } string func = Option::ObjectPrefix; const char *error = ", true"; std::string context = ""; bool doUnset = m_context & LValue && m_context & UnsetContext; bool needTemp = false; if (cg.getOutput() != CodeGenerator::SystemCPP) { context = originalClassName(cg, true); } if (doUnset) { func = "o_unset"; error = ""; } else if (doExist) { func = doExist > 0 ? "o_isset" : "o_empty"; error = ""; } else { if (m_context & ExistContext) { error = ", false"; } if (m_context & InvokeArgument) { if (cg.callInfoTop() != -1) { func += "argval"; } else { func += "get"; } } else if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) { if (m_context & UnsetContext) { always_assert(!(m_context & LValue)); // handled by doUnset func += "unsetLval"; } else { func += "lval"; } error = ""; needTemp = true; } else { func += "get"; if (isNonPrivate(ar)) { func += "Public"; context = ""; } } } if (m_valid && !m_object->isThis() && (!m_object->is(KindOfSimpleVariable) || !static_pointer_cast<SimpleVariable>(m_object)->isGuarded())) { cg_printf("(obj_tmp = "); outputCPPValidObject(cg, ar, false); bool write_context = hasAnyContext(LValue | RefValue | DeepReference | UnsetContext | OprLValue | DeepOprLValue | DeepAssignmentLHS | AssignmentLHS) && !doUnset; cg_printf(", LIKELY(obj_tmp != 0) %s ", write_context ? "||" : "?"); always_assert(m_property->is(KindOfScalarExpression)); ScalarExpressionPtr name = static_pointer_cast<ScalarExpression>(m_property); if (doExist || doUnset) { cg_printf(doUnset ? "unset" : doExist > 0 ? "isset" : "empty"); } ClassScopePtr cls = ar->findExactClass(shared_from_this(), m_object->getActualType()->getName()); if (write_context) { cg_printf("(throw_null_object_prop(),false),"); } cg_printf("(((%s%s*)obj_tmp)->%s%s)", Option::ClassPrefix, cls->getId().c_str(), Option::PropertyPrefix, name->getString().c_str()); if (!write_context) { cg_printf(" : (raise_null_object_prop(),%s)", doUnset ? "null_variant" : doExist ? doExist > 0 ? "false" : "true" : nullName(ar, getCPPType()).c_str()); } cg_printf(")"); return; } if (m_valid && (doExist || doUnset)) { cg_printf(doUnset ? "unset(" : doExist > 0 ? "isset(" : "empty("); } bool flag = outputCPPObject(cg, ar, !m_valid && (doUnset || doExist)); if (flag) { cg_printf("id("); outputCPPProperty(cg, ar); cg_printf(")"); if (doExist) cg_printf(", %s", doExist > 0 ? "false" : "true"); cg_printf(")"); } else if (m_valid) { always_assert(m_object->getActualType() && m_object->getActualType()->isSpecificObject()); ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(m_property); cg_printf("%s%s", Option::PropertyPrefix, name->getString().c_str()); if (doExist || doUnset) cg_printf(")"); } else { cg_printf("%s(", func.c_str()); if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) { cg_printf("cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum); } outputCPPProperty(cg, ar); if (needTemp) { const string &tmp = cg.getReferenceTemp(); context = ", " + (tmp.empty() ? "Variant()" : tmp) + context; } cg_printf("%s%s)", error, context.c_str()); } }
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; } }