1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include <stdlib.h> 29 30 #include "v8.h" 31 32 #include "accessors.h" 33 #include "api.h" 34 #include "arguments.h" 35 #include "bootstrapper.h" 36 #include "codegen.h" 37 #include "compilation-cache.h" 38 #include "compiler.h" 39 #include "cpu.h" 40 #include "dateparser-inl.h" 41 #include "debug.h" 42 #include "deoptimizer.h" 43 #include "date.h" 44 #include "execution.h" 45 #include "global-handles.h" 46 #include "isolate-inl.h" 47 #include "jsregexp.h" 48 #include "json-parser.h" 49 #include "liveedit.h" 50 #include "liveobjectlist-inl.h" 51 #include "misc-intrinsics.h" 52 #include "parser.h" 53 #include "platform.h" 54 #include "runtime-profiler.h" 55 #include "runtime.h" 56 #include "scopeinfo.h" 57 #include "smart-array-pointer.h" 58 #include "string-search.h" 59 #include "stub-cache.h" 60 #include "v8threads.h" 61 #include "vm-state-inl.h" 62 63 namespace v8 { 64 namespace internal { 65 66 67 #define RUNTIME_ASSERT(value) \ 68 if (!(value)) return isolate->ThrowIllegalOperation(); 69 70 // Cast the given object to a value of the specified type and store 71 // it in a variable with the given name. If the object is not of the 72 // expected type call IllegalOperation and return. 73 #define CONVERT_ARG_CHECKED(Type, name, index) \ 74 RUNTIME_ASSERT(args[index]->Is##Type()); \ 75 Type* name = Type::cast(args[index]); 76 77 #define CONVERT_ARG_HANDLE_CHECKED(Type, name, index) \ 78 RUNTIME_ASSERT(args[index]->Is##Type()); \ 79 Handle<Type> name = args.at<Type>(index); 80 81 // Cast the given object to a boolean and store it in a variable with 82 // the given name. If the object is not a boolean call IllegalOperation 83 // and return. 84 #define CONVERT_BOOLEAN_ARG_CHECKED(name, index) \ 85 RUNTIME_ASSERT(args[index]->IsBoolean()); \ 86 bool name = args[index]->IsTrue(); 87 88 // Cast the given argument to a Smi and store its value in an int variable 89 // with the given name. If the argument is not a Smi call IllegalOperation 90 // and return. 91 #define CONVERT_SMI_ARG_CHECKED(name, index) \ 92 RUNTIME_ASSERT(args[index]->IsSmi()); \ 93 int name = args.smi_at(index); 94 95 // Cast the given argument to a double and store it in a variable with 96 // the given name. If the argument is not a number (as opposed to 97 // the number not-a-number) call IllegalOperation and return. 98 #define CONVERT_DOUBLE_ARG_CHECKED(name, index) \ 99 RUNTIME_ASSERT(args[index]->IsNumber()); \ 100 double name = args.number_at(index); 101 102 // Call the specified converter on the object *comand store the result in 103 // a variable of the specified type with the given name. If the 104 // object is not a Number call IllegalOperation and return. 105 #define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \ 106 RUNTIME_ASSERT(obj->IsNumber()); \ 107 type name = NumberTo##Type(obj); 108 109 110 // Cast the given argument to PropertyDetails and store its value in a 111 // variable with the given name. If the argument is not a Smi call 112 // IllegalOperation and return. 113 #define CONVERT_PROPERTY_DETAILS_CHECKED(name, index) \ 114 RUNTIME_ASSERT(args[index]->IsSmi()); \ 115 PropertyDetails name = PropertyDetails(Smi::cast(args[index])); 116 117 118 // Assert that the given argument has a valid value for a StrictModeFlag 119 // and store it in a StrictModeFlag variable with the given name. 120 #define CONVERT_STRICT_MODE_ARG_CHECKED(name, index) \ 121 RUNTIME_ASSERT(args[index]->IsSmi()); \ 122 RUNTIME_ASSERT(args.smi_at(index) == kStrictMode || \ 123 args.smi_at(index) == kNonStrictMode); \ 124 StrictModeFlag name = \ 125 static_cast<StrictModeFlag>(args.smi_at(index)); 126 127 128 // Assert that the given argument has a valid value for a LanguageMode 129 // and store it in a LanguageMode variable with the given name. 130 #define CONVERT_LANGUAGE_MODE_ARG(name, index) \ 131 ASSERT(args[index]->IsSmi()); \ 132 ASSERT(args.smi_at(index) == CLASSIC_MODE || \ 133 args.smi_at(index) == STRICT_MODE || \ 134 args.smi_at(index) == EXTENDED_MODE); \ 135 LanguageMode name = \ 136 static_cast<LanguageMode>(args.smi_at(index)); 137 138 139 MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, 140 JSObject* boilerplate) { 141 StackLimitCheck check(isolate); 142 if (check.HasOverflowed()) return isolate->StackOverflow(); 143 144 Heap* heap = isolate->heap(); 145 Object* result; 146 { MaybeObject* maybe_result = heap->CopyJSObject(boilerplate); 147 if (!maybe_result->ToObject(&result)) return maybe_result; 148 } 149 JSObject* copy = JSObject::cast(result); 150 151 // Deep copy local properties. 152 if (copy->HasFastProperties()) { 153 FixedArray* properties = copy->properties(); 154 for (int i = 0; i < properties->length(); i++) { 155 Object* value = properties->get(i); 156 if (value->IsJSObject()) { 157 JSObject* js_object = JSObject::cast(value); 158 { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, js_object); 159 if (!maybe_result->ToObject(&result)) return maybe_result; 160 } 161 properties->set(i, result); 162 } 163 } 164 int nof = copy->map()->inobject_properties(); 165 for (int i = 0; i < nof; i++) { 166 Object* value = copy->InObjectPropertyAt(i); 167 if (value->IsJSObject()) { 168 JSObject* js_object = JSObject::cast(value); 169 { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, js_object); 170 if (!maybe_result->ToObject(&result)) return maybe_result; 171 } 172 copy->InObjectPropertyAtPut(i, result); 173 } 174 } 175 } else { 176 { MaybeObject* maybe_result = 177 heap->AllocateFixedArray(copy->NumberOfLocalProperties()); 178 if (!maybe_result->ToObject(&result)) return maybe_result; 179 } 180 FixedArray* names = FixedArray::cast(result); 181 copy->GetLocalPropertyNames(names, 0); 182 for (int i = 0; i < names->length(); i++) { 183 ASSERT(names->get(i)->IsString()); 184 String* key_string = String::cast(names->get(i)); 185 PropertyAttributes attributes = 186 copy->GetLocalPropertyAttribute(key_string); 187 // Only deep copy fields from the object literal expression. 188 // In particular, don't try to copy the length attribute of 189 // an array. 190 if (attributes != NONE) continue; 191 Object* value = 192 copy->GetProperty(key_string, &attributes)->ToObjectUnchecked(); 193 if (value->IsJSObject()) { 194 JSObject* js_object = JSObject::cast(value); 195 { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, js_object); 196 if (!maybe_result->ToObject(&result)) return maybe_result; 197 } 198 { MaybeObject* maybe_result = 199 // Creating object copy for literals. No strict mode needed. 200 copy->SetProperty(key_string, result, NONE, kNonStrictMode); 201 if (!maybe_result->ToObject(&result)) return maybe_result; 202 } 203 } 204 } 205 } 206 207 // Deep copy local elements. 208 // Pixel elements cannot be created using an object literal. 209 ASSERT(!copy->HasExternalArrayElements()); 210 switch (copy->GetElementsKind()) { 211 case FAST_SMI_ONLY_ELEMENTS: 212 case FAST_ELEMENTS: { 213 FixedArray* elements = FixedArray::cast(copy->elements()); 214 if (elements->map() == heap->fixed_cow_array_map()) { 215 isolate->counters()->cow_arrays_created_runtime()->Increment(); 216 #ifdef DEBUG 217 for (int i = 0; i < elements->length(); i++) { 218 ASSERT(!elements->get(i)->IsJSObject()); 219 } 220 #endif 221 } else { 222 for (int i = 0; i < elements->length(); i++) { 223 Object* value = elements->get(i); 224 ASSERT(value->IsSmi() || 225 value->IsTheHole() || 226 (copy->GetElementsKind() == FAST_ELEMENTS)); 227 if (value->IsJSObject()) { 228 JSObject* js_object = JSObject::cast(value); 229 { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, 230 js_object); 231 if (!maybe_result->ToObject(&result)) return maybe_result; 232 } 233 elements->set(i, result); 234 } 235 } 236 } 237 break; 238 } 239 case DICTIONARY_ELEMENTS: { 240 SeededNumberDictionary* element_dictionary = copy->element_dictionary(); 241 int capacity = element_dictionary->Capacity(); 242 for (int i = 0; i < capacity; i++) { 243 Object* k = element_dictionary->KeyAt(i); 244 if (element_dictionary->IsKey(k)) { 245 Object* value = element_dictionary->ValueAt(i); 246 if (value->IsJSObject()) { 247 JSObject* js_object = JSObject::cast(value); 248 { MaybeObject* maybe_result = DeepCopyBoilerplate(isolate, 249 js_object); 250 if (!maybe_result->ToObject(&result)) return maybe_result; 251 } 252 element_dictionary->ValueAtPut(i, result); 253 } 254 } 255 } 256 break; 257 } 258 case NON_STRICT_ARGUMENTS_ELEMENTS: 259 UNIMPLEMENTED(); 260 break; 261 case EXTERNAL_PIXEL_ELEMENTS: 262 case EXTERNAL_BYTE_ELEMENTS: 263 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 264 case EXTERNAL_SHORT_ELEMENTS: 265 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 266 case EXTERNAL_INT_ELEMENTS: 267 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 268 case EXTERNAL_FLOAT_ELEMENTS: 269 case EXTERNAL_DOUBLE_ELEMENTS: 270 case FAST_DOUBLE_ELEMENTS: 271 // No contained objects, nothing to do. 272 break; 273 } 274 return copy; 275 } 276 277 278 static Handle<Map> ComputeObjectLiteralMap( 279 Handle<Context> context, 280 Handle<FixedArray> constant_properties, 281 bool* is_result_from_cache) { 282 Isolate* isolate = context->GetIsolate(); 283 int properties_length = constant_properties->length(); 284 int number_of_properties = properties_length / 2; 285 // Check that there are only symbols and array indices among keys. 286 int number_of_symbol_keys = 0; 287 for (int p = 0; p != properties_length; p += 2) { 288 Object* key = constant_properties->get(p); 289 uint32_t element_index = 0; 290 if (key->IsSymbol()) { 291 number_of_symbol_keys++; 292 } else if (key->ToArrayIndex(&element_index)) { 293 // An index key does not require space in the property backing store. 294 number_of_properties--; 295 } else { 296 // Bail out as a non-symbol non-index key makes caching impossible. 297 // ASSERT to make sure that the if condition after the loop is false. 298 ASSERT(number_of_symbol_keys != number_of_properties); 299 break; 300 } 301 } 302 // If we only have symbols and array indices among keys then we can 303 // use the map cache in the global context. 304 const int kMaxKeys = 10; 305 if ((number_of_symbol_keys == number_of_properties) && 306 (number_of_symbol_keys < kMaxKeys)) { 307 // Create the fixed array with the key. 308 Handle<FixedArray> keys = 309 isolate->factory()->NewFixedArray(number_of_symbol_keys); 310 if (number_of_symbol_keys > 0) { 311 int index = 0; 312 for (int p = 0; p < properties_length; p += 2) { 313 Object* key = constant_properties->get(p); 314 if (key->IsSymbol()) { 315 keys->set(index++, key); 316 } 317 } 318 ASSERT(index == number_of_symbol_keys); 319 } 320 *is_result_from_cache = true; 321 return isolate->factory()->ObjectLiteralMapFromCache(context, keys); 322 } 323 *is_result_from_cache = false; 324 return isolate->factory()->CopyMap( 325 Handle<Map>(context->object_function()->initial_map()), 326 number_of_properties); 327 } 328 329 330 static Handle<Object> CreateLiteralBoilerplate( 331 Isolate* isolate, 332 Handle<FixedArray> literals, 333 Handle<FixedArray> constant_properties); 334 335 336 static Handle<Object> CreateObjectLiteralBoilerplate( 337 Isolate* isolate, 338 Handle<FixedArray> literals, 339 Handle<FixedArray> constant_properties, 340 bool should_have_fast_elements, 341 bool has_function_literal) { 342 // Get the global context from the literals array. This is the 343 // context in which the function was created and we use the object 344 // function from this context to create the object literal. We do 345 // not use the object function from the current global context 346 // because this might be the object function from another context 347 // which we should not have access to. 348 Handle<Context> context = 349 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals)); 350 351 // In case we have function literals, we want the object to be in 352 // slow properties mode for now. We don't go in the map cache because 353 // maps with constant functions can't be shared if the functions are 354 // not the same (which is the common case). 355 bool is_result_from_cache = false; 356 Handle<Map> map = has_function_literal 357 ? Handle<Map>(context->object_function()->initial_map()) 358 : ComputeObjectLiteralMap(context, 359 constant_properties, 360 &is_result_from_cache); 361 362 Handle<JSObject> boilerplate = isolate->factory()->NewJSObjectFromMap(map); 363 364 // Normalize the elements of the boilerplate to save space if needed. 365 if (!should_have_fast_elements) JSObject::NormalizeElements(boilerplate); 366 367 // Add the constant properties to the boilerplate. 368 int length = constant_properties->length(); 369 bool should_transform = 370 !is_result_from_cache && boilerplate->HasFastProperties(); 371 if (should_transform || has_function_literal) { 372 // Normalize the properties of object to avoid n^2 behavior 373 // when extending the object multiple properties. Indicate the number of 374 // properties to be added. 375 JSObject::NormalizeProperties( 376 boilerplate, KEEP_INOBJECT_PROPERTIES, length / 2); 377 } 378 379 for (int index = 0; index < length; index +=2) { 380 Handle<Object> key(constant_properties->get(index+0), isolate); 381 Handle<Object> value(constant_properties->get(index+1), isolate); 382 if (value->IsFixedArray()) { 383 // The value contains the constant_properties of a 384 // simple object or array literal. 385 Handle<FixedArray> array = Handle<FixedArray>::cast(value); 386 value = CreateLiteralBoilerplate(isolate, literals, array); 387 if (value.is_null()) return value; 388 } 389 Handle<Object> result; 390 uint32_t element_index = 0; 391 if (key->IsSymbol()) { 392 if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) { 393 // Array index as string (uint32). 394 result = JSObject::SetOwnElement( 395 boilerplate, element_index, value, kNonStrictMode); 396 } else { 397 Handle<String> name(String::cast(*key)); 398 ASSERT(!name->AsArrayIndex(&element_index)); 399 result = JSObject::SetLocalPropertyIgnoreAttributes( 400 boilerplate, name, value, NONE); 401 } 402 } else if (key->ToArrayIndex(&element_index)) { 403 // Array index (uint32). 404 result = JSObject::SetOwnElement( 405 boilerplate, element_index, value, kNonStrictMode); 406 } else { 407 // Non-uint32 number. 408 ASSERT(key->IsNumber()); 409 double num = key->Number(); 410 char arr[100]; 411 Vector<char> buffer(arr, ARRAY_SIZE(arr)); 412 const char* str = DoubleToCString(num, buffer); 413 Handle<String> name = 414 isolate->factory()->NewStringFromAscii(CStrVector(str)); 415 result = JSObject::SetLocalPropertyIgnoreAttributes( 416 boilerplate, name, value, NONE); 417 } 418 // If setting the property on the boilerplate throws an 419 // exception, the exception is converted to an empty handle in 420 // the handle based operations. In that case, we need to 421 // convert back to an exception. 422 if (result.is_null()) return result; 423 } 424 425 // Transform to fast properties if necessary. For object literals with 426 // containing function literals we defer this operation until after all 427 // computed properties have been assigned so that we can generate 428 // constant function properties. 429 if (should_transform && !has_function_literal) { 430 JSObject::TransformToFastProperties( 431 boilerplate, boilerplate->map()->unused_property_fields()); 432 } 433 434 return boilerplate; 435 } 436 437 438 MaybeObject* TransitionElements(Handle<Object> object, 439 ElementsKind to_kind, 440 Isolate* isolate) { 441 HandleScope scope(isolate); 442 if (!object->IsJSObject()) return isolate->ThrowIllegalOperation(); 443 ElementsKind from_kind = 444 Handle<JSObject>::cast(object)->map()->elements_kind(); 445 if (Map::IsValidElementsTransition(from_kind, to_kind)) { 446 Handle<Object> result = JSObject::TransitionElementsKind( 447 Handle<JSObject>::cast(object), to_kind); 448 if (result.is_null()) return isolate->ThrowIllegalOperation(); 449 return *result; 450 } 451 return isolate->ThrowIllegalOperation(); 452 } 453 454 455 static const int kSmiOnlyLiteralMinimumLength = 1024; 456 457 458 Handle<Object> Runtime::CreateArrayLiteralBoilerplate( 459 Isolate* isolate, 460 Handle<FixedArray> literals, 461 Handle<FixedArray> elements) { 462 // Create the JSArray. 463 Handle<JSFunction> constructor( 464 JSFunction::GlobalContextFromLiterals(*literals)->array_function()); 465 Handle<JSArray> object = 466 Handle<JSArray>::cast(isolate->factory()->NewJSObject(constructor)); 467 468 ElementsKind constant_elements_kind = 469 static_cast<ElementsKind>(Smi::cast(elements->get(0))->value()); 470 Handle<FixedArrayBase> constant_elements_values( 471 FixedArrayBase::cast(elements->get(1))); 472 473 Context* global_context = isolate->context()->global_context(); 474 if (constant_elements_kind == FAST_SMI_ONLY_ELEMENTS) { 475 object->set_map(Map::cast(global_context->smi_js_array_map())); 476 } else if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { 477 object->set_map(Map::cast(global_context->double_js_array_map())); 478 } else { 479 object->set_map(Map::cast(global_context->object_js_array_map())); 480 } 481 482 Handle<FixedArrayBase> copied_elements_values; 483 if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { 484 ASSERT(FLAG_smi_only_arrays); 485 copied_elements_values = isolate->factory()->CopyFixedDoubleArray( 486 Handle<FixedDoubleArray>::cast(constant_elements_values)); 487 } else { 488 ASSERT(constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || 489 constant_elements_kind == FAST_ELEMENTS); 490 const bool is_cow = 491 (constant_elements_values->map() == 492 isolate->heap()->fixed_cow_array_map()); 493 if (is_cow) { 494 copied_elements_values = constant_elements_values; 495 #if DEBUG 496 Handle<FixedArray> fixed_array_values = 497 Handle<FixedArray>::cast(copied_elements_values); 498 for (int i = 0; i < fixed_array_values->length(); i++) { 499 ASSERT(!fixed_array_values->get(i)->IsFixedArray()); 500 } 501 #endif 502 } else { 503 Handle<FixedArray> fixed_array_values = 504 Handle<FixedArray>::cast(constant_elements_values); 505 Handle<FixedArray> fixed_array_values_copy = 506 isolate->factory()->CopyFixedArray(fixed_array_values); 507 copied_elements_values = fixed_array_values_copy; 508 for (int i = 0; i < fixed_array_values->length(); i++) { 509 Object* current = fixed_array_values->get(i); 510 if (current->IsFixedArray()) { 511 // The value contains the constant_properties of a 512 // simple object or array literal. 513 Handle<FixedArray> fa(FixedArray::cast(fixed_array_values->get(i))); 514 Handle<Object> result = 515 CreateLiteralBoilerplate(isolate, literals, fa); 516 if (result.is_null()) return result; 517 fixed_array_values_copy->set(i, *result); 518 } 519 } 520 } 521 } 522 object->set_elements(*copied_elements_values); 523 object->set_length(Smi::FromInt(copied_elements_values->length())); 524 525 // Ensure that the boilerplate object has FAST_ELEMENTS, unless the flag is 526 // on or the object is larger than the threshold. 527 if (!FLAG_smi_only_arrays && 528 constant_elements_values->length() < kSmiOnlyLiteralMinimumLength) { 529 if (object->GetElementsKind() != FAST_ELEMENTS) { 530 CHECK(!TransitionElements(object, FAST_ELEMENTS, isolate)->IsFailure()); 531 } 532 } 533 534 return object; 535 } 536 537 538 static Handle<Object> CreateLiteralBoilerplate( 539 Isolate* isolate, 540 Handle<FixedArray> literals, 541 Handle<FixedArray> array) { 542 Handle<FixedArray> elements = CompileTimeValue::GetElements(array); 543 const bool kHasNoFunctionLiteral = false; 544 switch (CompileTimeValue::GetType(array)) { 545 case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS: 546 return CreateObjectLiteralBoilerplate(isolate, 547 literals, 548 elements, 549 true, 550 kHasNoFunctionLiteral); 551 case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS: 552 return CreateObjectLiteralBoilerplate(isolate, 553 literals, 554 elements, 555 false, 556 kHasNoFunctionLiteral); 557 case CompileTimeValue::ARRAY_LITERAL: 558 return Runtime::CreateArrayLiteralBoilerplate( 559 isolate, literals, elements); 560 default: 561 UNREACHABLE(); 562 return Handle<Object>::null(); 563 } 564 } 565 566 567 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateObjectLiteral) { 568 HandleScope scope(isolate); 569 ASSERT(args.length() == 4); 570 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); 571 CONVERT_SMI_ARG_CHECKED(literals_index, 1); 572 CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2); 573 CONVERT_SMI_ARG_CHECKED(flags, 3); 574 bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0; 575 bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0; 576 577 // Check if boilerplate exists. If not, create it first. 578 Handle<Object> boilerplate(literals->get(literals_index), isolate); 579 if (*boilerplate == isolate->heap()->undefined_value()) { 580 boilerplate = CreateObjectLiteralBoilerplate(isolate, 581 literals, 582 constant_properties, 583 should_have_fast_elements, 584 has_function_literal); 585 if (boilerplate.is_null()) return Failure::Exception(); 586 // Update the functions literal and return the boilerplate. 587 literals->set(literals_index, *boilerplate); 588 } 589 return DeepCopyBoilerplate(isolate, JSObject::cast(*boilerplate)); 590 } 591 592 593 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateObjectLiteralShallow) { 594 HandleScope scope(isolate); 595 ASSERT(args.length() == 4); 596 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); 597 CONVERT_SMI_ARG_CHECKED(literals_index, 1); 598 CONVERT_ARG_HANDLE_CHECKED(FixedArray, constant_properties, 2); 599 CONVERT_SMI_ARG_CHECKED(flags, 3); 600 bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0; 601 bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0; 602 603 // Check if boilerplate exists. If not, create it first. 604 Handle<Object> boilerplate(literals->get(literals_index), isolate); 605 if (*boilerplate == isolate->heap()->undefined_value()) { 606 boilerplate = CreateObjectLiteralBoilerplate(isolate, 607 literals, 608 constant_properties, 609 should_have_fast_elements, 610 has_function_literal); 611 if (boilerplate.is_null()) return Failure::Exception(); 612 // Update the functions literal and return the boilerplate. 613 literals->set(literals_index, *boilerplate); 614 } 615 return isolate->heap()->CopyJSObject(JSObject::cast(*boilerplate)); 616 } 617 618 619 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteral) { 620 HandleScope scope(isolate); 621 ASSERT(args.length() == 3); 622 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); 623 CONVERT_SMI_ARG_CHECKED(literals_index, 1); 624 CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2); 625 626 // Check if boilerplate exists. If not, create it first. 627 Handle<Object> boilerplate(literals->get(literals_index), isolate); 628 if (*boilerplate == isolate->heap()->undefined_value()) { 629 boilerplate = 630 Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements); 631 if (boilerplate.is_null()) return Failure::Exception(); 632 // Update the functions literal and return the boilerplate. 633 literals->set(literals_index, *boilerplate); 634 } 635 return DeepCopyBoilerplate(isolate, JSObject::cast(*boilerplate)); 636 } 637 638 639 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) { 640 HandleScope scope(isolate); 641 ASSERT(args.length() == 3); 642 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); 643 CONVERT_SMI_ARG_CHECKED(literals_index, 1); 644 CONVERT_ARG_HANDLE_CHECKED(FixedArray, elements, 2); 645 646 // Check if boilerplate exists. If not, create it first. 647 Handle<Object> boilerplate(literals->get(literals_index), isolate); 648 if (*boilerplate == isolate->heap()->undefined_value()) { 649 ASSERT(*elements != isolate->heap()->empty_fixed_array()); 650 boilerplate = 651 Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements); 652 if (boilerplate.is_null()) return Failure::Exception(); 653 // Update the functions literal and return the boilerplate. 654 literals->set(literals_index, *boilerplate); 655 } 656 if (JSObject::cast(*boilerplate)->elements()->map() == 657 isolate->heap()->fixed_cow_array_map()) { 658 isolate->counters()->cow_arrays_created_runtime()->Increment(); 659 } 660 return isolate->heap()->CopyJSObject(JSObject::cast(*boilerplate)); 661 } 662 663 664 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) { 665 ASSERT(args.length() == 2); 666 Object* handler = args[0]; 667 Object* prototype = args[1]; 668 Object* used_prototype = 669 prototype->IsJSReceiver() ? prototype : isolate->heap()->null_value(); 670 return isolate->heap()->AllocateJSProxy(handler, used_prototype); 671 } 672 673 674 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSFunctionProxy) { 675 ASSERT(args.length() == 4); 676 Object* handler = args[0]; 677 Object* call_trap = args[1]; 678 Object* construct_trap = args[2]; 679 Object* prototype = args[3]; 680 Object* used_prototype = 681 prototype->IsJSReceiver() ? prototype : isolate->heap()->null_value(); 682 return isolate->heap()->AllocateJSFunctionProxy( 683 handler, call_trap, construct_trap, used_prototype); 684 } 685 686 687 RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSProxy) { 688 ASSERT(args.length() == 1); 689 Object* obj = args[0]; 690 return isolate->heap()->ToBoolean(obj->IsJSProxy()); 691 } 692 693 694 RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSFunctionProxy) { 695 ASSERT(args.length() == 1); 696 Object* obj = args[0]; 697 return isolate->heap()->ToBoolean(obj->IsJSFunctionProxy()); 698 } 699 700 701 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) { 702 ASSERT(args.length() == 1); 703 CONVERT_ARG_CHECKED(JSProxy, proxy, 0); 704 return proxy->handler(); 705 } 706 707 708 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetCallTrap) { 709 ASSERT(args.length() == 1); 710 CONVERT_ARG_CHECKED(JSFunctionProxy, proxy, 0); 711 return proxy->call_trap(); 712 } 713 714 715 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetConstructTrap) { 716 ASSERT(args.length() == 1); 717 CONVERT_ARG_CHECKED(JSFunctionProxy, proxy, 0); 718 return proxy->construct_trap(); 719 } 720 721 722 RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) { 723 ASSERT(args.length() == 1); 724 CONVERT_ARG_CHECKED(JSProxy, proxy, 0); 725 proxy->Fix(); 726 return isolate->heap()->undefined_value(); 727 } 728 729 730 RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInitialize) { 731 HandleScope scope(isolate); 732 ASSERT(args.length() == 1); 733 CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); 734 Handle<ObjectHashSet> table = isolate->factory()->NewObjectHashSet(0); 735 holder->set_table(*table); 736 return *holder; 737 } 738 739 740 RUNTIME_FUNCTION(MaybeObject*, Runtime_SetAdd) { 741 HandleScope scope(isolate); 742 ASSERT(args.length() == 2); 743 CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); 744 Handle<Object> key(args[1]); 745 Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); 746 table = ObjectHashSetAdd(table, key); 747 holder->set_table(*table); 748 return isolate->heap()->undefined_symbol(); 749 } 750 751 752 RUNTIME_FUNCTION(MaybeObject*, Runtime_SetHas) { 753 HandleScope scope(isolate); 754 ASSERT(args.length() == 2); 755 CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); 756 Handle<Object> key(args[1]); 757 Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); 758 return isolate->heap()->ToBoolean(table->Contains(*key)); 759 } 760 761 762 RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDelete) { 763 HandleScope scope(isolate); 764 ASSERT(args.length() == 2); 765 CONVERT_ARG_HANDLE_CHECKED(JSSet, holder, 0); 766 Handle<Object> key(args[1]); 767 Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); 768 table = ObjectHashSetRemove(table, key); 769 holder->set_table(*table); 770 return isolate->heap()->undefined_symbol(); 771 } 772 773 774 RUNTIME_FUNCTION(MaybeObject*, Runtime_MapInitialize) { 775 HandleScope scope(isolate); 776 ASSERT(args.length() == 1); 777 CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); 778 Handle<ObjectHashTable> table = isolate->factory()->NewObjectHashTable(0); 779 holder->set_table(*table); 780 return *holder; 781 } 782 783 784 RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGet) { 785 HandleScope scope(isolate); 786 ASSERT(args.length() == 2); 787 CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); 788 Handle<Object> key(args[1]); 789 return ObjectHashTable::cast(holder->table())->Lookup(*key); 790 } 791 792 793 RUNTIME_FUNCTION(MaybeObject*, Runtime_MapSet) { 794 HandleScope scope(isolate); 795 ASSERT(args.length() == 3); 796 CONVERT_ARG_HANDLE_CHECKED(JSMap, holder, 0); 797 Handle<Object> key(args[1]); 798 Handle<Object> value(args[2]); 799 Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table())); 800 Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value); 801 holder->set_table(*new_table); 802 return *value; 803 } 804 805 806 RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapInitialize) { 807 HandleScope scope(isolate); 808 ASSERT(args.length() == 1); 809 CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); 810 ASSERT(weakmap->map()->inobject_properties() == 0); 811 Handle<ObjectHashTable> table = isolate->factory()->NewObjectHashTable(0); 812 weakmap->set_table(*table); 813 weakmap->set_next(Smi::FromInt(0)); 814 return *weakmap; 815 } 816 817 818 RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapGet) { 819 NoHandleAllocation ha; 820 ASSERT(args.length() == 2); 821 CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); 822 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, key, 1); 823 return ObjectHashTable::cast(weakmap->table())->Lookup(*key); 824 } 825 826 827 RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapSet) { 828 HandleScope scope(isolate); 829 ASSERT(args.length() == 3); 830 CONVERT_ARG_HANDLE_CHECKED(JSWeakMap, weakmap, 0); 831 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, key, 1); 832 Handle<Object> value(args[2]); 833 Handle<ObjectHashTable> table(ObjectHashTable::cast(weakmap->table())); 834 Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value); 835 weakmap->set_table(*new_table); 836 return *value; 837 } 838 839 840 RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) { 841 NoHandleAllocation ha; 842 ASSERT(args.length() == 1); 843 Object* obj = args[0]; 844 if (!obj->IsJSObject()) return isolate->heap()->null_value(); 845 return JSObject::cast(obj)->class_name(); 846 } 847 848 849 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPrototype) { 850 NoHandleAllocation ha; 851 ASSERT(args.length() == 1); 852 CONVERT_ARG_CHECKED(JSReceiver, input_obj, 0); 853 Object* obj = input_obj; 854 // We don't expect access checks to be needed on JSProxy objects. 855 ASSERT(!obj->IsAccessCheckNeeded() || obj->IsJSObject()); 856 do { 857 if (obj->IsAccessCheckNeeded() && 858 !isolate->MayNamedAccess(JSObject::cast(obj), 859 isolate->heap()->Proto_symbol(), 860 v8::ACCESS_GET)) { 861 isolate->ReportFailedAccessCheck(JSObject::cast(obj), v8::ACCESS_GET); 862 return isolate->heap()->undefined_value(); 863 } 864 obj = obj->GetPrototype(); 865 } while (obj->IsJSObject() && 866 JSObject::cast(obj)->map()->is_hidden_prototype()); 867 return obj; 868 } 869 870 871 RUNTIME_FUNCTION(MaybeObject*, Runtime_IsInPrototypeChain) { 872 NoHandleAllocation ha; 873 ASSERT(args.length() == 2); 874 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8). 875 Object* O = args[0]; 876 Object* V = args[1]; 877 while (true) { 878 Object* prototype = V->GetPrototype(); 879 if (prototype->IsNull()) return isolate->heap()->false_value(); 880 if (O == prototype) return isolate->heap()->true_value(); 881 V = prototype; 882 } 883 } 884 885 886 // Recursively traverses hidden prototypes if property is not found 887 static void GetOwnPropertyImplementation(JSObject* obj, 888 String* name, 889 LookupResult* result) { 890 obj->LocalLookupRealNamedProperty(name, result); 891 892 if (!result->IsProperty()) { 893 Object* proto = obj->GetPrototype(); 894 if (proto->IsJSObject() && 895 JSObject::cast(proto)->map()->is_hidden_prototype()) 896 GetOwnPropertyImplementation(JSObject::cast(proto), 897 name, result); 898 } 899 } 900 901 902 static bool CheckAccessException(LookupResult* result, 903 v8::AccessType access_type) { 904 if (result->type() == CALLBACKS) { 905 Object* callback = result->GetCallbackObject(); 906 if (callback->IsAccessorInfo()) { 907 AccessorInfo* info = AccessorInfo::cast(callback); 908 bool can_access = 909 (access_type == v8::ACCESS_HAS && 910 (info->all_can_read() || info->all_can_write())) || 911 (access_type == v8::ACCESS_GET && info->all_can_read()) || 912 (access_type == v8::ACCESS_SET && info->all_can_write()); 913 return can_access; 914 } 915 } 916 917 return false; 918 } 919 920 921 static bool CheckAccess(JSObject* obj, 922 String* name, 923 LookupResult* result, 924 v8::AccessType access_type) { 925 ASSERT(result->IsProperty()); 926 927 JSObject* holder = result->holder(); 928 JSObject* current = obj; 929 Isolate* isolate = obj->GetIsolate(); 930 while (true) { 931 if (current->IsAccessCheckNeeded() && 932 !isolate->MayNamedAccess(current, name, access_type)) { 933 // Access check callback denied the access, but some properties 934 // can have a special permissions which override callbacks descision 935 // (currently see v8::AccessControl). 936 break; 937 } 938 939 if (current == holder) { 940 return true; 941 } 942 943 current = JSObject::cast(current->GetPrototype()); 944 } 945 946 // API callbacks can have per callback access exceptions. 947 switch (result->type()) { 948 case CALLBACKS: { 949 if (CheckAccessException(result, access_type)) { 950 return true; 951 } 952 break; 953 } 954 case INTERCEPTOR: { 955 // If the object has an interceptor, try real named properties. 956 // Overwrite the result to fetch the correct property later. 957 holder->LookupRealNamedProperty(name, result); 958 if (result->IsProperty()) { 959 if (CheckAccessException(result, access_type)) { 960 return true; 961 } 962 } 963 break; 964 } 965 default: 966 break; 967 } 968 969 isolate->ReportFailedAccessCheck(current, access_type); 970 return false; 971 } 972 973 974 // TODO(1095): we should traverse hidden prototype hierachy as well. 975 static bool CheckElementAccess(JSObject* obj, 976 uint32_t index, 977 v8::AccessType access_type) { 978 if (obj->IsAccessCheckNeeded() && 979 !obj->GetIsolate()->MayIndexedAccess(obj, index, access_type)) { 980 return false; 981 } 982 983 return true; 984 } 985 986 987 // Enumerator used as indices into the array returned from GetOwnProperty 988 enum PropertyDescriptorIndices { 989 IS_ACCESSOR_INDEX, 990 VALUE_INDEX, 991 GETTER_INDEX, 992 SETTER_INDEX, 993 WRITABLE_INDEX, 994 ENUMERABLE_INDEX, 995 CONFIGURABLE_INDEX, 996 DESCRIPTOR_SIZE 997 }; 998 999 1000 static MaybeObject* GetOwnProperty(Isolate* isolate, 1001 Handle<JSObject> obj, 1002 Handle<String> name) { 1003 Heap* heap = isolate->heap(); 1004 Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE); 1005 Handle<JSArray> desc = isolate->factory()->NewJSArrayWithElements(elms); 1006 LookupResult result(isolate); 1007 // This could be an element. 1008 uint32_t index; 1009 if (name->AsArrayIndex(&index)) { 1010 switch (obj->HasLocalElement(index)) { 1011 case JSObject::UNDEFINED_ELEMENT: 1012 return heap->undefined_value(); 1013 1014 case JSObject::STRING_CHARACTER_ELEMENT: { 1015 // Special handling of string objects according to ECMAScript 5 1016 // 15.5.5.2. Note that this might be a string object with elements 1017 // other than the actual string value. This is covered by the 1018 // subsequent cases. 1019 Handle<JSValue> js_value = Handle<JSValue>::cast(obj); 1020 Handle<String> str(String::cast(js_value->value())); 1021 Handle<String> substr = SubString(str, index, index + 1, NOT_TENURED); 1022 1023 elms->set(IS_ACCESSOR_INDEX, heap->false_value()); 1024 elms->set(VALUE_INDEX, *substr); 1025 elms->set(WRITABLE_INDEX, heap->false_value()); 1026 elms->set(ENUMERABLE_INDEX, heap->true_value()); 1027 elms->set(CONFIGURABLE_INDEX, heap->false_value()); 1028 return *desc; 1029 } 1030 1031 case JSObject::INTERCEPTED_ELEMENT: 1032 case JSObject::FAST_ELEMENT: { 1033 elms->set(IS_ACCESSOR_INDEX, heap->false_value()); 1034 Handle<Object> value = Object::GetElement(obj, index); 1035 RETURN_IF_EMPTY_HANDLE(isolate, value); 1036 elms->set(VALUE_INDEX, *value); 1037 elms->set(WRITABLE_INDEX, heap->true_value()); 1038 elms->set(ENUMERABLE_INDEX, heap->true_value()); 1039 elms->set(CONFIGURABLE_INDEX, heap->true_value()); 1040 return *desc; 1041 } 1042 1043 case JSObject::DICTIONARY_ELEMENT: { 1044 Handle<JSObject> holder = obj; 1045 if (obj->IsJSGlobalProxy()) { 1046 Object* proto = obj->GetPrototype(); 1047 if (proto->IsNull()) return heap->undefined_value(); 1048 ASSERT(proto->IsJSGlobalObject()); 1049 holder = Handle<JSObject>(JSObject::cast(proto)); 1050 } 1051 FixedArray* elements = FixedArray::cast(holder->elements()); 1052 SeededNumberDictionary* dictionary = NULL; 1053 if (elements->map() == heap->non_strict_arguments_elements_map()) { 1054 dictionary = SeededNumberDictionary::cast(elements->get(1)); 1055 } else { 1056 dictionary = SeededNumberDictionary::cast(elements); 1057 } 1058 int entry = dictionary->FindEntry(index); 1059 ASSERT(entry != SeededNumberDictionary::kNotFound); 1060 PropertyDetails details = dictionary->DetailsAt(entry); 1061 switch (details.type()) { 1062 case CALLBACKS: { 1063 // This is an accessor property with getter and/or setter. 1064 AccessorPair* accessors = 1065 AccessorPair::cast(dictionary->ValueAt(entry)); 1066 elms->set(IS_ACCESSOR_INDEX, heap->true_value()); 1067 if (CheckElementAccess(*obj, index, v8::ACCESS_GET)) { 1068 elms->set(GETTER_INDEX, accessors->GetComponent(ACCESSOR_GETTER)); 1069 } 1070 if (CheckElementAccess(*obj, index, v8::ACCESS_SET)) { 1071 elms->set(SETTER_INDEX, accessors->GetComponent(ACCESSOR_SETTER)); 1072 } 1073 break; 1074 } 1075 case NORMAL: { 1076 // This is a data property. 1077 elms->set(IS_ACCESSOR_INDEX, heap->false_value()); 1078 Handle<Object> value = Object::GetElement(obj, index); 1079 ASSERT(!value.is_null()); 1080 elms->set(VALUE_INDEX, *value); 1081 elms->set(WRITABLE_INDEX, heap->ToBoolean(!details.IsReadOnly())); 1082 break; 1083 } 1084 default: 1085 UNREACHABLE(); 1086 break; 1087 } 1088 elms->set(ENUMERABLE_INDEX, heap->ToBoolean(!details.IsDontEnum())); 1089 elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!details.IsDontDelete())); 1090 return *desc; 1091 } 1092 } 1093 } 1094 1095 // Use recursive implementation to also traverse hidden prototypes 1096 GetOwnPropertyImplementation(*obj, *name, &result); 1097 1098 if (!result.IsProperty()) { 1099 return heap->undefined_value(); 1100 } 1101 1102 if (!CheckAccess(*obj, *name, &result, v8::ACCESS_HAS)) { 1103 return heap->false_value(); 1104 } 1105 1106 elms->set(ENUMERABLE_INDEX, heap->ToBoolean(!result.IsDontEnum())); 1107 elms->set(CONFIGURABLE_INDEX, heap->ToBoolean(!result.IsDontDelete())); 1108 1109 bool is_js_accessor = (result.type() == CALLBACKS) && 1110 (result.GetCallbackObject()->IsAccessorPair()); 1111 1112 if (is_js_accessor) { 1113 // __defineGetter__/__defineSetter__ callback. 1114 elms->set(IS_ACCESSOR_INDEX, heap->true_value()); 1115 1116 AccessorPair* accessors = AccessorPair::cast(result.GetCallbackObject()); 1117 if (CheckAccess(*obj, *name, &result, v8::ACCESS_GET)) { 1118 elms->set(GETTER_INDEX, accessors->GetComponent(ACCESSOR_GETTER)); 1119 } 1120 if (CheckAccess(*obj, *name, &result, v8::ACCESS_SET)) { 1121 elms->set(SETTER_INDEX, accessors->GetComponent(ACCESSOR_SETTER)); 1122 } 1123 } else { 1124 elms->set(IS_ACCESSOR_INDEX, heap->false_value()); 1125 elms->set(WRITABLE_INDEX, heap->ToBoolean(!result.IsReadOnly())); 1126 1127 PropertyAttributes attrs; 1128 Object* value; 1129 // GetProperty will check access and report any violations. 1130 { MaybeObject* maybe_value = obj->GetProperty(*obj, &result, *name, &attrs); 1131 if (!maybe_value->ToObject(&value)) return maybe_value; 1132 } 1133 elms->set(VALUE_INDEX, value); 1134 } 1135 1136 return *desc; 1137 } 1138 1139 1140 // Returns an array with the property description: 1141 // if args[1] is not a property on args[0] 1142 // returns undefined 1143 // if args[1] is a data property on args[0] 1144 // [false, value, Writeable, Enumerable, Configurable] 1145 // if args[1] is an accessor on args[0] 1146 // [true, GetFunction, SetFunction, Enumerable, Configurable] 1147 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOwnProperty) { 1148 ASSERT(args.length() == 2); 1149 HandleScope scope(isolate); 1150 CONVERT_ARG_HANDLE_CHECKED(JSObject, obj, 0); 1151 CONVERT_ARG_HANDLE_CHECKED(String, name, 1); 1152 return GetOwnProperty(isolate, obj, name); 1153 } 1154 1155 1156 RUNTIME_FUNCTION(MaybeObject*, Runtime_PreventExtensions) { 1157 ASSERT(args.length() == 1); 1158 CONVERT_ARG_CHECKED(JSObject, obj, 0); 1159 return obj->PreventExtensions(); 1160 } 1161 1162 1163 RUNTIME_FUNCTION(MaybeObject*, Runtime_IsExtensible) { 1164 ASSERT(args.length() == 1); 1165 CONVERT_ARG_CHECKED(JSObject, obj, 0); 1166 if (obj->IsJSGlobalProxy()) { 1167 Object* proto = obj->GetPrototype(); 1168 if (proto->IsNull()) return isolate->heap()->false_value(); 1169 ASSERT(proto->IsJSGlobalObject()); 1170 obj = JSObject::cast(proto); 1171 } 1172 return isolate->heap()->ToBoolean(obj->map()->is_extensible()); 1173 } 1174 1175 1176 RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpCompile) { 1177 HandleScope scope(isolate); 1178 ASSERT(args.length() == 3); 1179 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, re, 0); 1180 CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1); 1181 CONVERT_ARG_HANDLE_CHECKED(String, flags, 2); 1182 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags); 1183 if (result.is_null()) return Failure::Exception(); 1184 return *result; 1185 } 1186 1187 1188 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateApiFunction) { 1189 HandleScope scope(isolate); 1190 ASSERT(args.length() == 1); 1191 CONVERT_ARG_HANDLE_CHECKED(FunctionTemplateInfo, data, 0); 1192 return *isolate->factory()->CreateApiFunction(data); 1193 } 1194 1195 1196 RUNTIME_FUNCTION(MaybeObject*, Runtime_IsTemplate) { 1197 ASSERT(args.length() == 1); 1198 Object* arg = args[0]; 1199 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo(); 1200 return isolate->heap()->ToBoolean(result); 1201 } 1202 1203 1204 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetTemplateField) { 1205 ASSERT(args.length() == 2); 1206 CONVERT_ARG_CHECKED(HeapObject, templ, 0); 1207 CONVERT_SMI_ARG_CHECKED(index, 1) 1208 int offset = index * kPointerSize + HeapObject::kHeaderSize; 1209 InstanceType type = templ->map()->instance_type(); 1210 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE || 1211 type == OBJECT_TEMPLATE_INFO_TYPE); 1212 RUNTIME_ASSERT(offset > 0); 1213 if (type == FUNCTION_TEMPLATE_INFO_TYPE) { 1214 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize); 1215 } else { 1216 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize); 1217 } 1218 return *HeapObject::RawField(templ, offset); 1219 } 1220 1221 1222 RUNTIME_FUNCTION(MaybeObject*, Runtime_DisableAccessChecks) { 1223 ASSERT(args.length() == 1); 1224 CONVERT_ARG_CHECKED(HeapObject, object, 0); 1225 Map* old_map = object->map(); 1226 bool needs_access_checks = old_map->is_access_check_needed(); 1227 if (needs_access_checks) { 1228 // Copy map so it won't interfere constructor's initial map. 1229 Object* new_map; 1230 { MaybeObject* maybe_new_map = old_map->CopyDropTransitions(); 1231 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; 1232 } 1233 1234 Map::cast(new_map)->set_is_access_check_needed(false); 1235 object->set_map(Map::cast(new_map)); 1236 } 1237 return isolate->heap()->ToBoolean(needs_access_checks); 1238 } 1239 1240 1241 RUNTIME_FUNCTION(MaybeObject*, Runtime_EnableAccessChecks) { 1242 ASSERT(args.length() == 1); 1243 CONVERT_ARG_CHECKED(HeapObject, object, 0); 1244 Map* old_map = object->map(); 1245 if (!old_map->is_access_check_needed()) { 1246 // Copy map so it won't interfere constructor's initial map. 1247 Object* new_map; 1248 { MaybeObject* maybe_new_map = old_map->CopyDropTransitions(); 1249 if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; 1250 } 1251 1252 Map::cast(new_map)->set_is_access_check_needed(true); 1253 object->set_map(Map::cast(new_map)); 1254 } 1255 return isolate->heap()->undefined_value(); 1256 } 1257 1258 1259 static Failure* ThrowRedeclarationError(Isolate* isolate, 1260 const char* type, 1261 Handle<String> name) { 1262 HandleScope scope(isolate); 1263 Handle<Object> type_handle = 1264 isolate->factory()->NewStringFromAscii(CStrVector(type)); 1265 Handle<Object> args[2] = { type_handle, name }; 1266 Handle<Object> error = 1267 isolate->factory()->NewTypeError("redeclaration", HandleVector(args, 2)); 1268 return isolate->Throw(*error); 1269 } 1270 1271 1272 RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { 1273 ASSERT(args.length() == 3); 1274 HandleScope scope(isolate); 1275 Handle<GlobalObject> global = Handle<GlobalObject>( 1276 isolate->context()->global()); 1277 1278 Handle<Context> context = args.at<Context>(0); 1279 CONVERT_ARG_HANDLE_CHECKED(FixedArray, pairs, 1); 1280 CONVERT_SMI_ARG_CHECKED(flags, 2); 1281 1282 // Traverse the name/value pairs and set the properties. 1283 int length = pairs->length(); 1284 for (int i = 0; i < length; i += 2) { 1285 HandleScope scope(isolate); 1286 Handle<String> name(String::cast(pairs->get(i))); 1287 Handle<Object> value(pairs->get(i + 1), isolate); 1288 1289 // We have to declare a global const property. To capture we only 1290 // assign to it when evaluating the assignment for "const x = 1291 // <expr>" the initial value is the hole. 1292 bool is_const_property = value->IsTheHole(); 1293 bool is_function_declaration = false; 1294 if (value->IsUndefined() || is_const_property) { 1295 // Lookup the property in the global object, and don't set the 1296 // value of the variable if the property is already there. 1297 LookupResult lookup(isolate); 1298 global->Lookup(*name, &lookup); 1299 if (lookup.IsProperty()) { 1300 // We found an existing property. Unless it was an interceptor 1301 // that claims the property is absent, skip this declaration. 1302 if (lookup.type() != INTERCEPTOR) { 1303 continue; 1304 } 1305 PropertyAttributes attributes = global->GetPropertyAttribute(*name); 1306 if (attributes != ABSENT) { 1307 continue; 1308 } 1309 // Fall-through and introduce the absent property by using 1310 // SetProperty. 1311 } 1312 } else { 1313 is_function_declaration = true; 1314 // Copy the function and update its context. Use it as value. 1315 Handle<SharedFunctionInfo> shared = 1316 Handle<SharedFunctionInfo>::cast(value); 1317 Handle<JSFunction> function = 1318 isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, 1319 context, 1320 TENURED); 1321 value = function; 1322 } 1323 1324 LookupResult lookup(isolate); 1325 global->LocalLookup(*name, &lookup); 1326 1327 // Compute the property attributes. According to ECMA-262, section 1328 // 13, page 71, the property must be read-only and 1329 // non-deletable. However, neither SpiderMonkey nor KJS creates the 1330 // property as read-only, so we don't either. 1331 int attr = NONE; 1332 if (!DeclareGlobalsEvalFlag::decode(flags)) { 1333 attr |= DONT_DELETE; 1334 } 1335 bool is_native = DeclareGlobalsNativeFlag::decode(flags); 1336 if (is_const_property || (is_native && is_function_declaration)) { 1337 attr |= READ_ONLY; 1338 } 1339 1340 LanguageMode language_mode = DeclareGlobalsLanguageMode::decode(flags); 1341 1342 // Safari does not allow the invocation of callback setters for 1343 // function declarations. To mimic this behavior, we do not allow 1344 // the invocation of setters for function values. This makes a 1345 // difference for global functions with the same names as event 1346 // handlers such as "function onload() {}". Firefox does call the 1347 // onload setter in those case and Safari does not. We follow 1348 // Safari for compatibility. 1349 if (is_function_declaration) { 1350 if (lookup.IsProperty() && (lookup.type() != INTERCEPTOR)) { 1351 // Do not overwrite READ_ONLY properties. 1352 if (lookup.GetAttributes() & READ_ONLY) { 1353 if (language_mode != CLASSIC_MODE) { 1354 Handle<Object> args[] = { name }; 1355 return isolate->Throw(*isolate->factory()->NewTypeError( 1356 "strict_cannot_assign", HandleVector(args, ARRAY_SIZE(args)))); 1357 } 1358 continue; 1359 } 1360 // Do not change DONT_DELETE to false from true. 1361 attr |= lookup.GetAttributes() & DONT_DELETE; 1362 } 1363 PropertyAttributes attributes = static_cast<PropertyAttributes>(attr); 1364 1365 RETURN_IF_EMPTY_HANDLE( 1366 isolate, 1367 JSObject::SetLocalPropertyIgnoreAttributes(global, name, value, 1368 attributes)); 1369 } else { 1370 RETURN_IF_EMPTY_HANDLE( 1371 isolate, 1372 JSReceiver::SetProperty(global, name, value, 1373 static_cast<PropertyAttributes>(attr), 1374 language_mode == CLASSIC_MODE 1375 ? kNonStrictMode : kStrictMode)); 1376 } 1377 } 1378 1379 ASSERT(!isolate->has_pending_exception()); 1380 return isolate->heap()->undefined_value(); 1381 } 1382 1383 1384 RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) { 1385 HandleScope scope(isolate); 1386 ASSERT(args.length() == 4); 1387 1388 // Declarations are always made in a function or global context. In the 1389 // case of eval code, the context passed is the context of the caller, 1390 // which may be some nested context and not the declaration context. 1391 RUNTIME_ASSERT(args[0]->IsContext()); 1392 Handle<Context> context(Context::cast(args[0])->declaration_context()); 1393 1394 Handle<String> name(String::cast(args[1])); 1395 PropertyAttributes mode = static_cast<PropertyAttributes>(args.smi_at(2)); 1396 RUNTIME_ASSERT(mode == READ_ONLY || mode == NONE); 1397 Handle<Object> initial_value(args[3], isolate); 1398 1399 int index; 1400 PropertyAttributes attributes; 1401 ContextLookupFlags flags = DONT_FOLLOW_CHAINS; 1402 BindingFlags binding_flags; 1403 Handle<Object> holder = 1404 context->Lookup(name, flags, &index, &attributes, &binding_flags); 1405 1406 if (attributes != ABSENT) { 1407 // The name was declared before; check for conflicting re-declarations. 1408 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) { 1409 // Functions are not read-only. 1410 ASSERT(mode != READ_ONLY || initial_value->IsTheHole()); 1411 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var"; 1412 return ThrowRedeclarationError(isolate, type, name); 1413 } 1414 1415 // Initialize it if necessary. 1416 if (*initial_value != NULL) { 1417 if (index >= 0) { 1418 ASSERT(holder.is_identical_to(context)); 1419 if (((attributes & READ_ONLY) == 0) || 1420 context->get(index)->IsTheHole()) { 1421 context->set(index, *initial_value); 1422 } 1423 } else { 1424 // Slow case: The property is in the context extension object of a 1425 // function context or the global object of a global context. 1426 Handle<JSObject> object = Handle<JSObject>::cast(holder); 1427 RETURN_IF_EMPTY_HANDLE( 1428 isolate, 1429 JSReceiver::SetProperty(object, name, initial_value, mode, 1430 kNonStrictMode)); 1431 } 1432 } 1433 1434 } else { 1435 // The property is not in the function context. It needs to be 1436 // "declared" in the function context's extension context or as a 1437 // property of the the global object. 1438 Handle<JSObject> object; 1439 if (context->has_extension()) { 1440 object = Handle<JSObject>(JSObject::cast(context->extension())); 1441 } else { 1442 // Context extension objects are allocated lazily. 1443 ASSERT(context->IsFunctionContext()); 1444 object = isolate->factory()->NewJSObject( 1445 isolate->context_extension_function()); 1446 context->set_extension(*object); 1447 } 1448 ASSERT(*object != NULL); 1449 1450 // Declare the property by setting it to the initial value if provided, 1451 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for 1452 // constant declarations). 1453 ASSERT(!object->HasLocalProperty(*name)); 1454 Handle<Object> value(isolate->heap()->undefined_value(), isolate); 1455 if (*initial_value != NULL) value = initial_value; 1456 // Declaring a const context slot is a conflicting declaration if 1457 // there is a callback with that name in a prototype. It is 1458 // allowed to introduce const variables in 1459 // JSContextExtensionObjects. They are treated specially in 1460 // SetProperty and no setters are invoked for those since they are 1461 // not real JSObjects. 1462 if (initial_value->IsTheHole() && 1463 !object->IsJSContextExtensionObject()) { 1464 LookupResult lookup(isolate); 1465 object->Lookup(*name, &lookup); 1466 if (lookup.IsFound() && (lookup.type() == CALLBACKS)) { 1467 return ThrowRedeclarationError(isolate, "const", name); 1468 } 1469 } 1470 RETURN_IF_EMPTY_HANDLE( 1471 isolate, 1472 JSReceiver::SetProperty(object, name, value, mode, kNonStrictMode)); 1473 } 1474 1475 return isolate->heap()->undefined_value(); 1476 } 1477 1478 1479 RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { 1480 NoHandleAllocation nha; 1481 // args[0] == name 1482 // args[1] == language_mode 1483 // args[2] == value (optional) 1484 1485 // Determine if we need to assign to the variable if it already 1486 // exists (based on the number of arguments). 1487 RUNTIME_ASSERT(args.length() == 2 || args.length() == 3); 1488 bool assign = args.length() == 3; 1489 1490 CONVERT_ARG_HANDLE_CHECKED(String, name, 0); 1491 GlobalObject* global = isolate->context()->global(); 1492 RUNTIME_ASSERT(args[1]->IsSmi()); 1493 CONVERT_LANGUAGE_MODE_ARG(language_mode, 1); 1494 StrictModeFlag strict_mode_flag = (language_mode == CLASSIC_MODE) 1495 ? kNonStrictMode : kStrictMode; 1496 1497 // According to ECMA-262, section 12.2, page 62, the property must 1498 // not be deletable. 1499 PropertyAttributes attributes = DONT_DELETE; 1500 1501 // Lookup the property locally in the global object. If it isn't 1502 // there, there is a property with this name in the prototype chain. 1503 // We follow Safari and Firefox behavior and only set the property 1504 // locally if there is an explicit initialization value that we have 1505 // to assign to the property. 1506 // Note that objects can have hidden prototypes, so we need to traverse 1507 // the whole chain of hidden prototypes to do a 'local' lookup. 1508 Object* object = global; 1509 LookupResult lookup(isolate); 1510 while (object->IsJSObject() && 1511 JSObject::cast(object)->map()->is_hidden_prototype()) { 1512 JSObject* raw_holder = JSObject::cast(object); 1513 raw_holder->LocalLookup(*name, &lookup); 1514 if (lookup.IsFound() && lookup.type() == INTERCEPTOR) { 1515 HandleScope handle_scope(isolate); 1516 Handle<JSObject> holder(raw_holder); 1517 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name); 1518 // Update the raw pointer in case it's changed due to GC. 1519 raw_holder = *holder; 1520 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) { 1521 // Found an interceptor that's not read only. 1522 if (assign) { 1523 return raw_holder->SetProperty( 1524 &lookup, *name, args[2], attributes, strict_mode_flag); 1525 } else { 1526 return isolate->heap()->undefined_value(); 1527 } 1528 } 1529 } 1530 object = raw_holder->GetPrototype(); 1531 } 1532 1533 // Reload global in case the loop above performed a GC. 1534 global = isolate->context()->global(); 1535 if (assign) { 1536 return global->SetProperty(*name, args[2], attributes, strict_mode_flag); 1537 } 1538 return isolate->heap()->undefined_value(); 1539 } 1540 1541 1542 RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) { 1543 // All constants are declared with an initial value. The name 1544 // of the constant is the first argument and the initial value 1545 // is the second. 1546 RUNTIME_ASSERT(args.length() == 2); 1547 CONVERT_ARG_HANDLE_CHECKED(String, name, 0); 1548 Handle<Object> value = args.at<Object>(1); 1549 1550 // Get the current global object from top. 1551 GlobalObject* global = isolate->context()->global(); 1552 1553 // According to ECMA-262, section 12.2, page 62, the property must 1554 // not be deletable. Since it's a const, it must be READ_ONLY too. 1555 PropertyAttributes attributes = 1556 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY); 1557 1558 // Lookup the property locally in the global object. If it isn't 1559 // there, we add the property and take special precautions to always 1560 // add it as a local property even in case of callbacks in the 1561 // prototype chain (this rules out using SetProperty). 1562 // We use SetLocalPropertyIgnoreAttributes instead 1563 LookupResult lookup(isolate); 1564 global->LocalLookup(*name, &lookup); 1565 if (!lookup.IsProperty()) { 1566 return global->SetLocalPropertyIgnoreAttributes(*name, 1567 *value, 1568 attributes); 1569 } 1570 1571 if (!lookup.IsReadOnly()) { 1572 // Restore global object from context (in case of GC) and continue 1573 // with setting the value. 1574 HandleScope handle_scope(isolate); 1575 Handle<GlobalObject> global(isolate->context()->global()); 1576 1577 // BUG 1213575: Handle the case where we have to set a read-only 1578 // property through an interceptor and only do it if it's 1579 // uninitialized, e.g. the hole. Nirk... 1580 // Passing non-strict mode because the property is writable. 1581 RETURN_IF_EMPTY_HANDLE( 1582 isolate, 1583 JSReceiver::SetProperty(global, name, value, attributes, 1584 kNonStrictMode)); 1585 return *value; 1586 } 1587 1588 // Set the value, but only if we're assigning the initial value to a 1589 // constant. For now, we determine this by checking if the 1590 // current value is the hole. 1591 // Strict mode handling not needed (const is disallowed in strict mode). 1592 PropertyType type = lookup.type(); 1593 if (type == FIELD) { 1594 FixedArray* properties = global->properties(); 1595 int index = lookup.GetFieldIndex(); 1596 if (properties->get(index)->IsTheHole() || !lookup.IsReadOnly()) { 1597 properties->set(index, *value); 1598 } 1599 } else if (type == NORMAL) { 1600 if (global->GetNormalizedProperty(&lookup)->IsTheHole() || 1601 !lookup.IsReadOnly()) { 1602 global->SetNormalizedProperty(&lookup, *value); 1603 } 1604 } else { 1605 // Ignore re-initialization of constants that have already been 1606 // assigned a function value. 1607 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION); 1608 } 1609 1610 // Use the set value as the result of the operation. 1611 return *value; 1612 } 1613 1614 1615 RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstContextSlot) { 1616 HandleScope scope(isolate); 1617 ASSERT(args.length() == 3); 1618 1619 Handle<Object> value(args[0], isolate); 1620 ASSERT(!value->IsTheHole()); 1621 1622 // Initializations are always done in a function or global context. 1623 RUNTIME_ASSERT(args[1]->IsContext()); 1624 Handle<Context> context(Context::cast(args[1])->declaration_context()); 1625 1626 Handle<String> name(String::cast(args[2])); 1627 1628 int index; 1629 PropertyAttributes attributes; 1630 ContextLookupFlags flags = FOLLOW_CHAINS; 1631 BindingFlags binding_flags; 1632 Handle<Object> holder = 1633 context->Lookup(name, flags, &index, &attributes, &binding_flags); 1634 1635 if (index >= 0) { 1636 ASSERT(holder->IsContext()); 1637 // Property was found in a context. Perform the assignment if we 1638 // found some non-constant or an uninitialized constant. 1639 Handle<Context> context = Handle<Context>::cast(holder); 1640 if ((attributes & READ_ONLY) == 0 || context->get(index)->IsTheHole()) { 1641 context->set(index, *value); 1642 } 1643 return *value; 1644 } 1645 1646 // The property could not be found, we introduce it as a property of the 1647 // global object. 1648 if (attributes == ABSENT) { 1649 Handle<JSObject> global = Handle<JSObject>( 1650 isolate->context()->global()); 1651 // Strict mode not needed (const disallowed in strict mode). 1652 RETURN_IF_EMPTY_HANDLE( 1653 isolate, 1654 JSReceiver::SetProperty(global, name, value, NONE, kNonStrictMode)); 1655 return *value; 1656 } 1657 1658 // The property was present in some function's context extension object, 1659 // as a property on the subject of a with, or as a property of the global 1660 // object. 1661 // 1662 // In most situations, eval-introduced consts should still be present in 1663 // the context extension object. However, because declaration and 1664 // initialization are separate, the property might have been deleted 1665 // before we reach the initialization point. 1666 // 1667 // Example: 1668 // 1669 // function f() { eval("delete x; const x;"); } 1670 // 1671 // In that case, the initialization behaves like a normal assignment. 1672 Handle<JSObject> object = Handle<JSObject>::cast(holder); 1673 1674 if (*object == context->extension()) { 1675 // This is the property that was introduced by the const declaration. 1676 // Set it if it hasn't been set before. NOTE: We cannot use 1677 // GetProperty() to get the current value as it 'unholes' the value. 1678 LookupResult lookup(isolate); 1679 object->LocalLookupRealNamedProperty(*name, &lookup); 1680 ASSERT(lookup.IsFound()); // the property was declared 1681 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only 1682 1683 PropertyType type = lookup.type(); 1684 if (type == FIELD) { 1685 FixedArray* properties = object->properties(); 1686 int index = lookup.GetFieldIndex(); 1687 if (properties->get(index)->IsTheHole()) { 1688 properties->set(index, *value); 1689 } 1690 } else if (type == NORMAL) { 1691 if (object->GetNormalizedProperty(&lookup)->IsTheHole()) { 1692 object->SetNormalizedProperty(&lookup, *value); 1693 } 1694 } else { 1695 // We should not reach here. Any real, named property should be 1696 // either a field or a dictionary slot. 1697 UNREACHABLE(); 1698 } 1699 } else { 1700 // The property was found on some other object. Set it if it is not a 1701 // read-only property. 1702 if ((attributes & READ_ONLY) == 0) { 1703 // Strict mode not needed (const disallowed in strict mode). 1704 RETURN_IF_EMPTY_HANDLE( 1705 isolate, 1706 JSReceiver::SetProperty(object, name, value, attributes, 1707 kNonStrictMode)); 1708 } 1709 } 1710 1711 return *value; 1712 } 1713 1714 1715 RUNTIME_FUNCTION(MaybeObject*, 1716 Runtime_OptimizeObjectForAddingMultipleProperties) { 1717 HandleScope scope(isolate); 1718 ASSERT(args.length() == 2); 1719 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); 1720 CONVERT_SMI_ARG_CHECKED(properties, 1); 1721 if (object->HasFastProperties()) { 1722 JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties); 1723 } 1724 return *object; 1725 } 1726 1727 1728 RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) { 1729 HandleScope scope(isolate); 1730 ASSERT(args.length() == 4); 1731 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); 1732 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); 1733 // Due to the way the JS calls are constructed this must be less than the 1734 // length of a string, i.e. it is always a Smi. We check anyway for security. 1735 CONVERT_SMI_ARG_CHECKED(index, 2); 1736 CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3); 1737 RUNTIME_ASSERT(last_match_info->HasFastElements()); 1738 RUNTIME_ASSERT(index >= 0); 1739 RUNTIME_ASSERT(index <= subject->length()); 1740 isolate->counters()->regexp_entry_runtime()->Increment(); 1741 Handle<Object> result = RegExpImpl::Exec(regexp, 1742 subject, 1743 index, 1744 last_match_info); 1745 if (result.is_null()) return Failure::Exception(); 1746 return *result; 1747 } 1748 1749 1750 RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpConstructResult) { 1751 ASSERT(args.length() == 3); 1752 CONVERT_SMI_ARG_CHECKED(elements_count, 0); 1753 if (elements_count < 0 || 1754 elements_count > FixedArray::kMaxLength || 1755 !Smi::IsValid(elements_count)) { 1756 return isolate->ThrowIllegalOperation(); 1757 } 1758 Object* new_object; 1759 { MaybeObject* maybe_new_object = 1760 isolate->heap()->AllocateFixedArrayWithHoles(elements_count); 1761 if (!maybe_new_object->ToObject(&new_object)) return maybe_new_object; 1762 } 1763 FixedArray* elements = FixedArray::cast(new_object); 1764 { MaybeObject* maybe_new_object = isolate->heap()->AllocateRaw( 1765 JSRegExpResult::kSize, NEW_SPACE, OLD_POINTER_SPACE); 1766 if (!maybe_new_object->ToObject(&new_object)) return maybe_new_object; 1767 } 1768 { 1769 AssertNoAllocation no_gc; 1770 HandleScope scope(isolate); 1771 reinterpret_cast<HeapObject*>(new_object)-> 1772 set_map(isolate->global_context()->regexp_result_map()); 1773 } 1774 JSArray* array = JSArray::cast(new_object); 1775 array->set_properties(isolate->heap()->empty_fixed_array()); 1776 array->set_elements(elements); 1777 array->set_length(Smi::FromInt(elements_count)); 1778 // Write in-object properties after the length of the array. 1779 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, args[1]); 1780 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, args[2]); 1781 return array; 1782 } 1783 1784 1785 RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpInitializeObject) { 1786 AssertNoAllocation no_alloc; 1787 ASSERT(args.length() == 5); 1788 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0); 1789 CONVERT_ARG_CHECKED(String, source, 1); 1790 1791 Object* global = args[2]; 1792 if (!global->IsTrue()) global = isolate->heap()->false_value(); 1793 1794 Object* ignoreCase = args[3]; 1795 if (!ignoreCase->IsTrue()) ignoreCase = isolate->heap()->false_value(); 1796 1797 Object* multiline = args[4]; 1798 if (!multiline->IsTrue()) multiline = isolate->heap()->false_value(); 1799 1800 Map* map = regexp->map(); 1801 Object* constructor = map->constructor(); 1802 if (constructor->IsJSFunction() && 1803 JSFunction::cast(constructor)->initial_map() == map) { 1804 // If we still have the original map, set in-object properties directly. 1805 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, source); 1806 // Both true and false are immovable immortal objects so no need for write 1807 // barrier. 1808 regexp->InObjectPropertyAtPut( 1809 JSRegExp::kGlobalFieldIndex, global, SKIP_WRITE_BARRIER); 1810 regexp->InObjectPropertyAtPut( 1811 JSRegExp::kIgnoreCaseFieldIndex, ignoreCase, SKIP_WRITE_BARRIER); 1812 regexp->InObjectPropertyAtPut( 1813 JSRegExp::kMultilineFieldIndex, multiline, SKIP_WRITE_BARRIER); 1814 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, 1815 Smi::FromInt(0), 1816 SKIP_WRITE_BARRIER); // It's a Smi. 1817 return regexp; 1818 } 1819 1820 // Map has changed, so use generic, but slower, method. 1821 PropertyAttributes final = 1822 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE); 1823 PropertyAttributes writable = 1824 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE); 1825 Heap* heap = isolate->heap(); 1826 MaybeObject* result; 1827 result = regexp->SetLocalPropertyIgnoreAttributes(heap->source_symbol(), 1828 source, 1829 final); 1830 ASSERT(!result->IsFailure()); 1831 result = regexp->SetLocalPropertyIgnoreAttributes(heap->global_symbol(), 1832 global, 1833 final); 1834 ASSERT(!result->IsFailure()); 1835 result = 1836 regexp->SetLocalPropertyIgnoreAttributes(heap->ignore_case_symbol(), 1837 ignoreCase, 1838 final); 1839 ASSERT(!result->IsFailure()); 1840 result = regexp->SetLocalPropertyIgnoreAttributes(heap->multiline_symbol(), 1841 multiline, 1842 final); 1843 ASSERT(!result->IsFailure()); 1844 result = 1845 regexp->SetLocalPropertyIgnoreAttributes(heap->last_index_symbol(), 1846 Smi::FromInt(0), 1847 writable); 1848 ASSERT(!result->IsFailure()); 1849 USE(result); 1850 return regexp; 1851 } 1852 1853 1854 RUNTIME_FUNCTION(MaybeObject*, Runtime_FinishArrayPrototypeSetup) { 1855 HandleScope scope(isolate); 1856 ASSERT(args.length() == 1); 1857 CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0); 1858 // This is necessary to enable fast checks for absence of elements 1859 // on Array.prototype and below. 1860 prototype->set_elements(isolate->heap()->empty_fixed_array()); 1861 return Smi::FromInt(0); 1862 } 1863 1864 1865 static Handle<JSFunction> InstallBuiltin(Isolate* isolate, 1866 Handle<JSObject> holder, 1867 const char* name, 1868 Builtins::Name builtin_name) { 1869 Handle<String> key = isolate->factory()->LookupAsciiSymbol(name); 1870 Handle<Code> code(isolate->builtins()->builtin(builtin_name)); 1871 Handle<JSFunction> optimized = 1872 isolate->factory()->NewFunction(key, 1873 JS_OBJECT_TYPE, 1874 JSObject::kHeaderSize, 1875 code, 1876 false); 1877 optimized->shared()->DontAdaptArguments(); 1878 JSReceiver::SetProperty(holder, key, optimized, NONE, kStrictMode); 1879 return optimized; 1880 } 1881 1882 1883 RUNTIME_FUNCTION(MaybeObject*, Runtime_SpecialArrayFunctions) { 1884 HandleScope scope(isolate); 1885 ASSERT(args.length() == 1); 1886 CONVERT_ARG_HANDLE_CHECKED(JSObject, holder, 0); 1887 1888 InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop); 1889 InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush); 1890 InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift); 1891 InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift); 1892 InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice); 1893 InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice); 1894 InstallBuiltin(isolate, holder, "concat", Builtins::kArrayConcat); 1895 1896 return *holder; 1897 } 1898 1899 1900 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetDefaultReceiver) { 1901 ASSERT(args.length() == 1); 1902 CONVERT_ARG_CHECKED(JSReceiver, callable, 0); 1903 1904 if (!callable->IsJSFunction()) { 1905 HandleScope scope(isolate); 1906 bool threw = false; 1907 Handle<Object> delegate = 1908 Execution::TryGetFunctionDelegate(Handle<JSReceiver>(callable), &threw); 1909 if (threw) return Failure::Exception(); 1910 callable = JSFunction::cast(*delegate); 1911 } 1912 JSFunction* function = JSFunction::cast(callable); 1913 1914 SharedFunctionInfo* shared = function->shared(); 1915 if (shared->native() || !shared->is_classic_mode()) { 1916 return isolate->heap()->undefined_value(); 1917 } 1918 // Returns undefined for strict or native functions, or 1919 // the associated global receiver for "normal" functions. 1920 1921 Context* global_context = 1922 function->context()->global()->global_context(); 1923 return global_context->global()->global_receiver(); 1924 } 1925 1926 1927 RUNTIME_FUNCTION(MaybeObject*, Runtime_MaterializeRegExpLiteral) { 1928 HandleScope scope(isolate); 1929 ASSERT(args.length() == 4); 1930 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); 1931 int index = args.smi_at(1); 1932 Handle<String> pattern = args.at<String>(2); 1933 Handle<String> flags = args.at<String>(3); 1934 1935 // Get the RegExp function from the context in the literals array. 1936 // This is the RegExp function from the context in which the 1937 // function was created. We do not use the RegExp function from the 1938 // current global context because this might be the RegExp function 1939 // from another context which we should not have access to. 1940 Handle<JSFunction> constructor = 1941 Handle<JSFunction>( 1942 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function()); 1943 // Compute the regular expression literal. 1944 bool has_pending_exception; 1945 Handle<Object> regexp = 1946 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags, 1947 &has_pending_exception); 1948 if (has_pending_exception) { 1949 ASSERT(isolate->has_pending_exception()); 1950 return Failure::Exception(); 1951 } 1952 literals->set(index, *regexp); 1953 return *regexp; 1954 } 1955 1956 1957 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetName) { 1958 NoHandleAllocation ha; 1959 ASSERT(args.length() == 1); 1960 1961 CONVERT_ARG_CHECKED(JSFunction, f, 0); 1962 return f->shared()->name(); 1963 } 1964 1965 1966 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetName) { 1967 NoHandleAllocation ha; 1968 ASSERT(args.length() == 2); 1969 1970 CONVERT_ARG_CHECKED(JSFunction, f, 0); 1971 CONVERT_ARG_CHECKED(String, name, 1); 1972 f->shared()->set_name(name); 1973 return isolate->heap()->undefined_value(); 1974 } 1975 1976 1977 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionNameShouldPrintAsAnonymous) { 1978 NoHandleAllocation ha; 1979 ASSERT(args.length() == 1); 1980 CONVERT_ARG_CHECKED(JSFunction, f, 0); 1981 return isolate->heap()->ToBoolean( 1982 f->shared()->name_should_print_as_anonymous()); 1983 } 1984 1985 1986 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionMarkNameShouldPrintAsAnonymous) { 1987 NoHandleAllocation ha; 1988 ASSERT(args.length() == 1); 1989 CONVERT_ARG_CHECKED(JSFunction, f, 0); 1990 f->shared()->set_name_should_print_as_anonymous(true); 1991 return isolate->heap()->undefined_value(); 1992 } 1993 1994 1995 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionRemovePrototype) { 1996 NoHandleAllocation ha; 1997 ASSERT(args.length() == 1); 1998 1999 CONVERT_ARG_CHECKED(JSFunction, f, 0); 2000 Object* obj = f->RemovePrototype(); 2001 if (obj->IsFailure()) return obj; 2002 2003 return isolate->heap()->undefined_value(); 2004 } 2005 2006 2007 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetScript) { 2008 HandleScope scope(isolate); 2009 ASSERT(args.length() == 1); 2010 2011 CONVERT_ARG_CHECKED(JSFunction, fun, 0); 2012 Handle<Object> script = Handle<Object>(fun->shared()->script(), isolate); 2013 if (!script->IsScript()) return isolate->heap()->undefined_value(); 2014 2015 return *GetScriptWrapper(Handle<Script>::cast(script)); 2016 } 2017 2018 2019 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetSourceCode) { 2020 HandleScope scope(isolate); 2021 ASSERT(args.length() == 1); 2022 2023 CONVERT_ARG_HANDLE_CHECKED(JSFunction, f, 0); 2024 Handle<SharedFunctionInfo> shared(f->shared()); 2025 return *shared->GetSourceCode(); 2026 } 2027 2028 2029 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetScriptSourcePosition) { 2030 NoHandleAllocation ha; 2031 ASSERT(args.length() == 1); 2032 2033 CONVERT_ARG_CHECKED(JSFunction, fun, 0); 2034 int pos = fun->shared()->start_position(); 2035 return Smi::FromInt(pos); 2036 } 2037 2038 2039 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionGetPositionForOffset) { 2040 ASSERT(args.length() == 2); 2041 2042 CONVERT_ARG_CHECKED(Code, code, 0); 2043 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]); 2044 2045 RUNTIME_ASSERT(0 <= offset && offset < code->Size()); 2046 2047 Address pc = code->address() + offset; 2048 return Smi::FromInt(code->SourcePosition(pc)); 2049 } 2050 2051 2052 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetInstanceClassName) { 2053 NoHandleAllocation ha; 2054 ASSERT(args.length() == 2); 2055 2056 CONVERT_ARG_CHECKED(JSFunction, fun, 0); 2057 CONVERT_ARG_CHECKED(String, name, 1); 2058 fun->SetInstanceClassName(name); 2059 return isolate->heap()->undefined_value(); 2060 } 2061 2062 2063 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetLength) { 2064 NoHandleAllocation ha; 2065 ASSERT(args.length() == 2); 2066 2067 CONVERT_ARG_CHECKED(JSFunction, fun, 0); 2068 CONVERT_SMI_ARG_CHECKED(length, 1); 2069 fun->shared()->set_length(length); 2070 return isolate->heap()->undefined_value(); 2071 } 2072 2073 2074 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetPrototype) { 2075 NoHandleAllocation ha; 2076 ASSERT(args.length() == 2); 2077 2078 CONVERT_ARG_CHECKED(JSFunction, fun, 0); 2079 ASSERT(fun->should_have_prototype()); 2080 Object* obj; 2081 { MaybeObject* maybe_obj = 2082 Accessors::FunctionSetPrototype(fun, args[1], NULL); 2083 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 2084 } 2085 return args[0]; // return TOS 2086 } 2087 2088 2089 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetReadOnlyPrototype) { 2090 NoHandleAllocation ha; 2091 RUNTIME_ASSERT(args.length() == 1); 2092 CONVERT_ARG_CHECKED(JSFunction, function, 0); 2093 2094 MaybeObject* maybe_name = 2095 isolate->heap()->AllocateStringFromAscii(CStrVector("prototype")); 2096 String* name; 2097 if (!maybe_name->To(&name)) return maybe_name; 2098 2099 if (function->HasFastProperties()) { 2100 // Construct a new field descriptor with updated attributes. 2101 DescriptorArray* instance_desc = function->map()->instance_descriptors(); 2102 int index = instance_desc->Search(name); 2103 ASSERT(index != DescriptorArray::kNotFound); 2104 PropertyDetails details(instance_desc->GetDetails(index)); 2105 CallbacksDescriptor new_desc(name, 2106 instance_desc->GetValue(index), 2107 static_cast<PropertyAttributes>(details.attributes() | READ_ONLY), 2108 details.index()); 2109 // Construct a new field descriptors array containing the new descriptor. 2110 Object* descriptors_unchecked; 2111 { MaybeObject* maybe_descriptors_unchecked = 2112 instance_desc->CopyInsert(&new_desc, REMOVE_TRANSITIONS); 2113 if (!maybe_descriptors_unchecked->ToObject(&descriptors_unchecked)) { 2114 return maybe_descriptors_unchecked; 2115 } 2116 } 2117 DescriptorArray* new_descriptors = 2118 DescriptorArray::cast(descriptors_unchecked); 2119 // Create a new map featuring the new field descriptors array. 2120 Object* map_unchecked; 2121 { MaybeObject* maybe_map_unchecked = function->map()->CopyDropDescriptors(); 2122 if (!maybe_map_unchecked->ToObject(&map_unchecked)) { 2123 return maybe_map_unchecked; 2124 } 2125 } 2126 Map* new_map = Map::cast(map_unchecked); 2127 new_map->set_instance_descriptors(new_descriptors); 2128 function->set_map(new_map); 2129 } else { // Dictionary properties. 2130 // Directly manipulate the property details. 2131 int entry = function->property_dictionary()->FindEntry(name); 2132 ASSERT(entry != StringDictionary::kNotFound); 2133 PropertyDetails details = function->property_dictionary()->DetailsAt(entry); 2134 PropertyDetails new_details( 2135 static_cast<PropertyAttributes>(details.attributes() | READ_ONLY), 2136 details.type(), 2137 details.index()); 2138 function->property_dictionary()->DetailsAtPut(entry, new_details); 2139 } 2140 return function; 2141 } 2142 2143 2144 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsAPIFunction) { 2145 NoHandleAllocation ha; 2146 ASSERT(args.length() == 1); 2147 2148 CONVERT_ARG_CHECKED(JSFunction, f, 0); 2149 return isolate->heap()->ToBoolean(f->shared()->IsApiFunction()); 2150 } 2151 2152 2153 RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionIsBuiltin) { 2154 NoHandleAllocation ha; 2155 ASSERT(args.length() == 1); 2156 2157 CONVERT_ARG_CHECKED(JSFunction, f, 0); 2158 return isolate->heap()->ToBoolean(f->IsBuiltin()); 2159 } 2160 2161 2162 RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) { 2163 HandleScope scope(isolate); 2164 ASSERT(args.length() == 2); 2165 2166 CONVERT_ARG_HANDLE_CHECKED(JSFunction, target, 0); 2167 Handle<Object> code = args.at<Object>(1); 2168 2169 Handle<Context> context(target->context()); 2170 2171 if (!code->IsNull()) { 2172 RUNTIME_ASSERT(code->IsJSFunction()); 2173 Handle<JSFunction> fun = Handle<JSFunction>::cast(code); 2174 Handle<SharedFunctionInfo> shared(fun->shared()); 2175 2176 if (!SharedFunctionInfo::EnsureCompiled(shared, KEEP_EXCEPTION)) { 2177 return Failure::Exception(); 2178 } 2179 // Since we don't store the source for this we should never 2180 // optimize this. 2181 shared->code()->set_optimizable(false); 2182 // Set the code, scope info, formal parameter count, 2183 // and the length of the target function. 2184 target->shared()->set_code(shared->code()); 2185 target->ReplaceCode(shared->code()); 2186 target->shared()->set_scope_info(shared->scope_info()); 2187 target->shared()->set_length(shared->length()); 2188 target->shared()->set_formal_parameter_count( 2189 shared->formal_parameter_count()); 2190 // Set the source code of the target function to undefined. 2191 // SetCode is only used for built-in constructors like String, 2192 // Array, and Object, and some web code 2193 // doesn't like seeing source code for constructors. 2194 target->shared()->set_script(isolate->heap()->undefined_value()); 2195 target->shared()->code()->set_optimizable(false); 2196 // Clear the optimization hints related to the compiled code as these are no 2197 // longer valid when the code is overwritten. 2198 target->shared()->ClearThisPropertyAssignmentsInfo(); 2199 context = Handle<Context>(fun->context()); 2200 2201 // Make sure we get a fresh copy of the literal vector to avoid 2202 // cross context contamination. 2203 int number_of_literals = fun->NumberOfLiterals(); 2204 Handle<FixedArray> literals = 2205 isolate->factory()->NewFixedArray(number_of_literals, TENURED); 2206 if (number_of_literals > 0) { 2207 // Insert the object, regexp and array functions in the literals 2208 // array prefix. These are the functions that will be used when 2209 // creating object, regexp and array literals. 2210 literals->set(JSFunction::kLiteralGlobalContextIndex, 2211 context->global_context()); 2212 } 2213 target->set_literals(*literals); 2214 target->set_next_function_link(isolate->heap()->undefined_value()); 2215 2216 if (isolate->logger()->is_logging() || CpuProfiler::is_profiling(isolate)) { 2217 isolate->logger()->LogExistingFunction( 2218 shared, Handle<Code>(shared->code())); 2219 } 2220 } 2221 2222 target->set_context(*context); 2223 return *target; 2224 } 2225 2226 2227 RUNTIME_FUNCTION(MaybeObject*, Runtime_SetExpectedNumberOfProperties) { 2228 HandleScope scope(isolate); 2229 ASSERT(args.length() == 2); 2230 CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); 2231 CONVERT_SMI_ARG_CHECKED(num, 1); 2232 RUNTIME_ASSERT(num >= 0); 2233 SetExpectedNofProperties(function, num); 2234 return isolate->heap()->undefined_value(); 2235 } 2236 2237 2238 MUST_USE_RESULT static MaybeObject* CharFromCode(Isolate* isolate, 2239 Object* char_code) { 2240 uint32_t code; 2241 if (char_code->ToArrayIndex(&code)) { 2242 if (code <= 0xffff) { 2243 return isolate->heap()->LookupSingleCharacterStringFromCode(code); 2244 } 2245 } 2246 return isolate->heap()->empty_string(); 2247 } 2248 2249 2250 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringCharCodeAt) { 2251 NoHandleAllocation ha; 2252 ASSERT(args.length() == 2); 2253 2254 CONVERT_ARG_CHECKED(String, subject, 0); 2255 Object* index = args[1]; 2256 RUNTIME_ASSERT(index->IsNumber()); 2257 2258 uint32_t i = 0; 2259 if (index->IsSmi()) { 2260 int value = Smi::cast(index)->value(); 2261 if (value < 0) return isolate->heap()->nan_value(); 2262 i = value; 2263 } else { 2264 ASSERT(index->IsHeapNumber()); 2265 double value = HeapNumber::cast(index)->value(); 2266 i = static_cast<uint32_t>(DoubleToInteger(value)); 2267 } 2268 2269 // Flatten the string. If someone wants to get a char at an index 2270 // in a cons string, it is likely that more indices will be 2271 // accessed. 2272 Object* flat; 2273 { MaybeObject* maybe_flat = subject->TryFlatten(); 2274 if (!maybe_flat->ToObject(&flat)) return maybe_flat; 2275 } 2276 subject = String::cast(flat); 2277 2278 if (i >= static_cast<uint32_t>(subject->length())) { 2279 return isolate->heap()->nan_value(); 2280 } 2281 2282 return Smi::FromInt(subject->Get(i)); 2283 } 2284 2285 2286 RUNTIME_FUNCTION(MaybeObject*, Runtime_CharFromCode) { 2287 NoHandleAllocation ha; 2288 ASSERT(args.length() == 1); 2289 return CharFromCode(isolate, args[0]); 2290 } 2291 2292 2293 class FixedArrayBuilder { 2294 public: 2295 explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity) 2296 : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)), 2297 length_(0), 2298 has_non_smi_elements_(false) { 2299 // Require a non-zero initial size. Ensures that doubling the size to 2300 // extend the array will work. 2301 ASSERT(initial_capacity > 0); 2302 } 2303 2304 explicit FixedArrayBuilder(Handle<FixedArray> backing_store) 2305 : array_(backing_store), 2306 length_(0), 2307 has_non_smi_elements_(false) { 2308 // Require a non-zero initial size. Ensures that doubling the size to 2309 // extend the array will work. 2310 ASSERT(backing_store->length() > 0); 2311 } 2312 2313 bool HasCapacity(int elements) { 2314 int length = array_->length(); 2315 int required_length = length_ + elements; 2316 return (length >= required_length); 2317 } 2318 2319 void EnsureCapacity(int elements) { 2320 int length = array_->length(); 2321 int required_length = length_ + elements; 2322 if (length < required_length) { 2323 int new_length = length; 2324 do { 2325 new_length *= 2; 2326 } while (new_length < required_length); 2327 Handle<FixedArray> extended_array = 2328 array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length); 2329 array_->CopyTo(0, *extended_array, 0, length_); 2330 array_ = extended_array; 2331 } 2332 } 2333 2334 void Add(Object* value) { 2335 ASSERT(!value->IsSmi()); 2336 ASSERT(length_ < capacity()); 2337 array_->set(length_, value); 2338 length_++; 2339 has_non_smi_elements_ = true; 2340 } 2341 2342 void Add(Smi* value) { 2343 ASSERT(value->IsSmi()); 2344 ASSERT(length_ < capacity()); 2345 array_->set(length_, value); 2346 length_++; 2347 } 2348 2349 Handle<FixedArray> array() { 2350 return array_; 2351 } 2352 2353 int length() { 2354 return length_; 2355 } 2356 2357 int capacity() { 2358 return array_->length(); 2359 } 2360 2361 Handle<JSArray> ToJSArray() { 2362 Handle<JSArray> result_array = FACTORY->NewJSArrayWithElements(array_); 2363 result_array->set_length(Smi::FromInt(length_)); 2364 return result_array; 2365 } 2366 2367 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) { 2368 FACTORY->SetContent(target_array, array_); 2369 target_array->set_length(Smi::FromInt(length_)); 2370 return target_array; 2371 } 2372 2373 private: 2374 Handle<FixedArray> array_; 2375 int length_; 2376 bool has_non_smi_elements_; 2377 }; 2378 2379 2380 // Forward declarations. 2381 const int kStringBuilderConcatHelperLengthBits = 11; 2382 const int kStringBuilderConcatHelperPositionBits = 19; 2383 2384 template <typename schar> 2385 static inline void StringBuilderConcatHelper(String*, 2386 schar*, 2387 FixedArray*, 2388 int); 2389 2390 typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits> 2391 StringBuilderSubstringLength; 2392 typedef BitField<int, 2393 kStringBuilderConcatHelperLengthBits, 2394 kStringBuilderConcatHelperPositionBits> 2395 StringBuilderSubstringPosition; 2396 2397 2398 class ReplacementStringBuilder { 2399 public: 2400 ReplacementStringBuilder(Heap* heap, 2401 Handle<String> subject, 2402 int estimated_part_count) 2403 : heap_(heap), 2404 array_builder_(heap->isolate(), estimated_part_count), 2405 subject_(subject), 2406 character_count_(0), 2407 is_ascii_(subject->IsAsciiRepresentation()) { 2408 // Require a non-zero initial size. Ensures that doubling the size to 2409 // extend the array will work. 2410 ASSERT(estimated_part_count > 0); 2411 } 2412 2413 static inline void AddSubjectSlice(FixedArrayBuilder* builder, 2414 int from, 2415 int to) { 2416 ASSERT(from >= 0); 2417 int length = to - from; 2418 ASSERT(length > 0); 2419 if (StringBuilderSubstringLength::is_valid(length) && 2420 StringBuilderSubstringPosition::is_valid(from)) { 2421 int encoded_slice = StringBuilderSubstringLength::encode(length) | 2422 StringBuilderSubstringPosition::encode(from); 2423 builder->Add(Smi::FromInt(encoded_slice)); 2424 } else { 2425 // Otherwise encode as two smis. 2426 builder->Add(Smi::FromInt(-length)); 2427 builder->Add(Smi::FromInt(from)); 2428 } 2429 } 2430 2431 2432 void EnsureCapacity(int elements) { 2433 array_builder_.EnsureCapacity(elements); 2434 } 2435 2436 2437 void AddSubjectSlice(int from, int to) { 2438 AddSubjectSlice(&array_builder_, from, to); 2439 IncrementCharacterCount(to - from); 2440 } 2441 2442 2443 void AddString(Handle<String> string) { 2444 int length = string->length(); 2445 ASSERT(length > 0); 2446 AddElement(*string); 2447 if (!string->IsAsciiRepresentation()) { 2448 is_ascii_ = false; 2449 } 2450 IncrementCharacterCount(length); 2451 } 2452 2453 2454 Handle<String> ToString() { 2455 if (array_builder_.length() == 0) { 2456 return heap_->isolate()->factory()->empty_string(); 2457 } 2458 2459 Handle<String> joined_string; 2460 if (is_ascii_) { 2461 Handle<SeqAsciiString> seq = NewRawAsciiString(character_count_); 2462 AssertNoAllocation no_alloc; 2463 char* char_buffer = seq->GetChars(); 2464 StringBuilderConcatHelper(*subject_, 2465 char_buffer, 2466 *array_builder_.array(), 2467 array_builder_.length()); 2468 joined_string = Handle<String>::cast(seq); 2469 } else { 2470 // Non-ASCII. 2471 Handle<SeqTwoByteString> seq = NewRawTwoByteString(character_count_); 2472 AssertNoAllocation no_alloc; 2473 uc16* char_buffer = seq->GetChars(); 2474 StringBuilderConcatHelper(*subject_, 2475 char_buffer, 2476 *array_builder_.array(), 2477 array_builder_.length()); 2478 joined_string = Handle<String>::cast(seq); 2479 } 2480 return joined_string; 2481 } 2482 2483 2484 void IncrementCharacterCount(int by) { 2485 if (character_count_ > String::kMaxLength - by) { 2486 V8::FatalProcessOutOfMemory("String.replace result too large."); 2487 } 2488 character_count_ += by; 2489 } 2490 2491 Handle<JSArray> GetParts() { 2492 return array_builder_.ToJSArray(); 2493 } 2494 2495 private: 2496 Handle<SeqAsciiString> NewRawAsciiString(int length) { 2497 return heap_->isolate()->factory()->NewRawAsciiString(length); 2498 } 2499 2500 2501 Handle<SeqTwoByteString> NewRawTwoByteString(int length) { 2502 return heap_->isolate()->factory()->NewRawTwoByteString(length); 2503 } 2504 2505 2506 void AddElement(Object* element) { 2507 ASSERT(element->IsSmi() || element->IsString()); 2508 ASSERT(array_builder_.capacity() > array_builder_.length()); 2509 array_builder_.Add(element); 2510 } 2511 2512 Heap* heap_; 2513 FixedArrayBuilder array_builder_; 2514 Handle<String> subject_; 2515 int character_count_; 2516 bool is_ascii_; 2517 }; 2518 2519 2520 class CompiledReplacement { 2521 public: 2522 CompiledReplacement() 2523 : parts_(1), replacement_substrings_(0), simple_hint_(false) {} 2524 2525 void Compile(Handle<String> replacement, 2526 int capture_count, 2527 int subject_length); 2528 2529 void Apply(ReplacementStringBuilder* builder, 2530 int match_from, 2531 int match_to, 2532 Handle<JSArray> last_match_info); 2533 2534 // Number of distinct parts of the replacement pattern. 2535 int parts() { 2536 return parts_.length(); 2537 } 2538 2539 bool simple_hint() { 2540 return simple_hint_; 2541 } 2542 2543 private: 2544 enum PartType { 2545 SUBJECT_PREFIX = 1, 2546 SUBJECT_SUFFIX, 2547 SUBJECT_CAPTURE, 2548 REPLACEMENT_SUBSTRING, 2549 REPLACEMENT_STRING, 2550 2551 NUMBER_OF_PART_TYPES 2552 }; 2553 2554 struct ReplacementPart { 2555 static inline ReplacementPart SubjectMatch() { 2556 return ReplacementPart(SUBJECT_CAPTURE, 0); 2557 } 2558 static inline ReplacementPart SubjectCapture(int capture_index) { 2559 return ReplacementPart(SUBJECT_CAPTURE, capture_index); 2560 } 2561 static inline ReplacementPart SubjectPrefix() { 2562 return ReplacementPart(SUBJECT_PREFIX, 0); 2563 } 2564 static inline ReplacementPart SubjectSuffix(int subject_length) { 2565 return ReplacementPart(SUBJECT_SUFFIX, subject_length); 2566 } 2567 static inline ReplacementPart ReplacementString() { 2568 return ReplacementPart(REPLACEMENT_STRING, 0); 2569 } 2570 static inline ReplacementPart ReplacementSubString(int from, int to) { 2571 ASSERT(from >= 0); 2572 ASSERT(to > from); 2573 return ReplacementPart(-from, to); 2574 } 2575 2576 // If tag <= 0 then it is the negation of a start index of a substring of 2577 // the replacement pattern, otherwise it's a value from PartType. 2578 ReplacementPart(int tag, int data) 2579 : tag(tag), data(data) { 2580 // Must be non-positive or a PartType value. 2581 ASSERT(tag < NUMBER_OF_PART_TYPES); 2582 } 2583 // Either a value of PartType or a non-positive number that is 2584 // the negation of an index into the replacement string. 2585 int tag; 2586 // The data value's interpretation depends on the value of tag: 2587 // tag == SUBJECT_PREFIX || 2588 // tag == SUBJECT_SUFFIX: data is unused. 2589 // tag == SUBJECT_CAPTURE: data is the number of the capture. 2590 // tag == REPLACEMENT_SUBSTRING || 2591 // tag == REPLACEMENT_STRING: data is index into array of substrings 2592 // of the replacement string. 2593 // tag <= 0: Temporary representation of the substring of the replacement 2594 // string ranging over -tag .. data. 2595 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the 2596 // substring objects. 2597 int data; 2598 }; 2599 2600 template<typename Char> 2601 static bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts, 2602 Vector<Char> characters, 2603 int capture_count, 2604 int subject_length) { 2605 int length = characters.length(); 2606 int last = 0; 2607 for (int i = 0; i < length; i++) { 2608 Char c = characters[i]; 2609 if (c == '$') { 2610 int next_index = i + 1; 2611 if (next_index == length) { // No next character! 2612 break; 2613 } 2614 Char c2 = characters[next_index]; 2615 switch (c2) { 2616 case '$': 2617 if (i > last) { 2618 // There is a substring before. Include the first "$". 2619 parts->Add(ReplacementPart::ReplacementSubString(last, next_index)); 2620 last = next_index + 1; // Continue after the second "$". 2621 } else { 2622 // Let the next substring start with the second "$". 2623 last = next_index; 2624 } 2625 i = next_index; 2626 break; 2627 case '`': 2628 if (i > last) { 2629 parts->Add(ReplacementPart::ReplacementSubString(last, i)); 2630 } 2631 parts->Add(ReplacementPart::SubjectPrefix()); 2632 i = next_index; 2633 last = i + 1; 2634 break; 2635 case '\'': 2636 if (i > last) { 2637 parts->Add(ReplacementPart::ReplacementSubString(last, i)); 2638 } 2639 parts->Add(ReplacementPart::SubjectSuffix(subject_length)); 2640 i = next_index; 2641 last = i + 1; 2642 break; 2643 case '&': 2644 if (i > last) { 2645 parts->Add(ReplacementPart::ReplacementSubString(last, i)); 2646 } 2647 parts->Add(ReplacementPart::SubjectMatch()); 2648 i = next_index; 2649 last = i + 1; 2650 break; 2651 case '0': 2652 case '1': 2653 case '2': 2654 case '3': 2655 case '4': 2656 case '5': 2657 case '6': 2658 case '7': 2659 case '8': 2660 case '9': { 2661 int capture_ref = c2 - '0'; 2662 if (capture_ref > capture_count) { 2663 i = next_index; 2664 continue; 2665 } 2666 int second_digit_index = next_index + 1; 2667 if (second_digit_index < length) { 2668 // Peek ahead to see if we have two digits. 2669 Char c3 = characters[second_digit_index]; 2670 if ('0' <= c3 && c3 <= '9') { // Double digits. 2671 int double_digit_ref = capture_ref * 10 + c3 - '0'; 2672 if (double_digit_ref <= capture_count) { 2673 next_index = second_digit_index; 2674 capture_ref = double_digit_ref; 2675 } 2676 } 2677 } 2678 if (capture_ref > 0) { 2679 if (i > last) { 2680 parts->Add(ReplacementPart::ReplacementSubString(last, i)); 2681 } 2682 ASSERT(capture_ref <= capture_count); 2683 parts->Add(ReplacementPart::SubjectCapture(capture_ref)); 2684 last = next_index + 1; 2685 } 2686 i = next_index; 2687 break; 2688 } 2689 default: 2690 i = next_index; 2691 break; 2692 } 2693 } 2694 } 2695 if (length > last) { 2696 if (last == 0) { 2697 parts->Add(ReplacementPart::ReplacementString()); 2698 return true; 2699 } else { 2700 parts->Add(ReplacementPart::ReplacementSubString(last, length)); 2701 } 2702 } 2703 return false; 2704 } 2705 2706 ZoneList<ReplacementPart> parts_; 2707 ZoneList<Handle<String> > replacement_substrings_; 2708 bool simple_hint_; 2709 }; 2710 2711 2712 void CompiledReplacement::Compile(Handle<String> replacement, 2713 int capture_count, 2714 int subject_length) { 2715 { 2716 AssertNoAllocation no_alloc; 2717 String::FlatContent content = replacement->GetFlatContent(); 2718 ASSERT(content.IsFlat()); 2719 if (content.IsAscii()) { 2720 simple_hint_ = ParseReplacementPattern(&parts_, 2721 content.ToAsciiVector(), 2722 capture_count, 2723 subject_length); 2724 } else { 2725 ASSERT(content.IsTwoByte()); 2726 simple_hint_ = ParseReplacementPattern(&parts_, 2727 content.ToUC16Vector(), 2728 capture_count, 2729 subject_length); 2730 } 2731 } 2732 Isolate* isolate = replacement->GetIsolate(); 2733 // Find substrings of replacement string and create them as String objects. 2734 int substring_index = 0; 2735 for (int i = 0, n = parts_.length(); i < n; i++) { 2736 int tag = parts_[i].tag; 2737 if (tag <= 0) { // A replacement string slice. 2738 int from = -tag; 2739 int to = parts_[i].data; 2740 replacement_substrings_.Add( 2741 isolate->factory()->NewSubString(replacement, from, to)); 2742 parts_[i].tag = REPLACEMENT_SUBSTRING; 2743 parts_[i].data = substring_index; 2744 substring_index++; 2745 } else if (tag == REPLACEMENT_STRING) { 2746 replacement_substrings_.Add(replacement); 2747 parts_[i].data = substring_index; 2748 substring_index++; 2749 } 2750 } 2751 } 2752 2753 2754 void CompiledReplacement::Apply(ReplacementStringBuilder* builder, 2755 int match_from, 2756 int match_to, 2757 Handle<JSArray> last_match_info) { 2758 for (int i = 0, n = parts_.length(); i < n; i++) { 2759 ReplacementPart part = parts_[i]; 2760 switch (part.tag) { 2761 case SUBJECT_PREFIX: 2762 if (match_from > 0) builder->AddSubjectSlice(0, match_from); 2763 break; 2764 case SUBJECT_SUFFIX: { 2765 int subject_length = part.data; 2766 if (match_to < subject_length) { 2767 builder->AddSubjectSlice(match_to, subject_length); 2768 } 2769 break; 2770 } 2771 case SUBJECT_CAPTURE: { 2772 int capture = part.data; 2773 FixedArray* match_info = FixedArray::cast(last_match_info->elements()); 2774 int from = RegExpImpl::GetCapture(match_info, capture * 2); 2775 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1); 2776 if (from >= 0 && to > from) { 2777 builder->AddSubjectSlice(from, to); 2778 } 2779 break; 2780 } 2781 case REPLACEMENT_SUBSTRING: 2782 case REPLACEMENT_STRING: 2783 builder->AddString(replacement_substrings_[part.data]); 2784 break; 2785 default: 2786 UNREACHABLE(); 2787 } 2788 } 2789 } 2790 2791 2792 void FindAsciiStringIndices(Vector<const char> subject, 2793 char pattern, 2794 ZoneList<int>* indices, 2795 unsigned int limit) { 2796 ASSERT(limit > 0); 2797 // Collect indices of pattern in subject using memchr. 2798 // Stop after finding at most limit values. 2799 const char* subject_start = reinterpret_cast<const char*>(subject.start()); 2800 const char* subject_end = subject_start + subject.length(); 2801 const char* pos = subject_start; 2802 while (limit > 0) { 2803 pos = reinterpret_cast<const char*>( 2804 memchr(pos, pattern, subject_end - pos)); 2805 if (pos == NULL) return; 2806 indices->Add(static_cast<int>(pos - subject_start)); 2807 pos++; 2808 limit--; 2809 } 2810 } 2811 2812 2813 template <typename SubjectChar, typename PatternChar> 2814 void FindStringIndices(Isolate* isolate, 2815 Vector<const SubjectChar> subject, 2816 Vector<const PatternChar> pattern, 2817 ZoneList<int>* indices, 2818 unsigned int limit) { 2819 ASSERT(limit > 0); 2820 // Collect indices of pattern in subject. 2821 // Stop after finding at most limit values. 2822 int pattern_length = pattern.length(); 2823 int index = 0; 2824 StringSearch<PatternChar, SubjectChar> search(isolate, pattern); 2825 while (limit > 0) { 2826 index = search.Search(subject, index); 2827 if (index < 0) return; 2828 indices->Add(index); 2829 index += pattern_length; 2830 limit--; 2831 } 2832 } 2833 2834 2835 void FindStringIndicesDispatch(Isolate* isolate, 2836 String* subject, 2837 String* pattern, 2838 ZoneList<int>* indices, 2839 unsigned int limit) { 2840 { 2841 AssertNoAllocation no_gc; 2842 String::FlatContent subject_content = subject->GetFlatContent(); 2843 String::FlatContent pattern_content = pattern->GetFlatContent(); 2844 ASSERT(subject_content.IsFlat()); 2845 ASSERT(pattern_content.IsFlat()); 2846 if (subject_content.IsAscii()) { 2847 Vector<const char> subject_vector = subject_content.ToAsciiVector(); 2848 if (pattern_content.IsAscii()) { 2849 Vector<const char> pattern_vector = pattern_content.ToAsciiVector(); 2850 if (pattern_vector.length() == 1) { 2851 FindAsciiStringIndices(subject_vector, 2852 pattern_vector[0], 2853 indices, 2854 limit); 2855 } else { 2856 FindStringIndices(isolate, 2857 subject_vector, 2858 pattern_vector, 2859 indices, 2860 limit); 2861 } 2862 } else { 2863 FindStringIndices(isolate, 2864 subject_vector, 2865 pattern_content.ToUC16Vector(), 2866 indices, 2867 limit); 2868 } 2869 } else { 2870 Vector<const uc16> subject_vector = subject_content.ToUC16Vector(); 2871 if (pattern_content.IsAscii()) { 2872 FindStringIndices(isolate, 2873 subject_vector, 2874 pattern_content.ToAsciiVector(), 2875 indices, 2876 limit); 2877 } else { 2878 FindStringIndices(isolate, 2879 subject_vector, 2880 pattern_content.ToUC16Vector(), 2881 indices, 2882 limit); 2883 } 2884 } 2885 } 2886 } 2887 2888 2889 template<typename ResultSeqString> 2890 MUST_USE_RESULT static MaybeObject* StringReplaceStringWithString( 2891 Isolate* isolate, 2892 Handle<String> subject, 2893 Handle<JSRegExp> pattern_regexp, 2894 Handle<String> replacement) { 2895 ASSERT(subject->IsFlat()); 2896 ASSERT(replacement->IsFlat()); 2897 2898 ZoneScope zone_space(isolate, DELETE_ON_EXIT); 2899 ZoneList<int> indices(8); 2900 ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag()); 2901 String* pattern = 2902 String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex)); 2903 int subject_len = subject->length(); 2904 int pattern_len = pattern->length(); 2905 int replacement_len = replacement->length(); 2906 2907 FindStringIndicesDispatch(isolate, *subject, pattern, &indices, 0xffffffff); 2908 2909 int matches = indices.length(); 2910 if (matches == 0) return *subject; 2911 2912 int result_len = (replacement_len - pattern_len) * matches + subject_len; 2913 int subject_pos = 0; 2914 int result_pos = 0; 2915 2916 Handle<ResultSeqString> result; 2917 if (ResultSeqString::kHasAsciiEncoding) { 2918 result = Handle<ResultSeqString>::cast( 2919 isolate->factory()->NewRawAsciiString(result_len)); 2920 } else { 2921 result = Handle<ResultSeqString>::cast( 2922 isolate->factory()->NewRawTwoByteString(result_len)); 2923 } 2924 2925 for (int i = 0; i < matches; i++) { 2926 // Copy non-matched subject content. 2927 if (subject_pos < indices.at(i)) { 2928 String::WriteToFlat(*subject, 2929 result->GetChars() + result_pos, 2930 subject_pos, 2931 indices.at(i)); 2932 result_pos += indices.at(i) - subject_pos; 2933 } 2934 2935 // Replace match. 2936 if (replacement_len > 0) { 2937 String::WriteToFlat(*replacement, 2938 result->GetChars() + result_pos, 2939 0, 2940 replacement_len); 2941 result_pos += replacement_len; 2942 } 2943 2944 subject_pos = indices.at(i) + pattern_len; 2945 } 2946 // Add remaining subject content at the end. 2947 if (subject_pos < subject_len) { 2948 String::WriteToFlat(*subject, 2949 result->GetChars() + result_pos, 2950 subject_pos, 2951 subject_len); 2952 } 2953 return *result; 2954 } 2955 2956 2957 MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString( 2958 Isolate* isolate, 2959 String* subject, 2960 JSRegExp* regexp, 2961 String* replacement, 2962 JSArray* last_match_info) { 2963 ASSERT(subject->IsFlat()); 2964 ASSERT(replacement->IsFlat()); 2965 2966 HandleScope handles(isolate); 2967 2968 int length = subject->length(); 2969 Handle<String> subject_handle(subject); 2970 Handle<JSRegExp> regexp_handle(regexp); 2971 Handle<String> replacement_handle(replacement); 2972 Handle<JSArray> last_match_info_handle(last_match_info); 2973 Handle<Object> match = RegExpImpl::Exec(regexp_handle, 2974 subject_handle, 2975 0, 2976 last_match_info_handle); 2977 if (match.is_null()) { 2978 return Failure::Exception(); 2979 } 2980 if (match->IsNull()) { 2981 return *subject_handle; 2982 } 2983 2984 int capture_count = regexp_handle->CaptureCount(); 2985 2986 // CompiledReplacement uses zone allocation. 2987 ZoneScope zone(isolate, DELETE_ON_EXIT); 2988 CompiledReplacement compiled_replacement; 2989 compiled_replacement.Compile(replacement_handle, 2990 capture_count, 2991 length); 2992 2993 bool is_global = regexp_handle->GetFlags().is_global(); 2994 2995 // Shortcut for simple non-regexp global replacements 2996 if (is_global && 2997 regexp_handle->TypeTag() == JSRegExp::ATOM && 2998 compiled_replacement.simple_hint()) { 2999 if (subject_handle->HasOnlyAsciiChars() && 3000 replacement_handle->HasOnlyAsciiChars()) { 3001 return StringReplaceStringWithString<SeqAsciiString>( 3002 isolate, subject_handle, regexp_handle, replacement_handle); 3003 } else { 3004 return StringReplaceStringWithString<SeqTwoByteString>( 3005 isolate, subject_handle, regexp_handle, replacement_handle); 3006 } 3007 } 3008 3009 // Guessing the number of parts that the final result string is built 3010 // from. Global regexps can match any number of times, so we guess 3011 // conservatively. 3012 int expected_parts = 3013 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1; 3014 ReplacementStringBuilder builder(isolate->heap(), 3015 subject_handle, 3016 expected_parts); 3017 3018 // Index of end of last match. 3019 int prev = 0; 3020 3021 // Number of parts added by compiled replacement plus preceeding 3022 // string and possibly suffix after last match. It is possible for 3023 // all components to use two elements when encoded as two smis. 3024 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2); 3025 bool matched = true; 3026 do { 3027 ASSERT(last_match_info_handle->HasFastElements()); 3028 // Increase the capacity of the builder before entering local handle-scope, 3029 // so its internal buffer can safely allocate a new handle if it grows. 3030 builder.EnsureCapacity(parts_added_per_loop); 3031 3032 HandleScope loop_scope(isolate); 3033 int start, end; 3034 { 3035 AssertNoAllocation match_info_array_is_not_in_a_handle; 3036 FixedArray* match_info_array = 3037 FixedArray::cast(last_match_info_handle->elements()); 3038 3039 ASSERT_EQ(capture_count * 2 + 2, 3040 RegExpImpl::GetLastCaptureCount(match_info_array)); 3041 start = RegExpImpl::GetCapture(match_info_array, 0); 3042 end = RegExpImpl::GetCapture(match_info_array, 1); 3043 } 3044 3045 if (prev < start) { 3046 builder.AddSubjectSlice(prev, start); 3047 } 3048 compiled_replacement.Apply(&builder, 3049 start, 3050 end, 3051 last_match_info_handle); 3052 prev = end; 3053 3054 // Only continue checking for global regexps. 3055 if (!is_global) break; 3056 3057 // Continue from where the match ended, unless it was an empty match. 3058 int next = end; 3059 if (start == end) { 3060 next = end + 1; 3061 if (next > length) break; 3062 } 3063 3064 match = RegExpImpl::Exec(regexp_handle, 3065 subject_handle, 3066 next, 3067 last_match_info_handle); 3068 if (match.is_null()) { 3069 return Failure::Exception(); 3070 } 3071 matched = !match->IsNull(); 3072 } while (matched); 3073 3074 if (prev < length) { 3075 builder.AddSubjectSlice(prev, length); 3076 } 3077 3078 return *(builder.ToString()); 3079 } 3080 3081 3082 template <typename ResultSeqString> 3083 MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString( 3084 Isolate* isolate, 3085 String* subject, 3086 JSRegExp* regexp, 3087 JSArray* last_match_info) { 3088 ASSERT(subject->IsFlat()); 3089 3090 HandleScope handles(isolate); 3091 3092 Handle<String> subject_handle(subject); 3093 Handle<JSRegExp> regexp_handle(regexp); 3094 3095 // Shortcut for simple non-regexp global replacements 3096 if (regexp_handle->GetFlags().is_global() && 3097 regexp_handle->TypeTag() == JSRegExp::ATOM) { 3098 Handle<String> empty_string_handle(HEAP->empty_string()); 3099 if (subject_handle->HasOnlyAsciiChars()) { 3100 return StringReplaceStringWithString<SeqAsciiString>( 3101 isolate, subject_handle, regexp_handle, empty_string_handle); 3102 } else { 3103 return StringReplaceStringWithString<SeqTwoByteString>( 3104 isolate, subject_handle, regexp_handle, empty_string_handle); 3105 } 3106 } 3107 3108 Handle<JSArray> last_match_info_handle(last_match_info); 3109 Handle<Object> match = RegExpImpl::Exec(regexp_handle, 3110 subject_handle, 3111 0, 3112 last_match_info_handle); 3113 if (match.is_null()) return Failure::Exception(); 3114 if (match->IsNull()) return *subject_handle; 3115 3116 ASSERT(last_match_info_handle->HasFastElements()); 3117 3118 int start, end; 3119 { 3120 AssertNoAllocation match_info_array_is_not_in_a_handle; 3121 FixedArray* match_info_array = 3122 FixedArray::cast(last_match_info_handle->elements()); 3123 3124 start = RegExpImpl::GetCapture(match_info_array, 0); 3125 end = RegExpImpl::GetCapture(match_info_array, 1); 3126 } 3127 3128 int length = subject_handle->length(); 3129 int new_length = length - (end - start); 3130 if (new_length == 0) { 3131 return isolate->heap()->empty_string(); 3132 } 3133 Handle<ResultSeqString> answer; 3134 if (ResultSeqString::kHasAsciiEncoding) { 3135 answer = Handle<ResultSeqString>::cast( 3136 isolate->factory()->NewRawAsciiString(new_length)); 3137 } else { 3138 answer = Handle<ResultSeqString>::cast( 3139 isolate->factory()->NewRawTwoByteString(new_length)); 3140 } 3141 3142 // If the regexp isn't global, only match once. 3143 if (!regexp_handle->GetFlags().is_global()) { 3144 if (start > 0) { 3145 String::WriteToFlat(*subject_handle, 3146 answer->GetChars(), 3147 0, 3148 start); 3149 } 3150 if (end < length) { 3151 String::WriteToFlat(*subject_handle, 3152 answer->GetChars() + start, 3153 end, 3154 length); 3155 } 3156 return *answer; 3157 } 3158 3159 int prev = 0; // Index of end of last match. 3160 int next = 0; // Start of next search (prev unless last match was empty). 3161 int position = 0; 3162 3163 do { 3164 if (prev < start) { 3165 // Add substring subject[prev;start] to answer string. 3166 String::WriteToFlat(*subject_handle, 3167 answer->GetChars() + position, 3168 prev, 3169 start); 3170 position += start - prev; 3171 } 3172 prev = end; 3173 next = end; 3174 // Continue from where the match ended, unless it was an empty match. 3175 if (start == end) { 3176 next++; 3177 if (next > length) break; 3178 } 3179 match = RegExpImpl::Exec(regexp_handle, 3180 subject_handle, 3181 next, 3182 last_match_info_handle); 3183 if (match.is_null()) return Failure::Exception(); 3184 if (match->IsNull()) break; 3185 3186 ASSERT(last_match_info_handle->HasFastElements()); 3187 HandleScope loop_scope(isolate); 3188 { 3189 AssertNoAllocation match_info_array_is_not_in_a_handle; 3190 FixedArray* match_info_array = 3191 FixedArray::cast(last_match_info_handle->elements()); 3192 start = RegExpImpl::GetCapture(match_info_array, 0); 3193 end = RegExpImpl::GetCapture(match_info_array, 1); 3194 } 3195 } while (true); 3196 3197 if (prev < length) { 3198 // Add substring subject[prev;length] to answer string. 3199 String::WriteToFlat(*subject_handle, 3200 answer->GetChars() + position, 3201 prev, 3202 length); 3203 position += length - prev; 3204 } 3205 3206 if (position == 0) { 3207 return isolate->heap()->empty_string(); 3208 } 3209 3210 // Shorten string and fill 3211 int string_size = ResultSeqString::SizeFor(position); 3212 int allocated_string_size = ResultSeqString::SizeFor(new_length); 3213 int delta = allocated_string_size - string_size; 3214 3215 answer->set_length(position); 3216 if (delta == 0) return *answer; 3217 3218 Address end_of_string = answer->address() + string_size; 3219 isolate->heap()->CreateFillerObjectAt(end_of_string, delta); 3220 if (Marking::IsBlack(Marking::MarkBitFrom(*answer))) { 3221 MemoryChunk::IncrementLiveBytesFromMutator(answer->address(), -delta); 3222 } 3223 3224 return *answer; 3225 } 3226 3227 3228 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceRegExpWithString) { 3229 ASSERT(args.length() == 4); 3230 3231 CONVERT_ARG_CHECKED(String, subject, 0); 3232 if (!subject->IsFlat()) { 3233 Object* flat_subject; 3234 { MaybeObject* maybe_flat_subject = subject->TryFlatten(); 3235 if (!maybe_flat_subject->ToObject(&flat_subject)) { 3236 return maybe_flat_subject; 3237 } 3238 } 3239 subject = String::cast(flat_subject); 3240 } 3241 3242 CONVERT_ARG_CHECKED(String, replacement, 2); 3243 if (!replacement->IsFlat()) { 3244 Object* flat_replacement; 3245 { MaybeObject* maybe_flat_replacement = replacement->TryFlatten(); 3246 if (!maybe_flat_replacement->ToObject(&flat_replacement)) { 3247 return maybe_flat_replacement; 3248 } 3249 } 3250 replacement = String::cast(flat_replacement); 3251 } 3252 3253 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1); 3254 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3); 3255 3256 ASSERT(last_match_info->HasFastElements()); 3257 3258 if (replacement->length() == 0) { 3259 if (subject->HasOnlyAsciiChars()) { 3260 return StringReplaceRegExpWithEmptyString<SeqAsciiString>( 3261 isolate, subject, regexp, last_match_info); 3262 } else { 3263 return StringReplaceRegExpWithEmptyString<SeqTwoByteString>( 3264 isolate, subject, regexp, last_match_info); 3265 } 3266 } 3267 3268 return StringReplaceRegExpWithString(isolate, 3269 subject, 3270 regexp, 3271 replacement, 3272 last_match_info); 3273 } 3274 3275 3276 Handle<String> Runtime::StringReplaceOneCharWithString(Isolate* isolate, 3277 Handle<String> subject, 3278 Handle<String> search, 3279 Handle<String> replace, 3280 bool* found, 3281 int recursion_limit) { 3282 if (recursion_limit == 0) return Handle<String>::null(); 3283 if (subject->IsConsString()) { 3284 ConsString* cons = ConsString::cast(*subject); 3285 Handle<String> first = Handle<String>(cons->first()); 3286 Handle<String> second = Handle<String>(cons->second()); 3287 Handle<String> new_first = 3288 StringReplaceOneCharWithString(isolate, 3289 first, 3290 search, 3291 replace, 3292 found, 3293 recursion_limit - 1); 3294 if (*found) return isolate->factory()->NewConsString(new_first, second); 3295 if (new_first.is_null()) return new_first; 3296 3297 Handle<String> new_second = 3298 StringReplaceOneCharWithString(isolate, 3299 second, 3300 search, 3301 replace, 3302 found, 3303 recursion_limit - 1); 3304 if (*found) return isolate->factory()->NewConsString(first, new_second); 3305 if (new_second.is_null()) return new_second; 3306 3307 return subject; 3308 } else { 3309 int index = StringMatch(isolate, subject, search, 0); 3310 if (index == -1) return subject; 3311 *found = true; 3312 Handle<String> first = isolate->factory()->NewSubString(subject, 0, index); 3313 Handle<String> cons1 = isolate->factory()->NewConsString(first, replace); 3314 Handle<String> second = 3315 isolate->factory()->NewSubString(subject, index + 1, subject->length()); 3316 return isolate->factory()->NewConsString(cons1, second); 3317 } 3318 } 3319 3320 3321 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceOneCharWithString) { 3322 ASSERT(args.length() == 3); 3323 HandleScope scope(isolate); 3324 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); 3325 CONVERT_ARG_HANDLE_CHECKED(String, search, 1); 3326 CONVERT_ARG_HANDLE_CHECKED(String, replace, 2); 3327 3328 // If the cons string tree is too deep, we simply abort the recursion and 3329 // retry with a flattened subject string. 3330 const int kRecursionLimit = 0x1000; 3331 bool found = false; 3332 Handle<String> result = 3333 Runtime::StringReplaceOneCharWithString(isolate, 3334 subject, 3335 search, 3336 replace, 3337 &found, 3338 kRecursionLimit); 3339 if (!result.is_null()) return *result; 3340 return *Runtime::StringReplaceOneCharWithString(isolate, 3341 FlattenGetString(subject), 3342 search, 3343 replace, 3344 &found, 3345 kRecursionLimit); 3346 } 3347 3348 3349 // Perform string match of pattern on subject, starting at start index. 3350 // Caller must ensure that 0 <= start_index <= sub->length(), 3351 // and should check that pat->length() + start_index <= sub->length(). 3352 int Runtime::StringMatch(Isolate* isolate, 3353 Handle<String> sub, 3354 Handle<String> pat, 3355 int start_index) { 3356 ASSERT(0 <= start_index); 3357 ASSERT(start_index <= sub->length()); 3358 3359 int pattern_length = pat->length(); 3360 if (pattern_length == 0) return start_index; 3361 3362 int subject_length = sub->length(); 3363 if (start_index + pattern_length > subject_length) return -1; 3364 3365 if (!sub->IsFlat()) FlattenString(sub); 3366 if (!pat->IsFlat()) FlattenString(pat); 3367 3368 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid 3369 // Extract flattened substrings of cons strings before determining asciiness. 3370 String::FlatContent seq_sub = sub->GetFlatContent(); 3371 String::FlatContent seq_pat = pat->GetFlatContent(); 3372 3373 // dispatch on type of strings 3374 if (seq_pat.IsAscii()) { 3375 Vector<const char> pat_vector = seq_pat.ToAsciiVector(); 3376 if (seq_sub.IsAscii()) { 3377 return SearchString(isolate, 3378 seq_sub.ToAsciiVector(), 3379 pat_vector, 3380 start_index); 3381 } 3382 return SearchString(isolate, 3383 seq_sub.ToUC16Vector(), 3384 pat_vector, 3385 start_index); 3386 } 3387 Vector<const uc16> pat_vector = seq_pat.ToUC16Vector(); 3388 if (seq_sub.IsAscii()) { 3389 return SearchString(isolate, 3390 seq_sub.ToAsciiVector(), 3391 pat_vector, 3392 start_index); 3393 } 3394 return SearchString(isolate, 3395 seq_sub.ToUC16Vector(), 3396 pat_vector, 3397 start_index); 3398 } 3399 3400 3401 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringIndexOf) { 3402 HandleScope scope(isolate); // create a new handle scope 3403 ASSERT(args.length() == 3); 3404 3405 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0); 3406 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1); 3407 3408 Object* index = args[2]; 3409 uint32_t start_index; 3410 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1); 3411 3412 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length())); 3413 int position = 3414 Runtime::StringMatch(isolate, sub, pat, start_index); 3415 return Smi::FromInt(position); 3416 } 3417 3418 3419 template <typename schar, typename pchar> 3420 static int StringMatchBackwards(Vector<const schar> subject, 3421 Vector<const pchar> pattern, 3422 int idx) { 3423 int pattern_length = pattern.length(); 3424 ASSERT(pattern_length >= 1); 3425 ASSERT(idx + pattern_length <= subject.length()); 3426 3427 if (sizeof(schar) == 1 && sizeof(pchar) > 1) { 3428 for (int i = 0; i < pattern_length; i++) { 3429 uc16 c = pattern[i]; 3430 if (c > String::kMaxAsciiCharCode) { 3431 return -1; 3432 } 3433 } 3434 } 3435 3436 pchar pattern_first_char = pattern[0]; 3437 for (int i = idx; i >= 0; i--) { 3438 if (subject[i] != pattern_first_char) continue; 3439 int j = 1; 3440 while (j < pattern_length) { 3441 if (pattern[j] != subject[i+j]) { 3442 break; 3443 } 3444 j++; 3445 } 3446 if (j == pattern_length) { 3447 return i; 3448 } 3449 } 3450 return -1; 3451 } 3452 3453 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringLastIndexOf) { 3454 HandleScope scope(isolate); // create a new handle scope 3455 ASSERT(args.length() == 3); 3456 3457 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0); 3458 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1); 3459 3460 Object* index = args[2]; 3461 uint32_t start_index; 3462 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1); 3463 3464 uint32_t pat_length = pat->length(); 3465 uint32_t sub_length = sub->length(); 3466 3467 if (start_index + pat_length > sub_length) { 3468 start_index = sub_length - pat_length; 3469 } 3470 3471 if (pat_length == 0) { 3472 return Smi::FromInt(start_index); 3473 } 3474 3475 if (!sub->IsFlat()) FlattenString(sub); 3476 if (!pat->IsFlat()) FlattenString(pat); 3477 3478 int position = -1; 3479 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid 3480 3481 String::FlatContent sub_content = sub->GetFlatContent(); 3482 String::FlatContent pat_content = pat->GetFlatContent(); 3483 3484 if (pat_content.IsAscii()) { 3485 Vector<const char> pat_vector = pat_content.ToAsciiVector(); 3486 if (sub_content.IsAscii()) { 3487 position = StringMatchBackwards(sub_content.ToAsciiVector(), 3488 pat_vector, 3489 start_index); 3490 } else { 3491 position = StringMatchBackwards(sub_content.ToUC16Vector(), 3492 pat_vector, 3493 start_index); 3494 } 3495 } else { 3496 Vector<const uc16> pat_vector = pat_content.ToUC16Vector(); 3497 if (sub_content.IsAscii()) { 3498 position = StringMatchBackwards(sub_content.ToAsciiVector(), 3499 pat_vector, 3500 start_index); 3501 } else { 3502 position = StringMatchBackwards(sub_content.ToUC16Vector(), 3503 pat_vector, 3504 start_index); 3505 } 3506 } 3507 3508 return Smi::FromInt(position); 3509 } 3510 3511 3512 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringLocaleCompare) { 3513 NoHandleAllocation ha; 3514 ASSERT(args.length() == 2); 3515 3516 CONVERT_ARG_CHECKED(String, str1, 0); 3517 CONVERT_ARG_CHECKED(String, str2, 1); 3518 3519 if (str1 == str2) return Smi::FromInt(0); // Equal. 3520 int str1_length = str1->length(); 3521 int str2_length = str2->length(); 3522 3523 // Decide trivial cases without flattening. 3524 if (str1_length == 0) { 3525 if (str2_length == 0) return Smi::FromInt(0); // Equal. 3526 return Smi::FromInt(-str2_length); 3527 } else { 3528 if (str2_length == 0) return Smi::FromInt(str1_length); 3529 } 3530 3531 int end = str1_length < str2_length ? str1_length : str2_length; 3532 3533 // No need to flatten if we are going to find the answer on the first 3534 // character. At this point we know there is at least one character 3535 // in each string, due to the trivial case handling above. 3536 int d = str1->Get(0) - str2->Get(0); 3537 if (d != 0) return Smi::FromInt(d); 3538 3539 str1->TryFlatten(); 3540 str2->TryFlatten(); 3541 3542 StringInputBuffer& buf1 = 3543 *isolate->runtime_state()->string_locale_compare_buf1(); 3544 StringInputBuffer& buf2 = 3545 *isolate->runtime_state()->string_locale_compare_buf2(); 3546 3547 buf1.Reset(str1); 3548 buf2.Reset(str2); 3549 3550 for (int i = 0; i < end; i++) { 3551 uint16_t char1 = buf1.GetNext(); 3552 uint16_t char2 = buf2.GetNext(); 3553 if (char1 != char2) return Smi::FromInt(char1 - char2); 3554 } 3555 3556 return Smi::FromInt(str1_length - str2_length); 3557 } 3558 3559 3560 RUNTIME_FUNCTION(MaybeObject*, Runtime_SubString) { 3561 NoHandleAllocation ha; 3562 ASSERT(args.length() == 3); 3563 3564 CONVERT_ARG_CHECKED(String, value, 0); 3565 int start, end; 3566 // We have a fast integer-only case here to avoid a conversion to double in 3567 // the common case where from and to are Smis. 3568 if (args[1]->IsSmi() && args[2]->IsSmi()) { 3569 CONVERT_SMI_ARG_CHECKED(from_number, 1); 3570 CONVERT_SMI_ARG_CHECKED(to_number, 2); 3571 start = from_number; 3572 end = to_number; 3573 } else { 3574 CONVERT_DOUBLE_ARG_CHECKED(from_number, 1); 3575 CONVERT_DOUBLE_ARG_CHECKED(to_number, 2); 3576 start = FastD2I(from_number); 3577 end = FastD2I(to_number); 3578 } 3579 RUNTIME_ASSERT(end >= start); 3580 RUNTIME_ASSERT(start >= 0); 3581 RUNTIME_ASSERT(end <= value->length()); 3582 isolate->counters()->sub_string_runtime()->Increment(); 3583 return value->SubString(start, end); 3584 } 3585 3586 3587 RUNTIME_FUNCTION(MaybeObject*, Runtime_StringMatch) { 3588 ASSERT_EQ(3, args.length()); 3589 3590 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); 3591 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); 3592 CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2); 3593 HandleScope handles; 3594 3595 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info); 3596 3597 if (match.is_null()) { 3598 return Failure::Exception(); 3599 } 3600 if (match->IsNull()) { 3601 return isolate->heap()->null_value(); 3602 } 3603 int length = subject->length(); 3604 3605 ZoneScope zone_space(isolate, DELETE_ON_EXIT); 3606 ZoneList<int> offsets(8); 3607 int start; 3608 int end; 3609 do { 3610 { 3611 AssertNoAllocation no_alloc; 3612 FixedArray* elements = FixedArray::cast(regexp_info->elements()); 3613 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value(); 3614 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value(); 3615 } 3616 offsets.Add(start); 3617 offsets.Add(end); 3618 if (start == end) if (++end > length) break; 3619 match = RegExpImpl::Exec(regexp, subject, end, regexp_info); 3620 if (match.is_null()) { 3621 return Failure::Exception(); 3622 } 3623 } while (!match->IsNull()); 3624 int matches = offsets.length() / 2; 3625 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches); 3626 Handle<String> substring = isolate->factory()-> 3627 NewSubString(subject, offsets.at(0), offsets.at(1)); 3628 elements->set(0, *substring); 3629 for (int i = 1; i < matches ; i++) { 3630 int from = offsets.at(i * 2); 3631 int to = offsets.at(i * 2 + 1); 3632 Handle<String> substring = isolate->factory()-> 3633 NewProperSubString(subject, from, to); 3634 elements->set(i, *substring); 3635 } 3636 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements); 3637 result->set_length(Smi::FromInt(matches)); 3638 return *result; 3639 } 3640 3641 3642 // Two smis before and after the match, for very long strings. 3643 const int kMaxBuilderEntriesPerRegExpMatch = 5; 3644 3645 3646 static void SetLastMatchInfoNoCaptures(Handle<String> subject, 3647 Handle<JSArray> last_match_info, 3648 int match_start, 3649 int match_end) { 3650 // Fill last_match_info with a single capture. 3651 last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead); 3652 AssertNoAllocation no_gc; 3653 FixedArray* elements = FixedArray::cast(last_match_info->elements()); 3654 RegExpImpl::SetLastCaptureCount(elements, 2); 3655 RegExpImpl::SetLastInput(elements, *subject); 3656 RegExpImpl::SetLastSubject(elements, *subject); 3657 RegExpImpl::SetCapture(elements, 0, match_start); 3658 RegExpImpl::SetCapture(elements, 1, match_end); 3659 } 3660 3661 3662 template <typename SubjectChar, typename PatternChar> 3663 static bool SearchStringMultiple(Isolate* isolate, 3664 Vector<const SubjectChar> subject, 3665 Vector<const PatternChar> pattern, 3666 String* pattern_string, 3667 FixedArrayBuilder* builder, 3668 int* match_pos) { 3669 int pos = *match_pos; 3670 int subject_length = subject.length(); 3671 int pattern_length = pattern.length(); 3672 int max_search_start = subject_length - pattern_length; 3673 StringSearch<PatternChar, SubjectChar> search(isolate, pattern); 3674 while (pos <= max_search_start) { 3675 if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) { 3676 *match_pos = pos; 3677 return false; 3678 } 3679 // Position of end of previous match. 3680 int match_end = pos + pattern_length; 3681 int new_pos = search.Search(subject, match_end); 3682 if (new_pos >= 0) { 3683 // A match. 3684 if (new_pos > match_end) { 3685 ReplacementStringBuilder::AddSubjectSlice(builder, 3686 match_end, 3687 new_pos); 3688 } 3689 pos = new_pos; 3690 builder->Add(pattern_string); 3691 } else { 3692 break; 3693 } 3694 } 3695 3696 if (pos < max_search_start) { 3697 ReplacementStringBuilder::AddSubjectSlice(builder, 3698 pos + pattern_length, 3699 subject_length); 3700 } 3701 *match_pos = pos; 3702 return true; 3703 } 3704 3705 3706 static bool SearchStringMultiple(Isolate* isolate, 3707 Handle<String> subject, 3708 Handle<String> pattern, 3709 Handle<JSArray> last_match_info, 3710 FixedArrayBuilder* builder) { 3711 ASSERT(subject->IsFlat()); 3712 ASSERT(pattern->IsFlat()); 3713 3714 // Treating as if a previous match was before first character. 3715 int match_pos = -pattern->length(); 3716 3717 for (;;) { // Break when search complete. 3718 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); 3719 AssertNoAllocation no_gc; 3720 String::FlatContent subject_content = subject->GetFlatContent(); 3721 String::FlatContent pattern_content = pattern->GetFlatContent(); 3722 if (subject_content.IsAscii()) { 3723 Vector<const char> subject_vector = subject_content.ToAsciiVector(); 3724 if (pattern_content.IsAscii()) { 3725 if (SearchStringMultiple(isolate, 3726 subject_vector, 3727 pattern_content.ToAsciiVector(), 3728 *pattern, 3729 builder, 3730 &match_pos)) break; 3731 } else { 3732 if (SearchStringMultiple(isolate, 3733 subject_vector, 3734 pattern_content.ToUC16Vector(), 3735 *pattern, 3736 builder, 3737 &match_pos)) break; 3738 } 3739 } else { 3740 Vector<const uc16> subject_vector = subject_content.ToUC16Vector(); 3741 if (pattern_content.IsAscii()) { 3742 if (SearchStringMultiple(isolate, 3743 subject_vector, 3744 pattern_content.ToAsciiVector(), 3745 *pattern, 3746 builder, 3747 &match_pos)) break; 3748 } else { 3749 if (SearchStringMultiple(isolate, 3750 subject_vector, 3751 pattern_content.ToUC16Vector(), 3752 *pattern, 3753 builder, 3754 &match_pos)) break; 3755 } 3756 } 3757 } 3758 3759 if (match_pos >= 0) { 3760 SetLastMatchInfoNoCaptures(subject, 3761 last_match_info, 3762 match_pos, 3763 match_pos + pattern->length()); 3764 return true; 3765 } 3766 return false; // No matches at all. 3767 } 3768 3769 3770 static RegExpImpl::IrregexpResult SearchRegExpNoCaptureMultiple( 3771 Isolate* isolate, 3772 Handle<String> subject, 3773 Handle<JSRegExp> regexp, 3774 Handle<JSArray> last_match_array, 3775 FixedArrayBuilder* builder) { 3776 ASSERT(subject->IsFlat()); 3777 int match_start = -1; 3778 int match_end = 0; 3779 int pos = 0; 3780 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject); 3781 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION; 3782 3783 OffsetsVector registers(required_registers, isolate); 3784 Vector<int32_t> register_vector(registers.vector(), registers.length()); 3785 int subject_length = subject->length(); 3786 bool first = true; 3787 3788 for (;;) { // Break on failure, return on exception. 3789 RegExpImpl::IrregexpResult result = 3790 RegExpImpl::IrregexpExecOnce(regexp, 3791 subject, 3792 pos, 3793 register_vector); 3794 if (result == RegExpImpl::RE_SUCCESS) { 3795 match_start = register_vector[0]; 3796 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); 3797 if (match_end < match_start) { 3798 ReplacementStringBuilder::AddSubjectSlice(builder, 3799 match_end, 3800 match_start); 3801 } 3802 match_end = register_vector[1]; 3803 HandleScope loop_scope(isolate); 3804 if (!first) { 3805 builder->Add(*isolate->factory()->NewProperSubString(subject, 3806 match_start, 3807 match_end)); 3808 } else { 3809 builder->Add(*isolate->factory()->NewSubString(subject, 3810 match_start, 3811 match_end)); 3812 } 3813 if (match_start != match_end) { 3814 pos = match_end; 3815 } else { 3816 pos = match_end + 1; 3817 if (pos > subject_length) break; 3818 } 3819 } else if (result == RegExpImpl::RE_FAILURE) { 3820 break; 3821 } else { 3822 ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION); 3823 return result; 3824 } 3825 first = false; 3826 } 3827 3828 if (match_start >= 0) { 3829 if (match_end < subject_length) { 3830 ReplacementStringBuilder::AddSubjectSlice(builder, 3831 match_end, 3832 subject_length); 3833 } 3834 SetLastMatchInfoNoCaptures(subject, 3835 last_match_array, 3836 match_start, 3837 match_end); 3838 return RegExpImpl::RE_SUCCESS; 3839 } else { 3840 return RegExpImpl::RE_FAILURE; // No matches at all. 3841 } 3842 } 3843 3844 3845 static RegExpImpl::IrregexpResult SearchRegExpMultiple( 3846 Isolate* isolate, 3847 Handle<String> subject, 3848 Handle<JSRegExp> regexp, 3849 Handle<JSArray> last_match_array, 3850 FixedArrayBuilder* builder) { 3851 3852 ASSERT(subject->IsFlat()); 3853 int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject); 3854 if (required_registers < 0) return RegExpImpl::RE_EXCEPTION; 3855 3856 OffsetsVector registers(required_registers, isolate); 3857 Vector<int32_t> register_vector(registers.vector(), registers.length()); 3858 3859 RegExpImpl::IrregexpResult result = 3860 RegExpImpl::IrregexpExecOnce(regexp, 3861 subject, 3862 0, 3863 register_vector); 3864 3865 int capture_count = regexp->CaptureCount(); 3866 int subject_length = subject->length(); 3867 3868 // Position to search from. 3869 int pos = 0; 3870 // End of previous match. Differs from pos if match was empty. 3871 int match_end = 0; 3872 if (result == RegExpImpl::RE_SUCCESS) { 3873 // Need to keep a copy of the previous match for creating last_match_info 3874 // at the end, so we have two vectors that we swap between. 3875 OffsetsVector registers2(required_registers, isolate); 3876 Vector<int> prev_register_vector(registers2.vector(), registers2.length()); 3877 bool first = true; 3878 do { 3879 int match_start = register_vector[0]; 3880 builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); 3881 if (match_end < match_start) { 3882 ReplacementStringBuilder::AddSubjectSlice(builder, 3883 match_end, 3884 match_start); 3885 } 3886 match_end = register_vector[1]; 3887 3888 { 3889 // Avoid accumulating new handles inside loop. 3890 HandleScope temp_scope(isolate); 3891 // Arguments array to replace function is match, captures, index and 3892 // subject, i.e., 3 + capture count in total. 3893 Handle<FixedArray> elements = 3894 isolate->factory()->NewFixedArray(3 + capture_count); 3895 Handle<String> match; 3896 if (!first) { 3897 match = isolate->factory()->NewProperSubString(subject, 3898 match_start, 3899 match_end); 3900 } else { 3901 match = isolate->factory()->NewSubString(subject, 3902 match_start, 3903 match_end); 3904 } 3905 elements->set(0, *match); 3906 for (int i = 1; i <= capture_count; i++) { 3907 int start = register_vector[i * 2]; 3908 if (start >= 0) { 3909 int end = register_vector[i * 2 + 1]; 3910 ASSERT(start <= end); 3911 Handle<String> substring; 3912 if (!first) { 3913 substring = isolate->factory()->NewProperSubString(subject, 3914 start, 3915 end); 3916 } else { 3917 substring = isolate->factory()->NewSubString(subject, start, end); 3918 } 3919 elements->set(i, *substring); 3920 } else { 3921 ASSERT(register_vector[i * 2 + 1] < 0); 3922 elements->set(i, isolate->heap()->undefined_value()); 3923 } 3924 } 3925 elements->set(capture_count + 1, Smi::FromInt(match_start)); 3926 elements->set(capture_count + 2, *subject); 3927 builder->Add(*isolate->factory()->NewJSArrayWithElements(elements)); 3928 } 3929 // Swap register vectors, so the last successful match is in 3930 // prev_register_vector. 3931 Vector<int32_t> tmp = prev_register_vector; 3932 prev_register_vector = register_vector; 3933 register_vector = tmp; 3934 3935 if (match_end > match_start) { 3936 pos = match_end; 3937 } else { 3938 pos = match_end + 1; 3939 if (pos > subject_length) { 3940 break; 3941 } 3942 } 3943 3944 result = RegExpImpl::IrregexpExecOnce(regexp, 3945 subject, 3946 pos, 3947 register_vector); 3948 first = false; 3949 } while (result == RegExpImpl::RE_SUCCESS); 3950 3951 if (result != RegExpImpl::RE_EXCEPTION) { 3952 // Finished matching, with at least one match. 3953 if (match_end < subject_length) { 3954 ReplacementStringBuilder::AddSubjectSlice(builder, 3955 match_end, 3956 subject_length); 3957 } 3958 3959 int last_match_capture_count = (capture_count + 1) * 2; 3960 int last_match_array_size = 3961 last_match_capture_count + RegExpImpl::kLastMatchOverhead; 3962 last_match_array->EnsureSize(last_match_array_size); 3963 AssertNoAllocation no_gc; 3964 FixedArray* elements = FixedArray::cast(last_match_array->elements()); 3965 RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count); 3966 RegExpImpl::SetLastSubject(elements, *subject); 3967 RegExpImpl::SetLastInput(elements, *subject); 3968 for (int i = 0; i < last_match_capture_count; i++) { 3969 RegExpImpl::SetCapture(elements, i, prev_register_vector[i]); 3970 } 3971 return RegExpImpl::RE_SUCCESS; 3972 } 3973 } 3974 // No matches at all, return failure or exception result directly. 3975 return result; 3976 } 3977 3978 3979 RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExecMultiple) { 3980 ASSERT(args.length() == 4); 3981 HandleScope handles(isolate); 3982 3983 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); 3984 if (!subject->IsFlat()) FlattenString(subject); 3985 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); 3986 CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 2); 3987 CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3); 3988 3989 ASSERT(last_match_info->HasFastElements()); 3990 ASSERT(regexp->GetFlags().is_global()); 3991 Handle<FixedArray> result_elements; 3992 if (result_array->HasFastElements()) { 3993 result_elements = 3994 Handle<FixedArray>(FixedArray::cast(result_array->elements())); 3995 } 3996 if (result_elements.is_null() || result_elements->length() < 16) { 3997 result_elements = isolate->factory()->NewFixedArrayWithHoles(16); 3998 } 3999 FixedArrayBuilder builder(result_elements); 4000 4001 if (regexp->TypeTag() == JSRegExp::ATOM) { 4002 Handle<