Arquivos
hhvm/hphp/util/parser/parser.cpp
T
jan 10b770d342 Simplify hphp_unpack_continuation
Do not generate yield labels as normal goto labels in the parser. Create
them at YieldExpression and use m_yieldLabels array in emitter.
2013-03-25 12:43:42 -07:00

386 linhas
11 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- 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 <util/hash.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
Mutex ParserBase::s_mutex;
std::map<int64_t, int> ParserBase::s_closureIds;
char ParserBase::GetAnonPrefix(AnonFuncKind kind) {
static_assert(Closure == 0 && Continuation <= 9,
"AnonFuncKind enum has unexpected values");
static_assert(CharClosure == '0' && CharContinuation <= '9',
"AnonFuncKindChar enum has unexpected values");
return '0' + kind;
}
template <int i>
static bool NameImpl(const std::string &name) {
return !name.empty() && isdigit(name[0]) && i == (name[0] - '0');
}
bool ParserBase::IsClosureName(const std::string &name) {
return NameImpl<Closure>(name);
}
bool ParserBase::IsCreateFunctionName(const std::string &name) {
return NameImpl<CreateFunction>(name);
}
bool ParserBase::IsContinuationName(const std::string &name) {
return NameImpl<ContinuationFromClosure>(name) ||
NameImpl<Continuation>(name);
}
bool ParserBase::IsContinuationFromClosureName(const std::string &name) {
return NameImpl<ContinuationFromClosure>(name);
}
bool ParserBase::IsClosureOrContinuationName(const std::string &name) {
return IsClosureName(name) || IsContinuationName(name);
}
bool ParserBase::IsAnonFunctionName(const char *name) {
if (!*name) return true;
char begin = CharClosure;
char end = CharContinuation;
char test = name[0];
return begin <= test && test <= end;
}
void ParserBase::Reset() {
Lock lock(s_mutex);
s_closureIds.clear();
}
///////////////////////////////////////////////////////////////////////////////
ParserBase::ParserBase(Scanner &scanner, const char *fileName)
: m_scanner(scanner), m_fileName(fileName), m_nsState(SeenNothing) {
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<string>(line);
ret += ", Char: " + boost::lexical_cast<string>(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();
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();
}
std::string ParserBase::getAnonFuncName(AnonFuncKind kind) {
int64_t h = hash_string_cs(m_fileName, strlen(m_fileName));
int closureId;
{
Lock lock(s_mutex);
int &id = s_closureIds[h];
closureId = ++id;
}
string ret;
ret += GetAnonPrefix(kind);
ret += boost::lexical_cast<string>(h);
ret += "_";
ret += boost::lexical_cast<string>(closureId);
return ret;
}
///////////////////////////////////////////////////////////////////////////////
// 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();
}
///////////////////////////////////////////////////////////////////////////////
// namespace support
void ParserBase::nns(bool declare /* = false */) {
if (m_nsState == SeenNamespaceStatement) {
error("No code may exist outside of namespace {}: %s",
getMessage().c_str());
return;
}
if (m_nsState == SeenNothing && !declare) {
m_nsState = SeenNonNamespaceStatement;
}
}
void ParserBase::onNamespaceStart(const std::string &ns) {
if (m_nsState == SeenNonNamespaceStatement) {
error("Namespace declaration statement has to be the very first "
"statement in the script: %s", getMessage().c_str());
return;
}
m_nsState = InsideNamespace;
m_namespace = ns;
}
void ParserBase::onNamespaceEnd() {
m_nsState = SeenNamespaceStatement;
}
void ParserBase::onUse(const std::string &ns, const std::string &as) {
if (m_aliases.find(as) != m_aliases.end()) {
error("Cannot use %s as %s because the name is already in use: %s",
ns.c_str(), as.c_str(), getMessage().c_str());
return;
}
string key = as;
if (key.empty()) {
size_t pos = ns.rfind(NAMESPACE_SEP);
if (pos == string::npos) {
key = ns;
} else {
key = ns.substr(pos + 1);
}
}
m_aliases[key] = ns;
}
std::string ParserBase::nsDecl(const std::string &name) {
if (m_namespace.empty()) {
return name;
}
return m_namespace + NAMESPACE_SEP + name;
}
std::string ParserBase::resolve(const std::string &ns, bool cls) {
// try import rules first
string alias = ns;
size_t pos = ns.find(NAMESPACE_SEP);
if (pos != string::npos) {
alias = ns.substr(0, pos);
}
hphp_string_imap<std::string>::const_iterator iter = m_aliases.find(alias);
if (iter != m_aliases.end()) {
if (pos != string::npos) {
return iter->second + ns.substr(pos);
}
return iter->second;
}
// if qualified name, prepend current namespace
if (pos != string::npos) {
return nsDecl(ns);
}
// unqualified name in global namespace
if (m_namespace.empty()) {
return ns;
}
// unqualified class name always prefixed with NAMESPACE_SEP
if (cls) {
if (strcasecmp("self", ns.c_str()) && strcasecmp("parent", ns.c_str())) {
return m_namespace + NAMESPACE_SEP + ns;
}
return ns;
}
if (!strcasecmp("true", ns.c_str()) ||
!strcasecmp("false", ns.c_str()) ||
!strcasecmp("null", ns.c_str())) {
return ns;
}
// unqualified function name needs leading NAMESPACE_SEP to indicate this
// needs runtime resolution
return NAMESPACE_SEP + m_namespace + NAMESPACE_SEP + ns;
}
///////////////////////////////////////////////////////////////////////////////
}