998951619f
We did not intend to imply our copyrights last forever Closes #759
427 linhas
12 KiB
C++
427 linhas
12 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| 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 <stdarg.h>
|
|
|
|
#include "hphp/compiler/code_generator.h"
|
|
#include "hphp/compiler/statement/statement_list.h"
|
|
#include "hphp/compiler/option.h"
|
|
#include "hphp/compiler/analysis/file_scope.h"
|
|
#include "hphp/compiler/analysis/function_scope.h"
|
|
#include "hphp/compiler/analysis/analysis_result.h"
|
|
#include "hphp/compiler/analysis/variable_table.h"
|
|
#include "hphp/util/util.h"
|
|
#include "hphp/util/hash.h"
|
|
#include <boost/format.hpp>
|
|
#include <boost/scoped_array.hpp>
|
|
|
|
using namespace HPHP;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// statics
|
|
|
|
void CodeGenerator::BuildJumpTable(const std::vector<const char *> &strings,
|
|
MapIntToStringVec &out, int tableSize,
|
|
bool caseInsensitive) {
|
|
assert(!strings.empty());
|
|
assert(out.empty());
|
|
assert(tableSize > 0);
|
|
|
|
for (unsigned int i = 0; i < strings.size(); i++) {
|
|
const char *s = strings[i];
|
|
int hash = (caseInsensitive ? hash_string_i(s) : hash_string(s)) %
|
|
tableSize;
|
|
out[hash].push_back(s);
|
|
}
|
|
}
|
|
|
|
const char *CodeGenerator::STARTER_MARKER =
|
|
"namespace hphp_impl_starter {}";
|
|
const char *CodeGenerator::SPLITTER_MARKER =
|
|
"namespace hphp_impl_splitter {}";
|
|
const char *CodeGenerator::HASH_INCLUDE =
|
|
"#include";
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
CodeGenerator::CodeGenerator(std::ostream *primary,
|
|
Output output /* = PickledPHP */,
|
|
const std::string *filename /* = NULL */)
|
|
: m_out(nullptr), m_output(output),
|
|
m_context(NoContext), m_itemIndex(-1) {
|
|
for (int i = 0; i < StreamCount; i++) {
|
|
m_streams[i] = nullptr;
|
|
m_indentation[i] = 0;
|
|
m_indentPending[i] = true;
|
|
m_lineNo[i] = 1;
|
|
m_inComments[i] = 0;
|
|
m_wrappedExpression[i] = false;
|
|
m_inExpression[i] = false;
|
|
}
|
|
setStream(PrimaryStream, primary);
|
|
useStream(PrimaryStream);
|
|
|
|
if (filename) m_filename = *filename;
|
|
m_translatePredefined = false;
|
|
m_scalarVariant = false;
|
|
m_initListFirstElem = false;
|
|
m_inFileOrClassHeader = false;
|
|
m_inNamespace = false;
|
|
}
|
|
|
|
void CodeGenerator::useStream(Stream stream) {
|
|
assert(stream >= NullStream && stream < StreamCount);
|
|
m_curStream = stream;
|
|
if (stream == NullStream) {
|
|
m_out = nullptr;
|
|
} else {
|
|
m_out = m_streams[stream];
|
|
}
|
|
}
|
|
|
|
bool CodeGenerator::usingStream(Stream stream) {
|
|
assert(stream >= 0 && stream < StreamCount);
|
|
return m_out == m_streams[stream];
|
|
}
|
|
|
|
std::ostream *CodeGenerator::getStream(Stream stream) const {
|
|
assert(stream >= 0 && stream < StreamCount);
|
|
return m_streams[stream];
|
|
}
|
|
|
|
void CodeGenerator::setStream(Stream stream, std::ostream *out) {
|
|
assert(out);
|
|
assert(stream >= 0 && stream < StreamCount);
|
|
m_streams[stream] = out;
|
|
}
|
|
|
|
int CodeGenerator::getLineNo(Stream stream) const {
|
|
assert(stream >= 0 && stream < StreamCount);
|
|
return m_lineNo[stream];
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CodeGenerator::printf(const char *fmt, ...) {
|
|
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
|
|
}
|
|
|
|
void CodeGenerator::indentBegin(const char *fmt, ...) {
|
|
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
|
|
m_indentation[m_curStream]++;
|
|
}
|
|
|
|
void CodeGenerator::indentBegin() {
|
|
m_indentation[m_curStream]++;
|
|
}
|
|
|
|
void CodeGenerator::indentEnd(const char *fmt, ...) {
|
|
assert(m_indentation[m_curStream]);
|
|
m_indentation[m_curStream]--;
|
|
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
|
|
}
|
|
|
|
void CodeGenerator::indentEnd() {
|
|
assert(m_indentation[m_curStream]);
|
|
m_indentation[m_curStream]--;
|
|
}
|
|
|
|
bool CodeGenerator::inComments() const {
|
|
return m_inComments[m_curStream] > 0;
|
|
}
|
|
|
|
void CodeGenerator::startComments() {
|
|
m_inComments[m_curStream]++;
|
|
printf(" /*");
|
|
}
|
|
|
|
void CodeGenerator::endComments() {
|
|
assert(m_inComments[m_curStream] > 0);
|
|
m_inComments[m_curStream]--;
|
|
printf(" */");
|
|
}
|
|
|
|
void CodeGenerator::printSection(const char *name, bool newline /* = true */) {
|
|
if (newline) printf("\n");
|
|
printf("// %s\n", name);
|
|
}
|
|
|
|
void CodeGenerator::printSeparator() {
|
|
printf("///////////////////////////////////////"
|
|
"////////////////////////////////////////\n");
|
|
}
|
|
|
|
void CodeGenerator::namespaceBegin() {
|
|
always_assert(!m_inNamespace);
|
|
m_inNamespace = true;
|
|
printf("\n");
|
|
printf("namespace HPHP {\n");
|
|
printSeparator();
|
|
printf("\n");
|
|
}
|
|
|
|
void CodeGenerator::namespaceEnd() {
|
|
always_assert(m_inNamespace);
|
|
m_inNamespace = false;
|
|
printf("\n");
|
|
printSeparator();
|
|
printf("}\n");
|
|
}
|
|
|
|
std::string CodeGenerator::getFormattedName(const std::string &file) {
|
|
char *fn = strdup(file.c_str());
|
|
int len = strlen(fn);
|
|
always_assert(len == (int)file.size());
|
|
for (int i = 0; i < len; i++) {
|
|
if (!isalnum(fn[i])) fn[i] = '_';
|
|
}
|
|
string formatted = fn;
|
|
free(fn);
|
|
int hash = hash_string(file.data(), file.size());
|
|
formatted += boost::str(boost::format("%08x") % hash);
|
|
return formatted;
|
|
}
|
|
|
|
bool CodeGenerator::ensureInNamespace() {
|
|
if (m_inNamespace) return false;
|
|
namespaceBegin();
|
|
return true;
|
|
}
|
|
|
|
bool CodeGenerator::ensureOutOfNamespace() {
|
|
if (!m_inNamespace) return false;
|
|
namespaceEnd();
|
|
return true;
|
|
}
|
|
|
|
void CodeGenerator::ifdefBegin(bool ifdef, const char *fmt, ...) {
|
|
printf(ifdef ? "#ifdef " : "#ifndef ");
|
|
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
|
|
printf("\n");
|
|
}
|
|
|
|
void CodeGenerator::ifdefEnd(const char *fmt, ...) {
|
|
printf("#endif // ");
|
|
va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
|
|
printf("\n");
|
|
}
|
|
|
|
void CodeGenerator::printDocComment(const std::string comment) {
|
|
if (comment.empty()) return;
|
|
string escaped;
|
|
escaped.reserve(comment.size() + 10);
|
|
for (unsigned int i = 0; i < comment.size(); i++) {
|
|
char ch = comment[i];
|
|
escaped += ch;
|
|
if (ch == '/' && i > 1 && comment[i+1] == '*') {
|
|
escaped += '\\'; // splitting illegal /* into /\*
|
|
}
|
|
}
|
|
print(escaped.c_str(), false);
|
|
printf("\n");
|
|
}
|
|
|
|
std::string CodeGenerator::FormatLabel(const std::string &name) {
|
|
string ret;
|
|
ret.reserve(name.size());
|
|
for (size_t i = 0; i < name.size(); i++) {
|
|
unsigned char ch = name[i];
|
|
if ((ch >= 'a' && ch <= 'z') ||
|
|
(ch >= 'A' && ch <= 'Z') ||
|
|
(ch >= '0' && ch <= '9') || ch == '_') {
|
|
ret += ch;
|
|
} else {
|
|
char buf[10];
|
|
snprintf(buf, sizeof(buf), "%s%02X", Option::LabelEscape.c_str(),
|
|
(int)ch);
|
|
ret += buf;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::string CodeGenerator::EscapeLabel(const std::string &name,
|
|
bool *binary /* = NULL */) {
|
|
return Util::escapeStringForCPP(name, binary);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// helpers
|
|
|
|
void CodeGenerator::print(const char *fmt, va_list ap) {
|
|
if (!m_out) return;
|
|
boost::scoped_array<char> buf;
|
|
bool done = false;
|
|
for (int len = 1024; !done; len <<= 1) {
|
|
va_list v;
|
|
va_copy(v, ap);
|
|
|
|
buf.reset(new char[len]);
|
|
if (vsnprintf(buf.get(), len, fmt, v) < len) done = true;
|
|
|
|
va_end(v);
|
|
}
|
|
print(buf.get());
|
|
}
|
|
|
|
void CodeGenerator::print(const char *msg, bool indent /* = true */) {
|
|
const char *start = msg;
|
|
int length = 1;
|
|
for (const char *iter = msg; ; ++iter, ++length) {
|
|
if (*iter == '\n') {
|
|
if (indent) {
|
|
// Only indent if it is pending and if it is not an empty line
|
|
if (m_indentPending[m_curStream] && length > 1) printIndent();
|
|
|
|
// Printing substrings requires an additional copy operation,
|
|
// so do it only if necessary
|
|
if (iter[1] != '\0') {
|
|
printSubstring(start, length);
|
|
} else {
|
|
*m_out << start;
|
|
}
|
|
start = iter + 1;
|
|
length = 0;
|
|
}
|
|
m_lineNo[m_curStream]++;
|
|
m_indentPending[m_curStream] = true;
|
|
} else if (*iter == '\0') {
|
|
// Perform print only in case what's left is not an empty string
|
|
if (length > 1) {
|
|
if (indent && m_indentPending[m_curStream]) {
|
|
printIndent();
|
|
m_indentPending[m_curStream] = false;
|
|
}
|
|
*m_out << start;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CodeGenerator::printSubstring(const char *start, int length) {
|
|
const int BUF_LEN = 0x100;
|
|
char buf[BUF_LEN];
|
|
while (length > 0) {
|
|
int curLength = std::min(length, BUF_LEN - 1);
|
|
memcpy(buf, start, curLength);
|
|
buf[curLength] = '\0';
|
|
*m_out << buf;
|
|
length -= curLength;
|
|
start += curLength;
|
|
}
|
|
}
|
|
|
|
void CodeGenerator::printIndent() {
|
|
for (int i = 0; i < m_indentation[m_curStream]; i++) {
|
|
*m_out << Option::Tab;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
int CodeGenerator::s_idLambda = 0;
|
|
string CodeGenerator::GetNewLambda() {
|
|
return Option::LambdaPrefix + "lambda_" +
|
|
boost::lexical_cast<string>(++s_idLambda);
|
|
}
|
|
|
|
void CodeGenerator::resetIdCount(const std::string &key) {
|
|
m_idCounters[key] = 0;
|
|
}
|
|
|
|
int CodeGenerator::createNewId(const std::string &key) {
|
|
return ++m_idCounters[key];
|
|
}
|
|
|
|
int CodeGenerator::createNewId(ConstructPtr cs) {
|
|
FileScopePtr fs = cs->getFileScope();
|
|
if (fs) {
|
|
return createNewId(fs->getName());
|
|
}
|
|
return createNewId("");
|
|
}
|
|
|
|
int CodeGenerator::createNewLocalId(ConstructPtr ar) {
|
|
if (m_wrappedExpression[m_curStream]) {
|
|
return m_localId[m_curStream]++;
|
|
}
|
|
FunctionScopePtr func = ar->getFunctionScope();
|
|
if (func) {
|
|
return func->nextInlineIndex();
|
|
}
|
|
FileScopePtr fs = ar->getFileScope();
|
|
if (fs) {
|
|
return createNewId(fs->getName());
|
|
}
|
|
return createNewId("");
|
|
}
|
|
|
|
void CodeGenerator::pushBreakScope(int labelId,
|
|
bool loopCounter /* = true */) {
|
|
m_breakScopes.push_back(labelId);
|
|
if (loopCounter) {
|
|
printf("LOOP_COUNTER(%d);\n", int(labelId & ~BreakScopeBitMask));
|
|
}
|
|
}
|
|
|
|
void CodeGenerator::popBreakScope() {
|
|
m_breakScopes.pop_back();
|
|
if (m_breakScopes.size() == 0) {
|
|
m_breakLabelIds.clear();
|
|
m_contLabelIds.clear();
|
|
}
|
|
}
|
|
|
|
void CodeGenerator::pushCallInfo(int cit) {
|
|
m_callInfos.push_back(cit);
|
|
}
|
|
void CodeGenerator::popCallInfo() {
|
|
m_callInfos.pop_back();
|
|
}
|
|
int CodeGenerator::callInfoTop() {
|
|
if (m_callInfos.empty()) return -1;
|
|
return m_callInfos.back();
|
|
}
|
|
|
|
void CodeGenerator::addLabelId(const char *name, int labelId) {
|
|
if (!strcmp(name, "break")) {
|
|
m_breakLabelIds.insert(labelId);
|
|
} else if (!strcmp(name, "continue")) {
|
|
m_contLabelIds.insert(labelId);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
bool CodeGenerator::findLabelId(const char *name, int labelId) {
|
|
if (!strcmp(name, "break")) {
|
|
return m_breakLabelIds.find(labelId) != m_breakLabelIds.end();
|
|
} else if (!strcmp(name, "continue")) {
|
|
return m_contLabelIds.find(labelId) != m_contLabelIds.end();
|
|
} else {
|
|
assert(false);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int CodeGenerator::ClassScopeCompare::cmp(const ClassScopeRawPtr &p1,
|
|
const ClassScopeRawPtr &p2) const {
|
|
int d = p1->getRedeclaringId() - p2->getRedeclaringId();
|
|
if (d) return d;
|
|
return strcasecmp(p1->getName().c_str(), p2->getName().c_str());
|
|
}
|