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.
7 #include "src/assembler.h"
8 #include "src/compilation-cache.h"
14 // The number of generations for each sub cache.
15 static const int kRegExpGenerations = 2;
17 // Initial size of each compilation cache table allocated.
18 static const int kInitialCacheSize = 64;
21 CompilationCache::CompilationCache(Isolate* isolate)
24 eval_global_(isolate, 1),
25 eval_contextual_(isolate, 1),
26 reg_exp_(isolate, kRegExpGenerations),
28 CompilationSubCache* subcaches[kSubCacheCount] =
29 {&script_, &eval_global_, &eval_contextual_, ®_exp_};
30 for (int i = 0; i < kSubCacheCount; ++i) {
31 subcaches_[i] = subcaches[i];
36 CompilationCache::~CompilationCache() {}
39 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
40 DCHECK(generation < generations_);
41 Handle<CompilationCacheTable> result;
42 if (tables_[generation]->IsUndefined()) {
43 result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
44 tables_[generation] = *result;
46 CompilationCacheTable* table =
47 CompilationCacheTable::cast(tables_[generation]);
48 result = Handle<CompilationCacheTable>(table, isolate());
54 void CompilationSubCache::Age() {
55 // Don't directly age single-generation caches.
56 if (generations_ == 1) {
57 if (tables_[0] != isolate()->heap()->undefined_value()) {
58 CompilationCacheTable::cast(tables_[0])->Age();
63 // Age the generations implicitly killing off the oldest.
64 for (int i = generations_ - 1; i > 0; i--) {
65 tables_[i] = tables_[i - 1];
68 // Set the first generation as unborn.
69 tables_[0] = isolate()->heap()->undefined_value();
73 void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
74 Object* undefined = isolate()->heap()->undefined_value();
75 for (int i = 0; i < generations_; i++) {
76 if (tables_[i] != undefined) {
77 reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
83 void CompilationSubCache::Iterate(ObjectVisitor* v) {
84 v->VisitPointers(&tables_[0], &tables_[generations_]);
88 void CompilationSubCache::Clear() {
89 MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
93 void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
94 // Probe the script generation tables. Make sure not to leak handles
95 // into the caller's handle scope.
96 { HandleScope scope(isolate());
97 for (int generation = 0; generation < generations(); generation++) {
98 Handle<CompilationCacheTable> table = GetTable(generation);
99 table->Remove(*function_info);
105 CompilationCacheScript::CompilationCacheScript(Isolate* isolate,
107 : CompilationSubCache(isolate, generations) {}
110 // We only re-use a cached function for some script source code if the
111 // script originates from the same place. This is to avoid issues
112 // when reporting errors, etc.
113 bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
114 Handle<Object> name, int line_offset,
116 bool is_embedder_debug_script,
117 bool is_shared_cross_origin) {
118 Handle<Script> script =
119 Handle<Script>(Script::cast(function_info->script()), isolate());
120 // If the script name isn't set, the boilerplate script should have
121 // an undefined name to have the same origin.
122 if (name.is_null()) {
123 return script->name()->IsUndefined();
125 // Do the fast bailout checks first.
126 if (line_offset != script->line_offset()->value()) return false;
127 if (column_offset != script->column_offset()->value()) return false;
128 // Check that both names are strings. If not, no match.
129 if (!name->IsString() || !script->name()->IsString()) return false;
130 // Were both scripts tagged by the embedder as being internal script?
131 if (is_embedder_debug_script != script->is_embedder_debug_script()) {
134 // Were both scripts tagged by the embedder as being shared cross-origin?
135 if (is_shared_cross_origin != script->is_shared_cross_origin()) return false;
136 // Compare the two name strings for equality.
137 return String::Equals(Handle<String>::cast(name),
138 Handle<String>(String::cast(script->name())));
142 // TODO(245): Need to allow identical code from different contexts to
143 // be cached in the same script generation. Currently the first use
144 // will be cached, but subsequent code from different source / line
146 Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
147 Handle<String> source, Handle<Object> name, int line_offset,
148 int column_offset, bool is_embedder_debug_script,
149 bool is_shared_cross_origin, Handle<Context> context,
150 LanguageMode language_mode) {
151 Object* result = NULL;
154 // Probe the script generation tables. Make sure not to leak handles
155 // into the caller's handle scope.
156 { HandleScope scope(isolate());
157 for (generation = 0; generation < generations(); generation++) {
158 Handle<CompilationCacheTable> table = GetTable(generation);
159 Handle<Object> probe = table->Lookup(source, context, language_mode);
160 if (probe->IsSharedFunctionInfo()) {
161 Handle<SharedFunctionInfo> function_info =
162 Handle<SharedFunctionInfo>::cast(probe);
163 // Break when we've found a suitable shared function info that
164 // matches the origin.
165 if (HasOrigin(function_info, name, line_offset, column_offset,
166 is_embedder_debug_script, is_shared_cross_origin)) {
167 result = *function_info;
174 // Once outside the manacles of the handle scope, we need to recheck
175 // to see if we actually found a cached script. If so, we return a
176 // handle created in the caller's handle scope.
177 if (result != NULL) {
178 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
180 DCHECK(HasOrigin(shared, name, line_offset, column_offset,
181 is_embedder_debug_script, is_shared_cross_origin));
182 // If the script was found in a later generation, we promote it to
183 // the first generation to let it survive longer in the cache.
184 if (generation != 0) Put(source, context, language_mode, shared);
185 isolate()->counters()->compilation_cache_hits()->Increment();
188 isolate()->counters()->compilation_cache_misses()->Increment();
189 return Handle<SharedFunctionInfo>::null();
194 void CompilationCacheScript::Put(Handle<String> source,
195 Handle<Context> context,
196 LanguageMode language_mode,
197 Handle<SharedFunctionInfo> function_info) {
198 HandleScope scope(isolate());
199 Handle<CompilationCacheTable> table = GetFirstTable();
200 SetFirstTable(CompilationCacheTable::Put(table, source, context,
201 language_mode, function_info));
205 MaybeHandle<SharedFunctionInfo> CompilationCacheEval::Lookup(
206 Handle<String> source, Handle<SharedFunctionInfo> outer_info,
207 LanguageMode language_mode, int scope_position) {
208 HandleScope scope(isolate());
209 // Make sure not to leak the table into the surrounding handle
210 // scope. Otherwise, we risk keeping old tables around even after
211 // having cleared the cache.
212 Handle<Object> result = isolate()->factory()->undefined_value();
214 for (generation = 0; generation < generations(); generation++) {
215 Handle<CompilationCacheTable> table = GetTable(generation);
217 table->LookupEval(source, outer_info, language_mode, scope_position);
218 if (result->IsSharedFunctionInfo()) break;
220 if (result->IsSharedFunctionInfo()) {
221 Handle<SharedFunctionInfo> function_info =
222 Handle<SharedFunctionInfo>::cast(result);
223 if (generation != 0) {
224 Put(source, outer_info, function_info, scope_position);
226 isolate()->counters()->compilation_cache_hits()->Increment();
227 return scope.CloseAndEscape(function_info);
229 isolate()->counters()->compilation_cache_misses()->Increment();
230 return MaybeHandle<SharedFunctionInfo>();
235 void CompilationCacheEval::Put(Handle<String> source,
236 Handle<SharedFunctionInfo> outer_info,
237 Handle<SharedFunctionInfo> function_info,
238 int scope_position) {
239 HandleScope scope(isolate());
240 Handle<CompilationCacheTable> table = GetFirstTable();
241 table = CompilationCacheTable::PutEval(table, source, outer_info,
242 function_info, scope_position);
243 SetFirstTable(table);
247 MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
248 Handle<String> source,
249 JSRegExp::Flags flags) {
250 HandleScope scope(isolate());
251 // Make sure not to leak the table into the surrounding handle
252 // scope. Otherwise, we risk keeping old tables around even after
253 // having cleared the cache.
254 Handle<Object> result = isolate()->factory()->undefined_value();
256 for (generation = 0; generation < generations(); generation++) {
257 Handle<CompilationCacheTable> table = GetTable(generation);
258 result = table->LookupRegExp(source, flags);
259 if (result->IsFixedArray()) break;
261 if (result->IsFixedArray()) {
262 Handle<FixedArray> data = Handle<FixedArray>::cast(result);
263 if (generation != 0) {
264 Put(source, flags, data);
266 isolate()->counters()->compilation_cache_hits()->Increment();
267 return scope.CloseAndEscape(data);
269 isolate()->counters()->compilation_cache_misses()->Increment();
270 return MaybeHandle<FixedArray>();
275 void CompilationCacheRegExp::Put(Handle<String> source,
276 JSRegExp::Flags flags,
277 Handle<FixedArray> data) {
278 HandleScope scope(isolate());
279 Handle<CompilationCacheTable> table = GetFirstTable();
280 SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
284 void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
285 if (!IsEnabled()) return;
287 eval_global_.Remove(function_info);
288 eval_contextual_.Remove(function_info);
289 script_.Remove(function_info);
293 MaybeHandle<SharedFunctionInfo> CompilationCache::LookupScript(
294 Handle<String> source, Handle<Object> name, int line_offset,
295 int column_offset, bool is_embedder_debug_script,
296 bool is_shared_cross_origin, Handle<Context> context,
297 LanguageMode language_mode) {
298 if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
300 return script_.Lookup(source, name, line_offset, column_offset,
301 is_embedder_debug_script, is_shared_cross_origin,
302 context, language_mode);
306 MaybeHandle<SharedFunctionInfo> CompilationCache::LookupEval(
307 Handle<String> source, Handle<SharedFunctionInfo> outer_info,
308 Handle<Context> context, LanguageMode language_mode, int scope_position) {
309 if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
311 MaybeHandle<SharedFunctionInfo> result;
312 if (context->IsNativeContext()) {
314 eval_global_.Lookup(source, outer_info, language_mode, scope_position);
316 DCHECK(scope_position != RelocInfo::kNoPosition);
317 result = eval_contextual_.Lookup(source, outer_info, language_mode,
324 MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
325 JSRegExp::Flags flags) {
326 if (!IsEnabled()) return MaybeHandle<FixedArray>();
328 return reg_exp_.Lookup(source, flags);
332 void CompilationCache::PutScript(Handle<String> source,
333 Handle<Context> context,
334 LanguageMode language_mode,
335 Handle<SharedFunctionInfo> function_info) {
336 if (!IsEnabled()) return;
338 script_.Put(source, context, language_mode, function_info);
342 void CompilationCache::PutEval(Handle<String> source,
343 Handle<SharedFunctionInfo> outer_info,
344 Handle<Context> context,
345 Handle<SharedFunctionInfo> function_info,
346 int scope_position) {
347 if (!IsEnabled()) return;
349 HandleScope scope(isolate());
350 if (context->IsNativeContext()) {
351 eval_global_.Put(source, outer_info, function_info, scope_position);
353 DCHECK(scope_position != RelocInfo::kNoPosition);
354 eval_contextual_.Put(source, outer_info, function_info, scope_position);
360 void CompilationCache::PutRegExp(Handle<String> source,
361 JSRegExp::Flags flags,
362 Handle<FixedArray> data) {
367 reg_exp_.Put(source, flags, data);
371 void CompilationCache::Clear() {
372 for (int i = 0; i < kSubCacheCount; i++) {
373 subcaches_[i]->Clear();
378 void CompilationCache::Iterate(ObjectVisitor* v) {
379 for (int i = 0; i < kSubCacheCount; i++) {
380 subcaches_[i]->Iterate(v);
385 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
386 for (int i = 0; i < kSubCacheCount; i++) {
387 subcaches_[i]->IterateFunctions(v);
392 void CompilationCache::MarkCompactPrologue() {
393 for (int i = 0; i < kSubCacheCount; i++) {
394 subcaches_[i]->Age();
399 void CompilationCache::Enable() {
404 void CompilationCache::Disable() {
410 } } // namespace v8::internal