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

Side by Side Diff: test/cctest/test-api.cc

Issue 16578008: Improved function entry hook coverage (Closed) Base URL: https://chromium.googlesource.com/external/v8.git@post_fix
Patch Set: Fix Windows X64 compile warnings." Created 7 years, 5 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
« no previous file with comments | « src/x64/code-stubs-x64.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 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 13 matching lines...) Expand all
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 27
28 #include <limits.h> 28 #include <limits.h>
29 29
30 #ifndef WIN32 30 #ifndef WIN32
31 #include <signal.h> // kill 31 #include <signal.h> // kill
32 #include <unistd.h> // getpid 32 #include <unistd.h> // getpid
33 #endif // WIN32 33 #endif // WIN32
34 #include <string>
35 #include <map>
34 36
35 #include "v8.h" 37 #include "v8.h"
36 38
37 #include "api.h" 39 #include "api.h"
38 #include "arguments.h" 40 #include "arguments.h"
39 #include "isolate.h" 41 #include "isolate.h"
40 #include "compilation-cache.h" 42 #include "compilation-cache.h"
41 #include "execution.h" 43 #include "execution.h"
42 #include "objects.h" 44 #include "objects.h"
43 #include "snapshot.h" 45 #include "snapshot.h"
(...skipping 12212 matching lines...) Expand 10 before | Expand all | Expand 10 after
12256 v8::HandleScope outer(isolate); 12258 v8::HandleScope outer(isolate);
12257 v8::Local<Context> env = Context::New(isolate); 12259 v8::Local<Context> env = Context::New(isolate);
12258 env->Enter(); 12260 env->Enter();
12259 v8::Handle<Value> value = NestedScope(env); 12261 v8::Handle<Value> value = NestedScope(env);
12260 v8::Handle<String> str(value->ToString()); 12262 v8::Handle<String> str(value->ToString());
12261 CHECK(!str.IsEmpty()); 12263 CHECK(!str.IsEmpty());
12262 env->Exit(); 12264 env->Exit();
12263 } 12265 }
12264 12266
12265 12267
12266 static i::Handle<i::JSFunction>* foo_ptr = NULL; 12268 static bool MatchPointers(void* key1, void* key2) {
12267 static int foo_entry_count = 0; 12269 return key1 == key2;
12268 static i::Handle<i::JSFunction>* bar_ptr = NULL; 12270 }
12269 static int bar_entry_count = 0; 12271
12270 static int bar_caller_count = 0; 12272
12271 12273 struct SymbolInfo {
12272 12274 size_t id;
12273 static void entry_hook(uintptr_t function, 12275 size_t size;
12274 uintptr_t return_addr_location) { 12276 std::string name;
12275 i::Code* code = i::Code::GetCodeFromTargetAddress( 12277 };
12278
12279
12280 class SetFunctionEntryHookTest {
12281 public:
12282 SetFunctionEntryHookTest() {
12283 CHECK(instance_ == NULL);
12284 instance_ = this;
12285 }
12286 ~SetFunctionEntryHookTest() {
12287 CHECK(instance_ == this);
12288 instance_ = NULL;
12289 }
12290 void Reset() {
12291 symbols_.clear();
12292 symbol_locations_.clear();
12293 invocations_.clear();
12294 }
12295 void RunTest();
12296 void OnJitEvent(const v8::JitCodeEvent* event);
12297 static void JitEvent(const v8::JitCodeEvent* event) {
12298 CHECK(instance_ != NULL);
12299 instance_->OnJitEvent(event);
12300 }
12301
12302 void OnEntryHook(uintptr_t function,
12303 uintptr_t return_addr_location);
12304 static void EntryHook(uintptr_t function,
12305 uintptr_t return_addr_location) {
12306 CHECK(instance_ != NULL);
12307 instance_->OnEntryHook(function, return_addr_location);
12308 }
12309
12310 static void RuntimeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
12311 CHECK(instance_ != NULL);
12312 args.GetReturnValue().Set(v8_num(42));
12313 }
12314 void RunLoopInNewEnv(v8::Isolate* isolate);
12315
12316 // Records addr as location of symbol.
12317 void InsertSymbolAt(i::Address addr, SymbolInfo* symbol);
12318
12319 // Finds the symbol containing addr
12320 SymbolInfo* FindSymbolForAddr(i::Address addr);
12321 // Returns the number of invocations where the caller name contains
12322 // \p caller_name and the function name contains \p function_name.
12323 int CountInvocations(const char* caller_name,
12324 const char* function_name);
12325
12326 i::Handle<i::JSFunction> foo_func_;
12327 i::Handle<i::JSFunction> bar_func_;
12328
12329 typedef std::map<size_t, SymbolInfo> SymbolMap;
12330 typedef std::map<i::Address, SymbolInfo*> SymbolLocationMap;
12331 typedef std::map<std::pair<SymbolInfo*, SymbolInfo*>, int> InvocationMap;
12332 SymbolMap symbols_;
12333 SymbolLocationMap symbol_locations_;
12334 InvocationMap invocations_;
12335
12336 static SetFunctionEntryHookTest* instance_;
12337 };
12338 SetFunctionEntryHookTest* SetFunctionEntryHookTest::instance_ = NULL;
12339
12340
12341 // Returns true if addr is in the range [start, start+len).
12342 static bool Overlaps(i::Address start, size_t len, i::Address addr) {
12343 if (start <= addr && start + len > addr)
12344 return true;
12345
12346 return false;
12347 }
12348
12349 void SetFunctionEntryHookTest::InsertSymbolAt(i::Address addr,
12350 SymbolInfo* symbol) {
12351 // Insert the symbol at the new location.
12352 SymbolLocationMap::iterator it =
12353 symbol_locations_.insert(std::make_pair(addr, symbol)).first;
12354 // Now erase symbols to the left and right that overlap this one.
12355 while (it != symbol_locations_.begin()) {
12356 SymbolLocationMap::iterator left = it;
12357 --left;
12358 if (!Overlaps(left->first, left->second->size, addr))
12359 break;
12360 symbol_locations_.erase(left);
12361 }
12362
12363 // Now erase symbols to the left and right that overlap this one.
12364 while (true) {
12365 SymbolLocationMap::iterator right = it;
12366 ++right;
12367 if (right == symbol_locations_.end())
12368 break;
12369 if (!Overlaps(addr, symbol->size, right->first))
12370 break;
12371 symbol_locations_.erase(right);
12372 }
12373 }
12374
12375
12376 void SetFunctionEntryHookTest::OnJitEvent(const v8::JitCodeEvent* event) {
12377 switch (event->type) {
12378 case v8::JitCodeEvent::CODE_ADDED: {
12379 CHECK(event->code_start != NULL);
12380 CHECK_NE(0, static_cast<int>(event->code_len));
12381 CHECK(event->name.str != NULL);
12382 size_t symbol_id = symbols_.size();
12383
12384 // Record the new symbol.
12385 SymbolInfo& info = symbols_[symbol_id];
12386 info.id = symbol_id;
12387 info.size = event->code_len;
12388 info.name.assign(event->name.str, event->name.str + event->name.len);
12389
12390 // And record it's location.
12391 InsertSymbolAt(reinterpret_cast<i::Address>(event->code_start), &info);
12392 }
12393 break;
12394
12395 case v8::JitCodeEvent::CODE_MOVED: {
12396 // We would like to never see code move that we haven't seen before,
12397 // but the code creation event does not happen until the line endings
12398 // have been calculated (this is so that we can report the line in the
12399 // script at which the function source is found, see
12400 // Compiler::RecordFunctionCompilation) and the line endings
12401 // calculations can cause a GC, which can move the newly created code
12402 // before its existence can be logged.
12403 SymbolLocationMap::iterator it(
12404 symbol_locations_.find(
12405 reinterpret_cast<i::Address>(event->code_start)));
12406 if (it != symbol_locations_.end()) {
12407 // Found a symbol at this location, move it.
12408 SymbolInfo* info = it->second;
12409 symbol_locations_.erase(it);
12410 InsertSymbolAt(reinterpret_cast<i::Address>(event->new_code_start),
12411 info);
12412 }
12413 }
12414 default:
12415 break;
12416 }
12417 }
12418
12419 void SetFunctionEntryHookTest::OnEntryHook(
12420 uintptr_t function, uintptr_t return_addr_location) {
12421 // Get the function's code object.
12422 i::Code* function_code = i::Code::GetCodeFromTargetAddress(
12276 reinterpret_cast<i::Address>(function)); 12423 reinterpret_cast<i::Address>(function));
12277 CHECK(code != NULL); 12424 CHECK(function_code != NULL);
12278 12425
12279 if (bar_ptr != NULL && code == (*bar_ptr)->code()) 12426 // Then try and look up the caller's code object.
12280 ++bar_entry_count; 12427 i::Address caller = *reinterpret_cast<i::Address*>(return_addr_location);
12281 12428
12282 if (foo_ptr != NULL && code == (*foo_ptr)->code()) 12429 // Count the invocation.
12283 ++foo_entry_count; 12430 SymbolInfo* caller_symbol = FindSymbolForAddr(caller);
12284 12431 SymbolInfo* function_symbol =
12285 // Let's check whether bar is the caller. 12432 FindSymbolForAddr(reinterpret_cast<i::Address>(function));
12286 if (bar_ptr != NULL) { 12433 ++invocations_[std::make_pair(caller_symbol, function_symbol)];
12287 const v8::internal::byte* caller = 12434
12288 *reinterpret_cast<v8::internal::byte**>(return_addr_location); 12435 if (!bar_func_.is_null() && function_code == bar_func_->code()) {
12289 12436 // Check that we have a symbol for the "bar" function at the right location.
12290 if ((*bar_ptr)->code()->instruction_start() <= caller && 12437 SymbolLocationMap::iterator it(
12291 (*bar_ptr)->code()->instruction_end() > caller) { 12438 symbol_locations_.find(function_code->instruction_start()));
12292 ++bar_caller_count; 12439 CHECK(it != symbol_locations_.end());
12440 }
12441
12442 if (!foo_func_.is_null() && function_code == foo_func_->code()) {
12443 // Check that we have a symbol for "foo" at the right location.
12444 SymbolLocationMap::iterator it(
12445 symbol_locations_.find(function_code->instruction_start()));
12446 CHECK(it != symbol_locations_.end());
12447 }
12448 }
12449
12450
12451 SymbolInfo* SetFunctionEntryHookTest::FindSymbolForAddr(i::Address addr) {
12452 SymbolLocationMap::iterator it(symbol_locations_.lower_bound(addr));
12453 // Do we have a direct hit on a symbol?
12454 if (it != symbol_locations_.end()) {
12455 if (it->first == addr)
12456 return it->second;
12457 }
12458
12459 // If not a direct hit, it'll have to be the previous symbol.
12460 if (it == symbol_locations_.begin())
12461 return NULL;
12462
12463 --it;
12464 size_t offs = addr - it->first;
12465 if (offs < it->second->size)
12466 return it->second;
12467
12468 return NULL;
12469 }
12470
12471
12472 int SetFunctionEntryHookTest::CountInvocations(
12473 const char* caller_name, const char* function_name) {
12474 InvocationMap::iterator it(invocations_.begin());
12475 int invocations = 0;
12476 for (; it != invocations_.end(); ++it) {
12477 SymbolInfo* caller = it->first.first;
12478 SymbolInfo* function = it->first.second;
12479
12480 // Filter out non-matching functions.
12481 if (function_name != NULL) {
12482 if (function->name.find(function_name) == std::string::npos)
12483 continue;
12293 } 12484 }
12294 } 12485
12295 } 12486 // Filter out non-matching callers.
12296 12487 if (caller_name != NULL) {
12297 12488 if (caller == NULL)
12298 static void RunLoopInNewEnv() { 12489 continue;
12299 bar_ptr = NULL; 12490 if (caller->name.find(caller_name) == std::string::npos)
12300 foo_ptr = NULL; 12491 continue;
12301 12492 }
12302 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 12493
12494 // It matches add the invocation count to the tally.
12495 invocations += it->second;
12496 }
12497
12498 return invocations;
12499 }
12500
12501
12502 void SetFunctionEntryHookTest::RunLoopInNewEnv(v8::Isolate* isolate) {
12303 v8::HandleScope outer(isolate); 12503 v8::HandleScope outer(isolate);
12304 v8::Local<Context> env = Context::New(isolate); 12504 v8::Local<Context> env = Context::New(isolate);
12305 env->Enter(); 12505 env->Enter();
12306 12506
12507 Local<ObjectTemplate> t = ObjectTemplate::New();
12508 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(RuntimeCallback));
12509 env->Global()->Set(v8_str("obj"), t->NewInstance());
12510
12307 const char* script = 12511 const char* script =
12308 "function bar() {" 12512 "function bar() {\n"
12309 " var sum = 0;" 12513 " var sum = 0;\n"
12310 " for (i = 0; i < 100; ++i)" 12514 " for (i = 0; i < 100; ++i)\n"
12311 " sum = foo(i);" 12515 " sum = foo(i);\n"
12312 " return sum;" 12516 " return sum;\n"
12313 "}" 12517 "}\n"
12314 "function foo(i) { return i * i; }"; 12518 "function foo(i) { return i * i; }\n"
12519 "// Invoke on the runtime function.\n"
12520 "obj.asdf()";
12315 CompileRun(script); 12521 CompileRun(script);
12316 i::Handle<i::JSFunction> bar = 12522 bar_func_ = i::Handle<i::JSFunction>::cast(
12317 i::Handle<i::JSFunction>::cast(
12318 v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar")))); 12523 v8::Utils::OpenHandle(*env->Global()->Get(v8_str("bar"))));
12319 ASSERT(*bar); 12524 ASSERT(!bar_func_.is_null());
12320 12525
12321 i::Handle<i::JSFunction> foo = 12526 foo_func_ =
12322 i::Handle<i::JSFunction>::cast( 12527 i::Handle<i::JSFunction>::cast(
12323 v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo")))); 12528 v8::Utils::OpenHandle(*env->Global()->Get(v8_str("foo"))));
12324 ASSERT(*foo); 12529 ASSERT(!foo_func_.is_null());
12325
12326 bar_ptr = &bar;
12327 foo_ptr = &foo;
12328 12530
12329 v8::Handle<v8::Value> value = CompileRun("bar();"); 12531 v8::Handle<v8::Value> value = CompileRun("bar();");
12330 CHECK(value->IsNumber()); 12532 CHECK(value->IsNumber());
12331 CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); 12533 CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value());
12332 12534
12333 // Test the optimized codegen path. 12535 // Test the optimized codegen path.
12334 value = CompileRun("%OptimizeFunctionOnNextCall(foo);" 12536 value = CompileRun("%OptimizeFunctionOnNextCall(foo);"
12335 "bar();"); 12537 "bar();");
12336 CHECK(value->IsNumber()); 12538 CHECK(value->IsNumber());
12337 CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value()); 12539 CHECK_EQ(9801.0, v8::Number::Cast(*value)->Value());
12338 12540
12339 env->Exit(); 12541 env->Exit();
12340 } 12542 }
12341 12543
12544 void SetFunctionEntryHookTest::RunTest() {
12545 // Work in a new isolate throughout.
12546 v8::Isolate* isolate = v8::Isolate::New();
12547
12548 // Test setting the entry hook on the new isolate.
12549 CHECK(v8::V8::SetFunctionEntryHook(isolate, EntryHook));
12550
12551 // Replacing the hook, once set should fail.
12552 CHECK_EQ(false, v8::V8::SetFunctionEntryHook(isolate, EntryHook));
12553
12554 {
12555 v8::Isolate::Scope scope(isolate);
12556
12557 v8::V8::SetJitCodeEventHandler(v8::kJitCodeEventDefault, JitEvent);
12558
12559 RunLoopInNewEnv(isolate);
12560
12561 // Check the exepected invocation counts.
12562 CHECK_EQ(2, CountInvocations(NULL, "bar"));
12563 CHECK_EQ(200, CountInvocations("bar", "foo"));
12564 CHECK_EQ(200, CountInvocations(NULL, "foo"));
12565
12566 // Verify that we have an entry hook on some specific stubs.
12567 CHECK_NE(0, CountInvocations(NULL, "CEntryStub"));
12568 CHECK_NE(0, CountInvocations(NULL, "JSEntryStub"));
12569 CHECK_NE(0, CountInvocations(NULL, "JSEntryTrampoline"));
12570 }
12571 isolate->Dispose();
12572
12573 Reset();
12574
12575 // Make sure a second isolate is unaffected by the previous entry hook.
12576 isolate = v8::Isolate::New();
12577 {
12578 v8::Isolate::Scope scope(isolate);
12579
12580 // Reset the entry count to zero and set the entry hook.
12581 RunLoopInNewEnv(isolate);
12582
12583 // We should record no invocations in this isolate.
12584 CHECK_EQ(0, static_cast<int>(invocations_.size()));
12585 }
12586 // Since the isolate has been used, we shouldn't be able to set an entry
12587 // hook anymore.
12588 CHECK_EQ(false, v8::V8::SetFunctionEntryHook(isolate, EntryHook));
12589
12590 isolate->Dispose();
12591 }
12592
12342 12593
12343 TEST(SetFunctionEntryHook) { 12594 TEST(SetFunctionEntryHook) {
12344 // FunctionEntryHook does not work well with experimental natives. 12595 // FunctionEntryHook does not work well with experimental natives.
12345 // Experimental natives are compiled during snapshot deserialization. 12596 // Experimental natives are compiled during snapshot deserialization.
12346 // This test breaks because InstallGetter (function from snapshot that 12597 // This test breaks because InstallGetter (function from snapshot that
12347 // only gets called from experimental natives) is compiled with entry hooks. 12598 // only gets called from experimental natives) is compiled with entry hooks.
12348 i::FLAG_harmony_typed_arrays = false; 12599 i::FLAG_harmony_typed_arrays = false;
12349 i::FLAG_harmony_array_buffer = false; 12600 i::FLAG_harmony_array_buffer = false;
12350 12601
12351 i::FLAG_allow_natives_syntax = true; 12602 i::FLAG_allow_natives_syntax = true;
12352 i::FLAG_use_inlining = false; 12603 i::FLAG_use_inlining = false;
12353 12604
12354 // Test setting and resetting the entry hook. 12605 SetFunctionEntryHookTest test;
12355 // Nulling it should always succeed. 12606 test.RunTest();
12356 CHECK(v8::V8::SetFunctionEntryHook(NULL));
12357
12358 CHECK(v8::V8::SetFunctionEntryHook(entry_hook));
12359 // Setting a hook while one's active should fail.
12360 CHECK_EQ(false, v8::V8::SetFunctionEntryHook(entry_hook));
12361
12362 CHECK(v8::V8::SetFunctionEntryHook(NULL));
12363
12364 // Reset the entry count to zero and set the entry hook.
12365 bar_entry_count = 0;
12366 bar_caller_count = 0;
12367 foo_entry_count = 0;
12368 CHECK(v8::V8::SetFunctionEntryHook(entry_hook));
12369 RunLoopInNewEnv();
12370
12371 CHECK_EQ(2, bar_entry_count);
12372 CHECK_EQ(200, bar_caller_count);
12373 CHECK_EQ(200, foo_entry_count);
12374
12375 // Clear the entry hook and count.
12376 bar_entry_count = 0;
12377 bar_caller_count = 0;
12378 foo_entry_count = 0;
12379 v8::V8::SetFunctionEntryHook(NULL);
12380
12381 // Clear the compilation cache to make sure we don't reuse the
12382 // functions from the previous invocation.
12383 v8::internal::Isolate::Current()->compilation_cache()->Clear();
12384
12385 // Verify that entry hooking is now disabled.
12386 RunLoopInNewEnv();
12387 CHECK_EQ(0u, bar_entry_count);
12388 CHECK_EQ(0u, bar_caller_count);
12389 CHECK_EQ(0u, foo_entry_count);
12390 } 12607 }
12391 12608
12392 12609
12393 static i::HashMap* code_map = NULL; 12610 static i::HashMap* code_map = NULL;
12394 static i::HashMap* jitcode_line_info = NULL; 12611 static i::HashMap* jitcode_line_info = NULL;
12395 static int saw_bar = 0; 12612 static int saw_bar = 0;
12396 static int move_events = 0; 12613 static int move_events = 0;
12397 12614
12398 12615
12399 static bool FunctionNameIs(const char* expected, 12616 static bool FunctionNameIs(const char* expected,
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
12521 break; 12738 break;
12522 12739
12523 default: 12740 default:
12524 // Impossible event. 12741 // Impossible event.
12525 CHECK(false); 12742 CHECK(false);
12526 break; 12743 break;
12527 } 12744 }
12528 } 12745 }
12529 12746
12530 12747
12531 static bool MatchPointers(void* key1, void* key2) {
12532 return key1 == key2;
12533 }
12534
12535
12536 TEST(SetJitCodeEventHandler) { 12748 TEST(SetJitCodeEventHandler) {
12537 i::FLAG_stress_compaction = true; 12749 i::FLAG_stress_compaction = true;
12538 i::FLAG_incremental_marking = false; 12750 i::FLAG_incremental_marking = false;
12539 const char* script = 12751 const char* script =
12540 "function bar() {" 12752 "function bar() {"
12541 " var sum = 0;" 12753 " var sum = 0;"
12542 " for (i = 0; i < 100; ++i)" 12754 " for (i = 0; i < 100; ++i)"
12543 " sum = foo(i);" 12755 " sum = foo(i);"
12544 " return sum;" 12756 " return sum;"
12545 "}" 12757 "}"
(...skipping 6802 matching lines...) Expand 10 before | Expand all | Expand 10 after
19348 i::Semaphore* sem_; 19560 i::Semaphore* sem_;
19349 volatile int sem_value_; 19561 volatile int sem_value_;
19350 }; 19562 };
19351 19563
19352 19564
19353 THREADED_TEST(SemaphoreInterruption) { 19565 THREADED_TEST(SemaphoreInterruption) {
19354 ThreadInterruptTest().RunTest(); 19566 ThreadInterruptTest().RunTest();
19355 } 19567 }
19356 19568
19357 #endif // WIN32 19569 #endif // WIN32
OLDNEW
« no previous file with comments | « src/x64/code-stubs-x64.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698