diff --git a/bins/estree/src/builder/actions.yaml b/bins/estree/src/builder/actions.yaml index e44c45ee..6e1dcd58 100644 --- a/bins/estree/src/builder/actions.yaml +++ b/bins/estree/src/builder/actions.yaml @@ -2245,8 +2245,8 @@ StatementListItem_Yield_Await_Return action: append_to_list - rule: >- - MethodDefinition -> ClassElementName LPAREN UniqueFormalParameters RPAREN LBRACE FunctionBody - RBRACE + MethodDefinition -> ClassElementName _FUNCTION_CONTEXT_ LPAREN UniqueFormalParameters RPAREN + _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition - rule: MethodDefinition -> GeneratorMethod action: nop @@ -2254,11 +2254,13 @@ action: nop - rule: MethodDefinition -> AsyncGeneratorMethod action: nop -- rule: MethodDefinition -> GET ClassElementName LPAREN RPAREN LBRACE FunctionBody RBRACE +- rule: >- + MethodDefinition -> GET ClassElementName _FUNCTION_CONTEXT_ LPAREN RPAREN _FUNCTION_SIGNATURE_ + LBRACE FunctionBody RBRACE action: method_definition_get - rule: >- - MethodDefinition -> SET ClassElementName LPAREN PropertySetParameterList RPAREN LBRACE - FunctionBody RBRACE + MethodDefinition -> SET ClassElementName _FUNCTION_CONTEXT_ LPAREN PropertySetParameterList + RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition_set - rule: FieldDefinition -> ClassElementName action: property_definition @@ -2267,8 +2269,8 @@ - rule: ClassStaticBlock -> STATIC LBRACE ClassStaticBlockBody RBRACE action: static_block - rule: >- - MethodDefinition_Await -> ClassElementName_Await LPAREN UniqueFormalParameters RPAREN LBRACE - FunctionBody RBRACE + MethodDefinition_Await -> ClassElementName_Await _FUNCTION_CONTEXT_ LPAREN + UniqueFormalParameters RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition - rule: MethodDefinition_Await -> GeneratorMethod_Await action: nop @@ -2276,11 +2278,13 @@ action: nop - rule: MethodDefinition_Await -> AsyncGeneratorMethod_Await action: nop -- rule: MethodDefinition_Await -> GET ClassElementName_Await LPAREN RPAREN LBRACE FunctionBody RBRACE +- rule: >- + MethodDefinition_Await -> GET ClassElementName_Await _FUNCTION_CONTEXT_ LPAREN RPAREN + _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition_get - rule: >- - MethodDefinition_Await -> SET ClassElementName_Await LPAREN PropertySetParameterList RPAREN - LBRACE FunctionBody RBRACE + MethodDefinition_Await -> SET ClassElementName_Await _FUNCTION_CONTEXT_ LPAREN + PropertySetParameterList RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition_set - rule: FieldDefinition_Await -> ClassElementName_Await action: property_definition @@ -4525,8 +4529,8 @@ - rule: CoverInitializedName_Yield -> IdentifierReference_Yield Initializer_In_Yield action: cover_initialized_name - rule: >- - MethodDefinition_Yield -> ClassElementName_Yield LPAREN UniqueFormalParameters RPAREN LBRACE - FunctionBody RBRACE + MethodDefinition_Yield -> ClassElementName_Yield _FUNCTION_CONTEXT_ LPAREN + UniqueFormalParameters RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition - rule: MethodDefinition_Yield -> GeneratorMethod_Yield action: nop @@ -4534,11 +4538,13 @@ action: nop - rule: MethodDefinition_Yield -> AsyncGeneratorMethod_Yield action: nop -- rule: MethodDefinition_Yield -> GET ClassElementName_Yield LPAREN RPAREN LBRACE FunctionBody RBRACE +- rule: >- + MethodDefinition_Yield -> GET ClassElementName_Yield _FUNCTION_CONTEXT_ LPAREN RPAREN + _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition_get - rule: >- - MethodDefinition_Yield -> SET ClassElementName_Yield LPAREN PropertySetParameterList RPAREN - LBRACE FunctionBody RBRACE + MethodDefinition_Yield -> SET ClassElementName_Yield _FUNCTION_CONTEXT_ LPAREN + PropertySetParameterList RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition_set - rule: ClassElementList_Yield -> ClassElement_Yield action: class_element_list @@ -4581,8 +4587,8 @@ - rule: CoverInitializedName_Yield_Await -> IdentifierReference_Yield_Await Initializer_In_Yield_Await action: cover_initialized_name - rule: >- - MethodDefinition_Yield_Await -> ClassElementName_Yield_Await LPAREN UniqueFormalParameters - RPAREN LBRACE FunctionBody RBRACE + MethodDefinition_Yield_Await -> ClassElementName_Yield_Await _FUNCTION_CONTEXT_ LPAREN + UniqueFormalParameters RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition - rule: MethodDefinition_Yield_Await -> GeneratorMethod_Yield_Await action: nop @@ -4591,12 +4597,12 @@ - rule: MethodDefinition_Yield_Await -> AsyncGeneratorMethod_Yield_Await action: nop - rule: >- - MethodDefinition_Yield_Await -> GET ClassElementName_Yield_Await LPAREN RPAREN LBRACE - FunctionBody RBRACE + MethodDefinition_Yield_Await -> GET ClassElementName_Yield_Await _FUNCTION_CONTEXT_ LPAREN + RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition_get - rule: >- - MethodDefinition_Yield_Await -> SET ClassElementName_Yield_Await LPAREN - PropertySetParameterList RPAREN LBRACE FunctionBody RBRACE + MethodDefinition_Yield_Await -> SET ClassElementName_Yield_Await _FUNCTION_CONTEXT_ LPAREN + PropertySetParameterList RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: method_definition_set - rule: ClassElementList_Yield_Await -> ClassElement_Yield_Await action: class_element_list diff --git a/libs/jsparser/src/symbol/builtin.yaml b/libs/jsparser/src/symbol/builtin.yaml index 9b2ff405..c5ec0a0b 100644 --- a/libs/jsparser/src/symbol/builtin.yaml +++ b/libs/jsparser/src/symbol/builtin.yaml @@ -56,24 +56,33 @@ - [async, ASYNC] - all - arguments +- assign - at - cause - charAt - charCodeAt - codePointAt - concat +- configurable - constructor +- create - default +- defineProperties +- defineProperty - endsWith +- enumerable - eval - fromCharCode - fromCodePoint +- get - globalThis +- hasOwnProperty - includes - indexOf - isError - isFinite - [isNaN, IS_NAN] +- isPrototypeOf - isWellFormed - lastIndexOf - length @@ -83,14 +92,19 @@ - padStart - parseFloat - parseInt +- propertyIsEnumerable - prototype - repeat +- set - startsWith - substring - toString - trim - trimEnd - trimStart +- value +- valueOf +- writable # Special symbols for internal use only. - ['-Infinity', NEG_INFINITY] diff --git a/libs/jsparser/src/syntax/actions.yaml b/libs/jsparser/src/syntax/actions.yaml index fd59856d..e62d89a3 100644 --- a/libs/jsparser/src/syntax/actions.yaml +++ b/libs/jsparser/src/syntax/actions.yaml @@ -2468,9 +2468,10 @@ StatementListItem_Yield_Await_Return action: process_statement_list_item - rule: >- - MethodDefinition -> ClassElementName LPAREN UniqueFormalParameters RPAREN - LBRACE FunctionBody RBRACE - action: undefined + MethodDefinition -> ClassElementName _FUNCTION_CONTEXT_ LPAREN + UniqueFormalParameters RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody + RBRACE + action: process_method_definition - rule: MethodDefinition -> GeneratorMethod action: undefined - rule: MethodDefinition -> AsyncMethod @@ -2478,12 +2479,13 @@ - rule: MethodDefinition -> AsyncGeneratorMethod action: undefined - rule: >- - MethodDefinition -> GET ClassElementName LPAREN RPAREN LBRACE FunctionBody - RBRACE + MethodDefinition -> GET ClassElementName _FUNCTION_CONTEXT_ LPAREN RPAREN + _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: undefined - rule: >- - MethodDefinition -> SET ClassElementName LPAREN PropertySetParameterList - RPAREN LBRACE FunctionBody RBRACE + MethodDefinition -> SET ClassElementName _FUNCTION_CONTEXT_ LPAREN + PropertySetParameterList RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody + RBRACE action: undefined - rule: FieldDefinition -> ClassElementName action: undefined @@ -2492,9 +2494,10 @@ - rule: ClassStaticBlock -> STATIC LBRACE ClassStaticBlockBody RBRACE action: undefined - rule: >- - MethodDefinition_Await -> ClassElementName_Await LPAREN - UniqueFormalParameters RPAREN LBRACE FunctionBody RBRACE - action: undefined + MethodDefinition_Await -> ClassElementName_Await _FUNCTION_CONTEXT_ LPAREN + UniqueFormalParameters RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody + RBRACE + action: process_method_definition - rule: MethodDefinition_Await -> GeneratorMethod_Await action: undefined - rule: MethodDefinition_Await -> AsyncMethod_Await @@ -2502,12 +2505,13 @@ - rule: MethodDefinition_Await -> AsyncGeneratorMethod_Await action: undefined - rule: >- - MethodDefinition_Await -> GET ClassElementName_Await LPAREN RPAREN LBRACE - FunctionBody RBRACE + MethodDefinition_Await -> GET ClassElementName_Await _FUNCTION_CONTEXT_ + LPAREN RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: undefined - rule: >- - MethodDefinition_Await -> SET ClassElementName_Await LPAREN - PropertySetParameterList RPAREN LBRACE FunctionBody RBRACE + MethodDefinition_Await -> SET ClassElementName_Await _FUNCTION_CONTEXT_ + LPAREN PropertySetParameterList RPAREN _FUNCTION_SIGNATURE_ LBRACE + FunctionBody RBRACE action: undefined - rule: FieldDefinition_Await -> ClassElementName_Await action: undefined @@ -3046,9 +3050,9 @@ - rule: StatementListItem_Yield_Await_Return -> Declaration_Yield_Await action: nop - rule: ClassElementName -> PropertyName - action: undefined + action: process_class_element_name_property_name - rule: ClassElementName -> PRIVATE_IDENTIFIER - action: undefined + action: process_class_element_name_private_identifier - rule: >- GeneratorMethod -> MUL ClassElementName LPAREN UniqueFormalParameters_Yield RPAREN LBRACE GeneratorBody RBRACE @@ -3067,9 +3071,9 @@ - rule: ClassStaticBlockBody -> ClassStaticBlockStatementList action: undefined - rule: ClassElementName_Await -> PropertyName_Await - action: undefined + action: process_class_element_name_property_name - rule: ClassElementName_Await -> PRIVATE_IDENTIFIER - action: undefined + action: process_class_element_name_private_identifier - rule: >- GeneratorMethod_Await -> MUL ClassElementName_Await LPAREN UniqueFormalParameters_Yield RPAREN LBRACE GeneratorBody RBRACE @@ -3519,7 +3523,7 @@ AssignmentExpression_In_Await action: process_property_definition_key_value - rule: PropertyDefinition_Await -> MethodDefinition_Await - action: undefined + action: process_property_definition_method - rule: PropertyDefinition_Await -> ELLIPSIS AssignmentExpression_In_Await action: process_property_definition_spread - rule: _TO_STRING_ -> (empty) @@ -4097,7 +4101,7 @@ - rule: PropertyDefinition -> PropertyName COLON AssignmentExpression_In action: process_property_definition_key_value - rule: PropertyDefinition -> MethodDefinition - action: undefined + action: process_property_definition_method - rule: PropertyDefinition -> ELLIPSIS AssignmentExpression_In action: process_property_definition_spread - rule: TemplateSpans -> _TEMPLATE_TAIL_ @@ -4440,7 +4444,7 @@ AssignmentExpression_In_Yield action: process_property_definition_key_value - rule: PropertyDefinition_Yield -> MethodDefinition_Yield - action: undefined + action: process_property_definition_method - rule: PropertyDefinition_Yield -> ELLIPSIS AssignmentExpression_In_Yield action: process_property_definition_spread - rule: ClassHeritage_Yield -> EXTENDS LeftHandSideExpression_Yield @@ -4480,7 +4484,7 @@ AssignmentExpression_In_Yield_Await action: process_property_definition_key_value - rule: PropertyDefinition_Yield_Await -> MethodDefinition_Yield_Await - action: undefined + action: process_property_definition_method - rule: >- PropertyDefinition_Yield_Await -> ELLIPSIS AssignmentExpression_In_Yield_Await @@ -5067,9 +5071,10 @@ - rule: CoverInitializedName_Yield -> IdentifierReference_Yield Initializer_In_Yield action: process_cover_initialized_name - rule: >- - MethodDefinition_Yield -> ClassElementName_Yield LPAREN - UniqueFormalParameters RPAREN LBRACE FunctionBody RBRACE - action: undefined + MethodDefinition_Yield -> ClassElementName_Yield _FUNCTION_CONTEXT_ LPAREN + UniqueFormalParameters RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody + RBRACE + action: process_method_definition - rule: MethodDefinition_Yield -> GeneratorMethod_Yield action: undefined - rule: MethodDefinition_Yield -> AsyncMethod_Yield @@ -5077,12 +5082,13 @@ - rule: MethodDefinition_Yield -> AsyncGeneratorMethod_Yield action: undefined - rule: >- - MethodDefinition_Yield -> GET ClassElementName_Yield LPAREN RPAREN LBRACE - FunctionBody RBRACE + MethodDefinition_Yield -> GET ClassElementName_Yield _FUNCTION_CONTEXT_ + LPAREN RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: undefined - rule: >- - MethodDefinition_Yield -> SET ClassElementName_Yield LPAREN - PropertySetParameterList RPAREN LBRACE FunctionBody RBRACE + MethodDefinition_Yield -> SET ClassElementName_Yield _FUNCTION_CONTEXT_ + LPAREN PropertySetParameterList RPAREN _FUNCTION_SIGNATURE_ LBRACE + FunctionBody RBRACE action: undefined - rule: ClassElementList_Yield -> ClassElement_Yield action: undefined @@ -5131,9 +5137,10 @@ Initializer_In_Yield_Await action: process_cover_initialized_name - rule: >- - MethodDefinition_Yield_Await -> ClassElementName_Yield_Await LPAREN - UniqueFormalParameters RPAREN LBRACE FunctionBody RBRACE - action: undefined + MethodDefinition_Yield_Await -> ClassElementName_Yield_Await + _FUNCTION_CONTEXT_ LPAREN UniqueFormalParameters RPAREN _FUNCTION_SIGNATURE_ + LBRACE FunctionBody RBRACE + action: process_method_definition - rule: MethodDefinition_Yield_Await -> GeneratorMethod_Yield_Await action: undefined - rule: MethodDefinition_Yield_Await -> AsyncMethod_Yield_Await @@ -5141,12 +5148,14 @@ - rule: MethodDefinition_Yield_Await -> AsyncGeneratorMethod_Yield_Await action: undefined - rule: >- - MethodDefinition_Yield_Await -> GET ClassElementName_Yield_Await LPAREN - RPAREN LBRACE FunctionBody RBRACE + MethodDefinition_Yield_Await -> GET ClassElementName_Yield_Await + _FUNCTION_CONTEXT_ LPAREN RPAREN _FUNCTION_SIGNATURE_ LBRACE FunctionBody + RBRACE action: undefined - rule: >- - MethodDefinition_Yield_Await -> SET ClassElementName_Yield_Await LPAREN - PropertySetParameterList RPAREN LBRACE FunctionBody RBRACE + MethodDefinition_Yield_Await -> SET ClassElementName_Yield_Await + _FUNCTION_CONTEXT_ LPAREN PropertySetParameterList RPAREN + _FUNCTION_SIGNATURE_ LBRACE FunctionBody RBRACE action: undefined - rule: ClassElementList_Yield_Await -> ClassElement_Yield_Await action: undefined @@ -5293,9 +5302,9 @@ - rule: ShiftExpression_Yield -> ShiftExpression_Yield SHR AdditiveExpression_Yield action: process_unsigned_right_shift - rule: ClassElementName_Yield -> PropertyName_Yield - action: undefined + action: process_class_element_name_property_name - rule: ClassElementName_Yield -> PRIVATE_IDENTIFIER - action: undefined + action: process_class_element_name_private_identifier - rule: >- GeneratorMethod_Yield -> MUL ClassElementName_Yield LPAREN UniqueFormalParameters_Yield RPAREN LBRACE GeneratorBody RBRACE @@ -5337,9 +5346,9 @@ AdditiveExpression_Yield_Await action: process_unsigned_right_shift - rule: ClassElementName_Yield_Await -> PropertyName_Yield_Await - action: undefined + action: process_class_element_name_property_name - rule: ClassElementName_Yield_Await -> PRIVATE_IDENTIFIER - action: undefined + action: process_class_element_name_private_identifier - rule: >- GeneratorMethod_Yield_Await -> MUL ClassElementName_Yield_Await LPAREN UniqueFormalParameters_Yield RPAREN LBRACE GeneratorBody RBRACE diff --git a/libs/jsparser/src/syntax/mod.rs b/libs/jsparser/src/syntax/mod.rs index 50f6912e..e2483e48 100644 --- a/libs/jsparser/src/syntax/mod.rs +++ b/libs/jsparser/src/syntax/mod.rs @@ -139,6 +139,8 @@ enum Detail { Declaration, FormalParameters(SmallVec<[Symbol; 4]>), ConciseBody, + MethodDefinition(Symbol, bool), + ClassElementName(Symbol, bool), AsyncConciseBody, StatementList, CoverCallExpressionAndAsyncArrowHead, @@ -256,6 +258,7 @@ pub enum Node<'s> { AsyncFunctionExpression(bool), ArrowFunction, AsyncArrowFunction, + Method, AwaitExpression, Then(bool), Else(bool), @@ -285,6 +288,7 @@ pub enum PropertyDefinitionKind { ArraySpread, Reference, KeyValue, + Method, Spread, } @@ -575,10 +579,28 @@ where } fn make_symbol(&mut self, token_index: usize) -> Symbol { - let lexeme = self.tokens[token_index].lexeme; + let lexeme = self.lexeme(token_index); self.handler.make_symbol(lexeme) } + fn index_of_last_token(&self) -> usize { + debug_assert!(!self.tokens.is_empty()); + self.tokens.len() - 1 + } + + fn lexeme(&self, index: usize) -> &'s str { + self.token(index).lexeme + } + + fn last_token(&self) -> &Token<'s> { + self.token(self.index_of_last_token()) + } + + fn token(&self, index: usize) -> &Token<'s> { + debug_assert!(index < self.tokens.len()); + &self.tokens[index] + } + fn src(&self, range: Range) -> &'s str { &self.source[range] } @@ -664,8 +686,9 @@ where fn process_function_context(&mut self) -> Result<(), Error> { let name = match self.stack.last().unwrap().detail { Detail::BindingIdentifier(symbol) => symbol, + Detail::ClassElementName(symbol, _) => symbol, Detail::Token(index) => { - debug_assert!(matches!(self.tokens[index].kind, TokenKind::Function)); + debug_assert!(matches!(self.token(index).kind, TokenKind::Function)); Symbol::NONE // anonymous function } ref detail => unreachable!("{detail:?}"), @@ -679,7 +702,7 @@ where let name = match self.stack.last().unwrap().detail { Detail::BindingIdentifier(symbol) => symbol, Detail::Token(index) => { - debug_assert!(matches!(self.tokens[index].kind, TokenKind::Function)); + debug_assert!(matches!(self.token(index).kind, TokenKind::Function)); Symbol::NONE // anonymous function } ref detail => unreachable!("{detail:?}"), @@ -738,7 +761,7 @@ where fn process_identifier_reference(&mut self) -> Result<(), Error> { let symbol = match self.top().detail { Detail::Identifier(symbol) => symbol, - Detail::Token(index) => match self.tokens[index].kind { + Detail::Token(index) => match self.token(index).kind { TokenKind::Await => Symbol::KEYWORD_AWAIT, TokenKind::Yield => Symbol::KEYWORD_YIELD, kind => unreachable!("{kind:?}"), @@ -807,7 +830,7 @@ where fn process_binding_identifier(&mut self) -> Result<(), Error> { let symbol = match self.top().detail { Detail::Identifier(symbol) => symbol, - Detail::Token(index) => match self.tokens[index].kind { + Detail::Token(index) => match self.token(index).kind { TokenKind::Await => Symbol::KEYWORD_AWAIT, TokenKind::Yield => Symbol::KEYWORD_YIELD, kind => unreachable!("{kind:?}"), @@ -888,7 +911,7 @@ where fn process_label_identifier(&mut self) -> Result<(), Error> { let symbol = match self.top().detail { Detail::Identifier(symbol) => symbol, - Detail::Token(index) => match self.tokens[index].kind { + Detail::Token(index) => match self.token(index).kind { TokenKind::Await => Symbol::KEYWORD_AWAIT, TokenKind::Yield => Symbol::KEYWORD_YIELD, kind => unreachable!("{kind:?}"), @@ -952,8 +975,8 @@ where // Identifier : // IdentifierName but not ReservedWord fn process_identifier(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; - match self.tokens[token_index].kind { + let token_index = self.index_of_last_token(); + match self.token(token_index).kind { TokenKind::Implements | TokenKind::Let | TokenKind::Package @@ -1143,8 +1166,7 @@ where // NumericLiteral // StringLiteral fn process_literal(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; - let token = &self.tokens[token_index]; + let token = self.last_token(); let node_index = match token.kind { TokenKind::Null => self.enqueue(Node::Null), TokenKind::True => self.enqueue(Node::Boolean(true)), @@ -1483,6 +1505,19 @@ where Ok(()) } + // PropertyDefinition[Yield, Await] : + // MethodDefinition[?Yield, ?Await] + fn process_property_definition_method(&mut self) -> Result<(), Error> { + let name = match self.top().detail { + Detail::MethodDefinition(_, true) => return Err(Error::SyntaxError), + Detail::MethodDefinition(name, _) => name, + ref detail => unreachable!("{detail:?}"), + }; + self.enqueue(Node::PropertyDefinition(PropertyDefinitionKind::Method)); + self.replace(1, Detail::PropertyDefinition(name)); + Ok(()) + } + // PropertyDefinition[Yield, Await] : // ... AssignmentExpression[+In, ?Yield, ?Await] fn process_property_definition_spread(&mut self) -> Result<(), Error> { @@ -1494,7 +1529,7 @@ where // LiteralPropertyName : // IdentifierName fn process_literal_property_name_identifier_name(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; + let token_index = self.index_of_last_token(); let symbol = self.make_symbol(token_index); self.enqueue(Node::LiteralPropertyName( LiteralPropertyName::IdentifierName(symbol), @@ -1506,7 +1541,7 @@ where // LiteralPropertyName : // NumericLiteral fn process_literal_property_name_numeric_literal(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; + let token_index = self.index_of_last_token(); // TODO: RS // 1. Let nbr be the NumericValue of NumericLiteral. // 2. Return ! ToString(nbr). @@ -1541,8 +1576,7 @@ where // TODO(feat): 13.2.8.1 Static Semantics: Early Errors // It is a Syntax Error if the [Tagged] parameter was not set and NoSubstitutionTemplate // Contains NotEscapeSequence. - let token_index = self.tokens.len() - 1; - let token = &self.tokens[token_index]; + let token = self.last_token(); debug_assert!(matches!(token.kind, TokenKind::NoSubstitutionTemplate)); // TODO: perform `TV` let content = &token.lexeme[1..(token.lexeme.len() - 1)]; @@ -1617,8 +1651,7 @@ where } fn process_template_head(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; - let token = &self.tokens[token_index]; + let token = self.last_token(); debug_assert!(matches!(token.kind, TokenKind::TemplateHead)); // TODO: perform `TV` let content = &token.lexeme[1..(token.lexeme.len() - 2)]; @@ -1632,8 +1665,7 @@ where } fn process_template_middle(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; - let token = &self.tokens[token_index]; + let token = self.last_token(); debug_assert!(matches!(token.kind, TokenKind::TemplateMiddle)); // TODO: perform `TV` let content = &token.lexeme[1..(token.lexeme.len() - 2)]; @@ -1647,8 +1679,7 @@ where } fn process_template_tail(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; - let token = &self.tokens[token_index]; + let token = self.last_token(); debug_assert!(matches!(token.kind, TokenKind::TemplateTail)); // TODO: perform `TV` let content = &token.lexeme[1..(token.lexeme.len() - 1)]; @@ -1683,7 +1714,7 @@ where // MemberExpression[Yield, Await] : // MemberExpression[?Yield, ?Await] . IdentifierName fn process_member_expression_dot_notation(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; + let token_index = self.index_of_last_token(); let symbol = self.make_symbol(token_index); self.enqueue(Node::MemberExpression( MemberExpressionKind::PropertyAccessWithIdentifierKey(symbol), @@ -1715,7 +1746,7 @@ where // CallExpression[Yield, Await] : // CallExpression[?Yield, ?Await] . IdentifierName fn process_call_expression_dot_notation(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; + let token_index = self.index_of_last_token(); let symbol = self.make_symbol(token_index); self.enqueue(Node::MemberExpression( MemberExpressionKind::PropertyAccessWithIdentifierKey(symbol), @@ -1887,7 +1918,7 @@ where // OptionalChain[Yield, Await] : // ?. IdentifierName fn process_optional_chain_identifier_name(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; + let token_index = self.index_of_last_token(); let key = self.make_symbol(token_index); self.enqueue(Node::OptionalChain(PropertyAccessKind::IdentifierKey(key))); self.replace(2, Detail::OptionalChain); @@ -1905,7 +1936,7 @@ where // OptionalChain[Yield, Await] : // OptionalChain[?Yield, ?Await] . IdentifierName fn process_optional_chain_identifier_name_chain(&mut self) -> Result<(), Error> { - let token_index = self.tokens.len() - 1; + let token_index = self.index_of_last_token(); let key = self.make_symbol(token_index); self.enqueue(Node::MemberExpression( MemberExpressionKind::PropertyAccessWithIdentifierKey(key), @@ -3611,6 +3642,50 @@ where Ok(()) } + // 15.4 Method Definitions + + // MethodDefinition[Yield, Await] : + // ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) + // { FunctionBody[~Yield, ~Await] } + fn process_method_definition(&mut self) -> Result<(), Error> { + let (name, private) = match self.nth(6).detail { + Detail::ClassElementName(name, private) => (name, private), + ref detail => unreachable!("{detail:?}"), + }; + self.enqueue(Node::Method); + self.replace(7, Detail::MethodDefinition(name, private)); + Ok(()) + } + + // 15.7 Class Definitions + + // ClassElementName[Yield, Await] : + // PropertyName[?Yield, ?Await] + fn process_class_element_name_property_name(&mut self) -> Result<(), Error> { + let name = match self.top().detail { + Detail::Identifier(symbol) => symbol, + ref detail => unreachable!("{detail:?}"), + }; + self.replace(1, Detail::ClassElementName(name, false)); + Ok(()) + } + + // ClassElementName[Yield, Await] : + // PrivateIdentifier + fn process_class_element_name_private_identifier(&mut self) -> Result<(), Error> { + let name = match self.top().detail { + Detail::Token(index) => { + if self.lexeme(index) == "#constructor" { + return Err(Error::SyntaxError); + } + self.make_symbol(index) + } + ref detail => unreachable!("{detail:?}"), + }; + self.replace(1, Detail::ClassElementName(name, true)); + Ok(()) + } + // 15.8 Async Function Definitions // AsyncFunctionDeclaration[Yield, Await, Default] : diff --git a/libs/jsparser/src/transpile.js b/libs/jsparser/src/transpile.js index 6c962c43..3a98d11f 100644 --- a/libs/jsparser/src/transpile.js +++ b/libs/jsparser/src/transpile.js @@ -170,6 +170,7 @@ class Transpiler { modifyOptionalChain, modifyFunctionDeclaration, modifyAsyncFunctionDeclaration, + modifyMethodDefinition, modifyIfStatement, modifyConditionalExpression, modifyShortCircuitExpressions, @@ -870,6 +871,26 @@ function modifyAsyncFunctionDeclaration(rules) { return rules; } +function modifyMethodDefinition(rules) { + const TARGETS = [ + { + term: '`(`', + action: '_FUNCTION_CONTEXT_', + insertBefore: true, + }, + { + term: '`{`', + action: '_FUNCTION_SIGNATURE_', + insertBefore: true, + }, + ]; + log.debug('Modifying MethodDefinition...'); + const rule = rules.find((rule) => rule.name === 'MethodDefinition[Yield, Await]'); + assert(rule !== undefined); + modifyTargetsInRule(rule, TARGETS); + return rules; +} + function modifyIfStatement(rules) { log.debug('Modifying IfStatement...'); diff --git a/libs/jsparser/tests/static_semantics.rs b/libs/jsparser/tests/static_semantics.rs index 47ce3f66..b9502b74 100644 --- a/libs/jsparser/tests/static_semantics.rs +++ b/libs/jsparser/tests/static_semantics.rs @@ -36,6 +36,11 @@ fn test_13_2_5_1_duplicate_proto() { parse_fail!(script: "static_semantics_13_2_5_1_duplicate_proto.js"); } +#[test] +fn test_15_2_5_1_private_method() { + parse_fail!(script: "static_semantics_13_2_5_1_private_method.js"); +} + #[test] fn test_13_4_1_postfix_increment() { parse_fail!(script: "static_semantics_13_4_1_postfix_increment.js"); @@ -126,6 +131,11 @@ fn test_15_1_1() { parse_fail!(script: "static_semantics_15_1_1.js"); } +#[test] +fn test_15_7_1_class_element_name() { + parse_fail!(script: "static_semantics_15_7_1_class_element_name.js"); +} + #[test] fn test_continue_statement_with_label_not_in_label_set() { parse_fail!(script: "static_semantics_continue_statement_with_label_not_in_label_set.js"); diff --git a/libs/jsparser/tests/static_semantics_13_2_5_1_private_method.js b/libs/jsparser/tests/static_semantics_13_2_5_1_private_method.js new file mode 100644 index 00000000..2578bf2d --- /dev/null +++ b/libs/jsparser/tests/static_semantics_13_2_5_1_private_method.js @@ -0,0 +1,3 @@ +const a = { + #a() {}, +}; diff --git a/libs/jsparser/tests/static_semantics_15_7_1_class_element_name.js b/libs/jsparser/tests/static_semantics_15_7_1_class_element_name.js new file mode 100644 index 00000000..61077041 --- /dev/null +++ b/libs/jsparser/tests/static_semantics_15_7_1_class_element_name.js @@ -0,0 +1,3 @@ +const a = { + #constructor() {}, +}; diff --git a/libs/jsruntime/src/backend/bridge.rs b/libs/jsruntime/src/backend/bridge.rs index 6d0b71bc..387032fc 100644 --- a/libs/jsruntime/src/backend/bridge.rs +++ b/libs/jsruntime/src/backend/bridge.rs @@ -129,19 +129,25 @@ pub(crate) extern "C" fn runtime_lazy_compile_coroutine( } // 7.1.2 ToBoolean ( argument ) -pub(crate) extern "C" fn runtime_to_boolean(_runtime: &mut Runtime, value: &Value) -> bool { +pub(crate) extern "C" fn runtime_to_boolean(runtime: &mut Runtime, value: &Value) -> bool { logger::debug!(event = "runtime_to_boolean", ?value); - match value { - Value::None => unreachable!("Value::None"), - Value::Undefined => false, - Value::Null => false, - Value::Boolean(value) => *value, - Value::Number(0.0) => false, - Value::Number(value) if value.is_nan() => false, - Value::Number(_) => true, - Value::String(value) if value.is_empty() => false, - Value::String(_) => true, - Value::Object(_) => true, + runtime.value_to_boolean(value) +} + +impl Runtime { + pub(crate) fn value_to_boolean(&mut self, value: &Value) -> bool { + match value { + Value::None => unreachable!("Value::None"), + Value::Undefined => false, + Value::Null => false, + Value::Boolean(value) => *value, + Value::Number(0.0) => false, + Value::Number(value) if value.is_nan() => false, + Value::Number(_) => true, + Value::String(value) if value.is_empty() => false, + Value::String(_) => true, + Value::Object(_) => true, + } } } @@ -194,8 +200,8 @@ pub(crate) extern "C" fn runtime_to_object( ) -> Status { logger::debug!(event = "runtime_to_object", ?value); match runtime.value_to_object(value) { - Ok(value) => { - *retv = value; + Ok(object) => { + *retv = Value::Object(object); Status::Normal } Err(err) => { @@ -206,7 +212,8 @@ pub(crate) extern "C" fn runtime_to_object( } impl Runtime { - pub(crate) fn value_to_object(&mut self, value: &Value) -> Result { + // 7.1.18 ToObject ( argument ) + pub(crate) fn value_to_object(&mut self, value: &Value) -> Result, Error> { logger::debug!(event = "to_object", ?value); match value { Value::None => unreachable!("Value::None"), @@ -219,9 +226,12 @@ impl Runtime { } Value::String(value) => { // TODO(refactor): rewrite using `new String(value)` - self.create_string_object(None, &[Value::String(*value)], true) + match self.create_string_object(None, &[Value::String(*value)], true)? { + Value::Object(object) => Ok(object), + _ => unreachable!(), + } } - Value::Object(_) => Ok(value.clone()), + Value::Object(object) => Ok(*object), } } } diff --git a/libs/jsruntime/src/builtins/imp.js b/libs/jsruntime/src/builtins/imp.js index dc2dd3fc..aaaef633 100644 --- a/libs/jsruntime/src/builtins/imp.js +++ b/libs/jsruntime/src/builtins/imp.js @@ -262,6 +262,7 @@ function parseArgs(text) { let args = []; let optional = false; let rest = false; + let name; for (const part of text.split(' ')) { switch (part) { case '[': @@ -276,11 +277,10 @@ function parseArgs(text) { rest = true; break; default: - args.push({ - name: part.substring(1, part.length - 1), // remove '_' - optional, - rest, - }); + name = part.substring(1, part.length - 1); // remove '_' + if (name.length > 0) { + args.push({ name, optional, rest }); + } rest = false; break; } diff --git a/libs/jsruntime/src/builtins/object/README.md b/libs/jsruntime/src/builtins/object/README.md index a496d721..0cf0b368 100644 --- a/libs/jsruntime/src/builtins/object/README.md +++ b/libs/jsruntime/src/builtins/object/README.md @@ -1,10 +1,10 @@ # Object * [x] [Object](https://tc39.es/ecma262/#sec-object-value) -* [ ] [Object.assign](https://tc39.es/ecma262/#sec-object.assign) -* [ ] [Object.create](https://tc39.es/ecma262/#sec-object.create) -* [ ] [Object.defineProperties](https://tc39.es/ecma262/#sec-object.defineproperties) -* [ ] [Object.defineProperty](https://tc39.es/ecma262/#sec-object.defineproperty) +* [x] [Object.assign](https://tc39.es/ecma262/#sec-object.assign) +* [x] [Object.create](https://tc39.es/ecma262/#sec-object.create) +* [x] [Object.defineProperties](https://tc39.es/ecma262/#sec-object.defineproperties) +* [x] [Object.defineProperty](https://tc39.es/ecma262/#sec-object.defineproperty) * [ ] [Object.entries](https://tc39.es/ecma262/#sec-object.entries) * [ ] [Object.freeze](https://tc39.es/ecma262/#sec-object.freeze) * [ ] [Object.fromEntries](https://tc39.es/ecma262/#sec-object.fromentries) @@ -26,12 +26,12 @@ * [ ] [Object.setPrototypeOf](https://tc39.es/ecma262/#sec-object.setprototypeof) * [ ] [Object.values](https://tc39.es/ecma262/#sec-object.values) * [ ] [Object.prototype.constructor](https://tc39.es/ecma262/#sec-object.prototype.constructor) -* [ ] [Object.prototype.hasOwnProperty](https://tc39.es/ecma262/#sec-object.prototype.hasownproperty) -* [ ] [Object.prototype.isPrototypeOf](https://tc39.es/ecma262/#sec-object.prototype.isprototypeof) -* [ ] [Object.prototype.propertyIsEnumerable](https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable) +* [x] [Object.prototype.hasOwnProperty](https://tc39.es/ecma262/#sec-object.prototype.hasownproperty) +* [x] [Object.prototype.isPrototypeOf](https://tc39.es/ecma262/#sec-object.prototype.isprototypeof) +* [x] [Object.prototype.propertyIsEnumerable](https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable) * [ ] [Object.prototype.toLocaleString](https://tc39.es/ecma262/#sec-object.prototype.tolocalestring) -* [ ] [Object.prototype.toString](https://tc39.es/ecma262/#sec-object.prototype.tostring) -* [ ] [Object.prototype.valueOf](https://tc39.es/ecma262/#sec-object.prototype.valueof) +* [x] [Object.prototype.toString](https://tc39.es/ecma262/#sec-object.prototype.tostring) +* [x] [Object.prototype.valueOf](https://tc39.es/ecma262/#sec-object.prototype.valueof) * [ ] [Object.prototype.__proto__](https://tc39.es/ecma262/#sec-object.prototype.__proto__) * [ ] [Object.prototype.__defineGetter__](https://tc39.es/ecma262/#sec-object.prototype.__defineGetter__) * [ ] [Object.prototype.__defineSetter__](https://tc39.es/ecma262/#sec-object.prototype.__defineSetter__) diff --git a/libs/jsruntime/src/builtins/object/imp.rs b/libs/jsruntime/src/builtins/object/imp.rs index f30c7c81..e5bc68df 100644 --- a/libs/jsruntime/src/builtins/object/imp.rs +++ b/libs/jsruntime/src/builtins/object/imp.rs @@ -1,10 +1,17 @@ //$id object //$class Object +use jsgc::HandleMut; +use jsparser::Symbol; + use crate::Error; use crate::Runtime; use crate::logger; use crate::types::CallContext; +use crate::types::Object; +use crate::types::Property; +use crate::types::PropertyFlags; +use crate::types::PropertyKey; use crate::types::Value; //#sec-object-value constructor @@ -16,9 +23,279 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re runtime.create_object_object(Some(this), args, new) } +//#sec-object.assign constructor.function +pub fn object_assign( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_assign"); + let target = context.arg(0); + let mut to = runtime.value_to_object(target)?; + // TODO(feat): `sources` is a rest parameter. + for arg in context.args().iter().skip(1) { + match arg { + Value::None => unreachable!(), + Value::Null | Value::Undefined => continue, + _ => { + let from = runtime.value_to_object(arg)?; + for (key, prop) in from.iter_own_properties() { + if prop.is_enumerable() { + to.set_value(key, prop.value()); + } + } + } + } + } + Ok(Value::Object(to)) +} + +//#sec-object.create constructor.function +pub fn object_create( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_create"); + let proto = context.arg(0); + match proto { + Value::None => unreachable!(), + Value::Null | Value::Object(_) => (), + _ => return type_error!("Object prototype may only be an Object or null"), + } + let mut obj = runtime.create_object(); + if let Value::Object(proto) = proto { + obj.set_prototype(*proto); + } + let properties = context.arg(1); + match properties { + Value::None => unreachable!(), + Value::Undefined => Ok(Value::Object(obj)), + _ => Ok(Value::Object( + runtime.object_define_properties(obj, properties)?, + )), + } +} + +//#sec-object.defineproperties constructor.function +pub fn object_define_properties( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_define_properties"); + let obj = match context.arg(0) { + Value::None => unreachable!(), + Value::Object(object) => *object, + _ => return type_error!("Object.defineProperties called on non-object"), + }; + Ok(Value::Object( + runtime.object_define_properties(obj, context.arg(1))?, + )) +} + +//#sec-object.defineproperty constructor.function +pub fn object_define_property( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_define_property"); + let mut obj = match context.arg(0) { + Value::None => unreachable!(), + Value::Object(object) => *object, + _ => return type_error!("Object.defineProperty called on non-object"), + }; + let key = runtime.value_to_property_key(context.arg(1))?; + let prop = runtime.value_to_property(context.arg(2))?; + obj.define_own_property(key, prop)?; + Ok(Value::Object(obj)) +} + +//#sec-object.prototype.hasownproperty prototype.function +pub fn object_prototype_has_own_property( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_prototype_has_own_property"); + let key = runtime.value_to_property_key(context.arg(0))?; + let obj = runtime.value_to_object(context.this())?; + match obj.get_own_property(&key) { + Some(_) => Ok(Value::TRUE), + None => Ok(Value::FALSE), + } +} + +//#sec-object.prototype.isprototypeof prototype.function +pub fn object_prototype_is_prototype_of( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_prototype_is_prototype_of"); + let mut value = match context.arg(0) { + Value::Object(v) => *v, + _ => return Ok(Value::FALSE), + }; + let obj = runtime.value_to_object(context.this())?; + loop { + value = match value.prototype() { + Some(v) => v, + None => return Ok(Value::FALSE), + }; + if obj == value { + return Ok(Value::TRUE); + } + } +} + +//#sec-object.prototype.propertyisenumerable prototype.function +pub fn object_prototype_property_is_enumerable( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_prototype_property_is_enumerable"); + let key = runtime.value_to_property_key(context.arg(0))?; + let obj = runtime.value_to_object(context.this())?; + match obj.get_own_property(&key) { + Some(prop) => Ok(Value::Boolean(prop.is_enumerable())), + None => Ok(Value::FALSE), + } +} + +//#sec-object.prototype.tostring prototype.function +pub fn object_prototype_to_string( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_prototype_to_string"); + match context.this() { + Value::None => unreachable!(), + Value::Undefined => Ok(Value::String(const_string_handle!("[object Undefined]"))), + Value::Null => Ok(Value::String(const_string_handle!("[object Null]"))), + this => { + let obj = runtime.value_to_object(this)?; + // TODO(feat): IsArray(obj), "[object Array]" + // TODO(feat): "[object Arguments]" + if obj.is_callable() { + Ok(Value::String(const_string_handle!("[object Function]"))) + } else if obj.is_error() { + Ok(Value::String(const_string_handle!("[object Error]"))) + } else { + // TODO(feat): "[object Boolean]" + // TODO(feat): "[object Number]" + // TODO(feat): "[object String]" + // TODO(feat): "[object Date]" + // TODO(feat): "[object RegExp]" + // TODO(feat): Get(obj, %Symbol.toStringTag%) + Ok(Value::String(const_string_handle!("[object Object]"))) + } + } + } +} + +//#sec-object.prototype.valueof prototype.function +pub fn object_prototype_value_of( + runtime: &mut Runtime, + context: &mut CallContext, +) -> Result { + logger::debug!(event = "object_prototype_value_of"); + Ok(Value::Object(runtime.value_to_object(context.this())?)) +} + // helpers impl Runtime { + // 20.1.2.3.1 ObjectDefineProperties ( obj, properties ) + fn object_define_properties( + &mut self, + mut obj: HandleMut, + properties: &Value, + ) -> Result, Error> { + let props = self.value_to_object(properties)?; + for (key, prop) in props.iter_own_properties() { + let desc_obj = prop.value(); + if !matches!(desc_obj, Value::Undefined) && prop.is_enumerable() { + let new_prop = self.value_to_property(desc_obj)?; + obj.define_own_property(key.clone(), new_prop)?; + } + } + Ok(obj) + } + + // 7.1.19 ToPropertyKey ( argument ) + fn value_to_property_key(&mut self, value: &Value) -> Result { + // TODO: ToPrimitive(value, STRING) + let string = self.value_to_string(value)?; + // TODO(perf): inefficient + let symbol = self.symbol_registry.intern_utf16(string.make_utf16()); + Ok(symbol.into()) + } + + // 6.2.6.5 ToPropertyDescriptor ( obj ) + fn value_to_property(&mut self, desc: &Value) -> Result { + let obj = match desc { + Value::None => unreachable!(), + Value::Object(obj) => *obj, + _ => return type_error!(), + }; + + let mut flags = PropertyFlags::empty(); + let mut value = Value::None; + + if let Some(v) = obj.get_value(&Symbol::ENUMERABLE.into()) { + if self.value_to_boolean(v) { + flags |= PropertyFlags::ENUMERABLE; + } + } + + if let Some(v) = obj.get_value(&Symbol::CONFIGURABLE.into()) { + if self.value_to_boolean(v) { + flags |= PropertyFlags::CONFIGURABLE; + } + } + + if let Some(v) = obj.get_value(&Symbol::VALUE.into()) { + value = v.clone(); + } + + if let Some(v) = obj.get_value(&Symbol::WRITABLE.into()) { + if self.value_to_boolean(v) { + flags |= PropertyFlags::WRITABLE; + } + } + + let getter = if let Some(v) = obj.get_value(&Symbol::GET.into()) { + match v { + Value::None => unreachable!(), + Value::Undefined => None, + Value::Object(getter) if getter.is_callable() => Some(*getter), + _ => return type_error!("Getter must be callable"), + } + } else { + None + }; + + let setter = if let Some(v) = obj.get_value(&Symbol::SET.into()) { + match v { + Value::None => unreachable!(), + Value::Undefined => None, + Value::Object(setter) if setter.is_callable() => Some(*setter), + _ => return type_error!("Setter must be callable"), + } + } else { + None + }; + + if getter.is_some() || setter.is_some() { + if value.is_valid() || flags.contains(PropertyFlags::WRITABLE) { + return type_error!(); + } + return runtime_todo!("TODO: accessor"); + } + + if !value.is_valid() { + value = Value::Undefined; + } + + Ok(Property::data(value, flags)) + } + pub(crate) fn create_object_object( &mut self, this: Option<&Value>, @@ -44,7 +321,7 @@ impl Runtime { // TODO(feat): NewTarget Ok(Value::Object(object)) } - Some(value) => self.value_to_object(value), + Some(value) => Ok(Value::Object(self.value_to_object(value)?)), } } } diff --git a/libs/jsruntime/src/semantics/mod.rs b/libs/jsruntime/src/semantics/mod.rs index 9ea610ab..6f6cf507 100644 --- a/libs/jsruntime/src/semantics/mod.rs +++ b/libs/jsruntime/src/semantics/mod.rs @@ -1,3 +1,5 @@ +logging::define_logger! {"bee::jsruntime::semantics"} + mod scope; use bitflags::bitflags; @@ -28,7 +30,6 @@ use crate::Runtime; use crate::Value; use crate::lambda::LambdaId; use crate::lambda::LambdaKind; -use crate::logger; use crate::types::Property; use scope::ScopeTreeBuilder; @@ -438,6 +439,7 @@ where Node::AsyncFunctionExpression(named) => self.handle_async_function_expression(named), Node::ArrowFunction => self.handle_arrow_function(), Node::AsyncArrowFunction => self.handle_async_arrow_function(), + Node::Method => self.handle_method(), Node::AwaitExpression => self.handle_await_expression(), Node::Then(expr) => self.handle_then(expr), Node::Else(expr) => self.handle_else(expr), @@ -843,6 +845,13 @@ where self.do_handle_arrow_function(false); } + fn handle_method(&mut self) { + self.end_function_scope(); + + let func = self.functions.last().unwrap(); + analysis_mut!(self).process_closure_expression(func.scope_ref, func.id, true, false); + } + fn do_handle_arrow_function(&mut self, coroutine: bool) { // TODO: An ArrowFunction does not define local variables for arguments, super, this, or // new.target. Any reference to arguments, super, this, or new.target within an @@ -1580,6 +1589,9 @@ impl FunctionAnalysis { } self.commands.push(CompileCommand::CreateDataProperty); } + PropertyDefinitionKind::Method => { + self.commands.push(CompileCommand::CreateDataProperty); + } PropertyDefinitionKind::Spread => { self.commands.push(CompileCommand::CopyDataProperties); } diff --git a/libs/jsruntime/src/types/call_context.rs b/libs/jsruntime/src/types/call_context.rs index 89efa3e6..c3657a6c 100644 --- a/libs/jsruntime/src/types/call_context.rs +++ b/libs/jsruntime/src/types/call_context.rs @@ -145,6 +145,10 @@ impl CallContext { } } + pub(crate) fn arg(&self, nth: usize) -> &Value { + self.args().get(nth).unwrap_or(&Value::Undefined) + } + pub(crate) fn args(&self) -> &[Value] { // SAFETY: `argv` is always non-null and a valid pointer to an array of `Value`s. unsafe { diff --git a/libs/jsruntime/src/types/mod.rs b/libs/jsruntime/src/types/mod.rs index 22e99989..035fd8fa 100644 --- a/libs/jsruntime/src/types/mod.rs +++ b/libs/jsruntime/src/types/mod.rs @@ -26,6 +26,7 @@ pub use lambda::into_lambda; pub use object::Object; pub use object::ObjectFlags; pub use object::Property; +pub use object::PropertyFlags; pub use object::PropertyKey; pub use promise::Promise; pub use string::String; diff --git a/libs/jsruntime/src/types/object.rs b/libs/jsruntime/src/types/object.rs index 8837b435..a8e9a5d5 100644 --- a/libs/jsruntime/src/types/object.rs +++ b/libs/jsruntime/src/types/object.rs @@ -123,7 +123,7 @@ impl Property { } /// Creates a data property. - const fn data(value: Value, flags: PropertyFlags) -> Self { + pub const fn data(value: Value, flags: PropertyFlags) -> Self { Self { value, flags: PropertyFlags::DATA.union(flags), @@ -150,7 +150,7 @@ impl Property { bitflags! { #[derive(Clone, Copy)] - struct PropertyFlags: u8 { + pub struct PropertyFlags: u8 { /// The data property (true) or the accessor property (false). const DATA = 1 << 0; @@ -249,6 +249,10 @@ impl Object { } } + pub fn prototype(&self) -> Option> { + self.prototype + } + pub fn set_prototype(&mut self, prototype: HandleMut) { self.prototype = Some(prototype); } diff --git a/libs/jsruntime/src/types/value.rs b/libs/jsruntime/src/types/value.rs index dc9886c8..8b89c18a 100644 --- a/libs/jsruntime/src/types/value.rs +++ b/libs/jsruntime/src/types/value.rs @@ -31,6 +31,9 @@ base::static_assert_eq!(size_of::(), 16); base::static_assert_eq!(align_of::(), 8); impl Value { + pub const FALSE: Self = Value::Boolean(false); + pub const TRUE: Self = Value::Boolean(true); + // There is no way to define const function to extract the discriminant of each variant. pub(crate) const KIND_NONE: u8 = 0; pub(crate) const KIND_UNDEFINED: u8 = 1; diff --git a/libs/jsruntime/tests/scripts/object_assign.js b/libs/jsruntime/tests/scripts/object_assign.js new file mode 100644 index 00000000..c53e4d66 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_assign.js @@ -0,0 +1,9 @@ +print(typeof Object.assign); ///="function" +print(Object.assign.length); ///=2 + +const target = { a: 1, b: 2 }; +const source = { b: 4, c: 5 }; +const result = Object.assign(target, source); +print(result.a); ///=1 +print(result.b); ///=4 +print(result.c); ///=5 diff --git a/libs/jsruntime/tests/scripts/object_assign_no_arg.js b/libs/jsruntime/tests/scripts/object_assign_no_arg.js new file mode 100644 index 00000000..8626b570 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_assign_no_arg.js @@ -0,0 +1,5 @@ +try { + Object.assign(); +} catch (e) { + print(e.name); ///="TypeError" +} diff --git a/libs/jsruntime/tests/scripts/object_assign_sources.js b/libs/jsruntime/tests/scripts/object_assign_sources.js new file mode 100644 index 00000000..02571e20 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_assign_sources.js @@ -0,0 +1,9 @@ +const o1 = { a: 1, b: 1, c: 1 }; +const o2 = { b: 2, c: 2 }; +const o3 = { c: 3 }; + +const obj = Object.assign({}, o1, o2, o3); + +print(obj.a); ///=1 +print(obj.b); ///=2 +print(obj.c); ///=3 diff --git a/libs/jsruntime/tests/scripts/object_assign_to_null.js b/libs/jsruntime/tests/scripts/object_assign_to_null.js new file mode 100644 index 00000000..533dcd86 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_assign_to_null.js @@ -0,0 +1,5 @@ +try { + Object.assign(null); +} catch (e) { + print(e.name); ///="TypeError" +} diff --git a/libs/jsruntime/tests/scripts/object_create.js b/libs/jsruntime/tests/scripts/object_create.js new file mode 100644 index 00000000..1c436ce0 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_create.js @@ -0,0 +1,7 @@ +print(typeof Object.create); ///="function" +print(Object.create.length); ///=2 + +const o = Object.create({}, { + a: { value: 1 }, +}); +print(o.a); ///=1 diff --git a/libs/jsruntime/tests/scripts/object_create_undefined_proto.js b/libs/jsruntime/tests/scripts/object_create_undefined_proto.js new file mode 100644 index 00000000..a4ba281e --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_create_undefined_proto.js @@ -0,0 +1,5 @@ +try { + Object.create(undefined); +} catch (e) { + print(e.name); ///="TypeError" +} diff --git a/libs/jsruntime/tests/scripts/object_define_properties.js b/libs/jsruntime/tests/scripts/object_define_properties.js new file mode 100644 index 00000000..a1aba6ba --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_properties.js @@ -0,0 +1,15 @@ +print(typeof Object.defineProperties); ///="function" +print(Object.defineProperties.length); ///=2 + +const object = {}; + +Object.defineProperties(object, { + property1: { + value: 42, + enumerable: true, + }, + property2: {}, +}); + +print(object.property1); ///=42 +print(object.property2); ///=undefined diff --git a/libs/jsruntime/tests/scripts/object_define_properties_configurable.js b/libs/jsruntime/tests/scripts/object_define_properties_configurable.js new file mode 100644 index 00000000..05656f1d --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_properties_configurable.js @@ -0,0 +1,19 @@ +const o = Object.defineProperties({}, { + a: { + value: 1, + }, + b: { + value: 2, + configurable: true, + }, + c: { + value: 3, + configurable: false, + }, +}); + +print(o.a); ///=1 +print(o.b); ///=2 +print(o.c); ///=3 + +// TODO(test): configurable diff --git a/libs/jsruntime/tests/scripts/object_define_properties_enumerable.js b/libs/jsruntime/tests/scripts/object_define_properties_enumerable.js new file mode 100644 index 00000000..1dd001c6 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_properties_enumerable.js @@ -0,0 +1,19 @@ +const o = Object.defineProperties({}, { + a: { + value: 1, + }, + b: { + value: 2, + enumerable: true, + }, + c: { + value: 3, + enumerable: false, + }, +}); + +print(o.a); ///=1 +print(o.b); ///=2 +print(o.c); ///=3 + +// TODO(test): enumerable diff --git a/libs/jsruntime/tests/scripts/object_define_properties_getter.js b/libs/jsruntime/tests/scripts/object_define_properties_getter.js new file mode 100644 index 00000000..eb9e5ffd --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_properties_getter.js @@ -0,0 +1,17 @@ +try { +const o = Object.defineProperties({}, { + a: { + get() { + return 1; + }, + }, + b: { + get: () => 2, + }, +}); + +// TODO(test): print(o.a); ///=1 +// TODO(test): print(o.b); ///=2 +} catch (e) { + print(e.name); ///="InternalError" +} diff --git a/libs/jsruntime/tests/scripts/object_define_properties_setter.js b/libs/jsruntime/tests/scripts/object_define_properties_setter.js new file mode 100644 index 00000000..74a14085 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_properties_setter.js @@ -0,0 +1,17 @@ +try { +const o = Object.defineProperties({}, { + a: { + set() { + return 1; + }, + }, + b: { + set: () => 2, + }, +}); + +// TODO(test): print(o.a); ///=1 +// TODO(test): print(o.b); ///=2 +} catch (e) { + print(e.name); ///="InternalError" +} diff --git a/libs/jsruntime/tests/scripts/object_define_properties_writable.js b/libs/jsruntime/tests/scripts/object_define_properties_writable.js new file mode 100644 index 00000000..5ab8b9f4 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_properties_writable.js @@ -0,0 +1,42 @@ +const o = Object.defineProperties({}, { + a: { + value: 1, + }, + b: { + value: 2, + writable: true, + }, + c: { + value: 3, + writable: false, + }, +}); + +print(o.a); ///=1 +print(o.b); ///=2 +print(o.c); ///=3 + +// TODO(test): writable + +try { + Object.defineProperties({}, { + a: { + writable: true, + get() {}, + } + }); +} catch (e) { + print(e.name); ///="TypeError" +} + + +try { + Object.defineProperties({}, { + a: { + writable: true, + set() {}, + } + }); +} catch (e) { + print(e.name); ///="TypeError" +} diff --git a/libs/jsruntime/tests/scripts/object_define_property.js b/libs/jsruntime/tests/scripts/object_define_property.js new file mode 100644 index 00000000..49a9439c --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_property.js @@ -0,0 +1,8 @@ +print(typeof Object.defineProperty); ///="function" +print(Object.defineProperty.length); ///=3 + +const o = Object.defineProperty({}, 'a', { + value: 1 +}); + +print(o.a); ///=1 diff --git a/libs/jsruntime/tests/scripts/object_define_property_getter_not_callable.js b/libs/jsruntime/tests/scripts/object_define_property_getter_not_callable.js new file mode 100644 index 00000000..f17250e7 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_property_getter_not_callable.js @@ -0,0 +1,15 @@ +try { + Object.defineProperty({}, 'a', { + get: null, + }); +} catch (e) { + print(e.name); ///="TypeError" +} + +try { + Object.defineProperty({}, 'a', { + get: {}, + }); +} catch (e) { + print(e.name); ///="TypeError" +} diff --git a/libs/jsruntime/tests/scripts/object_define_property_setter_not_callable.js b/libs/jsruntime/tests/scripts/object_define_property_setter_not_callable.js new file mode 100644 index 00000000..b04f250b --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_property_setter_not_callable.js @@ -0,0 +1,15 @@ +try { + Object.defineProperty({}, 'a', { + set: null, + }); +} catch (e) { + print(e.name); ///="TypeError" +} + +try { + Object.defineProperty({}, 'a', { + set: {}, + }); +} catch (e) { + print(e.name); ///="TypeError" +} diff --git a/libs/jsruntime/tests/scripts/object_define_property_value_getter.js b/libs/jsruntime/tests/scripts/object_define_property_value_getter.js new file mode 100644 index 00000000..9096d5d6 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_property_value_getter.js @@ -0,0 +1,27 @@ +try { + Object.defineProperty({}, 'a', { + value: 1, + get() {}, + }); +} catch (e) { + print(e.name); ///="TypeError" +} + +try { + Object.defineProperty({}, 'a', { + value: 1, + get: () => {}, + }); +} catch (e) { + print(e.name); ///="TypeError" +} + +try { + const o = Object.defineProperty({}, 'a', { + value: 1, + get: undefined, + }); + print(o.a); ///=1 +} catch (e) { + print(e.name); +} diff --git a/libs/jsruntime/tests/scripts/object_define_property_value_setter.js b/libs/jsruntime/tests/scripts/object_define_property_value_setter.js new file mode 100644 index 00000000..357030eb --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_define_property_value_setter.js @@ -0,0 +1,27 @@ +try { + Object.defineProperty({}, 'a', { + value: 1, + set() {}, + }); +} catch (e) { + print(e.name); ///="TypeError" +} + +try { + Object.defineProperty({}, 'a', { + value: 1, + set: () => {}, + }); +} catch (e) { + print(e.name); ///="TypeError" +} + +try { + const o = Object.defineProperty({}, 'a', { + value: 1, + set: undefined, + }); + print(o.a); ///=1 +} catch (e) { + print(e.name); +} diff --git a/libs/jsruntime/tests/scripts/object_has_own_property.js b/libs/jsruntime/tests/scripts/object_has_own_property.js new file mode 100644 index 00000000..2282ad64 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_has_own_property.js @@ -0,0 +1,6 @@ +print(typeof Object.prototype.hasOwnProperty); ///="function" +print(Object.prototype.hasOwnProperty.length); ///=1 + +const o = { a: 1 }; +print(o.hasOwnProperty('a')); ///=true +print(o.hasOwnProperty('hasOwnProperty')); ///=false diff --git a/libs/jsruntime/tests/scripts/object_is_prototype_of.js b/libs/jsruntime/tests/scripts/object_is_prototype_of.js new file mode 100644 index 00000000..e451a1e7 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_is_prototype_of.js @@ -0,0 +1,6 @@ +print(typeof Object.prototype.isPrototypeOf); ///="function" +print(Object.prototype.isPrototypeOf.length); ///=1 + +const o = {}; +print(Object.prototype.isPrototypeOf(o)); ///=true +print(String.prototype.isPrototypeOf(o)); ///=false diff --git a/libs/jsruntime/tests/scripts/object_literal_method.js b/libs/jsruntime/tests/scripts/object_literal_method.js new file mode 100644 index 00000000..301264f4 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_literal_method.js @@ -0,0 +1,7 @@ +const o = { + print(a) { + print(a); + }, +}; + +o.print(1); ///=1 diff --git a/libs/jsruntime/tests/scripts/object_property_is_enumerable.js b/libs/jsruntime/tests/scripts/object_property_is_enumerable.js new file mode 100644 index 00000000..4e634a0f --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_property_is_enumerable.js @@ -0,0 +1,7 @@ +print(typeof Object.prototype.propertyIsEnumerable); ///="function" +print(Object.prototype.propertyIsEnumerable.length); ///=1 + +const o = { a: 1 }; +print(o.propertyIsEnumerable('a')); ///=true +print(o.propertyIsEnumerable('b')); ///=false +print(Object.propertyIsEnumerable('prototype')); ///=false diff --git a/libs/jsruntime/tests/scripts/object_to_string.js b/libs/jsruntime/tests/scripts/object_to_string.js new file mode 100644 index 00000000..a3ae89f9 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_to_string.js @@ -0,0 +1,5 @@ +print(typeof Object.prototype.toString); ///="function" +print(Object.prototype.toString.length); ///=0 + +print({}.toString()); ///="[object Object]" +// TODO(test): add tests diff --git a/libs/jsruntime/tests/scripts/object_value_of.js b/libs/jsruntime/tests/scripts/object_value_of.js new file mode 100644 index 00000000..55118506 --- /dev/null +++ b/libs/jsruntime/tests/scripts/object_value_of.js @@ -0,0 +1,4 @@ +print(typeof Object.prototype.valueOf); ///="function" +print(Object.prototype.valueOf.length); ///=0 + +print({}.valueOf()); ///=object