• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CompilationCache(Isolate * isolate)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 
~CompilationCache()37 CompilationCache::~CompilationCache() {}
38 
39 
GetTable(int generation)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 
Age()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 
IterateFunctions(ObjectVisitor * v)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 
Iterate(ObjectVisitor * v)84 void CompilationSubCache::Iterate(ObjectVisitor* v) {
85   v->VisitPointers(&tables_[0], &tables_[generations_]);
86 }
87 
88 
Clear()89 void CompilationSubCache::Clear() {
90   MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
91 }
92 
93 
Remove(Handle<SharedFunctionInfo> function_info)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 
CompilationCacheScript(Isolate * isolate)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.
HasOrigin(Handle<SharedFunctionInfo> function_info,Handle<Object> name,int line_offset,int column_offset,ScriptOriginOptions resource_options)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.
Lookup(Handle<String> source,Handle<Object> name,int line_offset,int column_offset,ScriptOriginOptions resource_options,Handle<Context> context,LanguageMode language_mode)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 
Put(Handle<String> source,Handle<Context> context,LanguageMode language_mode,Handle<SharedFunctionInfo> function_info,Handle<Cell> literals)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 
Lookup(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<Context> native_context,LanguageMode language_mode,int position)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 
Put(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<SharedFunctionInfo> function_info,Handle<Context> native_context,Handle<Cell> literals,int position)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 
Lookup(Handle<String> source,JSRegExp::Flags flags)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 
Put(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)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 
Remove(Handle<SharedFunctionInfo> function_info)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 
LookupScript(Handle<String> source,Handle<Object> name,int line_offset,int column_offset,ScriptOriginOptions resource_options,Handle<Context> context,LanguageMode language_mode)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 
LookupEval(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<Context> context,LanguageMode language_mode,int position)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 
LookupRegExp(Handle<String> source,JSRegExp::Flags flags)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 
PutScript(Handle<String> source,Handle<Context> context,LanguageMode language_mode,Handle<SharedFunctionInfo> function_info,Handle<Cell> literals)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 
PutEval(Handle<String> source,Handle<SharedFunctionInfo> outer_info,Handle<Context> context,Handle<SharedFunctionInfo> function_info,Handle<Cell> literals,int position)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 
PutRegExp(Handle<String> source,JSRegExp::Flags flags,Handle<FixedArray> data)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 
Clear()354 void CompilationCache::Clear() {
355   for (int i = 0; i < kSubCacheCount; i++) {
356     subcaches_[i]->Clear();
357   }
358 }
359 
360 
Iterate(ObjectVisitor * v)361 void CompilationCache::Iterate(ObjectVisitor* v) {
362   for (int i = 0; i < kSubCacheCount; i++) {
363     subcaches_[i]->Iterate(v);
364   }
365 }
366 
367 
IterateFunctions(ObjectVisitor * v)368 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
369   for (int i = 0; i < kSubCacheCount; i++) {
370     subcaches_[i]->IterateFunctions(v);
371   }
372 }
373 
374 
MarkCompactPrologue()375 void CompilationCache::MarkCompactPrologue() {
376   for (int i = 0; i < kSubCacheCount; i++) {
377     subcaches_[i]->Age();
378   }
379 }
380 
381 
Enable()382 void CompilationCache::Enable() {
383   enabled_ = true;
384 }
385 
386 
Disable()387 void CompilationCache::Disable() {
388   enabled_ = false;
389   Clear();
390 }
391 
392 
393 }  // namespace internal
394 }  // namespace v8
395