| OLD | NEW |
| 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 622 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 ZoneList<T*>* elements() { return list_; } | 633 ZoneList<T*>* elements() { return list_; } |
| 634 T* at(int index) { return list_->at(index); } | 634 T* at(int index) { return list_->at(index); } |
| 635 private: | 635 private: |
| 636 ZoneList<T*>* list_; | 636 ZoneList<T*>* list_; |
| 637 }; | 637 }; |
| 638 | 638 |
| 639 | 639 |
| 640 // Allocation macro that should be used to allocate objects that must | 640 // Allocation macro that should be used to allocate objects that must |
| 641 // only be allocated in real parsing mode. Note that in preparse mode | 641 // only be allocated in real parsing mode. Note that in preparse mode |
| 642 // not only is the syntax tree not created but the constructor | 642 // not only is the syntax tree not created but the constructor |
| 643 // arguments are not evaulated. | 643 // arguments are not evaluated. |
| 644 #define NEW(expr) (is_pre_parsing_ ? NULL : new expr) | 644 #define NEW(expr) (is_pre_parsing_ ? NULL : new expr) |
| 645 | 645 |
| 646 | 646 |
| 647 class ParserFactory BASE_EMBEDDED { | 647 class ParserFactory BASE_EMBEDDED { |
| 648 public: | 648 public: |
| 649 explicit ParserFactory(bool is_pre_parsing) : | 649 explicit ParserFactory(bool is_pre_parsing) : |
| 650 is_pre_parsing_(is_pre_parsing) { } | 650 is_pre_parsing_(is_pre_parsing) { } |
| 651 | 651 |
| 652 virtual ~ParserFactory() { } | 652 virtual ~ParserFactory() { } |
| 653 | 653 |
| (...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1085 FunctionLiteral* Parser::ParseProgram(Handle<String> source, | 1085 FunctionLiteral* Parser::ParseProgram(Handle<String> source, |
| 1086 unibrow::CharacterStream* stream, | 1086 unibrow::CharacterStream* stream, |
| 1087 bool in_global_context) { | 1087 bool in_global_context) { |
| 1088 ZoneScope zone_scope(DONT_DELETE_ON_EXIT); | 1088 ZoneScope zone_scope(DONT_DELETE_ON_EXIT); |
| 1089 | 1089 |
| 1090 StatsRateScope timer(&Counters::parse); | 1090 StatsRateScope timer(&Counters::parse); |
| 1091 StringShape shape(*source); | 1091 StringShape shape(*source); |
| 1092 Counters::total_parse_size.Increment(source->length(shape)); | 1092 Counters::total_parse_size.Increment(source->length(shape)); |
| 1093 | 1093 |
| 1094 // Initialize parser state. | 1094 // Initialize parser state. |
| 1095 source->TryFlatten(shape); | 1095 source->TryFlattenIfNotFlat(shape); |
| 1096 scanner_.Init(source, stream, 0); | 1096 scanner_.Init(source, stream, 0); |
| 1097 ASSERT(target_stack_ == NULL); | 1097 ASSERT(target_stack_ == NULL); |
| 1098 | 1098 |
| 1099 // Compute the parsing mode. | 1099 // Compute the parsing mode. |
| 1100 mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY; | 1100 mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY; |
| 1101 if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY; | 1101 if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY; |
| 1102 | 1102 |
| 1103 Scope::Type type = | 1103 Scope::Type type = |
| 1104 in_global_context | 1104 in_global_context |
| 1105 ? Scope::GLOBAL_SCOPE | 1105 ? Scope::GLOBAL_SCOPE |
| 1106 : Scope::EVAL_SCOPE; | 1106 : Scope::EVAL_SCOPE; |
| 1107 Handle<String> no_name = factory()->EmptySymbol(); | 1107 Handle<String> no_name = factory()->EmptySymbol(); |
| 1108 | 1108 |
| 1109 FunctionLiteral* result = NULL; | 1109 FunctionLiteral* result = NULL; |
| 1110 { Scope* scope = factory()->NewScope(top_scope_, type, inside_with()); | 1110 { Scope* scope = factory()->NewScope(top_scope_, type, inside_with()); |
| 1111 LexicalScope lexical_scope(this, scope); | 1111 LexicalScope lexical_scope(this, scope); |
| 1112 TemporaryScope temp_scope(this); | 1112 TemporaryScope temp_scope(this); |
| 1113 ZoneListWrapper<Statement> body(16); | 1113 ZoneListWrapper<Statement> body(16); |
| 1114 bool ok = true; | 1114 bool ok = true; |
| 1115 ParseSourceElements(&body, Token::EOS, &ok); | 1115 ParseSourceElements(&body, Token::EOS, &ok); |
| 1116 if (ok) { | 1116 if (ok) { |
| 1117 result = NEW(FunctionLiteral(no_name, top_scope_, | 1117 result = NEW(FunctionLiteral(no_name, top_scope_, |
| 1118 body.elements(), | 1118 body.elements(), |
| 1119 temp_scope.materialized_literal_count(), | 1119 temp_scope.materialized_literal_count(), |
| 1120 temp_scope.contains_array_literal(), | 1120 temp_scope.contains_array_literal(), |
| 1121 temp_scope.expected_property_count(), | 1121 temp_scope.expected_property_count(), |
| 1122 0, 0, source->length(shape), false)); | 1122 0, 0, source->length(), false)); |
| 1123 } else if (scanner().stack_overflow()) { | 1123 } else if (scanner().stack_overflow()) { |
| 1124 Top::StackOverflow(); | 1124 Top::StackOverflow(); |
| 1125 } | 1125 } |
| 1126 } | 1126 } |
| 1127 | 1127 |
| 1128 // Make sure the target stack is empty. | 1128 // Make sure the target stack is empty. |
| 1129 ASSERT(target_stack_ == NULL); | 1129 ASSERT(target_stack_ == NULL); |
| 1130 | 1130 |
| 1131 // If there was a syntax error we have to get rid of the AST | 1131 // If there was a syntax error we have to get rid of the AST |
| 1132 // and it is not safe to do so before the scope has been deleted. | 1132 // and it is not safe to do so before the scope has been deleted. |
| 1133 if (result == NULL) zone_scope.DeleteOnExit(); | 1133 if (result == NULL) zone_scope.DeleteOnExit(); |
| 1134 return result; | 1134 return result; |
| 1135 } | 1135 } |
| 1136 | 1136 |
| 1137 | 1137 |
| 1138 FunctionLiteral* Parser::ParseLazy(Handle<String> source, | 1138 FunctionLiteral* Parser::ParseLazy(Handle<String> source, |
| 1139 Handle<String> name, | 1139 Handle<String> name, |
| 1140 int start_position, | 1140 int start_position, |
| 1141 bool is_expression) { | 1141 bool is_expression) { |
| 1142 ZoneScope zone_scope(DONT_DELETE_ON_EXIT); | 1142 ZoneScope zone_scope(DONT_DELETE_ON_EXIT); |
| 1143 StatsRateScope timer(&Counters::parse_lazy); | 1143 StatsRateScope timer(&Counters::parse_lazy); |
| 1144 source->TryFlatten(StringShape(*source)); | 1144 source->TryFlattenIfNotFlat(StringShape(*source)); |
| 1145 StringShape shape(*source); | 1145 StringShape shape(*source); |
| 1146 Counters::total_parse_size.Increment(source->length(shape)); | 1146 Counters::total_parse_size.Increment(source->length(shape)); |
| 1147 SafeStringInputBuffer buffer(source.location()); | 1147 SafeStringInputBuffer buffer(source.location()); |
| 1148 | 1148 |
| 1149 // Initialize parser state. | 1149 // Initialize parser state. |
| 1150 scanner_.Init(source, &buffer, start_position); | 1150 scanner_.Init(source, &buffer, start_position); |
| 1151 ASSERT(target_stack_ == NULL); | 1151 ASSERT(target_stack_ == NULL); |
| 1152 mode_ = PARSE_EAGERLY; | 1152 mode_ = PARSE_EAGERLY; |
| 1153 | 1153 |
| 1154 // Place holder for the result. | 1154 // Place holder for the result. |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1247 // LabelledStatement | 1247 // LabelledStatement |
| 1248 // SwitchStatement | 1248 // SwitchStatement |
| 1249 // ThrowStatement | 1249 // ThrowStatement |
| 1250 // TryStatement | 1250 // TryStatement |
| 1251 // DebuggerStatement | 1251 // DebuggerStatement |
| 1252 | 1252 |
| 1253 // Note: Since labels can only be used by 'break' and 'continue' | 1253 // Note: Since labels can only be used by 'break' and 'continue' |
| 1254 // statements, which themselves are only valid within blocks, | 1254 // statements, which themselves are only valid within blocks, |
| 1255 // iterations or 'switch' statements (i.e., BreakableStatements), | 1255 // iterations or 'switch' statements (i.e., BreakableStatements), |
| 1256 // labels can be simply ignored in all other cases; except for | 1256 // labels can be simply ignored in all other cases; except for |
| 1257 // trivial labelled break statements 'label: break label' which is | 1257 // trivial labeled break statements 'label: break label' which is |
| 1258 // parsed into an empty statement. | 1258 // parsed into an empty statement. |
| 1259 | 1259 |
| 1260 // Keep the source position of the statement | 1260 // Keep the source position of the statement |
| 1261 int statement_pos = scanner().peek_location().beg_pos; | 1261 int statement_pos = scanner().peek_location().beg_pos; |
| 1262 Statement* stmt = NULL; | 1262 Statement* stmt = NULL; |
| 1263 switch (peek()) { | 1263 switch (peek()) { |
| 1264 case Token::LBRACE: | 1264 case Token::LBRACE: |
| 1265 return ParseBlock(labels, ok); | 1265 return ParseBlock(labels, ok); |
| 1266 | 1266 |
| 1267 case Token::CONST: // fall through | 1267 case Token::CONST: // fall through |
| (...skipping 594 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1862 // BreakStatement :: | 1862 // BreakStatement :: |
| 1863 // 'break' Identifier? ';' | 1863 // 'break' Identifier? ';' |
| 1864 | 1864 |
| 1865 Expect(Token::BREAK, CHECK_OK); | 1865 Expect(Token::BREAK, CHECK_OK); |
| 1866 Handle<String> label; | 1866 Handle<String> label; |
| 1867 Token::Value tok = peek(); | 1867 Token::Value tok = peek(); |
| 1868 if (!scanner_.has_line_terminator_before_next() && | 1868 if (!scanner_.has_line_terminator_before_next() && |
| 1869 tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { | 1869 tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) { |
| 1870 label = ParseIdentifier(CHECK_OK); | 1870 label = ParseIdentifier(CHECK_OK); |
| 1871 } | 1871 } |
| 1872 // Parse labelled break statements that target themselves into | 1872 // Parse labeled break statements that target themselves into |
| 1873 // empty statements, e.g. 'l1: l2: l3: break l2;' | 1873 // empty statements, e.g. 'l1: l2: l3: break l2;' |
| 1874 if (!label.is_null() && ContainsLabel(labels, label)) { | 1874 if (!label.is_null() && ContainsLabel(labels, label)) { |
| 1875 return factory()->EmptyStatement(); | 1875 return factory()->EmptyStatement(); |
| 1876 } | 1876 } |
| 1877 BreakableStatement* target = NULL; | 1877 BreakableStatement* target = NULL; |
| 1878 if (!is_pre_parsing_) { | 1878 if (!is_pre_parsing_) { |
| 1879 target = LookupBreakTarget(label, CHECK_OK); | 1879 target = LookupBreakTarget(label, CHECK_OK); |
| 1880 if (target == NULL) { | 1880 if (target == NULL) { |
| 1881 // Illegal break statement. To be consistent with KJS we delay | 1881 // Illegal break statement. To be consistent with KJS we delay |
| 1882 // reporting of the syntax error until runtime. | 1882 // reporting of the syntax error until runtime. |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2055 *ok = false; | 2055 *ok = false; |
| 2056 return NULL; | 2056 return NULL; |
| 2057 } | 2057 } |
| 2058 Expression* exception = ParseExpression(true, CHECK_OK); | 2058 Expression* exception = ParseExpression(true, CHECK_OK); |
| 2059 ExpectSemicolon(CHECK_OK); | 2059 ExpectSemicolon(CHECK_OK); |
| 2060 | 2060 |
| 2061 return NEW(ExpressionStatement(new Throw(exception, pos))); | 2061 return NEW(ExpressionStatement(new Throw(exception, pos))); |
| 2062 } | 2062 } |
| 2063 | 2063 |
| 2064 | 2064 |
| 2065 Expression* Parser::MakeCatchContext(Handle<String> id, VariableProxy* value) { | |
| 2066 ZoneListWrapper<ObjectLiteral::Property> properties = | |
| 2067 factory()->NewList<ObjectLiteral::Property>(1); | |
| 2068 Literal* key = NEW(Literal(id)); | |
| 2069 ObjectLiteral::Property* property = NEW(ObjectLiteral::Property(key, value)); | |
| 2070 properties.Add(property); | |
| 2071 | |
| 2072 // This must be called always, even during pre-parsing! | |
| 2073 // (Computation of literal index must happen before pre-parse bailout.) | |
| 2074 int literal_index = temp_scope_->NextMaterializedLiteralIndex(); | |
| 2075 if (is_pre_parsing_) { | |
| 2076 return NULL; | |
| 2077 } | |
| 2078 | |
| 2079 // Construct the expression for calling Runtime::CreateObjectLiteral | |
| 2080 // with the literal array as argument. | |
| 2081 Handle<FixedArray> constant_properties = Factory::empty_fixed_array(); | |
| 2082 ZoneList<Expression*>* arguments = new ZoneList<Expression*>(1); | |
| 2083 arguments->Add(new Literal(constant_properties)); | |
| 2084 | |
| 2085 return new ObjectLiteral(constant_properties, | |
| 2086 properties.elements(), | |
| 2087 literal_index); | |
| 2088 } | |
| 2089 | |
| 2090 | |
| 2091 TryStatement* Parser::ParseTryStatement(bool* ok) { | 2065 TryStatement* Parser::ParseTryStatement(bool* ok) { |
| 2092 // TryStatement :: | 2066 // TryStatement :: |
| 2093 // 'try' Block Catch | 2067 // 'try' Block Catch |
| 2094 // 'try' Block Finally | 2068 // 'try' Block Finally |
| 2095 // 'try' Block Catch Finally | 2069 // 'try' Block Catch Finally |
| 2096 // | 2070 // |
| 2097 // Catch :: | 2071 // Catch :: |
| 2098 // 'catch' '(' Identifier ')' Block | 2072 // 'catch' '(' Identifier ')' Block |
| 2099 // | 2073 // |
| 2100 // Finally :: | 2074 // Finally :: |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2133 Consume(Token::CATCH); | 2107 Consume(Token::CATCH); |
| 2134 | 2108 |
| 2135 Expect(Token::LPAREN, CHECK_OK); | 2109 Expect(Token::LPAREN, CHECK_OK); |
| 2136 Handle<String> name = ParseIdentifier(CHECK_OK); | 2110 Handle<String> name = ParseIdentifier(CHECK_OK); |
| 2137 Expect(Token::RPAREN, CHECK_OK); | 2111 Expect(Token::RPAREN, CHECK_OK); |
| 2138 | 2112 |
| 2139 if (peek() == Token::LBRACE) { | 2113 if (peek() == Token::LBRACE) { |
| 2140 // Allocate a temporary for holding the finally state while | 2114 // Allocate a temporary for holding the finally state while |
| 2141 // executing the finally block. | 2115 // executing the finally block. |
| 2142 catch_var = top_scope_->NewTemporary(Factory::catch_var_symbol()); | 2116 catch_var = top_scope_->NewTemporary(Factory::catch_var_symbol()); |
| 2143 Expression* obj = MakeCatchContext(name, catch_var); | 2117 Literal* name_literal = NEW(Literal(name)); |
| 2118 Expression* obj = NEW(CatchExtensionObject(name_literal, catch_var)); |
| 2144 { Target target(this, &catch_collector); | 2119 { Target target(this, &catch_collector); |
| 2145 catch_block = WithHelper(obj, NULL, true, CHECK_OK); | 2120 catch_block = WithHelper(obj, NULL, true, CHECK_OK); |
| 2146 } | 2121 } |
| 2147 } else { | 2122 } else { |
| 2148 Expect(Token::LBRACE, CHECK_OK); | 2123 Expect(Token::LBRACE, CHECK_OK); |
| 2149 } | 2124 } |
| 2150 | 2125 |
| 2151 tok = peek(); | 2126 tok = peek(); |
| 2152 } | 2127 } |
| 2153 | 2128 |
| (...skipping 941 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3095 // value for COMPUTED properties, the real value is filled in at | 3070 // value for COMPUTED properties, the real value is filled in at |
| 3096 // runtime. The enumeration order is maintained. | 3071 // runtime. The enumeration order is maintained. |
| 3097 Handle<Object> key = property->key()->handle(); | 3072 Handle<Object> key = property->key()->handle(); |
| 3098 Literal* literal = GetBoilerplateValue(property); | 3073 Literal* literal = GetBoilerplateValue(property); |
| 3099 | 3074 |
| 3100 // Add name, value pair to the fixed array. | 3075 // Add name, value pair to the fixed array. |
| 3101 constant_properties->set(position++, *key); | 3076 constant_properties->set(position++, *key); |
| 3102 constant_properties->set(position++, *literal->handle()); | 3077 constant_properties->set(position++, *literal->handle()); |
| 3103 } | 3078 } |
| 3104 | 3079 |
| 3105 // Construct the expression for calling Runtime::CreateObjectLiteral | |
| 3106 // with the literal array as argument. | |
| 3107 ZoneList<Expression*>* arguments = new ZoneList<Expression*>(1); | |
| 3108 arguments->Add(new Literal(constant_properties)); | |
| 3109 return new ObjectLiteral(constant_properties, | 3080 return new ObjectLiteral(constant_properties, |
| 3110 properties.elements(), | 3081 properties.elements(), |
| 3111 literal_index); | 3082 literal_index); |
| 3112 } | 3083 } |
| 3113 | 3084 |
| 3114 | 3085 |
| 3115 Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) { | 3086 Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) { |
| 3116 if (!scanner_.ScanRegExpPattern(seen_equal)) { | 3087 if (!scanner_.ScanRegExpPattern(seen_equal)) { |
| 3117 Next(); | 3088 Next(); |
| 3118 ReportMessage("unterminated_regexp", Vector<const char*>::empty()); | 3089 ReportMessage("unterminated_regexp", Vector<const char*>::empty()); |
| (...skipping 954 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4073 case 'r': | 4044 case 'r': |
| 4074 Advance(); | 4045 Advance(); |
| 4075 return '\r'; | 4046 return '\r'; |
| 4076 case 't': | 4047 case 't': |
| 4077 Advance(); | 4048 Advance(); |
| 4078 return '\t'; | 4049 return '\t'; |
| 4079 case 'v': | 4050 case 'v': |
| 4080 Advance(); | 4051 Advance(); |
| 4081 return '\v'; | 4052 return '\v'; |
| 4082 case 'c': | 4053 case 'c': |
| 4054 Advance(); |
| 4083 return ParseControlLetterEscape(); | 4055 return ParseControlLetterEscape(); |
| 4084 case '0': case '1': case '2': case '3': case '4': case '5': | 4056 case '0': case '1': case '2': case '3': case '4': case '5': |
| 4085 case '6': case '7': | 4057 case '6': case '7': |
| 4086 // For compatibility, we interpret a decimal escape that isn't | 4058 // For compatibility, we interpret a decimal escape that isn't |
| 4087 // a back reference (and therefore either \0 or not valid according | 4059 // a back reference (and therefore either \0 or not valid according |
| 4088 // to the specification) as a 1..3 digit octal character code. | 4060 // to the specification) as a 1..3 digit octal character code. |
| 4089 return ParseOctalLiteral(); | 4061 return ParseOctalLiteral(); |
| 4090 case 'x': { | 4062 case 'x': { |
| 4091 Advance(); | 4063 Advance(); |
| 4092 uc32 value; | 4064 uc32 value; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4169 | 4141 |
| 4170 if (type == '(') { | 4142 if (type == '(') { |
| 4171 RegExpCapture* capture = new RegExpCapture(body, capture_index); | 4143 RegExpCapture* capture = new RegExpCapture(body, capture_index); |
| 4172 captures_->at(capture_index - 1) = capture; | 4144 captures_->at(capture_index - 1) = capture; |
| 4173 return capture; | 4145 return capture; |
| 4174 } else if (type == ':') { | 4146 } else if (type == ':') { |
| 4175 return body; | 4147 return body; |
| 4176 } else { | 4148 } else { |
| 4177 ASSERT(type == '=' || type == '!'); | 4149 ASSERT(type == '=' || type == '!'); |
| 4178 bool is_positive = (type == '='); | 4150 bool is_positive = (type == '='); |
| 4179 return new RegExpLookahead(body, is_positive); | 4151 return new RegExpLookahead(body, |
| 4152 is_positive, |
| 4153 end_capture_index - capture_index, |
| 4154 capture_index); |
| 4180 } | 4155 } |
| 4181 } | 4156 } |
| 4182 | 4157 |
| 4183 | 4158 |
| 4184 CharacterRange RegExpParser::ParseClassAtom(uc16* char_class) { | 4159 CharacterRange RegExpParser::ParseClassAtom(uc16* char_class) { |
| 4185 ASSERT_EQ(0, *char_class); | 4160 ASSERT_EQ(0, *char_class); |
| 4186 uc32 first = current(); | 4161 uc32 first = current(); |
| 4187 if (first == '\\') { | 4162 if (first == '\\') { |
| 4188 switch (Next()) { | 4163 switch (Next()) { |
| 4189 case 'w': case 'W': case 'd': case 'D': case 's': case 'S': { | 4164 case 'w': case 'W': case 'd': case 'D': case 's': case 'S': { |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4377 start_position, | 4352 start_position, |
| 4378 is_expression); | 4353 is_expression); |
| 4379 return result; | 4354 return result; |
| 4380 } | 4355 } |
| 4381 | 4356 |
| 4382 | 4357 |
| 4383 #undef NEW | 4358 #undef NEW |
| 4384 | 4359 |
| 4385 | 4360 |
| 4386 } } // namespace v8::internal | 4361 } } // namespace v8::internal |
| OLD | NEW |