1 | // Copyright 2012 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/accessors.h" |
6 | |
7 | #include "src/api-inl.h" |
8 | #include "src/contexts.h" |
9 | #include "src/counters.h" |
10 | #include "src/deoptimizer.h" |
11 | #include "src/execution.h" |
12 | #include "src/field-index-inl.h" |
13 | #include "src/frames-inl.h" |
14 | #include "src/heap/factory.h" |
15 | #include "src/isolate-inl.h" |
16 | #include "src/messages.h" |
17 | #include "src/objects/api-callbacks.h" |
18 | #include "src/objects/js-array-inl.h" |
19 | #include "src/objects/module-inl.h" |
20 | #include "src/property-details.h" |
21 | #include "src/prototype.h" |
22 | |
23 | namespace v8 { |
24 | namespace internal { |
25 | |
26 | Handle<AccessorInfo> Accessors::MakeAccessor( |
27 | Isolate* isolate, Handle<Name> name, AccessorNameGetterCallback getter, |
28 | AccessorNameBooleanSetterCallback setter) { |
29 | Factory* factory = isolate->factory(); |
30 | Handle<AccessorInfo> info = factory->NewAccessorInfo(); |
31 | info->set_all_can_read(false); |
32 | info->set_all_can_write(false); |
33 | info->set_is_special_data_property(true); |
34 | info->set_is_sloppy(false); |
35 | info->set_replace_on_access(false); |
36 | info->set_getter_side_effect_type(SideEffectType::kHasSideEffect); |
37 | info->set_setter_side_effect_type(SideEffectType::kHasSideEffect); |
38 | name = factory->InternalizeName(name); |
39 | info->set_name(*name); |
40 | Handle<Object> get = v8::FromCData(isolate, getter); |
41 | if (setter == nullptr) setter = &ReconfigureToDataProperty; |
42 | Handle<Object> set = v8::FromCData(isolate, setter); |
43 | info->set_getter(*get); |
44 | info->set_setter(*set); |
45 | Address redirected = info->redirected_getter(); |
46 | if (redirected != kNullAddress) { |
47 | Handle<Object> js_get = v8::FromCData(isolate, redirected); |
48 | info->set_js_getter(*js_get); |
49 | } |
50 | return info; |
51 | } |
52 | |
53 | static V8_INLINE bool CheckForName(Isolate* isolate, Handle<Name> name, |
54 | Handle<String> property_name, int offset, |
55 | FieldIndex::Encoding encoding, |
56 | FieldIndex* index) { |
57 | if (Name::Equals(isolate, name, property_name)) { |
58 | *index = FieldIndex::ForInObjectOffset(offset, encoding); |
59 | return true; |
60 | } |
61 | return false; |
62 | } |
63 | |
64 | |
65 | // Returns true for properties that are accessors to object fields. |
66 | // If true, *object_offset contains offset of object field. |
67 | bool Accessors::IsJSObjectFieldAccessor(Isolate* isolate, Handle<Map> map, |
68 | Handle<Name> name, FieldIndex* index) { |
69 | switch (map->instance_type()) { |
70 | case JS_ARRAY_TYPE: |
71 | return CheckForName(isolate, name, isolate->factory()->length_string(), |
72 | JSArray::kLengthOffset, FieldIndex::kTagged, index); |
73 | default: |
74 | if (map->instance_type() < FIRST_NONSTRING_TYPE) { |
75 | return CheckForName(isolate, name, isolate->factory()->length_string(), |
76 | String::kLengthOffset, FieldIndex::kWord32, index); |
77 | } |
78 | |
79 | return false; |
80 | } |
81 | } |
82 | |
83 | V8_WARN_UNUSED_RESULT MaybeHandle<Object> |
84 | Accessors::ReplaceAccessorWithDataProperty(Handle<Object> receiver, |
85 | Handle<JSObject> holder, |
86 | Handle<Name> name, |
87 | Handle<Object> value) { |
88 | LookupIterator it(receiver, name, holder, |
89 | LookupIterator::OWN_SKIP_INTERCEPTOR); |
90 | // Skip any access checks we might hit. This accessor should never hit in a |
91 | // situation where the caller does not have access. |
92 | if (it.state() == LookupIterator::ACCESS_CHECK) { |
93 | CHECK(it.HasAccess()); |
94 | it.Next(); |
95 | } |
96 | DCHECK(holder.is_identical_to(it.GetHolder<JSObject>())); |
97 | CHECK_EQ(LookupIterator::ACCESSOR, it.state()); |
98 | it.ReconfigureDataProperty(value, it.property_attributes()); |
99 | return value; |
100 | } |
101 | |
102 | |
103 | // |
104 | // Accessors::ReconfigureToDataProperty |
105 | // |
106 | void Accessors::ReconfigureToDataProperty( |
107 | v8::Local<v8::Name> key, v8::Local<v8::Value> val, |
108 | const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
109 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
110 | RuntimeCallTimerScope stats_scope( |
111 | isolate, RuntimeCallCounterId::kReconfigureToDataProperty); |
112 | HandleScope scope(isolate); |
113 | Handle<Object> receiver = Utils::OpenHandle(*info.This()); |
114 | Handle<JSObject> holder = |
115 | Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder())); |
116 | Handle<Name> name = Utils::OpenHandle(*key); |
117 | Handle<Object> value = Utils::OpenHandle(*val); |
118 | MaybeHandle<Object> result = |
119 | Accessors::ReplaceAccessorWithDataProperty(receiver, holder, name, value); |
120 | if (result.is_null()) { |
121 | isolate->OptionalRescheduleException(false); |
122 | } else { |
123 | info.GetReturnValue().Set(true); |
124 | } |
125 | } |
126 | |
127 | |
128 | // |
129 | // Accessors::ArgumentsIterator |
130 | // |
131 | |
132 | |
133 | void Accessors::ArgumentsIteratorGetter( |
134 | v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
135 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
136 | DisallowHeapAllocation no_allocation; |
137 | HandleScope scope(isolate); |
138 | Object result = isolate->native_context()->array_values_iterator(); |
139 | info.GetReturnValue().Set(Utils::ToLocal(Handle<Object>(result, isolate))); |
140 | } |
141 | |
142 | Handle<AccessorInfo> Accessors::MakeArgumentsIteratorInfo(Isolate* isolate) { |
143 | Handle<Name> name = isolate->factory()->iterator_symbol(); |
144 | return MakeAccessor(isolate, name, &ArgumentsIteratorGetter, nullptr); |
145 | } |
146 | |
147 | |
148 | // |
149 | // Accessors::ArrayLength |
150 | // |
151 | |
152 | |
153 | void Accessors::ArrayLengthGetter( |
154 | v8::Local<v8::Name> name, |
155 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
156 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
157 | RuntimeCallTimerScope timer(isolate, |
158 | RuntimeCallCounterId::kArrayLengthGetter); |
159 | DisallowHeapAllocation no_allocation; |
160 | HandleScope scope(isolate); |
161 | JSArray holder = JSArray::cast(*Utils::OpenHandle(*info.Holder())); |
162 | Object result = holder->length(); |
163 | info.GetReturnValue().Set(Utils::ToLocal(Handle<Object>(result, isolate))); |
164 | } |
165 | |
166 | void Accessors::ArrayLengthSetter( |
167 | v8::Local<v8::Name> name, v8::Local<v8::Value> val, |
168 | const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
169 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
170 | RuntimeCallTimerScope timer(isolate, |
171 | RuntimeCallCounterId::kArrayLengthSetter); |
172 | HandleScope scope(isolate); |
173 | |
174 | DCHECK(Utils::OpenHandle(*name)->SameValue( |
175 | ReadOnlyRoots(isolate).length_string())); |
176 | |
177 | Handle<JSReceiver> object = Utils::OpenHandle(*info.Holder()); |
178 | Handle<JSArray> array = Handle<JSArray>::cast(object); |
179 | Handle<Object> length_obj = Utils::OpenHandle(*val); |
180 | |
181 | bool was_readonly = JSArray::HasReadOnlyLength(array); |
182 | |
183 | uint32_t length = 0; |
184 | if (!JSArray::AnythingToArrayLength(isolate, length_obj, &length)) { |
185 | isolate->OptionalRescheduleException(false); |
186 | return; |
187 | } |
188 | |
189 | if (!was_readonly && V8_UNLIKELY(JSArray::HasReadOnlyLength(array)) && |
190 | length != array->length()->Number()) { |
191 | // AnythingToArrayLength() may have called setter re-entrantly and modified |
192 | // its property descriptor. Don't perform this check if "length" was |
193 | // previously readonly, as this may have been called during |
194 | // DefineOwnPropertyIgnoreAttributes(). |
195 | if (info.ShouldThrowOnError()) { |
196 | Factory* factory = isolate->factory(); |
197 | isolate->Throw(*factory->NewTypeError( |
198 | MessageTemplate::kStrictReadOnlyProperty, Utils::OpenHandle(*name), |
199 | i::Object::TypeOf(isolate, object), object)); |
200 | isolate->OptionalRescheduleException(false); |
201 | } else { |
202 | info.GetReturnValue().Set(false); |
203 | } |
204 | return; |
205 | } |
206 | |
207 | JSArray::SetLength(array, length); |
208 | |
209 | uint32_t actual_new_len = 0; |
210 | CHECK(array->length()->ToArrayLength(&actual_new_len)); |
211 | // Fail if there were non-deletable elements. |
212 | if (actual_new_len != length) { |
213 | if (info.ShouldThrowOnError()) { |
214 | Factory* factory = isolate->factory(); |
215 | isolate->Throw(*factory->NewTypeError( |
216 | MessageTemplate::kStrictDeleteProperty, |
217 | factory->NewNumberFromUint(actual_new_len - 1), array)); |
218 | isolate->OptionalRescheduleException(false); |
219 | } else { |
220 | info.GetReturnValue().Set(false); |
221 | } |
222 | } else { |
223 | info.GetReturnValue().Set(true); |
224 | } |
225 | } |
226 | |
227 | Handle<AccessorInfo> Accessors::MakeArrayLengthInfo(Isolate* isolate) { |
228 | return MakeAccessor(isolate, isolate->factory()->length_string(), |
229 | &ArrayLengthGetter, &ArrayLengthSetter); |
230 | } |
231 | |
232 | // |
233 | // Accessors::ModuleNamespaceEntry |
234 | // |
235 | |
236 | void Accessors::ModuleNamespaceEntryGetter( |
237 | v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
238 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
239 | HandleScope scope(isolate); |
240 | JSModuleNamespace holder = |
241 | JSModuleNamespace::cast(*Utils::OpenHandle(*info.Holder())); |
242 | Handle<Object> result; |
243 | if (!holder |
244 | ->GetExport(isolate, Handle<String>::cast(Utils::OpenHandle(*name))) |
245 | .ToHandle(&result)) { |
246 | isolate->OptionalRescheduleException(false); |
247 | } else { |
248 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
249 | } |
250 | } |
251 | |
252 | void Accessors::ModuleNamespaceEntrySetter( |
253 | v8::Local<v8::Name> name, v8::Local<v8::Value> val, |
254 | const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
255 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
256 | HandleScope scope(isolate); |
257 | Factory* factory = isolate->factory(); |
258 | Handle<JSModuleNamespace> holder = |
259 | Handle<JSModuleNamespace>::cast(Utils::OpenHandle(*info.Holder())); |
260 | |
261 | if (info.ShouldThrowOnError()) { |
262 | isolate->Throw(*factory->NewTypeError( |
263 | MessageTemplate::kStrictReadOnlyProperty, Utils::OpenHandle(*name), |
264 | i::Object::TypeOf(isolate, holder), holder)); |
265 | isolate->OptionalRescheduleException(false); |
266 | } else { |
267 | info.GetReturnValue().Set(false); |
268 | } |
269 | } |
270 | |
271 | Handle<AccessorInfo> Accessors::MakeModuleNamespaceEntryInfo( |
272 | Isolate* isolate, Handle<String> name) { |
273 | return MakeAccessor(isolate, name, &ModuleNamespaceEntryGetter, |
274 | &ModuleNamespaceEntrySetter); |
275 | } |
276 | |
277 | |
278 | // |
279 | // Accessors::StringLength |
280 | // |
281 | |
282 | void Accessors::StringLengthGetter( |
283 | v8::Local<v8::Name> name, |
284 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
285 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
286 | RuntimeCallTimerScope timer(isolate, |
287 | RuntimeCallCounterId::kStringLengthGetter); |
288 | DisallowHeapAllocation no_allocation; |
289 | HandleScope scope(isolate); |
290 | |
291 | // We have a slight impedance mismatch between the external API and the way we |
292 | // use callbacks internally: Externally, callbacks can only be used with |
293 | // v8::Object, but internally we have callbacks on entities which are higher |
294 | // in the hierarchy, in this case for String values. |
295 | |
296 | Object value = *Utils::OpenHandle(*v8::Local<v8::Value>(info.This())); |
297 | if (!value->IsString()) { |
298 | // Not a string value. That means that we either got a String wrapper or |
299 | // a Value with a String wrapper in its prototype chain. |
300 | value = JSValue::cast(*Utils::OpenHandle(*info.Holder()))->value(); |
301 | } |
302 | Object result = Smi::FromInt(String::cast(value)->length()); |
303 | info.GetReturnValue().Set(Utils::ToLocal(Handle<Object>(result, isolate))); |
304 | } |
305 | |
306 | Handle<AccessorInfo> Accessors::MakeStringLengthInfo(Isolate* isolate) { |
307 | return MakeAccessor(isolate, isolate->factory()->length_string(), |
308 | &StringLengthGetter, nullptr); |
309 | } |
310 | |
311 | // |
312 | // Accessors::FunctionPrototype |
313 | // |
314 | |
315 | static Handle<Object> GetFunctionPrototype(Isolate* isolate, |
316 | Handle<JSFunction> function) { |
317 | if (!function->has_prototype()) { |
318 | Handle<Object> proto = isolate->factory()->NewFunctionPrototype(function); |
319 | JSFunction::SetPrototype(function, proto); |
320 | } |
321 | return Handle<Object>(function->prototype(), isolate); |
322 | } |
323 | |
324 | void Accessors::FunctionPrototypeGetter( |
325 | v8::Local<v8::Name> name, |
326 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
327 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
328 | RuntimeCallTimerScope timer(isolate, |
329 | RuntimeCallCounterId::kFunctionPrototypeGetter); |
330 | HandleScope scope(isolate); |
331 | Handle<JSFunction> function = |
332 | Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder())); |
333 | DCHECK(function->has_prototype_property()); |
334 | Handle<Object> result = GetFunctionPrototype(isolate, function); |
335 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
336 | } |
337 | |
338 | void Accessors::FunctionPrototypeSetter( |
339 | v8::Local<v8::Name> name, v8::Local<v8::Value> val, |
340 | const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
341 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
342 | RuntimeCallTimerScope timer(isolate, |
343 | RuntimeCallCounterId::kFunctionPrototypeSetter); |
344 | HandleScope scope(isolate); |
345 | Handle<Object> value = Utils::OpenHandle(*val); |
346 | Handle<JSFunction> object = |
347 | Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder())); |
348 | DCHECK(object->has_prototype_property()); |
349 | JSFunction::SetPrototype(object, value); |
350 | info.GetReturnValue().Set(true); |
351 | } |
352 | |
353 | Handle<AccessorInfo> Accessors::MakeFunctionPrototypeInfo(Isolate* isolate) { |
354 | return MakeAccessor(isolate, isolate->factory()->prototype_string(), |
355 | &FunctionPrototypeGetter, &FunctionPrototypeSetter); |
356 | } |
357 | |
358 | |
359 | // |
360 | // Accessors::FunctionLength |
361 | // |
362 | |
363 | |
364 | void Accessors::FunctionLengthGetter( |
365 | v8::Local<v8::Name> name, |
366 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
367 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
368 | RuntimeCallTimerScope timer(isolate, |
369 | RuntimeCallCounterId::kFunctionLengthGetter); |
370 | HandleScope scope(isolate); |
371 | Handle<JSFunction> function = |
372 | Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder())); |
373 | int length = function->length(); |
374 | Handle<Object> result(Smi::FromInt(length), isolate); |
375 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
376 | } |
377 | |
378 | Handle<AccessorInfo> Accessors::MakeFunctionLengthInfo(Isolate* isolate) { |
379 | return MakeAccessor(isolate, isolate->factory()->length_string(), |
380 | &FunctionLengthGetter, &ReconfigureToDataProperty); |
381 | } |
382 | |
383 | |
384 | // |
385 | // Accessors::FunctionName |
386 | // |
387 | |
388 | |
389 | void Accessors::FunctionNameGetter( |
390 | v8::Local<v8::Name> name, |
391 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
392 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
393 | HandleScope scope(isolate); |
394 | Handle<JSFunction> function = |
395 | Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder())); |
396 | Handle<Object> result = JSFunction::GetName(isolate, function); |
397 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
398 | } |
399 | |
400 | Handle<AccessorInfo> Accessors::MakeFunctionNameInfo(Isolate* isolate) { |
401 | return MakeAccessor(isolate, isolate->factory()->name_string(), |
402 | &FunctionNameGetter, &ReconfigureToDataProperty); |
403 | } |
404 | |
405 | |
406 | // |
407 | // Accessors::FunctionArguments |
408 | // |
409 | |
410 | namespace { |
411 | |
412 | Handle<JSObject> ArgumentsForInlinedFunction(JavaScriptFrame* frame, |
413 | int inlined_frame_index) { |
414 | Isolate* isolate = frame->isolate(); |
415 | Factory* factory = isolate->factory(); |
416 | |
417 | TranslatedState translated_values(frame); |
418 | translated_values.Prepare(frame->fp()); |
419 | |
420 | int argument_count = 0; |
421 | TranslatedFrame* translated_frame = |
422 | translated_values.GetArgumentsInfoFromJSFrameIndex(inlined_frame_index, |
423 | &argument_count); |
424 | TranslatedFrame::iterator iter = translated_frame->begin(); |
425 | |
426 | // Materialize the function. |
427 | bool should_deoptimize = iter->IsMaterializedObject(); |
428 | Handle<JSFunction> function = Handle<JSFunction>::cast(iter->GetValue()); |
429 | iter++; |
430 | |
431 | // Skip the receiver. |
432 | iter++; |
433 | argument_count--; |
434 | |
435 | Handle<JSObject> arguments = |
436 | factory->NewArgumentsObject(function, argument_count); |
437 | Handle<FixedArray> array = factory->NewFixedArray(argument_count); |
438 | for (int i = 0; i < argument_count; ++i) { |
439 | // If we materialize any object, we should deoptimize the frame because we |
440 | // might alias an object that was eliminated by escape analysis. |
441 | should_deoptimize = should_deoptimize || iter->IsMaterializedObject(); |
442 | Handle<Object> value = iter->GetValue(); |
443 | array->set(i, *value); |
444 | iter++; |
445 | } |
446 | arguments->set_elements(*array); |
447 | |
448 | if (should_deoptimize) { |
449 | translated_values.StoreMaterializedValuesAndDeopt(frame); |
450 | } |
451 | |
452 | // Return the freshly allocated arguments object. |
453 | return arguments; |
454 | } |
455 | |
456 | int FindFunctionInFrame(JavaScriptFrame* frame, Handle<JSFunction> function) { |
457 | std::vector<FrameSummary> frames; |
458 | frame->Summarize(&frames); |
459 | for (size_t i = frames.size(); i != 0; i--) { |
460 | if (*frames[i - 1].AsJavaScript().function() == *function) { |
461 | return static_cast<int>(i) - 1; |
462 | } |
463 | } |
464 | return -1; |
465 | } |
466 | |
467 | Handle<JSObject> GetFrameArguments(Isolate* isolate, |
468 | JavaScriptFrameIterator* it, |
469 | int function_index) { |
470 | JavaScriptFrame* frame = it->frame(); |
471 | |
472 | if (function_index > 0) { |
473 | // The function in question was inlined. Inlined functions have the |
474 | // correct number of arguments and no allocated arguments object, so |
475 | // we can construct a fresh one by interpreting the function's |
476 | // deoptimization input data. |
477 | return ArgumentsForInlinedFunction(frame, function_index); |
478 | } |
479 | |
480 | // Find the frame that holds the actual arguments passed to the function. |
481 | if (it->frame()->has_adapted_arguments()) { |
482 | it->AdvanceOneFrame(); |
483 | DCHECK(it->frame()->is_arguments_adaptor()); |
484 | } |
485 | frame = it->frame(); |
486 | |
487 | // Get the number of arguments and construct an arguments object |
488 | // mirror for the right frame and the underlying function. |
489 | const int length = frame->ComputeParametersCount(); |
490 | Handle<JSFunction> function(frame->function(), isolate); |
491 | Handle<JSObject> arguments = |
492 | isolate->factory()->NewArgumentsObject(function, length); |
493 | Handle<FixedArray> array = isolate->factory()->NewFixedArray(length); |
494 | |
495 | // Copy the parameters to the arguments object. |
496 | DCHECK(array->length() == length); |
497 | for (int i = 0; i < length; i++) { |
498 | Object value = frame->GetParameter(i); |
499 | if (value->IsTheHole(isolate)) { |
500 | // Generators currently use holes as dummy arguments when resuming. We |
501 | // must not leak those. |
502 | DCHECK(IsResumableFunction(function->shared()->kind())); |
503 | value = ReadOnlyRoots(isolate).undefined_value(); |
504 | } |
505 | array->set(i, value); |
506 | } |
507 | arguments->set_elements(*array); |
508 | |
509 | // Return the freshly allocated arguments object. |
510 | return arguments; |
511 | } |
512 | |
513 | } // namespace |
514 | |
515 | Handle<JSObject> Accessors::FunctionGetArguments(JavaScriptFrame* frame, |
516 | int inlined_jsframe_index) { |
517 | Isolate* isolate = frame->isolate(); |
518 | Address requested_frame_fp = frame->fp(); |
519 | // Forward a frame iterator to the requested frame. This is needed because we |
520 | // potentially need for advance it to the arguments adaptor frame later. |
521 | for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) { |
522 | if (it.frame()->fp() != requested_frame_fp) continue; |
523 | return GetFrameArguments(isolate, &it, inlined_jsframe_index); |
524 | } |
525 | UNREACHABLE(); // Requested frame not found. |
526 | return Handle<JSObject>(); |
527 | } |
528 | |
529 | |
530 | void Accessors::FunctionArgumentsGetter( |
531 | v8::Local<v8::Name> name, |
532 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
533 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
534 | HandleScope scope(isolate); |
535 | Handle<JSFunction> function = |
536 | Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder())); |
537 | Handle<Object> result = isolate->factory()->null_value(); |
538 | if (!function->shared()->native()) { |
539 | // Find the top invocation of the function by traversing frames. |
540 | for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) { |
541 | JavaScriptFrame* frame = it.frame(); |
542 | int function_index = FindFunctionInFrame(frame, function); |
543 | if (function_index >= 0) { |
544 | result = GetFrameArguments(isolate, &it, function_index); |
545 | break; |
546 | } |
547 | } |
548 | } |
549 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
550 | } |
551 | |
552 | Handle<AccessorInfo> Accessors::MakeFunctionArgumentsInfo(Isolate* isolate) { |
553 | return MakeAccessor(isolate, isolate->factory()->arguments_string(), |
554 | &FunctionArgumentsGetter, nullptr); |
555 | } |
556 | |
557 | |
558 | // |
559 | // Accessors::FunctionCaller |
560 | // |
561 | |
562 | static inline bool AllowAccessToFunction(Context current_context, |
563 | JSFunction function) { |
564 | return current_context->HasSameSecurityTokenAs(function->context()); |
565 | } |
566 | |
567 | class FrameFunctionIterator { |
568 | public: |
569 | explicit FrameFunctionIterator(Isolate* isolate) |
570 | : isolate_(isolate), frame_iterator_(isolate), inlined_frame_index_(-1) { |
571 | GetFrames(); |
572 | } |
573 | |
574 | // Iterate through functions until the first occurrence of 'function'. |
575 | // Returns true if one is found, and false if the iterator ends before. |
576 | bool Find(Handle<JSFunction> function) { |
577 | do { |
578 | if (!next().ToHandle(&function_)) return false; |
579 | } while (!function_.is_identical_to(function)); |
580 | return true; |
581 | } |
582 | |
583 | // Iterate through functions until the next non-toplevel one is found. |
584 | // Returns true if one is found, and false if the iterator ends before. |
585 | bool FindNextNonTopLevel() { |
586 | do { |
587 | if (!next().ToHandle(&function_)) return false; |
588 | } while (function_->shared()->is_toplevel()); |
589 | return true; |
590 | } |
591 | |
592 | // Iterate through function until the first native or user-provided function |
593 | // is found. Functions not defined in user-provided scripts are not visible |
594 | // unless directly exposed, in which case the native flag is set on them. |
595 | // Returns true if one is found, and false if the iterator ends before. |
596 | bool FindFirstNativeOrUserJavaScript() { |
597 | while (!function_->shared()->native() && |
598 | !function_->shared()->IsUserJavaScript()) { |
599 | if (!next().ToHandle(&function_)) return false; |
600 | } |
601 | return true; |
602 | } |
603 | |
604 | // In case of inlined frames the function could have been materialized from |
605 | // deoptimization information. If that is the case we need to make sure that |
606 | // subsequent call will see the same function, since we are about to hand out |
607 | // the value to JavaScript. Make sure to store the materialized value and |
608 | // trigger a deoptimization of the underlying frame. |
609 | Handle<JSFunction> MaterializeFunction() { |
610 | if (inlined_frame_index_ == 0) return function_; |
611 | |
612 | JavaScriptFrame* frame = frame_iterator_.frame(); |
613 | TranslatedState translated_values(frame); |
614 | translated_values.Prepare(frame->fp()); |
615 | |
616 | TranslatedFrame* translated_frame = |
617 | translated_values.GetFrameFromJSFrameIndex(inlined_frame_index_); |
618 | TranslatedFrame::iterator iter = translated_frame->begin(); |
619 | |
620 | // First value is the function. |
621 | bool should_deoptimize = iter->IsMaterializedObject(); |
622 | Handle<Object> value = iter->GetValue(); |
623 | if (should_deoptimize) { |
624 | translated_values.StoreMaterializedValuesAndDeopt(frame); |
625 | } |
626 | |
627 | return Handle<JSFunction>::cast(value); |
628 | } |
629 | |
630 | private: |
631 | MaybeHandle<JSFunction> next() { |
632 | while (true) { |
633 | if (inlined_frame_index_ <= 0) { |
634 | if (!frame_iterator_.done()) { |
635 | frame_iterator_.Advance(); |
636 | frames_.clear(); |
637 | inlined_frame_index_ = -1; |
638 | GetFrames(); |
639 | } |
640 | if (inlined_frame_index_ == -1) return MaybeHandle<JSFunction>(); |
641 | } |
642 | |
643 | --inlined_frame_index_; |
644 | Handle<JSFunction> next_function = |
645 | frames_[inlined_frame_index_].AsJavaScript().function(); |
646 | // Skip functions from other origins. |
647 | if (!AllowAccessToFunction(isolate_->context(), *next_function)) continue; |
648 | return next_function; |
649 | } |
650 | } |
651 | void GetFrames() { |
652 | DCHECK_EQ(-1, inlined_frame_index_); |
653 | if (frame_iterator_.done()) return; |
654 | JavaScriptFrame* frame = frame_iterator_.frame(); |
655 | frame->Summarize(&frames_); |
656 | inlined_frame_index_ = static_cast<int>(frames_.size()); |
657 | DCHECK_LT(0, inlined_frame_index_); |
658 | } |
659 | Isolate* isolate_; |
660 | Handle<JSFunction> function_; |
661 | JavaScriptFrameIterator frame_iterator_; |
662 | std::vector<FrameSummary> frames_; |
663 | int inlined_frame_index_; |
664 | }; |
665 | |
666 | |
667 | MaybeHandle<JSFunction> FindCaller(Isolate* isolate, |
668 | Handle<JSFunction> function) { |
669 | FrameFunctionIterator it(isolate); |
670 | if (function->shared()->native()) { |
671 | return MaybeHandle<JSFunction>(); |
672 | } |
673 | // Find the function from the frames. Return null in case no frame |
674 | // corresponding to the given function was found. |
675 | if (!it.Find(function)) { |
676 | return MaybeHandle<JSFunction>(); |
677 | } |
678 | // Find previously called non-toplevel function. |
679 | if (!it.FindNextNonTopLevel()) { |
680 | return MaybeHandle<JSFunction>(); |
681 | } |
682 | // Find the first user-land JavaScript function (or the entry point into |
683 | // native JavaScript builtins in case such a builtin was the caller). |
684 | if (!it.FindFirstNativeOrUserJavaScript()) { |
685 | return MaybeHandle<JSFunction>(); |
686 | } |
687 | |
688 | // Materialize the function that the iterator is currently sitting on. Note |
689 | // that this might trigger deoptimization in case the function was actually |
690 | // materialized. Identity of the function must be preserved because we are |
691 | // going to return it to JavaScript after this point. |
692 | Handle<JSFunction> caller = it.MaterializeFunction(); |
693 | |
694 | // Censor if the caller is not a sloppy mode function. |
695 | // Change from ES5, which used to throw, see: |
696 | // https://bugs.ecmascript.org/show_bug.cgi?id=310 |
697 | if (is_strict(caller->shared()->language_mode())) { |
698 | return MaybeHandle<JSFunction>(); |
699 | } |
700 | // Don't return caller from another security context. |
701 | if (!AllowAccessToFunction(isolate->context(), *caller)) { |
702 | return MaybeHandle<JSFunction>(); |
703 | } |
704 | return caller; |
705 | } |
706 | |
707 | |
708 | void Accessors::FunctionCallerGetter( |
709 | v8::Local<v8::Name> name, |
710 | const v8::PropertyCallbackInfo<v8::Value>& info) { |
711 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
712 | HandleScope scope(isolate); |
713 | Handle<JSFunction> function = |
714 | Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder())); |
715 | Handle<Object> result; |
716 | MaybeHandle<JSFunction> maybe_caller; |
717 | maybe_caller = FindCaller(isolate, function); |
718 | Handle<JSFunction> caller; |
719 | if (maybe_caller.ToHandle(&caller)) { |
720 | result = caller; |
721 | } else { |
722 | result = isolate->factory()->null_value(); |
723 | } |
724 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
725 | } |
726 | |
727 | Handle<AccessorInfo> Accessors::MakeFunctionCallerInfo(Isolate* isolate) { |
728 | return MakeAccessor(isolate, isolate->factory()->caller_string(), |
729 | &FunctionCallerGetter, nullptr); |
730 | } |
731 | |
732 | |
733 | // |
734 | // Accessors::BoundFunctionLength |
735 | // |
736 | |
737 | void Accessors::BoundFunctionLengthGetter( |
738 | v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
739 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
740 | RuntimeCallTimerScope timer(isolate, |
741 | RuntimeCallCounterId::kBoundFunctionLengthGetter); |
742 | HandleScope scope(isolate); |
743 | Handle<JSBoundFunction> function = |
744 | Handle<JSBoundFunction>::cast(Utils::OpenHandle(*info.Holder())); |
745 | |
746 | int length = 0; |
747 | if (!JSBoundFunction::GetLength(isolate, function).To(&length)) { |
748 | isolate->OptionalRescheduleException(false); |
749 | return; |
750 | } |
751 | Handle<Object> result(Smi::FromInt(length), isolate); |
752 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
753 | } |
754 | |
755 | Handle<AccessorInfo> Accessors::MakeBoundFunctionLengthInfo(Isolate* isolate) { |
756 | return MakeAccessor(isolate, isolate->factory()->length_string(), |
757 | &BoundFunctionLengthGetter, &ReconfigureToDataProperty); |
758 | } |
759 | |
760 | // |
761 | // Accessors::BoundFunctionName |
762 | // |
763 | |
764 | void Accessors::BoundFunctionNameGetter( |
765 | v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
766 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
767 | RuntimeCallTimerScope timer(isolate, |
768 | RuntimeCallCounterId::kBoundFunctionNameGetter); |
769 | HandleScope scope(isolate); |
770 | Handle<JSBoundFunction> function = |
771 | Handle<JSBoundFunction>::cast(Utils::OpenHandle(*info.Holder())); |
772 | Handle<Object> result; |
773 | if (!JSBoundFunction::GetName(isolate, function).ToHandle(&result)) { |
774 | isolate->OptionalRescheduleException(false); |
775 | return; |
776 | } |
777 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
778 | } |
779 | |
780 | Handle<AccessorInfo> Accessors::MakeBoundFunctionNameInfo(Isolate* isolate) { |
781 | return MakeAccessor(isolate, isolate->factory()->name_string(), |
782 | &BoundFunctionNameGetter, &ReconfigureToDataProperty); |
783 | } |
784 | |
785 | // |
786 | // Accessors::ErrorStack |
787 | // |
788 | |
789 | namespace { |
790 | |
791 | MaybeHandle<JSReceiver> ClearInternalStackTrace(Isolate* isolate, |
792 | Handle<JSObject> error) { |
793 | RETURN_ON_EXCEPTION( |
794 | isolate, |
795 | Object::SetProperty( |
796 | isolate, error, isolate->factory()->stack_trace_symbol(), |
797 | isolate->factory()->undefined_value(), StoreOrigin::kMaybeKeyed, |
798 | Just(ShouldThrow::kThrowOnError)), |
799 | JSReceiver); |
800 | return error; |
801 | } |
802 | |
803 | bool IsAccessor(Handle<Object> receiver, Handle<Name> name, |
804 | Handle<JSObject> holder) { |
805 | LookupIterator it(receiver, name, holder, |
806 | LookupIterator::OWN_SKIP_INTERCEPTOR); |
807 | // Skip any access checks we might hit. This accessor should never hit in a |
808 | // situation where the caller does not have access. |
809 | if (it.state() == LookupIterator::ACCESS_CHECK) { |
810 | CHECK(it.HasAccess()); |
811 | it.Next(); |
812 | } |
813 | return (it.state() == LookupIterator::ACCESSOR); |
814 | } |
815 | |
816 | } // namespace |
817 | |
818 | void Accessors::ErrorStackGetter( |
819 | v8::Local<v8::Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { |
820 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
821 | HandleScope scope(isolate); |
822 | Handle<JSObject> holder = |
823 | Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder())); |
824 | |
825 | // Retrieve the structured stack trace. |
826 | |
827 | Handle<Object> stack_trace; |
828 | Handle<Symbol> stack_trace_symbol = isolate->factory()->stack_trace_symbol(); |
829 | MaybeHandle<Object> maybe_stack_trace = |
830 | JSObject::GetProperty(isolate, holder, stack_trace_symbol); |
831 | if (!maybe_stack_trace.ToHandle(&stack_trace) || |
832 | stack_trace->IsUndefined(isolate)) { |
833 | Handle<Object> result = isolate->factory()->undefined_value(); |
834 | info.GetReturnValue().Set(Utils::ToLocal(result)); |
835 | return; |
836 | } |
837 | |
838 | // Format it, clear the internal structured trace and reconfigure as a data |
839 | // property. |
840 | |
841 | Handle<Object> formatted_stack_trace; |
842 | if (!ErrorUtils::FormatStackTrace(isolate, holder, stack_trace) |
843 | .ToHandle(&formatted_stack_trace)) { |
844 | isolate->OptionalRescheduleException(false); |
845 | return; |
846 | } |
847 | |
848 | MaybeHandle<Object> result = ClearInternalStackTrace(isolate, holder); |
849 | if (result.is_null()) { |
850 | isolate->OptionalRescheduleException(false); |
851 | return; |
852 | } |
853 | |
854 | // If stack is still an accessor (this could have changed in the meantime |
855 | // since FormatStackTrace can execute arbitrary JS), replace it with a data |
856 | // property. |
857 | Handle<Object> receiver = |
858 | Utils::OpenHandle(*v8::Local<v8::Value>(info.This())); |
859 | Handle<Name> name = Utils::OpenHandle(*key); |
860 | if (IsAccessor(receiver, name, holder)) { |
861 | result = Accessors::ReplaceAccessorWithDataProperty(receiver, holder, name, |
862 | formatted_stack_trace); |
863 | if (result.is_null()) { |
864 | isolate->OptionalRescheduleException(false); |
865 | return; |
866 | } |
867 | } else { |
868 | // The stack property has been modified in the meantime. |
869 | if (!JSObject::GetProperty(isolate, holder, name) |
870 | .ToHandle(&formatted_stack_trace)) { |
871 | isolate->OptionalRescheduleException(false); |
872 | return; |
873 | } |
874 | } |
875 | |
876 | v8::Local<v8::Value> value = Utils::ToLocal(formatted_stack_trace); |
877 | info.GetReturnValue().Set(value); |
878 | } |
879 | |
880 | void Accessors::ErrorStackSetter( |
881 | v8::Local<v8::Name> name, v8::Local<v8::Value> val, |
882 | const v8::PropertyCallbackInfo<v8::Boolean>& info) { |
883 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); |
884 | HandleScope scope(isolate); |
885 | Handle<JSObject> obj = Handle<JSObject>::cast( |
886 | Utils::OpenHandle(*v8::Local<v8::Value>(info.This()))); |
887 | |
888 | // Clear internal properties to avoid memory leaks. |
889 | Handle<Symbol> stack_trace_symbol = isolate->factory()->stack_trace_symbol(); |
890 | if (JSReceiver::HasOwnProperty(obj, stack_trace_symbol).FromMaybe(false)) { |
891 | ClearInternalStackTrace(isolate, obj); |
892 | } |
893 | |
894 | Accessors::ReconfigureToDataProperty(name, val, info); |
895 | } |
896 | |
897 | Handle<AccessorInfo> Accessors::MakeErrorStackInfo(Isolate* isolate) { |
898 | return MakeAccessor(isolate, isolate->factory()->stack_string(), |
899 | &ErrorStackGetter, &ErrorStackSetter); |
900 | } |
901 | |
902 | } // namespace internal |
903 | } // namespace v8 |
904 | |