Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(210)

Side by Side Diff: src/ia32/stub-cache-ia32.cc

Issue 6713074: Require an isolate parameter for most external reference creation to (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Further cleanup Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/ia32/regexp-macro-assembler-ia32.cc ('k') | src/parser.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. 1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 } 408 }
409 409
410 410
411 static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, 411 static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm,
412 Register receiver, 412 Register receiver,
413 Register holder, 413 Register holder,
414 Register name, 414 Register name,
415 JSObject* holder_obj) { 415 JSObject* holder_obj) {
416 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); 416 PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
417 __ CallExternalReference( 417 __ CallExternalReference(
418 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly)), 418 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
419 5); 419 masm->isolate()),
420 5);
420 } 421 }
421 422
422 423
423 // Number of pointers to be reserved on stack for fast API call. 424 // Number of pointers to be reserved on stack for fast API call.
424 static const int kFastApiCallArguments = 3; 425 static const int kFastApiCallArguments = 3;
425 426
426 427
427 // Reserves space for the extra arguments to API function in the 428 // Reserves space for the extra arguments to API function in the
428 // caller's frame. 429 // caller's frame.
429 // 430 //
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after
696 // Save the name_ register across the call. 697 // Save the name_ register across the call.
697 __ push(name_); 698 __ push(name_);
698 699
699 PushInterceptorArguments(masm, 700 PushInterceptorArguments(masm,
700 receiver, 701 receiver,
701 holder, 702 holder,
702 name_, 703 name_,
703 interceptor_holder); 704 interceptor_holder);
704 705
705 __ CallExternalReference( 706 __ CallExternalReference(
706 ExternalReference( 707 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
707 IC_Utility(IC::kLoadPropertyWithInterceptorForCall)), 708 masm->isolate()),
708 5); 709 5);
709 710
710 // Restore the name_ register. 711 // Restore the name_ register.
711 __ pop(name_); 712 __ pop(name_);
712 __ LeaveInternalFrame(); 713 __ LeaveInternalFrame();
713 } 714 }
714 715
715 void LoadWithInterceptor(MacroAssembler* masm, 716 void LoadWithInterceptor(MacroAssembler* masm,
716 Register receiver, 717 Register receiver,
717 Register holder, 718 Register holder,
718 JSObject* holder_obj, 719 JSObject* holder_obj,
719 Label* interceptor_succeeded) { 720 Label* interceptor_succeeded) {
720 __ EnterInternalFrame(); 721 __ EnterInternalFrame();
721 __ push(holder); // Save the holder. 722 __ push(holder); // Save the holder.
722 __ push(name_); // Save the name. 723 __ push(name_); // Save the name.
723 724
724 CompileCallLoadPropertyWithInterceptor(masm, 725 CompileCallLoadPropertyWithInterceptor(masm,
725 receiver, 726 receiver,
726 holder, 727 holder,
727 name_, 728 name_,
728 holder_obj); 729 holder_obj);
729 730
730 __ pop(name_); // Restore the name. 731 __ pop(name_); // Restore the name.
731 __ pop(receiver); // Restore the holder. 732 __ pop(receiver); // Restore the holder.
732 __ LeaveInternalFrame(); 733 __ LeaveInternalFrame();
733 734
734 __ cmp(eax, FACTORY->no_interceptor_result_sentinel()); 735 __ cmp(eax, masm->isolate()->factory()->no_interceptor_result_sentinel());
735 __ j(not_equal, interceptor_succeeded); 736 __ j(not_equal, interceptor_succeeded);
736 } 737 }
737 738
738 StubCompiler* stub_compiler_; 739 StubCompiler* stub_compiler_;
739 const ParameterCount& arguments_; 740 const ParameterCount& arguments_;
740 Register name_; 741 Register name_;
741 }; 742 };
742 743
743 744
744 void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { 745 void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
786 // Perform map transition for the receiver if necessary. 787 // Perform map transition for the receiver if necessary.
787 if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { 788 if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
788 // The properties must be extended before we can store the value. 789 // The properties must be extended before we can store the value.
789 // We jump to a runtime call that extends the properties array. 790 // We jump to a runtime call that extends the properties array.
790 __ pop(scratch); // Return address. 791 __ pop(scratch); // Return address.
791 __ push(receiver_reg); 792 __ push(receiver_reg);
792 __ push(Immediate(Handle<Map>(transition))); 793 __ push(Immediate(Handle<Map>(transition)));
793 __ push(eax); 794 __ push(eax);
794 __ push(scratch); 795 __ push(scratch);
795 __ TailCallExternalReference( 796 __ TailCallExternalReference(
796 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1); 797 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
798 masm->isolate()),
799 3,
800 1);
797 return; 801 return;
798 } 802 }
799 803
800 if (transition != NULL) { 804 if (transition != NULL) {
801 // Update the map of the object; no write barrier updating is 805 // Update the map of the object; no write barrier updating is
802 // needed because the map is never in new space. 806 // needed because the map is never in new space.
803 __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), 807 __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset),
804 Immediate(Handle<Map>(transition))); 808 Immediate(Handle<Map>(transition)));
805 } 809 }
806 810
(...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after
1255 __ pop(scratch2); // return address 1259 __ pop(scratch2); // return address
1256 __ push(receiver); 1260 __ push(receiver);
1257 __ push(holder_reg); 1261 __ push(holder_reg);
1258 __ mov(holder_reg, Immediate(Handle<AccessorInfo>(callback))); 1262 __ mov(holder_reg, Immediate(Handle<AccessorInfo>(callback)));
1259 __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); 1263 __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset));
1260 __ push(holder_reg); 1264 __ push(holder_reg);
1261 __ push(name_reg); 1265 __ push(name_reg);
1262 __ push(scratch2); // restore return address 1266 __ push(scratch2); // restore return address
1263 1267
1264 ExternalReference ref = 1268 ExternalReference ref =
1265 ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); 1269 ExternalReference(IC_Utility(IC::kLoadCallbackProperty),
1270 masm()->isolate());
1266 __ TailCallExternalReference(ref, 5, 1); 1271 __ TailCallExternalReference(ref, 5, 1);
1267 } 1272 }
1268 } else { // !compile_followup_inline 1273 } else { // !compile_followup_inline
1269 // Call the runtime system to load the interceptor. 1274 // Call the runtime system to load the interceptor.
1270 // Check that the maps haven't changed. 1275 // Check that the maps haven't changed.
1271 Register holder_reg = 1276 Register holder_reg =
1272 CheckPrototypes(object, receiver, interceptor_holder, 1277 CheckPrototypes(object, receiver, interceptor_holder,
1273 scratch1, scratch2, scratch3, name, miss); 1278 scratch1, scratch2, scratch3, name, miss);
1274 __ pop(scratch2); // save old return address 1279 __ pop(scratch2); // save old return address
1275 PushInterceptorArguments(masm(), receiver, holder_reg, 1280 PushInterceptorArguments(masm(), receiver, holder_reg,
1276 name_reg, interceptor_holder); 1281 name_reg, interceptor_holder);
1277 __ push(scratch2); // restore old return address 1282 __ push(scratch2); // restore old return address
1278 1283
1279 ExternalReference ref = ExternalReference( 1284 ExternalReference ref =
1280 IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); 1285 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad),
1286 masm()->isolate());
1281 __ TailCallExternalReference(ref, 5, 1); 1287 __ TailCallExternalReference(ref, 5, 1);
1282 } 1288 }
1283 } 1289 }
1284 1290
1285 1291
1286 void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { 1292 void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
1287 if (kind_ == Code::KEYED_CALL_IC) { 1293 if (kind_ == Code::KEYED_CALL_IC) {
1288 __ cmp(Operand(ecx), Immediate(Handle<String>(name))); 1294 __ cmp(Operand(ecx), Immediate(Handle<String>(name)));
1289 __ j(not_equal, miss, not_taken); 1295 __ j(not_equal, miss, not_taken);
1290 } 1296 }
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
1504 __ InNewSpace(ebx, ecx, equal, &exit); 1510 __ InNewSpace(ebx, ecx, equal, &exit);
1505 1511
1506 __ RecordWriteHelper(ebx, edx, ecx); 1512 __ RecordWriteHelper(ebx, edx, ecx);
1507 __ ret((argc + 1) * kPointerSize); 1513 __ ret((argc + 1) * kPointerSize);
1508 1514
1509 __ bind(&attempt_to_grow_elements); 1515 __ bind(&attempt_to_grow_elements);
1510 if (!FLAG_inline_new) { 1516 if (!FLAG_inline_new) {
1511 __ jmp(&call_builtin); 1517 __ jmp(&call_builtin);
1512 } 1518 }
1513 1519
1520 Isolate* isolate = masm()->isolate();
1514 ExternalReference new_space_allocation_top = 1521 ExternalReference new_space_allocation_top =
1515 ExternalReference::new_space_allocation_top_address(); 1522 ExternalReference::new_space_allocation_top_address(isolate);
1516 ExternalReference new_space_allocation_limit = 1523 ExternalReference new_space_allocation_limit =
1517 ExternalReference::new_space_allocation_limit_address(); 1524 ExternalReference::new_space_allocation_limit_address(isolate);
1518 1525
1519 const int kAllocationDelta = 4; 1526 const int kAllocationDelta = 4;
1520 // Load top. 1527 // Load top.
1521 __ mov(ecx, Operand::StaticVariable(new_space_allocation_top)); 1528 __ mov(ecx, Operand::StaticVariable(new_space_allocation_top));
1522 1529
1523 // Check if it's the end of elements. 1530 // Check if it's the end of elements.
1524 __ lea(edx, FieldOperand(ebx, 1531 __ lea(edx, FieldOperand(ebx,
1525 eax, times_half_pointer_size, 1532 eax, times_half_pointer_size,
1526 FixedArray::kHeaderSize - argc * kPointerSize)); 1533 FixedArray::kHeaderSize - argc * kPointerSize));
1527 __ cmp(edx, Operand(ecx)); 1534 __ cmp(edx, Operand(ecx));
(...skipping 20 matching lines...) Expand all
1548 // Increment element's and array's sizes. 1555 // Increment element's and array's sizes.
1549 __ add(FieldOperand(ebx, FixedArray::kLengthOffset), 1556 __ add(FieldOperand(ebx, FixedArray::kLengthOffset),
1550 Immediate(Smi::FromInt(kAllocationDelta))); 1557 Immediate(Smi::FromInt(kAllocationDelta)));
1551 __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax); 1558 __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
1552 1559
1553 // Elements are in new space, so write barrier is not required. 1560 // Elements are in new space, so write barrier is not required.
1554 __ ret((argc + 1) * kPointerSize); 1561 __ ret((argc + 1) * kPointerSize);
1555 } 1562 }
1556 1563
1557 __ bind(&call_builtin); 1564 __ bind(&call_builtin);
1558 __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush), 1565 __ TailCallExternalReference(
1559 argc + 1, 1566 ExternalReference(Builtins::c_ArrayPush, masm()->isolate()),
1560 1); 1567 argc + 1,
1568 1);
1561 } 1569 }
1562 1570
1563 __ bind(&miss); 1571 __ bind(&miss);
1564 Object* obj; 1572 Object* obj;
1565 { MaybeObject* maybe_obj = GenerateMissBranch(); 1573 { MaybeObject* maybe_obj = GenerateMissBranch();
1566 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 1574 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1567 } 1575 }
1568 1576
1569 // Return the generated code. 1577 // Return the generated code.
1570 return GetCode(function); 1578 return GetCode(function);
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1632 ecx, times_half_pointer_size, 1640 ecx, times_half_pointer_size,
1633 FixedArray::kHeaderSize), 1641 FixedArray::kHeaderSize),
1634 Immediate(FACTORY->the_hole_value())); 1642 Immediate(FACTORY->the_hole_value()));
1635 __ ret((argc + 1) * kPointerSize); 1643 __ ret((argc + 1) * kPointerSize);
1636 1644
1637 __ bind(&return_undefined); 1645 __ bind(&return_undefined);
1638 __ mov(eax, Immediate(FACTORY->undefined_value())); 1646 __ mov(eax, Immediate(FACTORY->undefined_value()));
1639 __ ret((argc + 1) * kPointerSize); 1647 __ ret((argc + 1) * kPointerSize);
1640 1648
1641 __ bind(&call_builtin); 1649 __ bind(&call_builtin);
1642 __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop), 1650 __ TailCallExternalReference(
1643 argc + 1, 1651 ExternalReference(Builtins::c_ArrayPop, masm()->isolate()),
1644 1); 1652 argc + 1,
1653 1);
1645 1654
1646 __ bind(&miss); 1655 __ bind(&miss);
1647 Object* obj; 1656 Object* obj;
1648 { MaybeObject* maybe_obj = GenerateMissBranch(); 1657 { MaybeObject* maybe_obj = GenerateMissBranch();
1649 if (!maybe_obj->ToObject(&obj)) return maybe_obj; 1658 if (!maybe_obj->ToObject(&obj)) return maybe_obj;
1650 } 1659 }
1651 1660
1652 // Return the generated code. 1661 // Return the generated code.
1653 return GetCode(function); 1662 return GetCode(function);
1654 } 1663 }
1655 1664
1656 1665
1657 MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( 1666 MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
1658 Object* object, 1667 Object* object,
1659 JSObject* holder, 1668 JSObject* holder,
1660 JSGlobalPropertyCell* cell, 1669 JSGlobalPropertyCell* cell,
1661 JSFunction* function, 1670 JSFunction* function,
1662 String* name) { 1671 String* name) {
1663 // ----------- S t a t e ------------- 1672 // ----------- S t a t e -------------
1664 // -- ecx : function name 1673 // -- ecx : function name
1665 // -- esp[0] : return address 1674 // -- esp[0] : return address
1666 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1675 // -- esp[(argc - n) * 4] : arg[n] (zero-based)
1667 // -- ... 1676 // -- ...
1668 // -- esp[(argc + 1) * 4] : receiver 1677 // -- esp[(argc + 1) * 4] : receiver
1669 // ----------------------------------- 1678 // -----------------------------------
1670 1679
1671 // If object is not a string, bail out to regular call. 1680 // If object is not a string, bail out to regular call.
1672 if (!object->IsString() || cell != NULL) return HEAP->undefined_value(); 1681 if (!object->IsString() || cell != NULL) {
1682 return masm()->isolate()->heap()->undefined_value();
1683 }
1673 1684
1674 const int argc = arguments().immediate(); 1685 const int argc = arguments().immediate();
1675 1686
1676 Label miss; 1687 Label miss;
1677 Label name_miss; 1688 Label name_miss;
1678 Label index_out_of_range; 1689 Label index_out_of_range;
1679 Label* index_out_of_range_label = &index_out_of_range; 1690 Label* index_out_of_range_label = &index_out_of_range;
1680 1691
1681 if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) { 1692 if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
1682 index_out_of_range_label = &miss; 1693 index_out_of_range_label = &miss;
(...skipping 848 matching lines...) Expand 10 before | Expand all | Expand 10 after
2531 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 2542 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
2532 2543
2533 __ pop(ebx); // remove the return address 2544 __ pop(ebx); // remove the return address
2534 __ push(edx); // receiver 2545 __ push(edx); // receiver
2535 __ push(Immediate(Handle<AccessorInfo>(callback))); // callback info 2546 __ push(Immediate(Handle<AccessorInfo>(callback))); // callback info
2536 __ push(ecx); // name 2547 __ push(ecx); // name
2537 __ push(eax); // value 2548 __ push(eax); // value
2538 __ push(ebx); // restore return address 2549 __ push(ebx); // restore return address
2539 2550
2540 // Do tail-call to the runtime system. 2551 // Do tail-call to the runtime system.
2552 Isolate* isolate = masm()->isolate();
2541 ExternalReference store_callback_property = 2553 ExternalReference store_callback_property =
2542 ExternalReference(IC_Utility(IC::kStoreCallbackProperty)); 2554 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate);
2543 __ TailCallExternalReference(store_callback_property, 4, 1); 2555 __ TailCallExternalReference(store_callback_property, 4, 1);
2544 2556
2545 // Handle store cache miss. 2557 // Handle store cache miss.
2546 __ bind(&miss); 2558 __ bind(&miss);
2547 Handle<Code> ic(Isolate::Current()->builtins()->builtin( 2559 Handle<Code> ic(isolate->builtins()->builtin(Builtins::StoreIC_Miss));
2548 Builtins::StoreIC_Miss));
2549 __ jmp(ic, RelocInfo::CODE_TARGET); 2560 __ jmp(ic, RelocInfo::CODE_TARGET);
2550 2561
2551 // Return the generated code. 2562 // Return the generated code.
2552 return GetCode(CALLBACKS, name); 2563 return GetCode(CALLBACKS, name);
2553 } 2564 }
2554 2565
2555 2566
2556 MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, 2567 MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver,
2557 String* name) { 2568 String* name) {
2558 // ----------- S t a t e ------------- 2569 // ----------- S t a t e -------------
(...skipping 23 matching lines...) Expand all
2582 ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded()); 2593 ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
2583 2594
2584 __ pop(ebx); // remove the return address 2595 __ pop(ebx); // remove the return address
2585 __ push(edx); // receiver 2596 __ push(edx); // receiver
2586 __ push(ecx); // name 2597 __ push(ecx); // name
2587 __ push(eax); // value 2598 __ push(eax); // value
2588 __ push(Immediate(Smi::FromInt(strict_mode_))); 2599 __ push(Immediate(Smi::FromInt(strict_mode_)));
2589 __ push(ebx); // restore return address 2600 __ push(ebx); // restore return address
2590 2601
2591 // Do tail-call to the runtime system. 2602 // Do tail-call to the runtime system.
2603 Isolate* isolate = masm()->isolate();
2592 ExternalReference store_ic_property = 2604 ExternalReference store_ic_property =
2593 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty)); 2605 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate);
2594 __ TailCallExternalReference(store_ic_property, 4, 1); 2606 __ TailCallExternalReference(store_ic_property, 4, 1);
2595 2607
2596 // Handle store cache miss. 2608 // Handle store cache miss.
2597 __ bind(&miss); 2609 __ bind(&miss);
2598 Handle<Code> ic(Isolate::Current()->builtins()->builtin( 2610 Handle<Code> ic(isolate->builtins()->builtin(Builtins::StoreIC_Miss));
2599 Builtins::StoreIC_Miss));
2600 __ jmp(ic, RelocInfo::CODE_TARGET); 2611 __ jmp(ic, RelocInfo::CODE_TARGET);
2601 2612
2602 // Return the generated code. 2613 // Return the generated code.
2603 return GetCode(INTERCEPTOR, name); 2614 return GetCode(INTERCEPTOR, name);
2604 } 2615 }
2605 2616
2606 2617
2607 MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, 2618 MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
2608 JSGlobalPropertyCell* cell, 2619 JSGlobalPropertyCell* cell,
2609 String* name) { 2620 String* name) {
(...skipping 1081 matching lines...) Expand 10 before | Expand all | Expand 10 after
3691 3702
3692 return GetCode(flags); 3703 return GetCode(flags);
3693 } 3704 }
3694 3705
3695 3706
3696 #undef __ 3707 #undef __
3697 3708
3698 } } // namespace v8::internal 3709 } } // namespace v8::internal
3699 3710
3700 #endif // V8_TARGET_ARCH_IA32 3711 #endif // V8_TARGET_ARCH_IA32
OLDNEW
« no previous file with comments | « src/ia32/regexp-macro-assembler-ia32.cc ('k') | src/parser.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698