From add5fc1bf242d3c45f5fa8dd02723e0081d3e1c9 Mon Sep 17 00:00:00 2001 From: Michael Tingley Date: Mon, 24 Apr 2017 14:09:55 -0700 Subject: [PATCH] Generate the state machine, and wire the closure to call it Summary: This diff more closely matches the lowering to the sample implementation in D4878298. A few things happen: 1. The following **new** method is generated on the original class: function methodName_GeneratedStateMachine( ClassName_methodName_GeneratedClosure $closure, mixed $coroutineData, ?Exception $exception, ): CoroutineResult { throw new Exception("Coroutines are not yet implemented."); } 2. The closure has been significantly reworked. It now receives a delegate representing the aforementioned state machine, and uses that state machine to implement the resumption logic: class ClassName_methodName_GeneratedClosure implements CoroutineContinuation { function __construct( CoroutineContinuation $coroutineContinuation_generated, private (function( ClassName_methodName_GeneratedClosure, mixed, ?Exception, ): CoroutineResult) $stateMachineFunction, ) {} function resume(mixed $coroutineData): void { $stateMachineFunction = $this->stateMachineFunction; $stateMachineFunction($this, $coroutineData, null); } function resumeWithException(Exception $exception): void { $stateMachineFunction = $this->stateMachineFunction; $stateMachineFunction($this, null, $exception); } } 3. The original method is lowered similarly to before, however now it also passes in the state machine delegate method: public function methodName( CoroutineContinuation $coroutineContinuation_generated, ): CoroutineResult { new ClassName_methodName_GeneratedClosure( $coroutineContinuation_generated, inst_meth($this, "methodName_GeneratedStateMachine"), )->resume(null); return SuspendedCoroutineResult::create(); } Reviewed By: ericlippert Differential Revision: D4935242 fbshipit-source-id: 7c9fe275f247203f30415eb2d49da85c35f19fdb --- .../coroutine/coroutine_closure_generator.ml | 74 +++++++++------ .../hack/src/parser/coroutine/coroutine_lowerer.ml | 43 +++++---- .../parser/coroutine/coroutine_method_lowerer.ml | 15 ++- .../coroutine/coroutine_state_machine_generator.ml | 38 ++++++++ hphp/hack/src/parser/coroutine/coroutine_syntax.ml | 102 ++++++++++++++++++--- 5 files changed, 213 insertions(+), 59 deletions(-) create mode 100644 hphp/hack/src/parser/coroutine/coroutine_state_machine_generator.ml diff --git a/hphp/hack/src/parser/coroutine/coroutine_closure_generator.ml b/hphp/hack/src/parser/coroutine/coroutine_closure_generator.ml index 156f96798cf..fa0454d1269 100644 --- a/hphp/hack/src/parser/coroutine/coroutine_closure_generator.ml +++ b/hphp/hack/src/parser/coroutine/coroutine_closure_generator.ml @@ -16,21 +16,36 @@ open CoroutineSyntax let generate_constructor_body = make_missing () -let generate_constructor_method { function_type; _; } = +let generate_constructor_method + classish_name + function_name + { function_type; _; } = make_methodish_declaration_syntax (make_constructor_decl_header_syntax constructor_member_name - [ make_continuation_parameter_syntax function_type ]) + [ + make_continuation_parameter_syntax function_type; + make_state_machine_parameter_syntax classish_name function_name; + ]) [] let generate_resume_body { methodish_function_body; _; } = - let select_do_resume_syntax = - make_member_selection_expression_syntax this_syntax do_resume_member_name in - let call_do_resume_syntax = + let select_state_machine_syntax = + make_member_selection_expression_syntax + this_syntax + state_machine_member_name_syntax in + let assign_state_machine_syntax = + make_assignment_syntax + state_machine_variable_name + select_state_machine_syntax in + let call_state_machine_syntax = make_function_call_expression_syntax - select_do_resume_syntax - [ coroutune_data_variable_syntax; null_syntax ] in - [ make_expression_statement call_do_resume_syntax ] + state_machine_variable_name_syntax + [ this_syntax; coroutune_data_variable_syntax; null_syntax ] in + [ + assign_state_machine_syntax; + make_expression_statement call_state_machine_syntax; + ] let generate_resume_method method_node = make_methodish_declaration_syntax @@ -41,13 +56,22 @@ let generate_resume_method method_node = (generate_resume_body method_node) let generate_resume_with_exception_body _ = - let select_do_resume_syntax = - make_member_selection_expression_syntax this_syntax do_resume_member_name in - let call_do_resume_syntax = + let select_state_machine_syntax = + make_member_selection_expression_syntax + this_syntax + state_machine_member_name_syntax in + let assign_state_machine_syntax = + make_assignment_syntax + state_machine_variable_name + select_state_machine_syntax in + let call_state_machine_syntax = make_function_call_expression_syntax - select_do_resume_syntax - [ null_syntax; exception_variable_syntax ] in - [ make_expression_statement call_do_resume_syntax ] + state_machine_variable_name_syntax + [ this_syntax; null_syntax; exception_variable_syntax ] in + [ + assign_state_machine_syntax; + make_expression_statement call_state_machine_syntax; + ] let generate_resume_with_exception_method method_node = make_methodish_declaration_syntax @@ -57,23 +81,15 @@ let generate_resume_with_exception_method method_node = void_syntax) (generate_resume_with_exception_body method_node) -let generate_do_resume_body _ = - [ throw_unimplemented_syntax "Coroutines are not yet implemented." ] - -let generate_do_resume_method method_node = - make_methodish_declaration_syntax - (make_function_decl_header_syntax - do_resume_member_name - [ coroutine_data_parameter_syntax; nullable_exception_parameter_syntax ] - void_syntax) - (generate_do_resume_body method_node) - -let generate_closure_body method_node header_node = +let generate_closure_body + classish_name + function_name + method_node + header_node = [ - generate_constructor_method header_node; + generate_constructor_method classish_name function_name header_node; generate_resume_method method_node; generate_resume_with_exception_method method_node; - generate_do_resume_method method_node; ] (** @@ -89,4 +105,4 @@ let generate_coroutine_closure make_classish_declaration_syntax (make_closure_classname classish_name function_name) [ make_continuation_type_syntax mixed_syntax ] - (generate_closure_body method_node header_node) + (generate_closure_body classish_name function_name method_node header_node) diff --git a/hphp/hack/src/parser/coroutine/coroutine_lowerer.ml b/hphp/hack/src/parser/coroutine/coroutine_lowerer.ml index 20ef91274ba..5407fd23db0 100644 --- a/hphp/hack/src/parser/coroutine/coroutine_lowerer.ml +++ b/hphp/hack/src/parser/coroutine/coroutine_lowerer.ml @@ -9,6 +9,7 @@ module CoroutineMethodLowerer = Coroutine_method_lowerer module CoroutineClosureGenerator = Coroutine_closure_generator +module CoroutineStateMachineGenerator = Coroutine_state_machine_generator module CoroutineSyntax = Coroutine_syntax module EditableSyntax = Full_fidelity_editable_syntax module EditableToken = Full_fidelity_editable_token @@ -33,30 +34,38 @@ let maybe_get_token_text node = (** * Returns the transformed method node and the generated closure's syntax. *) -let maybe_rewrite_function_and_generate_closure +let maybe_generate_methods_and_closure classish_name method_node header_node function_name = - Option.both + let state_machine_syntax = + CoroutineStateMachineGenerator.generate_coroutine_state_machine + classish_name + function_name + method_node + header_node in + let closure_syntax = + CoroutineClosureGenerator.generate_coroutine_closure + classish_name + function_name + method_node + header_node in + Option.map (CoroutineMethodLowerer.maybe_rewrite_methodish_declaration classish_name function_name method_node header_node) - (Some - (CoroutineClosureGenerator.generate_coroutine_closure - classish_name - function_name - method_node - header_node)) + (fun rewritten_method_syntax -> + [ rewritten_method_syntax; state_machine_syntax ], closure_syntax) (** * If the provided function header is for a coroutine, rewrites the declaration * header and the function body into a desugared coroutine implementation. * Also extracts the coroutine's closure. *) -let maybe_rewrite_function_header_and_generate_closure +let maybe_generate_methods_and_closure_from_header classish_name ({ methodish_function_decl_header; _; } as method_node) = match syntax methodish_function_decl_header with @@ -66,7 +75,7 @@ let maybe_rewrite_function_header_and_generate_closure option_flat_map (maybe_get_token_text function_name) ~f: - (maybe_rewrite_function_and_generate_closure + (maybe_generate_methods_and_closure classish_name method_node header_node) @@ -76,13 +85,13 @@ let maybe_rewrite_function_header_and_generate_closure None (** - * If the provided methodish declaration is for a coroutine, rewrites the - * methodish declaration into a desugared coroutine implementation. + * If the provided methodish declaration is for a coroutine, generates the + * appropriate methods and state machine for the coroutine. *) -let maybe_rewrite_method_and_generate_closure classish_name node = +let maybe_generate_methods_and_closure_from_method classish_name node = match syntax node with | MethodishDeclaration node -> - maybe_rewrite_function_header_and_generate_closure + maybe_generate_methods_and_closure_from_header classish_name node | _ -> @@ -100,15 +109,15 @@ let rewrite_classish_body_element (classish_body_elements_acc, closures_acc, any_rewritten_acc) classish_body_element_node = Option.value_map - (maybe_rewrite_method_and_generate_closure + (maybe_generate_methods_and_closure_from_method classish_name classish_body_element_node) ~default: (classish_body_element_node :: classish_body_elements_acc, closures_acc, any_rewritten_acc) - ~f:(fun (method_node, closure_node) -> - method_node :: classish_body_elements_acc, + ~f:(fun (method_nodes, closure_node) -> + method_nodes @ classish_body_elements_acc, closure_node :: closures_acc, true) diff --git a/hphp/hack/src/parser/coroutine/coroutine_method_lowerer.ml b/hphp/hack/src/parser/coroutine/coroutine_method_lowerer.ml index bc84c67be0a..dd374f08deb 100644 --- a/hphp/hack/src/parser/coroutine/coroutine_method_lowerer.ml +++ b/hphp/hack/src/parser/coroutine/coroutine_method_lowerer.ml @@ -67,14 +67,25 @@ let rewrite_coroutine_body let make_syntax node = make_syntax (CompoundStatement node) in match syntax methodish_function_body with | CompoundStatement ({ compound_statements; _; } as node) -> + let call_inst_method_on_state_machine_syntax = + make_function_call_expression_syntax + inst_meth_syntax + [ + this_syntax; + make_string_literal_syntax + (make_state_machine_method_name function_name); + ] in let new_closure_syntax = make_object_creation_expression_syntax (make_closure_classname classish_name function_name) - [continuation_variable_syntax] in + [ + continuation_variable_syntax; + call_inst_method_on_state_machine_syntax; + ] in let select_resume_member_syntax = make_member_selection_expression_syntax new_closure_syntax - resume_member_name in + resume_member_name_syntax in let call_resume_with_null_syntax = make_function_call_expression_syntax select_resume_member_syntax diff --git a/hphp/hack/src/parser/coroutine/coroutine_state_machine_generator.ml b/hphp/hack/src/parser/coroutine/coroutine_state_machine_generator.ml new file mode 100644 index 00000000000..7b4637c9d60 --- /dev/null +++ b/hphp/hack/src/parser/coroutine/coroutine_state_machine_generator.ml @@ -0,0 +1,38 @@ +(** + * Copyright (c) 2016, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the "hack" directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +module CoroutineSyntax = Coroutine_syntax +module EditableSyntax = Full_fidelity_editable_syntax + +open EditableSyntax +open CoroutineSyntax + +let generate_coroutine_state_machine_body = + [ throw_unimplemented_syntax "Coroutines are not yet implemented." ] + +(** + * If the provided methodish declaration is for a coroutine, rewrites the + * declaration header and the function body into a desugared coroutine + * implementation. + *) +let generate_coroutine_state_machine + classish_name + function_name + { methodish_function_decl_header; _; } + { function_type; _; } = + make_methodish_declaration_syntax + (make_function_decl_header_syntax + (make_state_machine_method_name function_name) + [ + make_closure_parameter_syntax classish_name function_name; + coroutine_data_parameter_syntax; + nullable_exception_parameter_syntax; + ] + (make_coroutine_result_type_syntax function_type)) + (generate_coroutine_state_machine_body) diff --git a/hphp/hack/src/parser/coroutine/coroutine_syntax.ml b/hphp/hack/src/parser/coroutine/coroutine_syntax.ml index 08e3c43f780..141691a4979 100644 --- a/hphp/hack/src/parser/coroutine/coroutine_syntax.ml +++ b/hphp/hack/src/parser/coroutine/coroutine_syntax.ml @@ -67,6 +67,9 @@ let semicolon_syntax = let colon_syntax = make_token_syntax ~space_after:true TokenKind.Colon ":" +let assignment_operator_syntax = + make_token_syntax TokenKind.Equal "=" + let nullable_syntax = make_token_syntax TokenKind.Question "?" @@ -79,6 +82,9 @@ let void_syntax = let null_syntax = make_token_syntax TokenKind.NullLiteral "null" +let private_syntax = + make_token_syntax ~space_after:true TokenKind.Private "private" + let class_keyword_syntax = make_token_syntax ~space_after:true TokenKind.Class "class" @@ -127,6 +133,9 @@ let make_string_literal_syntax string_literal = TokenKind.DoubleQuotedStringLiteral (Printf.sprintf "\"%s\"" string_literal)) +let make_qualified_name_syntax name = + make_qualified_name_expression (make_token_syntax TokenKind.Name name) + let make_expression_statement expression_syntax = make_expression_statement expression_syntax semicolon_syntax @@ -142,6 +151,16 @@ let make_return_statement_syntax expression_syntax = let make_throw_statement_syntax expression_syntax = make_throw_statement throw_keyword_syntax expression_syntax semicolon_syntax +let make_assignment_syntax receiver_variable assignment_expression_syntax = + let receiver_variable_syntax = + make_token_syntax TokenKind.Variable receiver_variable in + let assignment_binary_expression = + make_binary_expression + receiver_variable_syntax + assignment_operator_syntax + assignment_expression_syntax in + make_expression_statement assignment_binary_expression + let make_object_creation_expression_syntax classname arguments = let classname_syntax = make_token_syntax TokenKind.Classname classname in let arguments_syntax = make_delimited_list comma_syntax arguments in @@ -164,9 +183,7 @@ let make_static_function_call_expression_syntax receiver_name member_name argument_list = - let receiver_name_syntax = make_token_syntax TokenKind.Name receiver_name in - let qualified_receiver_syntax = - make_qualified_name_expression receiver_name_syntax in + let qualified_receiver_syntax = make_qualified_name_syntax receiver_name in let member_syntax = make_token_syntax TokenKind.Name member_name in let receiver_syntax = make_scope_resolution_expression @@ -175,25 +192,29 @@ let make_static_function_call_expression_syntax member_syntax in make_function_call_expression_syntax receiver_syntax argument_list -let make_member_selection_expression_syntax receiver_syntax member_name = - let member_name_syntax = make_token_syntax TokenKind.Name member_name in +let make_member_selection_expression_syntax receiver_syntax member_syntax = make_member_selection_expression receiver_syntax member_selection_syntax - member_name_syntax + member_syntax let make_parameter_declaration_syntax + ?(visibility_syntax = make_missing ()) parameter_type_syntax parameter_variable = let parameter_variable_syntax = make_token_syntax TokenKind.Variable parameter_variable in make_parameter_declaration (* attribute *) (make_missing ()) - (* visibility *) (make_missing ()) + visibility_syntax parameter_type_syntax parameter_variable_syntax (* default value *) (make_missing ()) +let make_simple_type_specifier_syntax classname = + let classname_syntax = make_token_syntax TokenKind.Classname classname in + make_simple_type_specifier classname_syntax + let make_generic_type_specifier_syntax classname generic_argument_list = let classname_syntax = make_token_syntax TokenKind.Classname classname in let generic_argument_list_syntax = @@ -205,6 +226,18 @@ let make_generic_type_specifier_syntax classname generic_argument_list = right_angle_syntax in make_generic_type_specifier classname_syntax type_arguments_syntax +let make_functional_type_syntax argument_types return_type_syntax = + let argument_types_syntax = make_delimited_list comma_syntax argument_types in + make_closure_type_specifier + left_paren_syntax + function_keyword_syntax + left_paren_syntax + argument_types_syntax + right_paren_syntax + colon_syntax + return_type_syntax + right_paren_syntax + (* TODO(tingley): Determine if it's worth tightening visibility here. *) let make_classish_declaration_syntax classname implements_list classish_body = let classname_syntax = @@ -293,18 +326,30 @@ let suspended_member_name = let make_closure_classname enclosing_classname function_name = Printf.sprintf "%s_%s_GeneratedClosure" enclosing_classname function_name +let make_closure_type_syntax enclosing_classname function_name = + make_simple_type_specifier_syntax + (make_closure_classname enclosing_classname function_name) + +let closure_variable = + "$closure" + +let make_closure_parameter_syntax enclosing_classname function_name = + make_parameter_declaration_syntax + (make_closure_type_syntax enclosing_classname function_name) + closure_variable + let constructor_member_name = "__construct" let resume_member_name = "resume" +let resume_member_name_syntax = + make_token_syntax TokenKind.Name resume_member_name + let resume_with_exception_member_name = "resumeWithException" -let do_resume_member_name = - "doResume" - let coroutune_data_variable = "$coroutineData" @@ -325,6 +370,9 @@ let exception_variable = let exception_variable_syntax = make_token_syntax TokenKind.Variable exception_variable +let nullable_exception_type_syntax = + make_nullable_type_specifier_syntax exception_type_syntax + let exception_parameter_syntax = make_parameter_declaration_syntax exception_type_syntax @@ -332,7 +380,7 @@ let exception_parameter_syntax = let nullable_exception_parameter_syntax = make_parameter_declaration_syntax - (make_nullable_type_specifier_syntax exception_type_syntax) + nullable_exception_type_syntax exception_variable let throw_unimplemented_syntax reason = @@ -341,3 +389,35 @@ let throw_unimplemented_syntax reason = "Exception" [make_string_literal_syntax reason] in make_throw_statement_syntax create_exception_syntax + +let make_state_machine_method_name function_name = + Printf.sprintf "%s_GeneratedStateMachine" function_name + +let state_machine_member_name = + "stateMachineFunction" + +let state_machine_member_name_syntax = + make_token_syntax TokenKind.Name state_machine_member_name + +let state_machine_variable_name = + "$" ^ state_machine_member_name + +let state_machine_variable_name_syntax = + make_token_syntax TokenKind.Variable state_machine_variable_name + +let make_state_machine_parameter_syntax enclosing_classname function_name = + let state_machine_type_syntax = + make_functional_type_syntax + [ + make_closure_type_syntax enclosing_classname function_name; + mixed_syntax; + nullable_exception_type_syntax; + ] + (make_coroutine_result_type_syntax mixed_syntax) in + make_parameter_declaration_syntax + ~visibility_syntax:private_syntax + state_machine_type_syntax + state_machine_variable_name + +let inst_meth_syntax = + make_qualified_name_syntax "inst_meth" -- 2.11.4.GIT