Home | History | Annotate | Download | only in src
      1 // Copyright 2011 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     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.
     27 
     28 #include "v8.h"
     29 
     30 #include "assembler.h"
     31 #include "compilation-cache.h"
     32 #include "serialize.h"
     33 
     34 namespace v8 {
     35 namespace internal {
     36 
     37 
     38 // The number of generations for each sub cache.
     39 // The number of ScriptGenerations is carefully chosen based on histograms.
     40 // See issue 458: http://code.google.com/p/v8/issues/detail?id=458
     41 static const int kScriptGenerations = 5;
     42 static const int kEvalGlobalGenerations = 2;
     43 static const int kEvalContextualGenerations = 2;
     44 static const int kRegExpGenerations = 2;
     45 
     46 // Initial size of each compilation cache table allocated.
     47 static const int kInitialCacheSize = 64;
     48 
     49 
     50 CompilationCache::CompilationCache(Isolate* isolate)
     51     : isolate_(isolate),
     52       script_(isolate, kScriptGenerations),
     53       eval_global_(isolate, kEvalGlobalGenerations),
     54       eval_contextual_(isolate, kEvalContextualGenerations),
     55       reg_exp_(isolate, kRegExpGenerations),
     56       enabled_(true) {
     57   CompilationSubCache* subcaches[kSubCacheCount] =
     58     {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
     59   for (int i = 0; i < kSubCacheCount; ++i) {
     60     subcaches_[i] = subcaches[i];
     61   }
     62 }
     63 
     64 
     65 CompilationCache::~CompilationCache() {}
     66 
     67 
     68 static Handle<CompilationCacheTable> AllocateTable(Isolate* isolate, int size) {
     69   CALL_HEAP_FUNCTION(isolate,
     70                      CompilationCacheTable::Allocate(isolate->heap(), size),
     71                      CompilationCacheTable);
     72 }
     73 
     74 
     75 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
     76   ASSERT(generation < generations_);
     77   Handle<CompilationCacheTable> result;
     78   if (tables_[generation]->IsUndefined()) {
     79     result = AllocateTable(isolate(), kInitialCacheSize);
     80     tables_[generation] = *result;
     81   } else {
     82     CompilationCacheTable* table =
     83         CompilationCacheTable::cast(tables_[generation]);
     84     result = Handle<CompilationCacheTable>(table, isolate());
     85   }
     86   return result;
     87 }
     88 
     89 
     90 void CompilationSubCache::Age() {
     91   // Age the generations implicitly killing off the oldest.
     92   for (int i = generations_ - 1; i > 0; i--) {
     93     tables_[i] = tables_[i - 1];
     94   }
     95 
     96   // Set the first generation as unborn.
     97   tables_[0] = isolate()->heap()->undefined_value();
     98 }
     99 
    100 
    101 void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
    102   Object* undefined = isolate()->heap()->undefined_value();
    103   for (int i = 0; i < generations_; i++) {
    104     if (tables_[i] != undefined) {
    105       reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
    106     }
    107   }
    108 }
    109 
    110 
    111 void CompilationSubCache::Iterate(ObjectVisitor* v) {
    112   v->VisitPointers(&tables_[0], &tables_[generations_]);
    113 }
    114 
    115 
    116 void CompilationSubCache::Clear() {
    117   MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
    118 }
    119 
    120 
    121 void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
    122   // Probe the script generation tables. Make sure not to leak handles
    123   // into the caller's handle scope.
    124   { HandleScope scope(isolate());
    125     for (int generation = 0; generation < generations(); generation++) {
    126       Handle<CompilationCacheTable> table = GetTable(generation);
    127       table->Remove(*function_info);
    128     }
    129   }
    130 }
    131 
    132 
    133 CompilationCacheScript::CompilationCacheScript(Isolate* isolate,
    134                                                int generations)
    135     : CompilationSubCache(isolate, generations),
    136       script_histogram_(NULL),
    137       script_histogram_initialized_(false) { }
    138 
    139 
    140 // We only re-use a cached function for some script source code if the
    141 // script originates from the same place. This is to avoid issues
    142 // when reporting errors, etc.
    143 bool CompilationCacheScript::HasOrigin(
    144     Handle<SharedFunctionInfo> function_info,
    145     Handle<Object> name,
    146     int line_offset,
    147     int column_offset,
    148     bool is_shared_cross_origin) {
    149   Handle<Script> script =
    150       Handle<Script>(Script::cast(function_info->script()), isolate());
    151   // If the script name isn't set, the boilerplate script should have
    152   // an undefined name to have the same origin.
    153   if (name.is_null()) {
    154     return script->name()->IsUndefined();
    155   }
    156   // Do the fast bailout checks first.
    157   if (line_offset != script->line_offset()->value()) return false;
    158   if (column_offset != script->column_offset()->value()) return false;
    159   // Check that both names are strings. If not, no match.
    160   if (!name->IsString() || !script->name()->IsString()) return false;
    161   // Were both scripts tagged by the embedder as being shared cross-origin?
    162   if (is_shared_cross_origin != script->is_shared_cross_origin()) return false;
    163   // Compare the two name strings for equality.
    164   return String::cast(*name)->Equals(String::cast(script->name()));
    165 }
    166 
    167 
    168 // TODO(245): Need to allow identical code from different contexts to
    169 // be cached in the same script generation. Currently the first use
    170 // will be cached, but subsequent code from different source / line
    171 // won't.
    172 Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
    173     Handle<String> source,
    174     Handle<Object> name,
    175     int line_offset,
    176     int column_offset,
    177     bool is_shared_cross_origin,
    178     Handle<Context> context) {
    179   Object* result = NULL;
    180   int generation;
    181 
    182   // Probe the script generation tables. Make sure not to leak handles
    183   // into the caller's handle scope.
    184   { HandleScope scope(isolate());
    185     for (generation = 0; generation < generations(); generation++) {
    186       Handle<CompilationCacheTable> table = GetTable(generation);
    187       Handle<Object> probe(table->Lookup(*source, *context), isolate());
    188       if (probe->IsSharedFunctionInfo()) {
    189         Handle<SharedFunctionInfo> function_info =
    190             Handle<SharedFunctionInfo>::cast(probe);
    191         // Break when we've found a suitable shared function info that
    192         // matches the origin.
    193         if (HasOrigin(function_info,
    194                       name,
    195                       line_offset,
    196                       column_offset,
    197                       is_shared_cross_origin)) {
    198           result = *function_info;
    199           break;
    200         }
    201       }
    202     }
    203   }
    204 
    205   if (!script_histogram_initialized_) {
    206     script_histogram_ = isolate()->stats_table()->CreateHistogram(
    207         "V8.ScriptCache",
    208         0,
    209         kScriptGenerations,
    210         kScriptGenerations + 1);
    211     script_histogram_initialized_ = true;
    212   }
    213 
    214   if (script_histogram_ != NULL) {
    215     // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss.
    216     isolate()->stats_table()->AddHistogramSample(script_histogram_, generation);
    217   }
    218 
    219   // Once outside the manacles of the handle scope, we need to recheck
    220   // to see if we actually found a cached script. If so, we return a
    221   // handle created in the caller's handle scope.
    222   if (result != NULL) {
    223     Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
    224                                       isolate());
    225     ASSERT(HasOrigin(shared,
    226                      name,
    227                      line_offset,
    228                      column_offset,
    229                      is_shared_cross_origin));
    230     // If the script was found in a later generation, we promote it to
    231     // the first generation to let it survive longer in the cache.
    232     if (generation != 0) Put(source, context, shared);
    233     isolate()->counters()->compilation_cache_hits()->Increment();
    234     return shared;
    235   } else {
    236     isolate()->counters()->compilation_cache_misses()->Increment();
    237     return Handle<SharedFunctionInfo>::null();
    238   }
    239 }
    240 
    241 
    242 MaybeObject* CompilationCacheScript::TryTablePut(
    243     Handle<String> source,
    244     Handle<Context> context,
    245     Handle<SharedFunctionInfo> function_info) {
    246   Handle<CompilationCacheTable> table = GetFirstTable();
    247   return table->Put(*source, *context, *function_info);
    248 }
    249 
    250 
    251 Handle<CompilationCacheTable> CompilationCacheScript::TablePut(
    252     Handle<String> source,
    253     Handle<Context> context,
    254     Handle<SharedFunctionInfo> function_info) {
    255   CALL_HEAP_FUNCTION(isolate(),
    256                      TryTablePut(source, context, function_info),
    257                      CompilationCacheTable);
    258 }
    259 
    260 
    261 void CompilationCacheScript::Put(Handle<String> source,
    262                                  Handle<Context> context,
    263                                  Handle<SharedFunctionInfo> function_info) {
    264   HandleScope scope(isolate());
    265   SetFirstTable(TablePut(source, context, function_info));
    266 }
    267 
    268 
    269 Handle<SharedFunctionInfo> CompilationCacheEval::Lookup(
    270     Handle<String> source,
    271     Handle<Context> context,
    272     LanguageMode language_mode,
    273     int scope_position) {
    274   // Make sure not to leak the table into the surrounding handle
    275   // scope. Otherwise, we risk keeping old tables around even after
    276   // having cleared the cache.
    277   Object* result = NULL;
    278   int generation;
    279   { HandleScope scope(isolate());
    280     for (generation = 0; generation < generations(); generation++) {
    281       Handle<CompilationCacheTable> table = GetTable(generation);
    282       result = table->LookupEval(
    283           *source, *context, language_mode, scope_position);
    284       if (result->IsSharedFunctionInfo()) {
    285         break;
    286       }
    287     }
    288   }
    289   if (result->IsSharedFunctionInfo()) {
    290     Handle<SharedFunctionInfo>
    291         function_info(SharedFunctionInfo::cast(result), isolate());
    292     if (generation != 0) {
    293       Put(source, context, function_info, scope_position);
    294     }
    295     isolate()->counters()->compilation_cache_hits()->Increment();
    296     return function_info;
    297   } else {
    298     isolate()->counters()->compilation_cache_misses()->Increment();
    299     return Handle<SharedFunctionInfo>::null();
    300   }
    301 }
    302 
    303 
    304 MaybeObject* CompilationCacheEval::TryTablePut(
    305     Handle<String> source,
    306     Handle<Context> context,
    307     Handle<SharedFunctionInfo> function_info,
    308     int scope_position) {
    309   Handle<CompilationCacheTable> table = GetFirstTable();
    310   return table->PutEval(*source, *context, *function_info, scope_position);
    311 }
    312 
    313 
    314 Handle<CompilationCacheTable> CompilationCacheEval::TablePut(
    315     Handle<String> source,
    316     Handle<Context> context,
    317     Handle<SharedFunctionInfo> function_info,
    318     int scope_position) {
    319   CALL_HEAP_FUNCTION(isolate(),
    320                      TryTablePut(
    321                          source, context, function_info, scope_position),
    322                      CompilationCacheTable);
    323 }
    324 
    325 
    326 void CompilationCacheEval::Put(Handle<String> source,
    327                                Handle<Context> context,
    328                                Handle<SharedFunctionInfo> function_info,
    329                                int scope_position) {
    330   HandleScope scope(isolate());
    331   SetFirstTable(TablePut(source, context, function_info, scope_position));
    332 }
    333 
    334 
    335 Handle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
    336                                                   JSRegExp::Flags flags) {
    337   // Make sure not to leak the table into the surrounding handle
    338   // scope. Otherwise, we risk keeping old tables around even after
    339   // having cleared the cache.
    340   Object* result = NULL;
    341   int generation;
    342   { HandleScope scope(isolate());
    343     for (generation = 0; generation < generations(); generation++) {
    344       Handle<CompilationCacheTable> table = GetTable(generation);
    345       result = table->LookupRegExp(*source, flags);
    346       if (result->IsFixedArray()) {
    347         break;
    348       }
    349     }
    350   }
    351   if (result->IsFixedArray()) {
    352     Handle<FixedArray> data(FixedArray::cast(result), isolate());
    353     if (generation != 0) {
    354       Put(source, flags, data);
    355     }
    356     isolate()->counters()->compilation_cache_hits()->Increment();
    357     return data;
    358   } else {
    359     isolate()->counters()->compilation_cache_misses()->Increment();
    360     return Handle<FixedArray>::null();
    361   }
    362 }
    363 
    364 
    365 MaybeObject* CompilationCacheRegExp::TryTablePut(
    366     Handle<String> source,
    367     JSRegExp::Flags flags,
    368     Handle<FixedArray> data) {
    369   Handle<CompilationCacheTable> table = GetFirstTable();
    370   return table->PutRegExp(*source, flags, *data);
    371 }
    372 
    373 
    374 Handle<CompilationCacheTable> CompilationCacheRegExp::TablePut(
    375     Handle<String> source,
    376     JSRegExp::Flags flags,
    377     Handle<FixedArray> data) {
    378   CALL_HEAP_FUNCTION(isolate(),
    379                      TryTablePut(source, flags, data),
    380                      CompilationCacheTable);
    381 }
    382 
    383 
    384 void CompilationCacheRegExp::Put(Handle<String> source,
    385                                  JSRegExp::Flags flags,
    386                                  Handle<FixedArray> data) {
    387   HandleScope scope(isolate());
    388   SetFirstTable(TablePut(source, flags, data));
    389 }
    390 
    391 
    392 void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
    393   if (!IsEnabled()) return;
    394 
    395   eval_global_.Remove(function_info);
    396   eval_contextual_.Remove(function_info);
    397   script_.Remove(function_info);
    398 }
    399 
    400 
    401 Handle<SharedFunctionInfo> CompilationCache::LookupScript(
    402     Handle<String> source,
    403     Handle<Object> name,
    404     int line_offset,
    405     int column_offset,
    406     bool is_shared_cross_origin,
    407     Handle<Context> context) {
    408   if (!IsEnabled()) {
    409     return Handle<SharedFunctionInfo>::null();
    410   }
    411 
    412   return script_.Lookup(source,
    413                         name,
    414                         line_offset,
    415                         column_offset,
    416                         is_shared_cross_origin,
    417                         context);
    418 }
    419 
    420 
    421 Handle<SharedFunctionInfo> CompilationCache::LookupEval(
    422     Handle<String> source,
    423     Handle<Context> context,
    424     bool is_global,
    425     LanguageMode language_mode,
    426     int scope_position) {
    427   if (!IsEnabled()) {
    428     return Handle<SharedFunctionInfo>::null();
    429   }
    430 
    431   Handle<SharedFunctionInfo> result;
    432   if (is_global) {
    433     result = eval_global_.Lookup(
    434         source, context, language_mode, scope_position);
    435   } else {
    436     ASSERT(scope_position != RelocInfo::kNoPosition);
    437     result = eval_contextual_.Lookup(
    438         source, context, language_mode, scope_position);
    439   }
    440   return result;
    441 }
    442 
    443 
    444 Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
    445                                                   JSRegExp::Flags flags) {
    446   if (!IsEnabled()) {
    447     return Handle<FixedArray>::null();
    448   }
    449 
    450   return reg_exp_.Lookup(source, flags);
    451 }
    452 
    453 
    454 void CompilationCache::PutScript(Handle<String> source,
    455                                  Handle<Context> context,
    456                                  Handle<SharedFunctionInfo> function_info) {
    457   if (!IsEnabled()) {
    458     return;
    459   }
    460 
    461   script_.Put(source, context, function_info);
    462 }
    463 
    464 
    465 void CompilationCache::PutEval(Handle<String> source,
    466                                Handle<Context> context,
    467                                bool is_global,
    468                                Handle<SharedFunctionInfo> function_info,
    469                                int scope_position) {
    470   if (!IsEnabled()) {
    471     return;
    472   }
    473 
    474   HandleScope scope(isolate());
    475   if (is_global) {
    476     eval_global_.Put(source, context, function_info, scope_position);
    477   } else {
    478     ASSERT(scope_position != RelocInfo::kNoPosition);
    479     eval_contextual_.Put(source, context, function_info, scope_position);
    480   }
    481 }
    482 
    483 
    484 
    485 void CompilationCache::PutRegExp(Handle<String> source,
    486                                  JSRegExp::Flags flags,
    487                                  Handle<FixedArray> data) {
    488   if (!IsEnabled()) {
    489     return;
    490   }
    491 
    492   reg_exp_.Put(source, flags, data);
    493 }
    494 
    495 
    496 void CompilationCache::Clear() {
    497   for (int i = 0; i < kSubCacheCount; i++) {
    498     subcaches_[i]->Clear();
    499   }
    500 }
    501 
    502 
    503 void CompilationCache::Iterate(ObjectVisitor* v) {
    504   for (int i = 0; i < kSubCacheCount; i++) {
    505     subcaches_[i]->Iterate(v);
    506   }
    507 }
    508 
    509 
    510 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
    511   for (int i = 0; i < kSubCacheCount; i++) {
    512     subcaches_[i]->IterateFunctions(v);
    513   }
    514 }
    515 
    516 
    517 void CompilationCache::MarkCompactPrologue() {
    518   for (int i = 0; i < kSubCacheCount; i++) {
    519     subcaches_[i]->Age();
    520   }
    521 }
    522 
    523 
    524 void CompilationCache::Enable() {
    525   enabled_ = true;
    526 }
    527 
    528 
    529 void CompilationCache::Disable() {
    530   enabled_ = false;
    531   Clear();
    532 }
    533 
    534 
    535 } }  // namespace v8::internal
    536