Remove deprecated slice() and kvzip() methods
[hiphop-php.git] / hphp / compiler / code_generator.cpp
bloba65be3a04f9efb226d07005698b67b5ba8876b59
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/compiler/code_generator.h"
18 #include "hphp/compiler/code_model_enums.h"
19 #include "hphp/compiler/statement/statement_list.h"
20 #include "hphp/compiler/expression/expression_list.h"
21 #include "hphp/compiler/option.h"
22 #include "hphp/compiler/analysis/file_scope.h"
23 #include "hphp/compiler/analysis/function_scope.h"
24 #include "hphp/compiler/analysis/analysis_result.h"
25 #include "hphp/compiler/analysis/variable_table.h"
26 #include "hphp/runtime/base/zend-printf.h"
27 #include "hphp/util/text-util.h"
28 #include "hphp/util/hash.h"
29 #include <boost/format.hpp>
30 #include <boost/scoped_array.hpp>
31 #include <algorithm>
32 #include <vector>
34 using namespace HPHP;
36 ///////////////////////////////////////////////////////////////////////////////
37 // statics
39 void CodeGenerator::BuildJumpTable(const std::vector<const char *> &strings,
40 MapIntToStringVec &out, int tableSize,
41 bool caseInsensitive) {
42 assert(!strings.empty());
43 assert(out.empty());
44 assert(tableSize > 0);
46 for (unsigned int i = 0; i < strings.size(); i++) {
47 const char *s = strings[i];
48 int hash = (caseInsensitive ? hash_string_i(s) : hash_string(s)) %
49 tableSize;
50 out[hash].push_back(s);
54 const char *CodeGenerator::STARTER_MARKER =
55 "namespace hphp_impl_starter {}";
56 const char *CodeGenerator::SPLITTER_MARKER =
57 "namespace hphp_impl_splitter {}";
58 const char *CodeGenerator::HASH_INCLUDE =
59 "#include";
61 ///////////////////////////////////////////////////////////////////////////////
63 CodeGenerator::CodeGenerator(std::ostream *primary,
64 Output output /* = PickledPHP */,
65 const std::string *filename /* = NULL */)
66 : m_out(nullptr), m_output(output),
67 m_context(NoContext), m_itemIndex(-1) {
68 for (int i = 0; i < StreamCount; i++) {
69 m_streams[i] = nullptr;
70 m_indentation[i] = 0;
71 m_indentPending[i] = true;
72 m_lineNo[i] = 1;
73 m_inComments[i] = 0;
74 m_wrappedExpression[i] = false;
75 m_inExpression[i] = false;
77 setStream(PrimaryStream, primary);
78 useStream(PrimaryStream);
80 if (filename) m_filename = *filename;
81 m_translatePredefined = false;
82 m_scalarVariant = false;
83 m_initListFirstElem = false;
84 m_inFileOrClassHeader = false;
85 m_inNamespace = false;
88 void CodeGenerator::useStream(Stream stream) {
89 assert(stream >= NullStream && stream < StreamCount);
90 m_curStream = stream;
91 if (stream == NullStream) {
92 m_out = nullptr;
93 } else {
94 m_out = m_streams[stream];
98 bool CodeGenerator::usingStream(Stream stream) {
99 assert(stream >= 0 && stream < StreamCount);
100 return m_out == m_streams[stream];
103 std::ostream *CodeGenerator::getStream(Stream stream) const {
104 assert(stream >= 0 && stream < StreamCount);
105 return m_streams[stream];
108 void CodeGenerator::setStream(Stream stream, std::ostream *out) {
109 assert(out);
110 assert(stream >= 0 && stream < StreamCount);
111 m_streams[stream] = out;
114 int CodeGenerator::getLineNo(Stream stream) const {
115 assert(stream >= 0 && stream < StreamCount);
116 return m_lineNo[stream];
119 ///////////////////////////////////////////////////////////////////////////////
121 void CodeGenerator::printf(const char *fmt, ...) {
122 va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
125 void CodeGenerator::indentBegin(const char *fmt, ...) {
126 va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
127 m_indentation[m_curStream]++;
130 void CodeGenerator::indentBegin() {
131 m_indentation[m_curStream]++;
134 void CodeGenerator::indentEnd(const char *fmt, ...) {
135 assert(m_indentation[m_curStream]);
136 m_indentation[m_curStream]--;
137 va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
140 void CodeGenerator::indentEnd() {
141 assert(m_indentation[m_curStream]);
142 m_indentation[m_curStream]--;
145 bool CodeGenerator::inComments() const {
146 return m_inComments[m_curStream] > 0;
149 void CodeGenerator::startComments() {
150 m_inComments[m_curStream]++;
151 printf(" /*");
154 void CodeGenerator::endComments() {
155 assert(m_inComments[m_curStream] > 0);
156 m_inComments[m_curStream]--;
157 printf(" */");
160 void CodeGenerator::printSection(const char *name, bool newline /* = true */) {
161 if (newline) printf("\n");
162 printf("// %s\n", name);
165 void CodeGenerator::printSeparator() {
166 printf("///////////////////////////////////////"
167 "////////////////////////////////////////\n");
170 void CodeGenerator::namespaceBegin() {
171 always_assert(!m_inNamespace);
172 m_inNamespace = true;
173 printf("\n");
174 printf("namespace HPHP {\n");
175 printSeparator();
176 printf("\n");
179 void CodeGenerator::namespaceEnd() {
180 always_assert(m_inNamespace);
181 m_inNamespace = false;
182 printf("\n");
183 printSeparator();
184 printf("}\n");
187 std::string CodeGenerator::getFormattedName(const std::string &file) {
188 char *fn = strdup(file.c_str());
189 int len = strlen(fn);
190 always_assert(len == (int)file.size());
191 for (int i = 0; i < len; i++) {
192 if (!isalnum(fn[i])) fn[i] = '_';
194 string formatted = fn;
195 free(fn);
196 int hash = hash_string(file.data(), file.size());
197 formatted += boost::str(boost::format("%08x") % hash);
198 return formatted;
201 bool CodeGenerator::ensureInNamespace() {
202 if (m_inNamespace) return false;
203 namespaceBegin();
204 return true;
207 bool CodeGenerator::ensureOutOfNamespace() {
208 if (!m_inNamespace) return false;
209 namespaceEnd();
210 return true;
213 void CodeGenerator::ifdefBegin(bool ifdef, const char *fmt, ...) {
214 printf(ifdef ? "#ifdef " : "#ifndef ");
215 va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
216 printf("\n");
219 void CodeGenerator::ifdefEnd(const char *fmt, ...) {
220 printf("#endif // ");
221 va_list ap; va_start(ap, fmt); print(fmt, ap); va_end(ap);
222 printf("\n");
225 void CodeGenerator::printDocComment(const std::string comment) {
226 if (comment.empty()) return;
227 string escaped;
228 escaped.reserve(comment.size() + 10);
229 for (unsigned int i = 0; i < comment.size(); i++) {
230 char ch = comment[i];
231 escaped += ch;
232 if (ch == '/' && i > 1 && comment[i+1] == '*') {
233 escaped += '\\'; // splitting illegal /* into /\*
236 print(escaped.c_str(), false);
237 printf("\n");
240 std::string CodeGenerator::EscapeLabel(const std::string &name,
241 bool *binary /* = NULL */) {
242 return escapeStringForCPP(name, binary);
245 ///////////////////////////////////////////////////////////////////////////////
246 // helpers
248 void CodeGenerator::print(const char *fmt, va_list ap) {
249 if (!m_out) return;
250 boost::scoped_array<char> buf;
251 bool done = false;
252 for (int len = 1024; !done; len <<= 1) {
253 va_list v;
254 va_copy(v, ap);
256 buf.reset(new char[len]);
257 if (vsnprintf(buf.get(), len, fmt, v) < len) done = true;
259 va_end(v);
261 print(buf.get());
264 void CodeGenerator::print(const char *msg, bool indent /* = true */) {
265 const char *start = msg;
266 int length = 1;
267 for (const char *iter = msg; ; ++iter, ++length) {
268 if (*iter == '\n') {
269 if (indent) {
270 // Only indent if it is pending and if it is not an empty line
271 if (m_indentPending[m_curStream] && length > 1) printIndent();
273 // Printing substrings requires an additional copy operation,
274 // so do it only if necessary
275 if (iter[1] != '\0') {
276 printSubstring(start, length);
277 } else {
278 *m_out << start;
280 start = iter + 1;
281 length = 0;
283 m_lineNo[m_curStream]++;
284 m_indentPending[m_curStream] = true;
285 } else if (*iter == '\0') {
286 // Perform print only in case what's left is not an empty string
287 if (length > 1) {
288 if (indent && m_indentPending[m_curStream]) {
289 printIndent();
290 m_indentPending[m_curStream] = false;
292 *m_out << start;
294 break;
299 void CodeGenerator::printSubstring(const char *start, int length) {
300 const int BUF_LEN = 0x100;
301 char buf[BUF_LEN];
302 while (length > 0) {
303 int curLength = std::min(length, BUF_LEN - 1);
304 memcpy(buf, start, curLength);
305 buf[curLength] = '\0';
306 *m_out << buf;
307 length -= curLength;
308 start += curLength;
312 void CodeGenerator::printIndent() {
313 for (int i = 0; i < m_indentation[m_curStream]; i++) {
314 *m_out << Option::Tab;
318 ///////////////////////////////////////////////////////////////////////////////
320 int CodeGenerator::s_idLambda = 0;
321 string CodeGenerator::GetNewLambda() {
322 return Option::LambdaPrefix + "lambda_" +
323 boost::lexical_cast<string>(++s_idLambda);
326 void CodeGenerator::resetIdCount(const std::string &key) {
327 m_idCounters[key] = 0;
330 int CodeGenerator::createNewId(const std::string &key) {
331 return ++m_idCounters[key];
334 int CodeGenerator::createNewId(ConstructPtr cs) {
335 FileScopePtr fs = cs->getFileScope();
336 if (fs) {
337 return createNewId(fs->getName());
339 return createNewId("");
342 int CodeGenerator::createNewLocalId(ConstructPtr ar) {
343 if (m_wrappedExpression[m_curStream]) {
344 return m_localId[m_curStream]++;
346 FunctionScopePtr func = ar->getFunctionScope();
347 if (func) {
348 return func->nextInlineIndex();
350 FileScopePtr fs = ar->getFileScope();
351 if (fs) {
352 return createNewId(fs->getName());
354 return createNewId("");
357 void CodeGenerator::pushBreakScope(int labelId,
358 bool loopCounter /* = true */) {
359 m_breakScopes.push_back(labelId);
360 if (loopCounter) {
361 printf("LOOP_COUNTER(%d);\n", int(labelId & ~BreakScopeBitMask));
365 void CodeGenerator::popBreakScope() {
366 m_breakScopes.pop_back();
367 if (m_breakScopes.size() == 0) {
368 m_breakLabelIds.clear();
369 m_contLabelIds.clear();
373 void CodeGenerator::pushCallInfo(int cit) {
374 m_callInfos.push_back(cit);
376 void CodeGenerator::popCallInfo() {
377 m_callInfos.pop_back();
379 int CodeGenerator::callInfoTop() {
380 if (m_callInfos.empty()) return -1;
381 return m_callInfos.back();
384 void CodeGenerator::addLabelId(const char *name, int labelId) {
385 if (!strcmp(name, "break")) {
386 m_breakLabelIds.insert(labelId);
387 } else if (!strcmp(name, "continue")) {
388 m_contLabelIds.insert(labelId);
389 } else {
390 assert(false);
394 bool CodeGenerator::findLabelId(const char *name, int labelId) {
395 if (!strcmp(name, "break")) {
396 return m_breakLabelIds.find(labelId) != m_breakLabelIds.end();
397 } else if (!strcmp(name, "continue")) {
398 return m_contLabelIds.find(labelId) != m_contLabelIds.end();
399 } else {
400 assert(false);
402 return false;
405 int CodeGenerator::ClassScopeCompare::cmp(const ClassScopeRawPtr &p1,
406 const ClassScopeRawPtr &p2) const {
407 int d = p1->getRedeclaringId() - p2->getRedeclaringId();
408 if (d) return d;
409 return strcasecmp(p1->getName().c_str(), p2->getName().c_str());
412 void CodeGenerator::printObjectHeader(const std::string className,
413 int numProperties) {
414 std::string prefixedClassName;
415 prefixedClassName.append(m_astPrefix);
416 prefixedClassName.append(className);
417 m_astClassNames.push_back(prefixedClassName);
418 printf("O:%d:\"%s\":%d:{",
419 (int)prefixedClassName.length(), prefixedClassName.c_str(), numProperties);
422 void CodeGenerator::printObjectFooter() {
423 printf("}");
424 m_astClassNames.pop_back();
427 void CodeGenerator::printPropertyHeader(const std::string propertyName) {
428 auto prefixedClassName = m_astClassNames.back();
429 auto len = 2+prefixedClassName.length()+propertyName.length();
430 printf("s:%d:\"", (int)len);
431 *m_out << (char)0;
432 printf("%s", prefixedClassName.c_str());
433 *m_out << (char)0;
434 printf("%s\";", propertyName.c_str());
437 void CodeGenerator::printNull() {
438 printf("N;");
441 void CodeGenerator::printBool(bool value) {
442 printf("b:%d;", value ? 1 : 0);
445 void CodeGenerator::printValue(double v) {
446 *m_out << "d:";
447 if (std::isnan(v)) {
448 *m_out << "NAN";
449 } else if (std::isinf(v)) {
450 if (v < 0) *m_out << '-';
451 *m_out << "INF";
452 } else {
453 char *buf;
454 if (v == 0.0) v = 0.0; // so to avoid "-0" output
455 vspprintf(&buf, 0, "%.*H", 14, v);
456 m_out->write(buf, strlen(buf));
457 free(buf);
459 *m_out << ';';
462 void CodeGenerator::printValue(int32_t value) {
463 printf("i:%d;", value);
466 void CodeGenerator::printValue(int64_t value) {
467 printf("i:%" PRId64 ";", value);
470 void CodeGenerator::printValue(std::string value) {
471 printf("s:%d:\"", (int)value.length());
472 getStream()->write(value.c_str(), value.length());
473 printf("\";");
476 void CodeGenerator::printModifierVector(std::string value) {
477 printf("V:9:\"HH\\Vector\":1:{");
478 printObjectHeader("Modifier", 1);
479 printPropertyHeader("name");
480 printValue(value);
481 printObjectFooter();
482 printf("}");
485 void CodeGenerator::printTypeExpression(std::string value) {
486 printObjectHeader("TypeExpression", 1);
487 printPropertyHeader("name");
488 printValue(value);
489 printObjectFooter();
492 void CodeGenerator::printTypeExpression(ExpressionPtr expression) {
493 printObjectHeader("TypeExpression", 2);
494 printPropertyHeader("name");
495 expression->outputCodeModel(*this);
496 printPropertyHeader("sourceLocation");
497 printLocation(expression->getLocation());
498 printObjectFooter();
501 void CodeGenerator::printExpression(ExpressionPtr expression, bool isRef) {
502 if (isRef) {
503 printObjectHeader("UnaryOpExpression", 3);
504 printPropertyHeader("expression");
505 expression->outputCodeModel(*this);
506 printPropertyHeader("operation");
507 printValue(PHP_REFERENCE_OP);
508 printPropertyHeader("sourceLocation");
509 printLocation(expression->getLocation());
510 printObjectFooter();
511 } else {
512 expression->outputCodeModel(*this);
516 void CodeGenerator::printExpressionVector(ExpressionListPtr el) {
517 auto count = el == nullptr ? 0 : el->getCount();
518 printf("V:9:\"HH\\Vector\":%d:{", count);
519 if (count > 0) {
520 el->outputCodeModel(*this);
522 printf("}");
525 void CodeGenerator::printExpressionVector(ExpressionPtr e) {
526 if (e->is(Expression::KindOfExpressionList)) {
527 auto sl = static_pointer_cast<ExpressionList>(e);
528 printExpressionVector(sl);
529 } else {
530 printf("V:9:\"HH\\Vector\":1:{");
531 e->outputCodeModel(*this);
532 printf("}");
536 void CodeGenerator::printTypeExpressionVector(ExpressionListPtr el) {
537 auto count = el == nullptr ? 0 : el->getCount();
538 printf("V:9:\"HH\\Vector\":%d:{", count);
539 for (int i = 0; i < count; i++) {
540 auto te = (*el)[i];
541 printTypeExpression(te);
543 printf("}");
546 void CodeGenerator::printAsBlock(StatementPtr s, bool isEnclosed) {
547 if (s != nullptr && s->is(Statement::KindOfBlockStatement)) {
548 s->outputCodeModel(*this);
549 } else {
550 auto numProps = s == nullptr ? 0 : 2;
551 if (isEnclosed) numProps++;
552 printObjectHeader("BlockStatement", numProps);
553 if (s != nullptr) {
554 printPropertyHeader("statements");
555 printStatementVector(s);
556 printPropertyHeader("sourceLocation");
557 printLocation(s->getLocation());
559 if (isEnclosed) {
560 printPropertyHeader("isEnclosed");
561 printBool(true);
563 printObjectFooter();
567 void CodeGenerator::printStatementVector(StatementListPtr sl) {
568 printf("V:9:\"HH\\Vector\":%d:{", sl->getCount());
569 if (sl->getCount() > 0) {
570 sl->outputCodeModel(*this);
572 printf("}");
575 void CodeGenerator::printStatementVector(StatementPtr s) {
576 if (s == nullptr) {
577 printf("V:9:\"HH\\Vector\":0:{}");
578 } else if (s->is(Statement::KindOfStatementList)) {
579 auto sl = static_pointer_cast<StatementList>(s);
580 printStatementVector(sl);
581 } else {
582 printf("V:9:\"HH\\Vector\":1:{");
583 s->outputCodeModel(*this);
584 printf("}");
588 void CodeGenerator::printLocation(LocationPtr location) {
589 if (location == nullptr) return;
590 printObjectHeader("SourceLocation", 4);
591 printPropertyHeader("startLine");
592 printValue(location->line0);
593 printPropertyHeader("endLine");
594 printValue(location->line1);
595 printPropertyHeader("startColumn");
596 printValue(location->char0);
597 printPropertyHeader("endColumn");
598 printValue(location->char1);
599 printObjectFooter();