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