/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/runtime/vm/preclass-emit.h" #include "hphp/runtime/vm/repo.h" #include "hphp/runtime/vm/blob_helper.h" namespace HPHP { //============================================================================= // PreClassEmitter::Prop. PreClassEmitter::Prop::Prop(const PreClassEmitter* pce, const StringData* n, Attr attrs, const StringData* typeConstraint, const StringData* docComment, TypedValue* val, DataType hphpcType) : m_name(n) , m_attrs(attrs) , m_typeConstraint(typeConstraint) , m_docComment(docComment) , m_hphpcType(hphpcType) { m_mangledName = PreClass::manglePropName(pce->name(), n, attrs); memcpy(&m_val, val, sizeof(TypedValue)); } PreClassEmitter::Prop::~Prop() { } //============================================================================= // PreClassEmitter. PreClassEmitter::PreClassEmitter(UnitEmitter& ue, Id id, const StringData* n, PreClass::Hoistable hoistable) : m_ue(ue) , m_name(n) , m_id(id) , m_hoistable(hoistable) , m_InstanceCtor(nullptr) , m_builtinPropSize(0) {} void PreClassEmitter::init(int line1, int line2, Offset offset, Attr attrs, const StringData* parent, const StringData* docComment) { m_line1 = line1; m_line2 = line2; m_offset = offset; m_attrs = attrs; m_parent = parent; m_docComment = docComment; } PreClassEmitter::~PreClassEmitter() { for (MethodVec::const_iterator it = m_methods.begin(); it != m_methods.end(); ++it) { delete *it; } } void PreClassEmitter::addInterface(const StringData* n) { m_interfaces.push_back(n); } bool PreClassEmitter::addMethod(FuncEmitter* method) { MethodMap::const_iterator it = m_methodMap.find(method->name()); if (it != m_methodMap.end()) { return false; } m_methods.push_back(method); m_methodMap[method->name()] = method; return true; } bool PreClassEmitter::addProperty(const StringData* n, Attr attrs, const StringData* typeConstraint, const StringData* docComment, TypedValue* val, DataType hphpcType) { PropMap::Builder::const_iterator it = m_propMap.find(n); if (it != m_propMap.end()) { return false; } PreClassEmitter::Prop prop(this, n, attrs, typeConstraint, docComment, val, hphpcType); m_propMap.add(prop.name(), prop); return true; } const PreClassEmitter::Prop& PreClassEmitter::lookupProp(const StringData* propName) const { PropMap::Builder::const_iterator it = m_propMap.find(propName); assert(it != m_propMap.end()); Slot idx = it->second; return m_propMap[idx]; } bool PreClassEmitter::addConstant(const StringData* n, const StringData* typeConstraint, TypedValue* val, const StringData* phpCode) { ConstMap::Builder::const_iterator it = m_constMap.find(n); if (it != m_constMap.end()) { return false; } PreClassEmitter::Const const_(n, typeConstraint, val, phpCode); m_constMap.add(const_.name(), const_); return true; } void PreClassEmitter::addUsedTrait(const StringData* traitName) { m_usedTraits.push_back(traitName); } void PreClassEmitter::addTraitPrecRule( const PreClass::TraitPrecRule &rule) { m_traitPrecRules.push_back(rule); } void PreClassEmitter::addTraitAliasRule( const PreClass::TraitAliasRule &rule) { m_traitAliasRules.push_back(rule); } void PreClassEmitter::addUserAttribute(const StringData* name, TypedValue tv) { m_userAttributes[name] = tv; } void PreClassEmitter::commit(RepoTxn& txn) const { Repo& repo = Repo::get(); PreClassRepoProxy& pcrp = repo.pcrp(); int repoId = m_ue.repoId(); int64_t usn = m_ue.sn(); pcrp.insertPreClass(repoId) .insert(*this, txn, usn, m_id, m_name, m_hoistable); for (MethodVec::const_iterator it = m_methods.begin(); it != m_methods.end(); ++it) { (*it)->commit(txn); } } void PreClassEmitter::setBuiltinClassInfo(const ClassInfo* info, BuiltinCtorFunction ctorFunc, int sz) { if (info->getAttribute() & ClassInfo::IsFinal) { m_attrs = m_attrs | AttrFinal; } if (info->getAttribute() & ClassInfo::IsAbstract) { m_attrs = m_attrs | AttrAbstract; } if (info->getAttribute() & ClassInfo::IsTrait) { m_attrs = m_attrs | AttrTrait; } m_attrs = m_attrs | AttrUnique; m_InstanceCtor = ctorFunc; m_builtinPropSize = sz - sizeof(ObjectData); } PreClass* PreClassEmitter::create(Unit& unit) const { Attr attrs = m_attrs; if (attrs & AttrPersistent && !RuntimeOption::RepoAuthoritative && SystemLib::s_inited) { attrs = Attr(attrs & ~AttrPersistent); } PreClass* pc = new PreClass(&unit, m_line1, m_line2, m_offset, m_name, attrs, m_parent, m_docComment, m_id, m_hoistable); pc->m_InstanceCtor = m_InstanceCtor; pc->m_builtinPropSize = m_builtinPropSize; pc->m_interfaces = m_interfaces; pc->m_usedTraits = m_usedTraits; pc->m_traitPrecRules = m_traitPrecRules; pc->m_traitAliasRules = m_traitAliasRules; pc->m_userAttributes = m_userAttributes; PreClass::MethodMap::Builder methodBuild; for (MethodVec::const_iterator it = m_methods.begin(); it != m_methods.end(); ++it) { Func* f = (*it)->create(unit, pc); methodBuild.add(f->name(), f); } pc->m_methods.create(methodBuild); PreClass::PropMap::Builder propBuild; for (unsigned i = 0; i < m_propMap.size(); ++i) { const Prop& prop = m_propMap[i]; propBuild.add(prop.name(), PreClass::Prop(pc, prop.name(), prop.attrs(), prop.typeConstraint(), prop.docComment(), prop.val(), prop.hphpcType())); } pc->m_properties.create(propBuild); PreClass::ConstMap::Builder constBuild; for (unsigned i = 0; i < m_constMap.size(); ++i) { const Const& const_ = m_constMap[i]; constBuild.add(const_.name(), PreClass::Const(pc, const_.name(), const_.typeConstraint(), const_.val(), const_.phpCode())); } pc->m_constants.create(constBuild); return pc; } template void PreClassEmitter::serdeMetaData(SerDe& sd) { // NOTE: name, hoistable, and a few other fields currently // serialized outside of this. sd(m_line1) (m_line2) (m_offset) (m_attrs) (m_parent) (m_docComment) (m_interfaces) (m_usedTraits) (m_traitPrecRules) (m_traitAliasRules) (m_userAttributes) (m_propMap) (m_constMap) ; } //============================================================================= // PreClassRepoProxy. PreClassRepoProxy::PreClassRepoProxy(Repo& repo) : RepoProxy(repo) #define PCRP_OP(c, o) \ , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral) PCRP_OPS #undef PCRP_OP { #define PCRP_OP(c, o) \ m_##o[RepoIdLocal] = &m_##o##Local; \ m_##o[RepoIdCentral] = &m_##o##Central; PCRP_OPS #undef PCRP_OP } PreClassRepoProxy::~PreClassRepoProxy() { } void PreClassRepoProxy::createSchema(int repoId, RepoTxn& txn) { { std::stringstream ssCreate; ssCreate << "CREATE TABLE " << m_repo.table(repoId, "PreClass") << "(unitSn INTEGER, preClassId INTEGER, name TEXT," " hoistable INTEGER, extraData BLOB," " PRIMARY KEY (unitSn, preClassId));"; txn.exec(ssCreate.str()); } } void PreClassRepoProxy::InsertPreClassStmt ::insert(const PreClassEmitter& pce, RepoTxn& txn, int64_t unitSn, Id preClassId, const StringData* name, PreClass::Hoistable hoistable) { if (!prepared()) { std::stringstream ssInsert; ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "PreClass") << " VALUES(@unitSn, @preClassId, @name, @hoistable, " "@extraData);"; txn.prepare(*this, ssInsert.str()); } BlobEncoder extraBlob; RepoTxnQuery query(txn, *this); query.bindInt64("@unitSn", unitSn); query.bindId("@preClassId", preClassId); query.bindStaticString("@name", name); query.bindInt("@hoistable", hoistable); const_cast(pce).serdeMetaData(extraBlob); query.bindBlob("@extraData", extraBlob, /* static */ true); query.exec(); } void PreClassRepoProxy::GetPreClassesStmt ::get(UnitEmitter& ue) { RepoTxn txn(m_repo); if (!prepared()) { std::stringstream ssSelect; ssSelect << "SELECT preClassId,name,hoistable,extraData FROM " << m_repo.table(m_repoId, "PreClass") << " WHERE unitSn == @unitSn ORDER BY preClassId ASC;"; txn.prepare(*this, ssSelect.str()); } RepoTxnQuery query(txn, *this); query.bindInt64("@unitSn", ue.sn()); do { query.step(); if (query.row()) { Id preClassId; /**/ query.getId(0, preClassId); StringData* name; /**/ query.getStaticString(1, name); int hoistable; /**/ query.getInt(2, hoistable); BlobDecoder extraBlob = /**/ query.getBlob(3); PreClassEmitter* pce = ue.newPreClassEmitter( name, (PreClass::Hoistable)hoistable); pce->serdeMetaData(extraBlob); assert(pce->id() == preClassId); } } while (!query.done()); txn.commit(); } } // HPHP