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_, ®_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