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/debug/debug.h"
9 #include "src/deoptimizer/deoptimizer.h"
10 #include "src/execution/execution.h"
11 #include "src/execution/frames-inl.h"
12 #include "src/execution/isolate-inl.h"
13 #include "src/execution/messages.h"
14 #include "src/heap/factory.h"
15 #include "src/logging/runtime-call-stats-scope.h"
16 #include "src/objects/api-callbacks.h"
17 #include "src/objects/contexts.h"
18 #include "src/objects/field-index-inl.h"
19 #include "src/objects/js-array-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 if (map->is_dictionary_map()) {
70 // There are not descriptors in a dictionary mode map.
71 return false;
72 }
73
74 switch (map->instance_type()) {
75 case JS_ARRAY_TYPE:
76 return CheckForName(isolate, name, isolate->factory()->length_string(),
77 JSArray::kLengthOffset, FieldIndex::kTagged, index);
78 default:
79 if (map->instance_type() < FIRST_NONSTRING_TYPE) {
80 return CheckForName(isolate, name, isolate->factory()->length_string(),
81 String::kLengthOffset, FieldIndex::kWord32, index);
82 }
83
84 return false;
85 }
86 }
87
88 V8_WARN_UNUSED_RESULT MaybeHandle<Object>
ReplaceAccessorWithDataProperty(Isolate * isolate,Handle<Object> receiver,Handle<JSObject> holder,Handle<Name> name,Handle<Object> value)89 Accessors::ReplaceAccessorWithDataProperty(Isolate* isolate,
90 Handle<Object> receiver,
91 Handle<JSObject> holder,
92 Handle<Name> name,
93 Handle<Object> value) {
94 LookupIterator it(isolate, receiver, PropertyKey(isolate, name), holder,
95 LookupIterator::OWN_SKIP_INTERCEPTOR);
96 // Skip any access checks we might hit. This accessor should never hit in a
97 // situation where the caller does not have access.
98 if (it.state() == LookupIterator::ACCESS_CHECK) {
99 CHECK(it.HasAccess());
100 it.Next();
101 }
102 DCHECK(holder.is_identical_to(it.GetHolder<JSObject>()));
103 CHECK_EQ(LookupIterator::ACCESSOR, it.state());
104 it.ReconfigureDataProperty(value, it.property_attributes());
105 return value;
106 }
107
108 //
109 // Accessors::ReconfigureToDataProperty
110 //
ReconfigureToDataProperty(v8::Local<v8::Name> key,v8::Local<v8::Value> val,const v8::PropertyCallbackInfo<v8::Boolean> & info)111 void Accessors::ReconfigureToDataProperty(
112 v8::Local<v8::Name> key, v8::Local<v8::Value> val,
113 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
114 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
115 RCS_SCOPE(isolate, RuntimeCallCounterId::kReconfigureToDataProperty);
116 HandleScope scope(isolate);
117 Handle<Object> receiver = Utils::OpenHandle(*info.This());
118 Handle<JSObject> holder =
119 Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
120 Handle<Name> name = Utils::OpenHandle(*key);
121 Handle<Object> value = Utils::OpenHandle(*val);
122 MaybeHandle<Object> result = Accessors::ReplaceAccessorWithDataProperty(
123 isolate, receiver, holder, name, value);
124 if (result.is_null()) {
125 isolate->OptionalRescheduleException(false);
126 } else {
127 info.GetReturnValue().Set(true);
128 }
129 }
130
131 //
132 // Accessors::ArgumentsIterator
133 //
134
ArgumentsIteratorGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)135 void Accessors::ArgumentsIteratorGetter(
136 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
137 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
138 DisallowGarbageCollection no_gc;
139 HandleScope scope(isolate);
140 Object result = isolate->native_context()->array_values_iterator();
141 info.GetReturnValue().Set(Utils::ToLocal(Handle<Object>(result, isolate)));
142 }
143
MakeArgumentsIteratorInfo(Isolate * isolate)144 Handle<AccessorInfo> Accessors::MakeArgumentsIteratorInfo(Isolate* isolate) {
145 Handle<Name> name = isolate->factory()->iterator_symbol();
146 return MakeAccessor(isolate, name, &ArgumentsIteratorGetter, nullptr);
147 }
148
149 //
150 // Accessors::ArrayLength
151 //
152
ArrayLengthGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)153 void Accessors::ArrayLengthGetter(
154 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
155 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
156 RCS_SCOPE(isolate, RuntimeCallCounterId::kArrayLengthGetter);
157 DisallowGarbageCollection no_gc;
158 HandleScope scope(isolate);
159 JSArray holder = JSArray::cast(*Utils::OpenHandle(*info.Holder()));
160 Object result = holder.length();
161 info.GetReturnValue().Set(Utils::ToLocal(Handle<Object>(result, isolate)));
162 }
163
ArrayLengthSetter(v8::Local<v8::Name> name,v8::Local<v8::Value> val,const v8::PropertyCallbackInfo<v8::Boolean> & info)164 void Accessors::ArrayLengthSetter(
165 v8::Local<v8::Name> name, v8::Local<v8::Value> val,
166 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
167 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
168 RCS_SCOPE(isolate, 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 // AnythingToArrayLength() may have called setter re-entrantly and modified
188 // its property descriptor. Don't perform this check if "length" was
189 // previously readonly, as this may have been called during
190 // DefineOwnPropertyIgnoreAttributes().
191 if (length == array->length().Number()) {
192 info.GetReturnValue().Set(true);
193 } else if (info.ShouldThrowOnError()) {
194 Factory* factory = isolate->factory();
195 isolate->Throw(*factory->NewTypeError(
196 MessageTemplate::kStrictReadOnlyProperty, Utils::OpenHandle(*name),
197 i::Object::TypeOf(isolate, object), object));
198 isolate->OptionalRescheduleException(false);
199 } else {
200 info.GetReturnValue().Set(false);
201 }
202 return;
203 }
204
205 if (JSArray::SetLength(array, length).IsNothing()) {
206 // TODO(victorgomes): AccessorNameBooleanSetterCallback does not handle
207 // exceptions.
208 FATAL("Fatal JavaScript invalid array length %u", length);
209 UNREACHABLE();
210 }
211
212 uint32_t actual_new_len = 0;
213 CHECK(array->length().ToArrayLength(&actual_new_len));
214 // Fail if there were non-deletable elements.
215 if (actual_new_len != length) {
216 if (info.ShouldThrowOnError()) {
217 Factory* factory = isolate->factory();
218 isolate->Throw(*factory->NewTypeError(
219 MessageTemplate::kStrictDeleteProperty,
220 factory->NewNumberFromUint(actual_new_len - 1), array));
221 isolate->OptionalRescheduleException(false);
222 } else {
223 info.GetReturnValue().Set(false);
224 }
225 } else {
226 info.GetReturnValue().Set(true);
227 }
228 }
229
MakeArrayLengthInfo(Isolate * isolate)230 Handle<AccessorInfo> Accessors::MakeArrayLengthInfo(Isolate* isolate) {
231 return MakeAccessor(isolate, isolate->factory()->length_string(),
232 &ArrayLengthGetter, &ArrayLengthSetter);
233 }
234
235 //
236 // Accessors::ModuleNamespaceEntry
237 //
238
ModuleNamespaceEntryGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)239 void Accessors::ModuleNamespaceEntryGetter(
240 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
241 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
242 HandleScope scope(isolate);
243 JSModuleNamespace holder =
244 JSModuleNamespace::cast(*Utils::OpenHandle(*info.Holder()));
245 Handle<Object> result;
246 if (!holder.GetExport(isolate, Handle<String>::cast(Utils::OpenHandle(*name)))
247 .ToHandle(&result)) {
248 isolate->OptionalRescheduleException(false);
249 } else {
250 info.GetReturnValue().Set(Utils::ToLocal(result));
251 }
252 }
253
ModuleNamespaceEntrySetter(v8::Local<v8::Name> name,v8::Local<v8::Value> val,const v8::PropertyCallbackInfo<v8::Boolean> & info)254 void Accessors::ModuleNamespaceEntrySetter(
255 v8::Local<v8::Name> name, v8::Local<v8::Value> val,
256 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
257 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
258 HandleScope scope(isolate);
259 Factory* factory = isolate->factory();
260 Handle<JSModuleNamespace> holder =
261 Handle<JSModuleNamespace>::cast(Utils::OpenHandle(*info.Holder()));
262
263 if (info.ShouldThrowOnError()) {
264 isolate->Throw(*factory->NewTypeError(
265 MessageTemplate::kStrictReadOnlyProperty, Utils::OpenHandle(*name),
266 i::Object::TypeOf(isolate, holder), holder));
267 isolate->OptionalRescheduleException(false);
268 } else {
269 info.GetReturnValue().Set(false);
270 }
271 }
272
MakeModuleNamespaceEntryInfo(Isolate * isolate,Handle<String> name)273 Handle<AccessorInfo> Accessors::MakeModuleNamespaceEntryInfo(
274 Isolate* isolate, Handle<String> name) {
275 return MakeAccessor(isolate, name, &ModuleNamespaceEntryGetter,
276 &ModuleNamespaceEntrySetter);
277 }
278
279 //
280 // Accessors::StringLength
281 //
282
StringLengthGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)283 void Accessors::StringLengthGetter(
284 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
285 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
286 RCS_SCOPE(isolate, RuntimeCallCounterId::kStringLengthGetter);
287 DisallowGarbageCollection no_gc;
288 HandleScope scope(isolate);
289
290 // We have a slight impedance mismatch between the external API and the way we
291 // use callbacks internally: Externally, callbacks can only be used with
292 // v8::Object, but internally we have callbacks on entities which are higher
293 // in the hierarchy, in this case for String values.
294
295 Object value = *Utils::OpenHandle(*v8::Local<v8::Value>(info.This()));
296 if (!value.IsString()) {
297 // Not a string value. That means that we either got a String wrapper or
298 // a Value with a String wrapper in its prototype chain.
299 value =
300 JSPrimitiveWrapper::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
MakeStringLengthInfo(Isolate * isolate)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
GetFunctionPrototype(Isolate * isolate,Handle<JSFunction> function)315 static Handle<Object> GetFunctionPrototype(Isolate* isolate,
316 Handle<JSFunction> function) {
317 if (!function->has_prototype()) {
318 // We lazily allocate .prototype for functions, which confuses debug
319 // evaluate which assumes we can write to temporary objects we allocated
320 // during evaluation. We err on the side of caution here and prevent the
321 // newly allocated prototype from going into the temporary objects set,
322 // which means writes to it will be considered a side effect.
323 DisableTemporaryObjectTracking no_temp_tracking(isolate->debug());
324 Handle<JSObject> proto = isolate->factory()->NewFunctionPrototype(function);
325 JSFunction::SetPrototype(function, proto);
326 }
327 return Handle<Object>(function->prototype(), isolate);
328 }
329
FunctionPrototypeGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)330 void Accessors::FunctionPrototypeGetter(
331 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
332 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
333 RCS_SCOPE(isolate, RuntimeCallCounterId::kFunctionPrototypeGetter);
334 HandleScope scope(isolate);
335 Handle<JSFunction> function =
336 Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
337 DCHECK(function->has_prototype_property());
338 Handle<Object> result = GetFunctionPrototype(isolate, function);
339 info.GetReturnValue().Set(Utils::ToLocal(result));
340 }
341
FunctionPrototypeSetter(v8::Local<v8::Name> name,v8::Local<v8::Value> val,const v8::PropertyCallbackInfo<v8::Boolean> & info)342 void Accessors::FunctionPrototypeSetter(
343 v8::Local<v8::Name> name, v8::Local<v8::Value> val,
344 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
345 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
346 RCS_SCOPE(isolate, RuntimeCallCounterId::kFunctionPrototypeSetter);
347 HandleScope scope(isolate);
348 Handle<Object> value = Utils::OpenHandle(*val);
349 Handle<JSFunction> object =
350 Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
351 DCHECK(object->has_prototype_property());
352 JSFunction::SetPrototype(object, value);
353 info.GetReturnValue().Set(true);
354 }
355
MakeFunctionPrototypeInfo(Isolate * isolate)356 Handle<AccessorInfo> Accessors::MakeFunctionPrototypeInfo(Isolate* isolate) {
357 return MakeAccessor(isolate, isolate->factory()->prototype_string(),
358 &FunctionPrototypeGetter, &FunctionPrototypeSetter);
359 }
360
361 //
362 // Accessors::FunctionLength
363 //
364
FunctionLengthGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)365 void Accessors::FunctionLengthGetter(
366 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
367 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
368 RCS_SCOPE(isolate, RuntimeCallCounterId::kFunctionLengthGetter);
369 HandleScope scope(isolate);
370 Handle<JSFunction> function =
371 Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
372 int length = function->length();
373 Handle<Object> result(Smi::FromInt(length), isolate);
374 info.GetReturnValue().Set(Utils::ToLocal(result));
375 }
376
MakeFunctionLengthInfo(Isolate * isolate)377 Handle<AccessorInfo> Accessors::MakeFunctionLengthInfo(Isolate* isolate) {
378 return MakeAccessor(isolate, isolate->factory()->length_string(),
379 &FunctionLengthGetter, &ReconfigureToDataProperty);
380 }
381
382 //
383 // Accessors::FunctionName
384 //
385
FunctionNameGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)386 void Accessors::FunctionNameGetter(
387 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
388 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
389 HandleScope scope(isolate);
390 Handle<JSFunction> function =
391 Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
392 Handle<Object> result = JSFunction::GetName(isolate, function);
393 info.GetReturnValue().Set(Utils::ToLocal(result));
394 }
395
MakeFunctionNameInfo(Isolate * isolate)396 Handle<AccessorInfo> Accessors::MakeFunctionNameInfo(Isolate* isolate) {
397 return MakeAccessor(isolate, isolate->factory()->name_string(),
398 &FunctionNameGetter, &ReconfigureToDataProperty);
399 }
400
401 //
402 // Accessors::FunctionArguments
403 //
404
405 namespace {
406
ArgumentsForInlinedFunction(JavaScriptFrame * frame,int inlined_frame_index)407 Handle<JSObject> ArgumentsForInlinedFunction(JavaScriptFrame* frame,
408 int inlined_frame_index) {
409 Isolate* isolate = frame->isolate();
410 Factory* factory = isolate->factory();
411
412 TranslatedState translated_values(frame);
413 translated_values.Prepare(frame->fp());
414
415 int argument_count = 0;
416 TranslatedFrame* translated_frame =
417 translated_values.GetArgumentsInfoFromJSFrameIndex(inlined_frame_index,
418 &argument_count);
419 TranslatedFrame::iterator iter = translated_frame->begin();
420
421 // Materialize the function.
422 bool should_deoptimize = iter->IsMaterializedObject();
423 Handle<JSFunction> function = Handle<JSFunction>::cast(iter->GetValue());
424 iter++;
425
426 // Skip the receiver.
427 iter++;
428 argument_count--;
429
430 Handle<JSObject> arguments =
431 factory->NewArgumentsObject(function, argument_count);
432 Handle<FixedArray> array = factory->NewFixedArray(argument_count);
433 for (int i = 0; i < argument_count; ++i) {
434 // If we materialize any object, we should deoptimize the frame because we
435 // might alias an object that was eliminated by escape analysis.
436 should_deoptimize = should_deoptimize || iter->IsMaterializedObject();
437 Handle<Object> value = iter->GetValue();
438 array->set(i, *value);
439 iter++;
440 }
441 arguments->set_elements(*array);
442
443 if (should_deoptimize) {
444 translated_values.StoreMaterializedValuesAndDeopt(frame);
445 }
446
447 // Return the freshly allocated arguments object.
448 return arguments;
449 }
450
FindFunctionInFrame(JavaScriptFrame * frame,Handle<JSFunction> function)451 int FindFunctionInFrame(JavaScriptFrame* frame, Handle<JSFunction> function) {
452 std::vector<FrameSummary> frames;
453 frame->Summarize(&frames);
454 for (size_t i = frames.size(); i != 0; i--) {
455 if (*frames[i - 1].AsJavaScript().function() == *function) {
456 return static_cast<int>(i) - 1;
457 }
458 }
459 return -1;
460 }
461
GetFrameArguments(Isolate * isolate,JavaScriptFrameIterator * it,int function_index)462 Handle<JSObject> GetFrameArguments(Isolate* isolate,
463 JavaScriptFrameIterator* it,
464 int function_index) {
465 JavaScriptFrame* frame = it->frame();
466
467 if (function_index > 0) {
468 // The function in question was inlined. Inlined functions have the
469 // correct number of arguments and no allocated arguments object, so
470 // we can construct a fresh one by interpreting the function's
471 // deoptimization input data.
472 return ArgumentsForInlinedFunction(frame, function_index);
473 }
474
475 // Construct an arguments object mirror for the right frame and the underlying
476 // function.
477 const int length = frame->GetActualArgumentCount();
478 Handle<JSFunction> function(frame->function(), isolate);
479 Handle<JSObject> arguments =
480 isolate->factory()->NewArgumentsObject(function, length);
481 Handle<FixedArray> array = isolate->factory()->NewFixedArray(length);
482
483 // Copy the parameters to the arguments object.
484 DCHECK(array->length() == length);
485 for (int i = 0; i < length; i++) {
486 Object value = frame->GetParameter(i);
487 if (value.IsTheHole(isolate)) {
488 // Generators currently use holes as dummy arguments when resuming. We
489 // must not leak those.
490 DCHECK(IsResumableFunction(function->shared().kind()));
491 value = ReadOnlyRoots(isolate).undefined_value();
492 }
493 array->set(i, value);
494 }
495 arguments->set_elements(*array);
496
497 // Return the freshly allocated arguments object.
498 return arguments;
499 }
500
501 } // namespace
502
FunctionGetArguments(JavaScriptFrame * frame,int inlined_jsframe_index)503 Handle<JSObject> Accessors::FunctionGetArguments(JavaScriptFrame* frame,
504 int inlined_jsframe_index) {
505 Isolate* isolate = frame->isolate();
506 Address requested_frame_fp = frame->fp();
507 // Forward a frame iterator to the requested frame. This is needed because we
508 // potentially need for advance it to the arguments adaptor frame later.
509 for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
510 if (it.frame()->fp() != requested_frame_fp) continue;
511 return GetFrameArguments(isolate, &it, inlined_jsframe_index);
512 }
513 UNREACHABLE(); // Requested frame not found.
514 }
515
FunctionArgumentsGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)516 void Accessors::FunctionArgumentsGetter(
517 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
518 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
519 isolate->CountUsage(v8::Isolate::kFunctionPrototypeArguments);
520 HandleScope scope(isolate);
521 Handle<JSFunction> function =
522 Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
523 Handle<Object> result = isolate->factory()->null_value();
524 if (!function->shared().native()) {
525 // Find the top invocation of the function by traversing frames.
526 for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
527 JavaScriptFrame* frame = it.frame();
528 int function_index = FindFunctionInFrame(frame, function);
529 if (function_index >= 0) {
530 result = GetFrameArguments(isolate, &it, function_index);
531 break;
532 }
533 }
534 }
535 info.GetReturnValue().Set(Utils::ToLocal(result));
536 }
537
MakeFunctionArgumentsInfo(Isolate * isolate)538 Handle<AccessorInfo> Accessors::MakeFunctionArgumentsInfo(Isolate* isolate) {
539 return MakeAccessor(isolate, isolate->factory()->arguments_string(),
540 &FunctionArgumentsGetter, nullptr);
541 }
542
543 //
544 // Accessors::FunctionCaller
545 //
546
AllowAccessToFunction(Context current_context,JSFunction function)547 static inline bool AllowAccessToFunction(Context current_context,
548 JSFunction function) {
549 return current_context.HasSameSecurityTokenAs(function.context());
550 }
551
552 class FrameFunctionIterator {
553 public:
FrameFunctionIterator(Isolate * isolate)554 explicit FrameFunctionIterator(Isolate* isolate)
555 : isolate_(isolate), frame_iterator_(isolate), inlined_frame_index_(-1) {
556 GetFrames();
557 }
558
559 // Iterate through functions until the first occurrence of 'function'.
560 // Returns true if one is found, and false if the iterator ends before.
Find(Handle<JSFunction> function)561 bool Find(Handle<JSFunction> function) {
562 do {
563 if (!next().ToHandle(&function_)) return false;
564 } while (!function_.is_identical_to(function));
565 return true;
566 }
567
568 // Iterate through functions until the next non-toplevel one is found.
569 // Returns true if one is found, and false if the iterator ends before.
FindNextNonTopLevel()570 bool FindNextNonTopLevel() {
571 do {
572 if (!next().ToHandle(&function_)) return false;
573 } while (function_->shared().is_toplevel());
574 return true;
575 }
576
577 // Iterate through function until the first native or user-provided function
578 // is found. Functions not defined in user-provided scripts are not visible
579 // unless directly exposed, in which case the native flag is set on them.
580 // Returns true if one is found, and false if the iterator ends before.
FindFirstNativeOrUserJavaScript()581 bool FindFirstNativeOrUserJavaScript() {
582 while (!function_->shared().native() &&
583 !function_->shared().IsUserJavaScript()) {
584 if (!next().ToHandle(&function_)) return false;
585 }
586 return true;
587 }
588
589 // In case of inlined frames the function could have been materialized from
590 // deoptimization information. If that is the case we need to make sure that
591 // subsequent call will see the same function, since we are about to hand out
592 // the value to JavaScript. Make sure to store the materialized value and
593 // trigger a deoptimization of the underlying frame.
MaterializeFunction()594 Handle<JSFunction> MaterializeFunction() {
595 if (inlined_frame_index_ == 0) return function_;
596
597 JavaScriptFrame* frame = frame_iterator_.frame();
598 TranslatedState translated_values(frame);
599 translated_values.Prepare(frame->fp());
600
601 TranslatedFrame* translated_frame =
602 translated_values.GetFrameFromJSFrameIndex(inlined_frame_index_);
603 TranslatedFrame::iterator iter = translated_frame->begin();
604
605 // First value is the function.
606 bool should_deoptimize = iter->IsMaterializedObject();
607 Handle<Object> value = iter->GetValue();
608 if (should_deoptimize) {
609 translated_values.StoreMaterializedValuesAndDeopt(frame);
610 }
611
612 return Handle<JSFunction>::cast(value);
613 }
614
615 private:
next()616 MaybeHandle<JSFunction> next() {
617 while (true) {
618 if (inlined_frame_index_ <= 0) {
619 if (!frame_iterator_.done()) {
620 frame_iterator_.Advance();
621 frames_.clear();
622 inlined_frame_index_ = -1;
623 GetFrames();
624 }
625 if (inlined_frame_index_ == -1) return MaybeHandle<JSFunction>();
626 }
627
628 --inlined_frame_index_;
629 Handle<JSFunction> next_function =
630 frames_[inlined_frame_index_].AsJavaScript().function();
631 // Skip functions from other origins.
632 if (!AllowAccessToFunction(isolate_->context(), *next_function)) continue;
633 return next_function;
634 }
635 }
GetFrames()636 void GetFrames() {
637 DCHECK_EQ(-1, inlined_frame_index_);
638 if (frame_iterator_.done()) return;
639 JavaScriptFrame* frame = frame_iterator_.frame();
640 frame->Summarize(&frames_);
641 inlined_frame_index_ = static_cast<int>(frames_.size());
642 DCHECK_LT(0, inlined_frame_index_);
643 }
644 Isolate* isolate_;
645 Handle<JSFunction> function_;
646 JavaScriptFrameIterator frame_iterator_;
647 std::vector<FrameSummary> frames_;
648 int inlined_frame_index_;
649 };
650
FindCaller(Isolate * isolate,Handle<JSFunction> function)651 MaybeHandle<JSFunction> FindCaller(Isolate* isolate,
652 Handle<JSFunction> function) {
653 FrameFunctionIterator it(isolate);
654 if (function->shared().native()) {
655 return MaybeHandle<JSFunction>();
656 }
657 // Find the function from the frames. Return null in case no frame
658 // corresponding to the given function was found.
659 if (!it.Find(function)) {
660 return MaybeHandle<JSFunction>();
661 }
662 // Find previously called non-toplevel function.
663 if (!it.FindNextNonTopLevel()) {
664 return MaybeHandle<JSFunction>();
665 }
666 // Find the first user-land JavaScript function (or the entry point into
667 // native JavaScript builtins in case such a builtin was the caller).
668 if (!it.FindFirstNativeOrUserJavaScript()) {
669 return MaybeHandle<JSFunction>();
670 }
671
672 // Materialize the function that the iterator is currently sitting on. Note
673 // that this might trigger deoptimization in case the function was actually
674 // materialized. Identity of the function must be preserved because we are
675 // going to return it to JavaScript after this point.
676 Handle<JSFunction> caller = it.MaterializeFunction();
677
678 // Censor if the caller is not a sloppy mode function.
679 // Change from ES5, which used to throw, see:
680 // https://bugs.ecmascript.org/show_bug.cgi?id=310
681 if (is_strict(caller->shared().language_mode())) {
682 return MaybeHandle<JSFunction>();
683 }
684 // Don't return caller from another security context.
685 if (!AllowAccessToFunction(isolate->context(), *caller)) {
686 return MaybeHandle<JSFunction>();
687 }
688 return caller;
689 }
690
FunctionCallerGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)691 void Accessors::FunctionCallerGetter(
692 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
693 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
694 isolate->CountUsage(v8::Isolate::kFunctionPrototypeCaller);
695 HandleScope scope(isolate);
696 Handle<JSFunction> function =
697 Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
698 Handle<Object> result;
699 MaybeHandle<JSFunction> maybe_caller;
700 maybe_caller = FindCaller(isolate, function);
701 Handle<JSFunction> caller;
702 // We don't support caller access with correctness fuzzing.
703 if (!FLAG_correctness_fuzzer_suppressions && 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 RCS_SCOPE(isolate, RuntimeCallCounterId::kBoundFunctionLengthGetter);
724 HandleScope scope(isolate);
725 Handle<JSBoundFunction> function =
726 Handle<JSBoundFunction>::cast(Utils::OpenHandle(*info.Holder()));
727
728 int length = 0;
729 if (!JSBoundFunction::GetLength(isolate, function).To(&length)) {
730 isolate->OptionalRescheduleException(false);
731 return;
732 }
733 Handle<Object> result(Smi::FromInt(length), isolate);
734 info.GetReturnValue().Set(Utils::ToLocal(result));
735 }
736
MakeBoundFunctionLengthInfo(Isolate * isolate)737 Handle<AccessorInfo> Accessors::MakeBoundFunctionLengthInfo(Isolate* isolate) {
738 return MakeAccessor(isolate, isolate->factory()->length_string(),
739 &BoundFunctionLengthGetter, &ReconfigureToDataProperty);
740 }
741
742 //
743 // Accessors::BoundFunctionName
744 //
745
BoundFunctionNameGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)746 void Accessors::BoundFunctionNameGetter(
747 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
748 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
749 RCS_SCOPE(isolate, RuntimeCallCounterId::kBoundFunctionNameGetter);
750 HandleScope scope(isolate);
751 Handle<JSBoundFunction> function =
752 Handle<JSBoundFunction>::cast(Utils::OpenHandle(*info.Holder()));
753 Handle<Object> result;
754 if (!JSBoundFunction::GetName(isolate, function).ToHandle(&result)) {
755 isolate->OptionalRescheduleException(false);
756 return;
757 }
758 info.GetReturnValue().Set(Utils::ToLocal(result));
759 }
760
MakeBoundFunctionNameInfo(Isolate * isolate)761 Handle<AccessorInfo> Accessors::MakeBoundFunctionNameInfo(Isolate* isolate) {
762 return MakeAccessor(isolate, isolate->factory()->name_string(),
763 &BoundFunctionNameGetter, &ReconfigureToDataProperty);
764 }
765
766 //
767 // Accessors::WrappedFunctionLength
768 //
769
WrappedFunctionLengthGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)770 void Accessors::WrappedFunctionLengthGetter(
771 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
772 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
773 RCS_SCOPE(isolate, RuntimeCallCounterId::kBoundFunctionLengthGetter);
774 HandleScope scope(isolate);
775 Handle<JSWrappedFunction> function =
776 Handle<JSWrappedFunction>::cast(Utils::OpenHandle(*info.Holder()));
777
778 int length = 0;
779 if (!JSWrappedFunction::GetLength(isolate, function).To(&length)) {
780 isolate->OptionalRescheduleException(false);
781 return;
782 }
783 Handle<Object> result(Smi::FromInt(length), isolate);
784 info.GetReturnValue().Set(Utils::ToLocal(result));
785 }
786
MakeWrappedFunctionLengthInfo(Isolate * isolate)787 Handle<AccessorInfo> Accessors::MakeWrappedFunctionLengthInfo(
788 Isolate* isolate) {
789 return MakeAccessor(isolate, isolate->factory()->length_string(),
790 &WrappedFunctionLengthGetter, &ReconfigureToDataProperty);
791 }
792
793 //
794 // Accessors::WrappedFunctionName
795 //
796
WrappedFunctionNameGetter(v8::Local<v8::Name> name,const v8::PropertyCallbackInfo<v8::Value> & info)797 void Accessors::WrappedFunctionNameGetter(
798 v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
799 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
800 RCS_SCOPE(isolate, RuntimeCallCounterId::kWrappedFunctionNameGetter);
801 HandleScope scope(isolate);
802 Handle<JSWrappedFunction> function =
803 Handle<JSWrappedFunction>::cast(Utils::OpenHandle(*info.Holder()));
804 Handle<Object> result;
805 if (!JSWrappedFunction::GetName(isolate, function).ToHandle(&result)) {
806 isolate->OptionalRescheduleException(false);
807 return;
808 }
809 info.GetReturnValue().Set(Utils::ToLocal(result));
810 }
811
MakeWrappedFunctionNameInfo(Isolate * isolate)812 Handle<AccessorInfo> Accessors::MakeWrappedFunctionNameInfo(Isolate* isolate) {
813 return MakeAccessor(isolate, isolate->factory()->name_string(),
814 &WrappedFunctionNameGetter, &ReconfigureToDataProperty);
815 }
816
817 //
818 // Accessors::ErrorStack
819 //
820
ErrorStackGetter(v8::Local<v8::Name> key,const v8::PropertyCallbackInfo<v8::Value> & info)821 void Accessors::ErrorStackGetter(
822 v8::Local<v8::Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
823 Isolate* isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
824 HandleScope scope(isolate);
825 Handle<Object> formatted_stack;
826 Handle<JSObject> error_object =
827 Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
828 if (!ErrorUtils::GetFormattedStack(isolate, error_object)
829 .ToHandle(&formatted_stack)) {
830 isolate->OptionalRescheduleException(false);
831 return;
832 }
833 info.GetReturnValue().Set(Utils::ToLocal(formatted_stack));
834 }
835
ErrorStackSetter(v8::Local<v8::Name> name,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<v8::Boolean> & info)836 void Accessors::ErrorStackSetter(
837 v8::Local<v8::Name> name, v8::Local<v8::Value> value,
838 const v8::PropertyCallbackInfo<v8::Boolean>& info) {
839 Isolate* isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
840 HandleScope scope(isolate);
841 Handle<JSObject> error_object =
842 Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
843 ErrorUtils::SetFormattedStack(isolate, error_object,
844 Utils::OpenHandle(*value));
845 }
846
MakeErrorStackInfo(Isolate * isolate)847 Handle<AccessorInfo> Accessors::MakeErrorStackInfo(Isolate* isolate) {
848 return MakeAccessor(isolate, isolate->factory()->stack_string(),
849 &ErrorStackGetter, &ErrorStackSetter);
850 }
851
852 } // namespace internal
853 } // namespace v8
854