diff --git a/bins/estree/src/builder/actions.yaml b/bins/estree/src/builder/actions.yaml index 957e23be..cdde36be 100644 --- a/bins/estree/src/builder/actions.yaml +++ b/bins/estree/src/builder/actions.yaml @@ -204,11 +204,9 @@ ExpressionStatement -> (?![ASYNC (!LINE_TERMINATOR_SEQUENCE) FUNCTION, CLASS, FUNCTION, LBRACE, LET LBRACK]) Expression_In SEMICOLON action: expression_statement -- rule: >- - IfStatement -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ Statement ELSE _ELSE_BLOCK_ - Statement +- rule: IfStatement -> IF LPAREN Expression_In RPAREN _THEN_ Statement ELSE _ELSE_ Statement action: if_else_statement -- rule: 'IfStatement -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ Statement (?![ELSE])' +- rule: 'IfStatement -> IF LPAREN Expression_In RPAREN _THEN_ Statement (?![ELSE])' action: if_statement - rule: BreakableStatement -> IterationStatement action: nop @@ -312,15 +310,15 @@ action: assignment_expression - rule: >- AssignmentExpression_In_Await -> LeftHandSideExpression_Await AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In_Await -> LeftHandSideExpression_Await OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In_Await -> LeftHandSideExpression_Await NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await _DEREFERENCE_ action: assignment_expression - rule: Statement_Await -> BlockStatement_Await action: nop @@ -424,9 +422,9 @@ action: nop - rule: Expression_In -> Expression_In COMMA AssignmentExpression_In action: sequence_expression -- rule: _THEN_BLOCK_ -> (empty) +- rule: _THEN_ -> (empty) action: nop -- rule: _ELSE_BLOCK_ -> (empty) +- rule: _ELSE_ -> (empty) action: nop - rule: IterationStatement -> DoWhileStatement action: nop @@ -575,8 +573,9 @@ - rule: ConditionalExpression_In_Await -> ShortCircuitExpression_In_Await action: nop - rule: >- - ConditionalExpression_In_Await -> ShortCircuitExpression_In_Await CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In_Await COLON _ELSE_BLOCK_ AssignmentExpression_In_Await + ConditionalExpression_In_Await -> ShortCircuitExpression_In_Await CONDITIONAL _THEN_ + AssignmentExpression_In_Await _DEREFERENCE_ COLON _ELSE_ AssignmentExpression_In_Await + _DEREFERENCE_ action: conditional_expression - rule: >- ArrowFunction_In_Await -> ArrowParameters_Await (!LINE_TERMINATOR_SEQUENCE) ARROW @@ -622,6 +621,8 @@ action: nop - rule: _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ -> (empty) action: nop +- rule: _DEREFERENCE_ -> (empty) + action: nop - rule: _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ -> (empty) action: nop - rule: _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ -> (empty) @@ -633,12 +634,10 @@ LBRACE, LET LBRACK]) Expression_In_Await SEMICOLON action: expression_statement - rule: >- - IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_BLOCK_ Statement_Await ELSE - _ELSE_BLOCK_ Statement_Await + IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_ Statement_Await ELSE _ELSE_ + Statement_Await action: if_else_statement -- rule: >- - IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_BLOCK_ Statement_Await - (?![ELSE]) +- rule: 'IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_ Statement_Await (?![ELSE])' action: if_statement - rule: BreakableStatement_Await -> IterationStatement_Await action: nop @@ -842,15 +841,15 @@ action: assignment_expression - rule: >- AssignmentExpression_In -> LeftHandSideExpression AND_ASSIGN _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression_In + AssignmentExpression_In _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In -> LeftHandSideExpression OR_ASSIGN _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression_In + AssignmentExpression_In _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In -> LeftHandSideExpression NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In _DEREFERENCE_ action: assignment_expression - rule: >- DoWhileStatement -> DO _LOOP_START_ Statement _LOOP_BODY_ WHILE LPAREN Expression_In RPAREN @@ -1292,15 +1291,15 @@ action: assignment_expression - rule: >- AssignmentExpression_In_Yield -> LeftHandSideExpression_Yield AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In_Yield -> LeftHandSideExpression_Yield OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In_Yield -> LeftHandSideExpression_Yield NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield _DEREFERENCE_ action: assignment_expression - rule: BindingRestProperty_Await -> ELLIPSIS BindingIdentifier_Await action: rest_element @@ -1344,21 +1343,21 @@ action: assignment_expression - rule: >- AssignmentExpression_In_Yield_Await -> LeftHandSideExpression_Yield_Await AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield_Await + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In_Yield_Await -> LeftHandSideExpression_Yield_Await OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield_Await + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_In_Yield_Await -> LeftHandSideExpression_Yield_Await NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield_Await + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: assignment_expression - rule: ConditionalExpression_In -> ShortCircuitExpression_In action: nop - rule: >- - ConditionalExpression_In -> ShortCircuitExpression_In CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In COLON _ELSE_BLOCK_ AssignmentExpression_In + ConditionalExpression_In -> ShortCircuitExpression_In CONDITIONAL _THEN_ + AssignmentExpression_In _DEREFERENCE_ COLON _ELSE_ AssignmentExpression_In _DEREFERENCE_ action: conditional_expression - rule: >- ArrowFunction_In -> ArrowParameters (!LINE_TERMINATOR_SEQUENCE) ARROW @@ -1452,11 +1451,11 @@ action: nop - rule: >- LogicalORExpression_In_Await -> LogicalORExpression_In_Await OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression_In_Await + LogicalANDExpression_In_Await _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression_In_Await -> CoalesceExpressionHead_In_Await NULLISH _NULLISH_SHORT_CIRCUIT_ - BitwiseORExpression_In_Await + BitwiseORExpression_In_Await _DEREFERENCE_ action: logical_expression - rule: CoverParenthesizedExpressionAndArrowParameterList_Await -> LPAREN Expression_In_Await RPAREN action: cpeaapl_expr @@ -1690,8 +1689,9 @@ - rule: ConditionalExpression_In_Yield -> ShortCircuitExpression_In_Yield action: nop - rule: >- - ConditionalExpression_In_Yield -> ShortCircuitExpression_In_Yield CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In_Yield COLON _ELSE_BLOCK_ AssignmentExpression_In_Yield + ConditionalExpression_In_Yield -> ShortCircuitExpression_In_Yield CONDITIONAL _THEN_ + AssignmentExpression_In_Yield _DEREFERENCE_ COLON _ELSE_ AssignmentExpression_In_Yield + _DEREFERENCE_ action: conditional_expression - rule: YieldExpression_In -> YIELD action: yield_expression_no_argument @@ -1738,8 +1738,8 @@ action: nop - rule: >- ConditionalExpression_In_Yield_Await -> ShortCircuitExpression_In_Yield_Await CONDITIONAL - _THEN_BLOCK_ AssignmentExpression_In_Yield_Await COLON _ELSE_BLOCK_ - AssignmentExpression_In_Yield_Await + _THEN_ AssignmentExpression_In_Yield_Await _DEREFERENCE_ COLON _ELSE_ + AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: conditional_expression - rule: YieldExpression_In_Await -> YIELD action: yield_expression_no_argument @@ -1818,15 +1818,15 @@ action: assignment_expression - rule: >- AssignmentExpression -> LeftHandSideExpression AND_ASSIGN _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression + AssignmentExpression _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression -> LeftHandSideExpression OR_ASSIGN _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression + AssignmentExpression _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression -> LeftHandSideExpression NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression _DEREFERENCE_ action: assignment_expression - rule: VariableDeclaration -> BindingIdentifier action: variable_declarator @@ -1888,7 +1888,7 @@ action: nop - rule: >- LogicalANDExpression_In_Await -> LogicalANDExpression_In_Await AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression_In_Await + BitwiseORExpression_In_Await _DEREFERENCE_ action: logical_expression - rule: _TRUTHY_SHORT_CIRCUIT_ -> (empty) action: nop @@ -2074,11 +2074,11 @@ action: nop - rule: >- LogicalORExpression_In -> LogicalORExpression_In OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression_In + LogicalANDExpression_In _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression_In -> CoalesceExpressionHead_In NULLISH _NULLISH_SHORT_CIRCUIT_ - BitwiseORExpression_In + BitwiseORExpression_In _DEREFERENCE_ action: logical_expression - rule: CoverParenthesizedExpressionAndArrowParameterList -> LPAREN Expression_In RPAREN action: cpeaapl_expr @@ -2153,8 +2153,8 @@ - rule: ConditionalExpression -> ShortCircuitExpression action: nop - rule: >- - ConditionalExpression -> ShortCircuitExpression CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In COLON _ELSE_BLOCK_ AssignmentExpression + ConditionalExpression -> ShortCircuitExpression CONDITIONAL _THEN_ AssignmentExpression_In + _DEREFERENCE_ COLON _ELSE_ AssignmentExpression _DEREFERENCE_ action: conditional_expression - rule: >- ArrowFunction -> ArrowParameters (!LINE_TERMINATOR_SEQUENCE) ARROW @@ -2338,15 +2338,15 @@ action: assignment_expression - rule: >- AssignmentExpression_Await -> LeftHandSideExpression_Await AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_Await -> LeftHandSideExpression_Await OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_Await -> LeftHandSideExpression_Await NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await _DEREFERENCE_ action: assignment_expression - rule: VariableDeclaration_Await -> BindingIdentifier_Await action: variable_declarator @@ -2376,11 +2376,11 @@ action: nop - rule: >- LogicalORExpression_In_Yield -> LogicalORExpression_In_Yield OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression_In_Yield + LogicalANDExpression_In_Yield _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression_In_Yield -> CoalesceExpressionHead_In_Yield NULLISH _NULLISH_SHORT_CIRCUIT_ - BitwiseORExpression_In_Yield + BitwiseORExpression_In_Yield _DEREFERENCE_ action: logical_expression - rule: CoverParenthesizedExpressionAndArrowParameterList_Yield -> LPAREN Expression_In_Yield RPAREN action: cpeaapl_expr @@ -2470,11 +2470,11 @@ action: nop - rule: >- LogicalORExpression_In_Yield_Await -> LogicalORExpression_In_Yield_Await OR - _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Yield_Await + _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Yield_Await _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression_In_Yield_Await -> CoalesceExpressionHead_In_Yield_Await NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield_Await + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield_Await _DEREFERENCE_ action: logical_expression - rule: >- CoverParenthesizedExpressionAndArrowParameterList_Yield_Await -> LPAREN @@ -2568,7 +2568,7 @@ action: nop - rule: >- LogicalANDExpression_In -> LogicalANDExpression_In AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression_In + BitwiseORExpression_In _DEREFERENCE_ action: logical_expression - rule: CoalesceExpressionHead_In -> CoalesceExpression_In action: nop @@ -2749,8 +2749,9 @@ - rule: ConditionalExpression_Await -> ShortCircuitExpression_Await action: nop - rule: >- - ConditionalExpression_Await -> ShortCircuitExpression_Await CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In_Await COLON _ELSE_BLOCK_ AssignmentExpression_Await + ConditionalExpression_Await -> ShortCircuitExpression_Await CONDITIONAL _THEN_ + AssignmentExpression_In_Await _DEREFERENCE_ COLON _ELSE_ AssignmentExpression_Await + _DEREFERENCE_ action: conditional_expression - rule: >- ArrowFunction_Await -> ArrowParameters_Await (!LINE_TERMINATOR_SEQUENCE) ARROW @@ -2776,7 +2777,7 @@ action: nop - rule: >- LogicalANDExpression_In_Yield -> LogicalANDExpression_In_Yield AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression_In_Yield + BitwiseORExpression_In_Yield _DEREFERENCE_ action: logical_expression - rule: CoalesceExpressionHead_In_Yield -> CoalesceExpression_In_Yield action: nop @@ -2834,7 +2835,7 @@ action: nop - rule: >- LogicalANDExpression_In_Yield_Await -> LogicalANDExpression_In_Yield_Await AND - _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield_Await + _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield_Await _DEREFERENCE_ action: logical_expression - rule: CoalesceExpressionHead_In_Yield_Await -> CoalesceExpression_In_Yield_Await action: nop @@ -2930,11 +2931,13 @@ action: template_spans_append - rule: LogicalORExpression -> LogicalANDExpression action: nop -- rule: LogicalORExpression -> LogicalORExpression OR _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression +- rule: >- + LogicalORExpression -> LogicalORExpression OR _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression + _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression -> CoalesceExpressionHead NULLISH _NULLISH_SHORT_CIRCUIT_ - BitwiseORExpression + BitwiseORExpression _DEREFERENCE_ action: logical_expression - rule: ExpressionBody -> AssignmentExpression action: nop @@ -2943,10 +2946,10 @@ - rule: BlockStatement_Return -> Block_Return action: nop - rule: >- - IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ Statement_Return ELSE - _ELSE_BLOCK_ Statement_Return + IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_ Statement_Return ELSE _ELSE_ + Statement_Return action: if_else_statement -- rule: 'IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ Statement_Return (?![ELSE])' +- rule: 'IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_ Statement_Return (?![ELSE])' action: if_statement - rule: BreakableStatement_Return -> IterationStatement_Return action: nop @@ -3210,7 +3213,9 @@ action: template_middle_list_append - rule: LogicalANDExpression -> BitwiseORExpression action: nop -- rule: LogicalANDExpression -> LogicalANDExpression AND _FALSY_SHORT_CIRCUIT_ BitwiseORExpression +- rule: >- + LogicalANDExpression -> LogicalANDExpression AND _FALSY_SHORT_CIRCUIT_ BitwiseORExpression + _DEREFERENCE_ action: logical_expression - rule: CoalesceExpressionHead -> CoalesceExpression action: nop @@ -3253,12 +3258,12 @@ LBRACE, LET LBRACK]) Expression_In_Yield SEMICOLON action: expression_statement - rule: >- - IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN _THEN_BLOCK_ - Statement_Yield_Return ELSE _ELSE_BLOCK_ Statement_Yield_Return + IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN _THEN_ Statement_Yield_Return + ELSE _ELSE_ Statement_Yield_Return action: if_else_statement - rule: >- - IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN _THEN_BLOCK_ - Statement_Yield_Return (?![ELSE]) + IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN _THEN_ Statement_Yield_Return + (?![ELSE]) action: if_statement - rule: BreakableStatement_Yield_Return -> IterationStatement_Yield_Return action: nop @@ -3311,12 +3316,12 @@ - rule: BlockStatement_Await_Return -> Block_Await_Return action: nop - rule: >- - IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN _THEN_BLOCK_ - Statement_Await_Return ELSE _ELSE_BLOCK_ Statement_Await_Return + IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN _THEN_ Statement_Await_Return + ELSE _ELSE_ Statement_Await_Return action: if_else_statement - rule: >- - IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN _THEN_BLOCK_ - Statement_Await_Return (?![ELSE]) + IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN _THEN_ Statement_Await_Return + (?![ELSE]) action: if_statement - rule: BreakableStatement_Await_Return -> IterationStatement_Await_Return action: nop @@ -3351,11 +3356,11 @@ FUNCTION, LBRACE, LET LBRACK]) Expression_In_Yield_Await SEMICOLON action: expression_statement - rule: >- - IfStatement_Yield_Await_Return -> IF LPAREN Expression_In_Yield_Await RPAREN _THEN_BLOCK_ - Statement_Yield_Await_Return ELSE _ELSE_BLOCK_ Statement_Yield_Await_Return + IfStatement_Yield_Await_Return -> IF LPAREN Expression_In_Yield_Await RPAREN _THEN_ + Statement_Yield_Await_Return ELSE _ELSE_ Statement_Yield_Await_Return action: if_else_statement - rule: >- - IfStatement_Yield_Await_Return -> IF LPAREN Expression_In_Yield_Await RPAREN _THEN_BLOCK_ + IfStatement_Yield_Await_Return -> IF LPAREN Expression_In_Yield_Await RPAREN _THEN_ Statement_Yield_Await_Return (?![ELSE]) action: if_statement - rule: BreakableStatement_Yield_Await_Return -> IterationStatement_Yield_Await_Return @@ -3444,11 +3449,11 @@ action: nop - rule: >- LogicalORExpression_Await -> LogicalORExpression_Await OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression_Await + LogicalANDExpression_Await _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression_Await -> CoalesceExpressionHead_Await NULLISH _NULLISH_SHORT_CIRCUIT_ - BitwiseORExpression_Await + BitwiseORExpression_Await _DEREFERENCE_ action: logical_expression - rule: BitwiseANDExpression_In_Yield -> EqualityExpression_In_Yield action: nop @@ -3835,7 +3840,7 @@ action: nop - rule: >- LogicalANDExpression_Await -> LogicalANDExpression_Await AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression_Await + BitwiseORExpression_Await _DEREFERENCE_ action: logical_expression - rule: CoalesceExpressionHead_Await -> CoalesceExpression_Await action: nop @@ -4704,15 +4709,15 @@ action: assignment_expression - rule: >- AssignmentExpression_Yield -> LeftHandSideExpression_Yield AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_Yield -> LeftHandSideExpression_Yield OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_Yield -> LeftHandSideExpression_Yield NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield _DEREFERENCE_ action: assignment_expression - rule: VariableDeclaration_Yield -> BindingIdentifier_Yield action: variable_declarator @@ -4754,15 +4759,15 @@ action: assignment_expression - rule: >- AssignmentExpression_Yield_Await -> LeftHandSideExpression_Yield_Await AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_Yield_Await -> LeftHandSideExpression_Yield_Await OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await _DEREFERENCE_ action: assignment_expression - rule: >- AssignmentExpression_Yield_Await -> LeftHandSideExpression_Yield_Await NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await _DEREFERENCE_ action: assignment_expression - rule: VariableDeclaration_Yield_Await -> BindingIdentifier_Yield_Await action: variable_declarator @@ -4829,8 +4834,9 @@ - rule: ConditionalExpression_Yield -> ShortCircuitExpression_Yield action: nop - rule: >- - ConditionalExpression_Yield -> ShortCircuitExpression_Yield CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In_Yield COLON _ELSE_BLOCK_ AssignmentExpression_Yield + ConditionalExpression_Yield -> ShortCircuitExpression_Yield CONDITIONAL _THEN_ + AssignmentExpression_In_Yield _DEREFERENCE_ COLON _ELSE_ AssignmentExpression_Yield + _DEREFERENCE_ action: conditional_expression - rule: YieldExpression -> YIELD action: yield_expression_no_argument @@ -4861,9 +4867,9 @@ - rule: ConditionalExpression_Yield_Await -> ShortCircuitExpression_Yield_Await action: nop - rule: >- - ConditionalExpression_Yield_Await -> ShortCircuitExpression_Yield_Await CONDITIONAL - _THEN_BLOCK_ AssignmentExpression_In_Yield_Await COLON _ELSE_BLOCK_ - AssignmentExpression_Yield_Await + ConditionalExpression_Yield_Await -> ShortCircuitExpression_Yield_Await CONDITIONAL _THEN_ + AssignmentExpression_In_Yield_Await _DEREFERENCE_ COLON _ELSE_ AssignmentExpression_Yield_Await + _DEREFERENCE_ action: conditional_expression - rule: YieldExpression_Await -> YIELD action: yield_expression_no_argument @@ -5014,21 +5020,21 @@ action: nop - rule: >- LogicalORExpression_Yield -> LogicalORExpression_Yield OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression_Yield + LogicalANDExpression_Yield _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression_Yield -> CoalesceExpressionHead_Yield NULLISH _NULLISH_SHORT_CIRCUIT_ - BitwiseORExpression_Yield + BitwiseORExpression_Yield _DEREFERENCE_ action: logical_expression - rule: LogicalORExpression_Yield_Await -> LogicalANDExpression_Yield_Await action: nop - rule: >- LogicalORExpression_Yield_Await -> LogicalORExpression_Yield_Await OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression_Yield_Await + LogicalANDExpression_Yield_Await _DEREFERENCE_ action: logical_expression - rule: >- CoalesceExpression_Yield_Await -> CoalesceExpressionHead_Yield_Await NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Yield_Await + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Yield_Await _DEREFERENCE_ action: logical_expression - rule: UnaryExpression_Yield -> UpdateExpression_Yield action: nop @@ -5092,7 +5098,7 @@ action: nop - rule: >- LogicalANDExpression_Yield -> LogicalANDExpression_Yield AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression_Yield + BitwiseORExpression_Yield _DEREFERENCE_ action: logical_expression - rule: CoalesceExpressionHead_Yield -> CoalesceExpression_Yield action: nop @@ -5106,7 +5112,7 @@ action: nop - rule: >- LogicalANDExpression_Yield_Await -> LogicalANDExpression_Yield_Await AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression_Yield_Await + BitwiseORExpression_Yield_Await _DEREFERENCE_ action: logical_expression - rule: CoalesceExpressionHead_Yield_Await -> CoalesceExpression_Yield_Await action: nop diff --git a/libs/jsparser/src/syntax/actions.yaml b/libs/jsparser/src/syntax/actions.yaml index a3b2500a..f77b67cf 100644 --- a/libs/jsparser/src/syntax/actions.yaml +++ b/libs/jsparser/src/syntax/actions.yaml @@ -219,12 +219,10 @@ CLASS, FUNCTION, LBRACE, LET LBRACK]) Expression_In SEMICOLON action: process_expression_statement - rule: >- - IfStatement -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ Statement ELSE - _ELSE_BLOCK_ Statement + IfStatement -> IF LPAREN Expression_In RPAREN _THEN_ Statement ELSE _ELSE_ + Statement action: process_if_else_statement -- rule: >- - IfStatement -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ Statement - (?![ELSE]) +- rule: 'IfStatement -> IF LPAREN Expression_In RPAREN _THEN_ Statement (?![ELSE])' action: process_if_statement - rule: BreakableStatement -> IterationStatement action: process_breakable_statement @@ -338,15 +336,17 @@ action: process_assignment_operator - rule: >- AssignmentExpression_In_Await -> LeftHandSideExpression_Await AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression_In_Await -> LeftHandSideExpression_Await OR_ASSIGN _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await + _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression_In_Await -> LeftHandSideExpression_Await NULLISH_ASSIGN _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Await + _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: Statement_Await -> BlockStatement_Await action: process_statement @@ -454,10 +454,10 @@ action: nop - rule: Expression_In -> Expression_In COMMA AssignmentExpression_In action: process_comma_operator -- rule: _THEN_BLOCK_ -> (empty) - action: process_then_block -- rule: _ELSE_BLOCK_ -> (empty) - action: process_else_block +- rule: _THEN_ -> (empty) + action: process_then +- rule: _ELSE_ -> (empty) + action: process_else - rule: IterationStatement -> DoWhileStatement action: process_iteration_statement - rule: IterationStatement -> WhileStatement @@ -611,8 +611,8 @@ action: nop - rule: >- ConditionalExpression_In_Await -> ShortCircuitExpression_In_Await - CONDITIONAL _THEN_BLOCK_ AssignmentExpression_In_Await COLON _ELSE_BLOCK_ - AssignmentExpression_In_Await + CONDITIONAL _THEN_ AssignmentExpression_In_Await _DEREFERENCE_ COLON _ELSE_ + AssignmentExpression_In_Await _DEREFERENCE_ action: process_conditional_expression - rule: >- ArrowFunction_In_Await -> ArrowParameters_Await (!LINE_TERMINATOR_SEQUENCE) @@ -660,6 +660,8 @@ action: nop - rule: _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ -> (empty) action: process_falsy_short_circuit_assignment +- rule: _DEREFERENCE_ -> (empty) + action: process_dereference - rule: _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ -> (empty) action: process_truthy_short_circuit_assignment - rule: _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ -> (empty) @@ -671,11 +673,11 @@ CLASS, FUNCTION, LBRACE, LET LBRACK]) Expression_In_Await SEMICOLON action: process_expression_statement - rule: >- - IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_BLOCK_ - Statement_Await ELSE _ELSE_BLOCK_ Statement_Await + IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_ + Statement_Await ELSE _ELSE_ Statement_Await action: process_if_else_statement - rule: >- - IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_BLOCK_ + IfStatement_Await -> IF LPAREN Expression_In_Await RPAREN _THEN_ Statement_Await (?![ELSE]) action: process_if_statement - rule: BreakableStatement_Await -> IterationStatement_Await @@ -926,15 +928,15 @@ action: process_assignment_operator - rule: >- AssignmentExpression_In -> LeftHandSideExpression AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression_In -> LeftHandSideExpression OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression_In -> LeftHandSideExpression NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: >- DoWhileStatement -> DO _LOOP_START_ Statement _LOOP_BODY_ WHILE LPAREN @@ -1407,15 +1409,17 @@ action: process_assignment_operator - rule: >- AssignmentExpression_In_Yield -> LeftHandSideExpression_Yield AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression_In_Yield -> LeftHandSideExpression_Yield OR_ASSIGN _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield + _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression_In_Yield -> LeftHandSideExpression_Yield NULLISH_ASSIGN _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_In_Yield + _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: BindingRestProperty_Await -> ELLIPSIS BindingIdentifier_Await action: undefined @@ -1464,24 +1468,24 @@ - rule: >- AssignmentExpression_In_Yield_Await -> LeftHandSideExpression_Yield_Await AND_ASSIGN _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression_In_Yield_Await + AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression_In_Yield_Await -> LeftHandSideExpression_Yield_Await OR_ASSIGN _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression_In_Yield_Await + AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression_In_Yield_Await -> LeftHandSideExpression_Yield_Await NULLISH_ASSIGN _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression_In_Yield_Await + AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: ConditionalExpression_In -> ShortCircuitExpression_In action: nop - rule: >- - ConditionalExpression_In -> ShortCircuitExpression_In CONDITIONAL - _THEN_BLOCK_ AssignmentExpression_In COLON _ELSE_BLOCK_ - AssignmentExpression_In + ConditionalExpression_In -> ShortCircuitExpression_In CONDITIONAL _THEN_ + AssignmentExpression_In _DEREFERENCE_ COLON _ELSE_ AssignmentExpression_In + _DEREFERENCE_ action: process_conditional_expression - rule: >- ArrowFunction_In -> ArrowParameters (!LINE_TERMINATOR_SEQUENCE) ARROW @@ -1576,11 +1580,11 @@ action: nop - rule: >- LogicalORExpression_In_Await -> LogicalORExpression_In_Await OR - _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Await + _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Await _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression_In_Await -> CoalesceExpressionHead_In_Await NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In_Await + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In_Await _DEREFERENCE_ action: process_nullish_coalescing - rule: >- CoverParenthesizedExpressionAndArrowParameterList_Await -> LPAREN @@ -1835,8 +1839,8 @@ action: nop - rule: >- ConditionalExpression_In_Yield -> ShortCircuitExpression_In_Yield - CONDITIONAL _THEN_BLOCK_ AssignmentExpression_In_Yield COLON _ELSE_BLOCK_ - AssignmentExpression_In_Yield + CONDITIONAL _THEN_ AssignmentExpression_In_Yield _DEREFERENCE_ COLON _ELSE_ + AssignmentExpression_In_Yield _DEREFERENCE_ action: process_conditional_expression - rule: YieldExpression_In -> YIELD action: undefined @@ -1892,9 +1896,9 @@ action: nop - rule: >- ConditionalExpression_In_Yield_Await -> - ShortCircuitExpression_In_Yield_Await CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In_Yield_Await COLON _ELSE_BLOCK_ - AssignmentExpression_In_Yield_Await + ShortCircuitExpression_In_Yield_Await CONDITIONAL _THEN_ + AssignmentExpression_In_Yield_Await _DEREFERENCE_ COLON _ELSE_ + AssignmentExpression_In_Yield_Await _DEREFERENCE_ action: process_conditional_expression - rule: YieldExpression_In_Await -> YIELD action: undefined @@ -1977,15 +1981,15 @@ action: process_assignment_operator - rule: >- AssignmentExpression -> LeftHandSideExpression AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression -> LeftHandSideExpression OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression -> LeftHandSideExpression NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: VariableDeclaration -> BindingIdentifier action: undefined @@ -2047,7 +2051,7 @@ action: nop - rule: >- LogicalANDExpression_In_Await -> LogicalANDExpression_In_Await AND - _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Await + _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Await _DEREFERENCE_ action: process_logical_and - rule: _TRUTHY_SHORT_CIRCUIT_ -> (empty) action: process_truthy_short_circuit @@ -2263,11 +2267,11 @@ action: nop - rule: >- LogicalORExpression_In -> LogicalORExpression_In OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression_In + LogicalANDExpression_In _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression_In -> CoalesceExpressionHead_In NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In _DEREFERENCE_ action: process_nullish_coalescing - rule: >- CoverParenthesizedExpressionAndArrowParameterList -> LPAREN Expression_In @@ -2350,8 +2354,9 @@ - rule: ConditionalExpression -> ShortCircuitExpression action: nop - rule: >- - ConditionalExpression -> ShortCircuitExpression CONDITIONAL _THEN_BLOCK_ - AssignmentExpression_In COLON _ELSE_BLOCK_ AssignmentExpression + ConditionalExpression -> ShortCircuitExpression CONDITIONAL _THEN_ + AssignmentExpression_In _DEREFERENCE_ COLON _ELSE_ AssignmentExpression + _DEREFERENCE_ action: process_conditional_expression - rule: >- ArrowFunction -> ArrowParameters (!LINE_TERMINATOR_SEQUENCE) ARROW @@ -2548,15 +2553,15 @@ action: process_assignment_operator - rule: >- AssignmentExpression_Await -> LeftHandSideExpression_Await AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression_Await -> LeftHandSideExpression_Await OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression_Await -> LeftHandSideExpression_Await NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Await _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: VariableDeclaration_Await -> BindingIdentifier_Await action: undefined @@ -2588,11 +2593,11 @@ action: nop - rule: >- LogicalORExpression_In_Yield -> LogicalORExpression_In_Yield OR - _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Yield + _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Yield _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression_In_Yield -> CoalesceExpressionHead_In_Yield NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield _DEREFERENCE_ action: process_nullish_coalescing - rule: >- CoverParenthesizedExpressionAndArrowParameterList_Yield -> LPAREN @@ -2694,11 +2699,12 @@ action: nop - rule: >- LogicalORExpression_In_Yield_Await -> LogicalORExpression_In_Yield_Await OR - _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Yield_Await + _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_In_Yield_Await _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression_In_Yield_Await -> CoalesceExpressionHead_In_Yield_Await NULLISH _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield_Await + _DEREFERENCE_ action: process_nullish_coalescing - rule: >- CoverParenthesizedExpressionAndArrowParameterList_Yield_Await -> LPAREN @@ -2819,7 +2825,7 @@ action: nop - rule: >- LogicalANDExpression_In -> LogicalANDExpression_In AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression_In + BitwiseORExpression_In _DEREFERENCE_ action: process_logical_and - rule: CoalesceExpressionHead_In -> CoalesceExpression_In action: nop @@ -3014,8 +3020,8 @@ action: nop - rule: >- ConditionalExpression_Await -> ShortCircuitExpression_Await CONDITIONAL - _THEN_BLOCK_ AssignmentExpression_In_Await COLON _ELSE_BLOCK_ - AssignmentExpression_Await + _THEN_ AssignmentExpression_In_Await _DEREFERENCE_ COLON _ELSE_ + AssignmentExpression_Await _DEREFERENCE_ action: process_conditional_expression - rule: >- ArrowFunction_Await -> ArrowParameters_Await (!LINE_TERMINATOR_SEQUENCE) @@ -3043,7 +3049,7 @@ action: nop - rule: >- LogicalANDExpression_In_Yield -> LogicalANDExpression_In_Yield AND - _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield + _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield _DEREFERENCE_ action: process_logical_and - rule: CoalesceExpressionHead_In_Yield -> CoalesceExpression_In_Yield action: nop @@ -3105,7 +3111,7 @@ action: nop - rule: >- LogicalANDExpression_In_Yield_Await -> LogicalANDExpression_In_Yield_Await - AND _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield_Await + AND _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_In_Yield_Await _DEREFERENCE_ action: process_logical_and - rule: CoalesceExpressionHead_In_Yield_Await -> CoalesceExpression_In_Yield_Await action: nop @@ -3209,11 +3215,11 @@ action: nop - rule: >- LogicalORExpression -> LogicalORExpression OR _TRUTHY_SHORT_CIRCUIT_ - LogicalANDExpression + LogicalANDExpression _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression -> CoalesceExpressionHead NULLISH _NULLISH_SHORT_CIRCUIT_ - BitwiseORExpression + BitwiseORExpression _DEREFERENCE_ action: process_nullish_coalescing - rule: ExpressionBody -> AssignmentExpression action: nop @@ -3222,12 +3228,12 @@ - rule: BlockStatement_Return -> Block_Return action: process_block_statement - rule: >- - IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ - Statement_Return ELSE _ELSE_BLOCK_ Statement_Return + IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_ Statement_Return + ELSE _ELSE_ Statement_Return action: process_if_else_statement - rule: >- - IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_BLOCK_ - Statement_Return (?![ELSE]) + IfStatement_Return -> IF LPAREN Expression_In RPAREN _THEN_ Statement_Return + (?![ELSE]) action: process_if_statement - rule: BreakableStatement_Return -> IterationStatement_Return action: process_breakable_statement @@ -3519,7 +3525,7 @@ action: nop - rule: >- LogicalANDExpression -> LogicalANDExpression AND _FALSY_SHORT_CIRCUIT_ - BitwiseORExpression + BitwiseORExpression _DEREFERENCE_ action: process_logical_and - rule: CoalesceExpressionHead -> CoalesceExpression action: nop @@ -3565,12 +3571,12 @@ CLASS, FUNCTION, LBRACE, LET LBRACK]) Expression_In_Yield SEMICOLON action: process_expression_statement - rule: >- - IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN - _THEN_BLOCK_ Statement_Yield_Return ELSE _ELSE_BLOCK_ Statement_Yield_Return + IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN _THEN_ + Statement_Yield_Return ELSE _ELSE_ Statement_Yield_Return action: process_if_else_statement - rule: >- - IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN - _THEN_BLOCK_ Statement_Yield_Return (?![ELSE]) + IfStatement_Yield_Return -> IF LPAREN Expression_In_Yield RPAREN _THEN_ + Statement_Yield_Return (?![ELSE]) action: process_if_statement - rule: BreakableStatement_Yield_Return -> IterationStatement_Yield_Return action: process_breakable_statement @@ -3635,12 +3641,12 @@ - rule: BlockStatement_Await_Return -> Block_Await_Return action: process_block_statement - rule: >- - IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN - _THEN_BLOCK_ Statement_Await_Return ELSE _ELSE_BLOCK_ Statement_Await_Return + IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN _THEN_ + Statement_Await_Return ELSE _ELSE_ Statement_Await_Return action: process_if_else_statement - rule: >- - IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN - _THEN_BLOCK_ Statement_Await_Return (?![ELSE]) + IfStatement_Await_Return -> IF LPAREN Expression_In_Await RPAREN _THEN_ + Statement_Await_Return (?![ELSE]) action: process_if_statement - rule: BreakableStatement_Await_Return -> IterationStatement_Await_Return action: process_breakable_statement @@ -3685,12 +3691,11 @@ action: process_expression_statement - rule: >- IfStatement_Yield_Await_Return -> IF LPAREN Expression_In_Yield_Await RPAREN - _THEN_BLOCK_ Statement_Yield_Await_Return ELSE _ELSE_BLOCK_ - Statement_Yield_Await_Return + _THEN_ Statement_Yield_Await_Return ELSE _ELSE_ Statement_Yield_Await_Return action: process_if_else_statement - rule: >- IfStatement_Yield_Await_Return -> IF LPAREN Expression_In_Yield_Await RPAREN - _THEN_BLOCK_ Statement_Yield_Await_Return (?![ELSE]) + _THEN_ Statement_Yield_Await_Return (?![ELSE]) action: process_if_statement - rule: >- BreakableStatement_Yield_Await_Return -> @@ -3801,11 +3806,11 @@ action: nop - rule: >- LogicalORExpression_Await -> LogicalORExpression_Await OR - _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_Await + _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_Await _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression_Await -> CoalesceExpressionHead_Await NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Await + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Await _DEREFERENCE_ action: process_nullish_coalescing - rule: BitwiseANDExpression_In_Yield -> EqualityExpression_In_Yield action: nop @@ -4239,7 +4244,7 @@ action: nop - rule: >- LogicalANDExpression_Await -> LogicalANDExpression_Await AND - _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_Await + _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_Await _DEREFERENCE_ action: process_logical_and - rule: CoalesceExpressionHead_Await -> CoalesceExpression_Await action: nop @@ -5236,15 +5241,15 @@ action: process_assignment_operator - rule: >- AssignmentExpression_Yield -> LeftHandSideExpression_Yield AND_ASSIGN - _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield + _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression_Yield -> LeftHandSideExpression_Yield OR_ASSIGN - _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield + _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression_Yield -> LeftHandSideExpression_Yield NULLISH_ASSIGN - _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield + _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: VariableDeclaration_Yield -> BindingIdentifier_Yield action: undefined @@ -5287,15 +5292,17 @@ - rule: >- AssignmentExpression_Yield_Await -> LeftHandSideExpression_Yield_Await AND_ASSIGN _FALSY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await + _DEREFERENCE_ action: process_logical_and_assignment - rule: >- AssignmentExpression_Yield_Await -> LeftHandSideExpression_Yield_Await OR_ASSIGN _TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_ AssignmentExpression_Yield_Await + _DEREFERENCE_ action: process_logical_or_assignment - rule: >- AssignmentExpression_Yield_Await -> LeftHandSideExpression_Yield_Await NULLISH_ASSIGN _NULLISH_SHORT_CIRCUIT_ASSIGNMENT_ - AssignmentExpression_Yield_Await + AssignmentExpression_Yield_Await _DEREFERENCE_ action: process_nullish_coalescing_assignment - rule: VariableDeclaration_Yield_Await -> BindingIdentifier_Yield_Await action: undefined @@ -5393,8 +5400,8 @@ action: nop - rule: >- ConditionalExpression_Yield -> ShortCircuitExpression_Yield CONDITIONAL - _THEN_BLOCK_ AssignmentExpression_In_Yield COLON _ELSE_BLOCK_ - AssignmentExpression_Yield + _THEN_ AssignmentExpression_In_Yield _DEREFERENCE_ COLON _ELSE_ + AssignmentExpression_Yield _DEREFERENCE_ action: process_conditional_expression - rule: YieldExpression -> YIELD action: undefined @@ -5432,8 +5439,8 @@ action: nop - rule: >- ConditionalExpression_Yield_Await -> ShortCircuitExpression_Yield_Await - CONDITIONAL _THEN_BLOCK_ AssignmentExpression_In_Yield_Await COLON - _ELSE_BLOCK_ AssignmentExpression_Yield_Await + CONDITIONAL _THEN_ AssignmentExpression_In_Yield_Await _DEREFERENCE_ COLON + _ELSE_ AssignmentExpression_Yield_Await _DEREFERENCE_ action: process_conditional_expression - rule: YieldExpression_Await -> YIELD action: undefined @@ -5609,21 +5616,21 @@ action: nop - rule: >- LogicalORExpression_Yield -> LogicalORExpression_Yield OR - _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_Yield + _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_Yield _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression_Yield -> CoalesceExpressionHead_Yield NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Yield + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Yield _DEREFERENCE_ action: process_nullish_coalescing - rule: LogicalORExpression_Yield_Await -> LogicalANDExpression_Yield_Await action: nop - rule: >- LogicalORExpression_Yield_Await -> LogicalORExpression_Yield_Await OR - _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_Yield_Await + _TRUTHY_SHORT_CIRCUIT_ LogicalANDExpression_Yield_Await _DEREFERENCE_ action: process_logical_or - rule: >- CoalesceExpression_Yield_Await -> CoalesceExpressionHead_Yield_Await NULLISH - _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Yield_Await + _NULLISH_SHORT_CIRCUIT_ BitwiseORExpression_Yield_Await _DEREFERENCE_ action: process_nullish_coalescing - rule: UnaryExpression_Yield -> UpdateExpression_Yield action: nop @@ -5691,7 +5698,7 @@ action: nop - rule: >- LogicalANDExpression_Yield -> LogicalANDExpression_Yield AND - _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_Yield + _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_Yield _DEREFERENCE_ action: process_logical_and - rule: CoalesceExpressionHead_Yield -> CoalesceExpression_Yield action: nop @@ -5707,7 +5714,7 @@ action: nop - rule: >- LogicalANDExpression_Yield_Await -> LogicalANDExpression_Yield_Await AND - _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_Yield_Await + _FALSY_SHORT_CIRCUIT_ BitwiseORExpression_Yield_Await _DEREFERENCE_ action: process_logical_and - rule: CoalesceExpressionHead_Yield_Await -> CoalesceExpression_Yield_Await action: nop diff --git a/libs/jsparser/src/syntax/mod.rs b/libs/jsparser/src/syntax/mod.rs index ee1a2cad..7a599199 100644 --- a/libs/jsparser/src/syntax/mod.rs +++ b/libs/jsparser/src/syntax/mod.rs @@ -218,8 +218,8 @@ pub enum Node<'s> { ArrowFunction, AsyncArrowFunction, AwaitExpression, - ThenBlock, - ElseBlock, + Then, + Else, FalsyShortCircuit, TruthyShortCircuit, NullishShortCircuit, @@ -235,6 +235,7 @@ pub enum Node<'s> { LoopBody, StartBlockScope, EndBlockScope, + Dereference, } #[derive(Clone, Copy)] @@ -531,15 +532,21 @@ where Err(Error::SyntaxError) } - // _THEN_BLOCK_ - fn process_then_block(&mut self) -> Result<(), Error> { - self.enqueue(Node::ThenBlock); + // _DEREFERENCE_ + fn process_dereference(&mut self) -> Result<(), Error> { + self.enqueue(Node::Dereference); Ok(()) } - // _ELSE_BLOCK_ - fn process_else_block(&mut self) -> Result<(), Error> { - self.enqueue(Node::ElseBlock); + // _THEN_ + fn process_then(&mut self) -> Result<(), Error> { + self.enqueue(Node::Then); + Ok(()) + } + + // _ELSE_ + fn process_else(&mut self) -> Result<(), Error> { + self.enqueue(Node::Else); Ok(()) } diff --git a/libs/jsparser/src/transpile.js b/libs/jsparser/src/transpile.js index 42ad248f..4089cd8a 100644 --- a/libs/jsparser/src/transpile.js +++ b/libs/jsparser/src/transpile.js @@ -598,8 +598,8 @@ function addActions(rules) { '_ASYNC_FUNCTION_CONTEXT_', '_FUNCTION_SIGNATURE_', '_ANONYMOUS_FUNCTION_SIGNATURE_', - '_ELSE_BLOCK_', - '_THEN_BLOCK_', + '_ELSE_', + '_THEN_', '_BLOCK_SCOPE_', '_FALSY_SHORT_CIRCUIT_', '_TRUTHY_SHORT_CIRCUIT_', @@ -621,6 +621,7 @@ function addActions(rules) { '_TRY_BLOCK_', '_CATCH_BLOCK_', '_FINALLY_BLOCK_', + '_DEREFERENCE_', ]; for (const action of ACTIONS) { @@ -686,12 +687,12 @@ function modifyIfStatement(rules) { rule.values[0] = rule .values[0] - .replace('`)` Statement[', '`)` _THEN_BLOCK_ Statement[') - .replace('`else` Statement[', '`else` _ELSE_BLOCK_ Statement['); + .replace('`)` Statement[', '`)` _THEN_ Statement[') + .replace('`else` Statement[', '`else` _ELSE_ Statement['); rule.values[1] = rule .values[1] - .replace('`)` Statement[', '`)` _THEN_BLOCK_ Statement['); + .replace('`)` Statement[', '`)` _THEN_ Statement['); return rules; } @@ -704,18 +705,27 @@ function modifyConditionalExpression(rules) { rule = rules.find((rule) => rule.name === 'ConditionalExpression[In, Yield, Await]'); assert(rule !== undefined); assert(rule.values.length === 2); - const [cond, thenBlock, elseBlock] = rule + const [cond, thenExpr, elseExpr] = rule .values[1] .split(/`\?`|`\:`/) .map((term) => term.trim()); rule.values[1] = [ cond, '`?`', - '_THEN_BLOCK_', - thenBlock, + '_THEN_', + thenExpr, + // Insert the _DEREFERENCE_ actions just after the expression in order to perform dereference + // on the expression before processing the _ELSE_ action. The _ELSE_ action + // creates a basic block and switches the current basic block to it. So, the dereference has + // to be perform before that. + // + // For similar reasons, the _DEREFERENCE_ actions are inserted into other production rules as + // well. + '_DEREFERENCE_', '`:`', - '_ELSE_BLOCK_', - elseBlock, + '_ELSE_', + elseExpr, + '_DEREFERENCE_', ].join(' '); return rules; @@ -725,33 +735,93 @@ function modifyShortCircuitExpressions(rules) { const TARGETS = [ { rule: 'LogicalANDExpression[In, Yield, Await]', - op: '`&&`', - action: '_FALSY_SHORT_CIRCUIT_', + targets: [ + { + term: '`&&` BitwiseORExpression[?In, ?Yield, ?Await]', + action: '_DEREFERENCE_', + insertBefore: false, + }, + { + term: '`&&`', + action: '_FALSY_SHORT_CIRCUIT_', + insertBefore: false, + }, + ], }, { rule: 'LogicalORExpression[In, Yield, Await]', - op: '`||`', - action: '_TRUTHY_SHORT_CIRCUIT_', + targets: [ + { + term: '`||` LogicalANDExpression[?In, ?Yield, ?Await]', + action: '_DEREFERENCE_', + insertBefore: false, + }, + { + term: '`||`', + action: '_TRUTHY_SHORT_CIRCUIT_', + insertBefore: false, + }, + ], }, { rule: 'CoalesceExpression[In, Yield, Await]', - op: '`??`', - action: '_NULLISH_SHORT_CIRCUIT_', + targets: [ + { + term: '`??` BitwiseORExpression[?In, ?Yield, ?Await]', + action: '_DEREFERENCE_', + insertBefore: false, + }, + { + term: '`??`', + action: '_NULLISH_SHORT_CIRCUIT_', + insertBefore: false, + }, + ], }, { rule: 'AssignmentExpression[In, Yield, Await]', - op: '`&&=`', - action: '_FALSY_SHORT_CIRCUIT_ASSIGNMENT_', + targets: [ + { + term: '`&&=` AssignmentExpression[?In, ?Yield, ?Await]', + action: '_DEREFERENCE_', + insertBefore: false, + }, + { + term: '`&&=`', + action: '_FALSY_SHORT_CIRCUIT_ASSIGNMENT_', + insertBefore: false, + }, + ], }, { rule: 'AssignmentExpression[In, Yield, Await]', - op: '`||=`', - action: '_TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_', + targets: [ + { + term: '`||=` AssignmentExpression[?In, ?Yield, ?Await]', + action: '_DEREFERENCE_', + insertBefore: false, + }, + { + term: '`||=`', + action: '_TRUTHY_SHORT_CIRCUIT_ASSIGNMENT_', + insertBefore: false, + }, + ], }, { rule: 'AssignmentExpression[In, Yield, Await]', - op: '`??=`', - action: '_NULLISH_SHORT_CIRCUIT_ASSIGNMENT_', + targets: [ + { + term: '`??=` AssignmentExpression[?In, ?Yield, ?Await]', + action: '_DEREFERENCE_', + insertBefore: false, + }, + { + term: '`??=`', + action: '_NULLISH_SHORT_CIRCUIT_ASSIGNMENT_', + insertBefore: false, + }, + ], }, ]; @@ -759,11 +829,7 @@ function modifyShortCircuitExpressions(rules) { log.debug(`Modifying ${target.rule}...`); const rule = rules.find((rule) => rule.name === target.rule); assert(rule !== undefined); - const index = rule.values.findIndex((production) => production.includes(target.op)); - assert(index !== -1); - const [lhs, rhs] = rule.values[index].split(target.op).map((term) => term.trim()); - // Insert target.action for the short-circuit evaluation of the LHS. - rule.values[index] = [lhs, target.op, target.action, rhs].join(' '); + modifyTargetsInRule(rule, target.targets); } return rules; diff --git a/libs/jsruntime/src/function.rs b/libs/jsruntime/src/function.rs deleted file mode 100644 index 79edf4a4..00000000 --- a/libs/jsruntime/src/function.rs +++ /dev/null @@ -1,155 +0,0 @@ -use jsparser::Symbol; - -/// The identifier of a function. -#[derive(Clone, Copy, Default, Eq, PartialEq)] -pub struct FunctionId(u32); - -impl FunctionId { - const HOST_BIT: u32 = 1 << 31; - const COROUTINE_BIT: u32 = 1 << 30; - - const VALUE_MASK: u32 = !(Self::HOST_BIT | Self::COROUTINE_BIT); - const MAX_INDEX: usize = Self::VALUE_MASK as usize; - - pub const MAIN: Self = Self::native(0, false); - - pub const fn is_native(&self) -> bool { - (self.0 & Self::HOST_BIT) == 0 - } - - pub const fn is_host(&self) -> bool { - (self.0 & Self::HOST_BIT) != 0 - } - - pub const fn is_coroutine(&self) -> bool { - (self.0 & Self::COROUTINE_BIT) != 0 - } - - const fn native(index: usize, coroutine: bool) -> Self { - debug_assert!(index <= Self::MAX_INDEX); - Self(if coroutine { - index as u32 | Self::COROUTINE_BIT - } else { - index as u32 - }) - } - - const fn host(index: usize) -> Self { - debug_assert!(index <= Self::MAX_INDEX); - Self(index as u32 | Self::HOST_BIT) - } - - const fn index(&self) -> usize { - (self.0 & Self::VALUE_MASK) as usize - } -} - -impl From for FunctionId { - fn from(value: u32) -> Self { - Self(value) - } -} - -impl From for u32 { - fn from(value: FunctionId) -> Self { - value.0 - } -} - -impl std::fmt::Debug for FunctionId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let index = self.index(); - if self.is_native() { - write!(f, "FunctionId::Native")?; - } else { - write!(f, "FunctionId::Host")?; - } - if self.is_coroutine() { - write!(f, "Coroutine")?; - } - write!(f, "({index})") - } -} - -pub struct FunctionRegistry { - functions: Vec, -} - -impl FunctionRegistry { - pub fn new() -> Self { - Self { functions: vec![] } - } - - pub fn create_native_function(&mut self, coroutine: bool) -> FunctionId { - let index = self.functions.len(); - assert!(index <= FunctionId::MAX_INDEX); - self.functions.push(Function::Native(NativeFunction { - scratch_buffer_len: 0, - })); - FunctionId::native(index, coroutine) - } - - pub fn register_host_function(&mut self, symbol: Symbol) -> FunctionId { - let index = self.functions.len(); - assert!(index <= FunctionId::MAX_INDEX); - self.functions.push(Function::Host(HostFunction { symbol })); - FunctionId::host(index) - } - - pub fn get_native(&self, func_id: FunctionId) -> &NativeFunction { - debug_assert!(func_id.is_native()); - match self.functions.get(func_id.index()) { - Some(Function::Native(func)) => func, - _ => unreachable!(), - } - } - - pub fn get_native_mut(&mut self, func_id: FunctionId) -> &mut NativeFunction { - debug_assert!(func_id.is_native()); - match self.functions.get_mut(func_id.index()) { - Some(Function::Native(func)) => func, - _ => unreachable!(), - } - } - - pub fn enumerate_host_function(&self) -> impl Iterator { - self.functions - .iter() - .enumerate() - .filter_map(|(index, func)| { - debug_assert!(index <= FunctionId::MAX_INDEX); - match func { - Function::Host(func) => Some((FunctionId::host(index), func)), - _ => None, - } - }) - } -} - -enum Function { - Native(NativeFunction), - Host(HostFunction), -} - -pub struct NativeFunction { - // [[ECMAScriptCode]] - pub scratch_buffer_len: u32, -} - -pub struct HostFunction { - pub symbol: Symbol, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_function_id_max() { - // TODO: checking at compile time is better. - assert_eq!( - FunctionId::MAX_INDEX, - (FunctionId::COROUTINE_BIT - 1) as usize - ); - } -} diff --git a/libs/jsruntime/src/lambda.rs b/libs/jsruntime/src/lambda.rs new file mode 100644 index 00000000..db83ed5f --- /dev/null +++ b/libs/jsruntime/src/lambda.rs @@ -0,0 +1,61 @@ +/// The identifier of a lambda function. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct LambdaId(u32); + +impl LambdaId { + pub const MAIN: Self = Self(0); + + const fn new(index: usize) -> Self { + debug_assert!(index <= u32::MAX as usize); + Self(index as u32) + } + + const fn index(&self) -> usize { + self.0 as usize + } +} + +impl From for LambdaId { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl From for u32 { + fn from(value: LambdaId) -> Self { + value.0 + } +} + +pub struct LambdaRegistry { + entries: Vec, +} + +impl LambdaRegistry { + pub fn new() -> Self { + Self { entries: vec![] } + } + + pub fn register(&mut self, is_coroutine: bool) -> LambdaId { + let index = self.entries.len(); + self.entries.push(LambdaInfo { + scratch_buffer_len: 0, + is_coroutine, + }); + LambdaId::new(index) + } + + pub fn get(&self, id: LambdaId) -> &LambdaInfo { + self.entries.get(id.index()).unwrap() + } + + pub fn get_mut(&mut self, id: LambdaId) -> &mut LambdaInfo { + self.entries.get_mut(id.index()).unwrap() + } +} + +pub struct LambdaInfo { + // [[ECMAScriptCode]] + pub scratch_buffer_len: u32, + pub is_coroutine: bool, +} diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index 807980a4..2b3e0747 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -1,6 +1,7 @@ -mod function; +mod lambda; mod llvmir; mod logger; +mod objects; mod semantics; mod tasklet; mod types; @@ -9,9 +10,12 @@ use std::ffi::c_void; use jsparser::SymbolRegistry; -use function::FunctionId; -use function::FunctionRegistry; +use lambda::LambdaId; +use lambda::LambdaRegistry; use llvmir::Executor; +use objects::Object; +use objects::Property; +use objects::PropertyFlags; use types::ReturnValue; pub use llvmir::CompileError; @@ -52,24 +56,30 @@ impl BasicRuntime { pub struct Runtime { pref: RuntimePref, symbol_registry: SymbolRegistry, - function_registry: FunctionRegistry, + lambda_registry: LambdaRegistry, executor: Executor, // TODO: GcArena allocator: bumpalo::Bump, tasklet_system: tasklet::System, + global_object: Object, extension: X, } impl Runtime { pub fn with_extension(extension: X) -> Self { let functions = llvmir::RuntimeFunctions::new::(); + + let mut global_object = Object::default(); + global_object.define_builtin_global_properties(); + Self { pref: Default::default(), symbol_registry: Default::default(), - function_registry: FunctionRegistry::new(), + lambda_registry: LambdaRegistry::new(), executor: Executor::new(&functions), allocator: bumpalo::Bump::new(), tasklet_system: tasklet::System::new(), + global_object, extension, } } @@ -96,17 +106,21 @@ impl Runtime { R: Clone + ReturnValue, { let symbol = self.symbol_registry.intern_str(name); - let func_id = self.function_registry.register_host_function(symbol); - self.executor - .register_host_function(func_id, types::into_lambda(host_fn)); - logger::debug!(event = "register_host_function", name, ?symbol, ?func_id); + logger::debug!(event = "register_host_function", name, ?symbol); + let lambda = types::into_lambda(host_fn); + let closure = self.create_closure(lambda, 0); + let value = Value::Closure(closure); + // TODO: add `flags` to the arguments. + let flags = PropertyFlags::empty(); + let prop = Property::Data { value, flags }; + self.global_object.define_own_property(symbol, prop); } pub fn evaluate(&mut self, module: Module) -> Result { logger::debug!(event = "evaluate"); self.executor.register_module(module); let mut retv = Value::Undefined; - let status = match self.executor.get_native_function(FunctionId::MAIN) { + let status = match self.executor.get_lambda(LambdaId::MAIN) { Some(main) => unsafe { main( // runtime @@ -133,6 +147,14 @@ impl Runtime { fn allocator(&self) -> &bumpalo::Bump { &self.allocator } + + fn global_object(&self) -> &Object { + &self.global_object + } + + fn global_object_mut(&mut self) -> &mut Object { + &mut self.global_object + } } impl Default for Runtime diff --git a/libs/jsruntime/src/llvmir/bridge.rs b/libs/jsruntime/src/llvmir/bridge.rs index 5876bacb..72797a11 100644 --- a/libs/jsruntime/src/llvmir/bridge.rs +++ b/libs/jsruntime/src/llvmir/bridge.rs @@ -1,12 +1,15 @@ use std::ffi::c_char; use std::ffi::c_void; +use jsparser::Symbol; + use crate::logger; use crate::types::Capture; use crate::types::Closure; use crate::types::Coroutine; use crate::types::Lambda; use crate::types::Value; +use crate::Runtime; pub fn initialize() { unsafe { @@ -29,6 +32,8 @@ pub struct RuntimeFunctions { await_promise: unsafe extern "C" fn(*mut c_void, u32, u32), resume: unsafe extern "C" fn(*mut c_void, u32), emit_promise_resolved: unsafe extern "C" fn(*mut c_void, u32, *const Value), + get: unsafe extern "C" fn(*mut c_void, u32) -> *const Value, + set: unsafe extern "C" fn(*mut c_void, u32, *const Value), assert: unsafe extern "C" fn(*mut c_void, bool, *const c_char), print_u32: unsafe extern "C" fn(*mut c_void, u32, *const c_char), print_f64: unsafe extern "C" fn(*mut c_void, f64, *const c_char), @@ -52,6 +57,8 @@ impl RuntimeFunctions { await_promise: runtime_await_promise::, resume: runtime_resume::, emit_promise_resolved: runtime_emit_promise_resolved::, + get: runtime_get::, + set: runtime_set::, assert: runtime_assert, print_u32: runtime_print_u32, print_f64: runtime_print_f64, @@ -240,33 +247,39 @@ unsafe extern "C" fn runtime_create_capture( capture } -unsafe extern "C" fn runtime_create_closure( - runtime: *mut c_void, - lambda: Lambda, - num_captures: u16, -) -> *mut Closure { - const BASE_LAYOUT: std::alloc::Layout = unsafe { - std::alloc::Layout::from_size_align_unchecked( - std::mem::offset_of!(Closure, captures), - std::mem::align_of::(), - ) - }; +impl Runtime { + pub(crate) fn create_closure(&mut self, lambda: Lambda, num_captures: u16) -> *mut Closure { + const BASE_LAYOUT: std::alloc::Layout = unsafe { + std::alloc::Layout::from_size_align_unchecked( + std::mem::offset_of!(Closure, captures), + std::mem::align_of::(), + ) + }; - let storage_layout = std::alloc::Layout::array::<*mut Capture>(num_captures as usize).unwrap(); - let (layout, _) = BASE_LAYOUT.extend(storage_layout).unwrap(); + let storage_layout = + std::alloc::Layout::array::<*mut Capture>(num_captures as usize).unwrap(); + let (layout, _) = BASE_LAYOUT.extend(storage_layout).unwrap(); - let runtime = into_runtime!(runtime, X); - let allocator = runtime.allocator(); + let allocator = self.allocator(); - // TODO: GC - let ptr = allocator.alloc_layout(layout); + // TODO: GC + let ptr = allocator.alloc_layout(layout); - let closure = ptr.cast::().as_ptr(); - (*closure).lambda = lambda; - (*closure).num_captures = num_captures; - // `(*closure).captures[]` will be filled with actual pointers to `Captures`. + let closure = unsafe { ptr.cast::().as_mut() }; + closure.lambda = lambda; + closure.num_captures = num_captures; + // `closure.captures[]` will be filled with actual pointers to `Captures`. - closure + closure as *mut Closure + } +} + +unsafe extern "C" fn runtime_create_closure( + runtime: *mut c_void, + lambda: Lambda, + num_captures: u16, +) -> *mut Closure { + into_runtime!(runtime, X).create_closure(lambda, num_captures) } unsafe extern "C" fn runtime_create_coroutine( @@ -337,6 +350,24 @@ unsafe extern "C" fn runtime_emit_promise_resolved( runtime.emit_promise_resolved(promise.into(), cloned); } +unsafe extern "C" fn runtime_get(runtime: *mut c_void, symbol: u32) -> *const Value { + debug_assert_ne!(symbol, 0); + let runtime = into_runtime!(runtime, X); + let symbol = Symbol::from(symbol); + match runtime.global_object().get(symbol) { + Some(value) => value, + None => std::ptr::null(), + } +} + +unsafe extern "C" fn runtime_set(runtime: *mut c_void, symbol: u32, value: *const Value) { + debug_assert_ne!(symbol, 0); + let runtime = into_runtime!(runtime, X); + let symbol = Symbol::from(symbol); + let value = value.as_ref().unwrap(); + runtime.global_object_mut().set(symbol, value); +} + unsafe extern "C" fn runtime_assert( _runtime: *mut c_void, assertion: bool, diff --git a/libs/jsruntime/src/llvmir/compiler/bridge.rs b/libs/jsruntime/src/llvmir/compiler/bridge.rs index 96c5522d..c90b36d7 100644 --- a/libs/jsruntime/src/llvmir/compiler/bridge.rs +++ b/libs/jsruntime/src/llvmir/compiler/bridge.rs @@ -4,11 +4,13 @@ use std::ffi::CStr; use paste::paste; +use jsparser::Symbol; + use crate::llvmir::module::Module; use crate::llvmir::module::ModulePeer; use crate::logger; use crate::semantics::ScopeRef; -use crate::FunctionId; +use crate::LambdaId; pub struct CompilerBridge(CompilerPeer); @@ -86,9 +88,9 @@ impl CompilerBridge { // function - pub fn start_function(&self, func_id: FunctionId) { + pub fn start_function(&self, lambda_id: LambdaId) { unsafe { - compiler_peer_start_function(self.0, func_id.into()); + compiler_peer_start_function(self.0, lambda_id.into()); } } @@ -105,8 +107,8 @@ impl CompilerBridge { } } - pub fn get_function(&self, func_id: FunctionId) -> LambdaIr { - lambda_ir!(compiler_peer_get_function(self.0, func_id.into())) + pub fn get_function(&self, lambda_id: LambdaId) -> LambdaIr { + lambda_ir!(compiler_peer_get_function(self.0, lambda_id.into())) } // basic block @@ -508,6 +510,12 @@ impl CompilerBridge { // value + pub fn create_is_nullptr(&self, value: ValueIr) -> BooleanIr { + boolean_ir! { + compiler_peer_create_is_nullptr(self.0, value.0) + } + } + pub fn create_has_value(&self, value: ValueIr) -> BooleanIr { boolean_ir! { compiler_peer_create_has_value(self.0, value.0) @@ -1011,6 +1019,20 @@ impl CompilerBridge { } } + // object + + pub fn create_get(&self, symbol: Symbol) -> ValueIr { + value_ir! { + compiler_peer_create_get(self.0, symbol.id()) + } + } + + pub fn create_set(&self, symbol: Symbol, value: ValueIr) { + unsafe { + compiler_peer_create_set(self.0, symbol.id(), value.0); + } + } + // scope cleanup checker pub fn enable_scope_cleanup_checker(&self, is_coroutine: bool) { @@ -1161,15 +1183,6 @@ impl ArgvIr { pub const NONE: Self = Self(std::ptr::null_mut()); } -impl CaptureIr { - pub fn get_name_or_as_operand<'a>(&self, buf: *mut std::ffi::c_char, len: usize) -> &'a CStr { - unsafe { - compiler_peer_get_value_name_or_as_operand(self.0 as ValueIrPtr, buf, len); - CStr::from_ptr(buf) - } - } -} - // DO NOT USE MACROS FOR THE FOLLOWING TYPE DEFINITIONS. // cbindgen does not support macro expansions. type CompilerPeer = *mut c_void; @@ -1197,10 +1210,10 @@ extern "C" { // function - fn compiler_peer_start_function(peer: CompilerPeer, func_id: u32); + fn compiler_peer_start_function(peer: CompilerPeer, lambda_id: u32); fn compiler_peer_end_function(peer: CompilerPeer, optimize: bool); fn compiler_peer_set_locals_block(peer: CompilerPeer, block: BasicBlockPtr); - fn compiler_peer_get_function(peer: CompilerPeer, func_id: u32) -> LambdaIrPtr; + fn compiler_peer_get_function(peer: CompilerPeer, lambda_id: u32) -> LambdaIrPtr; // basic block @@ -1418,6 +1431,7 @@ extern "C" { // value + fn compiler_peer_create_is_nullptr(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; fn compiler_peer_create_has_value(peer: CompilerPeer, value: ValueIrPtr) -> BooleanIrPtr; fn compiler_peer_create_is_loosely_equal( peer: CompilerPeer, @@ -1642,6 +1656,11 @@ extern "C" { offset: u32, ) -> ValueIrPtr; + // object + + fn compiler_peer_create_get(peer: CompilerPeer, symbol: u32) -> ValueIrPtr; + fn compiler_peer_create_set(peer: CompilerPeer, symbol: u32, value: ValueIrPtr); + // scope cleanup checker fn compiler_peer_enable_scope_cleanup_checker(peer: CompilerPeer, is_coroutine: bool); diff --git a/libs/jsruntime/src/llvmir/compiler/impl.hh b/libs/jsruntime/src/llvmir/compiler/impl.hh index 4249fbc3..61821105 100644 --- a/libs/jsruntime/src/llvmir/compiler/impl.hh +++ b/libs/jsruntime/src/llvmir/compiler/impl.hh @@ -498,6 +498,10 @@ class Compiler { // value + llvm::Value* CreateIsNullptr(llvm::Value* value) { + return builder_->CreateICmpEQ(value, GetNullptr(), REG_NAME("value.is_nullptr")); + } + llvm::Value* CreateHasValue(llvm::Value* value) { auto* kind = CreateLoadValueKindFromValue(value); return builder_->CreateICmpNE(kind, builder_->getInt8(kValueKindNone), @@ -907,6 +911,19 @@ class Compiler { REG_NAME("scratch.value.ptr")); } + // object + + llvm::Value* CreateGet(uint32_t symbol) { + auto* func = types_->CreateRuntimeGet(); + return builder_->CreateCall(func, {runtime_, builder_->getInt32(symbol)}, + REG_NAME("get." + llvm::Twine(symbol) + ".ptr")); + } + + void CreateSet(uint32_t symbol, llvm::Value* value) { + auto* func = types_->CreateRuntimeSet(); + builder_->CreateCall(func, {runtime_, builder_->getInt32(symbol), value}); + } + // scope cleanup checker void EnableScopeCleanupChecker(bool is_coroutine) { diff --git a/libs/jsruntime/src/llvmir/compiler/mod.rs b/libs/jsruntime/src/llvmir/compiler/mod.rs index ca65d2fc..e4d58775 100644 --- a/libs/jsruntime/src/llvmir/compiler/mod.rs +++ b/libs/jsruntime/src/llvmir/compiler/mod.rs @@ -11,9 +11,10 @@ use indexmap::IndexMap; use jsparser::syntax::LoopFlags; use jsparser::Symbol; -use crate::function::FunctionId; -use crate::function::FunctionRegistry; +use crate::lambda::LambdaId; +use crate::lambda::LambdaRegistry; use crate::logger; +use crate::semantics::BindingRef; use crate::semantics::CompileCommand; use crate::semantics::Locator; use crate::semantics::ScopeRef; @@ -46,7 +47,7 @@ impl Runtime { logger::debug!(event = "compile"); // TODO: Deferring the compilation until it's actually called improves the performance. // Because the program may contain unused functions. - let mut compiler = Compiler::new(&mut self.function_registry, &program.scope_tree); + let mut compiler = Compiler::new(&mut self.lambda_registry, &program.scope_tree); if self.pref.enable_scope_cleanup_checker { compiler.enable_scope_cleanup_checker(); } @@ -75,7 +76,7 @@ struct Compiler<'r, 's> { bridge: CompilerBridge, /// The function registry of the JavaScript program to compile. - function_registry: &'r mut FunctionRegistry, + lambda_registry: &'r mut LambdaRegistry, /// The scope tree of the JavaScript program to compile. scope_tree: &'s ScopeTree, @@ -154,11 +155,11 @@ macro_rules! dump_enabled { } impl<'r, 's> Compiler<'r, 's> { - pub fn new(function_registry: &'r mut FunctionRegistry, scope_tree: &'s ScopeTree) -> Self { + pub fn new(lambda_registry: &'r mut LambdaRegistry, scope_tree: &'s ScopeTree) -> Self { const DUMP_BUFFER_SIZE: usize = 512; Self { bridge: Default::default(), - function_registry, + lambda_registry, scope_tree, operand_stack: Default::default(), control_flow_stack: Default::default(), @@ -203,10 +204,10 @@ impl<'r, 's> Compiler<'r, 's> { self.bridge.set_target_triple(triple); } - fn start_function(&mut self, symbol: Symbol, func_id: FunctionId) { - logger::debug!(event = "start_function", ?symbol, ?func_id); + fn start_function(&mut self, symbol: Symbol, lambda_id: LambdaId) { + logger::debug!(event = "start_function", ?symbol, ?lambda_id); - self.bridge.start_function(func_id); + self.bridge.start_function(lambda_id); let locals_block = self.create_basic_block("locals"); let init_block = self.create_basic_block("init"); @@ -233,18 +234,20 @@ impl<'r, 's> Compiler<'r, 's> { self.bridge.create_alloc_status(); self.bridge.create_alloc_flow_selector(); if self.enable_scope_cleanup_checker { - self.bridge - .enable_scope_cleanup_checker(func_id.is_coroutine()); + let is_coroutine = self.lambda_registry.get(lambda_id).is_coroutine; + self.bridge.enable_scope_cleanup_checker(is_coroutine); } self.bridge.set_basic_block(body_block); } - fn end_function(&mut self, func_id: FunctionId, optimize: bool) { - logger::debug!(event = "end_function", ?func_id, optimize); + fn end_function(&mut self, lambda_id: LambdaId, optimize: bool) { + logger::debug!(event = "end_function", ?lambda_id, optimize); - let dormant_block = func_id - .is_coroutine() + let dormant_block = self + .lambda_registry + .get(lambda_id) + .is_coroutine .then(|| self.control_flow_stack.pop_coroutine_flow().dormant_block); self.control_flow_stack.pop_exit_target(); @@ -284,10 +287,9 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert!(self.control_flow_stack.is_empty()); self.control_flow_stack.clear(); - if func_id.is_coroutine() { - self.function_registry - .get_native_mut(func_id) - .scratch_buffer_len = self.max_scratch_buffer_len; + let info = self.lambda_registry.get_mut(lambda_id); + if info.is_coroutine { + info.scratch_buffer_len = self.max_scratch_buffer_len; } self.max_scratch_buffer_len = 0; } @@ -301,15 +303,15 @@ impl<'r, 's> Compiler<'r, 's> { CompileCommand::Boolean(value) => self.process_boolean(*value), CompileCommand::Number(value) => self.process_number(*value), CompileCommand::String(value) => self.process_string(value), - CompileCommand::Function(func_id) => self.process_function(*func_id), - CompileCommand::Closure(prologue, num_captures) => { - self.process_closure(*prologue, *num_captures) + CompileCommand::Function(lambda_id) => self.process_function(*lambda_id), + CompileCommand::Closure(prologue, func_scope_ref) => { + self.process_closure(*prologue, *func_scope_ref) } - CompileCommand::Coroutine(func_id, num_locals) => { - self.process_coroutine(*func_id, *num_locals) + CompileCommand::Coroutine(lambda_id, num_locals) => { + self.process_coroutine(*lambda_id, *num_locals) } CompileCommand::Promise => self.process_promise(), - CompileCommand::Reference(symbol, locator) => self.process_reference(*symbol, *locator), + CompileCommand::Reference(symbol) => self.process_reference(*symbol), CompileCommand::Exception => self.process_exception(), CompileCommand::AllocateLocals(num_locals) => self.process_allocate_locals(*num_locals), CompileCommand::MutableBinding => self.process_mutable_binding(), @@ -319,9 +321,6 @@ impl<'r, 's> Compiler<'r, 's> { CompileCommand::Call(nargs) => self.process_call(*nargs), CompileCommand::PushScope(scope_ref) => self.process_push_scope(*scope_ref), CompileCommand::PopScope(scope_ref) => self.process_pop_scope(*scope_ref), - CompileCommand::CaptureVariable(declaration) => { - self.process_capture_value(*declaration) - } CompileCommand::PostfixIncrement => self.process_postfix_increment(), CompileCommand::PostfixDecrement => self.process_postfix_decrement(), CompileCommand::PrefixIncrement => self.process_prefix_increment(), @@ -401,6 +400,7 @@ impl<'r, 's> Compiler<'r, 's> { CompileCommand::Discard => self.process_discard(), CompileCommand::Swap => self.process_swap(), CompileCommand::Duplicate(offset) => self.process_duplicate(*offset), + CompileCommand::Dereference => self.process_dereference(), CompileCommand::Debugger => self.process_debugger(), CompileCommand::PlaceHolder => unreachable!(), } @@ -439,8 +439,8 @@ impl<'r, 's> Compiler<'r, 's> { unimplemented!("string literal"); } - fn process_function(&mut self, func_id: FunctionId) { - let lambda = self.bridge.get_function(func_id); + fn process_function(&mut self, lambda_id: LambdaId) { + let lambda = self.bridge.get_function(lambda_id); self.operand_stack.push(Operand::Function(lambda)); } @@ -451,29 +451,36 @@ impl<'r, 's> Compiler<'r, 's> { } } - fn pop_capture(&mut self) -> CaptureIr { - match self.operand_stack.pop() { - Some(Operand::Capture(capture)) => capture, - _ => unreachable!(), - } - } - - fn process_closure(&mut self, prologue: bool, num_captures: u16) { - debug_assert!(self.operand_stack.len() > num_captures as usize); - + fn process_closure(&mut self, prologue: bool, func_scope_ref: ScopeRef) { let backup = self.bridge.get_basic_block(); if prologue { let block = self.control_flow_stack.scope_flow().hoisted_block; self.bridge.set_basic_block(block); } - let lambda = self.pop_lambda(); - let closure = self.bridge.create_closure(lambda, num_captures); + let scope = self.scope_tree.scope(func_scope_ref); + debug_assert!(scope.is_function()); - for i in 0..num_captures { - let capture = self.pop_capture(); + let lambda = self.pop_lambda(); + let closure = self.bridge.create_closure(lambda, scope.num_captures); + + let scope_ref = self.control_flow_stack.scope_flow().scope_ref; + for binding in scope.bindings.iter().filter(|binding| binding.is_capture()) { + // TODO(perf): improve if `find_binding()` is the primary case of performance + // bottleneck. + let binding_ref = self.scope_tree.find_binding(scope_ref, binding.symbol); + debug_assert_ne!(binding_ref, BindingRef::NONE); + let locator = self.scope_tree.compute_locator(binding_ref); + let capture = match locator { + Locator::Argument(_) | Locator::Local(_) => { + debug_assert!(self.captures.contains_key(&locator)); + *self.captures.get(&locator).unwrap() + } + Locator::Capture(i) => self.bridge.create_load_capture(i), + _ => unreachable!(), + }; self.bridge - .create_store_capture_to_closure(capture, closure, i); + .create_store_capture_to_closure(capture, closure, binding.index); } self.operand_stack.push(Operand::Closure(closure)); @@ -490,11 +497,8 @@ impl<'r, 's> Compiler<'r, 's> { } } - fn process_coroutine(&mut self, func_id: FunctionId, num_locals: u16) { - let scrach_buffer_len = self - .function_registry - .get_native(func_id) - .scratch_buffer_len; + fn process_coroutine(&mut self, lambda_id: LambdaId, num_locals: u16) { + let scrach_buffer_len = self.lambda_registry.get(lambda_id).scratch_buffer_len; debug_assert!(scrach_buffer_len <= u16::MAX as u32); let closure = self.pop_closure(); let coroutine = self @@ -509,8 +513,12 @@ impl<'r, 's> Compiler<'r, 's> { self.operand_stack.push(Operand::Promise(promise)); } - fn process_reference(&mut self, symbol: Symbol, locator: Locator) { - debug_assert!(!matches!(locator, Locator::None)); + fn process_reference(&mut self, symbol: Symbol) { + let scope_ref = self.control_flow_stack.scope_flow().scope_ref; + // TODO(perf): improve if `find_binding()` is the primary case of performance bottleneck. + let binding_ref = self.scope_tree.find_binding(scope_ref, symbol); + debug_assert_ne!(binding_ref, BindingRef::NONE); + let locator = self.scope_tree.compute_locator(binding_ref); self.operand_stack.push(Operand::Reference(symbol, locator)); } @@ -536,7 +544,7 @@ impl<'r, 's> Compiler<'r, 's> { _ => unreachable!(), }; - self.create_store_operand_to_value(operand, value); + self.create_store_operand_to_value(&operand, value); } fn dereference(&mut self) -> (Operand, Option<(Symbol, Locator)>) { @@ -545,19 +553,44 @@ impl<'r, 's> Compiler<'r, 's> { let operand = self.operand_stack.pop().unwrap(); match operand { Operand::Reference(symbol, locator) => { - let value = self.create_get_value_ptr(locator); + let value = self.create_get_value_ptr(symbol, locator); (Operand::Any(value), Some((symbol, locator))) } _ => (operand, None), } } - fn create_get_value_ptr(&mut self, locator: Locator) -> ValueIr { + fn create_get_value_ptr(&mut self, symbol: Symbol, locator: Locator) -> ValueIr { match locator { + Locator::None => unreachable!(), Locator::Argument(index) => self.bridge.create_get_argument_value_ptr(index), Locator::Local(index) => self.locals[index as usize], Locator::Capture(index) => self.bridge.create_get_capture_value_ptr(index), - _ => unreachable!(), + Locator::Global => { + // TODO(perf): return the value directly if it's a read-only global property. + + let then_block = self.create_basic_block("is_nullptr.then"); + let else_block = self.create_basic_block("is_nullptr.else"); + let end_block = self.create_basic_block("value_ptr"); + + let value = self.bridge.create_get(symbol); + // if value.is_nullptr() + let is_nullptr = self.bridge.create_is_nullptr(value); + self.bridge + .create_cond_br(is_nullptr, then_block, else_block); + // then + self.bridge.set_basic_block(then_block); + // TODO(feat): ReferenceError + self.process_number(1000.); + self.process_throw(); + self.bridge.create_br(end_block); + // else + self.bridge.set_basic_block(else_block); + self.bridge.create_br(end_block); + + self.bridge.set_basic_block(end_block); + value + } } } @@ -568,15 +601,15 @@ impl<'r, 's> Compiler<'r, 's> { } } - fn create_store_operand_to_value(&mut self, operand: Operand, dest: ValueIr) { + fn create_store_operand_to_value(&mut self, operand: &Operand, dest: ValueIr) { match operand { Operand::Undefined => self.bridge.create_store_undefined_to_value(dest), Operand::Null => self.bridge.create_store_null_to_value(dest), - Operand::Boolean(value) => self.bridge.create_store_boolean_to_value(value, dest), - Operand::Number(value) => self.bridge.create_store_number_to_value(value, dest), - Operand::Closure(value) => self.bridge.create_store_closure_to_value(value, dest), - Operand::Promise(value) => self.bridge.create_store_promise_to_value(value, dest), - Operand::Any(value) => self.bridge.create_store_value_to_value(value, dest), + Operand::Boolean(value) => self.bridge.create_store_boolean_to_value(*value, dest), + Operand::Number(value) => self.bridge.create_store_number_to_value(*value, dest), + Operand::Closure(value) => self.bridge.create_store_closure_to_value(*value, dest), + Operand::Promise(value) => self.bridge.create_store_promise_to_value(*value, dest), + Operand::Any(value) => self.bridge.create_store_value_to_value(*value, dest), _ => unreachable!(), } } @@ -590,7 +623,7 @@ impl<'r, 's> Compiler<'r, 's> { _ => unreachable!(), }; - self.create_store_operand_to_value(operand, value); + self.create_store_operand_to_value(&operand, value); } fn process_declare_function(&mut self) { @@ -608,7 +641,7 @@ impl<'r, 's> Compiler<'r, 's> { _ => unreachable!(), }; - self.create_store_operand_to_value(operand, value); + self.create_store_operand_to_value(&operand, value); self.bridge.set_basic_block(backup); } @@ -628,7 +661,7 @@ impl<'r, 's> Compiler<'r, 's> { _ => unreachable!(), }; - self.create_store_operand_to_value(operand, value); + self.create_store_operand_to_value(&operand, value); self.bridge.set_basic_block(backup); } @@ -653,7 +686,7 @@ impl<'r, 's> Compiler<'r, 's> { for i in (0..argc).rev() { let (operand, _) = self.dereference(); let ptr = self.bridge.create_get_arg_in_argv(argv, i); - self.create_store_operand_to_value(operand, ptr); + self.create_store_operand_to_value(&operand, ptr); } argv } else { @@ -701,8 +734,8 @@ impl<'r, 's> Compiler<'r, 's> { // else let (else_value, else_block) = { self.bridge.set_basic_block(else_block); - // TODO: TypeError - self.process_number(1.); + // TODO(feat): TypeError + self.process_number(1001.); self.process_throw(); self.bridge.create_br(end_block); ( @@ -866,34 +899,10 @@ impl<'r, 's> Compiler<'r, 's> { debug_assert!(!locator.is_capture()); debug_assert!(self.captures.contains_key(&locator)); let capture = self.captures.swap_remove(&locator).unwrap(); - let value = self.create_get_value_ptr(locator); + let value = self.create_get_value_ptr(Symbol::NONE, locator); self.bridge.create_escape_value(capture, value); } - fn process_capture_value(&mut self, declaration: bool) { - let backup = self.bridge.get_basic_block(); - if declaration { - let block = self.control_flow_stack.scope_flow().hoisted_block; - self.bridge.set_basic_block(block); - } - - let (_, locator) = self.pop_reference(); - let capture = match locator { - Locator::Argument(_) | Locator::Local(_) => { - debug_assert!(self.captures.contains_key(&locator)); - *self.captures.get(&locator).unwrap() - } - Locator::Capture(i) => self.bridge.create_load_capture(i), - _ => unreachable!(), - }; - - self.operand_stack.push(Operand::Capture(capture)); - - if declaration { - self.bridge.set_basic_block(backup); - } - } - // 13.4.2.1 Runtime Semantics: Evaluation // 13.4.3.1 Runtime Semantics: Evaluation // 13.4.4.1 Runtime Semantics: Evaluation @@ -917,7 +926,7 @@ impl<'r, 's> Compiler<'r, 's> { self.process_discard(); } _ => { - // TODO: throw a ReferenceError at runtime + // TODO(feat): throw a ReferenceError at runtime } } self.operand_stack.push(Operand::Number(if pos == '^' { @@ -1207,12 +1216,12 @@ impl<'r, 's> Compiler<'r, 's> { logger::debug!(event = "create_is_loosely_equal", ?lhs, ?rhs); if let Operand::Any(lhs) = lhs { // TODO: compile-time evaluation - let rhs = self.create_to_any(rhs); + let rhs = self.create_to_any(&rhs); return self.bridge.create_is_loosely_equal(lhs, rhs); } if let Operand::Any(rhs) = rhs { // TODO: compile-time evaluation - let lhs = self.create_to_any(lhs); + let lhs = self.create_to_any(&lhs); return self.bridge.create_is_loosely_equal(lhs, rhs); } @@ -1239,20 +1248,20 @@ impl<'r, 's> Compiler<'r, 's> { // TODO: 9. If x is a Boolean, return ! IsLooselyEqual(! ToNumber(x), y). // TODO: 10. If y is a Boolean, return ! IsLooselyEqual(x, ! ToNumber(y)). // TODO: ... - let lhs = self.create_to_any(lhs); - let rhs = self.create_to_any(rhs); + let lhs = self.create_to_any(&lhs); + let rhs = self.create_to_any(&rhs); self.bridge.create_is_loosely_equal(lhs, rhs) } - fn create_to_any(&mut self, operand: Operand) -> ValueIr { + fn create_to_any(&mut self, operand: &Operand) -> ValueIr { logger::debug!(event = "create_to_any", ?operand); match operand { - Operand::Any(value) => value, + Operand::Any(value) => *value, Operand::Undefined => self.bridge.create_undefined_to_any(), Operand::Null => self.bridge.create_null_to_any(), - Operand::Boolean(value) => self.bridge.create_boolean_to_any(value), - Operand::Number(value) => self.bridge.create_number_to_any(value), - Operand::Closure(value) => self.bridge.create_closure_to_any(value), + Operand::Boolean(value) => self.bridge.create_boolean_to_any(*value), + Operand::Number(value) => self.bridge.create_number_to_any(*value), + Operand::Closure(value) => self.bridge.create_closure_to_any(*value), _ => unreachable!(), } } @@ -1529,11 +1538,11 @@ impl<'r, 's> Compiler<'r, 's> { // We have to convert the value before the branch in each block. self.bridge.set_basic_block(then_block); - let then_value = self.create_to_any(then_operand); + let then_value = self.create_to_any(&then_operand); self.bridge.create_br(block); self.bridge.set_basic_block(else_block); - let else_value = self.create_to_any(else_operand); + let else_value = self.create_to_any(&else_operand); self.bridge.create_br(block); self.bridge.set_basic_block(block); @@ -1553,13 +1562,21 @@ impl<'r, 's> Compiler<'r, 's> { // 13.15.2 Runtime Semantics: Evaluation fn process_assignment(&mut self) { let (rhs, _) = self.dereference(); - let (_, locator) = self.pop_reference(); - - let value = self.create_get_value_ptr(locator); - // TODO: check the mutable flag - // auto* flags_ptr = CreateGetFlagsPtr(value_ptr); + let (symbol, locator) = self.pop_reference(); - self.create_store_operand_to_value(rhs.clone(), value); + match locator { + Locator::Global => { + let value = self.create_to_any(&rhs); + // TODO(feat): ReferenceError, TypeError + self.bridge.create_set(symbol, value); + } + _ => { + let value = self.create_get_value_ptr(symbol, locator); + // TODO: throw a TypeError in the strict mode. + // auto* flags_ptr = CreateGetFlagsPtr(value_ptr); + self.create_store_operand_to_value(&rhs, value); + } + } self.operand_stack.push(rhs); } @@ -2117,7 +2134,7 @@ impl<'r, 's> Compiler<'r, 's> { if n > 0 { debug_assert_eq!(n, 1); let (operand, _) = self.dereference(); - self.create_store_operand_to_retv(operand); + self.create_store_operand_to_retv(&operand); } self.bridge.create_store_normal_status(); @@ -2130,22 +2147,22 @@ impl<'r, 's> Compiler<'r, 's> { self.create_basic_block_for_deadcode(); } - fn create_store_operand_to_retv(&mut self, operand: Operand) { + fn create_store_operand_to_retv(&mut self, operand: &Operand) { match operand { Operand::Undefined => self.bridge.create_store_undefined_to_retv(), Operand::Null => self.bridge.create_store_null_to_retv(), - Operand::Boolean(value) => self.bridge.create_store_boolean_to_retv(value), - Operand::Number(value) => self.bridge.create_store_number_to_retv(value), - Operand::Closure(value) => self.bridge.create_store_closure_to_retv(value), - Operand::Promise(value) => self.bridge.create_store_promise_to_retv(value), - Operand::Any(value) => self.bridge.create_store_value_to_retv(value), + Operand::Boolean(value) => self.bridge.create_store_boolean_to_retv(*value), + Operand::Number(value) => self.bridge.create_store_number_to_retv(*value), + Operand::Closure(value) => self.bridge.create_store_closure_to_retv(*value), + Operand::Promise(value) => self.bridge.create_store_promise_to_retv(*value), + Operand::Any(value) => self.bridge.create_store_value_to_retv(*value), _ => unreachable!(), } } fn process_throw(&mut self) { let (operand, _) = self.dereference(); - self.create_store_operand_to_retv(operand); + self.create_store_operand_to_retv(&operand); self.bridge.create_store_exception_status(); self.bridge.create_set_flow_selector_throw(); @@ -2400,6 +2417,11 @@ impl<'r, 's> Compiler<'r, 's> { self.duplicate(offset); } + fn process_dereference(&mut self) { + let (operand, _) = self.dereference(); + self.operand_stack.push(operand); + } + fn process_debugger(&mut self) { self.bridge.create_debugger(); } @@ -2484,7 +2506,6 @@ enum Operand { Promise(PromiseIr), Any(ValueIr), Reference(Symbol, Locator), - Capture(CaptureIr), } impl Dump for Operand { @@ -2506,7 +2527,6 @@ impl Dump for Operand { Self::Promise(value) => eprintln!("Promise({:?})", ir2cstr!(value)), Self::Any(value) => eprintln!("Any({:?})", ir2cstr!(value)), Self::Reference(symbol, locator) => eprintln!("Reference({symbol}, {locator:?})"), - Self::Capture(value) => eprintln!("Capture({:?})", ir2cstr!(value)), } } } diff --git a/libs/jsruntime/src/llvmir/compiler/peer.cc b/libs/jsruntime/src/llvmir/compiler/peer.cc index 449955d8..8cd3190c 100644 --- a/libs/jsruntime/src/llvmir/compiler/peer.cc +++ b/libs/jsruntime/src/llvmir/compiler/peer.cc @@ -361,6 +361,10 @@ void compiler_peer_create_emit_promise_resolved(CompilerPeer peer, // value +BooleanIrPtr compiler_peer_create_is_nullptr(CompilerPeer peer, ValueIrPtr value) { + return PEER_BOOLEAN(IMPL(peer)->CreateIsNullptr(LLVM_VALUE(value))); +} + BooleanIrPtr compiler_peer_create_has_value(CompilerPeer peer, ValueIrPtr value) { return PEER_BOOLEAN(IMPL(peer)->CreateHasValue(LLVM_VALUE(value))); } @@ -719,6 +723,17 @@ ValueIrPtr compiler_peer_create_read_value_from_scratch_buffer(CompilerPeer peer return PEER_VALUE(IMPL(peer)->CreateReadValueFromScratchBuffer(offset)); } +// object + +ValueIrPtr compiler_peer_create_get(CompilerPeer peer, uint32_t symbol) { + return PEER_VALUE(IMPL(peer)->CreateGet(symbol)); +} + +void compiler_peer_create_set(CompilerPeer peer, uint32_t symbol, ValueIrPtr value) { + assert(value != nullptr); + IMPL(peer)->CreateSet(symbol, LLVM_VALUE(value)); +} + // scope cleanup checker void compiler_peer_enable_scope_cleanup_checker(CompilerPeer peer, bool is_coroutine) { diff --git a/libs/jsruntime/src/llvmir/executor/bridge.rs b/libs/jsruntime/src/llvmir/executor/bridge.rs index a9fe641d..e07081f7 100644 --- a/libs/jsruntime/src/llvmir/executor/bridge.rs +++ b/libs/jsruntime/src/llvmir/executor/bridge.rs @@ -6,7 +6,7 @@ use crate::llvmir::module::Module; use crate::llvmir::module::ModulePeer; use crate::llvmir::RuntimeFunctions; use crate::types::Lambda; -use crate::FunctionId; +use crate::LambdaId; pub struct ExecutorBridge(ExecutorPeer); @@ -19,12 +19,6 @@ impl ExecutorBridge { }) } - pub fn register_host_function(&self, func_id: FunctionId, lambda: Lambda) { - unsafe { - executor_peer_register_host_function(self.0, func_id.into(), lambda); - } - } - pub fn register_module(&self, module: Module) { unsafe { executor_peer_register_module(self.0, module.peer()); @@ -39,11 +33,11 @@ impl ExecutorBridge { unsafe { CStr::from_ptr(executor_peer_get_target_triple(self.0)) } } - pub fn get_native_function(&self, func_id: FunctionId) -> Option { + pub fn get_lambda(&self, lambda_id: LambdaId) -> Option { unsafe { - std::mem::transmute::>(executor_peer_get_native_function( + std::mem::transmute::>(executor_peer_get_lambda( self.0, - func_id.into(), + lambda_id.into(), )) } } @@ -64,9 +58,8 @@ extern "C" { fn executor_peer_new() -> ExecutorPeer; fn executor_peer_delete(peer: ExecutorPeer); fn executor_peer_register_runtime_functions(peer: ExecutorPeer, functions: &RuntimeFunctions); - fn executor_peer_register_host_function(peer: ExecutorPeer, func_id: u32, func: Lambda); fn executor_peer_register_module(peer: ExecutorPeer, module: ModulePeer); fn executor_peer_get_data_layout(peer: ExecutorPeer) -> *const c_char; fn executor_peer_get_target_triple(peer: ExecutorPeer) -> *const c_char; - fn executor_peer_get_native_function(peer: ExecutorPeer, func_id: u32) -> Lambda; + fn executor_peer_get_lambda(peer: ExecutorPeer, lambda_id: u32) -> Lambda; } diff --git a/libs/jsruntime/src/llvmir/executor/impl.hh b/libs/jsruntime/src/llvmir/executor/impl.hh index e7cc71bc..27536399 100644 --- a/libs/jsruntime/src/llvmir/executor/impl.hh +++ b/libs/jsruntime/src/llvmir/executor/impl.hh @@ -20,10 +20,10 @@ namespace { -// TODO(perf): Inefficient. Use a fixed size buffer for formatting func_id. -std::string FuncIdToName(uint32_t func_id) { +// TODO(perf): Inefficient. Use a fixed size buffer for formatting `id`. +std::string IdToName(uint32_t id) { std::stringstream ss; - ss << "fn" << func_id; + ss << "fn" << id; return ss.str(); } @@ -44,22 +44,12 @@ class Executor { void RegisterRuntimeFunctions(const RuntimeFunctions* functions); - void RegisterHostFunction(uint32_t func_id, Lambda lambda) { - llvm::orc::SymbolMap symbols; - auto name = FuncIdToName(func_id); - symbols[exec_session().intern(name)] = { - llvm::orc::ExecutorAddr::fromPtr(lambda), - llvm::JITSymbolFlags::Exported, - }; - ExitOnErr(main_jd().define(llvm::orc::absoluteSymbols(std::move(symbols)))); - } - void RegisterModule(Module* mod) { ExitOnErr(jit_->addIRModule(std::move(mod->mod))); } - Lambda GetNativeFunction(uint32_t func_id) { - auto name = FuncIdToName(func_id); + Lambda GetLambda(uint32_t id) { + auto name = IdToName(id); auto addr = ExitOnErr(jit_->lookup(name)); return addr.toPtr(); } diff --git a/libs/jsruntime/src/llvmir/executor/peer.cc b/libs/jsruntime/src/llvmir/executor/peer.cc index 748eb1eb..89823ca0 100644 --- a/libs/jsruntime/src/llvmir/executor/peer.cc +++ b/libs/jsruntime/src/llvmir/executor/peer.cc @@ -18,10 +18,6 @@ void executor_peer_register_runtime_functions(ExecutorPeer peer, IMPL(peer)->RegisterRuntimeFunctions(functions); } -void executor_peer_register_host_function(ExecutorPeer peer, uint32_t func_id, Lambda lambda) { - IMPL(peer)->RegisterHostFunction(func_id, lambda); -} - void executor_peer_register_module(ExecutorPeer peer, ModulePeer mod) { IMPL(peer)->RegisterModule(reinterpret_cast(mod)); } @@ -34,6 +30,6 @@ const char* executor_peer_get_target_triple(const ExecutorPeer peer) { return IMPL(peer)->target_triple().getTriple().c_str(); } -Lambda executor_peer_get_native_function(ExecutorPeer peer, uint32_t func_id) { - return IMPL(peer)->GetNativeFunction(func_id); +Lambda executor_peer_get_lambda(ExecutorPeer peer, uint32_t id) { + return IMPL(peer)->GetLambda(id); } diff --git a/libs/jsruntime/src/llvmir/runtime.yaml b/libs/jsruntime/src/llvmir/runtime.yaml index e4db108c..30269365 100644 --- a/libs/jsruntime/src/llvmir/runtime.yaml +++ b/libs/jsruntime/src/llvmir/runtime.yaml @@ -75,6 +75,19 @@ functions: type: u32 - name: result type: '&Value' + - name: get + args: + - name: symbol + type: u32 + # TODO(perf): add arguments for inline caching + ret: '&Value' + - name: set + args: + - name: symbol + type: u32 + - name: value + type: '&Value' + # TODO(perf): add arguments for inline caching - name: assert args: - name: assertion diff --git a/libs/jsruntime/src/objects/README.md b/libs/jsruntime/src/objects/README.md new file mode 100644 index 00000000..5a59a189 --- /dev/null +++ b/libs/jsruntime/src/objects/README.md @@ -0,0 +1,3 @@ +# objects + +The `objects` module implements the built-in JavaScript objects. diff --git a/libs/jsruntime/src/objects/mod.rs b/libs/jsruntime/src/objects/mod.rs new file mode 100644 index 00000000..f0243bb6 --- /dev/null +++ b/libs/jsruntime/src/objects/mod.rs @@ -0,0 +1,131 @@ +use bitflags::bitflags; +use rustc_hash::FxHashMap; + +use jsparser::Symbol; + +use crate::types::Value; + +// 6.1.7.1 Property Attributes + +pub enum Property { + /// A data property. + Data { + /// The `[[Value]]` attribute. + value: Value, + + /// Flags for boolean attributes. + flags: PropertyFlags, + }, + + /// A accessor property + #[allow(unused)] + Accessor { + /// Flags for boolean attributes. + flags: PropertyFlags, + }, +} + +bitflags! { + pub struct PropertyFlags: u8 { + /// The `[[Writable]]` attribute. + /// + /// Available only for the data property. + const WRITABLE = 1 << 0; + + /// The `[[Enumerable]]` attribute. + const ENUMERABLE = 1 << 1; + + /// The `[[Configurable]]` attribute. + const CONFIGURABLE = 1 << 2; + } +} + +// 10 Ordinary and Exotic Objects Behaviours + +// 10.1 Ordinary Object Internal Methods and Internal Slots + +#[derive(Default)] +pub struct Object { + properties: FxHashMap, +} + +impl Object { + // TODO(perf): Which one is better? `Option::None` or `&Value::None`. + // In JIT-compiled code, we need a `nullptr` check if we choose `Option::None`. + // If we choose `&Value::None`, we always need a memory access for the discriminant check of + // the value but no `nullptr` access happens. + pub fn get(&self, name: Symbol) -> Option<&Value> { + match self.properties.get(&name) { + Some(Property::Data { ref value, .. }) => Some(value), + Some(Property::Accessor { .. }) => todo!(), + None => None, + } + } + + // TODO(feat): strict, writable + pub fn set(&mut self, name: Symbol, value: &Value) { + self.properties + .entry(name) + .and_modify(|prop| match prop { + Property::Data { + ref mut value, + flags, + } => { + debug_assert!(flags.contains(PropertyFlags::WRITABLE)); + *value = value.clone(); + } + Property::Accessor { flags } => { + debug_assert!(flags.contains(PropertyFlags::WRITABLE)); + *prop = Property::Data { + value: value.clone(), + flags: PropertyFlags::empty(), + } + } + }) + .or_insert(Property::Data { + value: value.clone(), + flags: PropertyFlags::empty(), + }); + } + + pub fn define_own_property(&mut self, name: Symbol, prop: Property) { + self.properties.insert(name, prop); + } +} + +// 19 The Global Object +impl Object { + pub fn define_builtin_global_properties(&mut self) { + // TODO: 19.1.1 globalThis + + // 19.1.2 Infinity + self.define_own_property( + Symbol::INFINITY, + Property::Data { + value: Value::Number(f64::INFINITY), + // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false } + flags: PropertyFlags::empty(), + }, + ); + + // 19.1.3 NaN + self.define_own_property( + Symbol::NAN, + Property::Data { + value: Value::Number(f64::NAN), + // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false } + flags: PropertyFlags::empty(), + }, + ); + + // 19.1.4 undefined + self.define_own_property( + Symbol::UNDEFINED, + Property::Data { + value: Value::Undefined, + // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false } + flags: PropertyFlags::empty(), + }, + ); + } +} diff --git a/libs/jsruntime/src/semantics/mod.rs b/libs/jsruntime/src/semantics/mod.rs index e8eb45c6..73b73fce 100644 --- a/libs/jsruntime/src/semantics/mod.rs +++ b/libs/jsruntime/src/semantics/mod.rs @@ -1,7 +1,7 @@ mod scope; use bitflags::bitflags; -use indexmap::IndexMap; +use rustc_hash::FxHashSet; use jsparser::syntax::AssignmentOperator; use jsparser::syntax::BinaryOperator; @@ -16,11 +16,12 @@ use jsparser::Processor; use jsparser::Symbol; use jsparser::SymbolRegistry; -use super::logger; -use super::FunctionId; -use super::FunctionRegistry; -use super::Runtime; -use super::RuntimePref; +use crate::logger; +use crate::LambdaId; +use crate::LambdaRegistry; +use crate::Runtime; +use crate::RuntimePref; + use scope::ScopeTreeBuilder; pub use scope::BindingRef; @@ -31,12 +32,11 @@ impl Runtime { /// Parses a given source text as a script. pub fn parse_script(&mut self, source: &str) -> Result { logger::debug!(event = "parse", source_kind = "script"); - let mut analyzer = Analyzer::new_for_script( + let analyzer = Analyzer::new_for_script( &self.pref, &mut self.symbol_registry, - &mut self.function_registry, + &mut self.lambda_registry, ); - analyzer.use_global_bindings(); let processor = Processor::new(analyzer, false); Parser::for_script(source, processor).parse() } @@ -44,20 +44,36 @@ impl Runtime { /// Parses a given source text as a module. pub fn parse_module(&mut self, source: &str) -> Result { logger::debug!(event = "parse", source_kind = "module"); - let mut analyzer = Analyzer::new_for_module( + let analyzer = Analyzer::new_for_module( &self.pref, &mut self.symbol_registry, - &mut self.function_registry, + &mut self.lambda_registry, ); - analyzer.use_global_bindings(); let processor = Processor::new(analyzer, true); Parser::for_module(source, processor).parse() } } /// A type representing a JavaScript program after the semantic analysis. +/// +/// The program mainly consists of two kind of data. +/// +/// 1. Compile commands for each JavaScript function +/// 2. Analytics data +/// +/// Some of synthesized attributes (known as S-attributes) are computed during the semantic +/// analysis and values are embedded in each compile command. The others will be computed in a +/// state machine that interprets the compile commands. +/// +/// Inherited attributes are computed and stored into the analytics data. And the values will be +/// used in the state machine. +/// +/// In our processing model, a parser outputs stream of Nodes in the syntax tree in the buttom-up +/// order and it doesn't create the AST. So, it's impossible to compute a inherited attribute +/// value before a parent node comes from the parser. The computation has to be postponed. This +/// is why we need to introduce the analytics data. pub struct Program { - pub functions: Vec, + pub functions: Vec, pub scope_tree: ScopeTree, } @@ -75,21 +91,21 @@ impl Program { /// A type representing a JavaScript function after the semantic analysis. #[derive(Default)] -pub struct FunctionRecipe { +pub struct Function { // TODO: remove? pub symbol: Symbol, /// The function ID of the function. - pub id: FunctionId, + pub id: LambdaId, /// A list of [`CompileCommand`]s generated from the function definition. pub commands: Vec, - /// A list of free variables in the function. - pub captures: Vec, + /// The reference to the function scope. + pub scope_ref: ScopeRef, } -impl FunctionRecipe { +impl Function { pub fn print(&self, indent: &str) { println!("{indent}function: {:?}", self.id); if !self.commands.is_empty() { @@ -98,24 +114,9 @@ impl FunctionRecipe { println!("{indent} {command:?}"); } } - if !self.captures.is_empty() { - println!("{indent} captures:"); - for capture in self.captures.iter() { - println!("{indent} {capture:?}"); - } - } } } -/// Represents a free variable of a function. -#[derive(Debug)] -pub struct Capture { - /// The symbol of the captured variable defined outside the function. - pub symbol: Symbol, - - pub target: Locator, -} - /// A semantic analyzer. /// /// A semantic analyzer analyzes semantics of a JavaScript program. @@ -127,20 +128,18 @@ struct Analyzer<'r> { symbol_registry: &'r mut SymbolRegistry, /// A mutable reference to a function registry. - function_registry: &'r mut FunctionRegistry, + lambda_registry: &'r mut LambdaRegistry, /// A stack to keep the analysis data for outer JavaScript functions when analyzing nested /// JavaScript functions. context_stack: Vec, - /// A list of [`FunctionRecipe`]s. - functions: Vec, + /// A list of [`Function`]s. + functions: Vec, /// A scope tree builder used for building the scope tree of the JavaScript program. scope_tree_builder: ScopeTreeBuilder, - use_global_bindings: bool, - module: bool, } @@ -149,44 +148,39 @@ impl<'r> Analyzer<'r> { fn new_for_script( runtime_pref: &'r RuntimePref, symbol_registry: &'r mut SymbolRegistry, - function_registry: &'r mut FunctionRegistry, + lambda_registry: &'r mut LambdaRegistry, ) -> Self { - Self::new(runtime_pref, symbol_registry, function_registry, false) + Self::new(runtime_pref, symbol_registry, lambda_registry, false) } /// Creates a semantic analyzer. fn new_for_module( runtime_pref: &'r RuntimePref, symbol_registry: &'r mut SymbolRegistry, - function_registry: &'r mut FunctionRegistry, + lambda_registry: &'r mut LambdaRegistry, ) -> Self { - Self::new(runtime_pref, symbol_registry, function_registry, true) + Self::new(runtime_pref, symbol_registry, lambda_registry, true) } fn new( runtime_pref: &'r RuntimePref, symbol_registry: &'r mut SymbolRegistry, - function_registry: &'r mut FunctionRegistry, + lambda_registry: &'r mut LambdaRegistry, module: bool, ) -> Self { // TODO: modules including await expressions. - let _ = function_registry.create_native_function(false); + let _ = lambda_registry.register(false); Self { runtime_pref, symbol_registry, - function_registry, + lambda_registry, context_stack: vec![], functions: vec![], scope_tree_builder: Default::default(), - use_global_bindings: false, module, } } - fn use_global_bindings(&mut self) { - self.use_global_bindings = true; - } - fn set_in_body(&mut self) { self.context_stack .last_mut() @@ -287,8 +281,8 @@ impl<'r> Analyzer<'r> { Node::ArrowFunction => self.handle_arrow_function(), Node::AsyncArrowFunction => self.handle_async_arrow_function(), Node::AwaitExpression => self.handle_await_expression(), - Node::ThenBlock => self.handle_then_block(), - Node::ElseBlock => self.handle_else_block(), + Node::Then => self.handle_then(), + Node::Else => self.handle_else(), Node::FalsyShortCircuit => self.handle_falsy_short_circuit(), Node::TruthyShortCircuit => self.handle_truthy_short_circuit(), Node::NullishShortCircuit => self.handle_nullish_short_circuit(), @@ -307,6 +301,7 @@ impl<'r> Analyzer<'r> { Node::FunctionContext => self.handle_function_context(), Node::AsyncFunctionContext => self.handle_async_function_context(), Node::FunctionSignature(symbol) => self.handle_function_signature(symbol), + Node::Dereference => self.handle_dereference(), } } @@ -599,10 +594,10 @@ impl<'r> Analyzer<'r> { fn end_function_scope(&mut self) -> usize { let mut context = self.context_stack.pop().unwrap(); - context.end_scope(); + let func_scope_ref = context.end_scope(); self.scope_tree_builder.pop(); - self.resolve_references(&mut context); + let unresolved_reference = self.resolve_references(&mut context); if context.flags.contains(FunctionContextFlags::COROUTINE) { // The local variables allocated on the heap will be passed as arguments for the @@ -617,7 +612,40 @@ impl<'r> Analyzer<'r> { let func_index = context.func_index; let func = &mut self.functions[func_index]; func.commands = context.commands; - func.captures = context.captures.into_values().collect(); + func.scope_ref = context.scope_ref; + + let context = self.context_stack.last_mut().unwrap(); + let scope_ref = self.scope_tree_builder.current(); + let mut added = FxHashSet::default(); + for reference in unresolved_reference.iter() { + match reference.func_scope_ref { + Some(inner_func_scope_ref) => { + if !added.contains(&reference.symbol) { + context.references.push(Reference { + symbol: reference.symbol, + scope_ref, + func_scope_ref: Some(func_scope_ref), + }); + added.insert(reference.symbol); + } + context.references.push(Reference { + symbol: reference.symbol, + scope_ref, + func_scope_ref: Some(inner_func_scope_ref), + }); + } + None => { + if !added.contains(&reference.symbol) { + context.references.push(Reference { + symbol: reference.symbol, + scope_ref, + func_scope_ref: Some(func_scope_ref), + }); + added.insert(reference.symbol); + } + } + } + } func_index } @@ -629,11 +657,7 @@ impl<'r> Analyzer<'r> { self.context_stack .last_mut() .unwrap() - .process_closure_declaration( - self.scope_tree_builder.current(), - func.id, - &func.captures, - ); + .process_closure_declaration(func.scope_ref, func.id); } fn handle_async_function_declaration(&mut self) { @@ -650,12 +674,7 @@ impl<'r> Analyzer<'r> { self.context_stack .last_mut() .unwrap() - .process_closure_expression( - self.scope_tree_builder.current(), - func.id, - &func.captures, - named, - ); + .process_closure_expression(func.scope_ref, func.id, named); } fn handle_async_function_expression(&mut self, named: bool) { @@ -676,12 +695,7 @@ impl<'r> Analyzer<'r> { self.context_stack .last_mut() .unwrap() - .process_closure_expression( - self.scope_tree_builder.current(), - func.id, - &func.captures, - false, - ); + .process_closure_expression(func.scope_ref, func.id, false); } fn handle_async_arrow_function(&mut self) { @@ -697,13 +711,13 @@ impl<'r> Analyzer<'r> { self.context_stack.last_mut().unwrap().coroutine.state = next_state; } - fn handle_then_block(&mut self) { + fn handle_then(&mut self) { let context = self.context_stack.last_mut().unwrap(); context.put_command(CompileCommand::Truthy); context.put_command(CompileCommand::IfThen); } - fn handle_else_block(&mut self) { + fn handle_else(&mut self) { self.put_command(CompileCommand::Else); } @@ -827,12 +841,12 @@ impl<'r> Analyzer<'r> { } fn set_function_symbol(&mut self, symbol: Symbol) { - let id = self - .function_registry - .create_native_function(symbol == Symbol::HIDDEN_COROUTINE); + let lambda_id = self + .lambda_registry + .register(symbol == Symbol::HIDDEN_COROUTINE); let func_index = self.context_stack.last().unwrap().func_index; self.functions[func_index].symbol = symbol; - self.functions[func_index].id = id; + self.functions[func_index].id = lambda_id; } fn handle_function_signature(&mut self, symbol: Symbol) { @@ -874,126 +888,41 @@ impl<'r> Analyzer<'r> { fn end_coroutine_body(&mut self) { // TODO(perf): Some of the local variables can be placed on the stack. let context = self.context_stack.last().unwrap(); - let func_id = self.functions[context.func_index].id; + let lambda_id = self.functions[context.func_index].id; let num_locals = context.num_locals; self.handle_function_expression(false); - self.put_command(CompileCommand::Coroutine(func_id, num_locals)); + self.put_command(CompileCommand::Coroutine(lambda_id, num_locals)); self.put_command(CompileCommand::Promise); self.put_command(CompileCommand::Duplicate(0)); self.put_command(CompileCommand::Resume); self.put_command(CompileCommand::Return(1)); } - fn put_command(&mut self, command: CompileCommand) { - self.context_stack.last_mut().unwrap().put_command(command); + fn handle_dereference(&mut self) { + self.put_command(CompileCommand::Dereference); } - // TODO: global object - fn put_global_bindings(&mut self) { - let context = self.context_stack.last_mut().unwrap(); - - // Register `undefined`. - let symbol = Symbol::UNDEFINED; - context.put_reference(symbol, self.scope_tree_builder.current()); - context.put_lexical_binding(false); - context.process_immutable_bindings(1); - self.scope_tree_builder - .add_immutable(symbol, context.num_locals); - context.num_locals += 1; - - // Register `Infinity`. - let symbol = Symbol::INFINITY; - context.put_reference(symbol, self.scope_tree_builder.current()); - context.put_number(f64::INFINITY); - context.put_lexical_binding(true); - context.process_immutable_bindings(1); - self.scope_tree_builder - .add_immutable(symbol, context.num_locals); - context.num_locals += 1; - - // Register `NaN`. - let symbol = Symbol::NAN; - context.put_reference(symbol, self.scope_tree_builder.current()); - context.put_number(f64::NAN); - context.put_lexical_binding(true); - context.process_immutable_bindings(1); - self.scope_tree_builder - .add_immutable(symbol, context.num_locals); - context.num_locals += 1; - } - - // TODO: global object - fn register_host_functions(&mut self) { - let context = self.context_stack.last_mut().unwrap(); - - for (func_id, host_func) in self.function_registry.enumerate_host_function() { - context.put_reference(host_func.symbol, self.scope_tree_builder.current()); - context.process_closure_declaration(self.scope_tree_builder.current(), func_id, &[]); - self.scope_tree_builder - .add_immutable(host_func.symbol, context.num_locals); - context.num_locals += 1; - } - } - - fn resolve_references(&mut self, context: &mut FunctionContext) { - for reference in std::mem::take(&mut context.references).iter() { - self.resolve_reference(context, reference); - } + fn put_command(&mut self, command: CompileCommand) { + self.context_stack.last_mut().unwrap().put_command(command); } - // TODO: refactoring - fn resolve_reference(&mut self, context: &mut FunctionContext, reference: &Reference) { - let binding_ref = self.scope_tree_builder.resolve_reference(reference); - logger::debug!(event = "resolve_reference", ?reference, ?binding_ref); - - if binding_ref == BindingRef::NONE { - // This is a reference to a free variable. - let capture_index = match context.captures.get_full(&reference.symbol) { - Some((capture_index, ..)) => capture_index, - None => { - let (capture_index, _) = context.captures.insert_full( - reference.symbol, - Capture { - symbol: reference.symbol, - target: Locator::None, - }, - ); - self.context_stack - .last_mut() - .unwrap() - .references - .push(Reference { - symbol: reference.symbol, - scope_ref: self.scope_tree_builder.current(), - from: ReferenceFrom::Capture(context.func_index, capture_index), - }); - capture_index - } - }; - let locator = Locator::checked_capture(capture_index).unwrap(); - match reference.from { - ReferenceFrom::Command(command_index) => { - context.commands[command_index] = - CompileCommand::Reference(reference.symbol, locator); - } - ReferenceFrom::Capture(func_index, capture_index) => { - self.functions[func_index].captures[capture_index].target = locator; + fn resolve_references(&mut self, context: &mut FunctionContext) -> Vec { + let mut unresolved_reference = vec![]; + for reference in std::mem::take(&mut context.references).into_iter() { + let binding_ref = self.scope_tree_builder.resolve_reference(&reference); + if binding_ref != BindingRef::NONE { + logger::debug!(event = "reference_resolved", ?reference, ?binding_ref); + if let Some(func_scope_ref) = reference.func_scope_ref { + self.scope_tree_builder.set_captured(binding_ref); + self.scope_tree_builder + .add_capture(func_scope_ref, reference.symbol); } - } - return; - } - - let locator = self.scope_tree_builder.compute_locator(binding_ref); - match reference.from { - ReferenceFrom::Command(command_index) => { - context.commands[command_index] = - CompileCommand::Reference(reference.symbol, locator); - } - ReferenceFrom::Capture(func_index, capture_index) => { - self.functions[func_index].captures[capture_index].target = locator; - self.scope_tree_builder.set_captured(binding_ref); + } else { + logger::debug!(event = "reference_unresolved", ?reference); + unresolved_reference.push(reference); } } + unresolved_reference } } @@ -1006,12 +935,6 @@ impl<'s> NodeHandler<'s> for Analyzer<'_> { self.set_in_body(); - if self.use_global_bindings { - self.put_global_bindings(); - } - - self.register_host_functions(); - // The module is always treated as an async function body. if self.module { self.start_coroutine_body(); @@ -1026,16 +949,33 @@ impl<'s> NodeHandler<'s> for Analyzer<'_> { } let mut context = self.context_stack.pop().unwrap(); - context.end_scope(); + let global_scope_ref = context.end_scope(); self.scope_tree_builder.pop(); - self.resolve_references(&mut context); - debug_assert!(context.captures.is_empty()); + let unresolved_references = self.resolve_references(&mut context); context.commands[0] = CompileCommand::AllocateLocals(context.num_locals); self.functions[context.func_index].commands = context.commands; - self.functions[context.func_index].captures = context.captures.into_values().collect(); + self.functions[context.func_index].scope_ref = context.scope_ref; + + // References to global properties. + let mut added = FxHashSet::default(); + for reference in unresolved_references.iter() { + match reference.func_scope_ref { + Some(func_scope_ref) => { + self.scope_tree_builder + .add_global(func_scope_ref, reference.symbol); + } + None => { + if !added.contains(&reference.symbol) { + self.scope_tree_builder + .add_global(global_scope_ref, reference.symbol); + added.insert(reference.symbol); + } + } + } + } Ok(Program { functions: std::mem::take(&mut self.functions), @@ -1075,9 +1015,6 @@ struct FunctionContext { /// performed after all variable declarations are processed. references: Vec, - /// A list of captured variables outside the function scope. - captures: IndexMap, - /// A list of indexes of commands that have to be updated while analyzing. pending_lexical_bindings: Vec, @@ -1107,8 +1044,7 @@ struct FunctionContext { /// The index of the function in [`Analyzer::functions`]. func_index: usize, - /// A reference to the function scope in the scope tree. - #[allow(unused)] + /// The reference to the function scope in the scope tree. scope_ref: ScopeRef, num_locals: u16, @@ -1162,14 +1098,8 @@ impl FunctionContext { } fn put_reference(&mut self, symbol: Symbol, scope_ref: ScopeRef) { - // The placeholder command will be replaced with a `CompileCommand::Reference` in - // `resolve_reference()`. - let command_index = self.put_command(CompileCommand::PlaceHolder); - self.references.push(Reference { - symbol, - scope_ref, - from: ReferenceFrom::Command(command_index), - }); + self.put_command(CompileCommand::Reference(symbol)); + self.references.push(Reference::new(symbol, scope_ref)); } fn process_argument_list_head(&mut self, empty: bool, _spread: bool) { @@ -1262,42 +1192,25 @@ impl FunctionContext { self.pending_lexical_bindings.clear(); } - fn process_closure_declaration( - &mut self, - scope_ref: ScopeRef, - func_id: FunctionId, - captures: &[Capture], - ) { - for capture in captures.iter().rev() { - // `capture.target` has not been resolved at this point... - self.put_reference(capture.symbol, scope_ref); - self.commands.push(CompileCommand::CaptureVariable(true)); - } - self.commands.push(CompileCommand::Function(func_id)); - self.commands - .push(CompileCommand::Closure(true, captures.len() as u16)); + fn process_closure_declaration(&mut self, scope_ref: ScopeRef, lambda_id: LambdaId) { + self.commands.push(CompileCommand::Function(lambda_id)); + self.commands.push(CompileCommand::Closure(true, scope_ref)); self.commands.push(CompileCommand::DeclareClosure); } fn process_closure_expression( &mut self, scope_ref: ScopeRef, - func_id: FunctionId, - captures: &[Capture], + lambda_id: LambdaId, named: bool, ) { if named { // Remove the BindingIdentifier of the function. self.put_command(CompileCommand::Discard); } - for capture in captures.iter().rev() { - // `capture.target` has not been resolved at this point... - self.put_reference(capture.symbol, scope_ref); - self.commands.push(CompileCommand::CaptureVariable(false)); - } - self.commands.push(CompileCommand::Function(func_id)); + self.commands.push(CompileCommand::Function(lambda_id)); self.commands - .push(CompileCommand::Closure(false, captures.len() as u16)); + .push(CompileCommand::Closure(false, scope_ref)); } fn process_loop_start(&mut self, scope_ref: ScopeRef) { @@ -1503,7 +1416,7 @@ impl FunctionContext { self.scope_stack.push(Scope { scope_ref }); } - fn end_scope(&mut self) { + fn end_scope(&mut self) -> ScopeRef { let scope = self.scope_stack.pop().unwrap(); // NOTE(perf): The scope may has no binding. In this case, we can remove the @@ -1511,6 +1424,8 @@ impl FunctionContext { // post-process for optimization if it's needed. self.commands .push(CompileCommand::PopScope(scope.scope_ref)); + + scope.scope_ref } } @@ -1556,11 +1471,11 @@ pub enum CompileCommand { Boolean(bool), Number(f64), String(Vec), - Function(FunctionId), - Closure(bool, u16), - Coroutine(FunctionId, u16), + Function(LambdaId), + Closure(bool, ScopeRef), + Coroutine(LambdaId, u16), Promise, - Reference(Symbol, Locator), + Reference(Symbol), Exception, AllocateLocals(u16), @@ -1571,7 +1486,6 @@ pub enum CompileCommand { Call(u16), PushScope(ScopeRef), PopScope(ScopeRef), - CaptureVariable(bool), // update operators PostfixIncrement, @@ -1696,6 +1610,7 @@ pub enum CompileCommand { Discard, Swap, Duplicate(u8), // 0 or 1 + Dereference, // debugger Debugger, @@ -1789,11 +1704,10 @@ pub enum Locator { Argument(u16), Local(u16), Capture(u16), + Global, } impl Locator { - const MAX_INDEX: usize = u16::MAX as usize; - pub fn is_none(&self) -> bool { matches!(self, Self::None) } @@ -1809,20 +1723,6 @@ impl Locator { pub fn is_capture(&self) -> bool { matches!(self, Self::Capture(_)) } - - fn checked_capture(index: usize) -> Option { - Self::ensure_index(index)?; - Some(Self::Capture(index as u16)) - } - - fn ensure_index(index: usize) -> Option<()> { - if index > Self::MAX_INDEX { - crate::logger::error!(err = "too large", index); - None - } else { - Some(()) - } - } } /// A type representing information needed for resolving a reference to a symbol. @@ -1834,24 +1734,18 @@ struct Reference { /// The reference to a (function or block) scope where the symbol is referred. scope_ref: ScopeRef, - from: ReferenceFrom, + /// The reference to the function scope using the free variable. + func_scope_ref: Option, } -#[derive(Debug)] -enum ReferenceFrom { - /// A reference to a [`CompileCommand`] that needs to be updated. - /// - /// This is a tuple of two indexes. The first one is the index of a [`FunctionContext`] in - /// [`Analyzer::functions`]. The second one is the index of the [`CompileCommand`] in - /// the [`FunctionContext::commands`] identified by the first index. - Command(usize), - - /// A reference from a [`Capture`] that needs to be updated. - /// - /// This is a tuple of two indexes. The first one is the index of a [`FunctionContext`] in - /// [`Analyzer::functions`]. The second one is the index of the [`Capture`] in - /// the [`FunctionContext::captures`] identified by the first index. - Capture(usize, usize), +impl Reference { + fn new(symbol: Symbol, scope_ref: ScopeRef) -> Self { + Self { + symbol, + scope_ref, + func_scope_ref: None, + } + } } #[cfg(test)] @@ -1872,12 +1766,6 @@ mod tests { }; } - macro_rules! locator { - (local: $index:expr) => { - Locator::Local($index) - }; - } - macro_rules! script { ($src:literal) => { Source::Script($src) @@ -1901,16 +1789,16 @@ mod tests { CompileCommand::AllocateLocals(4), CompileCommand::Nop, CompileCommand::PushScope(scope_ref!(1)), - CompileCommand::Reference(symbol!(sreg, "a"), locator!(local: 0)), + CompileCommand::Reference(symbol!(sreg, "a")), CompileCommand::Undefined, CompileCommand::MutableBinding, - CompileCommand::Reference(symbol!(sreg, "b"), locator!(local: 1)), + CompileCommand::Reference(symbol!(sreg, "b")), CompileCommand::Number(2.0), CompileCommand::MutableBinding, - CompileCommand::Reference(symbol!(sreg, "c"), locator!(local: 2)), + CompileCommand::Reference(symbol!(sreg, "c")), CompileCommand::Number(3.0), CompileCommand::ImmutableBinding, - CompileCommand::Reference(symbol!(sreg, "d"), locator!(local: 3)), + CompileCommand::Reference(symbol!(sreg, "d")), CompileCommand::Number(4.0), CompileCommand::ImmutableBinding, CompileCommand::PopScope(scope_ref!(1)), @@ -1931,19 +1819,19 @@ mod tests { CompileCommand::AllocateLocals(4), CompileCommand::Nop, CompileCommand::PushScope(scope_ref!(1)), - CompileCommand::Reference(symbol!(sreg, "a"), locator!(local: 0)), + CompileCommand::Reference(symbol!(sreg, "a")), CompileCommand::Undefined, CompileCommand::MutableBinding, CompileCommand::PushScope(scope_ref!(2)), - CompileCommand::Reference(symbol!(sreg, "a"), locator!(local: 1)), + CompileCommand::Reference(symbol!(sreg, "a")), CompileCommand::Undefined, CompileCommand::MutableBinding, CompileCommand::PopScope(scope_ref!(2)), CompileCommand::PushScope(scope_ref!(3)), - CompileCommand::Reference(symbol!(sreg, "a"), locator!(local: 2)), + CompileCommand::Reference(symbol!(sreg, "a")), CompileCommand::Undefined, CompileCommand::MutableBinding, - CompileCommand::Reference(symbol!(sreg, "b"), locator!(local: 3)), + CompileCommand::Reference(symbol!(sreg, "b")), CompileCommand::Undefined, CompileCommand::MutableBinding, CompileCommand::PopScope(scope_ref!(3)), @@ -1983,10 +1871,10 @@ mod tests { CompileCommand::AllocateLocals(1), CompileCommand::Nop, CompileCommand::PushScope(scope_ref!(1)), - CompileCommand::Reference(symbol!(sreg, "a"), locator!(local: 0)), + CompileCommand::Reference(symbol!(sreg, "a")), CompileCommand::Number(1.0), CompileCommand::MutableBinding, - CompileCommand::Reference(symbol!(sreg, "a"), locator!(local: 0)), + CompileCommand::Reference(symbol!(sreg, "a")), CompileCommand::Number(2.0), CompileCommand::Duplicate(1), CompileCommand::Addition, @@ -2009,7 +1897,7 @@ mod tests { CompileCommand::Nop, CompileCommand::PushScope(scope_ref!(1)), CompileCommand::Function(program.functions[1].id), - CompileCommand::Closure(false, 0), + CompileCommand::Closure(false, scope_ref!(2)), CompileCommand::Coroutine(program.functions[1].id, 0), CompileCommand::Promise, CompileCommand::Duplicate(0), @@ -2033,13 +1921,13 @@ mod tests { }); } - fn test(src: Source, validate: fn(&Program, &SymbolRegistry, &FunctionRegistry)) { + fn test(src: Source, validate: fn(&Program, &SymbolRegistry, &LambdaRegistry)) { let runtime_pref = RuntimePref { enable_scope_cleanup_checker: true, ..Default::default() }; let mut symbol_registry = Default::default(); - let mut function_registry = FunctionRegistry::new(); + let mut lambda_registry = LambdaRegistry::new(); let mut parser = match src { Source::Script(src) => Parser::for_script( src, @@ -2047,7 +1935,7 @@ mod tests { Analyzer::new_for_script( &runtime_pref, &mut symbol_registry, - &mut function_registry, + &mut lambda_registry, ), false, ), @@ -2058,7 +1946,7 @@ mod tests { Analyzer::new_for_module( &runtime_pref, &mut symbol_registry, - &mut function_registry, + &mut lambda_registry, ), true, ), @@ -2067,7 +1955,7 @@ mod tests { let result = parser.parse(); assert!(result.is_ok()); if let Ok(program) = result { - validate(&program, &symbol_registry, &function_registry) + validate(&program, &symbol_registry, &lambda_registry) } } diff --git a/libs/jsruntime/src/semantics/scope.rs b/libs/jsruntime/src/semantics/scope.rs index 71579185..a44a60f5 100644 --- a/libs/jsruntime/src/semantics/scope.rs +++ b/libs/jsruntime/src/semantics/scope.rs @@ -89,6 +89,45 @@ impl ScopeTree { scope.bindings[binding_ref.binding_index()].symbol } + pub fn find_binding(&self, scope_ref: ScopeRef, symbol: Symbol) -> BindingRef { + let mut scope_ref = scope_ref; + loop { + let scope = &self.scopes[scope_ref.index()]; + debug_assert!(scope.is_sorted()); + match scope + .bindings + .binary_search_by_key(&symbol, |binding| binding.symbol) + { + Ok(index) => { + // TODO: should return an error + return BindingRef::checked_new(scope_ref, index).unwrap(); + } + Err(_) => { + if scope.is_function() { + // Reference to a free variable. + return BindingRef::NONE; + } + scope_ref = scope.outer; + if scope_ref == ScopeRef::NONE { + // Reference to a property of the global object. + return BindingRef::NONE; + } + } + } + } + } + + pub fn compute_locator(&self, binding_ref: BindingRef) -> Locator { + let scope = &self.scopes[binding_ref.scope_index()]; + let binding = &scope.bindings[binding_ref.binding_index()]; + match binding.kind { + BindingKind::FormalParameter => Locator::Argument(binding.index), + BindingKind::Mutable | BindingKind::Immutable => Locator::Local(binding.index), + BindingKind::Capture => Locator::Capture(binding.index), + BindingKind::Global => Locator::Global, + } + } + #[allow(unused)] pub fn print(&self, indent: &str) { for (index, scope) in self.scopes.iter().enumerate().skip(1) { @@ -130,6 +169,7 @@ impl ScopeTreeBuilder { bindings: vec![], num_formal_parameters: 0, num_locals: 0, + num_captures: 0, outer: self.current, depth: self.depth, max_child_block_depth: self.depth, @@ -182,18 +222,6 @@ impl ScopeTreeBuilder { scope.num_locals += 1; } - pub fn add_immutable(&mut self, symbol: Symbol, index: u16) { - let scope = &mut self.scopes[self.current.index()]; - debug_assert!(!scope.is_sorted()); - scope.bindings.push(Binding { - symbol, - index, - kind: BindingKind::Immutable, - flags: BindingFlags::empty(), - }); - scope.num_locals += 1; - } - #[allow(unused)] pub fn add_hidden(&mut self, symbol: Symbol, index: u16) { let scope = &mut self.scopes[self.current.index()]; @@ -216,6 +244,35 @@ impl ScopeTreeBuilder { } } + pub fn add_capture(&mut self, scope_ref: ScopeRef, symbol: Symbol) { + let scope = &mut self.scopes[scope_ref.index()]; + debug_assert!(scope.is_function()); + scope.bindings.push(Binding { + symbol, + index: scope.num_captures, + kind: BindingKind::Capture, + flags: BindingFlags::empty(), + }); + scope.num_captures += 1; + scope + .bindings + .sort_unstable_by_key(|binding| binding.symbol); // TODO(perf) + } + + pub fn add_global(&mut self, scope_ref: ScopeRef, symbol: Symbol) { + let scope = &mut self.scopes[scope_ref.index()]; + debug_assert!(scope.is_function()); + scope.bindings.push(Binding { + symbol, + index: 0, // TODO + kind: BindingKind::Global, + flags: BindingFlags::empty(), + }); + scope + .bindings + .sort_unstable_by_key(|binding| binding.symbol); // TODO(perf) + } + pub fn set_captured(&mut self, binding_ref: BindingRef) { let scope = &mut self.scopes[binding_ref.scope_index()]; debug_assert!(scope.is_sorted()); @@ -244,11 +301,13 @@ impl ScopeTreeBuilder { return BindingRef::checked_new(scope_ref, index).unwrap(); } Err(_) => { + if scope.is_function() { + // Reference to a free variable. + return BindingRef::NONE; + } scope_ref = scope.outer; if scope_ref == ScopeRef::NONE { - panic!("{reference:?}"); - } - if scope.is_function() { + // Reference to a property of the global object. return BindingRef::NONE; } } @@ -256,15 +315,6 @@ impl ScopeTreeBuilder { } } - pub fn compute_locator(&self, binding_ref: BindingRef) -> Locator { - let scope = &self.scopes[binding_ref.scope_index()]; - let binding = &scope.bindings[binding_ref.binding_index()]; - match binding.kind { - BindingKind::FormalParameter => Locator::Argument(binding.index), - _ => Locator::Local(binding.index), - } - } - pub fn build(&mut self) -> ScopeTree { ScopeTree { scopes: std::mem::take(&mut self.scopes), @@ -288,6 +338,7 @@ pub struct Scope { pub bindings: Vec, pub num_formal_parameters: u16, pub num_locals: u16, + pub num_captures: u16, outer: ScopeRef, depth: u16, max_child_block_depth: u16, @@ -300,6 +351,7 @@ impl Scope { bindings: vec![], num_formal_parameters: 0, num_locals: 0, + num_captures: 0, outer: ScopeRef::NONE, depth: 0, max_child_block_depth: 0, @@ -366,13 +418,19 @@ pub struct Binding { impl Binding { pub fn is_local(&self) -> bool { - !matches!(self.kind, BindingKind::FormalParameter) + matches!(self.kind, BindingKind::Immutable | BindingKind::Mutable) + } + + pub fn is_capture(&self) -> bool { + matches!(self.kind, BindingKind::Capture) } pub fn locator(&self) -> Locator { match self.kind { BindingKind::FormalParameter => Locator::Argument(self.index), - _ => Locator::Local(self.index), + BindingKind::Mutable | BindingKind::Immutable => Locator::Local(self.index), + BindingKind::Capture => Locator::Capture(self.index), + BindingKind::Global => Locator::Global, } } @@ -401,6 +459,8 @@ impl std::fmt::Display for Binding { BindingKind::FormalParameter => write!(f, "P@{}:{}", self.index, self.symbol)?, BindingKind::Mutable => write!(f, "M@{}:{}", self.index, self.symbol)?, BindingKind::Immutable => write!(f, "I@{}:{}", self.index, self.symbol)?, + BindingKind::Capture => write!(f, "C@{}:{}", self.index, self.symbol)?, + BindingKind::Global => write!(f, "G@{}:{}", self.index, self.symbol)?, } Ok(()) } @@ -411,6 +471,8 @@ pub enum BindingKind { FormalParameter, Mutable, Immutable, + Capture, + Global, } bitflags! { diff --git a/libs/jsruntime/src/types.rs b/libs/jsruntime/src/types.rs index c5891dc6..94ef6f4b 100644 --- a/libs/jsruntime/src/types.rs +++ b/libs/jsruntime/src/types.rs @@ -292,7 +292,7 @@ pub type Lambda = unsafe extern "C" fn( // This function generates a wrapper function for each `host_fn` at compile time. pub fn into_lambda(host_fn: F) -> Lambda where - F: Fn(&mut Runtime, &[Value]) -> R + Send + Sync + 'static, + F: Fn(&mut Runtime, &[Value]) -> R + 'static, R: Clone + ReturnValue, { debug_assert_eq!(std::mem::size_of::(), 0, "Function must have zero size"); @@ -308,7 +308,7 @@ unsafe extern "C" fn host_fn_wrapper( retv: *mut Value, ) -> Status where - F: Fn(&mut Runtime, &[Value]) -> R + Send + Sync + 'static, + F: Fn(&mut Runtime, &[Value]) -> R + 'static, R: Clone + ReturnValue, { #[allow(clippy::uninit_assumed_init)] diff --git a/libs/jsruntime/tests/scripts/global_object.js b/libs/jsruntime/tests/scripts/global_object.js new file mode 100644 index 00000000..84ea6507 --- /dev/null +++ b/libs/jsruntime/tests/scripts/global_object.js @@ -0,0 +1,18 @@ +try { + print(a); +} catch (e) { + // TODO(feat): ReferenceError + print(e); ///=1000 +} + +// `a` is never captured because it's a global variable. +function x() { + return () => a + b; +} + +a = 1; +print(a); ///=1 + +const y = x(); +const b = 2; +print(y()); ///=3 diff --git a/libs/jsruntime/tests/scripts/logical_and.js b/libs/jsruntime/tests/scripts/logical_and.js index d4712bea..b4379cf3 100644 --- a/libs/jsruntime/tests/scripts/logical_and.js +++ b/libs/jsruntime/tests/scripts/logical_and.js @@ -7,6 +7,11 @@ print(0 && 1); ///=0 print(1 && 0); ///=0 print(1 && 2); ///=2 +// short-circuit expressions using different types. +print(true && undefined); ///=undefined +print(true && null); ///=null +print(true && 0); ///=0 + let a; a = 0; diff --git a/libs/jsruntime/tests/scripts/logical_and_assignment.js b/libs/jsruntime/tests/scripts/logical_and_assignment.js index 53a55585..5836c31e 100644 --- a/libs/jsruntime/tests/scripts/logical_and_assignment.js +++ b/libs/jsruntime/tests/scripts/logical_and_assignment.js @@ -7,3 +7,17 @@ print(a); ///=0 a = 4; print(a &&= 1); ///=1 print(a); ///=1 + +// short-circuit expressions using different types. + +a = true; +print(a &&= undefined); ///=undefined +print(a); ///=undefined + +a = true; +print(a &&= null); ///=null +print(a); ///=null + +a = true; +print(a &&= 0); ///=0 +print(a); ///=0 diff --git a/libs/jsruntime/tests/scripts/logical_or.js b/libs/jsruntime/tests/scripts/logical_or.js index fcfb04dd..94590e7f 100644 --- a/libs/jsruntime/tests/scripts/logical_or.js +++ b/libs/jsruntime/tests/scripts/logical_or.js @@ -7,6 +7,11 @@ print(0 || 0); ///=0 print(0 || 1); ///=1 print(1 || 0); ///=1 +// short-circuit expressions using different types. +print(false || undefined); ///=undefined +print(false || null); ///=null +print(false || 0); ///=0 + let a; a = 0; diff --git a/libs/jsruntime/tests/scripts/logical_or_assignment.js b/libs/jsruntime/tests/scripts/logical_or_assignment.js index fb6f67aa..75b3fb65 100644 --- a/libs/jsruntime/tests/scripts/logical_or_assignment.js +++ b/libs/jsruntime/tests/scripts/logical_or_assignment.js @@ -7,3 +7,17 @@ print(a); ///=1 a = 4; print(a ||= 1); ///=4 print(a); ///=4 + +// short-circuit expressions using different types. + +a = false; +print(a ||= undefined); ///=undefined +print(a); ///=undefined + +a = false; +print(a ||= null); ///=null +print(a); ///=null + +a = false; +print(a ||= 0); ///=0 +print(a); ///=0 diff --git a/libs/jsruntime/tests/scripts/nullish_coalescing.js b/libs/jsruntime/tests/scripts/nullish_coalescing.js index 84a3aeba..649671a0 100644 --- a/libs/jsruntime/tests/scripts/nullish_coalescing.js +++ b/libs/jsruntime/tests/scripts/nullish_coalescing.js @@ -1,3 +1,5 @@ +print(null ?? undefined); ///=undefined +print(undefined ?? null); ///=null print(undefined ?? 1); ///=1 print(null ?? 1); ///=1 print(0 ?? 1); ///=0 diff --git a/libs/jsruntime/tests/scripts/nullish_coalescing_assignment.js b/libs/jsruntime/tests/scripts/nullish_coalescing_assignment.js index 5b7736bd..402ec9b2 100644 --- a/libs/jsruntime/tests/scripts/nullish_coalescing_assignment.js +++ b/libs/jsruntime/tests/scripts/nullish_coalescing_assignment.js @@ -7,3 +7,11 @@ print(a); ///=1 a = 0; print(a ??= 1); ///=0 print(a); ///=0 + +a = null; +print(a ??= undefined); ///=undefined +print(a); ///=undefined + +a = undefined; +print(a ??= null); ///=null +print(a); ///=null