Home | History | Annotate | Download | only in src
      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/factory.h"
      9 #include "src/globals.h"
     10 #include "src/objects-inl.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 
     16 // The number of generations for each sub cache.
     17 static const int kRegExpGenerations = 2;
     18 
     19 // Initial size of each compilation cache table allocated.
     20 static const int kInitialCacheSize = 64;
     21 
     22 CompilationCache::CompilationCache(Isolate* isolate)
     23     : isolate_(isolate),
     24       script_(isolate),
     25       eval_global_(isolate),
     26       eval_contextual_(isolate),
     27       reg_exp_(isolate, kRegExpGenerations),
     28       enabled_(true) {
     29   CompilationSubCache* subcaches[kSubCacheCount] =
     30     {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
     31   for (int i = 0; i < kSubCacheCount; ++i) {
     32     subcaches_[i] = subcaches[i];
     33   }
     34 }
     35 
     36 
     37 CompilationCache::~CompilationCache() {}
     38 
     39 
     40 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
     41   DCHECK(generation < generations_);
     42   Handle<CompilationCacheTable> result;
     43   if (tables_[generation]->IsUndefined(isolate())) {
     44     result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
     45     tables_[generation] = *result;
     46   } else {
     47     CompilationCacheTable* table =
     48         CompilationCacheTable::cast(tables_[generation]);
     49     result = Handle<CompilationCacheTable>(table, isolate());
     50   }
     51   return result;
     52 }
     53 
     54 
     55 void CompilationSubCache::Age() {
     56   // Don't directly age single-generation caches.
     57   if (generations_ == 1) {
     58     if (!tables_[0]->IsUndefined(isolate())) {
     59       CompilationCacheTable::cast(tables_[0])->Age();
     60     }
     61     return;
     62   }
     63 
     64   // Age the generations implicitly killing off the oldest.
     65   for (int i = generations_ - 1; i > 0; i--) {
     66     tables_[i] = tables_[i - 1];
     67   }
     68 
     69   // Set the first generation as unborn.
     70   tables_[0] = isolate()->heap()->undefined_value();
     71 }
     72 
     73 
     74 void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
     75   Object* undefined = isolate()->heap()->undefined_value();
     76   for (int i = 0; i < generations_; i++) {
     77     if (tables_[i] != undefined) {
     78       reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
     79     }
     80   }
     81 }
     82 
     83 
     84 void CompilationSubCache::Iterate(ObjectVisitor* v) {
     85   v->VisitPointers(&tables_[0], &tables_[generations_]);
     86 }
     87 
     88 
     89 void CompilationSubCache::Clear() {
     90   MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
     91 }
     92 
     93 
     94 void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
     95   // Probe the script generation tables. Make sure not to leak handles
     96   // into the caller's handle scope.
     97   { HandleScope scope(isolate());
     98     for (int generation = 0; generation < generations(); generation++) {
     99       Handle<CompilationCacheTable> table = GetTable(generation);
    100       table->Remove(*function_info);
    101     }
    102   }
    103 }
    104 
    105 CompilationCacheScript::CompilationCacheScript(Isolate* isolate)
    106     : CompilationSubCache(isolate, 1) {}
    107 
    108 // We only re-use a cached function for some script source code if the
    109 // script originates from the same place. This is to avoid issues
    110 // when reporting errors, etc.
    111 bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
    112                                        Handle<Object> name, int line_offset,
    113                                        int column_offset,
    114                                        ScriptOriginOptions resource_options) {
    115   Handle<Script> script =
    116       Handle<Script>(Script::cast(function_info->script()), isolate());
    117   // If the script name isn't set, the boilerplate script should have
    118   // an undefined name to have the same origin.
    119   if (name.is_null()) {
    120     return script->name()->IsUndefined(isolate());
    121   }
    122   // Do the fast bailout checks first.
    123   if (line_offset != script->line_offset()) return false;
    124   if (column_offset != script->column_offset()) return false;
    125   // Check that both names are strings. If not, no match.
    126   if (!name->IsString() || !script->name()->IsString()) return false;
    127   // Are the origin_options same?
    128   if (resource_options.Flags() != script->origin_options().Flags())
    129     return false;
    130   // Compare the two name strings for equality.
    131   return String::Equals(Handle<String>::cast(name),
    132                         Handle<String>(String::cast(script->name())));
    133 }
    134 
    135 
    136 // TODO(245): Need to allow identical code from different contexts to
    137 // be cached in the same script generation. Currently the first use
    138 // will be cached, but subsequent code from different source / line
    139 // won't.
    140 InfoVectorPair CompilationCacheScript::Lookup(
    141     Handle<String> source, Handle<Object> name, int line_offset,
    142     int column_offset, ScriptOriginOptions resource_options,
    143     Handle<Context> context, LanguageMode language_mode) {
    144   InfoVectorPair result;
    145 
    146   // Probe the script generation tables. Make sure not to leak handles
    147   // into the caller's handle scope.
    148   { HandleScope scope(isolate());
    149     const int generation = 0;
    150     DCHECK(generations() == 1);
    151     Handle<CompilationCacheTable> table = GetTable(generation);
    152     InfoVectorPair probe = table->LookupScript(source, context, language_mode);
    153     if (probe.has_shared()) {
    154       Handle<SharedFunctionInfo> function_info(probe.shared(), isolate());
    155       Handle<Cell> vector_handle;
    156       if (probe.has_vector()) {
    157         vector_handle = Handle<Cell>(probe.vector(), isolate());
    158       }
    159       // Break when we've found a suitable shared function info that
    160       // matches the origin.
    161       if (HasOrigin(function_info, name, line_offset, column_offset,
    162                     resource_options)) {
    163         result = InfoVectorPair(*function_info,
    164                                 probe.has_vector() ? *vector_handle : nullptr);
    165       }
    166     }
    167   }
    168 
    169   // Once outside the manacles of the handle scope, we need to recheck
    170   // to see if we actually found a cached script. If so, we return a
    171   // handle created in the caller's handle scope.
    172   if (result.has_shared()) {
    173     Handle<SharedFunctionInfo> shared(result.shared(), isolate());
    174     // TODO(mvstanton): Make sure HasOrigin can't allocate, or it will
    175     // mess up our InfoVectorPair.
    176     DCHECK(
    177         HasOrigin(shared, name, line_offset, column_offset, resource_options));
    178     isolate()->counters()->compilation_cache_hits()->Increment();
    179   } else {
    180     isolate()->counters()->compilation_cache_misses()->Increment();
    181   }
    182   return result;
    183 }
    184 
    185 void CompilationCacheScript::Put(Handle<String> source, Handle<Context> context,
    186                                  LanguageMode language_mode,
    187                                  Handle<SharedFunctionInfo> function_info,
    188                                  Handle<Cell> literals) {
    189   HandleScope scope(isolate());
    190   Handle<CompilationCacheTable> table = GetFirstTable();
    191   SetFirstTable(CompilationCacheTable::PutScript(
    192       table, source, context, language_mode, function_info, literals));
    193 }
    194 
    195 InfoVectorPair CompilationCacheEval::Lookup(
    196     Handle<String> source, Handle<SharedFunctionInfo> outer_info,
    197     Handle<Context> native_context, LanguageMode language_mode, int position) {
    198   HandleScope scope(isolate());
    199   // Make sure not to leak the table into the surrounding handle
    200   // scope. Otherwise, we risk keeping old tables around even after
    201   // having cleared the cache.
    202   InfoVectorPair result;
    203   const int generation = 0;
    204   DCHECK(generations() == 1);
    205   Handle<CompilationCacheTable> table = GetTable(generation);
    206   result = table->LookupEval(source, outer_info, native_context, language_mode,
    207                              position);
    208   if (result.has_shared()) {
    209     isolate()->counters()->compilation_cache_hits()->Increment();
    210   } else {
    211     isolate()->counters()->compilation_cache_misses()->Increment();
    212   }
    213   return result;
    214 }
    215 
    216 void CompilationCacheEval::Put(Handle<String> source,
    217                                Handle<SharedFunctionInfo> outer_info,
    218                                Handle<SharedFunctionInfo> function_info,
    219                                Handle<Context> native_context,
    220                                Handle<Cell> literals, int position) {
    221   HandleScope scope(isolate());
    222   Handle<CompilationCacheTable> table = GetFirstTable();
    223   table =
    224       CompilationCacheTable::PutEval(table, source, outer_info, function_info,
    225                                      native_context, literals, position);
    226   SetFirstTable(table);
    227 }
    228 
    229 
    230 MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
    231     Handle<String> source,
    232     JSRegExp::Flags flags) {
    233   HandleScope scope(isolate());
    234   // Make sure not to leak the table into the surrounding handle
    235   // scope. Otherwise, we risk keeping old tables around even after
    236   // having cleared the cache.
    237   Handle<Object> result = isolate()->factory()->undefined_value();
    238   int generation;
    239   for (generation = 0; generation < generations(); generation++) {
    240     Handle<CompilationCacheTable> table = GetTable(generation);
    241     result = table->LookupRegExp(source, flags);
    242     if (result->IsFixedArray()) break;
    243   }
    244   if (result->IsFixedArray()) {
    245     Handle<FixedArray> data = Handle<FixedArray>::cast(result);
    246     if (generation != 0) {
    247       Put(source, flags, data);
    248     }
    249     isolate()->counters()->compilation_cache_hits()->Increment();
    250     return scope.CloseAndEscape(data);
    251   } else {
    252     isolate()->counters()->compilation_cache_misses()->Increment();
    253     return MaybeHandle<FixedArray>();
    254   }
    255 }
    256 
    257 
    258 void CompilationCacheRegExp::Put(Handle<String> source,
    259                                  JSRegExp::Flags flags,
    260                                  Handle<FixedArray> data) {
    261   HandleScope scope(isolate());
    262   Handle<CompilationCacheTable> table = GetFirstTable();
    263   SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
    264 }
    265 
    266 
    267 void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
    268   if (!IsEnabled()) return;
    269 
    270   eval_global_.Remove(function_info);
    271   eval_contextual_.Remove(function_info);
    272   script_.Remove(function_info);
    273 }
    274 
    275 InfoVectorPair CompilationCache::LookupScript(
    276     Handle<String> source, Handle<Object> name, int line_offset,
    277     int column_offset, ScriptOriginOptions resource_options,
    278     Handle<Context> context, LanguageMode language_mode) {
    279   InfoVectorPair empty_result;
    280   if (!IsEnabled()) return empty_result;
    281 
    282   return script_.Lookup(source, name, line_offset, column_offset,
    283                         resource_options, context, language_mode);
    284 }
    285 
    286 InfoVectorPair CompilationCache::LookupEval(
    287     Handle<String> source, Handle<SharedFunctionInfo> outer_info,
    288     Handle<Context> context, LanguageMode language_mode, int position) {
    289   InfoVectorPair result;
    290   if (!IsEnabled()) return result;
    291 
    292   if (context->IsNativeContext()) {
    293     result = eval_global_.Lookup(source, outer_info, context, language_mode,
    294                                  position);
    295   } else {
    296     DCHECK(position != kNoSourcePosition);
    297     Handle<Context> native_context(context->native_context(), isolate());
    298     result = eval_contextual_.Lookup(source, outer_info, native_context,
    299                                      language_mode, position);
    300   }
    301 
    302   return result;
    303 }
    304 
    305 
    306 MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
    307                                                        JSRegExp::Flags flags) {
    308   if (!IsEnabled()) return MaybeHandle<FixedArray>();
    309 
    310   return reg_exp_.Lookup(source, flags);
    311 }
    312 
    313 void CompilationCache::PutScript(Handle<String> source, Handle<Context> context,
    314                                  LanguageMode language_mode,
    315                                  Handle<SharedFunctionInfo> function_info,
    316                                  Handle<Cell> literals) {
    317   if (!IsEnabled()) return;
    318 
    319   script_.Put(source, context, language_mode, function_info, literals);
    320 }
    321 
    322 void CompilationCache::PutEval(Handle<String> source,
    323                                Handle<SharedFunctionInfo> outer_info,
    324                                Handle<Context> context,
    325                                Handle<SharedFunctionInfo> function_info,
    326                                Handle<Cell> literals, int position) {
    327   if (!IsEnabled()) return;
    328 
    329   HandleScope scope(isolate());
    330   if (context->IsNativeContext()) {
    331     eval_global_.Put(source, outer_info, function_info, context, literals,
    332                      position);
    333   } else {
    334     DCHECK(position != kNoSourcePosition);
    335     Handle<Context> native_context(context->native_context(), isolate());
    336     eval_contextual_.Put(source, outer_info, function_info, native_context,
    337                          literals, position);
    338   }
    339 }
    340 
    341 
    342 
    343 void CompilationCache::PutRegExp(Handle<String> source,
    344                                  JSRegExp::Flags flags,
    345                                  Handle<FixedArray> data) {
    346   if (!IsEnabled()) {
    347     return;
    348   }
    349 
    350   reg_exp_.Put(source, flags, data);
    351 }
    352 
    353 
    354 void CompilationCache::Clear() {
    355   for (int i = 0; i < kSubCacheCount; i++) {
    356     subcaches_[i]->Clear();
    357   }
    358 }
    359 
    360 
    361 void CompilationCache::Iterate(ObjectVisitor* v) {
    362   for (int i = 0; i < kSubCacheCount; i++) {
    363     subcaches_[i]->Iterate(v);
    364   }
    365 }
    366 
    367 
    368 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
    369   for (int i = 0; i < kSubCacheCount; i++) {
    370     subcaches_[i]->IterateFunctions(v);
    371   }
    372 }
    373 
    374 
    375 void CompilationCache::MarkCompactPrologue() {
    376   for (int i = 0; i < kSubCacheCount; i++) {
    377     subcaches_[i]->Age();
    378   }
    379 }
    380 
    381 
    382 void CompilationCache::Enable() {
    383   enabled_ = true;
    384 }
    385 
    386 
    387 void CompilationCache::Disable() {
    388   enabled_ = false;
    389   Clear();
    390 }
    391 
    392 
    393 }  // namespace internal
    394 }  // namespace v8
    395