/* +----------------------------------------------------------------------+ | 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 "parser.h" #include "hphp/util/hash.h" #include namespace HPHP { /////////////////////////////////////////////////////////////////////////////// bool ParserBase::IsClosureName(const std::string &name) { return boost::istarts_with(name, "closure$"); } bool ParserBase::IsContinuationName(const std::string &name) { return boost::iends_with(name, "$continuation"); } bool ParserBase::IsClosureOrContinuationName(const std::string &name) { return IsClosureName(name) || IsContinuationName(name); } std::string ParserBase::newContinuationName(const std::string &name) { assert(!name.empty()); size_t pos = 0; std::string shorterName = name; std::string suffix("$continuation"); while((pos = shorterName.find(suffix, pos)) != std::string::npos) { shorterName.replace(pos, suffix.length(), ""); } return shorterName + suffix; } std::string ParserBase::newClosureName( const std::string &className, const std::string &funcName) { std::string name = "Closure$"; if (!className.empty()) { name += className + "::"; } name += funcName; int id = ++m_seenClosures[name]; if (id > 1) { // we've seen the same name before, uniquify name = name + '#' + std::to_string(id); } return name; } /////////////////////////////////////////////////////////////////////////////// ParserBase::ParserBase(Scanner &scanner, const char *fileName) : m_scanner(scanner), m_fileName(fileName) { if (m_fileName == nullptr) m_fileName = ""; // global scope m_labelInfos.reserve(3); m_labelInfos.resize(1); pushLabelScope(); } ParserBase::~ParserBase() { } std::string ParserBase::getMessage(bool filename /* = false */) const { string ret = m_scanner.getError(); if (!ret.empty()) { ret += " "; } ret += getMessage(m_scanner.getLocation(), filename); return ret; } std::string ParserBase::getMessage(Location *loc, bool filename /* = false */) const { int line = loc->line1; int column = loc->char1; string ret = "("; if (filename) { ret += string("File: ") + file() + ", "; } ret += string("Line: ") + boost::lexical_cast(line); ret += ", Char: " + boost::lexical_cast(column) + ")"; return ret; } LocationPtr ParserBase::getLocation() const { LocationPtr location(new Location()); location->file = file(); location->line0 = line0(); location->char0 = char0(); location->line1 = line1(); location->char1 = char1(); location->cursor = cursor(); return location; } /////////////////////////////////////////////////////////////////////////////// // T_FUNCTION related functions void ParserBase::pushFuncLocation() { m_funcLocs.push_back(getLocation()); } LocationPtr ParserBase::popFuncLocation() { assert(!m_funcLocs.empty()); LocationPtr loc = m_funcLocs.back(); m_funcLocs.pop_back(); return loc; } void ParserBase::pushClass(bool isXhpClass) { m_classes.push_back(isXhpClass); } bool ParserBase::peekClass() { assert(!m_classes.empty()); return m_classes.back(); } void ParserBase::popClass() { m_classes.pop_back(); } /////////////////////////////////////////////////////////////////////////////// // typevar scopes void ParserBase::pushTypeScope() { m_typeScopes.push_back(m_typeVars); m_typeVars.clear(); } void ParserBase::popTypeScope() { m_typeScopes.pop_back(); } void ParserBase::addTypeVar(const std::string &name) { m_typeVars.insert(name); } bool ParserBase::isTypeVar(const std::string &name) { for (TypevarScopeStack::iterator iter = m_typeScopes.begin(); iter != m_typeScopes.end(); iter++) { if (iter->find(name) != iter->end()) { return true; } } return false; } /////////////////////////////////////////////////////////////////////////////// // checks GOTO label syntax void ParserBase::pushLabelInfo() { m_labelInfos.resize(m_labelInfos.size() + 1); pushLabelScope(); } void ParserBase::pushLabelScope() { assert(!m_labelInfos.empty()); LabelInfo &info = m_labelInfos.back(); info.scopes.push_back(++info.scopeId); } void ParserBase::popLabelScope() { assert(!m_labelInfos.empty()); LabelInfo &info = m_labelInfos.back(); info.scopes.pop_back(); } void ParserBase::addLabel(const std::string &label, LocationPtr loc, ScannerToken *stmt) { assert(!m_labelInfos.empty()); LabelInfo &info = m_labelInfos.back(); if (info.labels.find(label) != info.labels.end()) { error("Label '%s' already defined: %s", label.c_str(), getMessage().c_str()); invalidateLabel(extractStatement(stmt)); return; } assert(!info.scopes.empty()); LabelStmtInfo labelInfo; labelInfo.scopeId = info.scopes.back(); labelInfo.stmt = extractStatement(stmt); labelInfo.loc = loc; info.labels[label] = labelInfo; } void ParserBase::addGoto(const std::string &label, LocationPtr loc, ScannerToken *stmt) { assert(!m_labelInfos.empty()); LabelInfo &info = m_labelInfos.back(); GotoInfo gotoInfo; gotoInfo.label = label; gotoInfo.scopes = info.scopes; gotoInfo.loc = loc; gotoInfo.stmt = extractStatement(stmt); info.gotos.push_back(gotoInfo); } void ParserBase::popLabelInfo() { assert(!m_labelInfos.empty()); LabelInfo &info = m_labelInfos.back(); LabelMap labels = info.labels; // shallow copy for (unsigned int i = 0; i < info.gotos.size(); i++) { const GotoInfo &gotoInfo = info.gotos[i]; LabelMap::const_iterator iter = info.labels.find(gotoInfo.label); if (iter == info.labels.end()) { invalidateGoto(gotoInfo.stmt, UndefLabel); error("'goto' to undefined label '%s': %s", gotoInfo.label.c_str(), getMessage(gotoInfo.loc.get()).c_str()); continue; } const LabelStmtInfo &labelInfo = iter->second; int labelScopeId = labelInfo.scopeId; bool found = false; for (int j = gotoInfo.scopes.size() - 1; j >= 0; j--) { if (labelScopeId == gotoInfo.scopes[j]) { found = true; break; } } if (!found) { invalidateGoto(gotoInfo.stmt, InvalidBlock); error("'goto' into loop or switch statement " "is disallowed: %s", getMessage(gotoInfo.loc.get()).c_str()); continue; } else { labels.erase(gotoInfo.label); } } // now invalidate all un-used labels for (LabelMap::const_iterator it(labels.begin()); it != labels.end(); ++it) { invalidateLabel(it->second.stmt); } m_labelInfos.pop_back(); } /////////////////////////////////////////////////////////////////////////////// }