The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.
Please select the desired protocol below to get the URL.
This URL has Read-Only access.
main_repo / deps / v8 / src / handles.cc @ f230a1cf
History | View | Annotate | Download (30.3 KB)
1 |
// Copyright 2012 the V8 project authors. All rights reserved.
|
---|---|
2 |
// Redistribution and use in source and binary forms, with or without
|
3 |
// modification, are permitted provided that the following conditions are
|
4 |
// met:
|
5 |
//
|
6 |
// * Redistributions of source code must retain the above copyright
|
7 |
// notice, this list of conditions and the following disclaimer.
|
8 |
// * Redistributions in binary form must reproduce the above
|
9 |
// copyright notice, this list of conditions and the following
|
10 |
// disclaimer in the documentation and/or other materials provided
|
11 |
// with the distribution.
|
12 |
// * Neither the name of Google Inc. nor the names of its
|
13 |
// contributors may be used to endorse or promote products derived
|
14 |
// from this software without specific prior written permission.
|
15 |
//
|
16 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17 |
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18 |
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19 |
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
20 |
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
23 |
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
24 |
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
25 |
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26 |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27 |
|
28 |
#include "v8.h" |
29 |
|
30 |
#include "accessors.h" |
31 |
#include "api.h" |
32 |
#include "arguments.h" |
33 |
#include "bootstrapper.h" |
34 |
#include "compiler.h" |
35 |
#include "debug.h" |
36 |
#include "execution.h" |
37 |
#include "global-handles.h" |
38 |
#include "natives.h" |
39 |
#include "runtime.h" |
40 |
#include "string-search.h" |
41 |
#include "stub-cache.h" |
42 |
#include "vm-state-inl.h" |
43 |
|
44 |
namespace v8 {
|
45 |
namespace internal {
|
46 |
|
47 |
|
48 |
int HandleScope::NumberOfHandles(Isolate* isolate) {
|
49 |
HandleScopeImplementer* impl = isolate->handle_scope_implementer(); |
50 |
int n = impl->blocks()->length();
|
51 |
if (n == 0) return 0; |
52 |
return ((n - 1) * kHandleBlockSize) + static_cast<int>( |
53 |
(isolate->handle_scope_data()->next - impl->blocks()->last())); |
54 |
} |
55 |
|
56 |
|
57 |
Object** HandleScope::Extend(Isolate* isolate) { |
58 |
v8::ImplementationUtilities::HandleScopeData* current = |
59 |
isolate->handle_scope_data(); |
60 |
|
61 |
Object** result = current->next; |
62 |
|
63 |
ASSERT(result == current->limit); |
64 |
// Make sure there's at least one scope on the stack and that the
|
65 |
// top of the scope stack isn't a barrier.
|
66 |
if (current->level == 0) { |
67 |
Utils::ReportApiFailure("v8::HandleScope::CreateHandle()",
|
68 |
"Cannot create a handle without a HandleScope");
|
69 |
return NULL; |
70 |
} |
71 |
HandleScopeImplementer* impl = isolate->handle_scope_implementer(); |
72 |
// If there's more room in the last block, we use that. This is used
|
73 |
// for fast creation of scopes after scope barriers.
|
74 |
if (!impl->blocks()->is_empty()) {
|
75 |
Object** limit = &impl->blocks()->last()[kHandleBlockSize]; |
76 |
if (current->limit != limit) {
|
77 |
current->limit = limit; |
78 |
ASSERT(limit - current->next < kHandleBlockSize); |
79 |
} |
80 |
} |
81 |
|
82 |
// If we still haven't found a slot for the handle, we extend the
|
83 |
// current handle scope by allocating a new handle block.
|
84 |
if (result == current->limit) {
|
85 |
// If there's a spare block, use it for growing the current scope.
|
86 |
result = impl->GetSpareOrNewBlock(); |
87 |
// Add the extension to the global list of blocks, but count the
|
88 |
// extension as part of the current scope.
|
89 |
impl->blocks()->Add(result); |
90 |
current->limit = &result[kHandleBlockSize]; |
91 |
} |
92 |
|
93 |
return result;
|
94 |
} |
95 |
|
96 |
|
97 |
void HandleScope::DeleteExtensions(Isolate* isolate) {
|
98 |
v8::ImplementationUtilities::HandleScopeData* current = |
99 |
isolate->handle_scope_data(); |
100 |
isolate->handle_scope_implementer()->DeleteExtensions(current->limit); |
101 |
} |
102 |
|
103 |
|
104 |
#ifdef ENABLE_HANDLE_ZAPPING
|
105 |
void HandleScope::ZapRange(Object** start, Object** end) {
|
106 |
ASSERT(end - start <= kHandleBlockSize); |
107 |
for (Object** p = start; p != end; p++) {
|
108 |
*reinterpret_cast<Address*>(p) = v8::internal::kHandleZapValue;
|
109 |
} |
110 |
} |
111 |
#endif
|
112 |
|
113 |
|
114 |
Address HandleScope::current_level_address(Isolate* isolate) { |
115 |
return reinterpret_cast<Address>(&isolate->handle_scope_data()->level); |
116 |
} |
117 |
|
118 |
|
119 |
Address HandleScope::current_next_address(Isolate* isolate) { |
120 |
return reinterpret_cast<Address>(&isolate->handle_scope_data()->next); |
121 |
} |
122 |
|
123 |
|
124 |
Address HandleScope::current_limit_address(Isolate* isolate) { |
125 |
return reinterpret_cast<Address>(&isolate->handle_scope_data()->limit); |
126 |
} |
127 |
|
128 |
|
129 |
Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content, |
130 |
Handle<JSArray> array) { |
131 |
CALL_HEAP_FUNCTION(content->GetIsolate(), |
132 |
content->AddKeysFromJSArray(*array), FixedArray); |
133 |
} |
134 |
|
135 |
|
136 |
Handle<FixedArray> UnionOfKeys(Handle<FixedArray> first, |
137 |
Handle<FixedArray> second) { |
138 |
CALL_HEAP_FUNCTION(first->GetIsolate(), |
139 |
first->UnionOfKeys(*second), FixedArray); |
140 |
} |
141 |
|
142 |
|
143 |
Handle<JSGlobalProxy> ReinitializeJSGlobalProxy( |
144 |
Handle<JSFunction> constructor, |
145 |
Handle<JSGlobalProxy> global) { |
146 |
CALL_HEAP_FUNCTION( |
147 |
constructor->GetIsolate(), |
148 |
constructor->GetHeap()->ReinitializeJSGlobalProxy(*constructor, *global), |
149 |
JSGlobalProxy); |
150 |
} |
151 |
|
152 |
|
153 |
void FlattenString(Handle<String> string) { |
154 |
CALL_HEAP_FUNCTION_VOID(string->GetIsolate(), string->TryFlatten()); |
155 |
} |
156 |
|
157 |
|
158 |
Handle<String> FlattenGetString(Handle<String> string) {
|
159 |
CALL_HEAP_FUNCTION(string->GetIsolate(), string->TryFlatten(), String); |
160 |
} |
161 |
|
162 |
|
163 |
Handle<Object> SetProperty(Isolate* isolate, |
164 |
Handle<Object> object, |
165 |
Handle<Object> key, |
166 |
Handle<Object> value, |
167 |
PropertyAttributes attributes, |
168 |
StrictModeFlag strict_mode) { |
169 |
CALL_HEAP_FUNCTION( |
170 |
isolate, |
171 |
Runtime::SetObjectProperty( |
172 |
isolate, object, key, value, attributes, strict_mode), |
173 |
Object); |
174 |
} |
175 |
|
176 |
|
177 |
Handle<Object> ForceSetProperty(Handle<JSObject> object, |
178 |
Handle<Object> key, |
179 |
Handle<Object> value, |
180 |
PropertyAttributes attributes) { |
181 |
Isolate* isolate = object->GetIsolate(); |
182 |
CALL_HEAP_FUNCTION( |
183 |
isolate, |
184 |
Runtime::ForceSetObjectProperty( |
185 |
isolate, object, key, value, attributes), |
186 |
Object); |
187 |
} |
188 |
|
189 |
|
190 |
Handle<Object> DeleteProperty(Handle<JSObject> object, Handle<Object> key) { |
191 |
Isolate* isolate = object->GetIsolate(); |
192 |
CALL_HEAP_FUNCTION(isolate, |
193 |
Runtime::DeleteObjectProperty( |
194 |
isolate, object, key, JSReceiver::NORMAL_DELETION), |
195 |
Object); |
196 |
} |
197 |
|
198 |
|
199 |
Handle<Object> ForceDeleteProperty(Handle<JSObject> object, |
200 |
Handle<Object> key) { |
201 |
Isolate* isolate = object->GetIsolate(); |
202 |
CALL_HEAP_FUNCTION(isolate, |
203 |
Runtime::DeleteObjectProperty( |
204 |
isolate, object, key, JSReceiver::FORCE_DELETION), |
205 |
Object); |
206 |
} |
207 |
|
208 |
|
209 |
Handle<Object> HasProperty(Handle<JSReceiver> obj, Handle<Object> key) { |
210 |
Isolate* isolate = obj->GetIsolate(); |
211 |
CALL_HEAP_FUNCTION(isolate, |
212 |
Runtime::HasObjectProperty(isolate, obj, key), Object); |
213 |
} |
214 |
|
215 |
|
216 |
Handle<Object> GetProperty(Handle<JSReceiver> obj, |
217 |
const char* name) { |
218 |
Isolate* isolate = obj->GetIsolate(); |
219 |
Handle<String> str = isolate->factory()->InternalizeUtf8String(name); |
220 |
CALL_HEAP_FUNCTION(isolate, obj->GetProperty(*str), Object); |
221 |
} |
222 |
|
223 |
|
224 |
Handle<Object> GetProperty(Isolate* isolate, |
225 |
Handle<Object> obj, |
226 |
Handle<Object> key) { |
227 |
CALL_HEAP_FUNCTION(isolate, |
228 |
Runtime::GetObjectProperty(isolate, obj, key), Object); |
229 |
} |
230 |
|
231 |
|
232 |
Handle<Object> LookupSingleCharacterStringFromCode(Isolate* isolate, |
233 |
uint32_t index) { |
234 |
CALL_HEAP_FUNCTION( |
235 |
isolate, |
236 |
isolate->heap()->LookupSingleCharacterStringFromCode(index), Object); |
237 |
} |
238 |
|
239 |
|
240 |
// Wrappers for scripts are kept alive and cached in weak global
|
241 |
// handles referred from foreign objects held by the scripts as long as
|
242 |
// they are used. When they are not used anymore, the garbage
|
243 |
// collector will call the weak callback on the global handle
|
244 |
// associated with the wrapper and get rid of both the wrapper and the
|
245 |
// handle.
|
246 |
static void ClearWrapperCache(v8::Isolate* v8_isolate, |
247 |
Persistent<v8::Value>* handle, |
248 |
void*) {
|
249 |
Handle<Object> cache = Utils::OpenPersistent(handle); |
250 |
JSValue* wrapper = JSValue::cast(*cache); |
251 |
Foreign* foreign = Script::cast(wrapper->value())->wrapper(); |
252 |
ASSERT(foreign->foreign_address() == |
253 |
reinterpret_cast<Address>(cache.location()));
|
254 |
foreign->set_foreign_address(0);
|
255 |
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
256 |
isolate->global_handles()->Destroy(cache.location()); |
257 |
isolate->counters()->script_wrappers()->Decrement(); |
258 |
} |
259 |
|
260 |
|
261 |
Handle<JSValue> GetScriptWrapper(Handle<Script> script) { |
262 |
if (script->wrapper()->foreign_address() != NULL) { |
263 |
// Return the script wrapper directly from the cache.
|
264 |
return Handle<JSValue>(
|
265 |
reinterpret_cast<JSValue**>(script->wrapper()->foreign_address()));
|
266 |
} |
267 |
Isolate* isolate = script->GetIsolate(); |
268 |
// Construct a new script wrapper.
|
269 |
isolate->counters()->script_wrappers()->Increment(); |
270 |
Handle<JSFunction> constructor = isolate->script_function(); |
271 |
Handle<JSValue> result = |
272 |
Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor)); |
273 |
|
274 |
// The allocation might have triggered a GC, which could have called this
|
275 |
// function recursively, and a wrapper has already been created and cached.
|
276 |
// In that case, simply return the cached wrapper.
|
277 |
if (script->wrapper()->foreign_address() != NULL) { |
278 |
return Handle<JSValue>(
|
279 |
reinterpret_cast<JSValue**>(script->wrapper()->foreign_address()));
|
280 |
} |
281 |
|
282 |
result->set_value(*script); |
283 |
|
284 |
// Create a new weak global handle and use it to cache the wrapper
|
285 |
// for future use. The cache will automatically be cleared by the
|
286 |
// garbage collector when it is not used anymore.
|
287 |
Handle<Object> handle = isolate->global_handles()->Create(*result); |
288 |
isolate->global_handles()->MakeWeak(handle.location(), |
289 |
NULL,
|
290 |
&ClearWrapperCache); |
291 |
script->wrapper()->set_foreign_address( |
292 |
reinterpret_cast<Address>(handle.location()));
|
293 |
return result;
|
294 |
} |
295 |
|
296 |
|
297 |
// Init line_ends array with code positions of line ends inside script
|
298 |
// source.
|
299 |
void InitScriptLineEnds(Handle<Script> script) {
|
300 |
if (!script->line_ends()->IsUndefined()) return; |
301 |
|
302 |
Isolate* isolate = script->GetIsolate(); |
303 |
|
304 |
if (!script->source()->IsString()) {
|
305 |
ASSERT(script->source()->IsUndefined()); |
306 |
Handle<FixedArray> empty = isolate->factory()->NewFixedArray(0);
|
307 |
script->set_line_ends(*empty); |
308 |
ASSERT(script->line_ends()->IsFixedArray()); |
309 |
return;
|
310 |
} |
311 |
|
312 |
Handle<String> src(String::cast(script->source()), isolate); |
313 |
|
314 |
Handle<FixedArray> array = CalculateLineEnds(src, true);
|
315 |
|
316 |
if (*array != isolate->heap()->empty_fixed_array()) {
|
317 |
array->set_map(isolate->heap()->fixed_cow_array_map()); |
318 |
} |
319 |
|
320 |
script->set_line_ends(*array); |
321 |
ASSERT(script->line_ends()->IsFixedArray()); |
322 |
} |
323 |
|
324 |
|
325 |
template <typename SourceChar> |
326 |
static void CalculateLineEnds(Isolate* isolate, |
327 |
List<int>* line_ends,
|
328 |
Vector<const SourceChar> src,
|
329 |
bool with_last_line) {
|
330 |
const int src_len = src.length(); |
331 |
StringSearch<uint8_t, SourceChar> search(isolate, STATIC_ASCII_VECTOR("\n"));
|
332 |
|
333 |
// Find and record line ends.
|
334 |
int position = 0; |
335 |
while (position != -1 && position < src_len) { |
336 |
position = search.Search(src, position); |
337 |
if (position != -1) { |
338 |
line_ends->Add(position); |
339 |
position++; |
340 |
} else if (with_last_line) { |
341 |
// Even if the last line misses a line end, it is counted.
|
342 |
line_ends->Add(src_len); |
343 |
return;
|
344 |
} |
345 |
} |
346 |
} |
347 |
|
348 |
|
349 |
Handle<FixedArray> CalculateLineEnds(Handle<String> src, |
350 |
bool with_last_line) {
|
351 |
src = FlattenGetString(src); |
352 |
// Rough estimate of line count based on a roughly estimated average
|
353 |
// length of (unpacked) code.
|
354 |
int line_count_estimate = src->length() >> 4; |
355 |
List<int> line_ends(line_count_estimate);
|
356 |
Isolate* isolate = src->GetIsolate(); |
357 |
{ |
358 |
DisallowHeapAllocation no_allocation; // ensure vectors stay valid.
|
359 |
// Dispatch on type of strings.
|
360 |
String::FlatContent content = src->GetFlatContent(); |
361 |
ASSERT(content.IsFlat()); |
362 |
if (content.IsAscii()) {
|
363 |
CalculateLineEnds(isolate, |
364 |
&line_ends, |
365 |
content.ToOneByteVector(), |
366 |
with_last_line); |
367 |
} else {
|
368 |
CalculateLineEnds(isolate, |
369 |
&line_ends, |
370 |
content.ToUC16Vector(), |
371 |
with_last_line); |
372 |
} |
373 |
} |
374 |
int line_count = line_ends.length();
|
375 |
Handle<FixedArray> array = isolate->factory()->NewFixedArray(line_count); |
376 |
for (int i = 0; i < line_count; i++) { |
377 |
array->set(i, Smi::FromInt(line_ends[i])); |
378 |
} |
379 |
return array;
|
380 |
} |
381 |
|
382 |
|
383 |
// Convert code position into line number.
|
384 |
int GetScriptLineNumber(Handle<Script> script, int code_pos) { |
385 |
InitScriptLineEnds(script); |
386 |
DisallowHeapAllocation no_allocation; |
387 |
FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); |
388 |
const int line_ends_len = line_ends_array->length(); |
389 |
|
390 |
if (!line_ends_len) return -1; |
391 |
|
392 |
if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) { |
393 |
return script->line_offset()->value();
|
394 |
} |
395 |
|
396 |
int left = 0; |
397 |
int right = line_ends_len;
|
398 |
while (int half = (right - left) / 2) { |
399 |
if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) {
|
400 |
right -= half; |
401 |
} else {
|
402 |
left += half; |
403 |
} |
404 |
} |
405 |
return right + script->line_offset()->value();
|
406 |
} |
407 |
|
408 |
|
409 |
// Convert code position into column number.
|
410 |
int GetScriptColumnNumber(Handle<Script> script, int code_pos) { |
411 |
int line_number = GetScriptLineNumber(script, code_pos);
|
412 |
if (line_number == -1) return -1; |
413 |
|
414 |
DisallowHeapAllocation no_allocation; |
415 |
FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); |
416 |
line_number = line_number - script->line_offset()->value(); |
417 |
if (line_number == 0) return code_pos + script->column_offset()->value(); |
418 |
int prev_line_end_pos =
|
419 |
Smi::cast(line_ends_array->get(line_number - 1))->value();
|
420 |
return code_pos - (prev_line_end_pos + 1); |
421 |
} |
422 |
|
423 |
|
424 |
int GetScriptLineNumberSafe(Handle<Script> script, int code_pos) { |
425 |
DisallowHeapAllocation no_allocation; |
426 |
if (!script->line_ends()->IsUndefined()) {
|
427 |
return GetScriptLineNumber(script, code_pos);
|
428 |
} |
429 |
// Slow mode: we do not have line_ends. We have to iterate through source.
|
430 |
if (!script->source()->IsString()) {
|
431 |
return -1; |
432 |
} |
433 |
String* source = String::cast(script->source()); |
434 |
int line = 0; |
435 |
int len = source->length();
|
436 |
for (int pos = 0; pos < len; pos++) { |
437 |
if (pos == code_pos) {
|
438 |
break;
|
439 |
} |
440 |
if (source->Get(pos) == '\n') { |
441 |
line++; |
442 |
} |
443 |
} |
444 |
return line;
|
445 |
} |
446 |
|
447 |
|
448 |
// Compute the property keys from the interceptor.
|
449 |
// TODO(rossberg): support symbols in API, and filter here if needed.
|
450 |
v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSReceiver> receiver, |
451 |
Handle<JSObject> object) { |
452 |
Isolate* isolate = receiver->GetIsolate(); |
453 |
Handle<InterceptorInfo> interceptor(object->GetNamedInterceptor()); |
454 |
PropertyCallbackArguments |
455 |
args(isolate, interceptor->data(), *receiver, *object); |
456 |
v8::Handle<v8::Array> result; |
457 |
if (!interceptor->enumerator()->IsUndefined()) {
|
458 |
v8::NamedPropertyEnumeratorCallback enum_fun = |
459 |
v8::ToCData<v8::NamedPropertyEnumeratorCallback>( |
460 |
interceptor->enumerator()); |
461 |
LOG(isolate, ApiObjectAccess("interceptor-named-enum", *object));
|
462 |
result = args.Call(enum_fun); |
463 |
} |
464 |
#if ENABLE_EXTRA_CHECKS
|
465 |
CHECK(result.IsEmpty() || v8::Utils::OpenHandle(*result)->IsJSObject()); |
466 |
#endif
|
467 |
return v8::Local<v8::Array>::New(reinterpret_cast<v8::Isolate*>(isolate), |
468 |
result); |
469 |
} |
470 |
|
471 |
|
472 |
// Compute the element keys from the interceptor.
|
473 |
v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSReceiver> receiver, |
474 |
Handle<JSObject> object) { |
475 |
Isolate* isolate = receiver->GetIsolate(); |
476 |
Handle<InterceptorInfo> interceptor(object->GetIndexedInterceptor()); |
477 |
PropertyCallbackArguments |
478 |
args(isolate, interceptor->data(), *receiver, *object); |
479 |
v8::Handle<v8::Array> result; |
480 |
if (!interceptor->enumerator()->IsUndefined()) {
|
481 |
v8::IndexedPropertyEnumeratorCallback enum_fun = |
482 |
v8::ToCData<v8::IndexedPropertyEnumeratorCallback>( |
483 |
interceptor->enumerator()); |
484 |
LOG(isolate, ApiObjectAccess("interceptor-indexed-enum", *object));
|
485 |
result = args.Call(enum_fun); |
486 |
#if ENABLE_EXTRA_CHECKS
|
487 |
CHECK(result.IsEmpty() || v8::Utils::OpenHandle(*result)->IsJSObject()); |
488 |
#endif
|
489 |
} |
490 |
return v8::Local<v8::Array>::New(reinterpret_cast<v8::Isolate*>(isolate), |
491 |
result); |
492 |
} |
493 |
|
494 |
|
495 |
Handle<Object> GetScriptNameOrSourceURL(Handle<Script> script) { |
496 |
Isolate* isolate = script->GetIsolate(); |
497 |
Handle<String> name_or_source_url_key = |
498 |
isolate->factory()->InternalizeOneByteString( |
499 |
STATIC_ASCII_VECTOR("nameOrSourceURL"));
|
500 |
Handle<JSValue> script_wrapper = GetScriptWrapper(script); |
501 |
Handle<Object> property = GetProperty(isolate, |
502 |
script_wrapper, |
503 |
name_or_source_url_key); |
504 |
ASSERT(property->IsJSFunction()); |
505 |
Handle<JSFunction> method = Handle<JSFunction>::cast(property); |
506 |
bool caught_exception;
|
507 |
Handle<Object> result = Execution::TryCall(method, script_wrapper, 0,
|
508 |
NULL, &caught_exception);
|
509 |
if (caught_exception) {
|
510 |
result = isolate->factory()->undefined_value(); |
511 |
} |
512 |
return result;
|
513 |
} |
514 |
|
515 |
|
516 |
static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { |
517 |
int len = array->length();
|
518 |
for (int i = 0; i < len; i++) { |
519 |
Object* e = array->get(i); |
520 |
if (!(e->IsString() || e->IsNumber())) return false; |
521 |
} |
522 |
return true; |
523 |
} |
524 |
|
525 |
|
526 |
Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSReceiver> object, |
527 |
KeyCollectionType type, |
528 |
bool* threw) {
|
529 |
USE(ContainsOnlyValidKeys); |
530 |
Isolate* isolate = object->GetIsolate(); |
531 |
Handle<FixedArray> content = isolate->factory()->empty_fixed_array(); |
532 |
Handle<JSObject> arguments_boilerplate = Handle<JSObject>( |
533 |
isolate->context()->native_context()->arguments_boilerplate(), |
534 |
isolate); |
535 |
Handle<JSFunction> arguments_function = Handle<JSFunction>( |
536 |
JSFunction::cast(arguments_boilerplate->map()->constructor()), |
537 |
isolate); |
538 |
|
539 |
// Only collect keys if access is permitted.
|
540 |
for (Handle<Object> p = object;
|
541 |
*p != isolate->heap()->null_value(); |
542 |
p = Handle<Object>(p->GetPrototype(isolate), isolate)) { |
543 |
if (p->IsJSProxy()) {
|
544 |
Handle<JSProxy> proxy(JSProxy::cast(*p), isolate); |
545 |
Handle<Object> args[] = { proxy }; |
546 |
Handle<Object> names = Execution::Call(isolate, |
547 |
isolate->proxy_enumerate(), |
548 |
object, |
549 |
ARRAY_SIZE(args), |
550 |
args, |
551 |
threw); |
552 |
if (*threw) return content; |
553 |
content = AddKeysFromJSArray(content, Handle<JSArray>::cast(names)); |
554 |
break;
|
555 |
} |
556 |
|
557 |
Handle<JSObject> current(JSObject::cast(*p), isolate); |
558 |
|
559 |
// Check access rights if required.
|
560 |
if (current->IsAccessCheckNeeded() &&
|
561 |
!isolate->MayNamedAccess(*current, |
562 |
isolate->heap()->undefined_value(), |
563 |
v8::ACCESS_KEYS)) { |
564 |
isolate->ReportFailedAccessCheck(*current, v8::ACCESS_KEYS); |
565 |
if (isolate->has_scheduled_exception()) {
|
566 |
isolate->PromoteScheduledException(); |
567 |
*threw = true;
|
568 |
} |
569 |
break;
|
570 |
} |
571 |
|
572 |
// Compute the element keys.
|
573 |
Handle<FixedArray> element_keys = |
574 |
isolate->factory()->NewFixedArray(current->NumberOfEnumElements()); |
575 |
current->GetEnumElementKeys(*element_keys); |
576 |
content = UnionOfKeys(content, element_keys); |
577 |
ASSERT(ContainsOnlyValidKeys(content)); |
578 |
|
579 |
// Add the element keys from the interceptor.
|
580 |
if (current->HasIndexedInterceptor()) {
|
581 |
v8::Handle<v8::Array> result = |
582 |
GetKeysForIndexedInterceptor(object, current); |
583 |
if (!result.IsEmpty())
|
584 |
content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result)); |
585 |
ASSERT(ContainsOnlyValidKeys(content)); |
586 |
} |
587 |
|
588 |
// We can cache the computed property keys if access checks are
|
589 |
// not needed and no interceptors are involved.
|
590 |
//
|
591 |
// We do not use the cache if the object has elements and
|
592 |
// therefore it does not make sense to cache the property names
|
593 |
// for arguments objects. Arguments objects will always have
|
594 |
// elements.
|
595 |
// Wrapped strings have elements, but don't have an elements
|
596 |
// array or dictionary. So the fast inline test for whether to
|
597 |
// use the cache says yes, so we should not create a cache.
|
598 |
bool cache_enum_keys =
|
599 |
((current->map()->constructor() != *arguments_function) && |
600 |
!current->IsJSValue() && |
601 |
!current->IsAccessCheckNeeded() && |
602 |
!current->HasNamedInterceptor() && |
603 |
!current->HasIndexedInterceptor()); |
604 |
// Compute the property keys and cache them if possible.
|
605 |
content = |
606 |
UnionOfKeys(content, GetEnumPropertyKeys(current, cache_enum_keys)); |
607 |
ASSERT(ContainsOnlyValidKeys(content)); |
608 |
|
609 |
// Add the property keys from the interceptor.
|
610 |
if (current->HasNamedInterceptor()) {
|
611 |
v8::Handle<v8::Array> result = |
612 |
GetKeysForNamedInterceptor(object, current); |
613 |
if (!result.IsEmpty())
|
614 |
content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result)); |
615 |
ASSERT(ContainsOnlyValidKeys(content)); |
616 |
} |
617 |
|
618 |
// If we only want local properties we bail out after the first
|
619 |
// iteration.
|
620 |
if (type == LOCAL_ONLY)
|
621 |
break;
|
622 |
} |
623 |
return content;
|
624 |
} |
625 |
|
626 |
|
627 |
Handle<JSArray> GetKeysFor(Handle<JSReceiver> object, bool* threw) {
|
628 |
Isolate* isolate = object->GetIsolate(); |
629 |
isolate->counters()->for_in()->Increment(); |
630 |
Handle<FixedArray> elements = |
631 |
GetKeysInFixedArrayFor(object, INCLUDE_PROTOS, threw); |
632 |
return isolate->factory()->NewJSArrayWithElements(elements);
|
633 |
} |
634 |
|
635 |
|
636 |
Handle<FixedArray> ReduceFixedArrayTo(Handle<FixedArray> array, int length) {
|
637 |
ASSERT(array->length() >= length); |
638 |
if (array->length() == length) return array; |
639 |
|
640 |
Handle<FixedArray> new_array = |
641 |
array->GetIsolate()->factory()->NewFixedArray(length); |
642 |
for (int i = 0; i < length; ++i) new_array->set(i, array->get(i)); |
643 |
return new_array;
|
644 |
} |
645 |
|
646 |
|
647 |
Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object, |
648 |
bool cache_result) {
|
649 |
Isolate* isolate = object->GetIsolate(); |
650 |
if (object->HasFastProperties()) {
|
651 |
if (object->map()->instance_descriptors()->HasEnumCache()) {
|
652 |
int own_property_count = object->map()->EnumLength();
|
653 |
// If we have an enum cache, but the enum length of the given map is set
|
654 |
// to kInvalidEnumCache, this means that the map itself has never used the
|
655 |
// present enum cache. The first step to using the cache is to set the
|
656 |
// enum length of the map by counting the number of own descriptors that
|
657 |
// are not DONT_ENUM or SYMBOLIC.
|
658 |
if (own_property_count == Map::kInvalidEnumCache) {
|
659 |
own_property_count = object->map()->NumberOfDescribedProperties( |
660 |
OWN_DESCRIPTORS, DONT_SHOW); |
661 |
|
662 |
if (cache_result) object->map()->SetEnumLength(own_property_count);
|
663 |
} |
664 |
|
665 |
DescriptorArray* desc = object->map()->instance_descriptors(); |
666 |
Handle<FixedArray> keys(desc->GetEnumCache(), isolate); |
667 |
|
668 |
// In case the number of properties required in the enum are actually
|
669 |
// present, we can reuse the enum cache. Otherwise, this means that the
|
670 |
// enum cache was generated for a previous (smaller) version of the
|
671 |
// Descriptor Array. In that case we regenerate the enum cache.
|
672 |
if (own_property_count <= keys->length()) {
|
673 |
isolate->counters()->enum_cache_hits()->Increment(); |
674 |
return ReduceFixedArrayTo(keys, own_property_count);
|
675 |
} |
676 |
} |
677 |
|
678 |
Handle<Map> map(object->map()); |
679 |
|
680 |
if (map->instance_descriptors()->IsEmpty()) {
|
681 |
isolate->counters()->enum_cache_hits()->Increment(); |
682 |
if (cache_result) map->SetEnumLength(0); |
683 |
return isolate->factory()->empty_fixed_array();
|
684 |
} |
685 |
|
686 |
isolate->counters()->enum_cache_misses()->Increment(); |
687 |
int num_enum = map->NumberOfDescribedProperties(ALL_DESCRIPTORS, DONT_SHOW);
|
688 |
|
689 |
Handle<FixedArray> storage = isolate->factory()->NewFixedArray(num_enum); |
690 |
Handle<FixedArray> indices = isolate->factory()->NewFixedArray(num_enum); |
691 |
|
692 |
Handle<DescriptorArray> descs = |
693 |
Handle<DescriptorArray>(object->map()->instance_descriptors(), isolate); |
694 |
|
695 |
int real_size = map->NumberOfOwnDescriptors();
|
696 |
int enum_size = 0; |
697 |
int index = 0; |
698 |
|
699 |
for (int i = 0; i < descs->number_of_descriptors(); i++) { |
700 |
PropertyDetails details = descs->GetDetails(i); |
701 |
Object* key = descs->GetKey(i); |
702 |
if (!(details.IsDontEnum() || key->IsSymbol())) {
|
703 |
if (i < real_size) ++enum_size;
|
704 |
storage->set(index, key); |
705 |
if (!indices.is_null()) {
|
706 |
if (details.type() != FIELD) {
|
707 |
indices = Handle<FixedArray>(); |
708 |
} else {
|
709 |
int field_index = descs->GetFieldIndex(i);
|
710 |
if (field_index >= map->inobject_properties()) {
|
711 |
field_index = -(field_index - map->inobject_properties() + 1);
|
712 |
} |
713 |
indices->set(index, Smi::FromInt(field_index)); |
714 |
} |
715 |
} |
716 |
index++; |
717 |
} |
718 |
} |
719 |
ASSERT(index == storage->length()); |
720 |
|
721 |
Handle<FixedArray> bridge_storage = |
722 |
isolate->factory()->NewFixedArray( |
723 |
DescriptorArray::kEnumCacheBridgeLength); |
724 |
DescriptorArray* desc = object->map()->instance_descriptors(); |
725 |
desc->SetEnumCache(*bridge_storage, |
726 |
*storage, |
727 |
indices.is_null() ? Object::cast(Smi::FromInt(0))
|
728 |
: Object::cast(*indices)); |
729 |
if (cache_result) {
|
730 |
object->map()->SetEnumLength(enum_size); |
731 |
} |
732 |
|
733 |
return ReduceFixedArrayTo(storage, enum_size);
|
734 |
} else {
|
735 |
Handle<NameDictionary> dictionary(object->property_dictionary()); |
736 |
|
737 |
int length = dictionary->NumberOfElements();
|
738 |
if (length == 0) { |
739 |
return Handle<FixedArray>(isolate->heap()->empty_fixed_array());
|
740 |
} |
741 |
|
742 |
// The enumeration array is generated by allocating an array big enough to
|
743 |
// hold all properties that have been seen, whether they are are deleted or
|
744 |
// not. Subsequently all visible properties are added to the array. If some
|
745 |
// properties were not visible, the array is trimmed so it only contains
|
746 |
// visible properties. This improves over adding elements and sorting by
|
747 |
// index by having linear complexity rather than n*log(n).
|
748 |
|
749 |
// By comparing the monotonous NextEnumerationIndex to the NumberOfElements,
|
750 |
// we can predict the number of holes in the final array. If there will be
|
751 |
// more than 50% holes, regenerate the enumeration indices to reduce the
|
752 |
// number of holes to a minimum. This avoids allocating a large array if
|
753 |
// many properties were added but subsequently deleted.
|
754 |
int next_enumeration = dictionary->NextEnumerationIndex();
|
755 |
if (!object->IsGlobalObject() && next_enumeration > (length * 3) / 2) { |
756 |
NameDictionary::DoGenerateNewEnumerationIndices(dictionary); |
757 |
next_enumeration = dictionary->NextEnumerationIndex(); |
758 |
} |
759 |
|
760 |
Handle<FixedArray> storage = |
761 |
isolate->factory()->NewFixedArray(next_enumeration); |
762 |
|
763 |
storage = Handle<FixedArray>(dictionary->CopyEnumKeysTo(*storage)); |
764 |
ASSERT(storage->length() == object->NumberOfLocalProperties(DONT_SHOW)); |
765 |
return storage;
|
766 |
} |
767 |
} |
768 |
|
769 |
|
770 |
Handle<ObjectHashSet> ObjectHashSetAdd(Handle<ObjectHashSet> table, |
771 |
Handle<Object> key) { |
772 |
CALL_HEAP_FUNCTION(table->GetIsolate(), |
773 |
table->Add(*key), |
774 |
ObjectHashSet); |
775 |
} |
776 |
|
777 |
|
778 |
Handle<ObjectHashSet> ObjectHashSetRemove(Handle<ObjectHashSet> table, |
779 |
Handle<Object> key) { |
780 |
CALL_HEAP_FUNCTION(table->GetIsolate(), |
781 |
table->Remove(*key), |
782 |
ObjectHashSet); |
783 |
} |
784 |
|
785 |
|
786 |
Handle<ObjectHashTable> PutIntoObjectHashTable(Handle<ObjectHashTable> table, |
787 |
Handle<Object> key, |
788 |
Handle<Object> value) { |
789 |
CALL_HEAP_FUNCTION(table->GetIsolate(), |
790 |
table->Put(*key, *value), |
791 |
ObjectHashTable); |
792 |
} |
793 |
|
794 |
|
795 |
DeferredHandleScope::DeferredHandleScope(Isolate* isolate) |
796 |
: impl_(isolate->handle_scope_implementer()) { |
797 |
impl_->BeginDeferredScope(); |
798 |
v8::ImplementationUtilities::HandleScopeData* data = |
799 |
impl_->isolate()->handle_scope_data(); |
800 |
Object** new_next = impl_->GetSpareOrNewBlock(); |
801 |
Object** new_limit = &new_next[kHandleBlockSize]; |
802 |
ASSERT(data->limit == &impl_->blocks()->last()[kHandleBlockSize]); |
803 |
impl_->blocks()->Add(new_next); |
804 |
|
805 |
#ifdef DEBUG
|
806 |
prev_level_ = data->level; |
807 |
#endif
|
808 |
data->level++; |
809 |
prev_limit_ = data->limit; |
810 |
prev_next_ = data->next; |
811 |
data->next = new_next; |
812 |
data->limit = new_limit; |
813 |
} |
814 |
|
815 |
|
816 |
DeferredHandleScope::~DeferredHandleScope() { |
817 |
impl_->isolate()->handle_scope_data()->level--; |
818 |
ASSERT(handles_detached_); |
819 |
ASSERT(impl_->isolate()->handle_scope_data()->level == prev_level_); |
820 |
} |
821 |
|
822 |
|
823 |
DeferredHandles* DeferredHandleScope::Detach() { |
824 |
DeferredHandles* deferred = impl_->Detach(prev_limit_); |
825 |
v8::ImplementationUtilities::HandleScopeData* data = |
826 |
impl_->isolate()->handle_scope_data(); |
827 |
data->next = prev_next_; |
828 |
data->limit = prev_limit_; |
829 |
#ifdef DEBUG
|
830 |
handles_detached_ = true;
|
831 |
#endif
|
832 |
return deferred;
|
833 |
} |
834 |
|
835 |
|
836 |
void AddWeakObjectToCodeDependency(Heap* heap,
|
837 |
Handle<Object> object, |
838 |
Handle<Code> code) { |
839 |
heap->EnsureWeakObjectToCodeTable(); |
840 |
Handle<DependentCode> dep(heap->LookupWeakObjectToCodeDependency(*object)); |
841 |
dep = DependentCode::Insert(dep, DependentCode::kWeaklyEmbeddedGroup, code); |
842 |
CALL_HEAP_FUNCTION_VOID(heap->isolate(), |
843 |
heap->AddWeakObjectToCodeDependency(*object, *dep)); |
844 |
} |
845 |
|
846 |
|
847 |
} } // namespace v8::internal
|