| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
| 6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
| 7 #include "src/code-factory.h" | 7 #include "src/code-factory.h" |
| 8 #include "src/code-stub-assembler.h" | 8 #include "src/code-stub-assembler.h" |
| 9 #include "src/regexp/regexp-utils.h" | 9 #include "src/regexp/regexp-utils.h" |
| 10 | 10 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 | 45 |
| 46 void BranchIfSimpleOneByteStringInstanceType(Node* instance_type, | 46 void BranchIfSimpleOneByteStringInstanceType(Node* instance_type, |
| 47 Label* if_true, | 47 Label* if_true, |
| 48 Label* if_false) { | 48 Label* if_false) { |
| 49 const int kMask = kStringRepresentationMask | kStringEncodingMask; | 49 const int kMask = kStringRepresentationMask | kStringEncodingMask; |
| 50 const int kType = kOneByteStringTag | kSeqStringTag; | 50 const int kType = kOneByteStringTag | kSeqStringTag; |
| 51 Branch(Word32Equal(Word32And(instance_type, Int32Constant(kMask)), | 51 Branch(Word32Equal(Word32And(instance_type, Int32Constant(kMask)), |
| 52 Int32Constant(kType)), | 52 Int32Constant(kType)), |
| 53 if_true, if_false); | 53 if_true, if_false); |
| 54 } | 54 } |
| 55 |
| 56 void GenerateStringEqual(ResultMode mode); |
| 57 void GenerateStringRelationalComparison(RelationalComparisonMode mode); |
| 58 |
| 59 Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit); |
| 60 |
| 61 Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, |
| 62 UnicodeEncoding encoding); |
| 55 }; | 63 }; |
| 56 | 64 |
| 57 namespace { | 65 void StringBuiltinsAssembler::GenerateStringEqual(ResultMode mode) { |
| 58 | |
| 59 void GenerateStringEqual(CodeStubAssembler* assembler, ResultMode mode) { | |
| 60 // Here's pseudo-code for the algorithm below in case of kDontNegateResult | 66 // Here's pseudo-code for the algorithm below in case of kDontNegateResult |
| 61 // mode; for kNegateResult mode we properly negate the result. | 67 // mode; for kNegateResult mode we properly negate the result. |
| 62 // | 68 // |
| 63 // if (lhs == rhs) return true; | 69 // if (lhs == rhs) return true; |
| 64 // if (lhs->length() != rhs->length()) return false; | 70 // if (lhs->length() != rhs->length()) return false; |
| 65 // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) { | 71 // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) { |
| 66 // return false; | 72 // return false; |
| 67 // } | 73 // } |
| 68 // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) { | 74 // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) { |
| 69 // for (i = 0; i != lhs->length(); ++i) { | 75 // for (i = 0; i != lhs->length(); ++i) { |
| 70 // if (lhs[i] != rhs[i]) return false; | 76 // if (lhs[i] != rhs[i]) return false; |
| 71 // } | 77 // } |
| 72 // return true; | 78 // return true; |
| 73 // } | 79 // } |
| 74 // return %StringEqual(lhs, rhs); | 80 // return %StringEqual(lhs, rhs); |
| 75 | 81 |
| 76 typedef CodeStubAssembler::Label Label; | 82 Node* lhs = Parameter(0); |
| 77 typedef compiler::Node Node; | 83 Node* rhs = Parameter(1); |
| 78 typedef CodeStubAssembler::Variable Variable; | 84 Node* context = Parameter(2); |
| 79 | 85 |
| 80 Node* lhs = assembler->Parameter(0); | 86 Label if_equal(this), if_notequal(this); |
| 81 Node* rhs = assembler->Parameter(1); | |
| 82 Node* context = assembler->Parameter(2); | |
| 83 | |
| 84 Label if_equal(assembler), if_notequal(assembler); | |
| 85 | 87 |
| 86 // Fast check to see if {lhs} and {rhs} refer to the same String object. | 88 // Fast check to see if {lhs} and {rhs} refer to the same String object. |
| 87 Label if_same(assembler), if_notsame(assembler); | 89 GotoIf(WordEqual(lhs, rhs), &if_equal); |
| 88 assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame); | |
| 89 | 90 |
| 90 assembler->Bind(&if_same); | 91 // Load the length of {lhs} and {rhs}. |
| 91 assembler->Goto(&if_equal); | 92 Node* lhs_length = LoadStringLength(lhs); |
| 93 Node* rhs_length = LoadStringLength(rhs); |
| 92 | 94 |
| 93 assembler->Bind(&if_notsame); | 95 // Strings with different lengths cannot be equal. |
| 96 GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal); |
| 97 |
| 98 // Load instance types of {lhs} and {rhs}. |
| 99 Node* lhs_instance_type = LoadInstanceType(lhs); |
| 100 Node* rhs_instance_type = LoadInstanceType(rhs); |
| 101 |
| 102 // Combine the instance types into a single 16-bit value, so we can check |
| 103 // both of them at once. |
| 104 Node* both_instance_types = Word32Or( |
| 105 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8))); |
| 106 |
| 107 // Check if both {lhs} and {rhs} are internalized. Since we already know |
| 108 // that they're not the same object, they're not equal in that case. |
| 109 int const kBothInternalizedMask = |
| 110 kIsNotInternalizedMask | (kIsNotInternalizedMask << 8); |
| 111 int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8); |
| 112 GotoIf(Word32Equal(Word32And(both_instance_types, |
| 113 Int32Constant(kBothInternalizedMask)), |
| 114 Int32Constant(kBothInternalizedTag)), |
| 115 &if_notequal); |
| 116 |
| 117 // Check that both {lhs} and {rhs} are flat one-byte strings. |
| 118 int const kBothSeqOneByteStringMask = |
| 119 kStringEncodingMask | kStringRepresentationMask | |
| 120 ((kStringEncodingMask | kStringRepresentationMask) << 8); |
| 121 int const kBothSeqOneByteStringTag = |
| 122 kOneByteStringTag | kSeqStringTag | |
| 123 ((kOneByteStringTag | kSeqStringTag) << 8); |
| 124 Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this); |
| 125 Branch(Word32Equal(Word32And(both_instance_types, |
| 126 Int32Constant(kBothSeqOneByteStringMask)), |
| 127 Int32Constant(kBothSeqOneByteStringTag)), |
| 128 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings); |
| 129 |
| 130 Bind(&if_bothonebyteseqstrings); |
| 94 { | 131 { |
| 95 // The {lhs} and {rhs} don't refer to the exact same String object. | 132 // Compute the effective offset of the first character. |
| 133 Node* begin = |
| 134 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag); |
| 96 | 135 |
| 97 // Load the length of {lhs} and {rhs}. | 136 // Compute the first offset after the string from the length. |
| 98 Node* lhs_length = assembler->LoadStringLength(lhs); | 137 Node* end = IntPtrAdd(begin, SmiUntag(lhs_length)); |
| 99 Node* rhs_length = assembler->LoadStringLength(rhs); | |
| 100 | 138 |
| 101 // Check if the lengths of {lhs} and {rhs} are equal. | 139 // Loop over the {lhs} and {rhs} strings to see if they are equal. |
| 102 Label if_lengthisequal(assembler), if_lengthisnotequal(assembler); | 140 Variable var_offset(this, MachineType::PointerRepresentation()); |
| 103 assembler->Branch(assembler->WordEqual(lhs_length, rhs_length), | 141 Label loop(this, &var_offset); |
| 104 &if_lengthisequal, &if_lengthisnotequal); | 142 var_offset.Bind(begin); |
| 143 Goto(&loop); |
| 144 Bind(&loop); |
| 145 { |
| 146 // If {offset} equals {end}, no difference was found, so the |
| 147 // strings are equal. |
| 148 Node* offset = var_offset.value(); |
| 149 GotoIf(WordEqual(offset, end), &if_equal); |
| 105 | 150 |
| 106 assembler->Bind(&if_lengthisequal); | 151 // Load the next characters from {lhs} and {rhs}. |
| 107 { | 152 Node* lhs_value = Load(MachineType::Uint8(), lhs, offset); |
| 108 // Load instance types of {lhs} and {rhs}. | 153 Node* rhs_value = Load(MachineType::Uint8(), rhs, offset); |
| 109 Node* lhs_instance_type = assembler->LoadInstanceType(lhs); | |
| 110 Node* rhs_instance_type = assembler->LoadInstanceType(rhs); | |
| 111 | 154 |
| 112 // Combine the instance types into a single 16-bit value, so we can check | 155 // Check if the characters match. |
| 113 // both of them at once. | 156 GotoIf(Word32NotEqual(lhs_value, rhs_value), &if_notequal); |
| 114 Node* both_instance_types = assembler->Word32Or( | |
| 115 lhs_instance_type, | |
| 116 assembler->Word32Shl(rhs_instance_type, assembler->Int32Constant(8))); | |
| 117 | 157 |
| 118 // Check if both {lhs} and {rhs} are internalized. | 158 // Advance to next character. |
| 119 int const kBothInternalizedMask = | 159 var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1))); |
| 120 kIsNotInternalizedMask | (kIsNotInternalizedMask << 8); | 160 Goto(&loop); |
| 121 int const kBothInternalizedTag = | 161 } |
| 122 kInternalizedTag | (kInternalizedTag << 8); | |
| 123 Label if_bothinternalized(assembler), if_notbothinternalized(assembler); | |
| 124 assembler->Branch(assembler->Word32Equal( | |
| 125 assembler->Word32And(both_instance_types, | |
| 126 assembler->Int32Constant( | |
| 127 kBothInternalizedMask)), | |
| 128 assembler->Int32Constant(kBothInternalizedTag)), | |
| 129 &if_bothinternalized, &if_notbothinternalized); | |
| 130 | |
| 131 assembler->Bind(&if_bothinternalized); | |
| 132 { | |
| 133 // Fast negative check for internalized-to-internalized equality. | |
| 134 assembler->Goto(&if_notequal); | |
| 135 } | |
| 136 | |
| 137 assembler->Bind(&if_notbothinternalized); | |
| 138 { | |
| 139 // Check that both {lhs} and {rhs} are flat one-byte strings. | |
| 140 int const kBothSeqOneByteStringMask = | |
| 141 kStringEncodingMask | kStringRepresentationMask | | |
| 142 ((kStringEncodingMask | kStringRepresentationMask) << 8); | |
| 143 int const kBothSeqOneByteStringTag = | |
| 144 kOneByteStringTag | kSeqStringTag | | |
| 145 ((kOneByteStringTag | kSeqStringTag) << 8); | |
| 146 Label if_bothonebyteseqstrings(assembler), | |
| 147 if_notbothonebyteseqstrings(assembler); | |
| 148 assembler->Branch( | |
| 149 assembler->Word32Equal( | |
| 150 assembler->Word32And( | |
| 151 both_instance_types, | |
| 152 assembler->Int32Constant(kBothSeqOneByteStringMask)), | |
| 153 assembler->Int32Constant(kBothSeqOneByteStringTag)), | |
| 154 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings); | |
| 155 | |
| 156 assembler->Bind(&if_bothonebyteseqstrings); | |
| 157 { | |
| 158 // Compute the effective offset of the first character. | |
| 159 Node* begin = assembler->IntPtrConstant( | |
| 160 SeqOneByteString::kHeaderSize - kHeapObjectTag); | |
| 161 | |
| 162 // Compute the first offset after the string from the length. | |
| 163 Node* end = | |
| 164 assembler->IntPtrAdd(begin, assembler->SmiUntag(lhs_length)); | |
| 165 | |
| 166 // Loop over the {lhs} and {rhs} strings to see if they are equal. | |
| 167 Variable var_offset(assembler, MachineType::PointerRepresentation()); | |
| 168 Label loop(assembler, &var_offset); | |
| 169 var_offset.Bind(begin); | |
| 170 assembler->Goto(&loop); | |
| 171 assembler->Bind(&loop); | |
| 172 { | |
| 173 // Check if {offset} equals {end}. | |
| 174 Node* offset = var_offset.value(); | |
| 175 Label if_done(assembler), if_notdone(assembler); | |
| 176 assembler->Branch(assembler->WordEqual(offset, end), &if_done, | |
| 177 &if_notdone); | |
| 178 | |
| 179 assembler->Bind(&if_notdone); | |
| 180 { | |
| 181 // Load the next characters from {lhs} and {rhs}. | |
| 182 Node* lhs_value = | |
| 183 assembler->Load(MachineType::Uint8(), lhs, offset); | |
| 184 Node* rhs_value = | |
| 185 assembler->Load(MachineType::Uint8(), rhs, offset); | |
| 186 | |
| 187 // Check if the characters match. | |
| 188 Label if_valueissame(assembler), if_valueisnotsame(assembler); | |
| 189 assembler->Branch(assembler->Word32Equal(lhs_value, rhs_value), | |
| 190 &if_valueissame, &if_valueisnotsame); | |
| 191 | |
| 192 assembler->Bind(&if_valueissame); | |
| 193 { | |
| 194 // Advance to next character. | |
| 195 var_offset.Bind( | |
| 196 assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1))); | |
| 197 } | |
| 198 assembler->Goto(&loop); | |
| 199 | |
| 200 assembler->Bind(&if_valueisnotsame); | |
| 201 assembler->Goto(&if_notequal); | |
| 202 } | |
| 203 | |
| 204 assembler->Bind(&if_done); | |
| 205 assembler->Goto(&if_equal); | |
| 206 } | |
| 207 } | 162 } |
| 208 | 163 |
| 209 assembler->Bind(&if_notbothonebyteseqstrings); | 164 Bind(&if_notbothonebyteseqstrings); |
| 210 { | 165 { |
| 211 // TODO(bmeurer): Add fast case support for flattened cons strings; | 166 // TODO(bmeurer): Add fast case support for flattened cons strings; |
| 212 // also add support for two byte string equality checks. | 167 // also add support for two byte string equality checks. |
| 213 Runtime::FunctionId function_id = | 168 Runtime::FunctionId function_id = |
| 214 (mode == ResultMode::kDontNegateResult) | 169 (mode == ResultMode::kDontNegateResult) |
| 215 ? Runtime::kStringEqual | 170 ? Runtime::kStringEqual |
| 216 : Runtime::kStringNotEqual; | 171 : Runtime::kStringNotEqual; |
| 217 assembler->TailCallRuntime(function_id, context, lhs, rhs); | 172 TailCallRuntime(function_id, context, lhs, rhs); |
| 218 } | 173 } |
| 174 |
| 175 Bind(&if_equal); |
| 176 Return(BooleanConstant(mode == ResultMode::kDontNegateResult)); |
| 177 |
| 178 Bind(&if_notequal); |
| 179 Return(BooleanConstant(mode == ResultMode::kNegateResult)); |
| 180 } |
| 181 |
| 182 void StringBuiltinsAssembler::GenerateStringRelationalComparison( |
| 183 RelationalComparisonMode mode) { |
| 184 Node* lhs = Parameter(0); |
| 185 Node* rhs = Parameter(1); |
| 186 Node* context = Parameter(2); |
| 187 |
| 188 Label if_less(this), if_equal(this), if_greater(this); |
| 189 |
| 190 // Fast check to see if {lhs} and {rhs} refer to the same String object. |
| 191 GotoIf(WordEqual(lhs, rhs), &if_equal); |
| 192 |
| 193 // Load instance types of {lhs} and {rhs}. |
| 194 Node* lhs_instance_type = LoadInstanceType(lhs); |
| 195 Node* rhs_instance_type = LoadInstanceType(rhs); |
| 196 |
| 197 // Combine the instance types into a single 16-bit value, so we can check |
| 198 // both of them at once. |
| 199 Node* both_instance_types = Word32Or( |
| 200 lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8))); |
| 201 |
| 202 // Check that both {lhs} and {rhs} are flat one-byte strings. |
| 203 int const kBothSeqOneByteStringMask = |
| 204 kStringEncodingMask | kStringRepresentationMask | |
| 205 ((kStringEncodingMask | kStringRepresentationMask) << 8); |
| 206 int const kBothSeqOneByteStringTag = |
| 207 kOneByteStringTag | kSeqStringTag | |
| 208 ((kOneByteStringTag | kSeqStringTag) << 8); |
| 209 Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this); |
| 210 Branch(Word32Equal(Word32And(both_instance_types, |
| 211 Int32Constant(kBothSeqOneByteStringMask)), |
| 212 Int32Constant(kBothSeqOneByteStringTag)), |
| 213 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings); |
| 214 |
| 215 Bind(&if_bothonebyteseqstrings); |
| 216 { |
| 217 // Load the length of {lhs} and {rhs}. |
| 218 Node* lhs_length = LoadStringLength(lhs); |
| 219 Node* rhs_length = LoadStringLength(rhs); |
| 220 |
| 221 // Determine the minimum length. |
| 222 Node* length = SmiMin(lhs_length, rhs_length); |
| 223 |
| 224 // Compute the effective offset of the first character. |
| 225 Node* begin = |
| 226 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag); |
| 227 |
| 228 // Compute the first offset after the string from the length. |
| 229 Node* end = IntPtrAdd(begin, SmiUntag(length)); |
| 230 |
| 231 // Loop over the {lhs} and {rhs} strings to see if they are equal. |
| 232 Variable var_offset(this, MachineType::PointerRepresentation()); |
| 233 Label loop(this, &var_offset); |
| 234 var_offset.Bind(begin); |
| 235 Goto(&loop); |
| 236 Bind(&loop); |
| 237 { |
| 238 // Check if {offset} equals {end}. |
| 239 Node* offset = var_offset.value(); |
| 240 Label if_done(this), if_notdone(this); |
| 241 Branch(WordEqual(offset, end), &if_done, &if_notdone); |
| 242 |
| 243 Bind(&if_notdone); |
| 244 { |
| 245 // Load the next characters from {lhs} and {rhs}. |
| 246 Node* lhs_value = Load(MachineType::Uint8(), lhs, offset); |
| 247 Node* rhs_value = Load(MachineType::Uint8(), rhs, offset); |
| 248 |
| 249 // Check if the characters match. |
| 250 Label if_valueissame(this), if_valueisnotsame(this); |
| 251 Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame, |
| 252 &if_valueisnotsame); |
| 253 |
| 254 Bind(&if_valueissame); |
| 255 { |
| 256 // Advance to next character. |
| 257 var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1))); |
| 258 } |
| 259 Goto(&loop); |
| 260 |
| 261 Bind(&if_valueisnotsame); |
| 262 Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater); |
| 263 } |
| 264 |
| 265 Bind(&if_done); |
| 266 { |
| 267 // All characters up to the min length are equal, decide based on |
| 268 // string length. |
| 269 GotoIf(SmiEqual(lhs_length, rhs_length), &if_equal); |
| 270 BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, &if_greater); |
| 219 } | 271 } |
| 220 } | 272 } |
| 221 | |
| 222 assembler->Bind(&if_lengthisnotequal); | |
| 223 { | |
| 224 // Mismatch in length of {lhs} and {rhs}, cannot be equal. | |
| 225 assembler->Goto(&if_notequal); | |
| 226 } | 273 } |
| 227 } | 274 |
| 228 | 275 Bind(&if_notbothonebyteseqstrings); |
| 229 assembler->Bind(&if_equal); | |
| 230 assembler->Return( | |
| 231 assembler->BooleanConstant(mode == ResultMode::kDontNegateResult)); | |
| 232 | |
| 233 assembler->Bind(&if_notequal); | |
| 234 assembler->Return( | |
| 235 assembler->BooleanConstant(mode == ResultMode::kNegateResult)); | |
| 236 } | |
| 237 | |
| 238 | |
| 239 void GenerateStringRelationalComparison(CodeStubAssembler* assembler, | |
| 240 RelationalComparisonMode mode) { | |
| 241 typedef CodeStubAssembler::Label Label; | |
| 242 typedef compiler::Node Node; | |
| 243 typedef CodeStubAssembler::Variable Variable; | |
| 244 | |
| 245 Node* lhs = assembler->Parameter(0); | |
| 246 Node* rhs = assembler->Parameter(1); | |
| 247 Node* context = assembler->Parameter(2); | |
| 248 | |
| 249 Label if_less(assembler), if_equal(assembler), if_greater(assembler); | |
| 250 | |
| 251 // Fast check to see if {lhs} and {rhs} refer to the same String object. | |
| 252 Label if_same(assembler), if_notsame(assembler); | |
| 253 assembler->Branch(assembler->WordEqual(lhs, rhs), &if_same, &if_notsame); | |
| 254 | |
| 255 assembler->Bind(&if_same); | |
| 256 assembler->Goto(&if_equal); | |
| 257 | |
| 258 assembler->Bind(&if_notsame); | |
| 259 { | |
| 260 // Load instance types of {lhs} and {rhs}. | |
| 261 Node* lhs_instance_type = assembler->LoadInstanceType(lhs); | |
| 262 Node* rhs_instance_type = assembler->LoadInstanceType(rhs); | |
| 263 | |
| 264 // Combine the instance types into a single 16-bit value, so we can check | |
| 265 // both of them at once. | |
| 266 Node* both_instance_types = assembler->Word32Or( | |
| 267 lhs_instance_type, | |
| 268 assembler->Word32Shl(rhs_instance_type, assembler->Int32Constant(8))); | |
| 269 | |
| 270 // Check that both {lhs} and {rhs} are flat one-byte strings. | |
| 271 int const kBothSeqOneByteStringMask = | |
| 272 kStringEncodingMask | kStringRepresentationMask | | |
| 273 ((kStringEncodingMask | kStringRepresentationMask) << 8); | |
| 274 int const kBothSeqOneByteStringTag = | |
| 275 kOneByteStringTag | kSeqStringTag | | |
| 276 ((kOneByteStringTag | kSeqStringTag) << 8); | |
| 277 Label if_bothonebyteseqstrings(assembler), | |
| 278 if_notbothonebyteseqstrings(assembler); | |
| 279 assembler->Branch(assembler->Word32Equal( | |
| 280 assembler->Word32And(both_instance_types, | |
| 281 assembler->Int32Constant( | |
| 282 kBothSeqOneByteStringMask)), | |
| 283 assembler->Int32Constant(kBothSeqOneByteStringTag)), | |
| 284 &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings); | |
| 285 | |
| 286 assembler->Bind(&if_bothonebyteseqstrings); | |
| 287 { | |
| 288 // Load the length of {lhs} and {rhs}. | |
| 289 Node* lhs_length = assembler->LoadStringLength(lhs); | |
| 290 Node* rhs_length = assembler->LoadStringLength(rhs); | |
| 291 | |
| 292 // Determine the minimum length. | |
| 293 Node* length = assembler->SmiMin(lhs_length, rhs_length); | |
| 294 | |
| 295 // Compute the effective offset of the first character. | |
| 296 Node* begin = assembler->IntPtrConstant(SeqOneByteString::kHeaderSize - | |
| 297 kHeapObjectTag); | |
| 298 | |
| 299 // Compute the first offset after the string from the length. | |
| 300 Node* end = assembler->IntPtrAdd(begin, assembler->SmiUntag(length)); | |
| 301 | |
| 302 // Loop over the {lhs} and {rhs} strings to see if they are equal. | |
| 303 Variable var_offset(assembler, MachineType::PointerRepresentation()); | |
| 304 Label loop(assembler, &var_offset); | |
| 305 var_offset.Bind(begin); | |
| 306 assembler->Goto(&loop); | |
| 307 assembler->Bind(&loop); | |
| 308 { | |
| 309 // Check if {offset} equals {end}. | |
| 310 Node* offset = var_offset.value(); | |
| 311 Label if_done(assembler), if_notdone(assembler); | |
| 312 assembler->Branch(assembler->WordEqual(offset, end), &if_done, | |
| 313 &if_notdone); | |
| 314 | |
| 315 assembler->Bind(&if_notdone); | |
| 316 { | |
| 317 // Load the next characters from {lhs} and {rhs}. | |
| 318 Node* lhs_value = assembler->Load(MachineType::Uint8(), lhs, offset); | |
| 319 Node* rhs_value = assembler->Load(MachineType::Uint8(), rhs, offset); | |
| 320 | |
| 321 // Check if the characters match. | |
| 322 Label if_valueissame(assembler), if_valueisnotsame(assembler); | |
| 323 assembler->Branch(assembler->Word32Equal(lhs_value, rhs_value), | |
| 324 &if_valueissame, &if_valueisnotsame); | |
| 325 | |
| 326 assembler->Bind(&if_valueissame); | |
| 327 { | |
| 328 // Advance to next character. | |
| 329 var_offset.Bind( | |
| 330 assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1))); | |
| 331 } | |
| 332 assembler->Goto(&loop); | |
| 333 | |
| 334 assembler->Bind(&if_valueisnotsame); | |
| 335 assembler->Branch(assembler->Uint32LessThan(lhs_value, rhs_value), | |
| 336 &if_less, &if_greater); | |
| 337 } | |
| 338 | |
| 339 assembler->Bind(&if_done); | |
| 340 { | |
| 341 // All characters up to the min length are equal, decide based on | |
| 342 // string length. | |
| 343 Label if_lengthisequal(assembler), if_lengthisnotequal(assembler); | |
| 344 assembler->Branch(assembler->SmiEqual(lhs_length, rhs_length), | |
| 345 &if_lengthisequal, &if_lengthisnotequal); | |
| 346 | |
| 347 assembler->Bind(&if_lengthisequal); | |
| 348 assembler->Goto(&if_equal); | |
| 349 | |
| 350 assembler->Bind(&if_lengthisnotequal); | |
| 351 assembler->BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, | |
| 352 &if_greater); | |
| 353 } | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 assembler->Bind(&if_notbothonebyteseqstrings); | |
| 358 { | 276 { |
| 359 // TODO(bmeurer): Add fast case support for flattened cons strings; | 277 // TODO(bmeurer): Add fast case support for flattened cons strings; |
| 360 // also add support for two byte string relational comparisons. | 278 // also add support for two byte string relational comparisons. |
| 361 switch (mode) { | 279 switch (mode) { |
| 362 case RelationalComparisonMode::kLessThan: | 280 case RelationalComparisonMode::kLessThan: |
| 363 assembler->TailCallRuntime(Runtime::kStringLessThan, context, lhs, | 281 TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs); |
| 364 rhs); | |
| 365 break; | 282 break; |
| 366 case RelationalComparisonMode::kLessThanOrEqual: | 283 case RelationalComparisonMode::kLessThanOrEqual: |
| 367 assembler->TailCallRuntime(Runtime::kStringLessThanOrEqual, context, | 284 TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs); |
| 368 lhs, rhs); | |
| 369 break; | 285 break; |
| 370 case RelationalComparisonMode::kGreaterThan: | 286 case RelationalComparisonMode::kGreaterThan: |
| 371 assembler->TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, | 287 TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs); |
| 372 rhs); | |
| 373 break; | 288 break; |
| 374 case RelationalComparisonMode::kGreaterThanOrEqual: | 289 case RelationalComparisonMode::kGreaterThanOrEqual: |
| 375 assembler->TailCallRuntime(Runtime::kStringGreaterThanOrEqual, | 290 TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, |
| 376 context, lhs, rhs); | 291 rhs); |
| 377 break; | 292 break; |
| 378 } | 293 } |
| 379 } | 294 } |
| 295 |
| 296 Bind(&if_less); |
| 297 switch (mode) { |
| 298 case RelationalComparisonMode::kLessThan: |
| 299 case RelationalComparisonMode::kLessThanOrEqual: |
| 300 Return(BooleanConstant(true)); |
| 301 break; |
| 302 |
| 303 case RelationalComparisonMode::kGreaterThan: |
| 304 case RelationalComparisonMode::kGreaterThanOrEqual: |
| 305 Return(BooleanConstant(false)); |
| 306 break; |
| 380 } | 307 } |
| 381 | 308 |
| 382 assembler->Bind(&if_less); | 309 Bind(&if_equal); |
| 310 switch (mode) { |
| 311 case RelationalComparisonMode::kLessThan: |
| 312 case RelationalComparisonMode::kGreaterThan: |
| 313 Return(BooleanConstant(false)); |
| 314 break; |
| 315 |
| 316 case RelationalComparisonMode::kLessThanOrEqual: |
| 317 case RelationalComparisonMode::kGreaterThanOrEqual: |
| 318 Return(BooleanConstant(true)); |
| 319 break; |
| 320 } |
| 321 |
| 322 Bind(&if_greater); |
| 383 switch (mode) { | 323 switch (mode) { |
| 384 case RelationalComparisonMode::kLessThan: | 324 case RelationalComparisonMode::kLessThan: |
| 385 case RelationalComparisonMode::kLessThanOrEqual: | 325 case RelationalComparisonMode::kLessThanOrEqual: |
| 386 assembler->Return(assembler->BooleanConstant(true)); | 326 Return(BooleanConstant(false)); |
| 387 break; | 327 break; |
| 388 | 328 |
| 389 case RelationalComparisonMode::kGreaterThan: | 329 case RelationalComparisonMode::kGreaterThan: |
| 390 case RelationalComparisonMode::kGreaterThanOrEqual: | 330 case RelationalComparisonMode::kGreaterThanOrEqual: |
| 391 assembler->Return(assembler->BooleanConstant(false)); | 331 Return(BooleanConstant(true)); |
| 392 break; | 332 break; |
| 393 } | 333 } |
| 394 | 334 } |
| 395 assembler->Bind(&if_equal); | 335 |
| 396 switch (mode) { | 336 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) { |
| 397 case RelationalComparisonMode::kLessThan: | 337 GenerateStringEqual(ResultMode::kDontNegateResult); |
| 398 case RelationalComparisonMode::kGreaterThan: | 338 } |
| 399 assembler->Return(assembler->BooleanConstant(false)); | 339 |
| 400 break; | 340 TF_BUILTIN(StringNotEqual, StringBuiltinsAssembler) { |
| 401 | 341 GenerateStringEqual(ResultMode::kNegateResult); |
| 402 case RelationalComparisonMode::kLessThanOrEqual: | 342 } |
| 403 case RelationalComparisonMode::kGreaterThanOrEqual: | 343 |
| 404 assembler->Return(assembler->BooleanConstant(true)); | 344 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) { |
| 405 break; | 345 GenerateStringRelationalComparison(RelationalComparisonMode::kLessThan); |
| 406 } | 346 } |
| 407 | 347 |
| 408 assembler->Bind(&if_greater); | 348 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) { |
| 409 switch (mode) { | |
| 410 case RelationalComparisonMode::kLessThan: | |
| 411 case RelationalComparisonMode::kLessThanOrEqual: | |
| 412 assembler->Return(assembler->BooleanConstant(false)); | |
| 413 break; | |
| 414 | |
| 415 case RelationalComparisonMode::kGreaterThan: | |
| 416 case RelationalComparisonMode::kGreaterThanOrEqual: | |
| 417 assembler->Return(assembler->BooleanConstant(true)); | |
| 418 break; | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 } // namespace | |
| 423 | |
| 424 // static | |
| 425 void Builtins::Generate_StringEqual(compiler::CodeAssemblerState* state) { | |
| 426 CodeStubAssembler assembler(state); | |
| 427 GenerateStringEqual(&assembler, ResultMode::kDontNegateResult); | |
| 428 } | |
| 429 | |
| 430 // static | |
| 431 void Builtins::Generate_StringNotEqual(compiler::CodeAssemblerState* state) { | |
| 432 CodeStubAssembler assembler(state); | |
| 433 GenerateStringEqual(&assembler, ResultMode::kNegateResult); | |
| 434 } | |
| 435 | |
| 436 // static | |
| 437 void Builtins::Generate_StringLessThan(compiler::CodeAssemblerState* state) { | |
| 438 CodeStubAssembler assembler(state); | |
| 439 GenerateStringRelationalComparison(&assembler, | |
| 440 RelationalComparisonMode::kLessThan); | |
| 441 } | |
| 442 | |
| 443 // static | |
| 444 void Builtins::Generate_StringLessThanOrEqual( | |
| 445 compiler::CodeAssemblerState* state) { | |
| 446 CodeStubAssembler assembler(state); | |
| 447 GenerateStringRelationalComparison( | 349 GenerateStringRelationalComparison( |
| 448 &assembler, RelationalComparisonMode::kLessThanOrEqual); | 350 RelationalComparisonMode::kLessThanOrEqual); |
| 449 } | 351 } |
| 450 | 352 |
| 451 // static | 353 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) { |
| 452 void Builtins::Generate_StringGreaterThan(compiler::CodeAssemblerState* state) { | 354 GenerateStringRelationalComparison(RelationalComparisonMode::kGreaterThan); |
| 453 CodeStubAssembler assembler(state); | 355 } |
| 454 GenerateStringRelationalComparison(&assembler, | 356 |
| 455 RelationalComparisonMode::kGreaterThan); | 357 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) { |
| 456 } | |
| 457 | |
| 458 // static | |
| 459 void Builtins::Generate_StringGreaterThanOrEqual( | |
| 460 compiler::CodeAssemblerState* state) { | |
| 461 CodeStubAssembler assembler(state); | |
| 462 GenerateStringRelationalComparison( | 358 GenerateStringRelationalComparison( |
| 463 &assembler, RelationalComparisonMode::kGreaterThanOrEqual); | 359 RelationalComparisonMode::kGreaterThanOrEqual); |
| 464 } | 360 } |
| 465 | 361 |
| 466 // static | 362 TF_BUILTIN(StringCharAt, CodeStubAssembler) { |
| 467 void Builtins::Generate_StringCharAt(compiler::CodeAssemblerState* state) { | 363 Node* receiver = Parameter(0); |
| 468 typedef compiler::Node Node; | 364 Node* position = Parameter(1); |
| 469 CodeStubAssembler assembler(state); | |
| 470 | |
| 471 Node* receiver = assembler.Parameter(0); | |
| 472 Node* position = assembler.Parameter(1); | |
| 473 | 365 |
| 474 // Load the character code at the {position} from the {receiver}. | 366 // Load the character code at the {position} from the {receiver}. |
| 475 Node* code = assembler.StringCharCodeAt(receiver, position, | 367 Node* code = StringCharCodeAt(receiver, position, |
| 476 CodeStubAssembler::INTPTR_PARAMETERS); | 368 CodeStubAssembler::INTPTR_PARAMETERS); |
| 477 | 369 |
| 478 // And return the single character string with only that {code} | 370 // And return the single character string with only that {code} |
| 479 Node* result = assembler.StringFromCharCode(code); | 371 Node* result = StringFromCharCode(code); |
| 480 assembler.Return(result); | 372 Return(result); |
| 481 } | 373 } |
| 482 | 374 |
| 483 // static | 375 TF_BUILTIN(StringCharCodeAt, CodeStubAssembler) { |
| 484 void Builtins::Generate_StringCharCodeAt(compiler::CodeAssemblerState* state) { | 376 Node* receiver = Parameter(0); |
| 485 typedef compiler::Node Node; | 377 Node* position = Parameter(1); |
| 486 CodeStubAssembler assembler(state); | |
| 487 | |
| 488 Node* receiver = assembler.Parameter(0); | |
| 489 Node* position = assembler.Parameter(1); | |
| 490 | 378 |
| 491 // Load the character code at the {position} from the {receiver}. | 379 // Load the character code at the {position} from the {receiver}. |
| 492 Node* code = assembler.StringCharCodeAt(receiver, position, | 380 Node* code = StringCharCodeAt(receiver, position, |
| 493 CodeStubAssembler::INTPTR_PARAMETERS); | 381 CodeStubAssembler::INTPTR_PARAMETERS); |
| 494 | 382 |
| 495 // And return it as TaggedSigned value. | 383 // And return it as TaggedSigned value. |
| 496 // TODO(turbofan): Allow builtins to return values untagged. | 384 // TODO(turbofan): Allow builtins to return values untagged. |
| 497 Node* result = assembler.SmiFromWord32(code); | 385 Node* result = SmiFromWord32(code); |
| 498 assembler.Return(result); | 386 Return(result); |
| 499 } | 387 } |
| 500 | 388 |
| 501 // ----------------------------------------------------------------------------- | 389 // ----------------------------------------------------------------------------- |
| 502 // ES6 section 21.1 String Objects | 390 // ES6 section 21.1 String Objects |
| 503 | 391 |
| 504 // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits ) | 392 // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits ) |
| 505 void Builtins::Generate_StringFromCharCode( | 393 TF_BUILTIN(StringFromCharCode, CodeStubAssembler) { |
| 506 compiler::CodeAssemblerState* state) { | 394 Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); |
| 507 typedef CodeStubAssembler::Label Label; | 395 Node* context = Parameter(BuiltinDescriptor::kContext); |
| 508 typedef compiler::Node Node; | 396 |
| 509 typedef CodeStubAssembler::Variable Variable; | 397 CodeStubArguments arguments(this, argc); |
| 510 CodeStubAssembler assembler(state); | |
| 511 | |
| 512 Node* argc = assembler.Parameter(BuiltinDescriptor::kArgumentsCount); | |
| 513 Node* context = assembler.Parameter(BuiltinDescriptor::kContext); | |
| 514 | |
| 515 CodeStubArguments arguments(&assembler, argc); | |
| 516 // From now on use word-size argc value. | 398 // From now on use word-size argc value. |
| 517 argc = arguments.GetLength(); | 399 argc = arguments.GetLength(); |
| 518 | 400 |
| 519 // Check if we have exactly one argument (plus the implicit receiver), i.e. | 401 // Check if we have exactly one argument (plus the implicit receiver), i.e. |
| 520 // if the parent frame is not an arguments adaptor frame. | 402 // if the parent frame is not an arguments adaptor frame. |
| 521 Label if_oneargument(&assembler), if_notoneargument(&assembler); | 403 Label if_oneargument(this), if_notoneargument(this); |
| 522 assembler.Branch(assembler.WordEqual(argc, assembler.IntPtrConstant(1)), | 404 Branch(WordEqual(argc, IntPtrConstant(1)), &if_oneargument, |
| 523 &if_oneargument, &if_notoneargument); | 405 &if_notoneargument); |
| 524 | 406 |
| 525 assembler.Bind(&if_oneargument); | 407 Bind(&if_oneargument); |
| 526 { | 408 { |
| 527 // Single argument case, perform fast single character string cache lookup | 409 // Single argument case, perform fast single character string cache lookup |
| 528 // for one-byte code units, or fall back to creating a single character | 410 // for one-byte code units, or fall back to creating a single character |
| 529 // string on the fly otherwise. | 411 // string on the fly otherwise. |
| 530 Node* code = arguments.AtIndex(0); | 412 Node* code = arguments.AtIndex(0); |
| 531 Node* code32 = assembler.TruncateTaggedToWord32(context, code); | 413 Node* code32 = TruncateTaggedToWord32(context, code); |
| 532 Node* code16 = assembler.Word32And( | 414 Node* code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)); |
| 533 code32, assembler.Int32Constant(String::kMaxUtf16CodeUnit)); | 415 Node* result = StringFromCharCode(code16); |
| 534 Node* result = assembler.StringFromCharCode(code16); | |
| 535 arguments.PopAndReturn(result); | 416 arguments.PopAndReturn(result); |
| 536 } | 417 } |
| 537 | 418 |
| 538 Node* code16 = nullptr; | 419 Node* code16 = nullptr; |
| 539 assembler.Bind(&if_notoneargument); | 420 Bind(&if_notoneargument); |
| 540 { | 421 { |
| 541 Label two_byte(&assembler); | 422 Label two_byte(this); |
| 542 // Assume that the resulting string contains only one-byte characters. | 423 // Assume that the resulting string contains only one-byte characters. |
| 543 Node* one_byte_result = assembler.AllocateSeqOneByteString(context, argc); | 424 Node* one_byte_result = AllocateSeqOneByteString(context, argc); |
| 544 | 425 |
| 545 Variable max_index(&assembler, MachineType::PointerRepresentation()); | 426 Variable max_index(this, MachineType::PointerRepresentation()); |
| 546 max_index.Bind(assembler.IntPtrConstant(0)); | 427 max_index.Bind(IntPtrConstant(0)); |
| 547 | 428 |
| 548 // Iterate over the incoming arguments, converting them to 8-bit character | 429 // Iterate over the incoming arguments, converting them to 8-bit character |
| 549 // codes. Stop if any of the conversions generates a code that doesn't fit | 430 // codes. Stop if any of the conversions generates a code that doesn't fit |
| 550 // in 8 bits. | 431 // in 8 bits. |
| 551 CodeStubAssembler::VariableList vars({&max_index}, assembler.zone()); | 432 CodeStubAssembler::VariableList vars({&max_index}, zone()); |
| 552 arguments.ForEach(vars, [&assembler, context, &two_byte, &max_index, | 433 arguments.ForEach(vars, [this, context, &two_byte, &max_index, &code16, |
| 553 &code16, one_byte_result](Node* arg) { | 434 one_byte_result](Node* arg) { |
| 554 Node* code32 = assembler.TruncateTaggedToWord32(context, arg); | 435 Node* code32 = TruncateTaggedToWord32(context, arg); |
| 555 code16 = assembler.Word32And( | 436 code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)); |
| 556 code32, assembler.Int32Constant(String::kMaxUtf16CodeUnit)); | 437 |
| 557 | 438 GotoIf( |
| 558 assembler.GotoIf( | 439 Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)), |
| 559 assembler.Int32GreaterThan( | |
| 560 code16, assembler.Int32Constant(String::kMaxOneByteCharCode)), | |
| 561 &two_byte); | 440 &two_byte); |
| 562 | 441 |
| 563 // The {code16} fits into the SeqOneByteString {one_byte_result}. | 442 // The {code16} fits into the SeqOneByteString {one_byte_result}. |
| 564 Node* offset = assembler.ElementOffsetFromIndex( | 443 Node* offset = ElementOffsetFromIndex( |
| 565 max_index.value(), UINT8_ELEMENTS, | 444 max_index.value(), UINT8_ELEMENTS, |
| 566 CodeStubAssembler::INTPTR_PARAMETERS, | 445 CodeStubAssembler::INTPTR_PARAMETERS, |
| 567 SeqOneByteString::kHeaderSize - kHeapObjectTag); | 446 SeqOneByteString::kHeaderSize - kHeapObjectTag); |
| 568 assembler.StoreNoWriteBarrier(MachineRepresentation::kWord8, | 447 StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result, |
| 569 one_byte_result, offset, code16); | 448 offset, code16); |
| 570 max_index.Bind( | 449 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); |
| 571 assembler.IntPtrAdd(max_index.value(), assembler.IntPtrConstant(1))); | |
| 572 }); | 450 }); |
| 573 arguments.PopAndReturn(one_byte_result); | 451 arguments.PopAndReturn(one_byte_result); |
| 574 | 452 |
| 575 assembler.Bind(&two_byte); | 453 Bind(&two_byte); |
| 576 | 454 |
| 577 // At least one of the characters in the string requires a 16-bit | 455 // At least one of the characters in the string requires a 16-bit |
| 578 // representation. Allocate a SeqTwoByteString to hold the resulting | 456 // representation. Allocate a SeqTwoByteString to hold the resulting |
| 579 // string. | 457 // string. |
| 580 Node* two_byte_result = assembler.AllocateSeqTwoByteString(context, argc); | 458 Node* two_byte_result = AllocateSeqTwoByteString(context, argc); |
| 581 | 459 |
| 582 // Copy the characters that have already been put in the 8-bit string into | 460 // Copy the characters that have already been put in the 8-bit string into |
| 583 // their corresponding positions in the new 16-bit string. | 461 // their corresponding positions in the new 16-bit string. |
| 584 Node* zero = assembler.IntPtrConstant(0); | 462 Node* zero = IntPtrConstant(0); |
| 585 assembler.CopyStringCharacters(one_byte_result, two_byte_result, zero, zero, | 463 CopyStringCharacters(one_byte_result, two_byte_result, zero, zero, |
| 586 max_index.value(), String::ONE_BYTE_ENCODING, | 464 max_index.value(), String::ONE_BYTE_ENCODING, |
| 587 String::TWO_BYTE_ENCODING, | 465 String::TWO_BYTE_ENCODING, |
| 588 CodeStubAssembler::INTPTR_PARAMETERS); | 466 CodeStubAssembler::INTPTR_PARAMETERS); |
| 589 | 467 |
| 590 // Write the character that caused the 8-bit to 16-bit fault. | 468 // Write the character that caused the 8-bit to 16-bit fault. |
| 591 Node* max_index_offset = assembler.ElementOffsetFromIndex( | 469 Node* max_index_offset = |
| 592 max_index.value(), UINT16_ELEMENTS, | 470 ElementOffsetFromIndex(max_index.value(), UINT16_ELEMENTS, |
| 593 CodeStubAssembler::INTPTR_PARAMETERS, | 471 CodeStubAssembler::INTPTR_PARAMETERS, |
| 594 SeqTwoByteString::kHeaderSize - kHeapObjectTag); | 472 SeqTwoByteString::kHeaderSize - kHeapObjectTag); |
| 595 assembler.StoreNoWriteBarrier(MachineRepresentation::kWord16, | 473 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result, |
| 596 two_byte_result, max_index_offset, code16); | 474 max_index_offset, code16); |
| 597 max_index.Bind( | 475 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); |
| 598 assembler.IntPtrAdd(max_index.value(), assembler.IntPtrConstant(1))); | |
| 599 | 476 |
| 600 // Resume copying the passed-in arguments from the same place where the | 477 // Resume copying the passed-in arguments from the same place where the |
| 601 // 8-bit copy stopped, but this time copying over all of the characters | 478 // 8-bit copy stopped, but this time copying over all of the characters |
| 602 // using a 16-bit representation. | 479 // using a 16-bit representation. |
| 603 arguments.ForEach( | 480 arguments.ForEach( |
| 604 vars, | 481 vars, |
| 605 [&assembler, context, two_byte_result, &max_index](Node* arg) { | 482 [this, context, two_byte_result, &max_index](Node* arg) { |
| 606 Node* code32 = assembler.TruncateTaggedToWord32(context, arg); | 483 Node* code32 = TruncateTaggedToWord32(context, arg); |
| 607 Node* code16 = assembler.Word32And( | 484 Node* code16 = |
| 608 code32, assembler.Int32Constant(String::kMaxUtf16CodeUnit)); | 485 Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)); |
| 609 | 486 |
| 610 Node* offset = assembler.ElementOffsetFromIndex( | 487 Node* offset = ElementOffsetFromIndex( |
| 611 max_index.value(), UINT16_ELEMENTS, | 488 max_index.value(), UINT16_ELEMENTS, |
| 612 CodeStubAssembler::INTPTR_PARAMETERS, | 489 CodeStubAssembler::INTPTR_PARAMETERS, |
| 613 SeqTwoByteString::kHeaderSize - kHeapObjectTag); | 490 SeqTwoByteString::kHeaderSize - kHeapObjectTag); |
| 614 assembler.StoreNoWriteBarrier(MachineRepresentation::kWord16, | 491 StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result, |
| 615 two_byte_result, offset, code16); | 492 offset, code16); |
| 616 max_index.Bind(assembler.IntPtrAdd(max_index.value(), | 493 max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1))); |
| 617 assembler.IntPtrConstant(1))); | |
| 618 }, | 494 }, |
| 619 max_index.value()); | 495 max_index.value()); |
| 620 | 496 |
| 621 arguments.PopAndReturn(two_byte_result); | 497 arguments.PopAndReturn(two_byte_result); |
| 622 } | 498 } |
| 623 } | 499 } |
| 624 | 500 |
| 625 namespace { // for String.fromCodePoint | 501 namespace { // for String.fromCodePoint |
| 626 | 502 |
| 627 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { | 503 bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 709 | 585 |
| 710 CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(), | 586 CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(), |
| 711 one_byte_buffer.length()); | 587 one_byte_buffer.length()); |
| 712 CopyChars(result->GetChars() + one_byte_buffer.length(), | 588 CopyChars(result->GetChars() + one_byte_buffer.length(), |
| 713 two_byte_buffer.ToConstVector().start(), two_byte_buffer.length()); | 589 two_byte_buffer.ToConstVector().start(), two_byte_buffer.length()); |
| 714 | 590 |
| 715 return *result; | 591 return *result; |
| 716 } | 592 } |
| 717 | 593 |
| 718 // ES6 section 21.1.3.1 String.prototype.charAt ( pos ) | 594 // ES6 section 21.1.3.1 String.prototype.charAt ( pos ) |
| 719 void Builtins::Generate_StringPrototypeCharAt( | 595 TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) { |
| 720 compiler::CodeAssemblerState* state) { | 596 Node* receiver = Parameter(0); |
| 721 typedef CodeStubAssembler::Label Label; | 597 Node* position = Parameter(1); |
| 722 typedef compiler::Node Node; | 598 Node* context = Parameter(4); |
| 723 CodeStubAssembler assembler(state); | |
| 724 | |
| 725 Node* receiver = assembler.Parameter(0); | |
| 726 Node* position = assembler.Parameter(1); | |
| 727 Node* context = assembler.Parameter(4); | |
| 728 | 599 |
| 729 // Check that {receiver} is coercible to Object and convert it to a String. | 600 // Check that {receiver} is coercible to Object and convert it to a String. |
| 730 receiver = | 601 receiver = ToThisString(context, receiver, "String.prototype.charAt"); |
| 731 assembler.ToThisString(context, receiver, "String.prototype.charAt"); | |
| 732 | 602 |
| 733 // Convert the {position} to a Smi and check that it's in bounds of the | 603 // Convert the {position} to a Smi and check that it's in bounds of the |
| 734 // {receiver}. | 604 // {receiver}. |
| 735 { | 605 { |
| 736 Label return_emptystring(&assembler, Label::kDeferred); | 606 Label return_emptystring(this, Label::kDeferred); |
| 737 position = assembler.ToInteger(context, position, | 607 position = |
| 738 CodeStubAssembler::kTruncateMinusZero); | 608 ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero); |
| 739 assembler.GotoUnless(assembler.TaggedIsSmi(position), &return_emptystring); | 609 GotoUnless(TaggedIsSmi(position), &return_emptystring); |
| 740 | 610 |
| 741 // Determine the actual length of the {receiver} String. | 611 // Determine the actual length of the {receiver} String. |
| 742 Node* receiver_length = | 612 Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset); |
| 743 assembler.LoadObjectField(receiver, String::kLengthOffset); | |
| 744 | 613 |
| 745 // Return "" if the Smi {position} is outside the bounds of the {receiver}. | 614 // Return "" if the Smi {position} is outside the bounds of the {receiver}. |
| 746 Label if_positioninbounds(&assembler); | 615 Label if_positioninbounds(this); |
| 747 assembler.Branch(assembler.SmiAboveOrEqual(position, receiver_length), | 616 Branch(SmiAboveOrEqual(position, receiver_length), &return_emptystring, |
| 748 &return_emptystring, &if_positioninbounds); | 617 &if_positioninbounds); |
| 749 | 618 |
| 750 assembler.Bind(&return_emptystring); | 619 Bind(&return_emptystring); |
| 751 assembler.Return(assembler.EmptyStringConstant()); | 620 Return(EmptyStringConstant()); |
| 752 | 621 |
| 753 assembler.Bind(&if_positioninbounds); | 622 Bind(&if_positioninbounds); |
| 754 } | 623 } |
| 755 | 624 |
| 756 // Load the character code at the {position} from the {receiver}. | 625 // Load the character code at the {position} from the {receiver}. |
| 757 Node* code = assembler.StringCharCodeAt(receiver, position); | 626 Node* code = StringCharCodeAt(receiver, position); |
| 758 | 627 |
| 759 // And return the single character string with only that {code}. | 628 // And return the single character string with only that {code}. |
| 760 Node* result = assembler.StringFromCharCode(code); | 629 Node* result = StringFromCharCode(code); |
| 761 assembler.Return(result); | 630 Return(result); |
| 762 } | 631 } |
| 763 | 632 |
| 764 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos ) | 633 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos ) |
| 765 void Builtins::Generate_StringPrototypeCharCodeAt( | 634 TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) { |
| 766 compiler::CodeAssemblerState* state) { | 635 Node* receiver = Parameter(0); |
| 767 typedef CodeStubAssembler::Label Label; | 636 Node* position = Parameter(1); |
| 768 typedef compiler::Node Node; | 637 Node* context = Parameter(4); |
| 769 CodeStubAssembler assembler(state); | |
| 770 | |
| 771 Node* receiver = assembler.Parameter(0); | |
| 772 Node* position = assembler.Parameter(1); | |
| 773 Node* context = assembler.Parameter(4); | |
| 774 | 638 |
| 775 // Check that {receiver} is coercible to Object and convert it to a String. | 639 // Check that {receiver} is coercible to Object and convert it to a String. |
| 776 receiver = | 640 receiver = ToThisString(context, receiver, "String.prototype.charCodeAt"); |
| 777 assembler.ToThisString(context, receiver, "String.prototype.charCodeAt"); | |
| 778 | 641 |
| 779 // Convert the {position} to a Smi and check that it's in bounds of the | 642 // Convert the {position} to a Smi and check that it's in bounds of the |
| 780 // {receiver}. | 643 // {receiver}. |
| 781 { | 644 { |
| 782 Label return_nan(&assembler, Label::kDeferred); | 645 Label return_nan(this, Label::kDeferred); |
| 783 position = assembler.ToInteger(context, position, | 646 position = |
| 784 CodeStubAssembler::kTruncateMinusZero); | 647 ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero); |
| 785 assembler.GotoUnless(assembler.TaggedIsSmi(position), &return_nan); | 648 GotoUnless(TaggedIsSmi(position), &return_nan); |
| 786 | 649 |
| 787 // Determine the actual length of the {receiver} String. | 650 // Determine the actual length of the {receiver} String. |
| 788 Node* receiver_length = | 651 Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset); |
| 789 assembler.LoadObjectField(receiver, String::kLengthOffset); | |
| 790 | 652 |
| 791 // Return NaN if the Smi {position} is outside the bounds of the {receiver}. | 653 // Return NaN if the Smi {position} is outside the bounds of the {receiver}. |
| 792 Label if_positioninbounds(&assembler); | 654 Label if_positioninbounds(this); |
| 793 assembler.Branch(assembler.SmiAboveOrEqual(position, receiver_length), | 655 Branch(SmiAboveOrEqual(position, receiver_length), &return_nan, |
| 794 &return_nan, &if_positioninbounds); | 656 &if_positioninbounds); |
| 795 | 657 |
| 796 assembler.Bind(&return_nan); | 658 Bind(&return_nan); |
| 797 assembler.Return(assembler.NaNConstant()); | 659 Return(NaNConstant()); |
| 798 | 660 |
| 799 assembler.Bind(&if_positioninbounds); | 661 Bind(&if_positioninbounds); |
| 800 } | 662 } |
| 801 | 663 |
| 802 // Load the character at the {position} from the {receiver}. | 664 // Load the character at the {position} from the {receiver}. |
| 803 Node* value = assembler.StringCharCodeAt(receiver, position); | 665 Node* value = StringCharCodeAt(receiver, position); |
| 804 Node* result = assembler.SmiFromWord32(value); | 666 Node* result = SmiFromWord32(value); |
| 805 assembler.Return(result); | 667 Return(result); |
| 806 } | 668 } |
| 807 | 669 |
| 808 // ES6 section 21.1.3.6 | 670 // ES6 section 21.1.3.6 |
| 809 // String.prototype.endsWith ( searchString [ , endPosition ] ) | 671 // String.prototype.endsWith ( searchString [ , endPosition ] ) |
| 810 BUILTIN(StringPrototypeEndsWith) { | 672 BUILTIN(StringPrototypeEndsWith) { |
| 811 HandleScope handle_scope(isolate); | 673 HandleScope handle_scope(isolate); |
| 812 TO_THIS_STRING(str, "String.prototype.endsWith"); | 674 TO_THIS_STRING(str, "String.prototype.endsWith"); |
| 813 | 675 |
| 814 // Check if the search string is a regExp and fail if it is. | 676 // Check if the search string is a regExp and fail if it is. |
| 815 Handle<Object> search = args.atOrUndefined(isolate, 1); | 677 Handle<Object> search = args.atOrUndefined(isolate, 1); |
| (...skipping 301 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1117 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD"); | 979 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD"); |
| 1118 THROW_NEW_ERROR_RETURN_FAILURE( | 980 THROW_NEW_ERROR_RETURN_FAILURE( |
| 1119 isolate, | 981 isolate, |
| 1120 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms)); | 982 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms)); |
| 1121 } | 983 } |
| 1122 | 984 |
| 1123 return *string; | 985 return *string; |
| 1124 } | 986 } |
| 1125 | 987 |
| 1126 // ES6 section B.2.3.1 String.prototype.substr ( start, length ) | 988 // ES6 section B.2.3.1 String.prototype.substr ( start, length ) |
| 1127 void Builtins::Generate_StringPrototypeSubstr( | 989 TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) { |
| 1128 compiler::CodeAssemblerState* state) { | 990 Label out(this), handle_length(this); |
| 1129 typedef CodeStubAssembler::Label Label; | 991 |
| 1130 typedef compiler::Node Node; | 992 Variable var_start(this, MachineRepresentation::kTagged); |
| 1131 typedef CodeStubAssembler::Variable Variable; | 993 Variable var_length(this, MachineRepresentation::kTagged); |
| 1132 CodeStubAssembler a(state); | 994 |
| 1133 | 995 Node* const receiver = Parameter(0); |
| 1134 Label out(&a), handle_length(&a); | 996 Node* const start = Parameter(1); |
| 1135 | 997 Node* const length = Parameter(2); |
| 1136 Variable var_start(&a, MachineRepresentation::kTagged); | 998 Node* const context = Parameter(5); |
| 1137 Variable var_length(&a, MachineRepresentation::kTagged); | 999 |
| 1138 | 1000 Node* const zero = SmiConstant(Smi::kZero); |
| 1139 Node* const receiver = a.Parameter(0); | |
| 1140 Node* const start = a.Parameter(1); | |
| 1141 Node* const length = a.Parameter(2); | |
| 1142 Node* const context = a.Parameter(5); | |
| 1143 | |
| 1144 Node* const zero = a.SmiConstant(Smi::kZero); | |
| 1145 | 1001 |
| 1146 // Check that {receiver} is coercible to Object and convert it to a String. | 1002 // Check that {receiver} is coercible to Object and convert it to a String. |
| 1147 Node* const string = | 1003 Node* const string = |
| 1148 a.ToThisString(context, receiver, "String.prototype.substr"); | 1004 ToThisString(context, receiver, "String.prototype.substr"); |
| 1149 | 1005 |
| 1150 Node* const string_length = a.LoadStringLength(string); | 1006 Node* const string_length = LoadStringLength(string); |
| 1151 | 1007 |
| 1152 // Conversions and bounds-checks for {start}. | 1008 // Conversions and bounds-checks for {start}. |
| 1153 { | 1009 { |
| 1154 Node* const start_int = | 1010 Node* const start_int = |
| 1155 a.ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero); | 1011 ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero); |
| 1156 | 1012 |
| 1157 Label if_issmi(&a), if_isheapnumber(&a, Label::kDeferred); | 1013 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); |
| 1158 a.Branch(a.TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber); | 1014 Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber); |
| 1159 | 1015 |
| 1160 a.Bind(&if_issmi); | 1016 Bind(&if_issmi); |
| 1161 { | 1017 { |
| 1162 Node* const length_plus_start = a.SmiAdd(string_length, start_int); | 1018 Node* const length_plus_start = SmiAdd(string_length, start_int); |
| 1163 var_start.Bind(a.Select(a.SmiLessThan(start_int, zero), | 1019 var_start.Bind(Select(SmiLessThan(start_int, zero), |
| 1164 [&] { return a.SmiMax(length_plus_start, zero); }, | 1020 [&] { return SmiMax(length_plus_start, zero); }, |
| 1165 [&] { return start_int; }, | 1021 [&] { return start_int; }, |
| 1166 MachineRepresentation::kTagged)); | 1022 MachineRepresentation::kTagged)); |
| 1167 a.Goto(&handle_length); | 1023 Goto(&handle_length); |
| 1168 } | 1024 } |
| 1169 | 1025 |
| 1170 a.Bind(&if_isheapnumber); | 1026 Bind(&if_isheapnumber); |
| 1171 { | 1027 { |
| 1172 // If {start} is a heap number, it is definitely out of bounds. If it is | 1028 // If {start} is a heap number, it is definitely out of bounds. If it is |
| 1173 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is | 1029 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is |
| 1174 // positive, set {start} to {string_length} which ultimately results in | 1030 // positive, set {start} to {string_length} which ultimately results in |
| 1175 // returning an empty string. | 1031 // returning an empty string. |
| 1176 Node* const float_zero = a.Float64Constant(0.); | 1032 Node* const float_zero = Float64Constant(0.); |
| 1177 Node* const start_float = a.LoadHeapNumberValue(start_int); | 1033 Node* const start_float = LoadHeapNumberValue(start_int); |
| 1178 var_start.Bind(a.SelectTaggedConstant( | 1034 var_start.Bind(SelectTaggedConstant( |
| 1179 a.Float64LessThan(start_float, float_zero), zero, string_length)); | 1035 Float64LessThan(start_float, float_zero), zero, string_length)); |
| 1180 a.Goto(&handle_length); | 1036 Goto(&handle_length); |
| 1181 } | 1037 } |
| 1182 } | 1038 } |
| 1183 | 1039 |
| 1184 // Conversions and bounds-checks for {length}. | 1040 // Conversions and bounds-checks for {length}. |
| 1185 a.Bind(&handle_length); | 1041 Bind(&handle_length); |
| 1186 { | 1042 { |
| 1187 Label if_issmi(&a), if_isheapnumber(&a, Label::kDeferred); | 1043 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); |
| 1188 | 1044 |
| 1189 // Default to {string_length} if {length} is undefined. | 1045 // Default to {string_length} if {length} is undefined. |
| 1190 { | 1046 { |
| 1191 Label if_isundefined(&a, Label::kDeferred), if_isnotundefined(&a); | 1047 Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this); |
| 1192 a.Branch(a.WordEqual(length, a.UndefinedConstant()), &if_isundefined, | 1048 Branch(WordEqual(length, UndefinedConstant()), &if_isundefined, |
| 1193 &if_isnotundefined); | 1049 &if_isnotundefined); |
| 1194 | 1050 |
| 1195 a.Bind(&if_isundefined); | 1051 Bind(&if_isundefined); |
| 1196 var_length.Bind(string_length); | 1052 var_length.Bind(string_length); |
| 1197 a.Goto(&if_issmi); | 1053 Goto(&if_issmi); |
| 1198 | 1054 |
| 1199 a.Bind(&if_isnotundefined); | 1055 Bind(&if_isnotundefined); |
| 1200 var_length.Bind( | 1056 var_length.Bind( |
| 1201 a.ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero)); | 1057 ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero)); |
| 1202 } | 1058 } |
| 1203 | 1059 |
| 1204 a.Branch(a.TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber); | 1060 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber); |
| 1205 | 1061 |
| 1206 // Set {length} to min(max({length}, 0), {string_length} - {start} | 1062 // Set {length} to min(max({length}, 0), {string_length} - {start} |
| 1207 a.Bind(&if_issmi); | 1063 Bind(&if_issmi); |
| 1208 { | 1064 { |
| 1209 Node* const positive_length = a.SmiMax(var_length.value(), zero); | 1065 Node* const positive_length = SmiMax(var_length.value(), zero); |
| 1210 | 1066 |
| 1211 Node* const minimal_length = a.SmiSub(string_length, var_start.value()); | 1067 Node* const minimal_length = SmiSub(string_length, var_start.value()); |
| 1212 var_length.Bind(a.SmiMin(positive_length, minimal_length)); | 1068 var_length.Bind(SmiMin(positive_length, minimal_length)); |
| 1213 | 1069 |
| 1214 a.GotoUnless(a.SmiLessThanOrEqual(var_length.value(), zero), &out); | 1070 GotoUnless(SmiLessThanOrEqual(var_length.value(), zero), &out); |
| 1215 a.Return(a.EmptyStringConstant()); | 1071 Return(EmptyStringConstant()); |
| 1216 } | 1072 } |
| 1217 | 1073 |
| 1218 a.Bind(&if_isheapnumber); | 1074 Bind(&if_isheapnumber); |
| 1219 { | 1075 { |
| 1220 // If {length} is a heap number, it is definitely out of bounds. There are | 1076 // If {length} is a heap number, it is definitely out of bounds. There are |
| 1221 // two cases according to the spec: if it is negative, "" is returned; if | 1077 // two cases according to the spec: if it is negative, "" is returned; if |
| 1222 // it is positive, then length is set to {string_length} - {start}. | 1078 // it is positive, then length is set to {string_length} - {start}. |
| 1223 | 1079 |
| 1224 CSA_ASSERT(&a, a.IsHeapNumberMap(a.LoadMap(var_length.value()))); | 1080 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value()))); |
| 1225 | 1081 |
| 1226 Label if_isnegative(&a), if_ispositive(&a); | 1082 Label if_isnegative(this), if_ispositive(this); |
| 1227 Node* const float_zero = a.Float64Constant(0.); | 1083 Node* const float_zero = Float64Constant(0.); |
| 1228 Node* const length_float = a.LoadHeapNumberValue(var_length.value()); | 1084 Node* const length_float = LoadHeapNumberValue(var_length.value()); |
| 1229 a.Branch(a.Float64LessThan(length_float, float_zero), &if_isnegative, | 1085 Branch(Float64LessThan(length_float, float_zero), &if_isnegative, |
| 1230 &if_ispositive); | 1086 &if_ispositive); |
| 1231 | 1087 |
| 1232 a.Bind(&if_isnegative); | 1088 Bind(&if_isnegative); |
| 1233 a.Return(a.EmptyStringConstant()); | 1089 Return(EmptyStringConstant()); |
| 1234 | 1090 |
| 1235 a.Bind(&if_ispositive); | 1091 Bind(&if_ispositive); |
| 1236 { | 1092 { |
| 1237 var_length.Bind(a.SmiSub(string_length, var_start.value())); | 1093 var_length.Bind(SmiSub(string_length, var_start.value())); |
| 1238 a.GotoUnless(a.SmiLessThanOrEqual(var_length.value(), zero), &out); | 1094 GotoUnless(SmiLessThanOrEqual(var_length.value(), zero), &out); |
| 1239 a.Return(a.EmptyStringConstant()); | 1095 Return(EmptyStringConstant()); |
| 1240 } | 1096 } |
| 1241 } | 1097 } |
| 1242 } | 1098 } |
| 1243 | 1099 |
| 1244 a.Bind(&out); | 1100 Bind(&out); |
| 1245 { | 1101 { |
| 1246 Node* const end = a.SmiAdd(var_start.value(), var_length.value()); | 1102 Node* const end = SmiAdd(var_start.value(), var_length.value()); |
| 1247 Node* const result = a.SubString(context, string, var_start.value(), end); | 1103 Node* const result = SubString(context, string, var_start.value(), end); |
| 1248 a.Return(result); | 1104 Return(result); |
| 1249 } | 1105 } |
| 1250 } | 1106 } |
| 1251 | 1107 |
| 1252 namespace { | 1108 compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context, |
| 1253 | 1109 Node* value, |
| 1254 compiler::Node* ToSmiBetweenZeroAnd(CodeStubAssembler* a, | 1110 Node* limit) { |
| 1255 compiler::Node* context, | 1111 Label out(this); |
| 1256 compiler::Node* value, | 1112 Variable var_result(this, MachineRepresentation::kTagged); |
| 1257 compiler::Node* limit) { | |
| 1258 typedef CodeStubAssembler::Label Label; | |
| 1259 typedef compiler::Node Node; | |
| 1260 typedef CodeStubAssembler::Variable Variable; | |
| 1261 | |
| 1262 Label out(a); | |
| 1263 Variable var_result(a, MachineRepresentation::kTagged); | |
| 1264 | 1113 |
| 1265 Node* const value_int = | 1114 Node* const value_int = |
| 1266 a->ToInteger(context, value, CodeStubAssembler::kTruncateMinusZero); | 1115 this->ToInteger(context, value, CodeStubAssembler::kTruncateMinusZero); |
| 1267 | 1116 |
| 1268 Label if_issmi(a), if_isnotsmi(a, Label::kDeferred); | 1117 Label if_issmi(this), if_isnotsmi(this, Label::kDeferred); |
| 1269 a->Branch(a->TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi); | 1118 Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi); |
| 1270 | 1119 |
| 1271 a->Bind(&if_issmi); | 1120 Bind(&if_issmi); |
| 1272 { | 1121 { |
| 1273 Label if_isinbounds(a), if_isoutofbounds(a, Label::kDeferred); | 1122 Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred); |
| 1274 a->Branch(a->SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds); | 1123 Branch(SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds); |
| 1275 | 1124 |
| 1276 a->Bind(&if_isinbounds); | 1125 Bind(&if_isinbounds); |
| 1277 { | 1126 { |
| 1278 var_result.Bind(value_int); | 1127 var_result.Bind(value_int); |
| 1279 a->Goto(&out); | 1128 Goto(&out); |
| 1280 } | 1129 } |
| 1281 | 1130 |
| 1282 a->Bind(&if_isoutofbounds); | 1131 Bind(&if_isoutofbounds); |
| 1283 { | 1132 { |
| 1284 Node* const zero = a->SmiConstant(Smi::kZero); | 1133 Node* const zero = SmiConstant(Smi::kZero); |
| 1285 var_result.Bind(a->SelectTaggedConstant(a->SmiLessThan(value_int, zero), | 1134 var_result.Bind( |
| 1286 zero, limit)); | 1135 SelectTaggedConstant(SmiLessThan(value_int, zero), zero, limit)); |
| 1287 a->Goto(&out); | 1136 Goto(&out); |
| 1288 } | 1137 } |
| 1289 } | 1138 } |
| 1290 | 1139 |
| 1291 a->Bind(&if_isnotsmi); | 1140 Bind(&if_isnotsmi); |
| 1292 { | 1141 { |
| 1293 // {value} is a heap number - in this case, it is definitely out of bounds. | 1142 // {value} is a heap number - in this case, it is definitely out of bounds. |
| 1294 CSA_ASSERT(a, a->IsHeapNumberMap(a->LoadMap(value_int))); | 1143 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(value_int))); |
| 1295 | 1144 |
| 1296 Node* const float_zero = a->Float64Constant(0.); | 1145 Node* const float_zero = Float64Constant(0.); |
| 1297 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1146 Node* const smi_zero = SmiConstant(Smi::kZero); |
| 1298 Node* const value_float = a->LoadHeapNumberValue(value_int); | 1147 Node* const value_float = LoadHeapNumberValue(value_int); |
| 1299 var_result.Bind(a->SelectTaggedConstant( | 1148 var_result.Bind(SelectTaggedConstant( |
| 1300 a->Float64LessThan(value_float, float_zero), smi_zero, limit)); | 1149 Float64LessThan(value_float, float_zero), smi_zero, limit)); |
| 1301 a->Goto(&out); | 1150 Goto(&out); |
| 1302 } | 1151 } |
| 1303 | 1152 |
| 1304 a->Bind(&out); | 1153 Bind(&out); |
| 1305 return var_result.value(); | 1154 return var_result.value(); |
| 1306 } | 1155 } |
| 1307 | 1156 |
| 1308 } // namespace | |
| 1309 | |
| 1310 // ES6 section 21.1.3.19 String.prototype.substring ( start, end ) | 1157 // ES6 section 21.1.3.19 String.prototype.substring ( start, end ) |
| 1311 void Builtins::Generate_StringPrototypeSubstring( | 1158 TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) { |
| 1312 compiler::CodeAssemblerState* state) { | 1159 Label out(this); |
| 1313 typedef CodeStubAssembler::Label Label; | 1160 |
| 1314 typedef compiler::Node Node; | 1161 Variable var_start(this, MachineRepresentation::kTagged); |
| 1315 typedef CodeStubAssembler::Variable Variable; | 1162 Variable var_end(this, MachineRepresentation::kTagged); |
| 1316 CodeStubAssembler a(state); | 1163 |
| 1317 | 1164 Node* const receiver = Parameter(0); |
| 1318 Label out(&a); | 1165 Node* const start = Parameter(1); |
| 1319 | 1166 Node* const end = Parameter(2); |
| 1320 Variable var_start(&a, MachineRepresentation::kTagged); | 1167 Node* const context = Parameter(5); |
| 1321 Variable var_end(&a, MachineRepresentation::kTagged); | |
| 1322 | |
| 1323 Node* const receiver = a.Parameter(0); | |
| 1324 Node* const start = a.Parameter(1); | |
| 1325 Node* const end = a.Parameter(2); | |
| 1326 Node* const context = a.Parameter(5); | |
| 1327 | 1168 |
| 1328 // Check that {receiver} is coercible to Object and convert it to a String. | 1169 // Check that {receiver} is coercible to Object and convert it to a String. |
| 1329 Node* const string = | 1170 Node* const string = |
| 1330 a.ToThisString(context, receiver, "String.prototype.substring"); | 1171 ToThisString(context, receiver, "String.prototype.substring"); |
| 1331 | 1172 |
| 1332 Node* const length = a.LoadStringLength(string); | 1173 Node* const length = LoadStringLength(string); |
| 1333 | 1174 |
| 1334 // Conversion and bounds-checks for {start}. | 1175 // Conversion and bounds-checks for {start}. |
| 1335 var_start.Bind(ToSmiBetweenZeroAnd(&a, context, start, length)); | 1176 var_start.Bind(ToSmiBetweenZeroAnd(context, start, length)); |
| 1336 | 1177 |
| 1337 // Conversion and bounds-checks for {end}. | 1178 // Conversion and bounds-checks for {end}. |
| 1338 { | 1179 { |
| 1339 var_end.Bind(length); | 1180 var_end.Bind(length); |
| 1340 a.GotoIf(a.WordEqual(end, a.UndefinedConstant()), &out); | 1181 GotoIf(WordEqual(end, UndefinedConstant()), &out); |
| 1341 | 1182 |
| 1342 var_end.Bind(ToSmiBetweenZeroAnd(&a, context, end, length)); | 1183 var_end.Bind(ToSmiBetweenZeroAnd(context, end, length)); |
| 1343 | 1184 |
| 1344 Label if_endislessthanstart(&a); | 1185 Label if_endislessthanstart(this); |
| 1345 a.Branch(a.SmiLessThan(var_end.value(), var_start.value()), | 1186 Branch(SmiLessThan(var_end.value(), var_start.value()), |
| 1346 &if_endislessthanstart, &out); | 1187 &if_endislessthanstart, &out); |
| 1347 | 1188 |
| 1348 a.Bind(&if_endislessthanstart); | 1189 Bind(&if_endislessthanstart); |
| 1349 { | 1190 { |
| 1350 Node* const tmp = var_end.value(); | 1191 Node* const tmp = var_end.value(); |
| 1351 var_end.Bind(var_start.value()); | 1192 var_end.Bind(var_start.value()); |
| 1352 var_start.Bind(tmp); | 1193 var_start.Bind(tmp); |
| 1353 a.Goto(&out); | 1194 Goto(&out); |
| 1354 } | 1195 } |
| 1355 } | 1196 } |
| 1356 | 1197 |
| 1357 a.Bind(&out); | 1198 Bind(&out); |
| 1358 { | 1199 { |
| 1359 Node* result = | 1200 Node* result = |
| 1360 a.SubString(context, string, var_start.value(), var_end.value()); | 1201 SubString(context, string, var_start.value(), var_end.value()); |
| 1361 a.Return(result); | 1202 Return(result); |
| 1362 } | 1203 } |
| 1363 } | 1204 } |
| 1364 | 1205 |
| 1365 BUILTIN(StringPrototypeStartsWith) { | 1206 BUILTIN(StringPrototypeStartsWith) { |
| 1366 HandleScope handle_scope(isolate); | 1207 HandleScope handle_scope(isolate); |
| 1367 TO_THIS_STRING(str, "String.prototype.startsWith"); | 1208 TO_THIS_STRING(str, "String.prototype.startsWith"); |
| 1368 | 1209 |
| 1369 // Check if the search string is a regExp and fail if it is. | 1210 // Check if the search string is a regExp and fail if it is. |
| 1370 Handle<Object> search = args.atOrUndefined(isolate, 1); | 1211 Handle<Object> search = args.atOrUndefined(isolate, 1); |
| 1371 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); | 1212 Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search); |
| 1372 if (is_reg_exp.IsNothing()) { | 1213 if (is_reg_exp.IsNothing()) { |
| 1373 DCHECK(isolate->has_pending_exception()); | 1214 DCHECK(isolate->has_pending_exception()); |
| 1374 return isolate->heap()->exception(); | 1215 return isolate->heap()->exception(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1403 | 1244 |
| 1404 for (int i = 0; i < search_string->length(); i++) { | 1245 for (int i = 0; i < search_string->length(); i++) { |
| 1405 if (str_reader.Get(start + i) != search_reader.Get(i)) { | 1246 if (str_reader.Get(start + i) != search_reader.Get(i)) { |
| 1406 return isolate->heap()->false_value(); | 1247 return isolate->heap()->false_value(); |
| 1407 } | 1248 } |
| 1408 } | 1249 } |
| 1409 return isolate->heap()->true_value(); | 1250 return isolate->heap()->true_value(); |
| 1410 } | 1251 } |
| 1411 | 1252 |
| 1412 // ES6 section 21.1.3.25 String.prototype.toString () | 1253 // ES6 section 21.1.3.25 String.prototype.toString () |
| 1413 void Builtins::Generate_StringPrototypeToString( | 1254 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) { |
| 1414 compiler::CodeAssemblerState* state) { | 1255 Node* receiver = Parameter(0); |
| 1415 typedef compiler::Node Node; | 1256 Node* context = Parameter(3); |
| 1416 CodeStubAssembler assembler(state); | |
| 1417 | 1257 |
| 1418 Node* receiver = assembler.Parameter(0); | 1258 Node* result = ToThisValue(context, receiver, PrimitiveType::kString, |
| 1419 Node* context = assembler.Parameter(3); | 1259 "String.prototype.toString"); |
| 1420 | 1260 Return(result); |
| 1421 Node* result = assembler.ToThisValue( | |
| 1422 context, receiver, PrimitiveType::kString, "String.prototype.toString"); | |
| 1423 assembler.Return(result); | |
| 1424 } | 1261 } |
| 1425 | 1262 |
| 1426 // ES6 section 21.1.3.27 String.prototype.trim () | 1263 // ES6 section 21.1.3.27 String.prototype.trim () |
| 1427 BUILTIN(StringPrototypeTrim) { | 1264 BUILTIN(StringPrototypeTrim) { |
| 1428 HandleScope scope(isolate); | 1265 HandleScope scope(isolate); |
| 1429 TO_THIS_STRING(string, "String.prototype.trim"); | 1266 TO_THIS_STRING(string, "String.prototype.trim"); |
| 1430 return *String::Trim(string, String::kTrim); | 1267 return *String::Trim(string, String::kTrim); |
| 1431 } | 1268 } |
| 1432 | 1269 |
| 1433 // Non-standard WebKit extension | 1270 // Non-standard WebKit extension |
| 1434 BUILTIN(StringPrototypeTrimLeft) { | 1271 BUILTIN(StringPrototypeTrimLeft) { |
| 1435 HandleScope scope(isolate); | 1272 HandleScope scope(isolate); |
| 1436 TO_THIS_STRING(string, "String.prototype.trimLeft"); | 1273 TO_THIS_STRING(string, "String.prototype.trimLeft"); |
| 1437 return *String::Trim(string, String::kTrimLeft); | 1274 return *String::Trim(string, String::kTrimLeft); |
| 1438 } | 1275 } |
| 1439 | 1276 |
| 1440 // Non-standard WebKit extension | 1277 // Non-standard WebKit extension |
| 1441 BUILTIN(StringPrototypeTrimRight) { | 1278 BUILTIN(StringPrototypeTrimRight) { |
| 1442 HandleScope scope(isolate); | 1279 HandleScope scope(isolate); |
| 1443 TO_THIS_STRING(string, "String.prototype.trimRight"); | 1280 TO_THIS_STRING(string, "String.prototype.trimRight"); |
| 1444 return *String::Trim(string, String::kTrimRight); | 1281 return *String::Trim(string, String::kTrimRight); |
| 1445 } | 1282 } |
| 1446 | 1283 |
| 1447 // ES6 section 21.1.3.28 String.prototype.valueOf ( ) | 1284 // ES6 section 21.1.3.28 String.prototype.valueOf ( ) |
| 1448 void Builtins::Generate_StringPrototypeValueOf( | 1285 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) { |
| 1449 compiler::CodeAssemblerState* state) { | 1286 Node* receiver = Parameter(0); |
| 1450 typedef compiler::Node Node; | 1287 Node* context = Parameter(3); |
| 1451 CodeStubAssembler assembler(state); | |
| 1452 | 1288 |
| 1453 Node* receiver = assembler.Parameter(0); | 1289 Node* result = ToThisValue(context, receiver, PrimitiveType::kString, |
| 1454 Node* context = assembler.Parameter(3); | 1290 "String.prototype.valueOf"); |
| 1455 | 1291 Return(result); |
| 1456 Node* result = assembler.ToThisValue( | |
| 1457 context, receiver, PrimitiveType::kString, "String.prototype.valueOf"); | |
| 1458 assembler.Return(result); | |
| 1459 } | 1292 } |
| 1460 | 1293 |
| 1461 void Builtins::Generate_StringPrototypeIterator( | 1294 TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) { |
| 1462 compiler::CodeAssemblerState* state) { | 1295 Node* receiver = Parameter(0); |
| 1463 typedef compiler::Node Node; | 1296 Node* context = Parameter(3); |
| 1464 CodeStubAssembler assembler(state); | |
| 1465 | 1297 |
| 1466 Node* receiver = assembler.Parameter(0); | 1298 Node* string = |
| 1467 Node* context = assembler.Parameter(3); | 1299 ToThisString(context, receiver, "String.prototype[Symbol.iterator]"); |
| 1468 | 1300 |
| 1469 Node* string = assembler.ToThisString(context, receiver, | 1301 Node* native_context = LoadNativeContext(context); |
| 1470 "String.prototype[Symbol.iterator]"); | 1302 Node* map = |
| 1471 | 1303 LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX); |
| 1472 Node* native_context = assembler.LoadNativeContext(context); | 1304 Node* iterator = Allocate(JSStringIterator::kSize); |
| 1473 Node* map = assembler.LoadContextElement(native_context, | 1305 StoreMapNoWriteBarrier(iterator, map); |
| 1474 Context::STRING_ITERATOR_MAP_INDEX); | 1306 StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset, |
| 1475 Node* iterator = assembler.Allocate(JSStringIterator::kSize); | 1307 Heap::kEmptyFixedArrayRootIndex); |
| 1476 assembler.StoreMapNoWriteBarrier(iterator, map); | 1308 StoreObjectFieldRoot(iterator, JSObject::kElementsOffset, |
| 1477 assembler.StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset, | 1309 Heap::kEmptyFixedArrayRootIndex); |
| 1478 Heap::kEmptyFixedArrayRootIndex); | 1310 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset, |
| 1479 assembler.StoreObjectFieldRoot(iterator, JSObject::kElementsOffset, | 1311 string); |
| 1480 Heap::kEmptyFixedArrayRootIndex); | 1312 Node* index = SmiConstant(Smi::kZero); |
| 1481 assembler.StoreObjectFieldNoWriteBarrier( | 1313 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset, |
| 1482 iterator, JSStringIterator::kStringOffset, string); | 1314 index); |
| 1483 Node* index = assembler.SmiConstant(Smi::kZero); | 1315 Return(iterator); |
| 1484 assembler.StoreObjectFieldNoWriteBarrier( | |
| 1485 iterator, JSStringIterator::kNextIndexOffset, index); | |
| 1486 assembler.Return(iterator); | |
| 1487 } | 1316 } |
| 1488 | 1317 |
| 1489 namespace { | |
| 1490 | |
| 1491 // Return the |word32| codepoint at {index}. Supports SeqStrings and | 1318 // Return the |word32| codepoint at {index}. Supports SeqStrings and |
| 1492 // ExternalStrings. | 1319 // ExternalStrings. |
| 1493 compiler::Node* LoadSurrogatePairInternal(CodeStubAssembler* assembler, | 1320 compiler::Node* StringBuiltinsAssembler::LoadSurrogatePairAt( |
| 1494 compiler::Node* string, | 1321 compiler::Node* string, compiler::Node* length, compiler::Node* index, |
| 1495 compiler::Node* length, | 1322 UnicodeEncoding encoding) { |
| 1496 compiler::Node* index, | 1323 Label handle_surrogate_pair(this), return_result(this); |
| 1497 UnicodeEncoding encoding) { | 1324 Variable var_result(this, MachineRepresentation::kWord32); |
| 1498 typedef CodeStubAssembler::Label Label; | 1325 Variable var_trail(this, MachineRepresentation::kWord32); |
| 1499 typedef compiler::Node Node; | 1326 var_result.Bind(StringCharCodeAt(string, index)); |
| 1500 typedef CodeStubAssembler::Variable Variable; | 1327 var_trail.Bind(Int32Constant(0)); |
| 1501 Label handle_surrogate_pair(assembler), return_result(assembler); | |
| 1502 Variable var_result(assembler, MachineRepresentation::kWord32); | |
| 1503 Variable var_trail(assembler, MachineRepresentation::kWord32); | |
| 1504 var_result.Bind(assembler->StringCharCodeAt(string, index)); | |
| 1505 var_trail.Bind(assembler->Int32Constant(0)); | |
| 1506 | 1328 |
| 1507 assembler->GotoIf(assembler->Word32NotEqual( | 1329 GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)), |
| 1508 assembler->Word32And(var_result.value(), | 1330 Int32Constant(0xD800)), |
| 1509 assembler->Int32Constant(0xFC00)), | 1331 &return_result); |
| 1510 assembler->Int32Constant(0xD800)), | 1332 Node* next_index = SmiAdd(index, SmiConstant(Smi::FromInt(1))); |
| 1511 &return_result); | |
| 1512 Node* next_index = | |
| 1513 assembler->SmiAdd(index, assembler->SmiConstant(Smi::FromInt(1))); | |
| 1514 | 1333 |
| 1515 assembler->GotoUnless(assembler->SmiLessThan(next_index, length), | 1334 GotoUnless(SmiLessThan(next_index, length), &return_result); |
| 1516 &return_result); | 1335 var_trail.Bind(StringCharCodeAt(string, next_index)); |
| 1517 var_trail.Bind(assembler->StringCharCodeAt(string, next_index)); | 1336 Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)), |
| 1518 assembler->Branch(assembler->Word32Equal( | 1337 Int32Constant(0xDC00)), |
| 1519 assembler->Word32And(var_trail.value(), | 1338 &handle_surrogate_pair, &return_result); |
| 1520 assembler->Int32Constant(0xFC00)), | |
| 1521 assembler->Int32Constant(0xDC00)), | |
| 1522 &handle_surrogate_pair, &return_result); | |
| 1523 | 1339 |
| 1524 assembler->Bind(&handle_surrogate_pair); | 1340 Bind(&handle_surrogate_pair); |
| 1525 { | 1341 { |
| 1526 Node* lead = var_result.value(); | 1342 Node* lead = var_result.value(); |
| 1527 Node* trail = var_trail.value(); | 1343 Node* trail = var_trail.value(); |
| 1528 | 1344 |
| 1529 // Check that this path is only taken if a surrogate pair is found | 1345 // Check that this path is only taken if a surrogate pair is found |
| 1530 CSA_SLOW_ASSERT(assembler, assembler->Uint32GreaterThanOrEqual( | 1346 CSA_SLOW_ASSERT(this, |
| 1531 lead, assembler->Int32Constant(0xD800))); | 1347 Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800))); |
| 1532 CSA_SLOW_ASSERT(assembler, assembler->Uint32LessThan( | 1348 CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00))); |
| 1533 lead, assembler->Int32Constant(0xDC00))); | 1349 CSA_SLOW_ASSERT(this, |
| 1534 CSA_SLOW_ASSERT(assembler, assembler->Uint32GreaterThanOrEqual( | 1350 Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00))); |
| 1535 trail, assembler->Int32Constant(0xDC00))); | 1351 CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000))); |
| 1536 CSA_SLOW_ASSERT(assembler, assembler->Uint32LessThan( | |
| 1537 trail, assembler->Int32Constant(0xE000))); | |
| 1538 | 1352 |
| 1539 switch (encoding) { | 1353 switch (encoding) { |
| 1540 case UnicodeEncoding::UTF16: | 1354 case UnicodeEncoding::UTF16: |
| 1541 var_result.Bind(assembler->Word32Or( | 1355 var_result.Bind(Word32Or( |
| 1542 // Need to swap the order for big-endian platforms | 1356 // Need to swap the order for big-endian platforms |
| 1543 #if V8_TARGET_BIG_ENDIAN | 1357 #if V8_TARGET_BIG_ENDIAN |
| 1544 assembler->Word32Shl(lead, assembler->Int32Constant(16)), trail)); | 1358 Word32Shl(lead, Int32Constant(16)), trail)); |
| 1545 #else | 1359 #else |
| 1546 assembler->Word32Shl(trail, assembler->Int32Constant(16)), lead)); | 1360 Word32Shl(trail, Int32Constant(16)), lead)); |
| 1547 #endif | 1361 #endif |
| 1548 break; | 1362 break; |
| 1549 | 1363 |
| 1550 case UnicodeEncoding::UTF32: { | 1364 case UnicodeEncoding::UTF32: { |
| 1551 // Convert UTF16 surrogate pair into |word32| code point, encoded as | 1365 // Convert UTF16 surrogate pair into |word32| code point, encoded as |
| 1552 // UTF32. | 1366 // UTF32. |
| 1553 Node* surrogate_offset = | 1367 Node* surrogate_offset = |
| 1554 assembler->Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00); | 1368 Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00); |
| 1555 | 1369 |
| 1556 // (lead << 10) + trail + SURROGATE_OFFSET | 1370 // (lead << 10) + trail + SURROGATE_OFFSET |
| 1557 var_result.Bind(assembler->Int32Add( | 1371 var_result.Bind(Int32Add(WordShl(lead, Int32Constant(10)), |
| 1558 assembler->WordShl(lead, assembler->Int32Constant(10)), | 1372 Int32Add(trail, surrogate_offset))); |
| 1559 assembler->Int32Add(trail, surrogate_offset))); | |
| 1560 break; | 1373 break; |
| 1561 } | 1374 } |
| 1562 } | 1375 } |
| 1563 assembler->Goto(&return_result); | 1376 Goto(&return_result); |
| 1564 } | 1377 } |
| 1565 | 1378 |
| 1566 assembler->Bind(&return_result); | 1379 Bind(&return_result); |
| 1567 return var_result.value(); | 1380 return var_result.value(); |
| 1568 } | 1381 } |
| 1569 | 1382 |
| 1570 compiler::Node* LoadSurrogatePairAt(CodeStubAssembler* assembler, | 1383 TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) { |
| 1571 compiler::Node* string, | 1384 Variable var_value(this, MachineRepresentation::kTagged); |
| 1572 compiler::Node* length, | 1385 Variable var_done(this, MachineRepresentation::kTagged); |
| 1573 compiler::Node* index) { | |
| 1574 return LoadSurrogatePairInternal(assembler, string, length, index, | |
| 1575 UnicodeEncoding::UTF16); | |
| 1576 } | |
| 1577 | 1386 |
| 1578 } // namespace | 1387 var_value.Bind(UndefinedConstant()); |
| 1388 var_done.Bind(BooleanConstant(true)); |
| 1579 | 1389 |
| 1580 void Builtins::Generate_StringIteratorPrototypeNext( | 1390 Label throw_bad_receiver(this), next_codepoint(this), return_result(this); |
| 1581 compiler::CodeAssemblerState* state) { | |
| 1582 typedef CodeStubAssembler::Label Label; | |
| 1583 typedef compiler::Node Node; | |
| 1584 typedef CodeStubAssembler::Variable Variable; | |
| 1585 CodeStubAssembler assembler(state); | |
| 1586 | 1391 |
| 1587 Variable var_value(&assembler, MachineRepresentation::kTagged); | 1392 Node* iterator = Parameter(0); |
| 1588 Variable var_done(&assembler, MachineRepresentation::kTagged); | 1393 Node* context = Parameter(3); |
| 1589 | 1394 |
| 1590 var_value.Bind(assembler.UndefinedConstant()); | 1395 GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver); |
| 1591 var_done.Bind(assembler.BooleanConstant(true)); | 1396 GotoUnless(Word32Equal(LoadInstanceType(iterator), |
| 1397 Int32Constant(JS_STRING_ITERATOR_TYPE)), |
| 1398 &throw_bad_receiver); |
| 1592 | 1399 |
| 1593 Label throw_bad_receiver(&assembler), next_codepoint(&assembler), | 1400 Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset); |
| 1594 return_result(&assembler); | 1401 Node* position = |
| 1402 LoadObjectField(iterator, JSStringIterator::kNextIndexOffset); |
| 1403 Node* length = LoadObjectField(string, String::kLengthOffset); |
| 1595 | 1404 |
| 1596 Node* iterator = assembler.Parameter(0); | 1405 Branch(SmiLessThan(position, length), &next_codepoint, &return_result); |
| 1597 Node* context = assembler.Parameter(3); | |
| 1598 | 1406 |
| 1599 assembler.GotoIf(assembler.TaggedIsSmi(iterator), &throw_bad_receiver); | 1407 Bind(&next_codepoint); |
| 1600 assembler.GotoUnless( | |
| 1601 assembler.Word32Equal(assembler.LoadInstanceType(iterator), | |
| 1602 assembler.Int32Constant(JS_STRING_ITERATOR_TYPE)), | |
| 1603 &throw_bad_receiver); | |
| 1604 | |
| 1605 Node* string = | |
| 1606 assembler.LoadObjectField(iterator, JSStringIterator::kStringOffset); | |
| 1607 Node* position = | |
| 1608 assembler.LoadObjectField(iterator, JSStringIterator::kNextIndexOffset); | |
| 1609 Node* length = assembler.LoadObjectField(string, String::kLengthOffset); | |
| 1610 | |
| 1611 assembler.Branch(assembler.SmiLessThan(position, length), &next_codepoint, | |
| 1612 &return_result); | |
| 1613 | |
| 1614 assembler.Bind(&next_codepoint); | |
| 1615 { | 1408 { |
| 1616 Node* ch = LoadSurrogatePairAt(&assembler, string, length, position); | 1409 UnicodeEncoding encoding = UnicodeEncoding::UTF16; |
| 1617 Node* value = assembler.StringFromCodePoint(ch, UnicodeEncoding::UTF16); | 1410 Node* ch = LoadSurrogatePairAt(string, length, position, encoding); |
| 1411 Node* value = StringFromCodePoint(ch, encoding); |
| 1618 var_value.Bind(value); | 1412 var_value.Bind(value); |
| 1619 Node* length = assembler.LoadObjectField(value, String::kLengthOffset); | 1413 Node* length = LoadObjectField(value, String::kLengthOffset); |
| 1620 assembler.StoreObjectFieldNoWriteBarrier( | 1414 StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset, |
| 1621 iterator, JSStringIterator::kNextIndexOffset, | 1415 SmiAdd(position, length)); |
| 1622 assembler.SmiAdd(position, length)); | 1416 var_done.Bind(BooleanConstant(false)); |
| 1623 var_done.Bind(assembler.BooleanConstant(false)); | 1417 Goto(&return_result); |
| 1624 assembler.Goto(&return_result); | |
| 1625 } | 1418 } |
| 1626 | 1419 |
| 1627 assembler.Bind(&return_result); | 1420 Bind(&return_result); |
| 1628 { | 1421 { |
| 1629 Node* native_context = assembler.LoadNativeContext(context); | 1422 Node* native_context = LoadNativeContext(context); |
| 1630 Node* map = assembler.LoadContextElement( | 1423 Node* map = |
| 1631 native_context, Context::ITERATOR_RESULT_MAP_INDEX); | 1424 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX); |
| 1632 Node* result = assembler.Allocate(JSIteratorResult::kSize); | 1425 Node* result = Allocate(JSIteratorResult::kSize); |
| 1633 assembler.StoreMapNoWriteBarrier(result, map); | 1426 StoreMapNoWriteBarrier(result, map); |
| 1634 assembler.StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset, | 1427 StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset, |
| 1635 Heap::kEmptyFixedArrayRootIndex); | 1428 Heap::kEmptyFixedArrayRootIndex); |
| 1636 assembler.StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset, | 1429 StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset, |
| 1637 Heap::kEmptyFixedArrayRootIndex); | 1430 Heap::kEmptyFixedArrayRootIndex); |
| 1638 assembler.StoreObjectFieldNoWriteBarrier( | 1431 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, |
| 1639 result, JSIteratorResult::kValueOffset, var_value.value()); | 1432 var_value.value()); |
| 1640 assembler.StoreObjectFieldNoWriteBarrier( | 1433 StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset, |
| 1641 result, JSIteratorResult::kDoneOffset, var_done.value()); | 1434 var_done.value()); |
| 1642 assembler.Return(result); | 1435 Return(result); |
| 1643 } | 1436 } |
| 1644 | 1437 |
| 1645 assembler.Bind(&throw_bad_receiver); | 1438 Bind(&throw_bad_receiver); |
| 1646 { | 1439 { |
| 1647 // The {receiver} is not a valid JSGeneratorObject. | 1440 // The {receiver} is not a valid JSGeneratorObject. |
| 1648 Node* result = assembler.CallRuntime( | 1441 Node* result = |
| 1649 Runtime::kThrowIncompatibleMethodReceiver, context, | 1442 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, |
| 1650 assembler.HeapConstant(assembler.factory()->NewStringFromAsciiChecked( | 1443 HeapConstant(factory()->NewStringFromAsciiChecked( |
| 1651 "String Iterator.prototype.next", TENURED)), | 1444 "String Iterator.prototype.next", TENURED)), |
| 1652 iterator); | 1445 iterator); |
| 1653 assembler.Return(result); // Never reached. | 1446 Return(result); // Never reached. |
| 1654 } | 1447 } |
| 1655 } | 1448 } |
| 1656 | 1449 |
| 1657 } // namespace internal | 1450 } // namespace internal |
| 1658 } // namespace v8 | 1451 } // namespace v8 |
| OLD | NEW |