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"
9 #include "src/serialize.h"
15 // The number of generations for each sub cache.
16 static const int kRegExpGenerations = 2;
18 // Initial size of each compilation cache table allocated.
19 static const int kInitialCacheSize = 64;
22 CompilationCache::CompilationCache(Isolate* isolate)
25 eval_global_(isolate, 1),
26 eval_contextual_(isolate, 1),
27 reg_exp_(isolate, kRegExpGenerations),
29 CompilationSubCache* subcaches[kSubCacheCount] =
30 {&script_, &eval_global_, &eval_contextual_, ®_exp_};
31 for (int i = 0; i < kSubCacheCount; ++i) {
32 subcaches_[i] = subcaches[i];
37 CompilationCache::~CompilationCache() {}
40 Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
41 DCHECK(generation < generations_);
42 Handle<CompilationCacheTable> result;
43 if (tables_[generation]->IsUndefined()) {
44 result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
45 tables_[generation] = *result;
47 CompilationCacheTable* table =
48 CompilationCacheTable::cast(tables_[generation]);
49 result = Handle<CompilationCacheTable>(table, isolate());
55 void CompilationSubCache::Age() {
56 // Don't directly age single-generation caches.
57 if (generations_ == 1) {
58 if (tables_[0] != isolate()->heap()->undefined_value()) {
59 CompilationCacheTable::cast(tables_[0])->Age();
64 // Age the generations implicitly killing off the oldest.
65 for (int i = generations_ - 1; i > 0; i--) {
66 tables_[i] = tables_[i - 1];
69 // Set the first generation as unborn.
70 tables_[0] = isolate()->heap()->undefined_value();
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);
84 void CompilationSubCache::Iterate(ObjectVisitor* v) {
85 v->VisitPointers(&tables_[0], &tables_[generations_]);
89 void CompilationSubCache::Clear() {
90 MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
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);
106 CompilationCacheScript::CompilationCacheScript(Isolate* isolate,
108 : CompilationSubCache(isolate, generations) {}
111 // We only re-use a cached function for some script source code if the
112 // script originates from the same place. This is to avoid issues
113 // when reporting errors, etc.
114 bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
115 Handle<Object> name, int line_offset,
117 bool is_embedder_debug_script,
118 bool is_shared_cross_origin) {
119 Handle<Script> script =
120 Handle<Script>(Script::cast(function_info->script()), isolate());
121 // If the script name isn't set, the boilerplate script should have
122 // an undefined name to have the same origin.
123 if (name.is_null()) {
124 return script->name()->IsUndefined();
126 // Do the fast bailout checks first.
127 if (line_offset != script->line_offset()->value()) return false;
128 if (column_offset != script->column_offset()->value()) return false;
129 // Check that both names are strings. If not, no match.
130 if (!name->IsString() || !script->name()->IsString()) return false;
131 // Were both scripts tagged by the embedder as being internal script?
132 if (is_embedder_debug_script != script->is_embedder_debug_script()) {
135 // Were both scripts tagged by the embedder as being shared cross-origin?
136 if (is_shared_cross_origin != script->is_shared_cross_origin()) return false;
137 // Compare the two name strings for equality.
138 return String::Equals(Handle<String>::cast(name),
139 Handle<String>(String::cast(script->name())));
143 // TODO(245): Need to allow identical code from different contexts to
144 // be cached in the same script generation. Currently the first use
145 // will be cached, but subsequent code from different source / line
147 Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(
148 Handle<String> source, Handle<Object> name, int line_offset,
149 int column_offset, bool is_embedder_debug_script,
150 bool is_shared_cross_origin, Handle<Context> context,
151 LanguageMode language_mode) {
152 Object* result = NULL;
155 // Probe the script generation tables. Make sure not to leak handles
156 // into the caller's handle scope.
157 { HandleScope scope(isolate());
158 for (generation = 0; generation < generations(); generation++) {
159 Handle<CompilationCacheTable> table = GetTable(generation);
160 Handle<Object> probe = table->Lookup(source, context, language_mode);
161 if (probe->IsSharedFunctionInfo()) {
162 Handle<SharedFunctionInfo> function_info =
163 Handle<SharedFunctionInfo>::cast(probe);
164 // Break when we've found a suitable shared function info that
165 // matches the origin.
166 if (HasOrigin(function_info, name, line_offset, column_offset,
167 is_embedder_debug_script, is_shared_cross_origin)) {
168 result = *function_info;
175 // Once outside the manacles of the handle scope, we need to recheck
176 // to see if we actually found a cached script. If so, we return a
177 // handle created in the caller's handle scope.
178 if (result != NULL) {
179 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
181 DCHECK(HasOrigin(shared, name, line_offset, column_offset,
182 is_embedder_debug_script, is_shared_cross_origin));
183 // If the script was found in a later generation, we promote it to
184 // the first generation to let it survive longer in the cache.
185 if (generation != 0) Put(source, context, language_mode, shared);
186 isolate()->counters()->compilation_cache_hits()->Increment();
189 isolate()->counters()->compilation_cache_misses()->Increment();
190 return Handle<SharedFunctionInfo>::null();
195 void CompilationCacheScript::Put(Handle<String> source,
196 Handle<Context> context,
197 LanguageMode language_mode,
198 Handle<SharedFunctionInfo> function_info) {
199 HandleScope scope(isolate());
200 Handle<CompilationCacheTable> table = GetFirstTable();
201 SetFirstTable(CompilationCacheTable::Put(table, source, context,
202 language_mode, function_info));
206 MaybeHandle<SharedFunctionInfo> CompilationCacheEval::Lookup(
207 Handle<String> source, Handle<SharedFunctionInfo> outer_info,
208 LanguageMode language_mode, int scope_position) {
209 HandleScope scope(isolate());
210 // Make sure not to leak the table into the surrounding handle
211 // scope. Otherwise, we risk keeping old tables around even after
212 // having cleared the cache.
213 Handle<Object> result = isolate()->factory()->undefined_value();
215 for (generation = 0; generation < generations(); generation++) {
216 Handle<CompilationCacheTable> table = GetTable(generation);
218 table->LookupEval(source, outer_info, language_mode, scope_position);
219 if (result->IsSharedFunctionInfo()) break;
221 if (result->IsSharedFunctionInfo()) {
222 Handle<SharedFunctionInfo> function_info =
223 Handle<SharedFunctionInfo>::cast(result);
224 if (generation != 0) {
225 Put(source, outer_info, function_info, scope_position);
227 isolate()->counters()->compilation_cache_hits()->Increment();
228 return scope.CloseAndEscape(function_info);
230 isolate()->counters()->compilation_cache_misses()->Increment();
231 return MaybeHandle<SharedFunctionInfo>();
236 void CompilationCacheEval::Put(Handle<String> source,
237 Handle<SharedFunctionInfo> outer_info,
238 Handle<SharedFunctionInfo> function_info,
239 int scope_position) {
240 HandleScope scope(isolate());
241 Handle<CompilationCacheTable> table = GetFirstTable();
242 table = CompilationCacheTable::PutEval(table, source, outer_info,
243 function_info, scope_position);
244 SetFirstTable(table);
248 MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
249 Handle<String> source,
250 JSRegExp::Flags flags) {
251 HandleScope scope(isolate());
252 // Make sure not to leak the table into the surrounding handle
253 // scope. Otherwise, we risk keeping old tables around even after
254 // having cleared the cache.
255 Handle<Object> result = isolate()->factory()->undefined_value();
257 for (generation = 0; generation < generations(); generation++) {
258 Handle<CompilationCacheTable> table = GetTable(generation);
259 result = table->LookupRegExp(source, flags);
260 if (result->IsFixedArray()) break;
262 if (result->IsFixedArray()) {
263 Handle<FixedArray> data = Handle<FixedArray>::cast(result);
264 if (generation != 0) {
265 Put(source, flags, data);
267 isolate()->counters()->compilation_cache_hits()->Increment();
268 return scope.CloseAndEscape(data);
270 isolate()->counters()->compilation_cache_misses()->Increment();
271 return MaybeHandle<FixedArray>();
276 void CompilationCacheRegExp::Put(Handle<String> source,
277 JSRegExp::Flags flags,
278 Handle<FixedArray> data) {
279 HandleScope scope(isolate());
280 Handle<CompilationCacheTable> table = GetFirstTable();
281 SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
285 void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
286 if (!IsEnabled()) return;
288 eval_global_.Remove(function_info);
289 eval_contextual_.Remove(function_info);
290 script_.Remove(function_info);
294 MaybeHandle<SharedFunctionInfo> CompilationCache::LookupScript(
295 Handle<String> source, Handle<Object> name, int line_offset,
296 int column_offset, bool is_embedder_debug_script,
297 bool is_shared_cross_origin, Handle<Context> context,
298 LanguageMode language_mode) {
299 if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
301 return script_.Lookup(source, name, line_offset, column_offset,
302 is_embedder_debug_script, is_shared_cross_origin,
303 context, language_mode);
307 MaybeHandle<SharedFunctionInfo> CompilationCache::LookupEval(
308 Handle<String> source, Handle<SharedFunctionInfo> outer_info,
309 Handle<Context> context, LanguageMode language_mode, int scope_position) {
310 if (!IsEnabled()) return MaybeHandle<SharedFunctionInfo>();
312 MaybeHandle<SharedFunctionInfo> result;
313 if (context->IsNativeContext()) {
315 eval_global_.Lookup(source, outer_info, language_mode, scope_position);
317 DCHECK(scope_position != RelocInfo::kNoPosition);
318 result = eval_contextual_.Lookup(source, outer_info, language_mode,
325 MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
326 JSRegExp::Flags flags) {
327 if (!IsEnabled()) return MaybeHandle<FixedArray>();
329 return reg_exp_.Lookup(source, flags);
333 void CompilationCache::PutScript(Handle<String> source,
334 Handle<Context> context,
335 LanguageMode language_mode,
336 Handle<SharedFunctionInfo> function_info) {
337 if (!IsEnabled()) return;
339 script_.Put(source, context, language_mode, function_info);
343 void CompilationCache::PutEval(Handle<String> source,
344 Handle<SharedFunctionInfo> outer_info,
345 Handle<Context> context,
346 Handle<SharedFunctionInfo> function_info,
347 int scope_position) {
348 if (!IsEnabled()) return;
350 HandleScope scope(isolate());
351 if (context->IsNativeContext()) {
352 eval_global_.Put(source, outer_info, function_info, scope_position);
354 DCHECK(scope_position != RelocInfo::kNoPosition);
355 eval_contextual_.Put(source, outer_info, function_info, scope_position);
361 void CompilationCache::PutRegExp(Handle<String> source,
362 JSRegExp::Flags flags,
363 Handle<FixedArray> data) {
368 reg_exp_.Put(source, flags, data);
372 void CompilationCache::Clear() {
373 for (int i = 0; i < kSubCacheCount; i++) {
374 subcaches_[i]->Clear();
379 void CompilationCache::Iterate(ObjectVisitor* v) {
380 for (int i = 0; i < kSubCacheCount; i++) {
381 subcaches_[i]->Iterate(v);
386 void CompilationCache::IterateFunctions(ObjectVisitor* v) {
387 for (int i = 0; i < kSubCacheCount; i++) {
388 subcaches_[i]->IterateFunctions(v);
393 void CompilationCache::MarkCompactPrologue() {
394 for (int i = 0; i < kSubCacheCount; i++) {
395 subcaches_[i]->Age();
400 void CompilationCache::Enable() {
405 void CompilationCache::Disable() {
411 } } // namespace v8::internal