void SimpleVariable::analyzeProgram(AnalysisResultPtr ar) { Expression::analyzeProgram(ar); if (m_name == "argc" || m_name == "argv") { // special case: they are NOT superglobals when not in global scope if (ar->getScope() == ar) { m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name); m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name); } } else { m_superGlobal = BuiltinSymbols::IsSuperGlobal(m_name); m_superGlobalType = BuiltinSymbols::GetSuperGlobalType(m_name); } if (m_superGlobal) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } if (m_name == "this" && ar->getClassScope()) { FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(ar->getScope()); func->setContainsThis(); m_this = true; } else if (m_name == "GLOBALS") { m_globals = true; } if (!(m_context & AssignmentLHS)) { BlockScopePtr scope = ar->getScope(); FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(scope); if (func) { func->getVariables()->addUsed(m_name); } } }
void MethodStatement::addParamRTTI(AnalysisResultPtr ar) { FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(ar->getScope()); VariableTablePtr variables = func->getVariables(); if (variables->getAttribute(VariableTable::ContainsDynamicVariable) || variables->getAttribute(VariableTable::ContainsExtract)) { return; } for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); const string ¶mName = param->getName(); if (variables->isLvalParam(paramName)) continue; TypePtr paramType = param->getActualType(); if ((paramType->is(Type::KindOfVariant) || paramType->is(Type::KindOfSome)) && !param->isRef()) { param->setHasRTTI(); ClassScopePtr cls = ar->getClassScope(); ar->addParamRTTIEntry(cls, func, paramName); const string funcId = ar->getFuncId(cls, func); ar->addRTTIFunction(funcId); } } }
TypePtr AssignmentExpression:: inferTypesImpl(AnalysisResultPtr ar, TypePtr type, bool coerce, ExpressionPtr variable, ExpressionPtr value /* = ExpressionPtr() */) { TypePtr ret = type; if (value) { if (coerce) { ret = value->inferAndCheck(ar, type, coerce); } else { ret = value->inferAndCheck(ar, NEW_TYPE(Some), coerce); } } BlockScopePtr scope = ar->getScope(); if (variable->is(Expression::KindOfConstantExpression)) { // ...as in ClassConstant statement ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(variable); bool p; scope->getConstants()->check(exp->getName(), ret, true, ar, variable, p); } else if (variable->is(Expression::KindOfDynamicVariable)) { // simptodo: not too sure about this ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable); } else if (variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); if (var->getName() == "this" && ar->getClassScope()) { if (ar->isFirstPass()) { ar->getCodeError()->record(variable, CodeError::ReassignThis, variable); } } if (ar->getPhase() == AnalysisResult::LastInference && value) { if (!value->getExpectedType()) { value->setExpectedType(variable->getActualType()); } } } // if the value may involve object, consider the variable as "referenced" // so that objects are not destructed prematurely. bool referenced = true; if (value && value->isScalar()) referenced = false; if (ret && ret->isNoObjectInvolved()) referenced = false; if (referenced && variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); const std::string &name = var->getName(); VariableTablePtr variables = ar->getScope()->getVariables(); variables->addReferenced(name); } TypePtr vt = variable->inferAndCheck(ar, ret, true); if (!coerce && type->is(Type::KindOfAny)) { ret = vt; } return ret; }
void ListAssignment::outputCPPAssignment(CodeGenerator &cg, AnalysisResultPtr ar, const string &arrTmp) { if (!m_variables) return; for (int i = m_variables->getCount() - 1; i >= 0; --i) { ExpressionPtr exp = (*m_variables)[i]; if (exp) { if (exp->is(Expression::KindOfListAssignment)) { ListAssignmentPtr sublist = dynamic_pointer_cast<ListAssignment>(exp); string subTmp = genCPPTemp(cg, ar); cg_printf("Variant %s((ref(%s[%d])));\n", subTmp.c_str(), arrTmp.c_str(), i); sublist->outputCPPAssignment(cg, ar, subTmp); } else { bool done = false; if (exp->is(Expression::KindOfArrayElementExpression)) { ArrayElementExpressionPtr arrExp = dynamic_pointer_cast<ArrayElementExpression>(exp); if (!arrExp->isSuperGlobal() && !arrExp->isDynamicGlobal()) { arrExp->getVariable()->outputCPP(cg, ar); if (arrExp->getOffset()) { cg_printf(".set("); arrExp->getOffset()->outputCPP(cg, ar); cg_printf(", "); } else { cg_printf(".append("); } cg_printf("%s[%d]);\n", arrTmp.c_str(), i); done = true; } } else if (exp->is(Expression::KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast<ObjectPropertyExpression>(exp)); if (!var->isValid()) { var->outputCPPObject(cg, ar); cg_printf("o_set("); var->outputCPPProperty(cg, ar); cg_printf(", %s[%d], %s);\n", arrTmp.c_str(), i, ar->getClassScope() ? "s_class_name" : "empty_string"); done = true; } } if (!done) { exp->outputCPP(cg, ar); if (arrTmp == "null") { cg_printf(" = null;\n"); } else { cg_printf(" = %s[%d];\n", arrTmp.c_str(), i); } } } } } }
void StatementList::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr func = ar->getFunctionScope(); bool inPseudoMain = func && func->inPseudoMain(); std::vector<bool> isDeclaration; if (inPseudoMain) { // We need these declarations to go first, because PHP allows top level // function and class declarations to appear after usage. for (unsigned int i = 0; i < m_stmts.size(); i++) { StatementPtr stmt = m_stmts[i]; bool isDecl = false; if (stmt->is(Statement::KindOfFunctionStatement)) { isDecl = true; } else if (stmt->is(Statement::KindOfClassStatement) || stmt->is(Statement::KindOfInterfaceStatement)) { ClassScopePtr cls = (dynamic_pointer_cast<InterfaceStatement>(stmt))->getClassScope(); isDecl = cls->isBaseClass() || !cls->isVolatile(); } if (isDecl) stmt->outputCPP(cg,ar); isDeclaration.push_back(isDecl); } } for (unsigned int i = 0; i < m_stmts.size(); i++) { StatementPtr stmt = m_stmts[i]; if (stmt->is(Statement::KindOfClassStatement)) { if (!inPseudoMain || !isDeclaration[i]) stmt->outputCPP(cg, ar); } else if (!(stmt->is(Statement::KindOfFunctionStatement) || stmt->is(Statement::KindOfInterfaceStatement)) || (!inPseudoMain || !isDeclaration[i])) { stmt->outputCPP(cg, ar); if (stmt->is(Statement::KindOfMethodStatement)) { MethodStatementPtr methodStmt = dynamic_pointer_cast<MethodStatement>(stmt); std::string methodName = methodStmt->getName(); if (methodName == "offsetget") { ClassScopePtr cls = ar->getClassScope(); std::string arrayAccess("arrayaccess"); if (cls->derivesFrom(ar, arrayAccess, false, false)) { FunctionScopePtr funcScope = methodStmt->getFunctionScope(); std::string name = funcScope->getName(); funcScope->setName("__offsetget_lval"); methodStmt->setName("__offsetget_lval"); methodStmt->outputCPP(cg, ar); funcScope->setName(name); methodStmt->setName("offsetget"); } } } } } }
void MethodStatement::outputCPPStmt(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_stmt) { m_stmt->outputCPP(cg, ar); if (!m_stmt->hasRetExp()) { FunctionScopePtr funcScope = m_funcScope.lock(); ClassScopePtr cls = ar->getClassScope(); if (funcScope->isConstructor(cls)) { cg.printf("gasInCtor(oldInCtor);\n"); } } } }
void FunctionScope::outputCPPCreateImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopePtr scope = ar->getClassScope(); string clsNameStr = scope->getId(cg); const char *clsName = clsNameStr.c_str(); const char *consName = scope->classNameCtor() ? scope->getName().c_str() : "__construct"; cg_printf("%s%s *%s%s::create(", Option::ClassPrefix, clsName, Option::ClassPrefix, clsName); outputCPPParamsImpl(cg, ar); cg_indentBegin(") {\n"); cg_printf("CountableHelper h(this);\n"); cg_printf("init();\n"); cg_printf("%s%s(", Option::MethodPrefix, consName); outputCPPParamsCall(cg, ar, false); cg_printf(");\n"); cg_printf("return this;\n"); cg_indentEnd("}\n"); cg_indentBegin("ObjectData *%s%s::dynCreate(CArrRef params, " "bool construct /* = true */) {\n", Option::ClassPrefix, clsName); cg_printf("init();\n"); cg_indentBegin("if (construct) {\n"); cg_printf("CountableHelper h(this);\n"); OutputCPPDynamicInvokeCount(cg); outputCPPDynamicInvoke(cg, ar, Option::MethodPrefix, cg.formatLabel(consName).c_str(), true, false, false, NULL, true); cg_indentEnd("}\n"); cg_printf("return this;\n"); cg_indentEnd("}\n"); if (isDynamic() || isSepExtension()) { cg_indentBegin("void %s%s::dynConstruct(CArrRef params) {\n", Option::ClassPrefix, clsName); OutputCPPDynamicInvokeCount(cg); outputCPPDynamicInvoke(cg, ar, Option::MethodPrefix, cg.formatLabel(consName).c_str(), true, false, false, NULL, true); cg_indentEnd("}\n"); if (cg.getOutput() == CodeGenerator::SystemCPP || Option::EnableEval >= Option::LimitedEval) { cg_indentBegin("void %s%s::dynConstructFromEval(" "Eval::VariableEnvironment &env, " "const Eval::FunctionCallExpression *caller) {\n", Option::ClassPrefix, clsName); outputCPPEvalInvoke(cg, ar, Option::MethodPrefix, cg.formatLabel(consName).c_str(), NULL, false); cg_indentEnd("}\n"); } } }
void ObjectMethodExpression::analyzeProgram(AnalysisResultPtr ar) { m_params->analyzeProgram(ar); m_object->analyzeProgram(ar); m_nameExp->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll) { FunctionScopePtr func = m_funcScope; if (!func && m_object->isThis() && !m_name.empty()) { ClassScopePtr cls = ar->getClassScope(); if (cls) { m_classScope = cls; m_funcScope = func = cls->findFunction(ar, m_name, true, true); if (!func) { cls->addMissingMethod(m_name); } } } ExpressionList ¶ms = *m_params; if (func) { int mpc = func->getMaxParamCount(); for (int i = params.getCount(); i--; ) { ExpressionPtr p = params[i]; if (i < mpc ? func->isRefParam(i) : func->isReferenceVariableArgument()) { p->setContext(Expression::RefValue); } } } else if (!m_name.empty()) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); if (info) { for (int i = params.getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } // If we cannot find information of the so-named function, it might not // exist, or it might go through __call(), either of which cannot have // reference parameters. } else { for (int i = params.getCount(); i--; ) { m_params->markParam(i, canInvokeFewArgs()); } } } }
void ClassVariable::analyzeProgramImpl(AnalysisResultPtr ar) { m_declaration->analyzeProgram(ar); if (ar->getPhase() != AnalysisResult::AnalyzeInclude) return; ClassScopePtr scope = ar->getClassScope(); for (int i = 0; i < m_declaration->getCount(); i++) { ExpressionPtr exp = (*m_declaration)[i]; if (exp->is(Expression::KindOfAssignmentExpression)) { AssignmentExpressionPtr assignment = dynamic_pointer_cast<AssignmentExpression>(exp); SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(assignment->getVariable()); ExpressionPtr value = assignment->getValue(); scope->getVariables()->setClassInitVal(var->getName(), value); } } }
bool UnaryOpExpression::outputCPPImplOpEqual(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_exp->is(Expression::KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast<ObjectPropertyExpression>(m_exp)); if (var->isValid()) return false; var->outputCPPObject(cg, ar); cg_printf("o_assign_op<%s,%d>(", isUnused() ? "void" : "Variant", m_op); var->outputCPPProperty(cg, ar); cg_printf(", %s, %s)", isUnused() || m_front ? "null_variant" : "Variant(0)", ar->getClassScope() ? "s_class_name" : "empty_string"); return true; } return false; }
void MethodStatement::analyzeProgramImpl(AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); // registering myself as a parent in dependency graph, so that // (1) we can tell orphaned parents // (2) overwrite non-master copy of function declarations if (ar->isFirstPass()) { ar->getDependencyGraph()->addParent(DependencyGraph::KindOfFunctionCall, "", getFullName(), shared_from_this()); if (Option::AllDynamic || hasHphpNote("Dynamic") || funcScope->isSepExtension() || BuiltinSymbols::IsDeclaredDynamic(m_name) || Option::IsDynamicFunction(m_method, m_name)) { funcScope->setDynamic(); } if (hasHphpNote("Volatile")) funcScope->setVolatile(); } funcScope->setIncludeLevel(ar->getIncludeLevel()); ar->pushScope(funcScope); if (m_params) { m_params->analyzeProgram(ar); if (Option::GenRTTIProfileData && ar->getPhase() == AnalysisResult::AnalyzeFinal) { addParamRTTI(ar); } } if (m_stmt) m_stmt->analyzeProgram(ar); if (ar->isFirstPass()) { if (!funcScope->isStatic() && ar->getClassScope() && funcScope->getVariables()-> getAttribute(VariableTable::ContainsDynamicVariable)) { // Add this to variable table if we'll need it in a lookup table // Use object because there's no point to specializing, just makes // code gen harder when dealing with redeclared classes. TypePtr tp(NEW_TYPE(Object)); funcScope->getVariables()->add("this", tp, true, ar, shared_from_this(), ModifierExpressionPtr()); } FunctionScope::RecordRefParamInfo(m_name, funcScope); } ar->popScope(); }
bool MethodStatement::outputFFI(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); ClassScopePtr clsScope = ar->getClassScope(); bool pseudoMain = funcScope->inPseudoMain(); bool inClass = !m_className.empty(); // only expose public methods, and ignore those in redeclared classes bool inaccessible = inClass && (!m_modifiers->isPublic() || clsScope->isRedeclaring()); // skip constructors bool isConstructor = inClass && funcScope->isConstructor(clsScope); bool valid = !pseudoMain && !inaccessible && !isConstructor; if (cg.getContext() == CodeGenerator::CppFFIDecl || cg.getContext() == CodeGenerator::CppFFIImpl) { if (valid) outputCPPFFIStub(cg, ar); return true; } if (cg.getContext() == CodeGenerator::HsFFI) { if (valid) outputHSFFIStub(cg, ar); return true; } if (cg.getContext() == CodeGenerator::JavaFFI || cg.getContext() == CodeGenerator::JavaFFIInterface) { if (valid) outputJavaFFIStub(cg, ar); return true; } if (cg.getContext() == CodeGenerator::JavaFFICppDecl || cg.getContext() == CodeGenerator::JavaFFICppImpl) { if (valid) outputJavaFFICPPStub(cg, ar); return true; } if (cg.getContext() == CodeGenerator::SwigFFIDecl || cg.getContext() == CodeGenerator::SwigFFIImpl) { if (valid) outputSwigFFIStub(cg, ar); return true; } return false; }
bool FunctionScope::outputCPPInvokeArgCountCheck(CodeGenerator &cg, AnalysisResultPtr ar, bool ret, bool constructor) { bool variable = isVariableArgument(); // system function has different handling of argument counts bool system = (m_system || m_sep || cg.getOutput() == CodeGenerator::SystemCPP); if (!system) { ClassScopePtr scope = ar->getClassScope(); if (scope) system = (!scope->isUserClass() || scope->isExtensionClass() || scope->isSepExtension()); } bool checkMissing = (m_minParam > 0); bool checkTooMany = (!variable && (system || RuntimeOption::ThrowTooManyArguments)); const char *sysret = (system && ret) ? "return " : ""; const char *level = (system ? (constructor ? ", 2" : ", 1") : ""); bool guarded = system && (ret || constructor); string fullname = getOriginalFullName(); if (checkMissing && checkTooMany) { if (!variable && m_minParam == m_maxParam) { cg_printf("if (count != %d)" " %sthrow_wrong_arguments(\"%s\", count, %d, %d%s);\n", m_minParam, sysret, fullname.c_str(), m_minParam, m_maxParam, level); } else { cg_printf("if (count < %d || count > %d)" " %sthrow_wrong_arguments(\"%s\", count, %d, %d%s);\n", m_minParam, m_maxParam, sysret, fullname.c_str(), m_minParam, variable ? -1 : m_maxParam, level); } } else if (checkMissing) { cg_printf("if (count < %d)" " %sthrow_missing_arguments(\"%s\", count+1%s);\n", m_minParam, sysret, fullname.c_str(), level); } else if (checkTooMany) { cg_printf("if (count > %d)" " %sthrow_toomany_arguments(\"%s\", %d%s);\n", m_maxParam, sysret, fullname.c_str(), m_maxParam, level); } return guarded; }
void ClassVariable::inferTypes(AnalysisResultPtr ar) { m_declaration->inferAndCheck(ar, NEW_TYPE(Some), false); if (m_modifiers->isStatic()) { ClassScopePtr scope = ar->getClassScope(); for (int i = 0; i < m_declaration->getCount(); i++) { ExpressionPtr exp = (*m_declaration)[i]; if (exp->is(Expression::KindOfAssignmentExpression)) { scope->setNeedStaticInitializer(); AssignmentExpressionPtr assignment = dynamic_pointer_cast<AssignmentExpression>(exp); // If the class variable's type is Object, we have to // force it to be a Variant, because we don't include // the class header files in global_variables.h SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(assignment->getVariable()); if (var) { TypePtr type = scope->getVariables()->getFinalType(var->getName()); if (type->is(Type::KindOfObject)) { scope->getVariables()->forceVariant(ar, var->getName()); } } ExpressionPtr value = assignment->getValue(); if (value->containsDynamicConstant(ar)) { scope->getVariables()-> setAttribute(VariableTable::ContainsDynamicStatic); } } else { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp); TypePtr type = scope->getVariables()->getFinalType(var->getName()); // If the class variable's type is Object, we have to // force it to be a Variant, because we don't include // the class header files in global_variables.h if (type->is(Type::KindOfObject)) { scope->getVariables()->forceVariant(ar, var->getName()); } const char *initializer = type->getCPPInitializer(); if (initializer) scope->setNeedStaticInitializer(); } } } }
void ConstantTable::outputCPPDynamicDecl(CodeGenerator &cg, AnalysisResultPtr ar) { const char *prefix = Option::ConstantPrefix; string classId; const char *fmt = "Variant %s%s%s;\n"; ClassScopePtr scope = ar->getClassScope(); if (scope) { prefix = Option::ClassConstantPrefix; classId = scope->getId(); fmt = "Variant %s%s_%s;\n"; } for (StringToConstructPtrMap::const_iterator iter = m_declarations.begin(); iter != m_declarations.end(); ++iter) { const string &name = iter->first; if (isDynamic(name)) { cg.printf(fmt, prefix, classId.c_str(), name.c_str()); } } }
void ConstantTable::outputCPPDynamicDecl(CodeGenerator &cg, AnalysisResultPtr ar) { const char *prefix = Option::ConstantPrefix; string classId; const char *fmt = "Variant %s%s%s;\n"; ClassScopePtr scope = ar->getClassScope(); if (scope) { prefix = Option::ClassConstantPrefix; classId = scope->getId(cg); fmt = "Variant %s%s_%s;\n"; } for (StringToSymbolMap::iterator iter = m_symbolMap.begin(), end = m_symbolMap.end(); iter != end; ++iter) { Symbol *sym = &iter->second; if (sym->declarationSet() && sym->isDynamic()) { cg_printf(fmt, prefix, classId.c_str(), cg.formatLabel(sym->getName()).c_str()); } } }
void FunctionScope::outputCPPCreateDecl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopePtr scope = ar->getClassScope(); cg.printf("public: %s%s *create(", Option::ClassPrefix, scope->getId().c_str()); outputCPPParamsDecl(cg, ar, dynamic_pointer_cast<MethodStatement>(getStmt()) ->getParams(), true); cg.printf(");\n"); cg.printf("public: ObjectData *dynCreate(CArrRef params, bool init = true);" "\n"); if (isDynamic()) { cg.printf("public: void dynConstruct(CArrRef params);\n"); if (cg.getOutput() == CodeGenerator::SystemCPP || Option::EnableEval >= Option::LimitedEval) { cg.printf("public: void dynConstructFromEval(Eval::VariableEnvironment " "&env, const Eval::FunctionCallExpression *call);\n"); } } }
void AssignmentExpression::onParse(AnalysisResultPtr ar) { BlockScopePtr scope = ar->getScope(); // This is that much we can do during parse phase. TypePtr type; if (m_value->is(Expression::KindOfScalarExpression)) { type = m_value->inferAndCheck(ar, NEW_TYPE(Some), false); } else if (m_value->is(Expression::KindOfUnaryOpExpression)) { UnaryOpExpressionPtr uexp = dynamic_pointer_cast<UnaryOpExpression>(m_value); if (uexp->getOp() == T_ARRAY) { type = Type::Array; } } if (!type) type = NEW_TYPE(Some); if (m_variable->is(Expression::KindOfConstantExpression)) { // ...as in ClassConstant statement // We are handling this one here, not in ClassConstant, purely because // we need "value" to store in constant table. ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(m_variable); scope->getConstants()->add(exp->getName(), type, m_value, ar, m_variable); string name = ar->getClassScope()->getName() + "::" + exp->getName(); ar->getDependencyGraph()-> addParent(DependencyGraph::KindOfConstant, "", name, exp); } else if (m_variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable); scope->getVariables()->add(var->getName(), type, true, ar, shared_from_this(), scope->getModifiers()); var->clearContext(Declaration); // to avoid wrong CodeError } else { ASSERT(false); // parse phase shouldn't handle anything else } }
void MethodStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { FunctionScopePtr funcScope = m_funcScope.lock(); ClassScopePtr scope = ar->getClassScope(); string origFuncName; ar->pushScope(funcScope); if (outputFFI(cg, ar)) return; cg.setPHPLineNo(-1); if (cg.getContext() == CodeGenerator::CppImplementation) { printSource(cg); } switch (cg.getContext()) { case CodeGenerator::CppDeclaration: { if (!m_stmt) { cg.printf("// "); } m_modifiers->outputCPP(cg, ar); if (m_name == "__offsetget_lval") { cg.printf("virtual "); } TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg.printf("void"); } if (m_name == "__lval") { cg.printf(" &___lval("); } else if (m_name == "__offsetget_lval") { cg.printf(" &___offsetget_lval("); } else if (m_modifiers->isStatic() && m_stmt) { // Static method wrappers get generated as support methods cg.printf(" %s%s(const char* cls%s", Option::MethodImplPrefix, m_name.c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg.printf(" %s%s(", Option::MethodPrefix, m_name.c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, true); if (m_stmt) { cg.printf(");\n"); } else { cg.printf(") = 0;\n"); } if (funcScope->isConstructor(scope) && !funcScope->isAbstract() && !scope->isInterface()) { funcScope->outputCPPCreateDecl(cg, ar); } } break; case CodeGenerator::CppImplementation: if (m_stmt) { TypePtr type = funcScope->getReturnType(); if (type) { type->outputCPPDecl(cg, ar); } else { cg.printf("void"); } origFuncName = std::string(scope->getOriginalName()) + "::" + m_originalName; if (Option::HotFunctions.find(origFuncName) != Option::HotFunctions.end()) { cg.printf(" __attribute((__section__(\".text.hot\")))"); } else if (Option::ColdFunctions.find(origFuncName) != Option::ColdFunctions.end()) { cg.printf(" __attribute((__section__(\".text.cold\")))"); } if (m_name == "__lval") { cg.printf(" &%s%s::___lval(", Option::ClassPrefix, scope->getId().c_str()); } else if (m_name == "__offsetget_lval") { cg.printf(" &%s%s::___offsetget_lval(", Option::ClassPrefix, scope->getId().c_str()); } else if (m_modifiers->isStatic()) { cg.printf(" %s%s::%s%s(const char* cls%s", Option::ClassPrefix, scope->getId().c_str(), Option::MethodImplPrefix, m_name.c_str(), funcScope->isVariableArgument() || (m_params && m_params->getCount()) ? ", " : ""); } else { cg.printf(" %s%s::%s%s(", Option::ClassPrefix, scope->getId().c_str(), Option::MethodPrefix, m_name.c_str()); } funcScope->outputCPPParamsDecl(cg, ar, m_params, false); cg.indentBegin(") {\n"); if (m_modifiers->isStatic()) { cg.printf("STATIC_METHOD_INJECTION(%s, %s::%s);\n", scope->getOriginalName(), scope->getOriginalName(), m_originalName.c_str()); } else { cg.printf("INSTANCE_METHOD_INJECTION(%s, %s::%s);\n", scope->getOriginalName(), scope->getOriginalName(), m_originalName.c_str()); } if (Option::GenRTTIProfileData && m_params) { for (int i = 0; i < m_params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (param->hasRTTI()) { const string ¶mName = param->getName(); int id = ar->getParamRTTIEntryId(ar->getClassScope(), funcScope, paramName); if (id != -1) { cg.printf("RTTI_INJECTION(%s%s, %d);\n", Option::VariablePrefix, paramName.c_str(), id); } } } } if (m_name == "__lval" || m_name == "__offsetget_lval") { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[0]); cg.printf("Variant &v = %s->__lvalProxy;\n", cg.getOutput() == CodeGenerator::SystemCPP ? "get_system_globals()" : "get_global_variables()"); string lowered = Util::toLower(m_originalName); cg.printf("v = %s%s(%s%s);\n", Option::MethodPrefix, lowered.c_str(), Option::VariablePrefix, param->getName().c_str()); cg.printf("return v;\n"); } else { if (funcScope->isConstructor(scope)) { cg.printf("bool oldInCtor = gasInCtor(true);\n"); } else if (m_name == "__destruct") { cg.printf("setInDtor();\n"); } else if (m_name == "__call") { ParameterExpressionPtr param; if (m_params->getCount() > 0) { param = dynamic_pointer_cast<ParameterExpression>((*m_params)[0]); cg.printf("INCALL_HELPER(%s%s);\n", Option::VariablePrefix, param->getName().c_str()); } else { cg.printf("INCALL_HELPER(\"\");\n"); } } funcScope->outputCPP(cg, ar); cg.setContext(CodeGenerator::NoContext); // no inner functions/classes if (!funcScope->isStatic() && funcScope->getVariables()-> getAttribute(VariableTable::ContainsDynamicVariable)) { cg.printf("%sthis = this;\n", Option::VariablePrefix); } outputCPPStmt(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } cg.indentEnd("} /* function */\n"); } break; default: break; } ar->popScope(); }
void ClassVariable::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopePtr scope = ar->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; switch (cg.getContext()) { case CodeGenerator::CppConstructor: if (m_modifiers->isStatic()) continue; if (exp->is(Expression::KindOfAssignmentExpression)) { AssignmentExpressionPtr assignment = dynamic_pointer_cast<AssignmentExpression>(exp); var = dynamic_pointer_cast<SimpleVariable>(assignment->getVariable()); ExpressionPtr value = assignment->getValue(); value->outputCPPBegin(cg, ar); if (derivFromRedec) { cg_printf("%sset(\"%s\",-1, ", Option::ObjectPrefix, var->getName().c_str()); value->outputCPP(cg, ar); cg_printf(")"); } else { cg_printf("%s%s = ", Option::PropertyPrefix, var->getName().c_str()); value->outputCPP(cg, ar); } cg_printf(";\n"); value->outputCPPEnd(cg, ar); } else { var = dynamic_pointer_cast<SimpleVariable>(exp); if (derivFromRedec) { cg_printf("%sset(\"%s\",-1, null);\n", Option::ObjectPrefix, var->getName().c_str()); } else { type = scope->getVariables()->getFinalType(var->getName()); const char *initializer = type->getCPPInitializer(); if (initializer) { cg_printf("%s%s = %s;\n", Option::PropertyPrefix, var->getName().c_str(), initializer); } } } break; case CodeGenerator::CppStaticInitializer: { if (!m_modifiers->isStatic()) continue; VariableTablePtr variables = scope->getVariables(); if (exp->is(Expression::KindOfAssignmentExpression)) { AssignmentExpressionPtr assignment = dynamic_pointer_cast<AssignmentExpression>(exp); var = dynamic_pointer_cast<SimpleVariable> (assignment->getVariable()); ExpressionPtr value = assignment->getValue(); if (value->containsDynamicConstant(ar)) continue; cg_printf("g->%s%s%s%s = ", Option::StaticPropertyPrefix, scope->getId(cg).c_str(), Option::IdPrefix.c_str(), var->getName().c_str()); value->outputCPP(cg, ar); } else { var = dynamic_pointer_cast<SimpleVariable>(exp); type = scope->getVariables()->getFinalType(var->getName()); const char *initializer = type->getCPPInitializer(); if (initializer) { cg_printf("g->%s%s%s%s = %s", Option::StaticPropertyPrefix, scope->getId(cg).c_str(), Option::IdPrefix.c_str(), var->getName().c_str(), initializer); } } cg_printf(";\n"); } break; case CodeGenerator::CppLazyStaticInitializer: { if (!m_modifiers->isStatic()) continue; if (!exp->is(Expression::KindOfAssignmentExpression)) continue; VariableTablePtr variables = scope->getVariables(); AssignmentExpressionPtr assignment = dynamic_pointer_cast<AssignmentExpression>(exp); var = dynamic_pointer_cast<SimpleVariable>(assignment->getVariable()); ExpressionPtr value = assignment->getValue(); if (!value->containsDynamicConstant(ar)) continue; value->outputCPPBegin(cg, ar); cg_printf("g->%s%s%s%s = ", Option::StaticPropertyPrefix, scope->getId(cg).c_str(), Option::IdPrefix.c_str(), var->getName().c_str()); value->outputCPP(cg, ar); cg_printf(";\n"); value->outputCPPEnd(cg, ar); } break; default: break; } } }
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type, bool coerce) { reset(); ConstructPtr self = shared_from_this(); // handling define("CONSTANT", ...); if (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_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->setStatic(); } 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; }
void AssignmentExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { BlockScopePtr scope = ar->getScope(); bool ref = (m_ref && m_value->isRefable()); bool setNull = false; bool arrayLike = false; if (m_variable->is(Expression::KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(m_variable); if (!exp->isSuperGlobal() && !exp->isDynamicGlobal()) { exp->getVariable()->outputCPP(cg, ar); if (exp->getOffset()) { cg_printf(".set("); exp->getOffset()->outputCPP(cg, ar); cg_printf(", ("); } else { cg_printf(".append(("); } wrapValue(cg, ar, m_value, ref, true); cg_printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off); if (sc) { if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); return; } } else if (m_variable->is(Expression::KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast<ObjectPropertyExpression>(m_variable)); if (!var->isValid()) { var->outputCPPObject(cg, ar); cg_printf("o_set("); var->outputCPPProperty(cg, ar); cg_printf(", %s", ref ? "ref(" : ""); m_value->outputCPP(cg, ar); cg_printf("%s, %s)", ref ? ")" : "", ar->getClassScope() ? "s_class_name" : "empty_string"); return; } } else if (m_variable->is(Expression::KindOfSimpleVariable) && m_value->is(Expression::KindOfConstantExpression)) { ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(m_value); if (exp->isNull()) setNull = true; } bool wrapped = true; if (setNull) { cg_printf("setNull("); m_variable->outputCPP(cg, ar); } else { if ((wrapped = !isUnused())) { cg_printf("("); } m_variable->outputCPP(cg, ar); cg_printf(" = "); wrapValue(cg, ar, m_value, ref, arrayLike); } if (wrapped) { cg_printf(")"); } }
FunctionScopePtr MethodStatement::onParseImpl(AnalysisResultPtr ar) { int minParam, maxParam; ConstructPtr self = shared_from_this(); minParam = maxParam = 0; bool hasRef = false; if (m_params) { set<string> names; int i = 0; maxParam = m_params->getCount(); for (; i < maxParam; i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (param->isRef()) hasRef = true; if (param->isOptional()) break; minParam++; } for (i++; i < maxParam; i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (param->isRef()) hasRef = true; if (!param->isOptional()) { ar->getCodeError()->record(self, CodeError::RequiredAfterOptionalParam, param); param->defaultToNull(ar); } } if (ar->isFirstPass()) { for (i = 0; i < maxParam; i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_params)[i]); if (names.find(param->getName()) == names.end()) { names.insert(param->getName()); } else { ar->getCodeError()->record(self, CodeError::RedundantParameter, param); for (int j = 0; j < 1000; j++) { string name = param->getName() + lexical_cast<string>(j); if (names.find(name) == names.end()) { param->rename(name); break; } } } } } } if (hasRef || m_ref) { m_attribute |= FileScope::ContainsReference; } StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this()); FunctionScopePtr funcScope (new FunctionScope(ar, m_method, m_name, stmt, m_ref, minParam, maxParam, m_modifiers, m_attribute, m_docComment, ar->getFileScope())); if (!m_stmt) { funcScope->setVirtual(); } m_funcScope = funcScope; // TODO: this may have to expand to a concept of "virtual" functions... if (ar->getClassScope()) { funcScope->disableInline(); if (m_name.length() > 2 && m_name.substr(0,2) == "__") { bool magic = true; int paramCount = 0; if (m_name == "__destruct") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__call") { funcScope->setOverriding(Type::Variant, Type::String, Type::Array); paramCount = 2; } else if (m_name == "__set") { funcScope->setOverriding(Type::Variant, Type::String, Type::Variant); paramCount = 2; } else if (m_name == "__get") { funcScope->setOverriding(Type::Variant, Type::String); paramCount = 1; } else if (m_name == "__isset") { funcScope->setOverriding(Type::Boolean, Type::String); paramCount = 1; } else if (m_name == "__unset") { funcScope->setOverriding(Type::Variant, Type::String); paramCount = 1; } else if (m_name == "__sleep") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__wakeup") { funcScope->setOverriding(Type::Variant); } else if (m_name == "__set_state") { funcScope->setOverriding(Type::Variant, Type::Variant); paramCount = 1; } else if (m_name == "__tostring") { funcScope->setOverriding(Type::String); } else if (m_name == "__clone") { funcScope->setOverriding(Type::Variant); } else { paramCount = -1; if (m_name != "__construct") { ar->getCodeError()->record(self, CodeError::UnknownMagicMethod, self); magic = false; } } if (paramCount >= 0 && (paramCount != minParam || paramCount != maxParam)) { ar->getCodeError()->record(self, CodeError::InvalidMagicMethod, self); magic = false; } if (magic) funcScope->setMagicMethod(); } // ArrayAccess methods else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") { if (m_name == "offsetexists") { funcScope->setOverriding(Type::Boolean, Type::Variant); } else if (m_name == "offsetget") { funcScope->setOverriding(Type::Variant, Type::Variant); } else if (m_name == "offsetset") { funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant); } else if (m_name == "offsetunset") { funcScope->setOverriding(Type::Variant, Type::Variant); } } } return funcScope; }
void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator &cg, AnalysisResultPtr ar) { if (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_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 { 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_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); } }
void UnaryOpExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if ((m_op == T_INC || m_op == T_DEC) && outputCPPImplOpEqual(cg, ar)) { return; } if (m_op == T_ARRAY && (getContext() & (RefValue|LValue)) == 0 && !ar->getInsideScalarArray()) { int id = -1; int hash = -1; int index = -1; if (m_exp) { ExpressionListPtr pairs = dynamic_pointer_cast<ExpressionList>(m_exp); Variant v; if (pairs && pairs->isScalarArrayPairs() && pairs->getScalarValue(v)) { id = ar->registerScalarArray(m_exp, hash, index); } } else { id = ar->registerScalarArray(m_exp, hash, index); // empty array } if (id != -1) { ar->outputCPPScalarArrayId(cg, id, hash, index); return; } } if ((m_op == T_ISSET || m_op == T_EMPTY || m_op == T_UNSET) && m_exp) { if (m_exp->is(Expression::KindOfExpressionList)) { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (exps->getListKind() == ExpressionList::ListKindParam) { int count = exps->getCount(); if (count > 1) { cg_printf("("); } for (int i = 0; i < count; i++) { if (m_op == T_UNSET) { if (i > 0) cg_printf(", "); (*exps)[i]->outputCPPUnset(cg, ar); } else { if (i > 0) cg_printf(" && "); (*exps)[i]->outputCPPExistTest(cg, ar, m_op); } } if (exps->getCount() > 1) { cg_printf(")"); } return; } } if (m_op == T_UNSET) { m_exp->outputCPPUnset(cg, ar); } else { m_exp->outputCPPExistTest(cg, ar, m_op); } return; } if (m_front) { switch (m_op) { case T_CLONE: cg_printf("f_clone("); break; case T_INC: cg_printf("++"); break; case T_DEC: cg_printf("--"); break; case '+': cg_printf("+"); break; case '-': cg_printf("negate("); break; case '!': cg_printf("!("); break; case '~': cg_printf("~"); break; case '(': cg_printf("("); break; case T_INT_CAST: cg_printf("("); break; case T_DOUBLE_CAST: cg_printf("("); break; case T_STRING_CAST: cg_printf("("); break; case T_ARRAY_CAST: cg_printf("("); break; case T_OBJECT_CAST: cg_printf("("); break; case T_BOOL_CAST: cg_printf("("); break; case T_UNSET_CAST: if (m_exp->hasCPPTemp()) { cg_printf("(id("); } else { cg_printf("("); } break; case T_EXIT: cg_printf("f_exit("); break; case T_ARRAY: cg_printf("Array("); break; case T_PRINT: cg_printf("print("); break; case T_EVAL: if (Option::EnableEval > Option::NoEval) { bool instance; if (ar->getClassScope()) { FunctionScopePtr fs = ar->getFunctionScope(); instance = fs && !fs->isStatic(); } else { instance = false; } cg_printf("eval(%s, Object(%s), ", ar->getScope()->inPseudoMain() ? "get_variable_table()" : "variables", instance ? "this" : ""); } else { cg_printf("f_eval("); } break; case '@': if (m_silencer >= 0) { cg_printf("(%s%d.enable(),%s%d.disable(", Option::SilencerPrefix, m_silencer, Option::SilencerPrefix, m_silencer); } break; case T_FILE: cg_printf("get_source_filename(\"%s\")", getLocation()->file); break; break; default: ASSERT(false); } } if (m_exp) { switch (m_op) { case '+': case '-': if (m_exp->getActualType() && (m_exp->getActualType()->is(Type::KindOfString) || m_exp->getActualType()->is(Type::KindOfArray))) { cg_printf("(Variant)("); m_exp->outputCPP(cg, ar); cg_printf(")"); } else { m_exp->outputCPP(cg, ar); } break; case '@': // Void needs to return something to silenceDec if (!m_exp->hasCPPTemp() && !m_exp->getActualType()) { cg_printf("("); m_exp->outputCPP(cg, ar); cg_printf(",null)"); } else { m_exp->outputCPP(cg, ar); } break; default: m_exp->outputCPP(cg, ar); break; } } if (m_front) { switch (m_op) { case T_ARRAY: { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (!exps) { cg_printf("ArrayData::Create()"); } cg_printf(")"); } break; case T_UNSET_CAST: if (m_exp->hasCPPTemp()) { cg_printf("),null"); } else { cg_printf(",null"); } case T_CLONE: case '!': case '(': case '-': case T_INT_CAST: case T_DOUBLE_CAST: case T_STRING_CAST: case T_ARRAY_CAST: case T_OBJECT_CAST: case T_BOOL_CAST: case T_EXIT: case T_PRINT: case T_EVAL: case T_INCLUDE: case T_INCLUDE_ONCE: case T_REQUIRE: case T_REQUIRE_ONCE: cg_printf(")"); break; case '@': if (m_silencer >= 0) { cg_printf("))"); } break; default: break; } } else { switch (m_op) { case T_INC: cg_printf("++"); break; case T_DEC: cg_printf("--"); break; default: ASSERT(false); } } }
void UnaryOpExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_arrayId != -1) { if (cg.getOutput() == CodeGenerator::SystemCPP) { cg.printf("SystemScalarArrays::%s[%d]", Option::SystemScalarArrayName, m_arrayId); } else { cg.printf("ScalarArrays::%s[%d]", Option::ScalarArrayName, m_arrayId); } return; } if ((m_op == T_ISSET || m_op == T_EMPTY) && m_exp) { if (m_exp->is(Expression::KindOfExpressionList)) { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (exps->getCount() > 1) { cg.printf("("); } for (int i = 0; i < exps->getCount(); i++) { if (i > 0) cg.printf(" && "); (*exps)[i]->outputCPPExistTest(cg, ar, m_op); } if (exps->getCount() > 1) { cg.printf(")"); } } else { m_exp->outputCPPExistTest(cg, ar, m_op); } return; } if (m_front) { switch (m_op) { case T_CLONE: cg.printf("f_clone("); break; case T_INC: cg.printf("++"); break; case T_DEC: cg.printf("--"); break; case '+': cg.printf("+"); break; case '-': cg.printf("negate("); break; case '!': cg.printf("!("); break; case '~': cg.printf("~"); break; case '(': cg.printf("("); break; case T_INT_CAST: cg.printf("("); break; case T_DOUBLE_CAST: cg.printf("("); break; case T_STRING_CAST: cg.printf("("); break; case T_ARRAY_CAST: cg.printf("("); break; case T_OBJECT_CAST: cg.printf("("); break; case T_BOOL_CAST: cg.printf("("); break; case T_UNSET_CAST: cg.printf("unset("); break; case T_EXIT: cg.printf("f_exit("); break; case T_ARRAY: if (ar->getInsideScalarArray()) { cg.printf("StaticArray("); } else { cg.printf("Array("); } break; case T_PRINT: cg.printf("print("); break; case T_EVAL: if (Option::EnableEval > Option::NoEval) { cg.printf("eval(%s, Object(%s), ", ar->getScope()->inPseudoMain() ? "get_variable_table()" : "variables", ar->getClassScope() ? "this" : ""); } else { cg.printf("f_eval("); } break; case '@': cg.printf("(%s%d.enable(),%s%d.disable(", Option::SilencerPrefix, m_silencer, Option::SilencerPrefix, m_silencer); break; case T_FILE: cg.printf("get_source_filename(\"%s\")", getLocation()->file); break; break; default: ASSERT(false); } } if (m_exp) { switch (m_op) { case '+': case '-': if (m_exp->getActualType() && (m_exp->getActualType()->is(Type::KindOfString) || m_exp->getActualType()->is(Type::KindOfArray))) { cg.printf("(Variant)("); m_exp->outputCPP(cg, ar); cg.printf(")"); } else { m_exp->outputCPP(cg, ar); } break; case '@': // Void needs to return something to silenceDec if (!m_exp->getActualType()) { cg.printf("("); m_exp->outputCPP(cg, ar); cg.printf(",null)"); } else { m_exp->outputCPP(cg, ar); } break; default: m_exp->outputCPP(cg, ar); break; } } if (m_front) { switch (m_op) { case T_ARRAY: { ExpressionListPtr exps = dynamic_pointer_cast<ExpressionList>(m_exp); if (!exps) { cg.printf("ArrayData::Create()"); } cg.printf(")"); } break; case T_CLONE: case '!': case '(': case '-': case T_INT_CAST: case T_DOUBLE_CAST: case T_STRING_CAST: case T_ARRAY_CAST: case T_OBJECT_CAST: case T_BOOL_CAST: case T_UNSET_CAST: case T_EXIT: case T_PRINT: case T_EVAL: case T_INCLUDE: case T_INCLUDE_ONCE: case T_REQUIRE: case T_REQUIRE_ONCE: cg.printf(")"); break; case '@': cg.printf("))"); break; default: break; } } else { switch (m_op) { case T_INC: cg.printf("++"); break; case T_DEC: cg.printf("--"); break; default: ASSERT(false); } } }
void FunctionScope::outputCPPDynamicInvoke(CodeGenerator &cg, AnalysisResultPtr ar, const char *funcPrefix, const char *name, bool voidWrapperOff /* = false */, bool fewArgs /* = false */, bool ret /* = true */, const char *extraArg /* = NULL */, bool constructor /* = false */) { const char *voidWrapper = (m_returnType || voidWrapperOff) ? "" : ", null"; const char *retrn = ret ? "return " : ""; int maxParam = fewArgs && m_maxParam > Option::InvokeFewArgsCount ? Option::InvokeFewArgsCount : m_maxParam; bool variable = isVariableArgument(); ASSERT(m_minParam >= 0); bool guarded = outputCPPInvokeArgCountCheck(cg, ar, ret, constructor); bool do_while = !ret && !fewArgs && maxParam > m_minParam; if (!fewArgs && maxParam) { for (int i = 0; i < maxParam; i++) { if (isRefParam(i)) { cg_printf("const_cast<Array&>(params).escalate(true);\n"); break; } } if (do_while) cg_printf("do "); cg_indentBegin("{\n"); cg_printf("ArrayData *ad(params.get());\n"); cg_printf("ssize_t pos = ad ? ad->iter_begin() : " "ArrayData::invalid_index;\n"); for (int i = 0; i < m_minParam; i++) { cg_printf("CVarRef arg%d(", i); if (!guarded) cg_printf("count <= %d ? null_variant : ", i); cg_printf("%s(ad->getValue%s(pos%s)));\n", isRefParam(i) ? "ref" : "", isRefParam(i) ? "Ref" : "", i ? " = ad->iter_advance(pos)" : ""); } } if (variable || getOptionalParamCount()) { if (!fewArgs || m_minParam < Option::InvokeFewArgsCount) { cg_printf("if (count <= %d) ", m_minParam); if (do_while) cg_indentBegin("{\n"); } } stringstream callss; callss << retrn << (m_refReturn ? "ref(" : "("); if (m_perfectVirtual) { ClassScopePtr cls = ar->getClassScope(); callss << Option::ClassPrefix << cls->getId(cg) << "::"; } callss << funcPrefix << name << "("; if (extraArg) { callss << extraArg; if (variable) { callss << ","; } } if (variable) { callss << "count"; } bool preArgs = variable || extraArg; string call = callss.str(); cg_printf("%s", call.c_str()); for (int i = 0; i < m_minParam; i++) { if (preArgs || i > 0) cg_printf(", "); if (isRefParam(i)) { if (fewArgs) { if (i < Option::InvokeFewArgsCount) { cg_printf("ref(a%d)", i); } else { cg_printf("null_variant"); } } else { cg_printf("arg%d", i); } } else { if (fewArgs) { if (i < Option::InvokeFewArgsCount) { cg_printf("a%d", i); } else { cg_printf("null"); } } else { cg_printf("arg%d", i); } } } cg_printf(")%s);\n", voidWrapper); for (int iMax = m_minParam; iMax < maxParam; ) { if (!ret) { if (do_while) { cg_printf("break;\n"); cg_indentEnd("}\n"); } else { cg_printf("else "); } } if (!fewArgs) { cg_printf("CVarRef arg%d(%s(ad->getValue%s(pos%s)));\n", iMax, isRefParam(iMax) ? "ref" : "", isRefParam(iMax) ? "Ref" : "", iMax ? " = ad->iter_advance(pos)" : ""); } if (++iMax < maxParam || variable) { cg_printf("if (count == %d) ", iMax); if (do_while) cg_indentBegin("{\n"); } cg_printf("%s", call.c_str()); for (int i = 0; i < iMax; i++) { if (preArgs || i > 0) cg_printf(", "); if (isRefParam(i)) { if (fewArgs) { if (i < Option::InvokeFewArgsCount) { cg_printf("ref(a%d)", i); } else { cg_printf("null_variant"); } } else { cg_printf("arg%d", i); } } else { if (fewArgs) { if (i < Option::InvokeFewArgsCount) { cg_printf("a%d", i); } else { cg_printf("null_variant"); } } else { cg_printf("arg%d", i); } } } cg_printf(")%s);\n", voidWrapper); } if (variable) { if (do_while) { cg_printf("break;\n"); cg_indentEnd("}\n"); } if (fewArgs) { if (maxParam == Option::InvokeFewArgsCount) return; cg_printf("Array params;\n"); for (int i = maxParam; i < Option::InvokeFewArgsCount; i++) { cg_printf("if (count >= %d) params.append(a%d);\n", i + 1, i); } } cg_printf("%s,", call.c_str()); for (int i = 0; i < maxParam; i++) { if (isRefParam(i)) { if (fewArgs) { if (i < Option::InvokeFewArgsCount) { cg_printf("ref(a%d), ", i); } else { cg_printf("null_variant, "); } } else { cg_printf("ref(arg%d), ", i); } } else { if (fewArgs) { if (i < Option::InvokeFewArgsCount) { cg_printf("a%d, ", i); } else { cg_printf("null_variant, "); } } else { cg_printf("arg%d, ", i); } } } if (fewArgs) { cg_printf("params)%s);\n", voidWrapper); } else { cg_printf("params.slice(%d, count - %d, false))%s);\n", maxParam, maxParam, voidWrapper); } } if (!fewArgs && maxParam) { if (do_while) { cg_indentEnd("} while (false);\n"); } else { cg_indentEnd("}\n"); } } }
void ForEachStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { cg_indentBegin("{\n"); int labelId = cg.createNewLocalId(ar); cg.pushBreakScope(labelId); int mapId = cg.createNewLocalId(ar); bool passTemp = true; bool isArray = false; if (m_ref || !m_array->is(Expression::KindOfSimpleVariable) || m_array->isThis()) { cg_printf("Variant %s%d", Option::MapPrefix, mapId); bool wrap = m_array->preOutputCPP(cg, ar, 0); if (wrap) { cg_printf(";\n"); m_array->outputCPPBegin(cg, ar); cg_printf("%s%d", Option::MapPrefix, mapId); } if (m_ref) { cg_printf(" = ref("); m_array->outputCPPImpl(cg, ar); cg.printf(");\n"); cg.printf("%s%d.escalate(true);\n", Option::MapPrefix, mapId); } else { TypePtr expectedType = m_array->getExpectedType(); // Clear m_expectedType to avoid type cast (toArray). m_array->setExpectedType(TypePtr()); cg_printf(" = "); m_array->outputCPP(cg, ar); cg_printf(";\n"); m_array->setExpectedType(expectedType); } if (wrap) { m_array->outputCPPEnd(cg, ar); } } else { passTemp = false; } cppDeclareBufs(cg, ar); int iterId = cg.createNewLocalId(ar); cg_printf("for ("); if (m_ref) { cg_printf("MutableArrayIterPtr %s%d = %s%d.begin(", Option::IterPrefix, iterId, Option::MapPrefix, mapId); if (m_name) { cg_printf("&"); m_name->outputCPP(cg, ar); } else { cg_printf("NULL"); } cg_printf(", "); m_value->outputCPP(cg, ar); cg_printf("); %s%d->advance();", Option::IterPrefix, iterId); } else { if (passTemp) { cg_printf("ArrayIterPtr %s%d = %s%d.begin(", Option::IterPrefix, iterId, Option::MapPrefix, mapId); ClassScopePtr cls = ar->getClassScope(); if (cls) { cg_printf("%sclass_name", Option::StaticPropertyPrefix); } cg_printf("); "); cg_printf("!%s%d->end(); %s%d->next()", Option::IterPrefix, iterId, Option::IterPrefix, iterId); } else { TypePtr actualType = m_array->getActualType(); if (actualType && actualType->is(Type::KindOfArray)) { isArray = true; cg_printf("ArrayIter %s%d = ", Option::IterPrefix, iterId); } else { cg_printf("ArrayIterPtr %s%d = ", Option::IterPrefix, iterId); } TypePtr expectedType = m_array->getExpectedType(); // Clear m_expectedType to avoid type cast (toArray). m_array->setExpectedType(TypePtr()); m_array->outputCPP(cg, ar); m_array->setExpectedType(expectedType); cg_printf(".begin("); ClassScopePtr cls = ar->getClassScope(); if (cls) { cg_printf("%sclass_name", Option::StaticPropertyPrefix); } cg_printf("); "); if (isArray) { cg_printf("!%s%d.end(); ", Option::IterPrefix, iterId); cg_printf("++%s%d", Option::IterPrefix, iterId); } else { cg_printf("!%s%d->end(); ", Option::IterPrefix, iterId); cg_printf("%s%d->next()", Option::IterPrefix, iterId); } } } cg_indentBegin(") {\n"); cg_printf("LOOP_COUNTER_CHECK(%d);\n", labelId); if (!m_ref) { cg_printf(isArray ? "%s%d.second(" : "%s%d->second(", Option::IterPrefix, iterId); m_value->outputCPP(cg, ar); cg_printf(");\n"); if (m_name) { m_name->outputCPP(cg, ar); cg_printf(isArray ? " = %s%d.first();\n" : " = %s%d->first();\n", Option::IterPrefix, iterId); } } if (m_stmt) { m_stmt->outputCPP(cg, ar); } if (cg.findLabelId("continue", labelId)) { cg_printf("continue%d:;\n", labelId); } cg_indentEnd("}\n"); if (cg.findLabelId("break", labelId)) { cg_printf("break%d:;\n", labelId); } cg.popBreakScope(); cppEndBufs(cg, ar); cg_indentEnd("}\n"); }
void ObjectPropertyExpression::outputCPPObjProperty(CodeGenerator &cg, AnalysisResultPtr ar, bool directVariant, int doExist) { bool bThis = m_object->isThis(); bool useGetThis = false; FunctionScopePtr funcScope = ar->getFunctionScope(); if (bThis) { if (funcScope && funcScope->isStatic()) { cg_printf("GET_THIS_ARROW()"); } else { // in order for __set() and __get() to be called useGetThis = true; } } const char *op = "."; string func = Option::ObjectPrefix; const char *error = ", true"; ClassScopePtr cls = ar->getClassScope(); const char *context = ""; if (cg.getOutput() != CodeGenerator::SystemCPP) { if (cls) { context = ", s_class_name"; } else if (funcScope && !funcScope->inPseudoMain()) { context = ", empty_string"; } } if (doExist) { func = doExist > 0 ? "doIsSet" : "doEmpty"; error = ""; } else { if (bThis && funcScope && funcScope->isStatic()) { func = Option::ObjectStaticPrefix; error = ""; context = ""; } else if (m_context & ExistContext) { error = ", false"; } if (m_context & (LValue | RefValue | UnsetContext)) { func += "lval"; error = ""; } else { func += "get"; } } if (m_property->getKindOf() == Expression::KindOfScalarExpression) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(m_property); const char *propName = name->getString().c_str(); if (m_valid && m_object->getType()->isSpecificObject()) { if (m_static) { if (!bThis) { ASSERT(m_class); if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty("); cg_printf("g->%s%s%s%s", Option::StaticPropertyPrefix, m_class->getName().c_str(), Option::IdPrefix.c_str(), propName); if (doExist) cg_printf(")"); } else { // if $val is a class static variable (static $val), then $val // cannot be declared as a class variable (var $val), $this->val // refers to a non-static class variable and has to use get/lval. if (useGetThis) cg_printf("GET_THIS_DOT()"); cg_printf("%s(", func.c_str()); cg_printString(propName, ar); cg_printf("%s%s)", error, context); } } else { if (doExist) cg_printf(doExist > 0 ? "isset(" : "empty("); if (!bThis) { ASSERT(!directVariant); m_object->outputCPP(cg, ar); cg_printf("->"); } cg_printf("%s%s", Option::PropertyPrefix, propName); if (doExist) cg_printf(")"); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg_printf(op); } else { if (useGetThis) cg_printf("GET_THIS_DOT()"); } cg_printf("%s(", func.c_str()); cg_printString(propName, ar); cg_printf("%s%s)", error, context); } } else { if (!bThis) { if (directVariant) { TypePtr expectedType = m_object->getExpectedType(); ASSERT(expectedType->is(Type::KindOfObject)); // Clear m_expectedType to avoid type cast (toObject). m_object->setExpectedType(TypePtr()); m_object->outputCPP(cg, ar); m_object->setExpectedType(expectedType); } else { m_object->outputCPP(cg, ar); } cg_printf(op); } else { if (useGetThis) cg_printf("GET_THIS_DOT()"); } cg_printf("%s(", func.c_str()); m_property->outputCPP(cg, ar); cg_printf("%s%s)", error, context); } }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), true); if (!m_property->is(Expression::KindOfScalarExpression)) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicProperty, self); } m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); string name = exp->getString(); ASSERT(!name.empty()); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findClass(objectType->getName()); ASSERT(cls); } else { // what ->property has told us cls = ar->findClass(name, AnalysisResult::PropertyName); if (!cls) { if (m_context & (LValue | RefValue)) { ar->forceClassVariants(name); } return Type::Variant; } m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()), true); } const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" : hasContext(ExistContext) ? "__isset" : hasContext(UnsetContext) ? "__unset" : "__get"; if (!cls->implementsAccessor(ar, accessorName)) { if (m_localEffects & AccessorEffect) { recomputeEffects(); } m_localEffects &= ~AccessorEffect; } // resolved to this class int present = 0; if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = ar->getFunctionScope(); if (func->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } TypePtr ret = cls->checkProperty(name, type, coerce, ar, self, present); if (!cls->derivesFromRedeclaring()) { // Have to use dynamic. // Private only valid if in the defining class if (present && (ar->getClassScope().get() == cls.get() || !(present & VariableTable::VariablePrivate))) { m_valid = true; m_static = present & VariableTable::VariableStatic; if (m_static) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_class = cls; } } // get() will return Variant if (!m_valid || !m_object->getType()->isSpecificObject()) { m_actualType = Type::Variant; return m_actualType; } if (m_localEffects & AccessorEffect) { recomputeEffects(); m_localEffects &= ~AccessorEffect; } if (ar->getPhase() == AnalysisResult::LastInference) { if (!(m_context & ObjectContext)) { m_object->clearContext(Expression::LValue); } setContext(Expression::NoLValueWrapper); } return ret; }