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());
    }
}
Пример #2
0
void ClassScope::collectMethods(AnalysisResultPtr ar,
                                StringToFunctionScopePtrMap &funcs,
                                bool collectPrivate) {
  // add all functions this class has
  for (FunctionScopePtrVec::const_iterator iter =
         m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
    const FunctionScopePtr &fs = *iter;
    if (!collectPrivate && fs->isPrivate()) continue;

    FunctionScopePtr &func = funcs[fs->getName()];
    if (!func) {
      func = fs;
    } else {
      func->setVirtual();
      fs->setVirtual();
      fs->setHasOverride();
      if (fs->isFinal()) {
        std::string s__MockClass = "__MockClass";
        ClassScopePtr derivedClass = func->getContainingClass();
        if (derivedClass->m_userAttributes.find(s__MockClass) ==
            derivedClass->m_userAttributes.end()) {
          Compiler::Error(Compiler::InvalidOverride,
                          fs->getStmt(), func->getStmt());
        }
      }
    }
  }

  int n = m_bases.size();
  for (int i = 0; i < n; i++) {
    const string &base = m_bases[i];
    ClassScopePtr super = ar->findClass(base);
    if (super) {
      if (super->isRedeclaring()) {
        const ClassScopePtrVec &classes = ar->findRedeclaredClasses(base);
        StringToFunctionScopePtrMap pristine(funcs);

        for (auto& cls : classes) {
          cls->m_derivedByDynamic = true;
          StringToFunctionScopePtrMap cur(pristine);
          derivedMagicMethods(cls);
          cls->collectMethods(ar, cur, false);
          inheritedMagicMethods(cls);
          funcs.insert(cur.begin(), cur.end());
          cls->getVariables()->
            forceVariants(ar, VariableTable::AnyNonPrivateVars);
        }

        m_derivesFromRedeclaring = Derivation::Redeclaring;
        getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars,
                                      false);
        getVariables()->setAttribute(VariableTable::NeedGlobalPointer);

        setVolatile();
      } else {
        derivedMagicMethods(super);
        super->collectMethods(ar, funcs, false);
        inheritedMagicMethods(super);
        if (super->derivesFromRedeclaring() == Derivation::Redeclaring) {
          m_derivesFromRedeclaring = Derivation::Redeclaring;
          getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars);
          setVolatile();
        } else if (super->isVolatile()) {
          setVolatile();
        }
      }
    } else {
      Compiler::Error(Compiler::UnknownBaseClass, m_stmt, base);
      if (base == m_parent) {
        ar->declareUnknownClass(m_parent);
        m_derivesFromRedeclaring = Derivation::Redeclaring;
        getVariables()->setAttribute(VariableTable::NeedGlobalPointer);
        getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars);
        setVolatile();
      } else {
        /*
         * TODO(#3685260): this should not be removing interfaces from
         * the base list.
         */
        if (isInterface()) {
          m_derivesFromRedeclaring = Derivation::Redeclaring;
        }
        m_bases.erase(m_bases.begin() + i);
        n--;
        i--;
      }
    }
  }
}
Пример #3
0
TypePtr Type::Coerce(AnalysisResultConstPtr ar, TypePtr type1, TypePtr type2) {
    if (SameType(type1, type2)) return type1;
    if (type1->m_kindOf == KindOfVariant ||
            type2->m_kindOf == KindOfVariant) return Type::Variant;
    if (type1->m_kindOf > type2->m_kindOf) {
        TypePtr tmp = type1;
        type1 = type2;
        type2 = tmp;
    }
    if (type1->m_kindOf == KindOfVoid &&
            (type2->m_kindOf == KindOfString ||
             type2->m_kindOf == KindOfArray ||
             type2->m_kindOf == KindOfObject)) {
        return type2;
    }
    if (type2->m_kindOf == KindOfSome ||
            type2->m_kindOf == KindOfAny) return type1;

    if (type2->m_kindOf & KindOfAuto) {
        if (type1->mustBe(type2->m_kindOf & ~KindOfAuto)) {
            if (!(type1->m_kindOf & Type::KindOfString)) {
                return type1;
            }
            if (type2->m_kindOf == KindOfAutoSequence) {
                return Type::Sequence;
            }
            return GetType((KindOf)(type2->m_kindOf & ~KindOfAuto));
        }
        return Type::Variant;
    }

    if (type1->mustBe(KindOfInteger)) {
        if (type2->mustBe(KindOfInteger)) {
            return type2;
        } else if (type2->mustBe(KindOfDouble)) {
            return Type::Numeric;
        }
    }

    if (type1->mustBe(Type::KindOfObject) &&
            type2->mustBe(Type::KindOfObject)) {
        if (type1->m_name.empty()) return type1;
        if (type2->m_name.empty()) return type2;
        ClassScopePtr cls1 = ar->findClass(type1->m_name);
        if (cls1 && !cls1->isRedeclaring() &&
                cls1->derivesFrom(ar, type2->m_name, true, false)) {
            return type2;
        }
        ClassScopePtr cls2 = ar->findClass(type2->m_name);
        if (cls2 && !cls2->isRedeclaring() &&
                cls2->derivesFrom(ar, type1->m_name, true, false)) {
            return type1;
        }
        if (cls1 && cls2 &&
                !cls1->isRedeclaring() && !cls2->isRedeclaring()) {
            ClassScopePtr parent =
                ClassScope::FindCommonParent(ar, type1->m_name,
                                             type2->m_name);
            if (parent) {
                return Type::CreateObjectType(parent->getName());
            }
        }
        return Type::Object;
    }

    if (type1->mustBe(type2->m_kindOf)) {
        return type2;
    }

    CT_ASSERT(Type::KindOfString < Type::KindOfArray);
    if (type1->m_kindOf == Type::KindOfString &&
            type2->m_kindOf == Type::KindOfArray) {
        return Type::Sequence;
    }

    return Type::Variant;
}
Пример #4
0
TypePtr SimpleVariable::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
                                      bool coerce) {
    IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());

    resetTypes();
    TypePtr ret;
    ConstructPtr construct = shared_from_this();
    BlockScopePtr scope = getScope();
    VariableTablePtr variables = scope->getVariables();

    // check function parameter that can occur in lval context
    if (m_sym && m_sym->isParameter() &&
            m_context & (LValue | RefValue | DeepReference |
                         UnsetContext | InvokeArgument | OprLValue | DeepOprLValue)) {
        m_sym->setLvalParam();
    }

    if (coerce && m_sym && type && type->is(Type::KindOfAutoSequence)) {
        TypePtr t = m_sym->getType();
        if (!t || t->is(Type::KindOfVoid) ||
                t->is(Type::KindOfSome) || t->is(Type::KindOfArray)) {
            type = Type::Array;
        }
    }

    if (m_this) {
        ret = Type::Object;
        ClassScopePtr cls = getOriginalClass();
        if (cls && (hasContext(ObjectContext) || !cls->derivedByDynamic())) {
            ret = Type::CreateObjectType(cls->getName());
        }
        if (!hasContext(ObjectContext) &&
                variables->getAttribute(VariableTable::ContainsDynamicVariable)) {
            if (variables->getAttribute(VariableTable::ContainsLDynamicVariable)) {
                ret = Type::Variant;
            }
            ret = variables->add(m_sym, ret, true, ar,
                                 construct, scope->getModifiers());
        }
    } else if ((m_context & (LValue|Declaration)) &&
               !(m_context & (ObjectContext|RefValue))) {
        if (m_globals) {
            ret = Type::Array;
        } else if (m_superGlobal) {
            ret = m_superGlobalType;
        } else if (m_superGlobalType) { // For system
            ret = variables->add(m_sym, m_superGlobalType,
                                 ((m_context & Declaration) != Declaration), ar,
                                 construct, scope->getModifiers());
        } else {
            ret = variables->add(m_sym, type,
                                 ((m_context & Declaration) != Declaration), ar,
                                 construct, scope->getModifiers());
        }
    } else {
        if (m_superGlobalType) {
            ret = m_superGlobalType;
        } else if (m_globals) {
            ret = Type::Array;
        } else if (scope->is(BlockScope::ClassScope)) {
            assert(getClassScope().get() == scope.get());
            // ClassVariable expression will come to this block of code
            ret = getClassScope()->checkProperty(getScope(), m_sym, type, true, ar);
        } else {
            TypePtr tmpType = type;
            if (m_context & RefValue) {
                tmpType = Type::Variant;
                coerce = true;
            }
            ret = variables->checkVariable(m_sym, tmpType, coerce, ar, construct);
            if (ret && (ret->is(Type::KindOfSome) || ret->is(Type::KindOfAny))) {
                ret = Type::Variant;
            }
        }
    }

    // if m_assertedType is set, then this is a type assertion node
    TypePtr inType = m_assertedType ?
                     GetAssertedInType(ar, m_assertedType, ret) : ret;
    TypePtr actual = propagateTypes(ar, inType);
    setTypes(ar, actual, type);
    if (Type::SameType(actual, ret)) {
        m_implementedType.reset();
    } else {
        m_implementedType = ret;
    }
    return actual;
}
Пример #5
0
void MethodStatement::setSpecialMethod(ClassScopePtr classScope) {
  if (m_name.size() < 2 || m_name.substr(0,2) != "__") {
    return;
  }
  int numArgs = -1;
  bool isStatic = false;
  if (m_name == "__construct") {
    classScope->setAttribute(ClassScope::HasConstructor);
  } else if (m_name == "__destruct") {
    classScope->setAttribute(ClassScope::HasDestructor);
  } else if (m_name == "__call") {
    classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
    numArgs = 2;
  } else if (m_name == "__get") {
    classScope->setAttribute(ClassScope::HasUnknownPropGetter);
    numArgs = 1;
  } else if (m_name == "__set") {
    classScope->setAttribute(ClassScope::HasUnknownPropSetter);
    numArgs = 2;
  } else if (m_name == "__isset") {
    classScope->setAttribute(ClassScope::HasUnknownPropTester);
    numArgs = 1;
  } else if (m_name == "__unset") {
    classScope->setAttribute(ClassScope::HasPropUnsetter);
    numArgs = 1;
  } else if (m_name == "__call") {
    classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
    numArgs = 2;
  } else if (m_name == "__callstatic") {
    classScope->setAttribute(ClassScope::HasUnknownStaticMethodHandler);
    numArgs = 2;
    isStatic = true;
  } else if (m_name == "__invoke") {
    classScope->setAttribute(ClassScope::HasInvokeMethod);
  } else if (m_name == "__tostring") {
    numArgs = 0;
  }
  if (numArgs >= 0) {
    // Fatal if the number of arguments is wrong
    int n = m_params ? m_params->getCount() : 0;
    if (numArgs != n) {
      parseTimeFatal(Compiler::InvalidMagicMethod,
        "Method %s::%s() must take exactly %d argument%s",
        m_originalClassName.c_str(), m_originalName.c_str(),
        numArgs, (numArgs == 1) ? "" : "s");
    }
    // Fatal if any arguments are pass by reference
    if (m_params && hasRefParam()) {
      parseTimeFatal(Compiler::InvalidMagicMethod,
        "Method %s::%s() cannot take arguments by reference",
        m_originalClassName.c_str(), m_originalName.c_str());
    }
    // Fatal if protected/private or if the staticness is wrong
    if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
        m_modifiers->isStatic() != isStatic) {
      parseTimeFatal(Compiler::InvalidMagicMethod,
        "Method %s::%s() must have public visibility and %sbe static",
        m_originalClassName.c_str(), m_originalName.c_str(),
        isStatic ? "" : "cannot ");
    }
  }
}
Пример #6
0
void ClassVariable::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {

  // bail out early if possible
  switch (cg.getContext()) {
  case CodeGenerator::CppConstructor:
  case CodeGenerator::CppInitializer:
    if (m_modifiers->isStatic()) return;
    break;
  default:
    return;
  }

  ClassScopePtr scope = getClassScope();
  bool derivFromRedec = scope->derivesFromRedeclaring() &&
    !m_modifiers->isPrivate();

  for (int i = 0; i < m_declaration->getCount(); i++) {
    ExpressionPtr exp = (*m_declaration)[i];
    SimpleVariablePtr var;
    TypePtr type;
    Symbol *sym;
    ExpressionPtr value;

    bool initInCtor = false;
    bool initInInit = false;
    getCtorAndInitInfo(exp, initInCtor, initInInit, var, type, sym, value);

    bool isAssign = exp->is(Expression::KindOfAssignmentExpression);
    bool isValueNull = isAssign ? value->isLiteralNull() : false;

    switch (cg.getContext()) {
    case CodeGenerator::CppConstructor:
      if (initInCtor) {
        if (!cg.hasInitListFirstElem()) {
          cg.setInitListFirstElem();
        } else {
          cg_printf(", ");
        }
        if (isAssign) {
          if (isValueNull) {
            cg_printf("%s%s(%s)",
                      Option::PropertyPrefix, var->getName().c_str(),
                      Type::IsMappedToVariant(type) ? "Variant::nullInit" : "");
          } else {
            assert(value);
            assert(value->is(Expression::KindOfScalarExpression));
            cg_printf("%s%s(",
                      Option::PropertyPrefix,
                      var->getName().c_str());
            value->outputCPP(cg, ar);
            cg_printf(")");
          }
        } else {
          if (type->is(Type::KindOfVariant)) {
            cg_printf("%s%s(Variant::nullInit)",
                      Option::PropertyPrefix,
                      var->getName().c_str());
          } else {
            const char *initializer = type->getCPPInitializer();
            assert(initializer);
            cg_printf("%s%s(%s)",
                      Option::PropertyPrefix,
                      var->getName().c_str(),
                      initializer);
          }
        }
      }
      break;
    case CodeGenerator::CppInitializer:
      if (initInInit) {
        if (isAssign) {
          value->outputCPPBegin(cg, ar);
          if (derivFromRedec) {
            cg_printf("%sset(", Option::ObjectPrefix);
            cg_printString(var->getName(), ar, shared_from_this());
            cg_printf(", ");
            value->outputCPP(cg, ar);
            cg_printf(")");
          } else if (isValueNull) {
            cg_printf("setNull(%s%s)", Option::PropertyPrefix,
                      var->getName().c_str());
          } else {
            cg_printf("%s%s = ", Option::PropertyPrefix, var->getName().c_str());
            value->outputCPP(cg, ar);
          }
          cg_printf(";\n");
          value->outputCPPEnd(cg, ar);
        } else {
          if (derivFromRedec) {
            cg_printf("%sset(", Option::ObjectPrefix);
            cg_printString(var->getName(), ar, shared_from_this());
            cg_printf(", null_variant);\n");
          } else {
            if (type->is(Type::KindOfVariant)) {
              cg_printf("setNull(%s%s);\n", Option::PropertyPrefix,
                        var->getName().c_str());
            } else {
              const char *initializer = type->getCPPInitializer();
              assert(initializer);
              cg_printf("%s%s = %s;\n", Option::PropertyPrefix,
                        var->getName().c_str(), initializer);
            }
          }
        }
      }
      break;
    default:
      break;
    }
  }
}
Пример #7
0
TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
                                        bool coerce) {
  reset();
  m_classScope.reset();
  FunctionScopePtr prev = m_funcScope;
  m_funcScope.reset();
  ConstructPtr self = shared_from_this();
  if (!m_name.empty() && !isStatic()) {
    ClassScopePtr cls = resolveClass();
    m_name = m_className;

    if (!cls) {
      if (isRedeclared()) {
        getScope()->getVariables()->
          setAttribute(VariableTable::NeedGlobalPointer);
      } else if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::UnknownClass, self);
      }
      if (m_params) m_params->inferAndCheck(ar, Type::Any, false);
      return Type::Object;
    }
    if (cls->isVolatile() && !isPresent()) {
      getScope()->getVariables()->
        setAttribute(VariableTable::NeedGlobalPointer);
    }
    m_dynamic = cls->derivesFromRedeclaring();
    bool valid = true;
    FunctionScopePtr func = cls->findConstructor(ar, true);
    if (!func) {
      if (m_params) {
        if (!m_dynamic && m_params->getCount()) {
          if (getScope()->isFirstPass()) {
            Compiler::Error(Compiler::BadConstructorCall, self);
          }
          m_params->setOutputCount(0);
        }
        m_params->inferAndCheck(ar, Type::Some, false);
      }
    } else {
      if (func != prev) func->addNewObjCaller(getScope());
      m_extraArg = func->inferParamTypes(ar, self, m_params, valid);
      m_variableArgument = func->isVariableArgument();
    }
    if (valid) {
      m_classScope = cls;
      m_funcScope = func;
    }
    if (!valid || m_dynamic) {
      m_implementedType = Type::Object;
    } else {
      m_implementedType.reset();
    }
    return Type::CreateObjectType(m_name);
  } else {
    if (m_params) {
      m_params->markParams(canInvokeFewArgs());
    }
  }

  m_implementedType.reset();
  m_nameExp->inferAndCheck(ar, Type::String, false);
  if (m_params) m_params->inferAndCheck(ar, Type::Any, false);
  return Type::Object;
}
Пример #8
0
ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) {
    if (m_class) ar->preOptimize(m_class);
    ar->preOptimize(m_nameExp);
    ar->preOptimize(m_params);
    if (ar->getPhase() != AnalysisResult::SecondPreOptimize) {
        return ExpressionPtr();
    }
    // optimize away various "exists" functions, this may trigger
    // dead code elimination and improve type-inference.
    if (!m_class && m_className.empty() &&
            (m_type == DefinedFunction ||
             m_type == FunctionExistsFunction ||
             m_type == ClassExistsFunction ||
             m_type == InterfaceExistsFunction) &&
            m_params && m_params->getCount() == 1) {
        ExpressionPtr value = (*m_params)[0];
        if (value->isScalar()) {
            ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value);
            if (name && name->isLiteralString()) {
                string symbol = name->getLiteralString();
                switch (m_type) {
                case DefinedFunction: {
                    ConstantTablePtr constants = ar->getConstants();
                    // system constant
                    if (constants->isPresent(symbol)) {
                        return CONSTANT("true");
                    }
                    // user constant
                    BlockScopePtr block = ar->findConstantDeclarer(symbol);
                    // not found (i.e., undefined)
                    if (!block) {
                        if (symbol.find("::") == std::string::npos) {
                            return CONSTANT("false");
                        } else {
                            // e.g., defined("self::ZERO")
                            return ExpressionPtr();
                        }
                    }
                    constants = block->getConstants();
                    // already set to be dynamic
                    if (constants->isDynamic(symbol)) return ExpressionPtr();
                    ConstructPtr decl = constants->getValue(symbol);
                    ExpressionPtr constValue = dynamic_pointer_cast<Expression>(decl);
                    if (constValue->isScalar()) {
                        return CONSTANT("true");
                    } else {
                        return ExpressionPtr();
                    }
                    break;
                }
                case FunctionExistsFunction: {
                    const std::string &lname = Util::toLower(symbol);
                    if (Option::DynamicInvokeFunctions.find(lname) ==
                            Option::DynamicInvokeFunctions.end()) {
                        FunctionScopePtr func = ar->findFunction(lname);
                        if (!func) {
                            return CONSTANT("false");
                        } else if (!func->isVolatile()) {
                            return CONSTANT("true");
                        }
                    }
                    break;
                }
                case InterfaceExistsFunction: {
                    ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                    if (!cls || !cls->isInterface()) {
                        return CONSTANT("false");
                    } else if (!cls->isVolatile()) {
                        return CONSTANT("true");
                    }
                    break;
                }
                case ClassExistsFunction: {
                    ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                    if (!cls || cls->isInterface()) {
                        return CONSTANT("false");
                    } else if (!cls->isVolatile()) {
                        return CONSTANT("true");
                    }
                    break;
                }
                default:
                    ASSERT(false);
                }
            }
        }
    }
    return ExpressionPtr();
}
Пример #9
0
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
        bool coerce) {
    reset();

    ConstructPtr self = shared_from_this();

    // handling define("CONSTANT", ...);
    if (!m_class && m_className.empty()) {
        if (m_type == DefineFunction && m_params && m_params->getCount() >= 2) {
            ScalarExpressionPtr name =
                dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
            string varName;
            if (name) {
                varName = name->getIdentifier();
                if (!varName.empty()) {
                    ExpressionPtr value = (*m_params)[1];
                    TypePtr varType = value->inferAndCheck(ar, NEW_TYPE(Some), false);
                    ar->getDependencyGraph()->
                    addParent(DependencyGraph::KindOfConstant,
                              ar->getName(), varName, self);
                    ConstantTablePtr constants =
                        ar->findConstantDeclarer(varName)->getConstants();
                    if (constants != ar->getConstants()) {
                        if (value && !value->isScalar()) {
                            constants->setDynamic(ar, varName);
                            varType = Type::Variant;
                        }
                        if (constants->isDynamic(varName)) {
                            m_dynamicConstant = true;
                            ar->getScope()->getVariables()->
                            setAttribute(VariableTable::NeedGlobalPointer);
                        } else {
                            constants->setType(ar, varName, varType, true);
                        }
                        // in case the old 'value' has been optimized
                        constants->setValue(ar, varName, value);
                    }
                    return checkTypesImpl(ar, type, Type::Boolean, coerce);
                }
            }
            if (varName.empty() && ar->isFirstPass()) {
                ar->getCodeError()->record(self, CodeError::BadDefine, self);
            }
        } else if (m_type == ExtractFunction) {
            ar->getScope()->getVariables()->forceVariants(ar);
        }
    }

    FunctionScopePtr func;

    // avoid raising both MissingObjectContext and UnknownFunction
    bool errorFlagged = false;

    if (!m_class && m_className.empty()) {
        func = ar->findFunction(m_name);
    } else {
        ClassScopePtr cls = ar->resolveClass(m_className);
        if (cls && cls->isVolatile()) {
            ar->getScope()->getVariables()
            ->setAttribute(VariableTable::NeedGlobalPointer);
        }
        if (!cls || cls->isRedeclaring()) {
            if (cls) {
                m_redeclaredClass = true;
            }
            if (!cls && ar->isFirstPass()) {
                ar->getCodeError()->record(self, CodeError::UnknownClass, self);
            }
            if (m_params) {
                m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
            }
            return checkTypesImpl(ar, type, Type::Variant, coerce);
        }
        m_derivedFromRedeclaring = cls->derivesFromRedeclaring();
        m_validClass = true;

        if (m_name == "__construct") {
            // if the class is known, php will try to identify class-name ctor
            func = cls->findConstructor(ar, true);
        }
        else {
            func = cls->findFunction(ar, m_name, true, true);
        }

        if (func && !func->isStatic()) {
            ClassScopePtr clsThis = ar->getClassScope();
            FunctionScopePtr funcThis = ar->getFunctionScope();
            if (!clsThis ||
                    (clsThis->getName() != m_className &&
                     !clsThis->derivesFrom(ar, m_className)) ||
                    funcThis->isStatic()) {
                // set the method static to avoid "unknown method" runtime exception
                if (Option::StaticMethodAutoFix && !func->containsThis()) {
                    func->setStaticMethodAutoFixed();
                }
                if (ar->isFirstPass()) {
                    ar->getCodeError()->record(self, CodeError::MissingObjectContext,
                                               self);
                    errorFlagged = true;
                }
                func.reset();
            }
        }
    }
    if (!func || func->isRedeclaring()) {
        if (func) {
            m_redeclared = true;
            ar->getScope()->getVariables()->
            setAttribute(VariableTable::NeedGlobalPointer);
        }
        if (!func && !errorFlagged && ar->isFirstPass()) {
            ar->getCodeError()->record(self, CodeError::UnknownFunction, self);
        }
        if (m_params) {
            if (func) {
                FunctionScope::RefParamInfoPtr info =
                    FunctionScope::GetRefParamInfo(m_name);
                ASSERT(info);
                for (int i = m_params->getCount(); i--; ) {
                    if (info->isRefParam(i)) {
                        m_params->markParam(i, canInvokeFewArgs());
                    }
                }
            }
            m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
        }
        return checkTypesImpl(ar, type, Type::Variant, coerce);
    }
    m_builtinFunction = !func->isUserFunction();

    if (m_redeclared) {
        if (m_params) {
            m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
        }
        return checkTypesImpl(ar, type, type, coerce);
    }

    CHECK_HOOK(beforeSimpleFunctionCallCheck);

    m_valid = true;
    type = checkParamsAndReturn(ar, type, coerce, func);

    if (!m_valid && m_params) {
        m_params->markParams(false);
    }

    CHECK_HOOK(afterSimpleFunctionCallCheck);

    return type;
}
bool DynamicFunctionCall::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
                                       int state) {
    bool nonStatic = !m_class && m_className.empty();
    if (!nonStatic && !m_class && !m_validClass && !m_redeclared)
        return FunctionCall::preOutputCPP(cg, ar, state);
    // Short circuit out if inExpression() returns false
    if (!ar->inExpression()) return true;

    if (m_class) {
        m_class->preOutputCPP(cg, ar, state);
    }

    m_nameExp->preOutputCPP(cg, ar, state);

    ar->wrapExpressionBegin(cg);
    m_ciTemp = cg.createNewId(shared_from_this());
    bool lsb = false;
    ClassScopePtr cls;
    if (m_validClass) {
        cls = ar->findClass(m_className);
    }

    if (!m_classScope && !m_className.empty() && m_cppTemp.empty() &&
            m_origClassName != "self" && m_origClassName != "parent" &&
            m_origClassName != "static") {
        // Create a temporary to hold the class name, in case it is not a
        // StaticString.
        m_clsNameTemp = cg.createNewId(shared_from_this());
        cg_printf("CStrRef clsName%d(", m_clsNameTemp);
        cg_printString(m_origClassName, ar, shared_from_this());
        cg_printf(");\n");
    }

    if (nonStatic) {
        cg_printf("const CallInfo *cit%d;\n", m_ciTemp);
        cg_printf("void *vt%d;\n", m_ciTemp);
        cg_printf("get_call_info_or_fail(cit%d, vt%d, ", m_ciTemp, m_ciTemp);
    } else {
        cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp);
        if (m_class) {
            if (m_class->is(KindOfScalarExpression)) {
                ASSERT(strcasecmp(dynamic_pointer_cast<ScalarExpression>(m_class)->
                                  getString().c_str(), "static") == 0);
                cg_printf("mcp%d.staticMethodCall(", m_ciTemp);
                cg_printf("\"static\"");
                lsb = true;
            } else {
                cg_printf("mcp%d.dynamicNamedCall(", m_ciTemp);
                m_class->outputCPP(cg, ar);
            }
            cg_printf(", ");
        } else if (m_validClass) {
            cg_printf("mcp%d.staticMethodCall(\"%s\", ", m_ciTemp,
                      cls->getId(cg).c_str());
        } else {
            cg_printf("mcp%d.staticMethodCall(\"%s\", ", m_ciTemp,
                      m_className.c_str());
        }
    }

    if (m_nameExp->is(Expression::KindOfSimpleVariable)) {
        m_nameExp->outputCPP(cg, ar);
    } else {
        cg_printf("(");
        m_nameExp->outputCPP(cg, ar);
        cg_printf(")");
    }

    if (!nonStatic) {
        cg_printf(");\n");
        if (lsb) cg_printf("mcp%d.lateStaticBind(info);\n");
        cg_printf("const CallInfo *&cit%d = mcp%d.ci;\n", m_ciTemp, m_ciTemp);
        if (m_validClass) {
            cg_printf("%s%s::%sget_call_info(mcp%d",
                      Option::ClassPrefix, cls->getId(cg).c_str(),
                      Option::ObjectStaticPrefix, m_ciTemp, m_className.c_str());
        } else if (m_redeclared) {
            cg_printf("g->%s%s->%sget_call_info(mcp%d",
                      Option::ClassStaticsObjectPrefix, m_className.c_str(),
                      Option::ObjectStaticPrefix, m_ciTemp, m_className.c_str());
        }
    }
    if (nonStatic || !m_class) {
        cg_printf(");\n");
    }
    if (m_params && m_params->getCount() > 0) {
        ar->pushCallInfo(m_ciTemp);
        m_params->preOutputCPP(cg, ar, state);
        ar->popCallInfo();
    }

    ar->pushCallInfo(m_ciTemp);
    preOutputStash(cg, ar, state);
    ar->popCallInfo();
    if (!(state & FixOrder)) {
        cg_printf("id(%s);\n", cppTemp().c_str());
    }
    return true;
}
Пример #11
0
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) {
    if (m_class) {
        m_class->analyzeProgram(ar);
        setDynamicByIdentifier(ar, m_name);
    } else {
        if (m_className.empty()) {
            addUserFunction(ar, m_name);
        } else if (m_className != "parent") {
            addUserClass(ar, m_className);
        } else {
            m_parentClass = true;
        }
    }

    if (ar->getPhase() == AnalysisResult::AnalyzeInclude) {

        CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude);

        ConstructPtr self = shared_from_this();

        // We need to know the name of the constant so that we can associate it
        // with this file before we do type inference.
        if (!m_class && m_className.empty() && m_type == DefineFunction) {
            ScalarExpressionPtr name =
                dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
            string varName;
            if (name) {
                varName = name->getIdentifier();
                if (!varName.empty()) {
                    ar->getFileScope()->declareConstant(ar, varName);
                }
            }
            // handling define("CONSTANT", ...);
            if (m_params && m_params->getCount() >= 2) {
                ScalarExpressionPtr name =
                    dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
                string varName;
                if (name) {
                    varName = name->getIdentifier();
                    if (!varName.empty()) {
                        ExpressionPtr value = (*m_params)[1];
                        ConstantTablePtr constants =
                            ar->findConstantDeclarer(varName)->getConstants();
                        if (constants != ar->getConstants()) {
                            constants->add(varName, NEW_TYPE(Some), value, ar, self);

                            if (name->hasHphpNote("Dynamic")) {
                                constants->setDynamic(ar, varName);
                            }
                        }
                    }
                }
            }
        }

        if (m_type == UnserializeFunction) {
            ar->forceClassVariants();
        }
    }

    if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
        // Look up the corresponding FunctionScope and ClassScope
        // for this function call
        {
            FunctionScopePtr func;
            ClassScopePtr cls;
            if (!m_class && m_className.empty()) {
                func = ar->findFunction(m_name);
            } else {
                cls = ar->resolveClass(m_className);
                if (cls) {
                    if (m_name == "__construct") {
                        func = cls->findConstructor(ar, true);
                    } else {
                        func = cls->findFunction(ar, m_name, true, true);
                    }
                }
            }
            if (func && !func->isRedeclaring()) {
                if (m_funcScope != func) {
                    m_funcScope = func;
                    Construct::recomputeEffects();
                }
            }
            if (cls && !cls->isRedeclaring())
                m_classScope = cls;
        }
        // check for dynamic constant and volatile function/class
        if (!m_class && m_className.empty() &&
                (m_type == DefinedFunction ||
                 m_type == FunctionExistsFunction ||
                 m_type == ClassExistsFunction ||
                 m_type == InterfaceExistsFunction) &&
                m_params && m_params->getCount() >= 1) {
            ExpressionPtr value = (*m_params)[0];
            if (value->isScalar()) {
                ScalarExpressionPtr name =
                    dynamic_pointer_cast<ScalarExpression>(value);
                if (name && name->isLiteralString()) {
                    string symbol = name->getLiteralString();
                    switch (m_type) {
                    case DefinedFunction: {
                        ConstantTablePtr constants = ar->getConstants();
                        if (!constants->isPresent(symbol)) {
                            // user constant
                            BlockScopePtr block = ar->findConstantDeclarer(symbol);
                            if (block) { // found the constant
                                constants = block->getConstants();
                                // set to be dynamic
                                constants->setDynamic(ar, symbol);
                            }
                        }
                        break;
                    }
                    case FunctionExistsFunction: {
                        FunctionScopePtr func = ar->findFunction(Util::toLower(symbol));
                        if (func && func->isUserFunction()) {
                            func->setVolatile();
                        }
                        break;
                    }
                    case InterfaceExistsFunction:
                    case ClassExistsFunction: {
                        ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                        if (cls && cls->isUserClass()) {
                            cls->setVolatile();
                        }
                        break;
                    }
                    default:
                        ASSERT(false);
                    }
                }
            }
        }
    }

    if (m_params) {
        if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
            if (m_funcScope) {
                ExpressionList &params = *m_params;
                int mpc = m_funcScope->getMaxParamCount();
                for (int i = params.getCount(); i--; ) {
                    ExpressionPtr p = params[i];
                    if (i < mpc ? m_funcScope->isRefParam(i) :
                            m_funcScope->isReferenceVariableArgument()) {
                        p->setContext(Expression::RefValue);
                    } else if (!(p->getContext() & Expression::RefParameter)) {
                        p->clearContext(Expression::RefValue);
                    }
                }
            } else {
                FunctionScopePtr func = ar->findFunction(m_name);
                if (func && func->isRedeclaring()) {
                    FunctionScope::RefParamInfoPtr info =
                        FunctionScope::GetRefParamInfo(m_name);
                    if (info) {
                        for (int i = m_params->getCount(); i--; ) {
                            if (info->isRefParam(i)) {
                                m_params->markParam(i, canInvokeFewArgs());
                            }
                        }
                    }
                } else {
                    m_params->markParams(false);
                }
            }
        }

        m_params->analyzeProgram(ar);
    }
}
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;
}
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()) {
      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 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) {
      m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()),
                              false);
    }
  }

  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;
}
Пример #16
0
void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator &cg,
        AnalysisResultPtr ar) {
    if (!m_class && m_className.empty()) {
        switch (m_type) {
        case ExtractFunction:
            cg.printf("extract(variables, ");
            FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false);
            cg.printf(")");
            return;
        case CompactFunction:
            cg.printf("compact(variables, ");
            FunctionScope::outputCPPArguments(m_params, cg, ar, -1, true);
            cg.printf(")");
            return;
        default:
            break;
        }
    }
    bool volatileCheck = false;
    ClassScopePtr cls;
    if (!m_className.empty()) {
        cls = ar->findClass(m_className);
        if (cls && !ar->checkClassPresent(m_origClassName)) {
            volatileCheck = true;
            cls->outputVolatileCheckBegin(cg, ar, cls->getOriginalName());
        }
    }
    if (m_valid) {
        bool tooManyArgs =
            (m_params && m_params->outputCPPTooManyArgsPre(cg, ar, m_name));
        if (!m_className.empty()) {
            cg.printf("%s%s::", Option::ClassPrefix, m_className.c_str());
            if (m_name == "__construct" && cls) {
                FunctionScopePtr func = cls->findConstructor(ar, true);
                cg.printf("%s%s(", Option::MethodPrefix, func->getName().c_str());
            } else {
                cg.printf("%s%s(", Option::MethodPrefix, m_name.c_str());
            }
        } else {
            int paramCount = m_params ? m_params->getCount() : 0;
            if (m_name == "get_class" && ar->getClassScope() && paramCount == 0) {
                cg.printf("(\"%s\"", ar->getClassScope()->getOriginalName());
            } else if (m_name == "get_parent_class" && ar->getClassScope() &&
                       paramCount == 0) {
                const std::string parentClass = ar->getClassScope()->getParent();
                if (!parentClass.empty()) {
                    cg.printf("(\"%s\"", ar->getClassScope()->getParent().c_str());
                } else {
                    cg.printf("(false");
                }
            } else {
                if (m_noPrefix) {
                    cg.printf("%s(", m_name.c_str());
                }
                else {
                    cg.printf("%s%s(", m_builtinFunction ? Option::BuiltinFunctionPrefix :
                              Option::FunctionPrefix, m_name.c_str());
                }
            }
        }
        FunctionScope::outputCPPArguments(m_params, cg, ar, m_extraArg,
                                          m_variableArgument, m_argArrayId);
        cg.printf(")");
        if (tooManyArgs) {
            m_params->outputCPPTooManyArgsPost(cg, ar, m_voidReturn);
        }
    } else {
        if (!m_class && m_className.empty()) {
            if (m_redeclared && !m_dynamicInvoke) {
                if (canInvokeFewArgs()) {
                    cg.printf("%s->%s%s_few_args(", cg.getGlobals(ar),
                              Option::InvokePrefix, m_name.c_str());
                    int left = Option::InvokeFewArgsCount;
                    if (m_params && m_params->getCount()) {
                        left -= m_params->getCount();
                        cg.printf("%d, ", m_params->getCount());
                        FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false);
                    } else {
                        cg.printf("0");
                    }
                    for (int i = 0; i < left; i++) {
                        cg.printf(", null_variant");
                    }
                    cg.printf(")");
                    return;
                } else {
                    cg.printf("%s->%s%s(", cg.getGlobals(ar), Option::InvokePrefix,
                              m_name.c_str());
                }
            } else {
                cg.printf("invoke(\"%s\", ", m_name.c_str());
            }
        } else {
            bool inObj = m_parentClass && ar->getClassScope() &&
                         !dynamic_pointer_cast<FunctionScope>(ar->getScope())->isStatic();
            if (m_redeclaredClass) {
                if (inObj) {  // parent is redeclared
                    cg.printf("parent->%sinvoke(\"%s\",", Option::ObjectPrefix,
                              m_name.c_str());
                } else {
                    cg.printf("%s->%s%s->%sinvoke(\"%s\", \"%s\",",
                              cg.getGlobals(ar),
                              Option::ClassStaticsObjectPrefix,
                              m_className.c_str(), Option::ObjectStaticPrefix,
                              m_className.c_str(),
                              m_name.c_str());
                }
            } else if (m_validClass) {
                if (inObj) {
                    cg.printf("%s%s::%sinvoke(\"%s\",",
                              Option::ClassPrefix, m_className.c_str(),
                              Option::ObjectPrefix, m_name.c_str());
                } else {
                    cg.printf("%s%s::%sinvoke(\"%s\", \"%s\",",
                              Option::ClassPrefix, m_className.c_str(),
                              Option::ObjectStaticPrefix,
                              m_className.c_str(),
                              m_name.c_str());
                }
            } else {
                if (m_class) {
                    cg.printf("INVOKE_STATIC_METHOD(toString(");
                    if (m_class->is(KindOfScalarExpression)) {
                        ASSERT(strcasecmp(dynamic_pointer_cast<ScalarExpression>(m_class)->
                                          getString().c_str(), "static") == 0);
                        cg.printf("\"static\"");
                    } else {
                        m_class->outputCPP(cg, ar);
                    }
                    cg.printf("), \"%s\",", m_name.c_str());
                } else {
                    cg.printf("invoke_static_method(\"%s\", \"%s\",",
                              m_className.c_str(), m_name.c_str());
                }
            }
        }
        if ((!m_params) || (m_params->getCount() == 0)) {
            cg.printf("Array()");
        } else {
            FunctionScope::outputCPPArguments(m_params, cg, ar, -1, false);
        }
        bool needHash = true;
        if (!m_class && m_className.empty()) {
            needHash = !(m_redeclared && !m_dynamicInvoke);
        } else {
            needHash = m_validClass || m_redeclaredClass;
        }
        if (!needHash) {
            cg.printf(")");
        } else {
            cg.printf(", 0x%.16lXLL)", hash_string_i(m_name.data(), m_name.size()));
        }
    }
    if (volatileCheck) {
        cls->outputVolatileCheckEnd(cg);
    }
}
Пример #17
0
void AnalysisResult::analyzeProgram(bool system /* = false */) {
  AnalysisResultPtr ar = shared_from_this();

  getVariables()->setAttribute(VariableTable::ContainsLDynamicVariable);
  getVariables()->setAttribute(VariableTable::ContainsExtract);
  getVariables()->setAttribute(VariableTable::ForceGlobal);

  // Analyze Includes
  Logger::Verbose("Analyzing Includes");
  sort(m_fileScopes.begin(), m_fileScopes.end(), by_filename); // fixed order
  unsigned int i = 0;
  for (i = 0; i < m_fileScopes.size(); i++) {
    collectFunctionsAndClasses(m_fileScopes[i]);
  }

  // Keep generated code identical without randomness
  canonicalizeSymbolOrder();

  markRedeclaringClasses();

  // Analyze some special cases
  for (auto& cls_name : Option::VolatileClasses) {
    ClassScopePtr cls = findClass(toLower(cls_name));
    if (cls && cls->isUserClass()) {
      cls->setVolatile();
    }
  }

  checkClassDerivations();
  resolveNSFallbackFuncs();

  // Analyze All
  Logger::Verbose("Analyzing All");
  setPhase(AnalysisResult::AnalyzeAll);
  for (i = 0; i < m_fileScopes.size(); i++) {
    m_fileScopes[i]->analyzeProgram(ar);
  }

  /*
    Note that cls->collectMethods() can add entries to m_classDecs,
    which can invalidate iterators. So we have to create an array
    and then iterate over that.
    The new entries added to m_classDecs are always empty, so it
    doesnt matter that we dont include them in the iteration
  */
  std::vector<ClassScopePtr> classes;
  classes.reserve(m_classDecs.size());
  for (auto& pair : m_classDecs) {
    for (auto cls : pair.second) {
      classes.push_back(cls);
    }
  }

  // Collect methods
  for (auto cls : classes) {
    StringToFunctionScopePtrMap methods;
    cls->collectMethods(ar, methods, true /* include privates */);
    bool needAbstractMethodImpl =
      (!cls->isAbstract() && !cls->isInterface() &&
       cls->derivesFromRedeclaring() == Derivation::Normal &&
       !cls->getAttribute(ClassScope::UsesUnknownTrait));
    for (auto& pair : methods) {
      auto func = pair.second;
      if (Option::WholeProgram && !func->hasImpl() && needAbstractMethodImpl) {
        auto tmpFunc = cls->findFunction(ar, func->getScopeName(), true, true);
        always_assert(!tmpFunc || !tmpFunc->hasImpl());
        Compiler::Error(Compiler::MissingAbstractMethodImpl,
                        func->getStmt(), cls->getStmt());
      }
    }
  }

  for (auto& item : m_systemClasses) {
    StringToFunctionScopePtrMap methods;
    item.second->collectMethods(ar, methods, true /* include privates */);
  }
}
Пример #18
0
void SimpleFunctionCall::outputCPPImpl(CodeGenerator &cg,
                                       AnalysisResultPtr ar) {
    bool linemap = outputLineMap(cg, ar, true);

    if (!m_lambda.empty()) {
        cg.printf("\"%s\"", m_lambda.c_str());
        if (linemap) cg.printf(")");
        return;
    }

    if (!m_class && m_className.empty()) {
        if (m_type == DefineFunction && m_params && m_params->getCount() >= 2) {
            ScalarExpressionPtr name =
                dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
            string varName;
            if (name) {
                varName = name->getIdentifier();
                ExpressionPtr value = (*m_params)[1];
                if (varName.empty()) {
                    cg.printf("throw_fatal(\"bad define\")");
                } else if (m_dynamicConstant) {
                    cg.printf("g->declareConstant(\"%s\", g->%s%s, ",
                              varName.c_str(), Option::ConstantPrefix,
                              varName.c_str());
                    value->outputCPP(cg, ar);
                    cg.printf(")");
                } else {
                    bool needAssignment = true;
                    bool isSystem = ar->getConstants()->isSystem(varName);
                    if (isSystem ||
                            ((!ar->isConstantRedeclared(varName)) && value->isScalar())) {
                        needAssignment = false;
                    }
                    if (needAssignment) {
                        cg.printf("%s%s = ", Option::ConstantPrefix, varName.c_str());
                        value->outputCPP(cg, ar);
                    }
                }
            } else {
                cg.printf("throw_fatal(\"bad define\")");
            }
            if (linemap) cg.printf(")");
            return;
        }
        if (m_name == "func_num_args") {
            cg.printf("num_args");
            if (linemap) cg.printf(")");
            return;
        }

        switch (m_type) {
        case VariableArgumentFunction:
        {
            FunctionScopePtr func =
                dynamic_pointer_cast<FunctionScope>(ar->getScope());
            if (func) {
                cg.printf("%s(", m_name.c_str());
                func->outputCPPParamsCall(cg, ar, true);
                if (m_params) {
                    cg.printf(",");
                    m_params->outputCPP(cg, ar);
                }
                cg.printf(")");
                if (linemap) cg.printf(")");
                return;
            }
        }
        break;
        case FunctionExistsFunction:
        case ClassExistsFunction:
        case InterfaceExistsFunction:
        {
            bool literalString = false;
            string symbol;
            if (m_params && m_params->getCount() == 1) {
                ExpressionPtr value = (*m_params)[0];
                if (value->isScalar()) {
                    ScalarExpressionPtr name =
                        dynamic_pointer_cast<ScalarExpression>(value);
                    if (name && name->isLiteralString()) {
                        literalString = true;
                        symbol = name->getLiteralString();
                    }
                }
            }
            if (literalString) {
                switch (m_type) {
                case FunctionExistsFunction:
                {
                    const std::string &lname = Util::toLower(symbol);
                    bool dynInvoke = Option::DynamicInvokeFunctions.find(lname) !=
                                     Option::DynamicInvokeFunctions.end();
                    if (!dynInvoke) {
                        FunctionScopePtr func = ar->findFunction(lname);
                        if (func) {
                            if (!func->isDynamic()) {
                                if (func->isRedeclaring()) {
                                    const char *name = func->getName().c_str();
                                    cg.printf("(%s->%s%s != invoke_failed_%s)",
                                              cg.getGlobals(ar), Option::InvokePrefix,
                                              name, name);
                                    break;
                                }
                                cg.printf("true");
                                break;
                            }
                        } else {
                            cg.printf("false");
                            break;
                        }
                    }
                    cg.printf("f_function_exists(\"%s\")", lname.c_str());
                }
                break;
                case ClassExistsFunction:
                {
                    ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                    if (cls && !cls->isInterface()) {
                        const char *name = cls->getName().c_str();
                        cg.printf("f_class_exists(\"%s\")", name);
                    } else {
                        cg.printf("false");
                    }
                }
                break;
                case InterfaceExistsFunction:
                {
                    ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
                    if (cls && cls->isInterface()) {
                        const char *name = cls->getName().c_str();
                        cg.printf("f_interface_exists(\"%s\")", name);
                    } else {
                        cg.printf("false");
                    }
                }
                break;
                default:
                    break;
                }
                if (linemap) cg.printf(")");
                return;
            }
        }
        break;
        case GetDefinedVarsFunction:
            cg.printf("get_defined_vars(variables)");
            if (linemap) cg.printf(")");
            return;
        default:
            break;
        }
    }

    outputCPPParamOrderControlled(cg, ar);
    if (linemap) cg.printf(")");
}
void InterfaceStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  ClassScopeRawPtr classScope = getClassScope();
  if (cg.getContext() == CodeGenerator::NoContext) {
    if (classScope->isVolatile()) {
      cg_printf("g->CDEC(%s) = true;\n", cg.formatLabel(m_name).c_str());
    }
    return;
  }

  string clsNameStr = classScope->getId(cg);
  const char *clsName = clsNameStr.c_str();

  switch (cg.getContext()) {
  case CodeGenerator::CppForwardDeclaration:
    if (Option::GenerateCPPMacros) {
      if (!Option::UseVirtualDispatch ||
          classScope->isRedeclaring()) {
        cg_printf("FORWARD_DECLARE_GENERIC_INTERFACE(%s);\n", clsName);
      } else {
        cg_printf("FORWARD_DECLARE_INTERFACE(%s);\n", clsName);
      }
    }
    break;
  case CodeGenerator::CppDeclaration:
    {
      printSource(cg);
      cg_printf("class %s%s", Option::ClassPrefix, clsName);
      if (m_base && Option::UseVirtualDispatch &&
          !classScope->isRedeclaring()) {
        const char *sep = " :";
        for (int i = 0; i < m_base->getCount(); i++) {
          ScalarExpressionPtr exp =
            dynamic_pointer_cast<ScalarExpression>((*m_base)[i]);
          const char *intf = exp->getString().c_str();
          ClassScopePtr intfClassScope = ar->findClass(intf);
          if (intfClassScope && !intfClassScope->isRedeclaring() &&
              classScope->derivesDirectlyFrom(ar, intf)) {
            string id = intfClassScope->getId(cg);
            cg_printf("%s public %s%s", sep, Option::ClassPrefix, id.c_str());
            sep = ",";
          }
        }
      }
      cg_indentBegin(" {\n");
      if (m_stmt) m_stmt->outputCPP(cg, ar);
      cg_indentEnd("};\n");
    }
    break;
  case CodeGenerator::CppImplementation:
    // do nothing
    break;
  case CodeGenerator::CppFFIDecl:
  case CodeGenerator::CppFFIImpl:
    // do nothing
    break;
  case CodeGenerator::JavaFFI:
    {
      // TODO support PHP namespaces, once HPHP supports it
      string packageName = Option::JavaFFIRootPackage;
      string packageDir = packageName;
      Util::replaceAll(packageDir, ".", "/");

      string outputDir = ar->getOutputPath() + "/" + Option::FFIFilePrefix +
        packageDir + "/";
      Util::mkdir(outputDir);

      // uses a different cg to generate a separate file for each PHP class
      string clsFile = outputDir + getOriginalName() + ".java";
      ofstream fcls(clsFile.c_str());
      CodeGenerator cgCls(&fcls, CodeGenerator::FileCPP);
      cgCls.setContext(CodeGenerator::JavaFFIInterface);

      cgCls.printf("package %s;\n\n", packageName.c_str());
      cgCls.printf("import hphp.*;\n\n");

      cgCls.printf("public interface %s", getOriginalName().c_str());
      if (m_base) {
        bool first = true;
        for (int i = 0; i < m_base->getCount(); i++) {
          ScalarExpressionPtr exp =
            dynamic_pointer_cast<ScalarExpression>((*m_base)[i]);
          const char *intf = exp->getString().c_str();
          ClassScopePtr intfClassScope = ar->findClass(intf);
          if (intfClassScope && classScope->derivesFrom(ar, intf, false, false)
           && intfClassScope->isUserClass()) {
            if (first) {
              cgCls.printf(" extends ");
              first = false;
            }
            else {
              cgCls.printf(", ");
            }
            cgCls.printf(intfClassScope->getOriginalName().c_str());
          }
        }
      }

      cgCls.indentBegin(" {\n");
      if (m_stmt) m_stmt->outputCPP(cgCls, ar);
      cgCls.indentEnd("}\n");

      fcls.close();
    }
    break;
  case CodeGenerator::JavaFFICppDecl:
  case CodeGenerator::JavaFFICppImpl:
    // do nothing
    break;
  default:
    ASSERT(false);
    break;
  }
}
Пример #20
0
void ClassScope::collectMethods(AnalysisResultPtr ar,
                                StringToFunctionScopePtrMap &funcs,
                                bool collectPrivate /* = true */,
                                bool forInvoke /* = false */) {
  // add all functions this class has
  for (FunctionScopePtrVec::const_iterator iter =
         m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
    const FunctionScopePtr &fs = *iter;
    if (!collectPrivate && fs->isPrivate()) continue;

    FunctionScopePtr &func = funcs[fs->getName()];
    if (!func) {
      func = fs;
    } else {
      func->setVirtual();
      fs->setVirtual();
      fs->setHasOverride();
      if (fs->isFinal()) {
        std::string s__MockClass = "__MockClass";
        ClassScopePtr derivedClass = func->getContainingClass();
        if (derivedClass->m_userAttributes.find(s__MockClass) ==
            derivedClass->m_userAttributes.end()) {
          Compiler::Error(Compiler::InvalidOverride,
                          fs->getStmt(), func->getStmt());
        }
      }
    }
  }

  int n = forInvoke ? m_parent.empty() ? 0 : 1 : m_bases.size();
  // walk up
  for (int i = 0; i < n; i++) {
    const string &base = m_bases[i];
    ClassScopePtr super = ar->findClass(base);
    if (super) {
      if (super->isRedeclaring()) {
        if (forInvoke) continue;

        const ClassScopePtrVec &classes = ar->findRedeclaredClasses(base);
        StringToFunctionScopePtrMap pristine(funcs);
        BOOST_FOREACH(ClassScopePtr cls, classes) {
          cls->m_derivedByDynamic = true;
          StringToFunctionScopePtrMap cur(pristine);
          derivedMagicMethods(cls);
          cls->collectMethods(ar, cur, false, forInvoke);
          inheritedMagicMethods(cls);
          funcs.insert(cur.begin(), cur.end());
          cls->getVariables()->
            forceVariants(ar, VariableTable::AnyNonPrivateVars);
        }

        if (base == m_parent) {
          m_derivesFromRedeclaring = DirectFromRedeclared;
          getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars,
                                        false);
          getVariables()->setAttribute(VariableTable::NeedGlobalPointer);
        } else if (isInterface()) {
          m_derivesFromRedeclaring = DirectFromRedeclared;
        }
        setVolatile();
      } else {
        derivedMagicMethods(super);
        super->collectMethods(ar, funcs, false, forInvoke);
        inheritedMagicMethods(super);
        if (super->derivesFromRedeclaring()) {
          if (base == m_parent) {
            m_derivesFromRedeclaring = IndirectFromRedeclared;
            getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars);
          } else if (isInterface()) {
            m_derivesFromRedeclaring = IndirectFromRedeclared;
          }
          setVolatile();
        } else if (super->isVolatile()) {
          setVolatile();
        }
      }
    } else {
Пример #21
0
void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) {
    m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name);
    m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name);

    VariableTablePtr variables = getScope()->getVariables();
    if (m_superGlobal) {
        variables->setAttribute(VariableTable::NeedGlobalPointer);
    } else if (m_name == "GLOBALS") {
        m_globals = true;
    } else {
        m_sym = variables->addDeclaredSymbol(
                    m_name,
                    Option::OutputHHBC ? shared_from_this() : ConstructPtr());
    }

    if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
        if (FunctionScopePtr func = getFunctionScope()) {
            if (m_name == "this" && (func->inPseudoMain() || getClassScope())) {
                func->setContainsThis();
                m_this = true;
                if (!hasContext(ObjectContext)) {
                    bool unset = hasAllContext(UnsetContext | LValue);
                    func->setContainsBareThis(
                        true,
                        hasAnyContext(RefValue | RefAssignmentLHS) ||
                        m_sym->isRefClosureVar() || unset);
                    if (variables->getAttribute(VariableTable::ContainsDynamicVariable)) {
                        ClassScopePtr cls = getClassScope();
                        TypePtr t = !cls || cls->isRedeclaring() ?
                                    Type::Variant : Type::CreateObjectType(cls->getName());
                        variables->add(m_sym, t, true, ar, shared_from_this(),
                                       getScope()->getModifiers());
                    }
                }
            }
            if (m_sym && !(m_context & AssignmentLHS) &&
                    !((m_context & UnsetContext) && (m_context & LValue))) {
                m_sym->setUsed();
            }
        }
    } else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
        if (m_sym) {
            if (!m_sym->isSystem() &&
                    !(getContext() &
                      (LValue|RefValue|RefParameter|UnsetContext|ExistContext)) &&
                    m_sym->getDeclaration().get() == this &&
                    !variables->getAttribute(VariableTable::ContainsLDynamicVariable) &&
                    !getScope()->is(BlockScope::ClassScope)) {
                if (getScope()->inPseudoMain()) {
                    Compiler::Error(Compiler::UseUndeclaredGlobalVariable,
                                    shared_from_this());
                } else if (!m_sym->isClosureVar()) {
                    Compiler::Error(Compiler::UseUndeclaredVariable, shared_from_this());
                }
            }
            // check function parameter that can occur in lval context
            if (m_sym->isParameter() &&
                    m_context & (LValue | RefValue | DeepReference |
                                 UnsetContext | InvokeArgument | OprLValue |
                                 DeepOprLValue)) {
                m_sym->setLvalParam();
            }
        }
        if (m_superGlobal || m_name == "GLOBALS") {
            FunctionScopePtr func = getFunctionScope();
            if (func) func->setNeedsCheckMem();
        }
    }
}
Пример #22
0
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;
  }
}
Пример #23
0
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
                                   ClassScopePtr classScope) {

  if (m_modifiers) {
    if (classScope->isInterface()) {
      if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
          m_modifiers->isAbstract()  || m_modifiers->isFinal()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Access type for interface method %s::%s() must be omitted",
          classScope->getOriginalName().c_str(), getOriginalName().c_str());
      }
    }
    if (m_modifiers->isAbstract()) {
      if (m_modifiers->isPrivate() || m_modifiers->isFinal()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Cannot declare abstract method %s::%s() %s",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str(),
          m_modifiers->isPrivate() ? "private" : "final");
      }
      if (!classScope->isInterface() && !classScope->isAbstract()) {
        /* note that classScope->isAbstract() returns true for traits */
        m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
                                    "Class %s contains abstract method %s and "
                                    "must therefore be declared abstract",
                                    classScope->getOriginalName().c_str(),
                                    getOriginalName().c_str());
      }
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Abstract method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
    }
  }
  if ((!m_modifiers || !m_modifiers->isAbstract()) &&
      !getStmts() && !classScope->isInterface()) {
    parseTimeFatal(Compiler::InvalidAttribute,
                   "Non-abstract method %s::%s() must contain body",
                   classScope->getOriginalName().c_str(),
                   getOriginalName().c_str());
  }

  FunctionScopeRawPtr fs = getFunctionScope();

  classScope->addFunction(ar, fs);

  m_className = classScope->getName();
  m_originalClassName = classScope->getOriginalName();

  setSpecialMethod(classScope);

  if (Option::DynamicInvokeFunctions.find(getFullName()) !=
      Option::DynamicInvokeFunctions.end()) {
    fs->setDynamicInvoke();
  }
  if (m_params) {
    for (int i = 0; i < m_params->getCount(); i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      param->parseHandler(classScope);
    }
  }
  FunctionScope::RecordFunctionInfo(m_name, fs);
}
Пример #24
0
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) {
  ClassScopeRawPtr classScope = getClassScope();
  if (cg.getContext() == CodeGenerator::NoContext) {
    if (classScope->isVolatile()) {
      string name = CodeGenerator::FormatLabel(m_name);
      if (classScope->isRedeclaring()) {
        cg_printf("g->%s%s = &%s%s;\n",
                  Option::ClassStaticsCallbackPrefix,
                  name.c_str(),
                  Option::ClassStaticsCallbackPrefix,
                  classScope->getId().c_str());
      }
      cg_printf("g->CDEC(%s) = true;\n", name.c_str());

      const vector<string> &bases = classScope->getBases();
      for (vector<string>::const_iterator it = bases.begin();
           it != bases.end(); ++it) {
        if (cg.checkHoistedClass(*it)) continue;
        ClassScopePtr base = ar->findClass(*it);
        if (base && base->isVolatile()) {
          cg_printf("checkClassExistsThrow(");
          cg_printString(base->getOriginalName(), ar, shared_from_this());
          string lname = Util::toLower(base->getOriginalName());
          cg_printf(", &%s->CDEC(%s));\n",
                    cg.getGlobals(ar),
                    CodeGenerator::FormatLabel(lname).c_str());
        }
      }
    }
    return;
  }

  if (cg.getContext() != CodeGenerator::CppForwardDeclaration) {
    printSource(cg);
  }

  string clsNameStr = classScope->getId();
  const char *clsName = clsNameStr.c_str();

  switch (cg.getContext()) {
  case CodeGenerator::CppDeclaration:
    {
      if (Option::GenerateCPPMacros) {
        classScope->outputForwardDeclaration(cg);
      }
      classScope->outputCPPGlobalTableWrappersDecl(cg, ar);

      bool system = cg.getOutput() == CodeGenerator::SystemCPP;
      ClassScopePtr parCls;
      if (!m_parent.empty()) {
        parCls = ar->findClass(m_parent);
        if (parCls && parCls->isRedeclaring()) parCls.reset();
      }
      if (Option::GenerateCppLibCode) {
        cg.printDocComment(classScope->getDocComment());
      }
      cg_printf("class %s%s", Option::ClassPrefix, clsName);
      if (!m_parent.empty() && classScope->derivesDirectlyFrom(m_parent)) {
        if (!parCls) {
          cg_printf(" : public DynamicObjectData");
        } else {
          cg_printf(" : public %s%s", Option::ClassPrefix,
                    parCls->getId().c_str());
        }
      } else {
        if (classScope->derivesFromRedeclaring()) {
          cg_printf(" : public DynamicObjectData");
        } else if (system) {
          cg_printf(" : public ExtObjectData");
        } else {
          cg_printf(" : public ObjectData");
        }
      }
      if (m_base && Option::UseVirtualDispatch) {
        for (int i = 0; i < m_base->getCount(); i++) {
          ScalarExpressionPtr exp =
            dynamic_pointer_cast<ScalarExpression>((*m_base)[i]);
          const char *intf = exp->getString().c_str();
          ClassScopePtr intfClassScope = ar->findClass(intf);
          if (intfClassScope && !intfClassScope->isRedeclaring() &&
              classScope->derivesDirectlyFrom(intf) &&
              (!parCls || !parCls->derivesFrom(ar, intf, true, false))) {
            string id = intfClassScope->getId();
            cg_printf(", public %s%s", Option::ClassPrefix, id.c_str());
          }
        }
      }
      cg_indentBegin(" {\n");
      cg_printf("public:\n");

      cg.printSection("Properties");
      classScope->getVariables()->outputCPPPropertyDecl(cg, ar,
          classScope->derivesFromRedeclaring());

      if (Option::GenerateCppLibCode) {
        cg.printSection("Methods");
        classScope->outputMethodWrappers(cg, ar);
        cg.printSection(">>>>>>>>>> Internal Implementation <<<<<<<<<<");
        cg_printf("// NOTE: Anything below is subject to change. "
                  "Use everything above instead.\n");
      }

      cg.printSection("Class Map");

      bool hasEmitCppCtor = false;
      bool needsCppCtor = classScope->needsCppCtor();
      bool needsInit    = classScope->needsInitMethod();

      if (Option::GenerateCPPMacros) {
        bool dyn = (!parCls && !m_parent.empty()) ||
          classScope->derivesFromRedeclaring() ==
          ClassScope::DirectFromRedeclared;
        bool idyn = parCls && classScope->derivesFromRedeclaring() ==
          ClassScope::IndirectFromRedeclared;
        bool redec = classScope->isRedeclaring();
        if (!classScope->derivesFromRedeclaring()) {
          outputCPPClassDecl(cg, ar, clsName, m_originalName.c_str(),
                             parCls ? parCls->getId().c_str()
                                    : "ObjectData");
        } else {
          cg_printf("DECLARE_DYNAMIC_CLASS(%s, %s, %s)\n", clsName,
                    m_originalName.c_str(),
                    dyn || !parCls ? "DynamicObjectData" :
                    parCls->getId().c_str());
        }

        if (classScope->checkHasPropTable()) {
          cg_printf("static const ClassPropTable %sprop_table;\n",
                    Option::ObjectStaticPrefix);
        }

        bool hasGet = classScope->getAttribute(
          ClassScope::HasUnknownPropGetter);
        bool hasSet = classScope->getAttribute(
          ClassScope::HasUnknownPropSetter);
        bool hasCall = classScope->getAttribute(
          ClassScope::HasUnknownMethodHandler);
        bool hasCallStatic = classScope->getAttribute(
          ClassScope::HasUnknownStaticMethodHandler);

        if (dyn || idyn || redec || hasGet || hasSet ||
            hasCall || hasCallStatic) {
          if (redec && classScope->derivedByDynamic()) {
            if (!dyn && !idyn) {
              cg_printf("private: ObjectData* root;\n");
              cg_printf("public:\n");
              cg_printf("virtual ObjectData *getRoot() { return root; }\n");
            }
          }

          string conInit = "";
          bool hasParam = false;
          bool needsLateInit = false;
          if (dyn) {
            conInit = " : DynamicObjectData(\"" + m_parent + "\", r)";
            hasParam = true;
          } else if (idyn) {
            conInit = " : " + string(Option::ClassPrefix) + parCls->getId() +
              "(r ? r : this)";
            hasParam = true;
          } else {
            if (redec && classScope->derivedByDynamic()) {
              conInit = "root(r ? r : this)";
              needsLateInit = true;
            }
            hasParam = true;
          }

          // this dance around the initialization list is to make
          // sure members get set in the right order - since properties
          // come first, and root comes later, we need to init the
          // properties first, and then root.
          if (needsLateInit) {
            cg_printf("%s%s(%s) : ",
                      Option::ClassPrefix,
                      clsName,
                      hasParam ? "ObjectData* r = NULL" : "");
            if (needsCppCtor) {
              cg.setContext(CodeGenerator::CppConstructor);
              ASSERT(!cg.hasInitListFirstElem());
              m_stmt->outputCPP(cg, ar);
              cg.clearInitListFirstElem();
              cg.setContext(CodeGenerator::CppDeclaration);
              cg_printf(", ");
            }
            cg_printf("%s", conInit.c_str());
          } else {
            cg_printf("%s%s(%s)%s%s",
                      Option::ClassPrefix,
                      clsName,
                      hasParam ? "ObjectData* r = NULL" : "",
                      conInit.c_str(),
                      needsCppCtor ? conInit.empty() ? " : " : ", " : "");
            if (needsCppCtor) {
              cg.setContext(CodeGenerator::CppConstructor);
              ASSERT(!cg.hasInitListFirstElem());
              m_stmt->outputCPP(cg, ar);
              cg.clearInitListFirstElem();
              cg.setContext(CodeGenerator::CppDeclaration);
            }
          }

          cg_indentBegin(" {%s",
                         hasGet || hasSet || hasCall || hasCallStatic ?
                         "\n" : "");
          if (hasGet) cg_printf("setAttribute(UseGet);\n");
          if (hasSet) cg_printf("setAttribute(UseSet);\n");
          if (hasCall) cg_printf("setAttribute(HasCall);\n");
          if (hasCallStatic) cg_printf("setAttribute(HasCallStatic);\n");
          cg_indentEnd("}\n");
          hasEmitCppCtor = true;
        }
      }

      if (needsCppCtor && !hasEmitCppCtor) {
        cg_printf("%s%s() : ", Option::ClassPrefix, clsName);
        cg.setContext(CodeGenerator::CppConstructor);
        ASSERT(!cg.hasInitListFirstElem());
        m_stmt->outputCPP(cg, ar);
        cg.clearInitListFirstElem();
        cg.setContext(CodeGenerator::CppDeclaration);
        cg_printf(" {}\n");
      }

      if (needsInit) {
        cg_printf("void init();\n");
      }

      if (!classScope->getAttribute(ClassScope::HasConstructor)) {
        FunctionScopePtr func = classScope->findFunction(ar, "__construct",
                                                         false);
        if (func && !func->isAbstract() && !classScope->isInterface()) {
          func->outputCPPCreateDecl(cg, ar);
        }
      }

      // doCall
      if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) {
        cg_printf("Variant doCall(Variant v_name, Variant v_arguments, "
                  "bool fatal);\n");
      }

      if (classScope->getAttribute(ClassScope::HasInvokeMethod)) {
        FunctionScopePtr func =
          classScope->findFunction(ar, "__invoke", false);
        ASSERT(func);
        if (!func->isAbstract()) {
          cg_printf("const CallInfo *"
                    "t___invokeCallInfoHelper(void *&extra);\n");
        }
      }

      if (classScope->isRedeclaring() &&
          !classScope->derivesFromRedeclaring() &&
          classScope->derivedByDynamic()) {
        cg_printf("Variant doRootCall(Variant v_name, Variant v_arguments, "
                  "bool fatal);\n");
      }

      if (m_stmt) m_stmt->outputCPP(cg, ar);
      {
        set<string> done;
        classScope->outputCPPStaticMethodWrappers(cg, ar, done, clsName);
      }
      if (Option::GenerateCPPMacros) {
        classScope->outputCPPJumpTableDecl(cg, ar);
      }
      cg_indentEnd("};\n");

      classScope->outputCPPDynamicClassDecl(cg);

      if (m_stmt) {
        cg.setContext(CodeGenerator::CppClassConstantsDecl);
        m_stmt->outputCPP(cg, ar);
        cg.setContext(CodeGenerator::CppDeclaration);
      }
    }
    break;
  case CodeGenerator::CppImplementation:
    {
      if (m_stmt) {
        cg.setContext(CodeGenerator::CppClassConstantsImpl);
        m_stmt->outputCPP(cg, ar);
        cg.setContext(CodeGenerator::CppImplementation);
      }

      classScope->outputCPPSupportMethodsImpl(cg, ar);

      bool needsInit = classScope->needsInitMethod();
      if (needsInit) {
        cg_indentBegin("void %s%s::init() {\n",
                       Option::ClassPrefix, clsName);
        if (!m_parent.empty()) {
          if (classScope->derivesFromRedeclaring() ==
              ClassScope::DirectFromRedeclared) {
            cg_printf("parent->init();\n");
          } else {
            ClassScopePtr parCls = ar->findClass(m_parent);
            cg_printf("%s%s::init();\n", Option::ClassPrefix,
                      parCls->getId().c_str());
          }
        }
        if (classScope->getVariables()->
            getAttribute(VariableTable::NeedGlobalPointer)) {
          cg.printDeclareGlobals();
        }
        cg.setContext(CodeGenerator::CppInitializer);
        if (m_stmt) m_stmt->outputCPP(cg, ar);

        // This is lame. Exception base class needs to prepare stacktrace
        // outside of its PHP constructor. Every subclass of exception also
        // needs this stacktrace, so we're adding an artificial __init__ in
        // exception.php and calling it here.
        if (m_name == "exception") {
          cg_printf("{CountableHelper h(this); t___init__();}\n");
        }

        cg_indentEnd("}\n");
      }

      cg.setContext(CodeGenerator::CppImplementation);
      if (m_stmt) m_stmt->outputCPP(cg, ar);
    }
    break;
  case CodeGenerator::CppFFIDecl:
  case CodeGenerator::CppFFIImpl:
    if (m_stmt) m_stmt->outputCPP(cg, ar);
    break;
  case CodeGenerator::JavaFFI:
    {
      if (classScope->isRedeclaring()) break;

      // TODO support PHP namespaces, once HPHP supports it
      string packageName = Option::JavaFFIRootPackage;
      string packageDir = packageName;
      Util::replaceAll(packageDir, ".", "/");

      string outputDir = ar->getOutputPath() + "/" + Option::FFIFilePrefix +
        packageDir + "/";
      Util::mkdir(outputDir);

      // uses a different cg to generate a separate file for each PHP class
      // also, uses the original capitalized class name
      string clsFile = outputDir + getOriginalName() + ".java";
      ofstream fcls(clsFile.c_str());
      CodeGenerator cgCls(&fcls, CodeGenerator::FileCPP);
      cgCls.setContext(CodeGenerator::JavaFFI);

      cgCls.printf("package %s;\n\n", packageName.c_str());
      cgCls.printf("import hphp.*;\n\n");

      printSource(cgCls);

      string clsModifier;
      switch (m_type) {
      case T_CLASS:
        break;
      case T_ABSTRACT:
        clsModifier = "abstract ";
        break;
      case T_FINAL:
        clsModifier = "final ";
        break;
      }
      cgCls.printf("public %sclass %s ", clsModifier.c_str(),
                   getOriginalName().c_str());

      ClassScopePtr parCls;
      if (!m_parent.empty()) parCls = ar->findClass(m_parent);
      if (!m_parent.empty() && classScope->derivesDirectlyFrom(m_parent)
          && parCls && parCls->isUserClass() && !parCls->isRedeclaring()) {
        // system classes are not supported in static FFI translation
        // they shouldn't appear as superclasses as well
        cgCls.printf("extends %s", parCls->getOriginalName().c_str());
      }
      else {
        cgCls.printf("extends HphpObject");
      }
      if (m_base) {
        bool first = true;
        for (int i = 0; i < m_base->getCount(); i++) {
          ScalarExpressionPtr exp =
            dynamic_pointer_cast<ScalarExpression>((*m_base)[i]);
          const char *intf = exp->getString().c_str();
          ClassScopePtr intfClassScope = ar->findClass(intf);
          if (intfClassScope && classScope->derivesFrom(ar, intf, false, false)
           && intfClassScope->isUserClass()) {
            if (first) {
              cgCls.printf(" implements ");
              first = false;
            }
            else {
              cgCls.printf(", ");
            }
            cgCls.printf(intfClassScope->getOriginalName().c_str());
          }
        }
      }

      cgCls.indentBegin(" {\n");

      // constructor for initializing the variant pointer
      cgCls.printf("protected %s(long ptr) { super(ptr); }\n\n",
                   getOriginalName().c_str());

      FunctionScopePtr cons = classScope->findConstructor(ar, true);
      if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) {
        // if not an abstract class and not having an explicit constructor,
        // adds a default constructor
        outputJavaFFIConstructor(cgCls, ar, cons);
      }

      if (m_stmt) m_stmt->outputCPP(cgCls, ar);
      cgCls.indentEnd("}\n");

      fcls.close();
    }
    break;
  case CodeGenerator::JavaFFICppDecl:
  case CodeGenerator::JavaFFICppImpl:
    {
      if (classScope->isRedeclaring()) break;

      if (m_stmt) m_stmt->outputCPP(cg, ar);
      FunctionScopePtr cons = classScope->findConstructor(ar, true);
      if (cons && !cons->isAbstract() || m_type != T_ABSTRACT) {
        outputJavaFFICPPCreator(cg, ar, cons);
      }
    }
    break;
  default:
    ASSERT(false);
    break;
  }
}
Пример #25
0
bool ClassScope::NeedStaticArray(ClassScopePtr cls, FunctionScopePtr func) {
  return cls && cls->getAttribute(NotFinal) && !func->isPrivate();
}
Пример #26
0
void NewObjectExpression::outputCPPImpl(CodeGenerator &cg,
                                        AnalysisResultPtr ar) {
  bool linemap = outputLineMap(cg, ar, true);
  bool outsideClass = !ar->checkClassPresent(m_origName);
  if (!m_name.empty() && !m_redeclared && m_validClass && !m_dynamic) {
    bool tooManyArgs =
      (m_params && m_params->outputCPPTooManyArgsPre(cg, ar, m_name));
    ClassScopePtr cls = ar->resolveClass(m_name);
    ASSERT(cls);
    if (m_receiverTemp.empty()) {
      if (outsideClass) {
        cls->outputVolatileCheckBegin(cg, ar, m_origName);
      }
      cg.printf("%s%s((NEWOBJ(%s%s)())->create(",
                Option::SmartPtrPrefix, m_name.c_str(),
                Option::ClassPrefix, m_name.c_str());
    } else {
      cg.printf("%s%s(%s->create(",
                Option::SmartPtrPrefix, m_name.c_str(),
                m_receiverTemp.c_str());
    }

    FunctionScope::outputCPPArguments(m_params, cg, ar, m_extraArg,
                                      m_variableArgument, m_argArrayId);
    cg.printf("))");
    if (m_receiverTemp.empty()) {
      if (outsideClass) {
        cls->outputVolatileCheckEnd(cg);
      }
    }
    if (tooManyArgs) {
      m_params->outputCPPTooManyArgsPost(cg, ar, m_voidReturn);
    }
  } else {
    if (m_redeclared) {
      if (outsideClass) {
        ClassScope::OutputVolatileCheckBegin(cg, ar, m_origName);
      }
      cg.printf("g->%s%s->create(", Option::ClassStaticsObjectPrefix,
                m_name.c_str());
    } else {
      cg.printf("create_object(");
      if (!m_name.empty()) {
        cg.printf("\"%s\"", m_name.c_str());
      } else if (m_nameExp->is(Expression::KindOfSimpleVariable)) {
        m_nameExp->outputCPP(cg, ar);
      } else {
        cg.printf("(");
        m_nameExp->outputCPP(cg, ar);
        cg.printf(")");
      }
      cg.printf(", ");
    }
    if (m_params && m_params->getOutputCount()) {
      FunctionScope::outputCPPArguments(m_params, cg, ar, -1, false);
    } else {
      cg.printf("Array()");
    }
    cg.printf(")");
    if (m_redeclared && outsideClass) {
      ClassScope::OutputVolatileCheckEnd(cg);
    }
  }
  if (linemap) cg.printf(")");
}
Пример #27
0
void ClassScope::importUsedTraits(AnalysisResultPtr ar) {
  // Trait flattening is supposed to happen only when we have awareness of the
  // whole program.
  assert(Option::WholeProgram);

  if (m_traitStatus == FLATTENED) return;
  if (m_traitStatus == BEING_FLATTENED) {
    getStmt()->analysisTimeFatal(
      Compiler::CyclicDependentTraits,
      "Cyclic dependency between traits involving %s",
      getOriginalName().c_str()
    );
    return;
  }
  if (m_usedTraitNames.size() == 0) {
    m_traitStatus = FLATTENED;
    return;
  }
  m_traitStatus = BEING_FLATTENED;

  m_numDeclMethods = m_functionsVec.size();

  // First, make sure that parent classes have their traits imported.
  if (!m_parent.empty()) {
    ClassScopePtr parent = ar->findClass(m_parent);
    if (parent) {
      parent->importUsedTraits(ar);
    }
  }

  TMIData tmid;

  if (isTrait()) {
    for (auto const& req : getClassRequiredExtends()) {
      ClassScopePtr rCls = ar->findClass(req);
      if (!rCls || rCls->isFinal() || rCls->isInterface()) {
        getStmt()->analysisTimeFatal(
          Compiler::InvalidDerivation,
          Strings::TRAIT_BAD_REQ_EXTENDS,
          m_originalName.c_str(),
          req.c_str(),
          req.c_str()
        );
      }
    }
    for (auto const& req : getClassRequiredImplements()) {
      ClassScopePtr rCls = ar->findClass(req);
      if (!rCls || !(rCls->isInterface())) {
        getStmt()->analysisTimeFatal(
          Compiler::InvalidDerivation,
          Strings::TRAIT_BAD_REQ_IMPLEMENTS,
          m_originalName.c_str(),
          req.c_str(),
          req.c_str()
        );
      }
    }
  }

  // Find trait methods to be imported.
  for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
    ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
    if (!tCls || !(tCls->isTrait())) {
      setAttribute(UsesUnknownTrait); // XXX: is this useful ... for anything?
      getStmt()->analysisTimeFatal(
        Compiler::UnknownTrait,
        Strings::TRAITS_UNKNOWN_TRAIT,
        m_usedTraitNames[i].c_str()
      );
    }

    // First, make sure the used trait is flattened.
    tCls->importUsedTraits(ar);

    findTraitMethodsToImport(ar, tCls, tmid);

    // Import any interfaces implemented.
    tCls->getInterfaces(ar, m_bases, /* recursive */ false);

    importClassRequirements(ar, tCls);
  }

  // Apply rules.
  applyTraitRules(tmid);

  // Remove methods declared on the current class from the trait import list;
  // the class methods take precedence.
  for (auto const& methName : tmid.methodNames()) {
    if (findFunction(ar, methName, false /* recursive */,
                     false /* exclIntfBase */)) {
      // This does not affect the methodNames() vector.
      tmid.erase(methName);
    }
  }

  auto traitMethods = tmid.finish(this);

  std::map<string, MethodStatementPtr> importedTraitMethods;
  std::vector<std::pair<string,const TraitMethod*>> importedTraitsWithOrigName;

  // Actually import the methods.
  for (auto const& mdata : traitMethods) {
    if ((mdata.tm.modifiers ? mdata.tm.modifiers
                            : mdata.tm.method->getModifiers()
        )->isAbstract()) {
      // Skip abstract methods, if the method already exists in the class.
      if (findFunction(ar, mdata.name, true) ||
          importedTraitMethods.count(mdata.name)) {
        continue;
      }
    }

    auto sourceName = mdata.tm.ruleStmt
      ? toLower(
          ((TraitAliasStatement*)mdata.tm.ruleStmt.get())->getMethodName())
      : mdata.name;

    importedTraitMethods[sourceName] = MethodStatementPtr();
    importedTraitsWithOrigName.push_back(
      std::make_pair(sourceName, &mdata.tm));
  }

  // Make sure there won't be 2 constructors after importing
  auto traitConstruct = importedTraitMethods.count("__construct");
  auto traitName = importedTraitMethods.count(getName());
  auto classConstruct = m_functions.count("__construct");
  auto className = m_functions.count(getName());
  if ((traitConstruct && traitName) ||
      (traitConstruct && className) ||
      (classConstruct && traitName)) {
    getStmt()->analysisTimeFatal(
      Compiler::InvalidDerivation,
      "%s has colliding constructor definitions coming from traits",
      getOriginalName().c_str()
    );
  }

  for (auto const& traitPair : importedTraitsWithOrigName) {
    auto traitMethod = traitPair.second;

    MethodStatementPtr newMeth = importTraitMethod(
      *traitMethod,
      ar,
      toLower(traitMethod->originalName)
    );
  }

  // Import trait properties
  importTraitProperties(ar);

  m_traitStatus = FLATTENED;
}
Пример #28
0
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
                                   ClassScopePtr classScope) {

  FunctionScopeRawPtr fs = getFunctionScope();
  const bool isNative = fs->isNative();
  if (m_modifiers) {
    if ((m_modifiers->isExplicitlyPublic() +
         m_modifiers->isProtected() +
         m_modifiers->isPrivate()) > 1) {
      m_modifiers->parseTimeFatal(
        Compiler::InvalidAttribute,
        Strings::PICK_ACCESS_MODIFIER
      );
    }

    if (m_modifiers->hasDuplicates()) {
      m_modifiers->parseTimeFatal(
        Compiler::InvalidAttribute,
        Strings::PICK_ACCESS_MODIFIER);
    }

    if (classScope->isInterface()) {
      if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
          m_modifiers->isAbstract()  || m_modifiers->isFinal() ||
          isNative) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Access type for interface method %s::%s() must be omitted",
          classScope->getOriginalName().c_str(), getOriginalName().c_str());
      }
      if (m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "interface", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
      if (getStmts()) {
        getStmts()->parseTimeFatal(
          Compiler::InvalidMethodDefinition,
          "Interface method %s::%s() cannot contain body",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str());
      }
    }
    if (m_modifiers->isAbstract()) {
      if (m_modifiers->isPrivate() || m_modifiers->isFinal() || isNative) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Cannot declare abstract method %s::%s() %s",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str(),
          m_modifiers->isPrivate() ? "private" :
           (m_modifiers->isFinal() ? "final" : "native"));
      }
      if (!classScope->isInterface() && !classScope->isAbstract()) {
        /* note that classScope->isAbstract() returns true for traits */
        m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
                                    "Class %s contains abstract method %s and "
                                    "must therefore be declared abstract",
                                    classScope->getOriginalName().c_str(),
                                    getOriginalName().c_str());
      }
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Abstract method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      if (m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "abstract", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
    }
    if (!m_modifiers->isStatic() && classScope->isStaticUtil()) {
      m_modifiers->parseTimeFatal(
        Compiler::InvalidAttribute,
        "Class %s contains non-static method %s and "
        "therefore cannot be declared 'abstract final'",
        classScope->getOriginalName().c_str(),
        getOriginalName().c_str()
      );
    }

    if (isNative) {
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      auto is_ctordtor = (m_name == "__construct") || (m_name == "__destruct");
      if (!m_retTypeAnnotation && !is_ctordtor) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method %s::%s() must have a return type hint",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      } else if (m_retTypeAnnotation &&
                 is_ctordtor &&
                (m_retTypeAnnotation->dataType() != KindOfNull)) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method %s::%s() must return void",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
    }
  }
  if ((!m_modifiers || !m_modifiers->isAbstract()) &&
      !getStmts() && !classScope->isInterface() && !isNative) {
    parseTimeFatal(Compiler::InvalidAttribute,
                   "Non-abstract method %s::%s() must contain body",
                   classScope->getOriginalName().c_str(),
                   getOriginalName().c_str());
  }

  classScope->addFunction(ar, fs);

  m_className = classScope->getName();
  m_originalClassName = classScope->getOriginalName();

  setSpecialMethod(classScope);

  if (Option::DynamicInvokeFunctions.find(getFullName()) !=
      Option::DynamicInvokeFunctions.end()) {
    fs->setDynamicInvoke();
  }
  if (m_params) {
    auto nParams = m_params->getCount();
    for (int i = 0; i < nParams; i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      param->parseHandler(classScope);
      // Variadic capture params don't need types because they'll
      // be treated as Arrays as far as HNI is concerned.
      if (isNative && !param->hasUserType() && !param->isVariadic()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method calls must have type hints on all args");
      }
    }
  }
  FunctionScope::RecordFunctionInfo(m_name, fs);
}
Пример #29
0
TypePtr Type::Intersection(AnalysisResultPtr ar, TypePtr from, TypePtr to) {
  // Special case: if we're casting to Some or Any, return the "from" type;
  // if we're casting to Variant, return Variant.
  if (to->m_kindOf == KindOfSome || to->m_kindOf == KindOfAny) {
    return from;
  } else if (to->m_kindOf == KindOfVariant) {
    return Variant;
  }

  int resultKind = to->m_kindOf & from->m_kindOf;
  std::string resultName = "";

  if (resultKind & KindOfObject) {
    // if they're the same, or we don't know one's name, then use
    // the other
    if (to->m_name == from->m_name || from->m_name.empty()) {
      resultName = to->m_name;
    } else if (to->m_name.empty()) {
      resultName = from->m_name;
    } else {
      // make sure there's a subclass relation
      ClassScopePtr cls = ar->findClass(from->m_name);
      if (cls) {
        if (cls->derivesFrom(ar, to->m_name, true, false)) {
          resultName = to->m_name;
        } else {
          resultKind &= ~KindOfObject;
        }
      }
    }
  }

  TypePtr res;

  // If there is overlap (for instance, they were the same, or we've narrowed
  // down something like Sequenece to be more specific), then return the
  // intersection of the types.
  if (resultKind) {
    res = TypePtr(new Type((KindOf)resultKind, resultName));
  } else if (from->mustBe(KindOfObject) && to->m_kindOf == KindOfPrimitive) {
    // Special case Object -> Primitive: can we tostring it?
    if (!from->m_name.empty()) {
      ClassScopePtr cls = ar->findClass(from->m_name);
      if (cls && cls->findFunction(ar, "__tostring", true)) {
        res = Type::String;
      }
    }

    // Otherwise, return Byte
    res = Byte;
  } else if (from->m_kindOf == KindOfBoolean
             && to->mustBe(KindOfNumeric | KindOfArray | KindOfString)
             && !IsExactType(to->m_kindOf)) {
    res = Byte;
  } else {
    res = to;
  }

  if (from->mustBe(KindOfBoolean) && to->m_kindOf == KindOfPrimitive) {
    res = Byte;
  }

  return res;
}
Пример #30
0
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
                                   ClassScopePtr classScope) {

  FunctionScopeRawPtr fs = getFunctionScope();
  const bool isNative = fs->isNative();
  if (m_modifiers) {
    if ((m_modifiers->isExplicitlyPublic() +
         m_modifiers->isProtected() +
         m_modifiers->isPrivate()) > 1) {
      m_modifiers->parseTimeFatal(
        Compiler::InvalidAttribute,
        "%s: method %s::%s()",
        Strings::PICK_ACCESS_MODIFIER,
        classScope->getOriginalName().c_str(),
        getOriginalName().c_str()
      );
    }

    if (classScope->isInterface()) {
      if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
          m_modifiers->isAbstract()  || m_modifiers->isFinal() ||
          isNative) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Access type for interface method %s::%s() must be omitted",
          classScope->getOriginalName().c_str(), getOriginalName().c_str());
      }
      if (m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "interface", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
    }
    if (m_modifiers->isAbstract()) {
      if (m_modifiers->isPrivate() || m_modifiers->isFinal() || isNative) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          "Cannot declare abstract method %s::%s() %s",
          classScope->getOriginalName().c_str(),
          getOriginalName().c_str(),
          m_modifiers->isPrivate() ? "private" :
           (m_modifiers->isFinal() ? "final" : "native"));
      }
      if (!classScope->isInterface() && !classScope->isAbstract()) {
        /* note that classScope->isAbstract() returns true for traits */
        m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
                                    "Class %s contains abstract method %s and "
                                    "must therefore be declared abstract",
                                    classScope->getOriginalName().c_str(),
                                    getOriginalName().c_str());
      }
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Abstract method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      if (m_modifiers->isAsync()) {
        m_modifiers->parseTimeFatal(
          Compiler::InvalidAttribute,
          Strings::ASYNC_WITHOUT_BODY,
          "abstract", classScope->getOriginalName().c_str(),
          getOriginalName().c_str()
        );
      }
    }
    if (isNative) {
      if (getStmts()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method %s::%s() cannot contain body",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
      if (!m_retTypeAnnotation) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method %s::%s() must have a return type hint",
                       classScope->getOriginalName().c_str(),
                       getOriginalName().c_str());
      }
    }
  }
  if ((!m_modifiers || !m_modifiers->isAbstract()) &&
      !getStmts() && !classScope->isInterface() && !isNative) {
    parseTimeFatal(Compiler::InvalidAttribute,
                   "Non-abstract method %s::%s() must contain body",
                   classScope->getOriginalName().c_str(),
                   getOriginalName().c_str());
  }

  classScope->addFunction(ar, fs);

  m_className = classScope->getName();
  m_originalClassName = classScope->getOriginalName();

  setSpecialMethod(classScope);

  if (Option::DynamicInvokeFunctions.find(getFullName()) !=
      Option::DynamicInvokeFunctions.end()) {
    fs->setDynamicInvoke();
  }
  if (m_params) {
    for (int i = 0; i < m_params->getCount(); i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      param->parseHandler(classScope);
      if (isNative && !param->hasUserType()) {
        parseTimeFatal(Compiler::InvalidAttribute,
                       "Native method calls must have type hints on all args");
      }
    }
  }
  FunctionScope::RecordFunctionInfo(m_name, fs);
}