1 // Copyright 2015 Google Inc. All rights reserved
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // +build ignore
16 
17 #include "eval.h"
18 
19 #include <errno.h>
20 #include <pthread.h>
21 #include <string.h>
22 
23 #include "expr.h"
24 #include "file.h"
25 #include "file_cache.h"
26 #include "fileutil.h"
27 #include "parser.h"
28 #include "rule.h"
29 #include "stmt.h"
30 #include "strutil.h"
31 #include "symtab.h"
32 #include "var.h"
33 
Evaluator()34 Evaluator::Evaluator()
35     : last_rule_(NULL),
36       current_scope_(NULL),
37       avoid_io_(false),
38       eval_depth_(0),
39       posix_sym_(Intern(".POSIX")),
40       is_posix_(false),
41       export_error_(false),
42       kati_readonly_(Intern(".KATI_READONLY")) {
43 #if defined(__APPLE__)
44   stack_size_ = pthread_get_stacksize_np(pthread_self());
45   stack_addr_ = (char*)pthread_get_stackaddr_np(pthread_self()) - stack_size_;
46 #else
47   pthread_attr_t attr;
48   CHECK(pthread_getattr_np(pthread_self(), &attr) == 0);
49   CHECK(pthread_attr_getstack(&attr, &stack_addr_, &stack_size_) == 0);
50   CHECK(pthread_attr_destroy(&attr) == 0);
51 #endif
52 
53   lowest_stack_ = (char*)stack_addr_ + stack_size_;
54   LOG_STAT("Stack size: %zd bytes", stack_size_);
55 }
56 
~Evaluator()57 Evaluator::~Evaluator() {
58   // delete vars_;
59   // for (auto p : rule_vars) {
60   //   delete p.second;
61   // }
62 }
63 
EvalRHS(Symbol lhs,Value * rhs_v,StringPiece orig_rhs,AssignOp op,bool is_override)64 Var* Evaluator::EvalRHS(Symbol lhs,
65                         Value* rhs_v,
66                         StringPiece orig_rhs,
67                         AssignOp op,
68                         bool is_override) {
69   VarOrigin origin =
70       ((is_bootstrap_ ? VarOrigin::DEFAULT
71                       : is_commandline_ ? VarOrigin::COMMAND_LINE
72                                         : is_override ? VarOrigin::OVERRIDE
73                                                       : VarOrigin::FILE));
74 
75   Var* rhs = NULL;
76   Var* prev = NULL;
77   bool needs_assign = true;
78 
79   switch (op) {
80     case AssignOp::COLON_EQ: {
81       prev = PeekVarInCurrentScope(lhs);
82       SimpleVar* sv = new SimpleVar(origin);
83       rhs_v->Eval(this, sv->mutable_value());
84       rhs = sv;
85       break;
86     }
87     case AssignOp::EQ:
88       prev = PeekVarInCurrentScope(lhs);
89       rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
90       break;
91     case AssignOp::PLUS_EQ: {
92       prev = LookupVarInCurrentScope(lhs);
93       if (!prev->IsDefined()) {
94         rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
95       } else if (prev->ReadOnly()) {
96         Error(StringPrintf("*** cannot assign to readonly variable: %s",
97                            lhs.c_str()));
98       } else {
99         prev->AppendVar(this, rhs_v);
100         rhs = prev;
101         needs_assign = false;
102       }
103       break;
104     }
105     case AssignOp::QUESTION_EQ: {
106       prev = LookupVarInCurrentScope(lhs);
107       if (!prev->IsDefined()) {
108         rhs = new RecursiveVar(rhs_v, origin, orig_rhs);
109       } else {
110         rhs = prev;
111         needs_assign = false;
112       }
113       break;
114     }
115   }
116 
117   if (prev != NULL) {
118     prev->Used(this, lhs);
119     if (prev->Deprecated()) {
120       if (needs_assign) {
121         rhs->SetDeprecated(prev->DeprecatedMessage());
122       }
123     }
124   }
125 
126   LOG("Assign: %s=%s", lhs.c_str(), rhs->DebugString().c_str());
127   if (needs_assign) {
128     return rhs;
129   }
130   return NULL;
131 }
132 
EvalAssign(const AssignStmt * stmt)133 void Evaluator::EvalAssign(const AssignStmt* stmt) {
134   loc_ = stmt->loc();
135   last_rule_ = NULL;
136   Symbol lhs = stmt->GetLhsSymbol(this);
137   if (lhs.empty())
138     Error("*** empty variable name.");
139 
140   if (lhs == kati_readonly_) {
141     string rhs;
142     stmt->rhs->Eval(this, &rhs);
143     for (auto const& name : WordScanner(rhs)) {
144       Var* var = Intern(name).GetGlobalVar();
145       if (!var->IsDefined()) {
146         Error(
147             StringPrintf("*** unknown variable: %s", name.as_string().c_str()));
148       }
149       var->SetReadOnly();
150     }
151     return;
152   }
153 
154   Var* rhs = EvalRHS(lhs, stmt->rhs, stmt->orig_rhs, stmt->op,
155                      stmt->directive == AssignDirective::OVERRIDE);
156   if (rhs) {
157     bool readonly;
158     lhs.SetGlobalVar(rhs, stmt->directive == AssignDirective::OVERRIDE,
159                      &readonly);
160     if (readonly) {
161       Error(StringPrintf("*** cannot assign to readonly variable: %s",
162                          lhs.c_str()));
163     }
164   }
165 }
166 
EvalRule(const RuleStmt * stmt)167 void Evaluator::EvalRule(const RuleStmt* stmt) {
168   loc_ = stmt->loc();
169   last_rule_ = NULL;
170 
171   const string&& expr = stmt->expr->Eval(this);
172   // See semicolon.mk.
173   if (expr.find_first_not_of(" \t;") == string::npos) {
174     if (stmt->term == ';')
175       Error("*** missing rule before commands.");
176     return;
177   }
178 
179   Rule* rule;
180   RuleVarAssignment rule_var;
181   function<string()> after_term_fn = [this, stmt]() {
182     return stmt->after_term ? stmt->after_term->Eval(this) : "";
183   };
184   ParseRule(loc_, expr, stmt->term, after_term_fn, &rule, &rule_var);
185 
186   if (rule) {
187     if (stmt->term == ';') {
188       rule->cmds.push_back(stmt->after_term);
189     }
190 
191     for (Symbol o : rule->outputs) {
192       if (o == posix_sym_)
193         is_posix_ = true;
194     }
195 
196     LOG("Rule: %s", rule->DebugString().c_str());
197     rules_.push_back(rule);
198     last_rule_ = rule;
199     return;
200   }
201 
202   Symbol lhs = Intern(rule_var.lhs);
203   for (Symbol output : rule_var.outputs) {
204     auto p = rule_vars_.emplace(output, nullptr);
205     if (p.second) {
206       p.first->second = new Vars;
207     }
208 
209     Value* rhs = stmt->after_term;
210     if (!rule_var.rhs.empty()) {
211       Value* lit = NewLiteral(rule_var.rhs);
212       if (rhs) {
213         // TODO: We always insert two whitespaces around the
214         // terminator. Preserve whitespaces properly.
215         if (stmt->term == ';') {
216           rhs = NewExpr3(lit, NewLiteral(StringPiece(" ; ")), rhs);
217         } else {
218           rhs = NewExpr3(lit, NewLiteral(StringPiece(" = ")), rhs);
219         }
220       } else {
221         rhs = lit;
222       }
223     }
224 
225     current_scope_ = p.first->second;
226 
227     if (lhs == kati_readonly_) {
228       string rhs_value;
229       rhs->Eval(this, &rhs_value);
230       for (auto const& name : WordScanner(rhs_value)) {
231         Var* var = current_scope_->Lookup(Intern(name));
232         if (!var->IsDefined()) {
233           Error(StringPrintf("*** unknown variable: %s",
234                              name.as_string().c_str()));
235         }
236         var->SetReadOnly();
237       }
238       current_scope_ = NULL;
239       continue;
240     }
241 
242     Var* rhs_var = EvalRHS(lhs, rhs, StringPiece("*TODO*"), rule_var.op);
243     if (rhs_var) {
244       bool readonly;
245       current_scope_->Assign(lhs, new RuleVar(rhs_var, rule_var.op), &readonly);
246       if (readonly) {
247         Error(StringPrintf("*** cannot assign to readonly variable: %s",
248                            lhs.c_str()));
249       }
250     }
251     current_scope_ = NULL;
252   }
253 }
254 
EvalCommand(const CommandStmt * stmt)255 void Evaluator::EvalCommand(const CommandStmt* stmt) {
256   loc_ = stmt->loc();
257 
258   if (!last_rule_) {
259     vector<Stmt*> stmts;
260     ParseNotAfterRule(stmt->orig, stmt->loc(), &stmts);
261     for (Stmt* a : stmts)
262       a->Eval(this);
263     return;
264   }
265 
266   last_rule_->cmds.push_back(stmt->expr);
267   if (last_rule_->cmd_lineno == 0)
268     last_rule_->cmd_lineno = stmt->loc().lineno;
269   LOG("Command: %s", stmt->expr->DebugString().c_str());
270 }
271 
EvalIf(const IfStmt * stmt)272 void Evaluator::EvalIf(const IfStmt* stmt) {
273   loc_ = stmt->loc();
274 
275   bool is_true;
276   switch (stmt->op) {
277     case CondOp::IFDEF:
278     case CondOp::IFNDEF: {
279       string var_name;
280       stmt->lhs->Eval(this, &var_name);
281       Symbol lhs = Intern(TrimRightSpace(var_name));
282       if (lhs.str().find_first_of(" \t") != string::npos)
283         Error("*** invalid syntax in conditional.");
284       Var* v = LookupVarInCurrentScope(lhs);
285       is_true = (v->String().empty() == (stmt->op == CondOp::IFNDEF));
286       break;
287     }
288     case CondOp::IFEQ:
289     case CondOp::IFNEQ: {
290       const string&& lhs = stmt->lhs->Eval(this);
291       const string&& rhs = stmt->rhs->Eval(this);
292       is_true = ((lhs == rhs) == (stmt->op == CondOp::IFEQ));
293       break;
294     }
295     default:
296       CHECK(false);
297       abort();
298   }
299 
300   const vector<Stmt*>* stmts;
301   if (is_true) {
302     stmts = &stmt->true_stmts;
303   } else {
304     stmts = &stmt->false_stmts;
305   }
306   for (Stmt* a : *stmts) {
307     LOG("%s", a->DebugString().c_str());
308     a->Eval(this);
309   }
310 }
311 
DoInclude(const string & fname)312 void Evaluator::DoInclude(const string& fname) {
313   CheckStack();
314 
315   Makefile* mk = MakefileCacheManager::Get()->ReadMakefile(fname);
316   if (!mk->Exists()) {
317     Error(StringPrintf("%s does not exist", fname.c_str()));
318   }
319 
320   Var* var_list = LookupVar(Intern("MAKEFILE_LIST"));
321   var_list->AppendVar(this, NewLiteral(Intern(TrimLeadingCurdir(fname)).str()));
322   for (Stmt* stmt : mk->stmts()) {
323     LOG("%s", stmt->DebugString().c_str());
324     stmt->Eval(this);
325   }
326 }
327 
EvalInclude(const IncludeStmt * stmt)328 void Evaluator::EvalInclude(const IncludeStmt* stmt) {
329   loc_ = stmt->loc();
330   last_rule_ = NULL;
331 
332   const string&& pats = stmt->expr->Eval(this);
333   for (StringPiece pat : WordScanner(pats)) {
334     ScopedTerminator st(pat);
335     vector<string>* files;
336     Glob(pat.data(), &files);
337 
338     if (stmt->should_exist) {
339       if (files->empty()) {
340         // TODO: Kati does not support building a missing include file.
341         Error(StringPrintf("%s: %s", pat.data(), strerror(errno)));
342       }
343     }
344 
345     for (const string& fname : *files) {
346       if (!stmt->should_exist && g_flags.ignore_optional_include_pattern &&
347           Pattern(g_flags.ignore_optional_include_pattern).Match(fname)) {
348         continue;
349       }
350       DoInclude(fname);
351     }
352   }
353 }
354 
EvalExport(const ExportStmt * stmt)355 void Evaluator::EvalExport(const ExportStmt* stmt) {
356   loc_ = stmt->loc();
357   last_rule_ = NULL;
358 
359   const string&& exports = stmt->expr->Eval(this);
360   for (StringPiece tok : WordScanner(exports)) {
361     size_t equal_index = tok.find('=');
362     StringPiece lhs;
363     if (equal_index == string::npos) {
364       lhs = tok;
365     } else if (equal_index == 0 ||
366                (equal_index == 1 &&
367                 (tok[0] == ':' || tok[0] == '?' || tok[0] == '+'))) {
368       // Do not export tokens after an assignment.
369       break;
370     } else {
371       StringPiece rhs;
372       AssignOp op;
373       ParseAssignStatement(tok, equal_index, &lhs, &rhs, &op);
374     }
375     Symbol sym = Intern(lhs);
376     exports_[sym] = stmt->is_export;
377 
378     if (export_message_) {
379       const char* prefix = "";
380       if (!stmt->is_export) {
381         prefix = "un";
382       }
383 
384       if (export_error_) {
385         Error(StringPrintf("*** %s: %sexport is obsolete%s.", sym.c_str(),
386                            prefix, export_message_->c_str()));
387       } else {
388         WARN_LOC(loc(), "%s: %sexport has been deprecated%s.", sym.c_str(),
389                  prefix, export_message_->c_str());
390       }
391     }
392   }
393 }
394 
LookupVarGlobal(Symbol name)395 Var* Evaluator::LookupVarGlobal(Symbol name) {
396   Var* v = name.GetGlobalVar();
397   if (v->IsDefined())
398     return v;
399   used_undefined_vars_.insert(name);
400   return v;
401 }
402 
LookupVar(Symbol name)403 Var* Evaluator::LookupVar(Symbol name) {
404   if (current_scope_) {
405     Var* v = current_scope_->Lookup(name);
406     if (v->IsDefined())
407       return v;
408   }
409   return LookupVarGlobal(name);
410 }
411 
PeekVar(Symbol name)412 Var* Evaluator::PeekVar(Symbol name) {
413   if (current_scope_) {
414     Var* v = current_scope_->Peek(name);
415     if (v->IsDefined())
416       return v;
417   }
418   return name.PeekGlobalVar();
419 }
420 
LookupVarInCurrentScope(Symbol name)421 Var* Evaluator::LookupVarInCurrentScope(Symbol name) {
422   if (current_scope_) {
423     return current_scope_->Lookup(name);
424   }
425   return LookupVarGlobal(name);
426 }
427 
PeekVarInCurrentScope(Symbol name)428 Var* Evaluator::PeekVarInCurrentScope(Symbol name) {
429   if (current_scope_) {
430     return current_scope_->Peek(name);
431   }
432   return name.PeekGlobalVar();
433 }
434 
EvalVar(Symbol name)435 string Evaluator::EvalVar(Symbol name) {
436   return LookupVar(name)->Eval(this);
437 }
438 
GetShell()439 string Evaluator::GetShell() {
440   return EvalVar(kShellSym);
441 }
442 
GetShellFlag()443 string Evaluator::GetShellFlag() {
444   // TODO: Handle $(.SHELLFLAGS)
445   return is_posix_ ? "-ec" : "-c";
446 }
447 
GetShellAndFlag()448 string Evaluator::GetShellAndFlag() {
449   string shell = GetShell();
450   shell += ' ';
451   shell += GetShellFlag();
452   return shell;
453 }
454 
Error(const string & msg)455 void Evaluator::Error(const string& msg) {
456   ERROR_LOC(loc_, "%s", msg.c_str());
457 }
458 
DumpStackStats() const459 void Evaluator::DumpStackStats() const {
460   LOG_STAT("Max stack use: %zd bytes at %s:%d",
461            ((char*)stack_addr_ - (char*)lowest_stack_) + stack_size_,
462            LOCF(lowest_loc_));
463 }
464 
465 unordered_set<Symbol> Evaluator::used_undefined_vars_;
466