ExpressionPtr ClassConstantExpression::preOptimize(AnalysisResultPtr ar) { if (ar->getPhase() < AnalysisResult::FirstPreOptimize) { return ExpressionPtr(); } if (m_redeclared) return ExpressionPtr(); ClassScopePtr cls = ar->resolveClass(m_className); if (!cls || cls->isRedeclaring()) return ExpressionPtr(); ConstantTablePtr constants = cls->getConstants(); if (constants->isExplicitlyDeclared(m_varName)) { ConstructPtr decl = constants->getValue(m_varName); if (decl) { ExpressionPtr value = dynamic_pointer_cast<Expression>(decl); if (!m_visited) { m_visited = true; ar->pushScope(cls); ExpressionPtr optExp = value->preOptimize(ar); ar->popScope(); m_visited = false; if (optExp) value = optExp; } if (value->isScalar()) { // inline the value if (value->is(Expression::KindOfScalarExpression)) { ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(Clone(value)); exp->setComment(getText()); return exp; } else if (value->is(Expression::KindOfConstantExpression)) { // inline the value ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(Clone(value)); exp->setComment(getText()); return exp; } } } } return ExpressionPtr(); }
Parameter::Parameter(CONSTRUCT_ARGS, const string &type, const string &name, int idx, bool ref, ExpressionPtr defVal, int argNum) : Construct(CONSTRUCT_PASS), m_type(type), m_name(Name::fromString(CONSTRUCT_PASS, name)), m_defVal(defVal), m_fnName(NULL), m_idx(idx), m_kind(KindOfNull), m_argNum(argNum), m_ref(ref), m_nullDefault(false) { if (!type.empty()) { m_fnName = parser->peekFunc()->name().c_str(); if (strcasecmp(type.c_str(), "array") == 0) { m_kind = KindOfArray; } else { m_kind = KindOfObject; if (strcasecmp(type.c_str(), "self") == 0 && parser->haveClass()) { m_type = parser->peekClass()->name(); } } if (m_defVal) { ScalarExpressionPtr s = m_defVal->cast<ScalarExpression>(); bool correct = false; if (s) { DataType dtype = s->getValue().getType(); correct = m_nullDefault = dtype == KindOfNull; } else { ArrayExpressionPtr a = m_defVal->cast<ArrayExpression>(); correct = a && m_kind == KindOfArray; } if (!correct) { if (m_kind == KindOfArray) { throw_fatal("Default value for parameters with array type hint can " "only be an array or NULL"); } else { throw_fatal("Default value for parameters with a class type hint can" " only be NULL"); } } } } }
void Parser::onScalar(Token *out, int type, Token *scalar) { ScalarExpressionPtr exp; switch (type) { case T_STRING: case T_LNUMBER: case T_DNUMBER: case T_LINE: case T_FILE: case T_CLASS_C: case T_METHOD_C: case T_FUNC_C: exp = NEW_EXP(ScalarExpression, type, scalar->text()); break; case T_CONSTANT_ENCAPSED_STRING: exp = NEW_EXP(ScalarExpression, type, scalar->text(), true); break; default: ASSERT(false); } exp->onParse(m_ar); out->exp = exp; }
bool ArrayPairExpression::outputCPPName(CodeGenerator &cg, AnalysisResultPtr ar) { assert(m_name); ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_name); if (sc) { if (sc->isLiteralString()) { string s = sc->getLiteralString(); int64 res; if (is_strictly_integer(s.c_str(), s.size(), res)) { cg_printf("%sL", s.c_str()); } else { m_name->outputCPP(cg, ar); } return true; } if (sc->isLiteralInteger()) { m_name->outputCPP(cg, ar); return true; } } m_name->outputCPP(cg, ar); return false; }
FunctionCall::FunctionCall (EXPRESSION_CONSTRUCTOR_PARAMETERS, ExpressionPtr nameExp, const std::string &name, ExpressionListPtr params, ExpressionPtr classExp) : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES), m_nameExp(nameExp), m_params(params), m_class(classExp), m_valid(false), m_validClass(false), m_extraArg(0), m_variableArgument(false), m_voidReturn(false), m_voidWrapper(false), m_allowVoidReturn(false), m_redeclared(false), m_redeclaredClass(false), m_derivedFromRedeclaring(false), m_argArrayId(-1) { if (m_nameExp && m_nameExp->getKindOf() == Expression::KindOfScalarExpression) { ASSERT(m_name.empty()); ScalarExpressionPtr c = dynamic_pointer_cast<ScalarExpression>(m_nameExp); m_origName = c->getString(); c->toLower(true /* func call*/); m_name = c->getString(); ASSERT(!m_name.empty()); } else { m_origName = name; m_name = Util::toLower(name); } if (m_class && m_class->is(KindOfScalarExpression)) { ScalarExpressionPtr s(dynamic_pointer_cast<ScalarExpression>(m_class)); const string &className = s->getString(); m_className = Util::toLower(className); if (m_className == "static") { m_className.clear(); } else { m_origClassName = className; m_class.reset(); } } }
void ArrayElementExpression::outputCPPUnset(CodeGenerator &cg, AnalysisResultPtr ar) { if (isSuperGlobal()) { Expression::outputCPPUnset(cg, ar); } else { TypePtr expected = m_variable->getExpectedType(); TypePtr implemented = m_variable->getImplementedType(); bool wrap = false; if (TypePtr t = m_variable->getActualType()) { if (t->is(Type::KindOfObject)) { if (!m_variable->getImplementedType() || !m_variable->getImplementedType()->is(Type::KindOfVariant)) { cg_printf("((Variant)("); wrap = true; } m_variable->setImplementedType(TypePtr()); m_variable->setExpectedType(TypePtr()); } } m_variable->outputCPP(cg, ar); if (wrap) cg_printf("))"); m_variable->setExpectedType(expected); m_variable->setImplementedType(implemented); cg_printf(".weakRemove("); m_offset->outputCPP(cg, ar); ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s->isStrictlyInteger(n)) { cg_printf(", true"); } } cg_printf(")"); } }
FunctionCall::FunctionCall (EXPRESSION_CONSTRUCTOR_BASE_PARAMETERS, ExpressionPtr nameExp, const std::string &name, bool hadBackslash, ExpressionListPtr params, ExpressionPtr classExp) : Expression(EXPRESSION_CONSTRUCTOR_BASE_PARAMETER_VALUES), StaticClassName(classExp), m_nameExp(nameExp), m_params(params), m_valid(false), m_variableArgument(false), m_redeclared(false), m_arrayParams(false), m_hadBackslash(hadBackslash) { if (m_nameExp && m_nameExp->getKindOf() == Expression::KindOfScalarExpression) { assert(m_origName.empty()); ScalarExpressionPtr c = dynamic_pointer_cast<ScalarExpression>(m_nameExp); m_origName = c->getOriginalLiteralString(); c->toLower(true /* func call*/); } else { m_origName = name; } }
void ArrayElementExpression::outputCPPExistTest(CodeGenerator &cg, AnalysisResultPtr ar, int op) { switch (op) { case T_ISSET: cg_printf("isset("); break; case T_EMPTY: cg_printf("empty("); break; default: ASSERT(false); } if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = ar->getScope()->getVariables(); string name = variables->getGlobalVariableName(cg, ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("((LVariableTable *)g)->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { m_variable->outputCPP(cg, ar); cg_printf(", "); m_offset->outputCPP(cg, ar); ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); 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(")"); }
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 ArrayElementExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = getScope()->getVariables(); string name = variables->getGlobalVariableName(cg, ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("((LVariableTable *)g)->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { TypePtr type = m_variable->getActualType(); if (hasContext(UnsetContext)) { cg_printf("unsetLval("); m_variable->outputCPP(cg, ar); cg_printf(", "); } else { if (m_variable->is(Expression::KindOfScalarExpression) || (type && (type->isInteger() || type->is(Type::KindOfDouble) || type->is(Type::KindOfObject) || type->is(Type::KindOfBoolean)))) { cg_printf(type && type->is(Type::KindOfString) ? "((String)" : "((Variant)"); m_variable->outputCPP(cg, ar); cg_printf(")"); } else { TypePtr act; if (!m_variable->hasCPPTemp() && m_variable->getImplementedType() && type->is(Type::KindOfArray) && !Type::SameType(m_variable->getImplementedType(), type)) { act = type; type = m_variable->getImplementedType(); m_variable->setActualType(m_variable->getImplementedType()); } m_variable->outputCPP(cg, ar); if (act) { m_variable->setActualType(act); } } } if (m_offset) { bool lvalAt = false; bool rvalAt = false; bool byRef = false; bool arrRef = false; const char *sep = ", AccessFlags::"; if (hasContext(UnsetContext)) { // do nothing } else if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) { cg_printf(".argvalAt(cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum); } else if (m_context & (LValue|RefValue|DeepReference)) { cg_printf(".lvalAt("); lvalAt = true; } else { byRef = (m_context & AccessContext) && (!type || !type->is(Type::KindOfString)); arrRef = byRef && type && type->is(Type::KindOfArray); cg_printf(".rval%s%s(", arrRef || !byRef ? "At" : "", byRef ? "Ref" : ""); rvalAt = true; } m_offset->outputCPP(cg, ar); if (!type || !type->is(Type::KindOfString)) { if (rvalAt) { if (byRef && !arrRef) { const string &tmp = cg.getReferenceTemp(); cg_printf(", %s", tmp.empty() ? "Variant()" : tmp.c_str()); } if (!hasContext(ExistContext)) { cg_printf(", AccessFlags::Error"); // raise undefined index error sep = "_"; } } else if (lvalAt) { if (hasContext(AccessContext)) { // Dont copy the array if the element is an object, or // is referenced. // This is safe in AccessContext (the parent is an ArrayElement, // or an ObjectProperty) because applying [] to an object will // either invoke OffsetGet, or fatal, and modifications to a // referenced element would be reflected in all copies // of the array anyway. cg_printf(", AccessFlags::CheckExist"); sep = "_"; } } ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { if (lvalAt || rvalAt) { cg_printf("%sKey", sep); } else { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); } else { cg_printf(".lvalAt()"); } } }
/** * ArrayElementExpression comes from: * * reference_variable[|expr] * ->object_dim_list[|expr] * encaps T_VARIABLE[expr] * encaps ${T_STRING[expr]} */ TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { ConstructPtr self = shared_from_this(); if (m_offset && !(m_context & (UnsetContext | ExistContext | InvokeArgument | LValue | RefValue))) { setEffect(DiagnosticEffect); } if (m_context & (AssignmentLHS|OprLValue)) { clearEffect(AccessorEffect); } else if (m_context & (LValue | RefValue)) { setEffect(CreateEffect); } // handling $GLOBALS[...] if (m_variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable); if (var->getName() == "GLOBALS") { clearEffect(AccessorEffect); m_global = true; m_dynamicGlobal = true; getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); VariableTablePtr vars = ar->getVariables(); if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) { ScalarExpressionPtr offset = dynamic_pointer_cast<ScalarExpression>(m_offset); if (offset->isLiteralString()) { m_globalName = offset->getIdentifier(); if (!m_globalName.empty()) { m_dynamicGlobal = false; clearEffect(DiagnosticEffect); getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); TypePtr ret; if (coerce) { ret = vars->add(m_globalName, type, true, ar, self, ModifierExpressionPtr()); } else { int p; ret = vars->checkVariable(m_globalName, type, coerce, ar, self, p); } getScope()->getVariables()->addSuperGlobal(m_globalName); return ret; } } } else { vars->setAttribute(VariableTable::ContainsDynamicVariable); } if (hasContext(LValue) || hasContext(RefValue)) { ar->getVariables()->forceVariants(ar, VariableTable::AnyVars); ar->getVariables()-> setAttribute(VariableTable::ContainsLDynamicVariable); } if (m_offset) { m_offset->inferAndCheck(ar, Type::Primitive, false); } return m_implementedType = Type::Variant; // so not to lose values } } if ((hasContext(LValue) || hasContext(RefValue)) && !hasContext(UnsetContext)) { m_variable->setContext(LValue); } TypePtr varType; if (m_offset) { varType = m_variable->inferAndCheck(ar, coerce ? Type::AutoSequence : Type::Sequence, coerce); m_offset->inferAndCheck(ar, Type::Some, false); } else { if (hasContext(ExistContext) || hasContext(UnsetContext)) { if (getScope()->isFirstPass()) { Compiler::Error(Compiler::InvalidArrayElement, self); } } m_variable->inferAndCheck(ar, Type::Array, true); } if (varType && Type::SameType(varType, Type::String)) { clearEffect(AccessorEffect); m_implementedType.reset(); return Type::String; } if (varType && Type::SameType(varType, Type::Array)) { clearEffect(AccessorEffect); } TypePtr ret = propagateTypes(ar, Type::Variant); m_implementedType = Type::Variant; return ret; // so not to lose values }
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopeRawPtr classScope = getClassScope(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isVolatile()) { string name = cg.formatLabel(m_name); if (classScope->isRedeclaring()) { cg_printf("g->%s%s = ClassStaticsPtr(NEWOBJ(%s%s)());\n", Option::ClassStaticsObjectPrefix, name.c_str(), Option::ClassStaticsPrefix, classScope->getId(cg).c_str()); cg_printf("g->%s%s = &%s%s;\n", Option::ClassStaticsCallbackPrefix, name.c_str(), Option::ClassWrapperFunctionPrefix, classScope->getId(cg).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("checkClassExists("); cg_printString(base->getOriginalName(), ar, shared_from_this()); string lname = Util::toLower(base->getOriginalName()); cg_printf(", &%s->CDEC(%s), %s->FVF(__autoload));\n", cg.getGlobals(ar), cg.formatLabel(lname).c_str(), cg.getGlobals(ar)); } } } return; } if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } string clsNameStr = classScope->getId(cg); const char *clsName = clsNameStr.c_str(); bool redeclared = classScope->isRedeclaring(); switch (cg.getContext()) { case CodeGenerator::CppDeclaration: { if (Option::GenerateCPPMacros) { classScope->outputForwardDeclaration(cg); } 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(cg).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); 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"); if (Option::GenerateCPPMacros) { cg_printf("virtual bool o_instanceof(CStrRef s) const;\n"); } 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(cg).c_str() : "ObjectData"); } else { cg_printf("DECLARE_DYNAMIC_CLASS(%s, %s, %s)\n", clsName, m_originalName.c_str(), dyn || !parCls ? "DynamicObjectData" : parCls->getId(cg).c_str()); } 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; if (dyn) { conInit = " : DynamicObjectData(\"" + m_parent + "\", r)"; hasParam = true; } else if (idyn) { conInit = " : " + string(Option::ClassPrefix) + parCls->getId(cg) + "(r ? r : this)"; hasParam = true; } else { if (redec && classScope->derivedByDynamic()) { conInit = " : root(r ? r : this)"; } hasParam = true; } cg_indentBegin("%s%s(%s)%s {%s", Option::ClassPrefix, clsName, hasParam ? "ObjectData* r = NULL" : "", conInit.c_str(), 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"); } } cg_printf("void init();\n"); if (classScope->needLazyStaticInitializer()) { cg_printf("static GlobalVariables *lazy_initializer" "(GlobalVariables *g);\n"); } if (!classScope->getAttribute(ClassScope::HasConstructor)) { FunctionScopePtr func = classScope->findFunction(ar, "__construct", false); if (func && !func->isAbstract() && !classScope->isInterface()) { func->outputCPPCreateDecl(cg, ar); } } if (classScope->getAttribute(ClassScope::HasDestructor)) { cg_printf("public: virtual void destruct();\n"); } // doCall if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) { cg_printf("Variant doCall(Variant v_name, Variant v_arguments, " "bool fatal);\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 (cg.getOutput() == CodeGenerator::SystemCPP && ar->isBaseSysRsrcClass(clsName) && !classScope->hasProperty("rsrc")) { cg_printf("public: Variant %srsrc;\n", Option::PropertyPrefix); } if (Option::GenerateCPPMacros) { classScope->outputCPPJumpTableDecl(cg, ar); } cg_indentEnd("};\n"); if (redeclared) { cg_indentBegin("class %s%s : public ClassStatics {\n", Option::ClassStaticsPrefix, clsName); cg_printf("public:\n"); cg_printf("DECLARE_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); cg_printf("%s%s() : ClassStatics(%d) {}\n", Option::ClassStaticsPrefix, clsName, classScope->getRedeclaringId()); cg_indentBegin("Variant %sgetInit(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sgetInit(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sget(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant &%slval(CStrRef s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%slval(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Object createOnly(ObjectData* root = NULL) {\n"); cg_printf("Object r((NEWOBJ(%s%s)(root)));\n", Option::ClassPrefix, clsName); cg_printf("r->init();\n"); cg_printf("return r;\n"); cg_indentEnd("}\n"); cg_indentBegin("Variant %sconstant(const char* s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sconstant(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("bool %sget_call_info(MethodCallPackage &mcp, " "int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget_call_info(mcp, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentEnd("};\n"); } if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsDecl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppDeclaration); } classScope->outputCPPGlobalTableWrappersDecl(cg, ar); } break; case CodeGenerator::CppImplementation: if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsImpl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } classScope->outputCPPSupportMethodsImpl(cg, ar); if (redeclared) { cg_printf("IMPLEMENT_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); } 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(cg).c_str()); } } if (classScope->getVariables()-> getAttribute(VariableTable::NeedGlobalPointer)) { cg.printDeclareGlobals(); } cg.setContext(CodeGenerator::CppConstructor); 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"); if (classScope->needStaticInitializer()) { cg_indentBegin("void %s%s::os_static_initializer() {\n", Option::ClassPrefix, clsName); cg.printDeclareGlobals(); cg.setContext(CodeGenerator::CppStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_indentBegin("void %s%s() {\n", Option::ClassStaticInitializerPrefix, clsName); cg_printf("%s%s::os_static_initializer();\n", Option::ClassPrefix, clsName); cg_indentEnd("}\n"); } if (classScope->needLazyStaticInitializer()) { cg_indentBegin("GlobalVariables *%s%s::lazy_initializer(" "GlobalVariables *g) {\n", Option::ClassPrefix, clsName); cg_indentBegin("if (!g->%s%s) {\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg_printf("g->%s%s = true;\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg.setContext(CodeGenerator::CppLazyStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_printf("return g;\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; } }
string CaseStatement::getLiteralString() const { assert(m_condition->is(Expression::KindOfScalarExpression)); ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_condition); return exp->getLiteralString(); }
void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = ar->getScope()->getVariables(); string name = variables->getGlobalVariableName(cg, ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("get_variable_table()->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { TypePtr type = m_variable->getActualType(); if (hasContext(UnsetContext)) { cg_printf("unsetLval("); m_variable->outputCPP(cg, ar); cg_printf(", "); } else { if (m_variable->is(Expression::KindOfScalarExpression) || (type && (type->isInteger() || type->is(Type::KindOfDouble) || type->is(Type::KindOfObject) || type->is(Type::KindOfBoolean)))) { cg_printf(type && type->is(Type::KindOfString) ? "((String)" : "((Variant)"); m_variable->outputCPP(cg, ar); cg_printf(")"); } else { m_variable->outputCPP(cg, ar); } } if (m_offset) { bool lvalAt = false; bool rvalAt = false; if (hasContext(UnsetContext)) { // do nothing } else if (m_context & InvokeArgument) { cg_printf(".refvalAt("); } else if (m_context & (LValue|RefValue)) { cg_printf(".lvalAt("); lvalAt = true; } else { cg_printf(".rvalAt("); rvalAt = true; } m_offset->outputCPP(cg, ar); if (!type || !type->is(Type::KindOfString)) { bool prehashed = false; ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (sc) { int64 hash = sc->getHash(); if (hash >= 0) { cg_printf(", 0x%016llXLL", hash); prehashed = true; } } if (!prehashed) { cg_printf(", -1"); } if (rvalAt) { if (!hasContext(ExistContext)) { cg_printf(", true"); // raise undefined index error } else { cg_printf(", false"); } } else if (lvalAt) { if (hasContext(ObjectContext)) { // object target might not trigger an array copy cg_printf(", true"); } else { cg_printf(", false"); } } if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg_printf(", true"); // skip toKey() at run time } } } cg_printf(")"); } else { cg_printf(".lvalAt()"); } } }
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopePtr classScope = m_classScope.lock(); if (cg.getContext() == CodeGenerator::NoContext) { if (classScope->isRedeclaring()) { cg_printf("g->%s%s = ClassStaticsPtr(NEW(%s%s)());\n", Option::ClassStaticsObjectPrefix, cg.formatLabel(m_name).c_str(), Option::ClassStaticsPrefix, classScope->getId(cg).c_str()); cg_printf("g->%s%s = &%s%s;\n", Option::ClassStaticsCallbackPrefix, cg.formatLabel(m_name).c_str(), Option::ClassWrapperFunctionPrefix, classScope->getId(cg).c_str()); } if (classScope->isVolatile()) { cg_printf("g->CDEC(%s) = true;\n", m_name.c_str()); } const vector<string> &bases = classScope->getBases(); for (vector<string>::const_iterator it = bases.begin(); it != bases.end(); ++it) { ClassScopePtr base = ar->findClass(*it); if (base && base->isVolatile()) { cg_printf("checkClassExists(\"%s\", g);\n", base->getOriginalName().c_str()); } } return; } if (cg.getContext() != CodeGenerator::CppForwardDeclaration) { printSource(cg); } ar->pushScope(classScope); string clsNameStr = classScope->getId(cg); const char *clsName = clsNameStr.c_str(); bool redeclared = classScope->isRedeclaring(); switch (cg.getContext()) { case CodeGenerator::CppForwardDeclaration: if (Option::GenerateCPPMacros) { cg_printf("FORWARD_DECLARE_CLASS(%s)\n", clsName); if (redeclared) { cg_printf("FORWARD_DECLARE_REDECLARED_CLASS(%s)\n", clsName); } } if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsDecl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppForwardDeclaration); } break; case CodeGenerator::CppDeclaration: { bool system = cg.getOutput() == CodeGenerator::SystemCPP; ClassScopePtr parCls; if (!m_parent.empty()) { parCls = ar->findClass(m_parent); if (parCls && parCls->isRedeclaring()) parCls.reset(); } cg_printf("class %s%s", Option::ClassPrefix, clsName); if (!m_parent.empty() && classScope->derivesDirectlyFrom(ar, m_parent)) { if (!parCls) { cg_printf(" : public DynamicObjectData"); } else { cg_printf(" : public %s%s", Option::ClassPrefix, parCls->getId(cg).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(ar, intf) && (!parCls || !parCls->derivesFrom(ar, intf, true, false))) { string id = intfClassScope->getId(cg); cg_printf(", public %s%s", Option::ClassPrefix, id.c_str()); } } } cg_indentBegin(" {\n"); if (Option::GenerateCPPMacros) { // Get all of this class's ancestors vector<string> bases; getAllParents(ar, bases); // Eliminate duplicates sort(bases.begin(), bases.end()); bases.erase(unique(bases.begin(), bases.end()), bases.end()); cg_indentBegin("BEGIN_CLASS_MAP(%s)\n", Util::toLower(classScope->getName()).c_str()); for (unsigned int i = 0; i < bases.size(); i++) { cg_printf("PARENT_CLASS(%s)\n", bases[i].c_str()); } if (classScope->derivesFromRedeclaring()) { cg_printf("CLASS_MAP_REDECLARED()\n"); } cg_indentEnd("END_CLASS_MAP(%s)\n", clsName); } 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(cg).c_str() : "ObjectData"); } else { cg_printf("DECLARE_DYNAMIC_CLASS(%s, %s, %s)\n", clsName, m_originalName.c_str(), dyn || !parCls ? "DynamicObjectData" : parCls->getId(cg).c_str()); } if (system || Option::EnableEval >= Option::LimitedEval) { cg_printf("DECLARE_INVOKES_FROM_EVAL\n"); } if (dyn || idyn || redec) { if (redec) { cg_printf("DECLARE_ROOT;\n"); if (!dyn && !idyn) { cg_printf("private: ObjectData* root;\n"); cg_printf("public:\n"); cg_printf("virtual ObjectData *getRoot() { return root; }\n"); } } string conInit = ":"; if (dyn) { conInit += "DynamicObjectData(\"" + m_parent + "\", r)"; } else if (idyn) { conInit += string(Option::ClassPrefix) + parCls->getId(cg) + "(r?r:this)"; } else { conInit += "root(r?r:this)"; } cg_printf("%s%s(ObjectData* r = NULL)%s {}\n", Option::ClassPrefix, clsName, conInit.c_str()); } } cg_printf("void init();\n", Option::ClassPrefix, clsName); if (classScope->needLazyStaticInitializer()) { cg_printf("static GlobalVariables *lazy_initializer" "(GlobalVariables *g);\n"); } classScope->getVariables()->outputCPPPropertyDecl(cg, ar, classScope->derivesFromRedeclaring()); if (!classScope->getAttribute(ClassScope::HasConstructor)) { FunctionScopePtr func = classScope->findFunction(ar, "__construct", false); if (func && !func->isAbstract() && !classScope->isInterface()) { ar->pushScope(func); func->outputCPPCreateDecl(cg, ar); ar->popScope(); } } if (classScope->getAttribute(ClassScope::HasDestructor)) { cg_printf("public: virtual void destruct();\n"); } // doCall if (classScope->getAttribute(ClassScope::HasUnknownMethodHandler)) { cg_printf("Variant doCall(Variant v_name, Variant v_arguments, " "bool fatal);\n"); } // doGet if (classScope->getAttribute(ClassScope::HasUnknownPropHandler)) { cg_printf("Variant doGet(Variant v_name, bool error);\n"); } if (classScope->isRedeclaring() && !classScope->derivesFromRedeclaring()) { 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 (cg.getOutput() == CodeGenerator::SystemCPP && ar->isBaseSysRsrcClass(clsName) && !classScope->hasProperty("rsrc")) { cg_printf("public: Variant %srsrc;\n", Option::PropertyPrefix); } cg_indentEnd("};\n"); if (redeclared) { cg_indentBegin("class %s%s : public ClassStatics {\n", Option::ClassStaticsPrefix, clsName); cg_printf("public:\n"); cg_printf("DECLARE_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); cg_printf("%s%s() : ClassStatics(%d) {}\n", Option::ClassStaticsPrefix, clsName, classScope->getRedeclaringId()); cg_indentBegin("Variant %sgetInit(const char *s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sgetInit(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sget(const char *s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sget(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant &%slval(const char* s, int64 hash = -1) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%slval(s, hash);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sinvoke(const char *c, const char *s, " "CArrRef params, int64 hash = -1, bool fatal = true) " "{\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sinvoke(c, s, params, hash, fatal);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Object create(CArrRef params, bool init = true, " "ObjectData* root = NULL) {\n"); cg_printf("return Object((NEW(%s%s)(root))->" "dynCreate(params, init));\n", Option::ClassPrefix, clsName); cg_indentEnd("}\n"); cg_indentBegin("Variant %sconstant(const char* s) {\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sconstant(s);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentBegin("Variant %sinvoke_from_eval(const char *c, " "const char *s, Eval::VariableEnvironment &env, " "const Eval::FunctionCallExpression *call, " "int64 hash = -1, bool fatal = true) " "{\n", Option::ObjectStaticPrefix); cg_printf("return %s%s::%sinvoke_from_eval(c, s, env, call, hash, " "fatal);\n", Option::ClassPrefix, clsName, Option::ObjectStaticPrefix); cg_indentEnd("}\n"); cg_indentEnd("};\n"); } classScope->outputCPPGlobalTableWrappersDecl(cg, ar); } break; case CodeGenerator::CppImplementation: if (m_stmt) { cg.setContext(CodeGenerator::CppClassConstantsImpl); m_stmt->outputCPP(cg, ar); cg.setContext(CodeGenerator::CppImplementation); } classScope->outputCPPSupportMethodsImpl(cg, ar); if (redeclared) { cg_printf("IMPLEMENT_OBJECT_ALLOCATION(%s%s);\n", Option::ClassStaticsPrefix, clsName); } 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 { cg_printf("%s%s::init();\n", Option::ClassPrefix, m_parent.c_str()); } } if (classScope->getVariables()-> getAttribute(VariableTable::NeedGlobalPointer)) { cg.printDeclareGlobals(); } cg.setContext(CodeGenerator::CppConstructor); 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"); if (classScope->needStaticInitializer()) { cg_indentBegin("void %s%s::os_static_initializer() {\n", Option::ClassPrefix, clsName); cg.printDeclareGlobals(); cg.setContext(CodeGenerator::CppStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_indentBegin("void %s%s() {\n", Option::ClassStaticInitializerPrefix, clsName); cg_printf("%s%s::os_static_initializer();\n", Option::ClassPrefix, clsName); cg_indentEnd("}\n"); } if (classScope->needLazyStaticInitializer()) { cg_indentBegin("GlobalVariables *%s%s::lazy_initializer(" "GlobalVariables *g) {\n", Option::ClassPrefix, clsName); cg_indentBegin("if (!g->%s%s) {\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg_printf("g->%s%s = true;\n", Option::ClassStaticInitializerFlagPrefix, clsName); cg.setContext(CodeGenerator::CppLazyStaticInitializer); if (m_stmt) m_stmt->outputCPP(cg, ar); cg_indentEnd("}\n"); cg_printf("return g;\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(ar, 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; } ar->popScope(); }
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_valid = false; ConstructPtr self = shared_from_this(); TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), false); if (!m_property->is(Expression::KindOfScalarExpression)) { // if dynamic property or method, we have nothing to find out if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseDynamicProperty, self); } m_property->inferAndCheck(ar, Type::String, false); // we also lost track of which class variable an expression is about, hence // any type inference could be wrong. Instead, we just force variants on // all class variables. if (m_context & (LValue | RefValue)) { ar->forceClassVariants(); } return Type::Variant; // we have to use a variant to hold dynamic value } ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property); string name = exp->getString(); ASSERT(!name.empty()); m_property->inferAndCheck(ar, Type::String, false); ClassScopePtr cls; if (objectType && !objectType->getName().empty()) { // what object-> has told us cls = ar->findExactClass(objectType->getName()); } else { // what ->property has told us cls = ar->findClass(name, AnalysisResult::PropertyName); if (cls) { objectType = m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()), false); } if ((m_context & LValue) && objectType && !objectType->is(Type::KindOfObject) && !objectType->is(Type::KindOfVariant) && !objectType->is(Type::KindOfSome) && !objectType->is(Type::KindOfAny)) { m_object->inferAndCheck(ar, NEW_TYPE(Object), true); } } if (!cls) { if (m_context & (LValue | RefValue)) { ar->forceClassVariants(name); } return Type::Variant; } const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" : hasContext(ExistContext) ? "__isset" : hasContext(UnsetContext) ? "__unset" : "__get"; if (!cls->implementsAccessor(ar, accessorName)) clearEffect(AccessorEffect); // resolved to this class int present = 0; if (m_context & RefValue) { type = Type::Variant; coerce = true; } // use $this inside a static function if (m_object->isThis()) { FunctionScopePtr func = ar->getFunctionScope(); if (func->isStatic()) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::MissingObjectContext, self); } m_actualType = Type::Variant; return m_actualType; } } TypePtr ret; if (!cls->derivesFromRedeclaring()) { // Have to use dynamic. ret = cls->checkProperty(name, type, coerce, ar, self, present); // Private only valid if in the defining class if (present && (getOriginalScope(ar) == cls || !(present & VariableTable::VariablePrivate))) { m_valid = true; m_static = present & VariableTable::VariableStatic; if (m_static) { ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } m_class = cls; } } // get() will return Variant if (!m_valid || !m_object->getType()->isSpecificObject()) { m_actualType = Type::Variant; return m_actualType; } clearEffect(AccessorEffect); if (ar->getPhase() == AnalysisResult::LastInference) { if (!(m_context & ObjectContext)) { m_object->clearContext(Expression::LValue); } setContext(Expression::NoLValueWrapper); } return ret; }
void ArrayElementExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (m_global) { if (!m_globalName.empty()) { VariableTablePtr variables = getScope()->getVariables(); string name = variables->getGlobalVariableName(ar, m_globalName); cg_printf("g->%s", name.c_str()); } else { cg_printf("((LVariableTable *)g)->get("); m_offset->outputCPP(cg, ar); cg_printf(")"); } } else { TypePtr type = m_variable->getType(); if (hasContext(UnsetContext)) { cg_printf("unsetLval("); m_variable->outputCPP(cg, ar); cg_printf(", "); } else { if (m_variable->is(Expression::KindOfScalarExpression) || (type && (type->isInteger() || type->is(Type::KindOfDouble) || type->is(Type::KindOfObject) || type->is(Type::KindOfBoolean)))) { cg_printf(type && type->is(Type::KindOfString) ? "((String)" : "((Variant)"); m_variable->outputCPP(cg, ar); cg_printf(")"); } else { m_variable->outputCPP(cg, ar); } } if (m_offset) { bool lvalAt = false; bool rvalAt = false; bool byRef = false; bool arrRef = false; const char *sep = ", AccessFlags::"; bool isArrayType = type && type->is(Type::KindOfArray); bool isStringType = type && type->is(Type::KindOfString); bool isRealChainRoot = isChainRoot() && hasCPPCseTemp(); TypePtr t; bool hasCseStore = isRealChainRoot && GetCseTempInfo( ar, static_pointer_cast<Expression>(shared_from_this()), t); if (hasContext(UnsetContext)) { // do nothing } else if (hasContext(InvokeArgument) && cg.callInfoTop() != -1) { ASSERT(!isRealChainRoot); // TODO: handle this case cg_printf(".argvalAt(cit%d->isRef(%d), ", cg.callInfoTop(), m_argNum); } else if (m_context & (LValue|RefValue|DeepReference)) { // if we see an array access element in LValue context, the // type inference pass will never infer its type to be a string ASSERT(!isStringType); if (isRealChainRoot && !isArrayType) { // chain roots for non array types (variants) should call // lvalRef() cg_printf(".lvalRef("); } else { cg_printf(".lvalAt("); } lvalAt = true; } else { byRef = ((m_context & AccessContext) || isRealChainRoot) && !isStringType; arrRef = byRef && isArrayType; cg_printf(".rval%s%s(", arrRef || !byRef ? "At" : "", byRef ? "Ref" : ""); rvalAt = true; } m_offset->outputCPP(cg, ar); if (!isStringType) { if (rvalAt) { if (byRef && !arrRef) { string tmp; if (hasCseStore) { tmp = string(Option::CseTempStoragePrefix) + m_cppCseTemp; } else { tmp = cg.getReferenceTemp(); } cg_printf(", %s", tmp.empty() ? "Variant()" : tmp.c_str()); } if (!hasContext(ExistContext)) { cg_printf(", AccessFlags::Error"); // raise undefined index error sep = "_"; } } else if (lvalAt) { if (hasCseStore && !isArrayType) { cg_printf(", %s%s", Option::CseTempStoragePrefix, m_cppCseTemp.c_str()); } if (hasContext(AccessContext)) { // Dont copy the array if the element is an object, or // is referenced. // This is safe in AccessContext (the parent is an ArrayElement, // or an ObjectProperty) because applying [] to an object will // either invoke OffsetGet, or fatal, and modifications to a // referenced element would be reflected in all copies // of the array anyway. cg_printf(", AccessFlags::CheckExist"); sep = "_"; } } ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(m_offset); if (!hasContext(UnsetContext) && sc && sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { if (lvalAt || rvalAt) { cg_printf("%sKey", sep); } else { cg_printf(", true"); // skip toKey() at run time } } } } cg_printf(")"); } else { cg_printf(".lvalAt()"); } } }
ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) { 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_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(); }
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) { 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_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_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_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 ¶ms = *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); } }
void ClassStatement::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (cg.getContext() == CodeGenerator::NoContext) { InterfaceStatement::outputCPPImpl(cg, ar); return; } ClassScopeRawPtr classScope = getClassScope(); 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"); if (classScope->getVariables()->outputCPPPropertyDecl( cg, ar, classScope->derivesFromRedeclaring())) { cg.printSection("Destructor"); cg_printf("~%s%s() NEVER_INLINE {}", Option::ClassPrefix, clsName); } 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(); bool disableDestructor = !classScope->canSkipCreateMethod(ar) || (!classScope->derivesFromRedeclaring() && !classScope->hasAttribute(ClassScope::HasDestructor, ar)); if (Option::GenerateCPPMacros) { bool dyn = classScope->derivesFromRedeclaring() == ClassScope::DirectFromRedeclared; bool idyn = parCls && classScope->derivesFromRedeclaring() == ClassScope::IndirectFromRedeclared; bool redec = classScope->isRedeclaring(); if (!parCls && !m_parent.empty()) { assert(dyn); } 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 hasIsset = classScope->getAttribute( ClassScope::HasUnknownPropTester); bool hasUnset = classScope->getAttribute( ClassScope::HasPropUnsetter); bool hasCall = classScope->getAttribute( ClassScope::HasUnknownMethodHandler); bool hasCallStatic = classScope->getAttribute( ClassScope::HasUnknownStaticMethodHandler); bool hasRootParam = classScope->derivedByDynamic() && (redec || dyn || idyn); string lateInit = ""; if (redec && classScope->derivedByDynamic()) { if (!dyn && !idyn && (!parCls || parCls->isUserClass())) { cg_printf("private: ObjectData* root;\n"); cg_printf("public:\n"); cg_printf("virtual ObjectData *getRoot() { return root; }\n"); lateInit = "root(r ? r : this)"; } } string callbacks = Option::ClassStaticsCallbackPrefix + clsNameStr; string conInit = ""; if (dyn) { conInit = "DynamicObjectData(cb, \"" + CodeGenerator::EscapeLabel(m_parent) + "\", "; if (hasRootParam) { conInit += "r)"; } else { conInit += "this)"; } } else if (parCls) { conInit = string(Option::ClassPrefix) + parCls->getId() + "("; if (parCls->derivedByDynamic() && (parCls->isRedeclaring() || parCls->derivesFromRedeclaring() != ClassScope::FromNormal)) { if (hasRootParam) { conInit += "r ? r : "; } conInit += "this, "; } conInit += "cb)"; } else { if (system) { conInit = "ExtObjectData(cb)"; } else { if (hasRootParam) { conInit = "ObjectData(cb, r)"; } else { conInit = "ObjectData(cb, false)"; } } } cg_printf("%s%s(%sconst ObjectStaticCallbacks *cb = &%s%s) : %s", Option::ClassPrefix, clsName, hasRootParam ? "ObjectData* r = NULL," : "", callbacks.c_str(), redec ? ".oscb" : "", conInit.c_str()); if (needsCppCtor) { cg_printf(", "); cg.setContext(CodeGenerator::CppConstructor); ASSERT(!cg.hasInitListFirstElem()); m_stmt->outputCPP(cg, ar); cg.clearInitListFirstElem(); cg.setContext(CodeGenerator::CppDeclaration); } if (!lateInit.empty()) { cg_printf(", %s", lateInit.c_str()); } cg_indentBegin(" {%s", hasGet || hasSet || hasIsset || hasUnset || hasCall || hasCallStatic || disableDestructor || hasRootParam ? "\n" : ""); if (hasRootParam) { cg_printf("setId(r);\n"); } if (hasGet) cg_printf("setAttribute(UseGet);\n"); if (hasSet) cg_printf("setAttribute(UseSet);\n"); if (hasIsset) cg_printf("setAttribute(UseIsset);\n"); if (hasUnset) cg_printf("setAttribute(UseUnset);\n"); if (hasCall) cg_printf("setAttribute(HasCall);\n"); if (hasCallStatic) cg_printf("setAttribute(HasCallStatic);\n"); if (disableDestructor) { cg_printf("if (!hhvm) setAttribute(NoDestructor);\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(" {%s}\n", disableDestructor ? " if (!hhvm) setAttribute(NoDestructor); " : ""); } if (needsInit) { cg_printf("void init();\n"); } // 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); { std::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"; std::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; } }
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(")"); } }
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_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(")"); }
bool AssignmentExpression::SpecialAssignment(CodeGenerator &cg, AnalysisResultPtr ar, ExpressionPtr lval, ExpressionPtr rval, const char *rvalStr, bool ref) { if (lval->is(KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(lval); 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(("); } if (rval) { wrapValue(cg, ar, rval, ref, (exp->getVariable()->is(KindOfArrayElementExpression) || exp->getVariable()->is(KindOfObjectPropertyExpression)) && (exp->getVariable()->getContainedEffects() && (CreateEffect|AccessorEffect))); } else { cg_printf(ref ? "ref(%s)" : "%s", rvalStr); } 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 true; } } else if (lval->is(KindOfObjectPropertyExpression)) { ObjectPropertyExpressionPtr var( dynamic_pointer_cast<ObjectPropertyExpression>(lval)); if (!var->isValid()) { bool nonPrivate = var->isNonPrivate(ar); var->outputCPPObject(cg, ar); if (nonPrivate) { cg_printf("o_setPublic("); } else { cg_printf("o_set("); } var->outputCPPProperty(cg, ar); cg_printf(", %s", ref ? "ref(" : ""); if (rval) { rval->outputCPP(cg, ar); } else { cg_printf(ref ? "ref(%s)" : "%s", rvalStr); } if (nonPrivate) { cg_printf("%s)", ref ? ")" : ""); } else { cg_printf("%s%s)", ref ? ")" : "", lval->originalClassName(cg, true).c_str()); } return true; } } return false; }
void AssignmentExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { BlockScopePtr scope = ar->getScope(); bool ref = (m_ref && !m_value->is(Expression::KindOfNewObjectExpression)); bool setElement = false; // turning $a['elem'] = $b into $a.set('elem', $b); bool type_cast = false; bool setNull = false; TypePtr m_actualType; if (m_variable->is(Expression::KindOfArrayElementExpression)) { ArrayElementExpressionPtr exp = dynamic_pointer_cast<ArrayElementExpression>(m_variable); m_actualType = m_variable->getActualType(); if (m_actualType && m_actualType->getKindOf() == Type::KindOfVariant && !ref) { //type_cast = true; } 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(("); } if (type_cast) { m_actualType->outputCPPCast(cg, ar); cg.printf("("); } if (ref && m_value->isRefable()) cg.printf("ref("); m_value->outputCPP(cg, ar); if (ref && m_value->isRefable()) cg.printf(")"); if (type_cast) cg.printf(")"); cg.printf(")"); ExpressionPtr off = exp->getOffset(); if (off) { ScalarExpressionPtr sc = dynamic_pointer_cast<ScalarExpression>(off); if (sc) { int64 hash = sc->getHash(); if (hash >= 0) { cg.printf(", 0x%016llXLL", hash); } else { cg.printf(", -1"); } if (sc->isLiteralString()) { String s(sc->getLiteralString()); int64 n; if (!s.get()->isStrictlyInteger(n)) { cg.printf(", true"); // skip toKey() at run time } } } } cg.printf(")"); setElement = true; } } if (m_variable->is(Expression::KindOfSimpleVariable) && m_value->is(Expression::KindOfConstantExpression)) { ConstantExpressionPtr exp = dynamic_pointer_cast<ConstantExpression>(m_value); if (exp->isNull()) setNull = true; } if (!setElement) { if (setNull) { cg.printf("setNull("); m_variable->outputCPP(cg, ar); } else { cg.printf("("); m_variable->outputCPP(cg, ar); cg.printf(" = "); if (type_cast) { m_actualType->outputCPPCast(cg, ar); cg.printf("("); } if (ref && m_value->isRefable()) cg.printf("ref("); m_value->outputCPP(cg, ar); if (ref && m_value->isRefable()) cg.printf(")"); if (type_cast) cg.printf(")"); } cg.printf(")"); } }
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); } }
void ExpressionList::getOriginalStrings(std::vector<std::string> &strings) { for (unsigned int i = 0; i < m_exps.size(); i++) { ScalarExpressionPtr s = dynamic_pointer_cast<ScalarExpression>(m_exps[i]); strings.push_back(s->getOriginalString()); } }
/** * ArrayElementExpression comes from: * * reference_variable[|expr] * ->object_dim_list[|expr] * encaps T_VARIABLE[expr] * encaps ${T_STRING[expr]} */ TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { ConstructPtr self = shared_from_this(); // handling $GLOBALS[...] if (m_variable->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(m_variable); if (var->getName() == "GLOBALS") { clearEffect(AccessorEffect); m_global = true; m_dynamicGlobal = true; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); VariableTablePtr vars = ar->getVariables(); if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) { ScalarExpressionPtr offset = dynamic_pointer_cast<ScalarExpression>(m_offset); if (offset->isLiteralString()) { m_globalName = offset->getIdentifier(); if (!m_globalName.empty()) { m_dynamicGlobal = false; ar->getScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); TypePtr ret; ConstructPtr decl = vars->getDeclaration(m_globalName); if (decl) { ar->getDependencyGraph()-> add(DependencyGraph::KindOfGlobalVariable, ar->getName(), m_globalName, self, m_globalName, decl); } if (coerce) { ret = vars->add(m_globalName, type, true, ar, self, ModifierExpressionPtr()); } else { int p; ret = vars->checkVariable(m_globalName, type, coerce, ar, self, p); } ar->getScope()->getVariables()->addSuperGlobal(m_globalName); return ret; } } } else { vars->setAttribute(VariableTable::ContainsDynamicVariable); } if (hasContext(LValue) || hasContext(RefValue)) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseLDynamicVariable, self); } ar->getVariables()->forceVariants(ar); ar->getVariables()-> setAttribute(VariableTable::ContainsLDynamicVariable); } else { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::UseRDynamicVariable, self); } } if (m_offset) { m_offset->inferAndCheck(ar, NEW_TYPE(Primitive), false); } return m_implementedType = Type::Variant; // so not to lose values } } if ((hasContext(LValue) || hasContext(RefValue)) && !hasContext(UnsetContext)) { m_variable->setContext(LValue); } TypePtr varType; if (m_offset) { varType = m_variable->inferAndCheck(ar, NEW_TYPE(Sequence), false); m_offset->inferAndCheck(ar, NEW_TYPE(Some), false); } else { if (hasContext(ExistContext) || hasContext(UnsetContext)) { if (ar->isFirstPass()) { ar->getCodeError()->record(self, CodeError::InvalidArrayElement, self); } } m_variable->inferAndCheck(ar, Type::Array, true); } if (varType && Type::SameType(varType, Type::String)) { clearEffect(AccessorEffect); m_implementedType.reset(); return Type::String; } if (varType && Type::SameType(varType, Type::Array)) { clearEffect(AccessorEffect); } if (hasContext(LValue) || hasContext(RefValue)) setEffect(CreateEffect); TypePtr ret = propagateTypes(ar, Type::Variant); m_implementedType = Type::Variant; return ret; // so not to lose values }
void ExpressionList::toLower() { for (unsigned int i = 0; i < m_exps.size(); i++) { ScalarExpressionPtr s = dynamic_pointer_cast<ScalarExpression>(m_exps[i]); s->toLower(); } }
void ClassConstant::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { if (cg.getContext() != CodeGenerator::CppClassConstantsDecl && cg.getContext() != CodeGenerator::CppClassConstantsImpl) { return; } ClassScopePtr scope = getClassScope(); for (int i = 0; i < m_exp->getCount(); i++) { AssignmentExpressionPtr exp = dynamic_pointer_cast<AssignmentExpression>((*m_exp)[i]); ConstantExpressionPtr var = dynamic_pointer_cast<ConstantExpression>(exp->getVariable()); TypePtr type = scope->getConstants()->getFinalType(var->getName()); ExpressionPtr value = exp->getValue(); if (scope->getConstants()->isDynamic(var->getName())) { continue; } switch (cg.getContext()) { case CodeGenerator::CppClassConstantsDecl: cg_printf("extern const "); if (type->is(Type::KindOfString)) { cg_printf("StaticString"); } else { type->outputCPPDecl(cg, ar, getScope()); } cg_printf(" %s%s%s%s;\n", Option::ClassConstantPrefix, scope->getId().c_str(), Option::IdPrefix.c_str(), var->getName().c_str()); break; case CodeGenerator::CppClassConstantsImpl: { bool isString = type->is(Type::KindOfString); bool isVariant = Type::IsMappedToVariant(type); ScalarExpressionPtr scalarExp = dynamic_pointer_cast<ScalarExpression>(value); bool stringForVariant = false; if (isVariant && scalarExp && scalarExp->getActualType() && scalarExp->getActualType()->is(Type::KindOfString)) { cg_printf("static const StaticString %s%s%s%s%sv(LITSTR_INIT(%s));\n", Option::ClassConstantPrefix, scope->getId().c_str(), Option::IdPrefix.c_str(), var->getName().c_str(), Option::IdPrefix.c_str(), scalarExp->getCPPLiteralString().c_str()); stringForVariant = true; } cg_printf("const "); if (isString) { cg_printf("StaticString"); } else { type->outputCPPDecl(cg, ar, getScope()); } value->outputCPPBegin(cg, ar); cg_printf(" %s%s%s%s", Option::ClassConstantPrefix, scope->getId().c_str(), Option::IdPrefix.c_str(), var->getName().c_str()); cg_printf(isString ? "(" : " = "); if (stringForVariant) { cg_printf("%s%s%s%s%sv", Option::ClassConstantPrefix, scope->getId().c_str(), Option::IdPrefix.c_str(), var->getName().c_str(), Option::IdPrefix.c_str()); } else if (isString && scalarExp) { cg_printf("LITSTR_INIT(%s)", scalarExp->getCPPLiteralString().c_str()); } else { value->outputCPP(cg, ar); } cg_printf(isString ? ");\n" : ";\n"); value->outputCPPEnd(cg, ar); break; } default: assert(false); } } }
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; } }