LCOV - code coverage report
Current view: top level - src - compilation-cache.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 144 146 98.6 %
Date: 2019-01-20 Functions: 27 28 96.4 %

          Line data    Source code
       1             : // Copyright 2011 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/compilation-cache.h"
       6             : 
       7             : #include "src/counters.h"
       8             : #include "src/globals.h"
       9             : #include "src/heap/factory.h"
      10             : #include "src/objects-inl.h"
      11             : #include "src/objects/compilation-cache-inl.h"
      12             : #include "src/objects/slots.h"
      13             : #include "src/visitors.h"
      14             : 
      15             : namespace v8 {
      16             : namespace internal {
      17             : 
      18             : // The number of generations for each sub cache.
      19             : static const int kRegExpGenerations = 2;
      20             : 
      21             : // Initial size of each compilation cache table allocated.
      22             : static const int kInitialCacheSize = 64;
      23             : 
      24       62883 : CompilationCache::CompilationCache(Isolate* isolate)
      25             :     : isolate_(isolate),
      26             :       script_(isolate),
      27             :       eval_global_(isolate),
      28             :       eval_contextual_(isolate),
      29             :       reg_exp_(isolate, kRegExpGenerations),
      30      125766 :       enabled_(true) {
      31             :   CompilationSubCache* subcaches[kSubCacheCount] =
      32       62883 :     {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
      33      314415 :   for (int i = 0; i < kSubCacheCount; ++i) {
      34      251532 :     subcaches_[i] = subcaches[i];
      35             :   }
      36       62883 : }
      37             : 
      38    12329194 : Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
      39             :   DCHECK(generation < generations_);
      40             :   Handle<CompilationCacheTable> result;
      41    12329194 :   if (tables_[generation]->IsUndefined(isolate())) {
      42       61722 :     result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
      43      123444 :     tables_[generation] = *result;
      44             :   } else {
      45             :     CompilationCacheTable table =
      46     6102875 :         CompilationCacheTable::cast(tables_[generation]);
      47             :     result = Handle<CompilationCacheTable>(table, isolate());
      48             :   }
      49     6164597 :   return result;
      50             : }
      51             : 
      52      888800 : void CompilationSubCache::Age() {
      53             :   // Don't directly age single-generation caches.
      54      444400 :   if (generations_ == 1) {
      55      666600 :     if (!tables_[0]->IsUndefined(isolate())) {
      56      152782 :       CompilationCacheTable::cast(tables_[0])->Age();
      57             :     }
      58      444400 :     return;
      59             :   }
      60             : 
      61             :   // Age the generations implicitly killing off the oldest.
      62      222200 :   for (int i = generations_ - 1; i > 0; i--) {
      63      111100 :     tables_[i] = tables_[i - 1];
      64             :   }
      65             : 
      66             :   // Set the first generation as unborn.
      67      222200 :   tables_[0] = ReadOnlyRoots(isolate()).undefined_value();
      68             : }
      69             : 
      70     1229000 : void CompilationSubCache::Iterate(RootVisitor* v) {
      71             :   v->VisitRootPointers(Root::kCompilationCache, nullptr,
      72             :                        FullObjectSlot(&tables_[0]),
      73     2458000 :                        FullObjectSlot(&tables_[generations_]));
      74     1229000 : }
      75             : 
      76       44832 : void CompilationSubCache::Clear() {
      77             :   MemsetPointer(reinterpret_cast<Address*>(tables_),
      78             :                 ReadOnlyRoots(isolate()).undefined_value()->ptr(),
      79       44832 :                 generations_);
      80       22416 : }
      81             : 
      82        2412 : void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
      83             :   // Probe the script generation tables. Make sure not to leak handles
      84             :   // into the caller's handle scope.
      85             :   { HandleScope scope(isolate());
      86        3216 :     for (int generation = 0; generation < generations(); generation++) {
      87         804 :       Handle<CompilationCacheTable> table = GetTable(generation);
      88        1608 :       table->Remove(*function_info);
      89             :     }
      90             :   }
      91         804 : }
      92             : 
      93           0 : CompilationCacheScript::CompilationCacheScript(Isolate* isolate)
      94           0 :     : CompilationSubCache(isolate, 1) {}
      95             : 
      96             : // We only re-use a cached function for some script source code if the
      97             : // script originates from the same place. This is to avoid issues
      98             : // when reporting errors, etc.
      99      130674 : bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
     100             :                                        MaybeHandle<Object> maybe_name,
     101             :                                        int line_offset, int column_offset,
     102             :                                        ScriptOriginOptions resource_options) {
     103             :   Handle<Script> script =
     104      585728 :       Handle<Script>(Script::cast(function_info->script()), isolate());
     105             :   // If the script name isn't set, the boilerplate script should have
     106             :   // an undefined name to have the same origin.
     107             :   Handle<Object> name;
     108      130674 :   if (!maybe_name.ToHandle(&name)) {
     109      135216 :     return script->name()->IsUndefined(isolate());
     110             :   }
     111             :   // Do the fast bailout checks first.
     112       63066 :   if (line_offset != script->line_offset()) return false;
     113       63060 :   if (column_offset != script->column_offset()) return false;
     114             :   // Check that both names are strings. If not, no match.
     115      252240 :   if (!name->IsString() || !script->name()->IsString()) return false;
     116             :   // Are the origin_options same?
     117      126118 :   if (resource_options.Flags() != script->origin_options().Flags())
     118             :     return false;
     119             :   // Compare the two name strings for equality.
     120             :   return String::Equals(
     121             :       isolate(), Handle<String>::cast(name),
     122      189147 :       Handle<String>(String::cast(script->name()), isolate()));
     123             : }
     124             : 
     125             : // TODO(245): Need to allow identical code from different contexts to
     126             : // be cached in the same script generation. Currently the first use
     127             : // will be cached, but subsequent code from different source / line
     128             : // won't.
     129      271788 : MaybeHandle<SharedFunctionInfo> CompilationCacheScript::Lookup(
     130             :     Handle<String> source, MaybeHandle<Object> name, int line_offset,
     131             :     int column_offset, ScriptOriginOptions resource_options,
     132             :     Handle<Context> native_context, LanguageMode language_mode) {
     133             :   MaybeHandle<SharedFunctionInfo> result;
     134             : 
     135             :   // Probe the script generation tables. Make sure not to leak handles
     136             :   // into the caller's handle scope.
     137      673765 :   { HandleScope scope(isolate());
     138             :     const int generation = 0;
     139             :     DCHECK_EQ(generations(), 1);
     140      271788 :     Handle<CompilationCacheTable> table = GetTable(generation);
     141             :     MaybeHandle<SharedFunctionInfo> probe = CompilationCacheTable::LookupScript(
     142      271788 :         table, source, native_context, language_mode);
     143             :     Handle<SharedFunctionInfo> function_info;
     144      271788 :     if (probe.ToHandle(&function_info)) {
     145             :       // Break when we've found a suitable shared function info that
     146             :       // matches the origin.
     147      130674 :       if (HasOrigin(function_info, name, line_offset, column_offset,
     148             :                     resource_options)) {
     149      130189 :         result = scope.CloseAndEscape(function_info);
     150             :       }
     151             :     }
     152             :   }
     153             : 
     154             :   // Once outside the manacles of the handle scope, we need to recheck
     155             :   // to see if we actually found a cached script. If so, we return a
     156             :   // handle created in the caller's handle scope.
     157             :   Handle<SharedFunctionInfo> function_info;
     158      271788 :   if (result.ToHandle(&function_info)) {
     159             : #ifdef DEBUG
     160             :     // Since HasOrigin can allocate, we need to protect the SharedFunctionInfo
     161             :     // with handles during the call.
     162             :     DCHECK(HasOrigin(function_info, name, line_offset, column_offset,
     163             :                      resource_options));
     164             : #endif
     165      130189 :     isolate()->counters()->compilation_cache_hits()->Increment();
     166      130189 :     LOG(isolate(), CompilationCacheEvent("hit", "script", *function_info));
     167             :   } else {
     168      141599 :     isolate()->counters()->compilation_cache_misses()->Increment();
     169             :   }
     170      271788 :   return result;
     171             : }
     172             : 
     173      140120 : void CompilationCacheScript::Put(Handle<String> source,
     174             :                                  Handle<Context> native_context,
     175             :                                  LanguageMode language_mode,
     176             :                                  Handle<SharedFunctionInfo> function_info) {
     177      280240 :   HandleScope scope(isolate());
     178      280240 :   Handle<CompilationCacheTable> table = GetFirstTable();
     179             :   SetFirstTable(CompilationCacheTable::PutScript(table, source, native_context,
     180      140120 :                                                  language_mode, function_info));
     181      140120 : }
     182             : 
     183     3521393 : InfoCellPair CompilationCacheEval::Lookup(Handle<String> source,
     184             :                                           Handle<SharedFunctionInfo> outer_info,
     185             :                                           Handle<Context> native_context,
     186             :                                           LanguageMode language_mode,
     187             :                                           int position) {
     188     7042786 :   HandleScope scope(isolate());
     189             :   // Make sure not to leak the table into the surrounding handle
     190             :   // scope. Otherwise, we risk keeping old tables around even after
     191             :   // having cleared the cache.
     192             :   InfoCellPair result;
     193             :   const int generation = 0;
     194             :   DCHECK_EQ(generations(), 1);
     195     3521393 :   Handle<CompilationCacheTable> table = GetTable(generation);
     196             :   result = CompilationCacheTable::LookupEval(
     197     3521393 :       table, source, outer_info, native_context, language_mode, position);
     198     3521393 :   if (result.has_shared()) {
     199     2596970 :     isolate()->counters()->compilation_cache_hits()->Increment();
     200             :   } else {
     201      924423 :     isolate()->counters()->compilation_cache_misses()->Increment();
     202             :   }
     203     3521393 :   return result;
     204             : }
     205             : 
     206     1187312 : void CompilationCacheEval::Put(Handle<String> source,
     207             :                                Handle<SharedFunctionInfo> outer_info,
     208             :                                Handle<SharedFunctionInfo> function_info,
     209             :                                Handle<Context> native_context,
     210             :                                Handle<FeedbackCell> feedback_cell,
     211             :                                int position) {
     212     2374624 :   HandleScope scope(isolate());
     213     1187312 :   Handle<CompilationCacheTable> table = GetFirstTable();
     214             :   table =
     215             :       CompilationCacheTable::PutEval(table, source, outer_info, function_info,
     216     1187312 :                                      native_context, feedback_cell, position);
     217             :   SetFirstTable(table);
     218     1187312 : }
     219             : 
     220      460207 : MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
     221             :     Handle<String> source,
     222             :     JSRegExp::Flags flags) {
     223     2426090 :   HandleScope scope(isolate());
     224             :   // Make sure not to leak the table into the surrounding handle
     225             :   // scope. Otherwise, we risk keeping old tables around even after
     226             :   // having cleared the cache.
     227             :   Handle<Object> result = isolate()->factory()->undefined_value();
     228             :   int generation;
     229     2090938 :   for (generation = 0; generation < generations(); generation++) {
     230      753104 :     Handle<CompilationCacheTable> table = GetTable(generation);
     231      753104 :     result = table->LookupRegExp(source, flags);
     232     1506208 :     if (result->IsFixedArray()) break;
     233             :   }
     234      920414 :   if (result->IsFixedArray()) {
     235      167842 :     Handle<FixedArray> data = Handle<FixedArray>::cast(result);
     236      167842 :     if (generation != 0) {
     237         532 :       Put(source, flags, data);
     238             :     }
     239      167842 :     isolate()->counters()->compilation_cache_hits()->Increment();
     240      167842 :     return scope.CloseAndEscape(data);
     241             :   } else {
     242      292365 :     isolate()->counters()->compilation_cache_misses()->Increment();
     243      292365 :     return MaybeHandle<FixedArray>();
     244             :   }
     245             : }
     246             : 
     247      290076 : void CompilationCacheRegExp::Put(Handle<String> source,
     248             :                                  JSRegExp::Flags flags,
     249             :                                  Handle<FixedArray> data) {
     250      870228 :   HandleScope scope(isolate());
     251      580152 :   Handle<CompilationCacheTable> table = GetFirstTable();
     252             :   SetFirstTable(
     253      290076 :       CompilationCacheTable::PutRegExp(isolate(), table, source, flags, data));
     254      290076 : }
     255             : 
     256        1819 : void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
     257        3638 :   if (!IsEnabled()) return;
     258             : 
     259         268 :   eval_global_.Remove(function_info);
     260         268 :   eval_contextual_.Remove(function_info);
     261         268 :   script_.Remove(function_info);
     262             : }
     263             : 
     264      284909 : MaybeHandle<SharedFunctionInfo> CompilationCache::LookupScript(
     265             :     Handle<String> source, MaybeHandle<Object> name, int line_offset,
     266             :     int column_offset, ScriptOriginOptions resource_options,
     267             :     Handle<Context> native_context, LanguageMode language_mode) {
     268      284909 :   if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
     269             : 
     270             :   return script_.Lookup(source, name, line_offset, column_offset,
     271      271788 :                         resource_options, native_context, language_mode);
     272             : }
     273             : 
     274     3550725 : InfoCellPair CompilationCache::LookupEval(Handle<String> source,
     275             :                                           Handle<SharedFunctionInfo> outer_info,
     276             :                                           Handle<Context> context,
     277             :                                           LanguageMode language_mode,
     278     4847045 :                                           int position) {
     279             :   InfoCellPair result;
     280     3550725 :   if (!IsEnabled()) return result;
     281             : 
     282             :   const char* cache_type;
     283             : 
     284     7042786 :   if (context->IsNativeContext()) {
     285             :     result = eval_global_.Lookup(source, outer_info, context, language_mode,
     286     1271318 :                                  position);
     287             :     cache_type = "eval-global";
     288             : 
     289             :   } else {
     290             :     DCHECK_NE(position, kNoSourcePosition);
     291     4500150 :     Handle<Context> native_context(context->native_context(), isolate());
     292             :     result = eval_contextual_.Lookup(source, outer_info, native_context,
     293     2250075 :                                      language_mode, position);
     294             :     cache_type = "eval-contextual";
     295             :   }
     296             : 
     297     3521393 :   if (result.has_shared()) {
     298     2596970 :     LOG(isolate(), CompilationCacheEvent("hit", cache_type, result.shared()));
     299             :   }
     300             : 
     301             :   return result;
     302             : }
     303             : 
     304      461483 : MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
     305             :                                                        JSRegExp::Flags flags) {
     306      461483 :   if (!IsEnabled()) return MaybeHandle<FixedArray>();
     307             : 
     308      460207 :   return reg_exp_.Lookup(source, flags);
     309             : }
     310             : 
     311      153141 : void CompilationCache::PutScript(Handle<String> source,
     312             :                                  Handle<Context> native_context,
     313             :                                  LanguageMode language_mode,
     314      140120 :                                  Handle<SharedFunctionInfo> function_info) {
     315      306282 :   if (!IsEnabled()) return;
     316      140174 :   LOG(isolate(), CompilationCacheEvent("put", "script", *function_info));
     317             : 
     318      140120 :   script_.Put(source, native_context, language_mode, function_info);
     319             : }
     320             : 
     321     1216523 : void CompilationCache::PutEval(Handle<String> source,
     322             :                                Handle<SharedFunctionInfo> outer_info,
     323             :                                Handle<Context> context,
     324             :                                Handle<SharedFunctionInfo> function_info,
     325             :                                Handle<FeedbackCell> feedback_cell,
     326     3025708 :                                int position) {
     327     2433046 :   if (!IsEnabled()) return;
     328             : 
     329             :   const char* cache_type;
     330             :   HandleScope scope(isolate());
     331     2374624 :   if (context->IsNativeContext()) {
     332             :     eval_global_.Put(source, outer_info, function_info, context, feedback_cell,
     333      536228 :                      position);
     334             :     cache_type = "eval-global";
     335             :   } else {
     336             :     DCHECK_NE(position, kNoSourcePosition);
     337     1302168 :     Handle<Context> native_context(context->native_context(), isolate());
     338             :     eval_contextual_.Put(source, outer_info, function_info, native_context,
     339      651084 :                          feedback_cell, position);
     340             :     cache_type = "eval-contextual";
     341             :   }
     342     1187312 :   LOG(isolate(), CompilationCacheEvent("put", cache_type, *function_info));
     343             : }
     344             : 
     345      290815 : void CompilationCache::PutRegExp(Handle<String> source,
     346             :                                  JSRegExp::Flags flags,
     347             :                                  Handle<FixedArray> data) {
     348      581630 :   if (!IsEnabled()) return;
     349             : 
     350      289544 :   reg_exp_.Put(source, flags, data);
     351             : }
     352             : 
     353        1578 : void CompilationCache::Clear() {
     354       23994 :   for (int i = 0; i < kSubCacheCount; i++) {
     355       22416 :     subcaches_[i]->Clear();
     356             :   }
     357        1578 : }
     358             : 
     359      307250 : void CompilationCache::Iterate(RootVisitor* v) {
     360     1536250 :   for (int i = 0; i < kSubCacheCount; i++) {
     361     1229000 :     subcaches_[i]->Iterate(v);
     362             :   }
     363      307250 : }
     364             : 
     365      111100 : void CompilationCache::MarkCompactPrologue() {
     366      555500 :   for (int i = 0; i < kSubCacheCount; i++) {
     367      444400 :     subcaches_[i]->Age();
     368             :   }
     369      111100 : }
     370             : 
     371        3906 : void CompilationCache::Enable() {
     372        3906 :   enabled_ = true;
     373        3906 : }
     374             : 
     375        4026 : void CompilationCache::Disable() {
     376        4026 :   enabled_ = false;
     377             :   Clear();
     378        4026 : }
     379             : 
     380             : }  // namespace internal
     381      183867 : }  // namespace v8

Generated by: LCOV version 1.10