 Chromium Code Reviews
 Chromium Code Reviews Issue 2891053003:
  Add support for converted closures with explicit contexts to VM  (Closed)
    
  
    Issue 2891053003:
  Add support for converted closures with explicit contexts to VM  (Closed) 
  | Index: runtime/vm/kernel_to_il.cc | 
| diff --git a/runtime/vm/kernel_to_il.cc b/runtime/vm/kernel_to_il.cc | 
| index 1130320dcf1afbdfa98ba157d685267494722cf6..63e590a1624d7d3e10586680aec4f44247192679 100644 | 
| --- a/runtime/vm/kernel_to_il.cc | 
| +++ b/runtime/vm/kernel_to_il.cc | 
| @@ -329,6 +329,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() { | 
| switch (function.kind()) { | 
| case RawFunction::kClosureFunction: | 
| + case RawFunction::kConvertedClosureFunction: | 
| case RawFunction::kRegularFunction: | 
| case RawFunction::kGetterFunction: | 
| case RawFunction::kSetterFunction: | 
| @@ -350,6 +351,9 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() { | 
| Symbols::ClosureParameter(), AbstractType::dynamic_type()); | 
| variable->set_is_forced_stack(); | 
| scope_->InsertParameterAt(pos++, variable); | 
| + } else if (function.IsConvertedClosureFunction()) { | 
| + // Do nothing. The explicit context parameter is already included into | 
| + // the parameter list of the parent function. | 
| } else if (!function.is_static()) { | 
| // We use [is_static] instead of [IsStaticFunction] because the latter | 
| // returns `false` for constructors. | 
| @@ -384,10 +388,14 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() { | 
| } | 
| AddParameters(node, pos); | 
| - // We generate a syntethic body for implicit closure functions - which | 
| - // will forward the call to the real function. | 
| + // We generate a syntethic body for implicit closure functions and | 
| + // converted | 
| + // closure functions - which will forward the call to the real function. | 
| // -> see BuildGraphOfImplicitClosureFunction | 
| - if (!function.IsImplicitClosureFunction()) { | 
| + // -> see BuildGraphOfConvertedClosureFunction | 
| + | 
| + if (!function.IsImplicitClosureFunction() && | 
| + !function.IsConvertedClosureFunction()) { | 
| // TODO(jensj): HACK: Push the begin token to after any parameters to | 
| // avoid crash when breaking on definition line of async method in | 
| // debugger. It seems that another scope needs to be added | 
| @@ -3201,6 +3209,7 @@ FlowGraph* FlowGraphBuilder::BuildGraph() { | 
| switch (function.kind()) { | 
| case RawFunction::kClosureFunction: | 
| + case RawFunction::kConvertedClosureFunction: | 
| case RawFunction::kRegularFunction: | 
| case RawFunction::kGetterFunction: | 
| case RawFunction::kSetterFunction: { | 
| @@ -3209,10 +3218,13 @@ FlowGraph* FlowGraphBuilder::BuildGraph() { | 
| : FunctionNode::Cast(node_); | 
| ActiveFunctionScope active_function_scope(&active_class_, | 
| kernel_function); | 
| - return function.IsImplicitClosureFunction() | 
| - ? BuildGraphOfImplicitClosureFunction(kernel_function, | 
| - function) | 
| - : BuildGraphOfFunction(kernel_function); | 
| + if (function.IsImplicitClosureFunction()) { | 
| + return BuildGraphOfImplicitClosureFunction(kernel_function, function); | 
| + } else if (function.IsConvertedClosureFunction()) { | 
| + return BuildGraphOfConvertedClosureFunction(kernel_function, function); | 
| + } else { | 
| + return BuildGraphOfFunction(kernel_function); | 
| + } | 
| } | 
| case RawFunction::kConstructor: { | 
| bool is_factory = function.IsFactory(); | 
| @@ -4028,6 +4040,64 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfImplicitClosureFunction( | 
| } | 
| +// This method follows the logic similar to that of | 
| +// FlowGraphBuilder::BuildGraphOfImplicitClosureFunction. For additional | 
| +// details on converted closure functions, please, see the comment on the method | 
| +// Function::ConvertedClosureFunction. | 
| +FlowGraph* FlowGraphBuilder::BuildGraphOfConvertedClosureFunction( | 
| + FunctionNode* kernel_function, | 
| + const Function& function) { | 
| + const Function& target = Function::ZoneHandle(Z, function.parent_function()); | 
| + | 
| + TargetEntryInstr* normal_entry = BuildTargetEntry(); | 
| + graph_entry_ = new (Z) | 
| + GraphEntryInstr(*parsed_function_, normal_entry, Compiler::kNoOSRDeoptId); | 
| + SetupDefaultParameterValues(kernel_function); | 
| + | 
| + Fragment body(normal_entry); | 
| + body += CheckStackOverflowInPrologue(); | 
| + | 
| + // Load all the arguments. | 
| + ASSERT(target.is_static()); | 
| + // The first argument is the value that the target function is converted over. | 
| + // It is stored in the context field of the Closure class instance. The | 
| + // instance is passed as the first argument to the call. | 
| + body += LoadLocal(parsed_function_->node_sequence()->scope()->VariableAt(0)); | 
| + body += LoadField(Closure::context_offset()); | 
| + body += PushArgument(); | 
| + | 
| + // The rest of the parameters are the same for the method of the Closure class | 
| + // being invoked and the top-level target function. | 
| + intptr_t positional_argument_count = | 
| + kernel_function->positional_parameters().length(); | 
| + for (intptr_t i = 1; i < positional_argument_count; i++) { | 
| + body += | 
| + LoadLocal(LookupVariable(kernel_function->positional_parameters()[i])); | 
| + body += PushArgument(); | 
| + } | 
| + intptr_t named_argument_count = kernel_function->named_parameters().length(); | 
| + Array& argument_names = Array::ZoneHandle(Z); | 
| + if (named_argument_count > 0) { | 
| + argument_names = Array::New(named_argument_count); | 
| + for (intptr_t i = 0; i < named_argument_count; i++) { | 
| + VariableDeclaration* variable = kernel_function->named_parameters()[i]; | 
| + body += LoadLocal(LookupVariable(variable)); | 
| + body += PushArgument(); | 
| + argument_names.SetAt(i, H.DartSymbol(variable->name())); | 
| + } | 
| + } | 
| + // Forward them to the target. | 
| + intptr_t argument_count = positional_argument_count + named_argument_count; | 
| + body += StaticCall(TokenPosition::kNoSource, target, argument_count, | 
| + argument_names); | 
| + | 
| + // Return the result. | 
| + body += Return(kernel_function->end_position()); | 
| + | 
| + return new (Z) FlowGraph(*parsed_function_, graph_entry_, next_block_id_ - 1); | 
| +} | 
| + | 
| + | 
| FlowGraph* FlowGraphBuilder::BuildGraphOfNoSuchMethodDispatcher( | 
| const Function& function) { | 
| // This function is specialized for a receiver class, a method name, and | 
| @@ -4731,6 +4801,11 @@ void DartTypeTranslator::VisitVoidType(VoidType* node) { | 
| } | 
| +void DartTypeTranslator::VisitVectorType(VectorType* node) { | 
| + result_ = Object::vector_type().raw(); | 
| +} | 
| + | 
| + | 
| void DartTypeTranslator::VisitBottomType(BottomType* node) { | 
| result_ = | 
| dart::Class::Handle(Z, I->object_store()->null_class()).CanonicalType(); | 
| @@ -5659,6 +5734,89 @@ void FlowGraphBuilder::VisitRethrow(Rethrow* node) { | 
| } | 
| +void FlowGraphBuilder::VisitVectorCreation(VectorCreation* node) { | 
| + ASSERT(node->value() > 0); | 
| 
jensj
2017/05/18 11:19:06
STREAM_EXPRESSION_IF_POSSIBLE(node); ? (the same f
 
Dmitry Stefantsov
2017/05/18 12:18:53
Yep, that explains why the code worked without the
 | 
| + Fragment instructions = AllocateContext(node->value()); | 
| + fragment_ = instructions; | 
| +} | 
| + | 
| + | 
| +void FlowGraphBuilder::VisitVectorGet(VectorGet* node) { | 
| + Fragment instructions; | 
| + | 
| + instructions += TranslateExpression(node->vector_expression()); | 
| + instructions += LoadField(Context::variable_offset(node->index())); | 
| + | 
| + fragment_ = instructions; | 
| +} | 
| + | 
| + | 
| +void FlowGraphBuilder::VisitVectorSet(VectorSet* node) { | 
| + Fragment instructions; | 
| + | 
| + instructions += TranslateExpression(node->vector_expression()); | 
| + instructions += TranslateExpression(node->value()); | 
| + LocalVariable* result = MakeTemporary(); | 
| + | 
| + Value* value = Pop(); | 
| + StoreInstanceFieldInstr* store = new (Z) | 
| + StoreInstanceFieldInstr(Context::variable_offset(node->index()), Pop(), | 
| + value, kNoStoreBarrier, TokenPosition::kNoSource); | 
| + instructions <<= store; | 
| + | 
| + // Result of a vector-set operation is its rhs. It is useful, especially for | 
| + // assignment chains. | 
| + instructions += LoadLocal(result); | 
| + | 
| + fragment_ = instructions; | 
| +} | 
| + | 
| + | 
| +void FlowGraphBuilder::VisitVectorCopy(VectorCopy* node) { | 
| + Fragment instructions; | 
| + | 
| + instructions += TranslateExpression(node->vector_expression()); | 
| + CloneContextInstr* clone_instruction = | 
| + new (Z) CloneContextInstr(TokenPosition::kNoSource, Pop()); | 
| + instructions <<= clone_instruction; | 
| + Push(clone_instruction); | 
| + | 
| + fragment_ = instructions; | 
| +} | 
| + | 
| + | 
| +void FlowGraphBuilder::VisitClosureCreation(ClosureCreation* node) { | 
| + Fragment instructions; | 
| + | 
| + Function& function = Function::ZoneHandle( | 
| + Z, H.LookupStaticMethodByKernelProcedure(node->top_level_function())); | 
| + function = function.ConvertedClosureFunction(); | 
| + ASSERT(!function.IsNull()); | 
| + | 
| + const dart::Class& closure_class = | 
| + dart::Class::ZoneHandle(Z, I->object_store()->closure_class()); | 
| + instructions += AllocateObject(closure_class, function); | 
| + LocalVariable* closure = MakeTemporary(); | 
| + | 
| + instructions += TranslateExpression(node->context_vector()); | 
| + LocalVariable* context = MakeTemporary(); | 
| + | 
| + instructions += LoadLocal(closure); | 
| + instructions += Constant(function); | 
| + instructions += | 
| + StoreInstanceField(TokenPosition::kNoSource, Closure::function_offset()); | 
| + | 
| + instructions += LoadLocal(closure); | 
| + instructions += LoadLocal(context); | 
| + instructions += | 
| + StoreInstanceField(TokenPosition::kNoSource, Closure::context_offset()); | 
| + | 
| + instructions += Drop(); | 
| + | 
| + fragment_ = instructions; | 
| +} | 
| + | 
| + | 
| Fragment FlowGraphBuilder::TranslateArguments(Arguments* node, | 
| Array* argument_names) { | 
| Fragment instructions; |