Tolerate nullptrs in AST
[hiphop-php.git] / hphp / php7 / compiler.cpp
blobbbb6ac3c5569bd9e454791beda838f4aa2758324
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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/php7/compiler.h"
19 #include <folly/Format.h>
21 #include <iostream>
23 namespace HPHP { namespace php7 {
25 namespace {
26 // helpers to cope with these functions taking non-const pointers
27 // in zend_ast.h
29 inline const zval* zend_ast_get_zval(const zend_ast* ast) {
30 return &reinterpret_cast<const zend_ast_zval*>(ast)->val;
33 inline const zend_ast_list* zend_ast_get_list(const zend_ast* ast) {
34 return reinterpret_cast<const zend_ast_list*>(ast);
37 inline std::string zval_to_string(const zval* zv) {
38 std::string out;
39 switch (Z_TYPE_P(zv)) {
40 case IS_LONG:
41 folly::format(&out, "{}", Z_LVAL_P(zv));
42 break;
43 case IS_NULL:
44 case IS_FALSE:
45 break;
46 case IS_TRUE:
47 out.append("1");
48 break;
49 case IS_DOUBLE:
50 folly::format(&out, "{}", Z_DVAL_P(zv));
51 break;
52 case IS_STRING:
53 folly::format(&out, "{}", Z_STRVAL_P(zv));
54 break;
56 return out;
59 } // namespace
62 using namespace bc;
64 Compiler::Compiler() {
65 unit = std::make_unique<Unit>();
66 unit->name = "unit.hhas";
69 void Compiler::compileProgram(const zend_ast* ast) {
70 assert(ast->kind == ZEND_AST_STMT_LIST);
72 auto pseudomain = unit->getPseudomain();
73 activeFunction = pseudomain;
74 activeBlock = pseudomain->entry;
76 compileStatement(ast);
78 if (activeBlock) {
79 activeBlock->emit(Int{-1});
80 activeBlock->exit(RetC{});
84 void Compiler::panic(const std::string& msg) {
85 throw CompilerException(folly::sformat("panic: {}", msg));
88 void Compiler::compileZvalLiteral(const zval* zv) {
89 switch (Z_TYPE_P(zv)) {
90 case IS_LONG:
91 activeBlock->emit(Int{Z_LVAL_P(zv)});
92 break;
93 case IS_NULL:
94 activeBlock->emit(Null{});
95 break;
96 case IS_FALSE:
97 activeBlock->emit(False{});
98 break;
99 case IS_TRUE:
100 activeBlock->emit(True{});
101 break;
102 case IS_DOUBLE:
103 activeBlock->emit(Double{Z_DVAL_P(zv)});
104 break;
105 case IS_STRING:
106 activeBlock->emit(String{Z_STRVAL_P(zv)});
107 break;
108 default:
109 panic("unsupported literal");
113 void Compiler::compileConstant(const zend_ast* ast) {
114 auto name = ast->child[0];
115 const char* str = Z_STRVAL_P(zend_ast_get_zval(name));
116 if (name->attr & ZEND_NAME_NOT_FQ) {
117 if (strcasecmp(str, "true") == 0) {
118 activeBlock->emit(True{});
119 } else if (strcasecmp(str, "false") == 0) {
120 activeBlock->emit(False{});
121 } else if (strcasecmp(str, "null") == 0) {
122 activeBlock->emit(Null{});
123 } else {
124 panic("unknown unqualified constant");
126 } else {
127 panic("unknown constant");
131 void Compiler::compileVar(const zend_ast* ast) {
132 getLvalue(ast)->getC();
135 Bytecode Compiler::opForBinaryOp(const zend_ast* ast) {
136 // NB: bizarrely, greater-than (>,>=) have their own AST type
137 // and there is no ZEND_IS_GREATER since it doesn't correspond to a VM
138 // instruction
139 if (ast->kind == ZEND_AST_GREATER) {
140 return Gt{};
141 } else if (ast->kind == ZEND_AST_GREATER_EQUAL) {
142 return Gte{};
145 switch (ast->attr) {
146 case ZEND_ADD: return Add{};
147 case ZEND_SUB: return Sub{};
148 case ZEND_MUL: return Mul{};
149 case ZEND_DIV: return Div{};
150 case ZEND_POW: return Pow{};
151 case ZEND_MOD: return Mod{};
152 case ZEND_SL: return Shl{};
153 case ZEND_SR: return Shr{};
154 case ZEND_BW_OR: return BitOr{};
155 case ZEND_BW_AND: return BitAnd{};
156 case ZEND_BW_XOR: return BitXor{};
157 case ZEND_CONCAT: return Concat{};
159 case ZEND_IS_IDENTICAL: return Same{};
160 case ZEND_IS_NOT_IDENTICAL: return NSame{};
161 case ZEND_IS_EQUAL: return Eq{};
162 case ZEND_IS_NOT_EQUAL: return Neq{};
163 case ZEND_IS_SMALLER: return Lt{};
164 case ZEND_IS_SMALLER_OR_EQUAL: return Lte{};
165 case ZEND_SPACESHIP: return Cmp{};
166 default:
167 panic("unknown binop");
171 void Compiler::compileUnaryOp(const zend_ast* ast) {
172 switch (ast->kind) {
173 case ZEND_AST_UNARY_MINUS:
174 activeBlock->emit(Int{0});
175 compileExpression(ast->child[0]);
176 activeBlock->emit(Sub{});
177 return;
178 case ZEND_AST_UNARY_PLUS:
179 activeBlock->emit(Int{0});
180 compileExpression(ast->child[0]);
181 activeBlock->emit(Add{});
182 return;
183 case ZEND_AST_UNARY_OP:
184 compileExpression(ast->child[0]);
185 switch(ast->attr) {
186 case ZEND_BOOL_NOT:
187 activeBlock->emit(Not{});
188 return;
189 case ZEND_BW_NOT:
190 activeBlock->emit(BitNot{});
191 return;
194 panic("unknown unop");
197 IncDecOp Compiler::getIncDecOpForNode(zend_ast_kind kind) {
198 switch (kind) {
199 case ZEND_AST_PRE_INC:
200 return IncDecOp::PreInc;
201 case ZEND_AST_PRE_DEC:
202 return IncDecOp::PreDec;
203 case ZEND_AST_POST_INC:
204 return IncDecOp::PostInc;
205 case ZEND_AST_POST_DEC:
206 return IncDecOp::PostDec;
207 default:
208 panic("not a inc/dec node");
212 SetOpOp Compiler::getSetOpOp(zend_ast_attr attr) {
213 switch (attr) {
214 case ZEND_ASSIGN_ADD:
215 return SetOpOp::PlusEqual;
216 case ZEND_ASSIGN_SUB:
217 return SetOpOp::MinusEqual;
218 case ZEND_ASSIGN_MUL:
219 return SetOpOp::MulEqual;
220 case ZEND_ASSIGN_POW:
221 return SetOpOp::PowEqual;
222 case ZEND_ASSIGN_DIV:
223 return SetOpOp::DivEqual;
224 case ZEND_ASSIGN_CONCAT:
225 return SetOpOp::ConcatEqual;
226 case ZEND_ASSIGN_MOD:
227 return SetOpOp::ModEqual;
228 case ZEND_ASSIGN_BW_AND:
229 return SetOpOp::AndEqual;
230 case ZEND_ASSIGN_BW_OR:
231 return SetOpOp::OrEqual;
232 case ZEND_ASSIGN_BW_XOR:
233 return SetOpOp::XorEqual;
234 case ZEND_ASSIGN_SL:
235 return SetOpOp::SlEqual;
236 case ZEND_ASSIGN_SR:
237 return SetOpOp::SrEqual;
238 default:
239 panic("unsupported set-op");
243 void Compiler::compileIncDec(const zend_ast* ast) {
244 auto op = getIncDecOpForNode(ast->kind);
245 auto var = ast->child[0];
247 getLvalue(var)->incDec(op);
250 void Compiler::compileAssignment(const zend_ast* ast) {
251 auto rhs = ast->child[1];
252 auto lhs = ast->child[0];
254 getLvalue(lhs)->assign(rhs);
257 void Compiler::compileAssignOp(const zend_ast* ast) {
258 auto rhs = ast->child[1];
259 auto op = getSetOpOp(ast->attr);
260 auto lhs = ast->child[0];
262 getLvalue(lhs)->assignOp(op, rhs);
266 void Compiler::compileExpression(const zend_ast* ast) {
267 switch (ast->kind) {
268 case ZEND_AST_ZVAL:
269 compileZvalLiteral(zend_ast_get_zval(ast));
270 break;
271 case ZEND_AST_CONST:
272 compileConstant(ast);
273 break;
274 case ZEND_AST_UNARY_MINUS:
275 case ZEND_AST_UNARY_PLUS:
276 case ZEND_AST_UNARY_OP:
277 compileUnaryOp(ast);
278 break;
279 case ZEND_AST_BINARY_OP:
280 case ZEND_AST_GREATER:
281 case ZEND_AST_GREATER_EQUAL:
282 compileExpression(ast->child[0]);
283 compileExpression(ast->child[1]);
284 activeBlock->emit(opForBinaryOp(ast));
285 break;
286 case ZEND_AST_POST_INC:
287 case ZEND_AST_POST_DEC:
288 case ZEND_AST_PRE_INC:
289 case ZEND_AST_PRE_DEC:
290 compileIncDec(ast);
291 break;
292 case ZEND_AST_VAR:
293 compileVar(ast);
294 break;
295 case ZEND_AST_ASSIGN:
296 compileAssignment(ast);
297 break;
298 case ZEND_AST_ASSIGN_OP:
299 compileAssignOp(ast);
300 break;
301 default:
302 panic("unsupported expression");
306 void Compiler::compileStatement(const zend_ast* ast) {
307 switch (ast->kind) {
308 case ZEND_AST_STMT_LIST: {
309 // just a block, so recur please :)
310 auto list = zend_ast_get_list(ast);
311 for (uint32_t i = 0; i < list->children; i++) {
312 if (!list->child[i]) {
313 continue;
315 compileStatement(list->child[i]);
317 break;
319 case ZEND_AST_ECHO:
320 compileExpression(ast->child[0]);
321 activeBlock->emit(Print{});
322 activeBlock->emit(PopC{});
323 break;
324 case ZEND_AST_IF:
325 compileIf(ast);
326 break;
327 case ZEND_AST_WHILE:
328 compileWhile(ast->child[0], ast->child[1], false);
329 break;
330 case ZEND_AST_DO_WHILE:
331 compileWhile(ast->child[1], ast->child[0], true);
332 break;
333 default:
334 compileExpression(ast);
335 activeBlock->emit(PopC{});
339 void Compiler::compileIf(const zend_ast* ast) {
340 auto list = zend_ast_get_list(ast);
342 // where control will return after we're finished
343 auto end = activeFunction->allocateBlock();
345 for (uint32_t i = 0; i <list->children; i++) {
346 auto elem = list->child[i];
347 auto condition = elem->child[0];
348 auto contents = elem->child[1];
349 if (!condition) {
350 // if no condition is provided, this is the 'else' branch
351 compileStatement(contents);
352 break;
353 } else {
354 // otherwise, we have a conditional
355 auto branch = activeFunction->allocateBlock();
357 withBlock(branch, [&]() {
358 compileStatement(contents);
359 activeBlock->exit(Jmp{end});
362 compileExpression(condition);
363 branchTo<JmpNZ>(branch);
367 activeBlock->exit(Jmp{end});
368 activeBlock = end;
371 void Compiler::compileWhile(const zend_ast* condition, const zend_ast* body,
372 bool bodyFirst) {
374 auto continuation = activeFunction->allocateBlock();
375 auto bodyBlock = activeFunction->allocateBlock();
376 auto testBlock = activeFunction->allocateBlock();
378 withBlock(bodyBlock, [&]() {
379 activeLoops.push({continuation, testBlock});
380 compileStatement(body);
381 activeBlock->exit(Jmp{testBlock});
382 activeLoops.pop();
385 withBlock(testBlock, [&]() {
386 compileExpression(condition);
387 activeBlock->exit(JmpNZ{bodyBlock});
388 activeBlock->exit(Jmp{continuation});
391 if (bodyFirst) {
392 activeBlock->exit(Jmp{bodyBlock});
393 } else {
394 activeBlock->exit(Jmp{testBlock});
397 activeBlock = continuation;
400 struct Compiler::LocalLvalue : Compiler::Lvalue {
401 LocalLvalue(Compiler& c, std::string name)
402 : c(c)
403 , name(std::move(name)) {}
405 void getC() override {
406 c.activeFunction->locals.insert(name);
407 c.activeBlock->emit(CGetL{name});
410 void assign(const zend_ast* rhs) override {
411 c.activeFunction->locals.insert(name);
412 c.compileExpression(rhs);
413 c.activeBlock->emit(SetL{name});
416 void assignOp(SetOpOp op, const zend_ast* rhs) override {
417 c.activeFunction->locals.insert(name);
418 c.compileExpression(rhs);
419 c.activeBlock->emit(SetOpL{name, op});
422 void incDec(IncDecOp op) override {
423 c.activeFunction->locals.insert(name);
424 c.activeBlock->emit(IncDecL{name, op});
427 Compiler& c;
428 std::string name;
431 struct Compiler::DynamicLocalLvalue : Compiler::Lvalue {
432 DynamicLocalLvalue(Compiler& c, const zend_ast* nameExpr)
433 : c(c)
434 , nameExpr(nameExpr) {}
436 void getC() override {
437 c.compileExpression(nameExpr);
438 c.activeBlock->emit(CGetN{});
441 void assign(const zend_ast* rhs) override {
442 c.compileExpression(nameExpr);
443 c.compileExpression(rhs);
444 c.activeBlock->emit(SetN{});
447 void assignOp(SetOpOp op, const zend_ast* rhs) override {
448 c.compileExpression(nameExpr);
449 c.compileExpression(rhs);
450 c.activeBlock->emit(SetOpN{op});
453 void incDec(IncDecOp op) override {
454 c.compileExpression(nameExpr);
455 c.activeBlock->emit(IncDecN{op});
458 Compiler& c;
459 const zend_ast* nameExpr;
462 std::unique_ptr<Compiler::Lvalue> Compiler::getLvalue(const zend_ast* ast) {
463 if (ast->kind == ZEND_AST_VAR) {
464 auto name = ast->child[0];
465 switch (name->kind) {
466 case ZEND_AST_ZVAL:
467 return std::make_unique<Compiler::LocalLvalue>(
468 *this,
469 zval_to_string(zend_ast_get_zval(name)));
470 default:
471 return std::make_unique<Compiler::DynamicLocalLvalue>(
472 *this,
473 name);
477 panic("unsupported lvalue");
480 }} // HPHP::php7