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