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