Chromium Code Reviews| Index: tools/gn/template.cc |
| diff --git a/tools/gn/template.cc b/tools/gn/template.cc |
| index cae5e0c2d092c4439fa1028624d79fc286e2dd5c..fa0ccf31c64c4e3a43b5a4a8b1e24d51e87deea6 100644 |
| --- a/tools/gn/template.cc |
| +++ b/tools/gn/template.cc |
| @@ -39,22 +39,33 @@ Value Template::Invoke(Scope* scope, |
| if (!EnsureNotProcessingImport(invocation, scope, err)) |
| return Value(); |
| - // First run the invocation's block. |
| - Scope invocation_scope(scope); |
| + // First run the invocation's block. Need to allocate the scope on the heap |
| + // so we can pass ownership to the template. |
| + scoped_ptr<Scope> invocation_scope(new Scope(scope)); |
| if (!FillTargetBlockScope(scope, invocation, |
| invocation->function().value().as_string(), |
| - block, args, &invocation_scope, err)) |
| + block, args, invocation_scope.get(), err)) |
| return Value(); |
| - block->ExecuteBlockInScope(&invocation_scope, err); |
| + block->ExecuteBlockInScope(invocation_scope.get(), err); |
| if (err->has_error()) |
| return Value(); |
| // Set up the scope to run the template. This should be dependent on the |
| // closure, but have the "invoker" and "target_name" values injected, and the |
| - // current dir matching the invoker. |
| + // current dir matching the invoker. We jump through some hoops to avoid |
| + // copying the invocation scope when setting it in the template scope (since |
| + // the invocation scope may have large lists of source files in it and could |
| + // be expensive to copy). |
| + // |
| + // Scope.SetValue will copy the value which will in turn copy the scope, but |
| + // if we instead create a value and then set the scope on it, the copy can |
| + // be avoided. |
| Scope template_scope(closure_.get()); |
| - template_scope.SetValue("invoker", Value(NULL, &invocation_scope), |
| + const char kInvoker[] = "invoker"; |
| + template_scope.SetValue(kInvoker, Value(NULL, scoped_ptr<Scope>()), |
| invocation); |
| + Value* invoker_value = template_scope.GetMutableValue(kInvoker, false); |
| + invoker_value->SetScopeValue(invocation_scope.Pass()); |
| template_scope.set_source_dir(scope->GetSourceDir()); |
| const base::StringPiece target_name("target_name"); |
| @@ -65,7 +76,28 @@ Value Template::Invoke(Scope* scope, |
| // Run the template code. Don't check for unused variables since the |
| // template could be executed in many different ways and it could be that |
| // not all executions use all values in the closure. |
| - return definition_->block()->ExecuteBlockInScope(&template_scope, err); |
| + Value result = |
| + definition_->block()->ExecuteBlockInScope(&template_scope, err); |
| + |
| + // Check for unused variables in the invocation scope. This will find typos |
| + // of things the caller meant to pass to the template but the template didn't |
| + // read out. |
| + // |
| + // This is a bit tricky because it's theoretically possible for the template |
|
scottmg
2014/04/04 20:15:47
Is there some way to make invoker readonly to avoi
brettw
2014/04/04 20:35:13
Nope, I don't have support for readonly variables.
|
| + // to overwrite the value of "invoker" and free the Scope owned by the |
| + // value. So we need to look it up again and don't do anything if it doesn't |
| + // exist. |
| + invoker_value = template_scope.GetMutableValue(kInvoker, false); |
| + if (invoker_value && invoker_value->type() == Value::SCOPE) { |
| + if (!invoker_value->scope_value()->CheckForUnusedVars(err)) |
| + return Value(); |
| + } |
| + |
| + // Check for unused variables in the template itself. |
| + if (!template_scope.CheckForUnusedVars(err)) |
| + return Value(); |
| + |
| + return result; |
| } |
| LocationRange Template::GetDefinitionRange() const { |