Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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 1740 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1751 ASSERT(new_code->has_debug_break_slots()); | 1751 ASSERT(new_code->has_debug_break_slots()); |
| 1752 ASSERT(current_code->is_compiled_optimizable() == | 1752 ASSERT(current_code->is_compiled_optimizable() == |
| 1753 new_code->is_compiled_optimizable()); | 1753 new_code->is_compiled_optimizable()); |
| 1754 ASSERT(current_code->instruction_size() <= new_code->instruction_size()); | 1754 ASSERT(current_code->instruction_size() <= new_code->instruction_size()); |
| 1755 } | 1755 } |
| 1756 #endif | 1756 #endif |
| 1757 return result; | 1757 return result; |
| 1758 } | 1758 } |
| 1759 | 1759 |
| 1760 | 1760 |
| 1761 static void CollectActiveFunctionsFromThread( | |
| 1762 Isolate* isolate, | |
| 1763 ThreadLocalTop* top, | |
| 1764 List<Handle<JSFunction> >* active_functions, | |
| 1765 Object* active_code_marker) { | |
| 1766 // Find all non-optimized code functions with activation frames | |
| 1767 // on the stack. This includes functions which have optimized | |
| 1768 // activations (including inlined functions) on the stack as the | |
| 1769 // non-optimized code is needed for the lazy deoptimization. | |
| 1770 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { | |
| 1771 JavaScriptFrame* frame = it.frame(); | |
| 1772 if (frame->is_optimized()) { | |
| 1773 List<JSFunction*> functions(Compiler::kMaxInliningLevels + 1); | |
| 1774 frame->GetFunctions(&functions); | |
| 1775 for (int i = 0; i < functions.length(); i++) { | |
| 1776 JSFunction* function = functions[i]; | |
| 1777 active_functions->Add(Handle<JSFunction>(function)); | |
| 1778 function->shared()->code()->set_gc_metadata(active_code_marker); | |
| 1779 } | |
| 1780 } else if (frame->function()->IsJSFunction()) { | |
| 1781 JSFunction* function = JSFunction::cast(frame->function()); | |
| 1782 ASSERT(frame->LookupCode()->kind() == Code::FUNCTION); | |
| 1783 active_functions->Add(Handle<JSFunction>(function)); | |
| 1784 function->shared()->code()->set_gc_metadata(active_code_marker); | |
| 1785 } | |
| 1786 } | |
| 1787 } | |
| 1788 | |
| 1789 | |
| 1790 static void RedirectActivationsToRecompiledCodeOnThread( | |
| 1791 Isolate* isolate, | |
| 1792 ThreadLocalTop* top) { | |
| 1793 for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { | |
| 1794 JavaScriptFrame* frame = it.frame(); | |
| 1795 | |
| 1796 if (frame->is_optimized() || !frame->function()->IsJSFunction()) continue; | |
| 1797 | |
| 1798 JSFunction* function = JSFunction::cast(frame->function()); | |
| 1799 | |
| 1800 ASSERT(frame->LookupCode()->kind() == Code::FUNCTION); | |
| 1801 | |
| 1802 Handle<Code> frame_code(frame->LookupCode()); | |
| 1803 if (frame_code->has_debug_break_slots()) continue; | |
| 1804 | |
| 1805 Handle<Code> new_code(function->shared()->code()); | |
| 1806 if (new_code->kind() != Code::FUNCTION || | |
| 1807 !new_code->has_debug_break_slots()) { | |
| 1808 continue; | |
| 1809 } | |
| 1810 | |
| 1811 intptr_t delta = frame->pc() - frame_code->instruction_start(); | |
| 1812 int debug_break_slot_count = 0; | |
| 1813 int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT); | |
| 1814 for (RelocIterator it(*new_code, mask); !it.done(); it.next()) { | |
| 1815 // Check if the pc in the new code with debug break | |
| 1816 // slots is before this slot. | |
| 1817 RelocInfo* info = it.rinfo(); | |
| 1818 int debug_break_slot_bytes = | |
| 1819 debug_break_slot_count * Assembler::kDebugBreakSlotLength; | |
| 1820 intptr_t new_delta = | |
| 1821 info->pc() - | |
| 1822 new_code->instruction_start() - | |
| 1823 debug_break_slot_bytes; | |
| 1824 if (new_delta > delta) { | |
| 1825 break; | |
| 1826 } | |
| 1827 | |
| 1828 // Passed a debug break slot in the full code with debug | |
| 1829 // break slots. | |
| 1830 debug_break_slot_count++; | |
| 1831 } | |
| 1832 int debug_break_slot_bytes = | |
| 1833 debug_break_slot_count * Assembler::kDebugBreakSlotLength; | |
| 1834 if (FLAG_trace_deopt) { | |
| 1835 PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " | |
| 1836 "with %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " | |
| 1837 "for debugging, " | |
| 1838 "changing pc from %08" V8PRIxPTR " to %08" V8PRIxPTR "\n", | |
| 1839 reinterpret_cast<intptr_t>( | |
| 1840 frame_code->instruction_start()), | |
| 1841 reinterpret_cast<intptr_t>( | |
| 1842 frame_code->instruction_start()) + | |
| 1843 frame_code->instruction_size(), | |
|
Erik Corry
2012/01/25 15:00:57
Can we indent lines like this so that we have only
| |
| 1844 frame_code->instruction_size(), | |
| 1845 reinterpret_cast<intptr_t>(new_code->instruction_start()), | |
| 1846 reinterpret_cast<intptr_t>(new_code->instruction_start()) + | |
| 1847 new_code->instruction_size(), | |
| 1848 new_code->instruction_size(), | |
| 1849 reinterpret_cast<intptr_t>(frame->pc()), | |
| 1850 reinterpret_cast<intptr_t>(new_code->instruction_start()) + | |
| 1851 delta + debug_break_slot_bytes); | |
| 1852 } | |
| 1853 | |
| 1854 // Patch the return address to return into the code with | |
| 1855 // debug break slots. | |
| 1856 frame->set_pc( | |
| 1857 new_code->instruction_start() + delta + debug_break_slot_bytes); | |
| 1858 } | |
| 1859 } | |
| 1860 | |
| 1861 | |
| 1862 class ActiveFunctionsCollector : public ThreadVisitor { | |
| 1863 public: | |
| 1864 explicit ActiveFunctionsCollector(List<Handle<JSFunction> >* active_functions, | |
| 1865 Object* active_code_marker) | |
| 1866 : active_functions_(active_functions), | |
| 1867 active_code_marker_(active_code_marker) { } | |
| 1868 | |
| 1869 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { | |
| 1870 CollectActiveFunctionsFromThread(isolate, | |
| 1871 top, | |
| 1872 active_functions_, | |
| 1873 active_code_marker_); | |
| 1874 } | |
| 1875 | |
| 1876 private: | |
| 1877 List<Handle<JSFunction> >* active_functions_; | |
| 1878 Object* active_code_marker_; | |
| 1879 }; | |
| 1880 | |
| 1881 | |
| 1882 class ActiveFunctionsRedirector : public ThreadVisitor { | |
| 1883 public: | |
| 1884 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { | |
| 1885 RedirectActivationsToRecompiledCodeOnThread(isolate, top); | |
| 1886 } | |
| 1887 }; | |
| 1888 | |
| 1889 | |
| 1761 void Debug::PrepareForBreakPoints() { | 1890 void Debug::PrepareForBreakPoints() { |
| 1762 // If preparing for the first break point make sure to deoptimize all | 1891 // If preparing for the first break point make sure to deoptimize all |
| 1763 // functions as debugging does not work with optimized code. | 1892 // functions as debugging does not work with optimized code. |
| 1764 if (!has_break_points_) { | 1893 if (!has_break_points_) { |
| 1765 Deoptimizer::DeoptimizeAll(); | 1894 Deoptimizer::DeoptimizeAll(); |
| 1766 | 1895 |
| 1767 Handle<Code> lazy_compile = | 1896 Handle<Code> lazy_compile = |
| 1768 Handle<Code>(isolate_->builtins()->builtin(Builtins::kLazyCompile)); | 1897 Handle<Code>(isolate_->builtins()->builtin(Builtins::kLazyCompile)); |
| 1769 | 1898 |
| 1770 // Keep the list of activated functions in a handlified list as it | 1899 // Keep the list of activated functions in a handlified list as it |
| 1771 // is used both in GC and non-GC code. | 1900 // is used both in GC and non-GC code. |
| 1772 List<Handle<JSFunction> > active_functions(100); | 1901 List<Handle<JSFunction> > active_functions(100); |
| 1773 | 1902 |
| 1774 { | 1903 { |
| 1904 Object* active_code_marker = isolate_->heap()->the_hole_value(); | |
|
Erik Corry
2012/01/25 15:00:57
I know the hole never moves or dies, but it seems
| |
| 1905 | |
| 1775 // We are going to iterate heap to find all functions without | 1906 // We are going to iterate heap to find all functions without |
| 1776 // debug break slots. | 1907 // debug break slots. |
| 1777 isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask); | 1908 isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask); |
| 1778 | 1909 |
| 1779 // Ensure no GC in this scope as we are comparing raw pointer | 1910 // Ensure no GC in this scope as we are going to use gc_metadata |
| 1780 // values and performing a heap iteration. | 1911 // field in the Code object to mark active functions. |
| 1781 AssertNoAllocation no_allocation; | 1912 AssertNoAllocation no_allocation; |
| 1782 | 1913 |
| 1783 // Find all non-optimized code functions with activation frames | 1914 CollectActiveFunctionsFromThread(isolate_, |
| 1784 // on the stack. This includes functions which have optimized | 1915 isolate_->thread_local_top(), |
| 1785 // activations (including inlined functions) on the stack as the | 1916 &active_functions, |
| 1786 // non-optimized code is needed for the lazy deoptimization. | 1917 active_code_marker); |
| 1787 for (JavaScriptFrameIterator it(isolate_); !it.done(); it.Advance()) { | 1918 ActiveFunctionsCollector active_functions_collector(&active_functions, |
| 1788 JavaScriptFrame* frame = it.frame(); | 1919 active_code_marker); |
| 1789 if (frame->is_optimized()) { | 1920 isolate_->thread_manager()->IterateArchivedThreads( |
| 1790 List<JSFunction*> functions(Compiler::kMaxInliningLevels + 1); | 1921 &active_functions_collector); |
| 1791 frame->GetFunctions(&functions); | |
| 1792 for (int i = 0; i < functions.length(); i++) { | |
| 1793 if (!functions[i]->shared()->code()->has_debug_break_slots()) { | |
| 1794 active_functions.Add(Handle<JSFunction>(functions[i])); | |
| 1795 } | |
| 1796 } | |
| 1797 } else if (frame->function()->IsJSFunction()) { | |
| 1798 JSFunction* function = JSFunction::cast(frame->function()); | |
| 1799 ASSERT(frame->LookupCode()->kind() == Code::FUNCTION); | |
| 1800 if (!frame->LookupCode()->has_debug_break_slots() || | |
| 1801 !function->shared()->code()->has_debug_break_slots()) { | |
| 1802 active_functions.Add(Handle<JSFunction>(function)); | |
| 1803 } | |
| 1804 } | |
| 1805 } | |
| 1806 | |
| 1807 // Sort the functions on the object pointer value to prepare for | |
| 1808 // the binary search below. | |
| 1809 active_functions.Sort(HandleObjectPointerCompare<JSFunction>); | |
| 1810 | 1922 |
| 1811 // Scan the heap for all non-optimized functions which has no | 1923 // Scan the heap for all non-optimized functions which has no |
| 1812 // debug break slots. | 1924 // debug break slots and are not active or inlined into an active |
| 1925 // function and mark them for lazy compilation. | |
| 1813 HeapIterator iterator; | 1926 HeapIterator iterator; |
| 1814 HeapObject* obj = NULL; | 1927 HeapObject* obj = NULL; |
| 1815 while (((obj = iterator.next()) != NULL)) { | 1928 while (((obj = iterator.next()) != NULL)) { |
| 1816 if (obj->IsJSFunction()) { | 1929 if (obj->IsJSFunction()) { |
| 1817 JSFunction* function = JSFunction::cast(obj); | 1930 JSFunction* function = JSFunction::cast(obj); |
| 1818 if (function->shared()->allows_lazy_compilation() && | 1931 SharedFunctionInfo* shared = function->shared(); |
| 1819 function->shared()->script()->IsScript() && | 1932 if (shared->allows_lazy_compilation() && |
| 1933 shared->script()->IsScript() && | |
| 1820 function->code()->kind() == Code::FUNCTION && | 1934 function->code()->kind() == Code::FUNCTION && |
| 1821 !function->code()->has_debug_break_slots()) { | 1935 !function->code()->has_debug_break_slots() && |
| 1822 bool has_activation = | 1936 shared->code()->gc_metadata() != active_code_marker) { |
| 1823 SortedListBSearch<Handle<JSFunction> >( | 1937 function->set_code(*lazy_compile); |
| 1824 active_functions, | 1938 function->shared()->set_code(*lazy_compile); |
| 1825 Handle<JSFunction>(function), | |
| 1826 HandleObjectPointerCompare<JSFunction>) != -1; | |
| 1827 if (!has_activation) { | |
| 1828 function->set_code(*lazy_compile); | |
| 1829 function->shared()->set_code(*lazy_compile); | |
| 1830 } | |
| 1831 } | 1939 } |
| 1832 } | 1940 } |
| 1833 } | 1941 } |
| 1942 | |
| 1943 // Clear gc_metadata field. | |
| 1944 for (int i = 0; i < active_functions.length(); i++) { | |
| 1945 Handle<JSFunction> function = active_functions[i]; | |
| 1946 function->shared()->code()->set_gc_metadata(Smi::FromInt(0)); | |
| 1947 } | |
| 1834 } | 1948 } |
| 1835 | 1949 |
| 1836 // Now the non-GC scope is left, and the sorting of the functions | |
| 1837 // in active_function is not ensured any more. The code below does | |
| 1838 // not rely on it. | |
| 1839 | |
| 1840 // Now recompile all functions with activation frames and and | 1950 // Now recompile all functions with activation frames and and |
| 1841 // patch the return address to run in the new compiled code. | 1951 // patch the return address to run in the new compiled code. |
| 1842 for (int i = 0; i < active_functions.length(); i++) { | 1952 for (int i = 0; i < active_functions.length(); i++) { |
| 1843 Handle<JSFunction> function = active_functions[i]; | 1953 Handle<JSFunction> function = active_functions[i]; |
| 1954 | |
| 1955 if (function->code()->kind() == Code::FUNCTION && | |
| 1956 function->code()->has_debug_break_slots()) { | |
| 1957 // Nothing to do. Function code already had debug break slots. | |
| 1958 continue; | |
| 1959 } | |
| 1960 | |
| 1844 Handle<SharedFunctionInfo> shared(function->shared()); | 1961 Handle<SharedFunctionInfo> shared(function->shared()); |
| 1845 // If recompilation is not possible just skip it. | 1962 // If recompilation is not possible just skip it. |
| 1846 if (shared->is_toplevel() || | 1963 if (shared->is_toplevel() || |
| 1847 !shared->allows_lazy_compilation() || | 1964 !shared->allows_lazy_compilation() || |
| 1848 shared->code()->kind() == Code::BUILTIN) { | 1965 shared->code()->kind() == Code::BUILTIN) { |
| 1849 continue; | 1966 continue; |
| 1850 } | 1967 } |
| 1851 | 1968 |
| 1852 // Make sure that the shared full code is compiled with debug | 1969 // Make sure that the shared full code is compiled with debug |
| 1853 // break slots. | 1970 // break slots. |
| 1854 if (function->code() == *lazy_compile) { | |
| 1855 function->set_code(shared->code()); | |
| 1856 } | |
| 1857 if (!shared->code()->has_debug_break_slots()) { | 1971 if (!shared->code()->has_debug_break_slots()) { |
| 1858 // Try to compile the full code with debug break slots. If it | 1972 // Try to compile the full code with debug break slots. If it |
| 1859 // fails just keep the current code. | 1973 // fails just keep the current code. |
| 1860 Handle<Code> current_code(function->shared()->code()); | 1974 Handle<Code> current_code(function->shared()->code()); |
| 1861 ZoneScope zone_scope(isolate_, DELETE_ON_EXIT); | 1975 ZoneScope zone_scope(isolate_, DELETE_ON_EXIT); |
| 1862 shared->set_code(*lazy_compile); | 1976 shared->set_code(*lazy_compile); |
| 1863 bool prev_force_debugger_active = | 1977 bool prev_force_debugger_active = |
| 1864 isolate_->debugger()->force_debugger_active(); | 1978 isolate_->debugger()->force_debugger_active(); |
| 1865 isolate_->debugger()->set_force_debugger_active(true); | 1979 isolate_->debugger()->set_force_debugger_active(true); |
| 1866 ASSERT(current_code->kind() == Code::FUNCTION); | 1980 ASSERT(current_code->kind() == Code::FUNCTION); |
| 1867 CompileFullCodeForDebugging(shared, current_code); | 1981 CompileFullCodeForDebugging(shared, current_code); |
| 1868 isolate_->debugger()->set_force_debugger_active( | 1982 isolate_->debugger()->set_force_debugger_active( |
| 1869 prev_force_debugger_active); | 1983 prev_force_debugger_active); |
| 1870 if (!shared->is_compiled()) { | 1984 if (!shared->is_compiled()) { |
| 1871 shared->set_code(*current_code); | 1985 shared->set_code(*current_code); |
| 1872 continue; | 1986 continue; |
| 1873 } | 1987 } |
| 1874 } | 1988 } |
| 1875 Handle<Code> new_code(shared->code()); | |
| 1876 | 1989 |
| 1877 // Find the function and patch the return address. | 1990 // Keep function code in sync with shared function info. |
| 1878 for (JavaScriptFrameIterator it(isolate_); !it.done(); it.Advance()) { | 1991 function->set_code(shared->code()); |
| 1879 JavaScriptFrame* frame = it.frame(); | 1992 } |
| 1880 // If the current frame is for this function in its | |
| 1881 // non-optimized form rewrite the return address to continue | |
| 1882 // in the newly compiled full code with debug break slots. | |
| 1883 if (!frame->is_optimized() && | |
| 1884 frame->function()->IsJSFunction() && | |
| 1885 frame->function() == *function) { | |
| 1886 ASSERT(frame->LookupCode()->kind() == Code::FUNCTION); | |
| 1887 Handle<Code> frame_code(frame->LookupCode()); | |
| 1888 if (frame_code->has_debug_break_slots()) continue; | |
| 1889 intptr_t delta = frame->pc() - frame_code->instruction_start(); | |
| 1890 int debug_break_slot_count = 0; | |
| 1891 int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT); | |
| 1892 for (RelocIterator it(*new_code, mask); !it.done(); it.next()) { | |
| 1893 // Check if the pc in the new code with debug break | |
| 1894 // slots is before this slot. | |
| 1895 RelocInfo* info = it.rinfo(); | |
| 1896 int debug_break_slot_bytes = | |
| 1897 debug_break_slot_count * Assembler::kDebugBreakSlotLength; | |
| 1898 intptr_t new_delta = | |
| 1899 info->pc() - | |
| 1900 new_code->instruction_start() - | |
| 1901 debug_break_slot_bytes; | |
| 1902 if (new_delta > delta) { | |
| 1903 break; | |
| 1904 } | |
| 1905 | 1993 |
| 1906 // Passed a debug break slot in the full code with debug | 1994 RedirectActivationsToRecompiledCodeOnThread(isolate_, |
| 1907 // break slots. | 1995 isolate_->thread_local_top()); |
| 1908 debug_break_slot_count++; | |
| 1909 } | |
| 1910 int debug_break_slot_bytes = | |
| 1911 debug_break_slot_count * Assembler::kDebugBreakSlotLength; | |
| 1912 if (FLAG_trace_deopt) { | |
| 1913 PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " | |
| 1914 "with %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " | |
| 1915 "for debugging, " | |
| 1916 "changing pc from %08" V8PRIxPTR " to %08" V8PRIxPTR "\n", | |
| 1917 reinterpret_cast<intptr_t>( | |
| 1918 frame_code->instruction_start()), | |
| 1919 reinterpret_cast<intptr_t>( | |
| 1920 frame_code->instruction_start()) + | |
| 1921 frame_code->instruction_size(), | |
| 1922 frame_code->instruction_size(), | |
| 1923 reinterpret_cast<intptr_t>(new_code->instruction_start()), | |
| 1924 reinterpret_cast<intptr_t>(new_code->instruction_start()) + | |
| 1925 new_code->instruction_size(), | |
| 1926 new_code->instruction_size(), | |
| 1927 reinterpret_cast<intptr_t>(frame->pc()), | |
| 1928 reinterpret_cast<intptr_t>(new_code->instruction_start()) + | |
| 1929 delta + debug_break_slot_bytes); | |
| 1930 } | |
| 1931 | 1996 |
| 1932 // Patch the return address to return into the code with | 1997 ActiveFunctionsRedirector active_functions_redirector; |
| 1933 // debug break slots. | 1998 isolate_->thread_manager()->IterateArchivedThreads( |
| 1934 frame->set_pc( | 1999 &active_functions_redirector); |
| 1935 new_code->instruction_start() + delta + debug_break_slot_bytes); | 2000 |
| 1936 } | |
| 1937 } | |
| 1938 } | |
| 1939 } | 2001 } |
| 1940 } | 2002 } |
| 1941 | 2003 |
| 1942 | 2004 |
| 1943 // Ensures the debug information is present for shared. | 2005 // Ensures the debug information is present for shared. |
| 1944 bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) { | 2006 bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) { |
| 1945 // Return if we already have the debug info for shared. | 2007 // Return if we already have the debug info for shared. |
| 1946 if (HasDebugInfo(shared)) { | 2008 if (HasDebugInfo(shared)) { |
| 1947 ASSERT(shared->is_compiled()); | 2009 ASSERT(shared->is_compiled()); |
| 1948 return true; | 2010 return true; |
| (...skipping 1541 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3490 { | 3552 { |
| 3491 Locker locker; | 3553 Locker locker; |
| 3492 Isolate::Current()->debugger()->CallMessageDispatchHandler(); | 3554 Isolate::Current()->debugger()->CallMessageDispatchHandler(); |
| 3493 } | 3555 } |
| 3494 } | 3556 } |
| 3495 } | 3557 } |
| 3496 | 3558 |
| 3497 #endif // ENABLE_DEBUGGER_SUPPORT | 3559 #endif // ENABLE_DEBUGGER_SUPPORT |
| 3498 | 3560 |
| 3499 } } // namespace v8::internal | 3561 } } // namespace v8::internal |
| OLD | NEW |