2 +----------------------------------------------------------------------+
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>
36 ///////////////////////////////////////////////////////////////////////////////
39 void CodeGenerator::BuildJumpTable(const std::vector
<const char *> &strings
,
40 MapIntToStringVec
&out
, int tableSize
,
41 bool caseInsensitive
) {
42 assert(!strings
.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
)) %
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
=
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;
71 m_indentPending
[i
] = true;
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
);
91 if (stream
== NullStream
) {
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
) {
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
]++;
154 void CodeGenerator::endComments() {
155 assert(m_inComments
[m_curStream
] > 0);
156 m_inComments
[m_curStream
]--;
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;
174 printf("namespace HPHP {\n");
179 void CodeGenerator::namespaceEnd() {
180 always_assert(m_inNamespace
);
181 m_inNamespace
= false;
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
;
196 int hash
= hash_string(file
.data(), file
.size());
197 formatted
+= boost::str(boost::format("%08x") % hash
);
201 bool CodeGenerator::ensureInNamespace() {
202 if (m_inNamespace
) return false;
207 bool CodeGenerator::ensureOutOfNamespace() {
208 if (!m_inNamespace
) return false;
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
);
219 void CodeGenerator::ifdefEnd(const char *fmt
, ...) {
220 printf("#endif // ");
221 va_list ap
; va_start(ap
, fmt
); print(fmt
, ap
); va_end(ap
);
225 void CodeGenerator::printDocComment(const std::string comment
) {
226 if (comment
.empty()) return;
228 escaped
.reserve(comment
.size() + 10);
229 for (unsigned int i
= 0; i
< comment
.size(); i
++) {
230 char ch
= comment
[i
];
232 if (ch
== '/' && i
> 1 && comment
[i
+1] == '*') {
233 escaped
+= '\\'; // splitting illegal /* into /\*
236 print(escaped
.c_str(), false);
240 std::string
CodeGenerator::EscapeLabel(const std::string
&name
,
241 bool *binary
/* = NULL */) {
242 return escapeStringForCPP(name
, binary
);
245 ///////////////////////////////////////////////////////////////////////////////
248 void CodeGenerator::print(const char *fmt
, va_list ap
) {
250 boost::scoped_array
<char> buf
;
252 for (int len
= 1024; !done
; len
<<= 1) {
256 buf
.reset(new char[len
]);
257 if (vsnprintf(buf
.get(), len
, fmt
, v
) < len
) done
= true;
264 void CodeGenerator::print(const char *msg
, bool indent
/* = true */) {
265 const char *start
= msg
;
267 for (const char *iter
= msg
; ; ++iter
, ++length
) {
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
);
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
288 if (indent
&& m_indentPending
[m_curStream
]) {
290 m_indentPending
[m_curStream
] = false;
299 void CodeGenerator::printSubstring(const char *start
, int length
) {
300 const int BUF_LEN
= 0x100;
303 int curLength
= std::min(length
, BUF_LEN
- 1);
304 memcpy(buf
, start
, curLength
);
305 buf
[curLength
] = '\0';
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();
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();
348 return func
->nextInlineIndex();
350 FileScopePtr fs
= ar
->getFileScope();
352 return createNewId(fs
->getName());
354 return createNewId("");
357 void CodeGenerator::pushBreakScope(int labelId
,
358 bool loopCounter
/* = true */) {
359 m_breakScopes
.push_back(labelId
);
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
);
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();
405 int CodeGenerator::ClassScopeCompare::cmp(const ClassScopeRawPtr
&p1
,
406 const ClassScopeRawPtr
&p2
) const {
407 int d
= p1
->getRedeclaringId() - p2
->getRedeclaringId();
409 return strcasecmp(p1
->getName().c_str(), p2
->getName().c_str());
412 void CodeGenerator::printObjectHeader(const std::string className
,
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() {
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
);
432 printf("%s", prefixedClassName
.c_str());
434 printf("%s\";", propertyName
.c_str());
437 void CodeGenerator::printNull() {
441 void CodeGenerator::printBool(bool value
) {
442 printf("b:%d;", value
? 1 : 0);
445 void CodeGenerator::printValue(double v
) {
449 } else if (std::isinf(v
)) {
450 if (v
< 0) *m_out
<< '-';
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
));
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());
476 void CodeGenerator::printModifierVector(std::string value
) {
477 printf("V:9:\"HH\\Vector\":1:{");
478 printObjectHeader("Modifier", 1);
479 printPropertyHeader("name");
485 void CodeGenerator::printTypeExpression(std::string value
) {
486 printObjectHeader("TypeExpression", 1);
487 printPropertyHeader("name");
492 void CodeGenerator::printTypeExpression(ExpressionPtr expression
) {
493 printObjectHeader("TypeExpression", 2);
494 printPropertyHeader("name");
495 expression
->outputCodeModel(*this);
496 printPropertyHeader("sourceLocation");
497 printLocation(expression
->getLocation());
501 void CodeGenerator::printExpression(ExpressionPtr expression
, bool 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());
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
);
520 el
->outputCodeModel(*this);
525 void CodeGenerator::printExpressionVector(ExpressionPtr e
) {
526 if (e
->is(Expression::KindOfExpressionList
)) {
527 auto sl
= static_pointer_cast
<ExpressionList
>(e
);
528 printExpressionVector(sl
);
530 printf("V:9:\"HH\\Vector\":1:{");
531 e
->outputCodeModel(*this);
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
++) {
541 printTypeExpression(te
);
546 void CodeGenerator::printAsBlock(StatementPtr s
, bool isEnclosed
) {
547 if (s
!= nullptr && s
->is(Statement::KindOfBlockStatement
)) {
548 s
->outputCodeModel(*this);
550 auto numProps
= s
== nullptr ? 0 : 2;
551 if (isEnclosed
) numProps
++;
552 printObjectHeader("BlockStatement", numProps
);
554 printPropertyHeader("statements");
555 printStatementVector(s
);
556 printPropertyHeader("sourceLocation");
557 printLocation(s
->getLocation());
560 printPropertyHeader("isEnclosed");
567 void CodeGenerator::printStatementVector(StatementListPtr sl
) {
568 printf("V:9:\"HH\\Vector\":%d:{", sl
->getCount());
569 if (sl
->getCount() > 0) {
570 sl
->outputCodeModel(*this);
575 void CodeGenerator::printStatementVector(StatementPtr s
) {
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
);
582 printf("V:9:\"HH\\Vector\":1:{");
583 s
->outputCodeModel(*this);
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
);