1 // Copyright 2020 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/objects/js-function.h"
6
7 #include "src/codegen/compiler.h"
8 #include "src/diagnostics/code-tracer.h"
9 #include "src/heap/heap-inl.h"
10 #include "src/ic/ic.h"
11 #include "src/init/bootstrapper.h"
12 #include "src/objects/feedback-cell-inl.h"
13 #include "src/strings/string-builder-inl.h"
14
15 // Has to be the last include (doesn't have include guards):
16 #include "src/objects/object-macros.h"
17
18 namespace v8 {
19 namespace internal {
20
GetAttachedCodeKinds() const21 CodeKinds JSFunction::GetAttachedCodeKinds() const {
22 CodeKinds result;
23
24 // Note: There's a special case when bytecode has been aged away. After
25 // flushing the bytecode, the JSFunction will still have the interpreter
26 // entry trampoline attached, but the bytecode is no longer available.
27 if (code().is_interpreter_trampoline_builtin()) {
28 result |= CodeKindFlag::INTERPRETED_FUNCTION;
29 }
30
31 const CodeKind kind = code().kind();
32 if (!CodeKindIsOptimizedJSFunction(kind) ||
33 code().marked_for_deoptimization()) {
34 DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
35 return result;
36 }
37
38 DCHECK(CodeKindIsOptimizedJSFunction(kind));
39 result |= CodeKindToCodeKindFlag(kind);
40
41 DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
42 return result;
43 }
44
GetAvailableCodeKinds() const45 CodeKinds JSFunction::GetAvailableCodeKinds() const {
46 CodeKinds result = GetAttachedCodeKinds();
47
48 if ((result & CodeKindFlag::INTERPRETED_FUNCTION) == 0) {
49 // The SharedFunctionInfo could have attached bytecode.
50 if (shared().HasBytecodeArray()) {
51 result |= CodeKindFlag::INTERPRETED_FUNCTION;
52 }
53 }
54
55 if ((result & kOptimizedJSFunctionCodeKindsMask) == 0) {
56 // Check the optimized code cache.
57 if (has_feedback_vector() && feedback_vector().has_optimized_code() &&
58 !feedback_vector().optimized_code().marked_for_deoptimization()) {
59 Code code = feedback_vector().optimized_code();
60 DCHECK(CodeKindIsOptimizedJSFunction(code.kind()));
61 result |= CodeKindToCodeKindFlag(code.kind());
62 }
63 }
64
65 DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
66 return result;
67 }
68
HasAttachedOptimizedCode() const69 bool JSFunction::HasAttachedOptimizedCode() const {
70 CodeKinds result = GetAttachedCodeKinds();
71 return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
72 }
73
HasAvailableOptimizedCode() const74 bool JSFunction::HasAvailableOptimizedCode() const {
75 CodeKinds result = GetAvailableCodeKinds();
76 return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
77 }
78
HasAvailableCodeKind(CodeKind kind) const79 bool JSFunction::HasAvailableCodeKind(CodeKind kind) const {
80 CodeKinds result = GetAvailableCodeKinds();
81 return (result & CodeKindToCodeKindFlag(kind)) != 0;
82 }
83
84 namespace {
85
86 // Returns false if no highest tier exists (i.e. the function is not compiled),
87 // otherwise returns true and sets highest_tier.
HighestTierOf(CodeKinds kinds,CodeKind * highest_tier)88 bool HighestTierOf(CodeKinds kinds, CodeKind* highest_tier) {
89 DCHECK_EQ((kinds & ~kJSFunctionCodeKindsMask), 0);
90 if ((kinds & CodeKindFlag::TURBOFAN) != 0) {
91 *highest_tier = CodeKind::TURBOFAN;
92 return true;
93 } else if ((kinds & CodeKindFlag::TURBOPROP) != 0) {
94 *highest_tier = CodeKind::TURBOPROP;
95 return true;
96 } else if ((kinds & CodeKindFlag::NATIVE_CONTEXT_INDEPENDENT) != 0) {
97 *highest_tier = CodeKind::NATIVE_CONTEXT_INDEPENDENT;
98 return true;
99 } else if ((kinds & CodeKindFlag::INTERPRETED_FUNCTION) != 0) {
100 *highest_tier = CodeKind::INTERPRETED_FUNCTION;
101 return true;
102 }
103 DCHECK_EQ(kinds, 0);
104 return false;
105 }
106
107 } // namespace
108
ActiveTierIsIgnition() const109 bool JSFunction::ActiveTierIsIgnition() const {
110 CodeKind highest_tier;
111 if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
112 bool result = (highest_tier == CodeKind::INTERPRETED_FUNCTION);
113 DCHECK_IMPLIES(result,
114 code().is_interpreter_trampoline_builtin() ||
115 (CodeKindIsOptimizedJSFunction(code().kind()) &&
116 code().marked_for_deoptimization()) ||
117 (code().builtin_index() == Builtins::kCompileLazy &&
118 shared().IsInterpreted()));
119 return result;
120 }
121
ActiveTierIsTurbofan() const122 bool JSFunction::ActiveTierIsTurbofan() const {
123 CodeKind highest_tier;
124 if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
125 return highest_tier == CodeKind::TURBOFAN;
126 }
127
ActiveTierIsNCI() const128 bool JSFunction::ActiveTierIsNCI() const {
129 CodeKind highest_tier;
130 if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
131 return highest_tier == CodeKind::NATIVE_CONTEXT_INDEPENDENT;
132 }
133
ActiveTierIsToptierTurboprop() const134 bool JSFunction::ActiveTierIsToptierTurboprop() const {
135 CodeKind highest_tier;
136 if (!FLAG_turboprop) return false;
137 if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
138 return highest_tier == CodeKind::TURBOPROP && !FLAG_turboprop_as_midtier;
139 }
140
ActiveTierIsMidtierTurboprop() const141 bool JSFunction::ActiveTierIsMidtierTurboprop() const {
142 CodeKind highest_tier;
143 if (!FLAG_turboprop_as_midtier) return false;
144 if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
145 return highest_tier == CodeKind::TURBOPROP && FLAG_turboprop_as_midtier;
146 }
147
NextTier() const148 CodeKind JSFunction::NextTier() const {
149 if (V8_UNLIKELY(FLAG_turbo_nci_as_midtier && ActiveTierIsIgnition())) {
150 return CodeKind::NATIVE_CONTEXT_INDEPENDENT;
151 } else if (V8_UNLIKELY(FLAG_turboprop) && ActiveTierIsMidtierTurboprop()) {
152 return CodeKind::TURBOFAN;
153 } else if (V8_UNLIKELY(FLAG_turboprop)) {
154 DCHECK(ActiveTierIsIgnition());
155 return CodeKind::TURBOPROP;
156 }
157 return CodeKind::TURBOFAN;
158 }
159
CanDiscardCompiled() const160 bool JSFunction::CanDiscardCompiled() const {
161 // Essentially, what we are asking here is, has this function been compiled
162 // from JS code? We can currently tell only indirectly, by looking at
163 // available code kinds. If any JS code kind exists, we can discard.
164 //
165 // Attached optimized code that is marked for deoptimization will not show up
166 // in the list of available code kinds, thus we must check for it manually.
167 //
168 // Note that when the function has not yet been compiled we also return
169 // false; that's fine, since nothing must be discarded in that case.
170 if (CodeKindIsOptimizedJSFunction(code().kind())) return true;
171 CodeKinds result = GetAvailableCodeKinds();
172 return (result & kJSFunctionCodeKindsMask) != 0;
173 }
174
175 // static
GetFunctionRealm(Handle<JSBoundFunction> function)176 MaybeHandle<NativeContext> JSBoundFunction::GetFunctionRealm(
177 Handle<JSBoundFunction> function) {
178 DCHECK(function->map().is_constructor());
179 return JSReceiver::GetFunctionRealm(
180 handle(function->bound_target_function(), function->GetIsolate()));
181 }
182
183 // static
GetName(Isolate * isolate,Handle<JSBoundFunction> function)184 MaybeHandle<String> JSBoundFunction::GetName(Isolate* isolate,
185 Handle<JSBoundFunction> function) {
186 Handle<String> prefix = isolate->factory()->bound__string();
187 Handle<String> target_name = prefix;
188 Factory* factory = isolate->factory();
189 // Concatenate the "bound " up to the last non-bound target.
190 while (function->bound_target_function().IsJSBoundFunction()) {
191 ASSIGN_RETURN_ON_EXCEPTION(isolate, target_name,
192 factory->NewConsString(prefix, target_name),
193 String);
194 function = handle(JSBoundFunction::cast(function->bound_target_function()),
195 isolate);
196 }
197 if (function->bound_target_function().IsJSFunction()) {
198 Handle<JSFunction> target(
199 JSFunction::cast(function->bound_target_function()), isolate);
200 Handle<Object> name = JSFunction::GetName(isolate, target);
201 if (!name->IsString()) return target_name;
202 return factory->NewConsString(target_name, Handle<String>::cast(name));
203 }
204 // This will omit the proper target name for bound JSProxies.
205 return target_name;
206 }
207
208 // static
GetLength(Isolate * isolate,Handle<JSBoundFunction> function)209 Maybe<int> JSBoundFunction::GetLength(Isolate* isolate,
210 Handle<JSBoundFunction> function) {
211 int nof_bound_arguments = function->bound_arguments().length();
212 while (function->bound_target_function().IsJSBoundFunction()) {
213 function = handle(JSBoundFunction::cast(function->bound_target_function()),
214 isolate);
215 // Make sure we never overflow {nof_bound_arguments}, the number of
216 // arguments of a function is strictly limited by the max length of an
217 // JSAarray, Smi::kMaxValue is thus a reasonably good overestimate.
218 int length = function->bound_arguments().length();
219 if (V8_LIKELY(Smi::kMaxValue - nof_bound_arguments > length)) {
220 nof_bound_arguments += length;
221 } else {
222 nof_bound_arguments = Smi::kMaxValue;
223 }
224 }
225 // All non JSFunction targets get a direct property and don't use this
226 // accessor.
227 Handle<JSFunction> target(JSFunction::cast(function->bound_target_function()),
228 isolate);
229 int target_length = target->length();
230
231 int length = std::max(0, target_length - nof_bound_arguments);
232 return Just(length);
233 }
234
235 // static
ToString(Handle<JSBoundFunction> function)236 Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) {
237 Isolate* const isolate = function->GetIsolate();
238 return isolate->factory()->function_native_code_string();
239 }
240
241 // static
GetName(Isolate * isolate,Handle<JSFunction> function)242 Handle<Object> JSFunction::GetName(Isolate* isolate,
243 Handle<JSFunction> function) {
244 if (function->shared().name_should_print_as_anonymous()) {
245 return isolate->factory()->anonymous_string();
246 }
247 return handle(function->shared().Name(), isolate);
248 }
249
250 // static
GetFunctionRealm(Handle<JSFunction> function)251 Handle<NativeContext> JSFunction::GetFunctionRealm(
252 Handle<JSFunction> function) {
253 DCHECK(function->map().is_constructor());
254 return handle(function->context().native_context(), function->GetIsolate());
255 }
256
257 // static
EnsureClosureFeedbackCellArray(Handle<JSFunction> function)258 void JSFunction::EnsureClosureFeedbackCellArray(Handle<JSFunction> function) {
259 Isolate* const isolate = function->GetIsolate();
260 DCHECK(function->shared().is_compiled());
261 DCHECK(function->shared().HasFeedbackMetadata());
262 if (function->has_closure_feedback_cell_array() ||
263 function->has_feedback_vector()) {
264 return;
265 }
266 if (function->shared().HasAsmWasmData()) return;
267
268 Handle<SharedFunctionInfo> shared(function->shared(), isolate);
269 DCHECK(function->shared().HasBytecodeArray());
270 Handle<HeapObject> feedback_cell_array =
271 ClosureFeedbackCellArray::New(isolate, shared);
272 // Many closure cell is used as a way to specify that there is no
273 // feedback cell for this function and a new feedback cell has to be
274 // allocated for this funciton. For ex: for eval functions, we have to create
275 // a feedback cell and cache it along with the code. It is safe to use
276 // many_closure_cell to indicate this because in regular cases, it should
277 // already have a feedback_vector / feedback cell array allocated.
278 if (function->raw_feedback_cell() == isolate->heap()->many_closures_cell()) {
279 Handle<FeedbackCell> feedback_cell =
280 isolate->factory()->NewOneClosureCell(feedback_cell_array);
281 function->set_raw_feedback_cell(*feedback_cell);
282 } else {
283 function->raw_feedback_cell().set_value(*feedback_cell_array);
284 }
285 }
286
287 // static
EnsureFeedbackVector(Handle<JSFunction> function,IsCompiledScope * is_compiled_scope)288 void JSFunction::EnsureFeedbackVector(Handle<JSFunction> function,
289 IsCompiledScope* is_compiled_scope) {
290 Isolate* const isolate = function->GetIsolate();
291 DCHECK(is_compiled_scope->is_compiled());
292 DCHECK(function->shared().HasFeedbackMetadata());
293 if (function->has_feedback_vector()) return;
294 if (function->shared().HasAsmWasmData()) return;
295
296 Handle<SharedFunctionInfo> shared(function->shared(), isolate);
297 DCHECK(function->shared().HasBytecodeArray());
298
299 EnsureClosureFeedbackCellArray(function);
300 Handle<ClosureFeedbackCellArray> closure_feedback_cell_array =
301 handle(function->closure_feedback_cell_array(), isolate);
302 Handle<HeapObject> feedback_vector = FeedbackVector::New(
303 isolate, shared, closure_feedback_cell_array, is_compiled_scope);
304 // EnsureClosureFeedbackCellArray should handle the special case where we need
305 // to allocate a new feedback cell. Please look at comment in that function
306 // for more details.
307 DCHECK(function->raw_feedback_cell() !=
308 isolate->heap()->many_closures_cell());
309 function->raw_feedback_cell().set_value(*feedback_vector);
310 function->raw_feedback_cell().SetInterruptBudget();
311 }
312
313 // static
InitializeFeedbackCell(Handle<JSFunction> function,IsCompiledScope * is_compiled_scope)314 void JSFunction::InitializeFeedbackCell(Handle<JSFunction> function,
315 IsCompiledScope* is_compiled_scope) {
316 Isolate* const isolate = function->GetIsolate();
317
318 if (function->has_feedback_vector()) {
319 CHECK_EQ(function->feedback_vector().length(),
320 function->feedback_vector().metadata().slot_count());
321 return;
322 }
323
324 const bool needs_feedback_vector =
325 !FLAG_lazy_feedback_allocation || FLAG_always_opt ||
326 function->shared().may_have_cached_code() ||
327 // We also need a feedback vector for certain log events, collecting type
328 // profile and more precise code coverage.
329 FLAG_log_function_events || !isolate->is_best_effort_code_coverage() ||
330 isolate->is_collecting_type_profile();
331
332 if (needs_feedback_vector) {
333 EnsureFeedbackVector(function, is_compiled_scope);
334 } else {
335 EnsureClosureFeedbackCellArray(function);
336 }
337 }
338
339 namespace {
340
SetInstancePrototype(Isolate * isolate,Handle<JSFunction> function,Handle<JSReceiver> value)341 void SetInstancePrototype(Isolate* isolate, Handle<JSFunction> function,
342 Handle<JSReceiver> value) {
343 // Now some logic for the maps of the objects that are created by using this
344 // function as a constructor.
345 if (function->has_initial_map()) {
346 // If the function has allocated the initial map replace it with a
347 // copy containing the new prototype. Also complete any in-object
348 // slack tracking that is in progress at this point because it is
349 // still tracking the old copy.
350 function->CompleteInobjectSlackTrackingIfActive();
351
352 Handle<Map> initial_map(function->initial_map(), isolate);
353
354 if (!isolate->bootstrapper()->IsActive() &&
355 initial_map->instance_type() == JS_OBJECT_TYPE) {
356 // Put the value in the initial map field until an initial map is needed.
357 // At that point, a new initial map is created and the prototype is put
358 // into the initial map where it belongs.
359 function->set_prototype_or_initial_map(*value);
360 } else {
361 Handle<Map> new_map =
362 Map::Copy(isolate, initial_map, "SetInstancePrototype");
363 JSFunction::SetInitialMap(function, new_map, value);
364
365 // If the function is used as the global Array function, cache the
366 // updated initial maps (and transitioned versions) in the native context.
367 Handle<Context> native_context(function->context().native_context(),
368 isolate);
369 Handle<Object> array_function(
370 native_context->get(Context::ARRAY_FUNCTION_INDEX), isolate);
371 if (array_function->IsJSFunction() &&
372 *function == JSFunction::cast(*array_function)) {
373 CacheInitialJSArrayMaps(isolate, native_context, new_map);
374 }
375 }
376
377 // Deoptimize all code that embeds the previous initial map.
378 initial_map->dependent_code().DeoptimizeDependentCodeGroup(
379 DependentCode::kInitialMapChangedGroup);
380 } else {
381 // Put the value in the initial map field until an initial map is
382 // needed. At that point, a new initial map is created and the
383 // prototype is put into the initial map where it belongs.
384 function->set_prototype_or_initial_map(*value);
385 if (value->IsJSObject()) {
386 // Optimize as prototype to detach it from its transition tree.
387 JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
388 }
389 }
390 }
391
392 } // anonymous namespace
393
SetPrototype(Handle<JSFunction> function,Handle<Object> value)394 void JSFunction::SetPrototype(Handle<JSFunction> function,
395 Handle<Object> value) {
396 DCHECK(function->IsConstructor() ||
397 IsGeneratorFunction(function->shared().kind()));
398 Isolate* isolate = function->GetIsolate();
399 Handle<JSReceiver> construct_prototype;
400
401 // If the value is not a JSReceiver, store the value in the map's
402 // constructor field so it can be accessed. Also, set the prototype
403 // used for constructing objects to the original object prototype.
404 // See ECMA-262 13.2.2.
405 if (!value->IsJSReceiver()) {
406 // Copy the map so this does not affect unrelated functions.
407 // Remove map transitions because they point to maps with a
408 // different prototype.
409 Handle<Map> new_map =
410 Map::Copy(isolate, handle(function->map(), isolate), "SetPrototype");
411
412 JSObject::MigrateToMap(isolate, function, new_map);
413 new_map->SetConstructor(*value);
414 new_map->set_has_non_instance_prototype(true);
415
416 FunctionKind kind = function->shared().kind();
417 Handle<Context> native_context(function->context().native_context(),
418 isolate);
419
420 construct_prototype = Handle<JSReceiver>(
421 IsGeneratorFunction(kind)
422 ? IsAsyncFunction(kind)
423 ? native_context->initial_async_generator_prototype()
424 : native_context->initial_generator_prototype()
425 : native_context->initial_object_prototype(),
426 isolate);
427 } else {
428 construct_prototype = Handle<JSReceiver>::cast(value);
429 function->map().set_has_non_instance_prototype(false);
430 }
431
432 SetInstancePrototype(isolate, function, construct_prototype);
433 }
434
SetInitialMap(Handle<JSFunction> function,Handle<Map> map,Handle<HeapObject> prototype)435 void JSFunction::SetInitialMap(Handle<JSFunction> function, Handle<Map> map,
436 Handle<HeapObject> prototype) {
437 if (map->prototype() != *prototype) {
438 Map::SetPrototype(function->GetIsolate(), map, prototype);
439 }
440 function->set_prototype_or_initial_map(*map);
441 map->SetConstructor(*function);
442 if (FLAG_trace_maps) {
443 LOG(function->GetIsolate(), MapEvent("InitialMap", Handle<Map>(), map, "",
444 handle(function->shared().DebugName(),
445 function->GetIsolate())));
446 }
447 }
448
EnsureHasInitialMap(Handle<JSFunction> function)449 void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
450 DCHECK(function->has_prototype_slot());
451 DCHECK(function->IsConstructor() ||
452 IsResumableFunction(function->shared().kind()));
453 if (function->has_initial_map()) return;
454 Isolate* isolate = function->GetIsolate();
455
456 int expected_nof_properties =
457 CalculateExpectedNofProperties(isolate, function);
458
459 // {CalculateExpectedNofProperties} can have had the side effect of creating
460 // the initial map (e.g. it could have triggered an optimized compilation
461 // whose dependency installation reentered {EnsureHasInitialMap}).
462 if (function->has_initial_map()) return;
463
464 // Create a new map with the size and number of in-object properties suggested
465 // by the function.
466 InstanceType instance_type;
467 if (IsResumableFunction(function->shared().kind())) {
468 instance_type = IsAsyncGeneratorFunction(function->shared().kind())
469 ? JS_ASYNC_GENERATOR_OBJECT_TYPE
470 : JS_GENERATOR_OBJECT_TYPE;
471 } else {
472 instance_type = JS_OBJECT_TYPE;
473 }
474
475 int instance_size;
476 int inobject_properties;
477 CalculateInstanceSizeHelper(instance_type, false, 0, expected_nof_properties,
478 &instance_size, &inobject_properties);
479
480 Handle<Map> map = isolate->factory()->NewMap(instance_type, instance_size,
481 TERMINAL_FAST_ELEMENTS_KIND,
482 inobject_properties);
483
484 // Fetch or allocate prototype.
485 Handle<HeapObject> prototype;
486 if (function->has_instance_prototype()) {
487 prototype = handle(function->instance_prototype(), isolate);
488 } else {
489 prototype = isolate->factory()->NewFunctionPrototype(function);
490 }
491 DCHECK(map->has_fast_object_elements());
492
493 // Finally link initial map and constructor function.
494 DCHECK(prototype->IsJSReceiver());
495 JSFunction::SetInitialMap(function, map, prototype);
496 map->StartInobjectSlackTracking();
497 }
498
499 namespace {
500
501 #ifdef DEBUG
CanSubclassHaveInobjectProperties(InstanceType instance_type)502 bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
503 switch (instance_type) {
504 case JS_API_OBJECT_TYPE:
505 case JS_ARRAY_BUFFER_TYPE:
506 case JS_ARRAY_TYPE:
507 case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
508 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
509 case JS_DATA_VIEW_TYPE:
510 case JS_DATE_TYPE:
511 case JS_FUNCTION_TYPE:
512 case JS_GENERATOR_OBJECT_TYPE:
513 #ifdef V8_INTL_SUPPORT
514 case JS_COLLATOR_TYPE:
515 case JS_DATE_TIME_FORMAT_TYPE:
516 case JS_DISPLAY_NAMES_TYPE:
517 case JS_LIST_FORMAT_TYPE:
518 case JS_LOCALE_TYPE:
519 case JS_NUMBER_FORMAT_TYPE:
520 case JS_PLURAL_RULES_TYPE:
521 case JS_RELATIVE_TIME_FORMAT_TYPE:
522 case JS_SEGMENT_ITERATOR_TYPE:
523 case JS_SEGMENTER_TYPE:
524 case JS_SEGMENTS_TYPE:
525 case JS_V8_BREAK_ITERATOR_TYPE:
526 #endif
527 case JS_ASYNC_FUNCTION_OBJECT_TYPE:
528 case JS_ASYNC_GENERATOR_OBJECT_TYPE:
529 case JS_MAP_TYPE:
530 case JS_MESSAGE_OBJECT_TYPE:
531 case JS_OBJECT_TYPE:
532 case JS_ERROR_TYPE:
533 case JS_FINALIZATION_REGISTRY_TYPE:
534 case JS_ARGUMENTS_OBJECT_TYPE:
535 case JS_PROMISE_TYPE:
536 case JS_REG_EXP_TYPE:
537 case JS_SET_TYPE:
538 case JS_SPECIAL_API_OBJECT_TYPE:
539 case JS_TYPED_ARRAY_TYPE:
540 case JS_PRIMITIVE_WRAPPER_TYPE:
541 case JS_WEAK_MAP_TYPE:
542 case JS_WEAK_REF_TYPE:
543 case JS_WEAK_SET_TYPE:
544 case WASM_GLOBAL_OBJECT_TYPE:
545 case WASM_INSTANCE_OBJECT_TYPE:
546 case WASM_MEMORY_OBJECT_TYPE:
547 case WASM_MODULE_OBJECT_TYPE:
548 case WASM_TABLE_OBJECT_TYPE:
549 return true;
550
551 case BIGINT_TYPE:
552 case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
553 case BYTECODE_ARRAY_TYPE:
554 case BYTE_ARRAY_TYPE:
555 case CELL_TYPE:
556 case CODE_TYPE:
557 case FILLER_TYPE:
558 case FIXED_ARRAY_TYPE:
559 case SCRIPT_CONTEXT_TABLE_TYPE:
560 case FIXED_DOUBLE_ARRAY_TYPE:
561 case FEEDBACK_METADATA_TYPE:
562 case FOREIGN_TYPE:
563 case FREE_SPACE_TYPE:
564 case HASH_TABLE_TYPE:
565 case ORDERED_HASH_MAP_TYPE:
566 case ORDERED_HASH_SET_TYPE:
567 case ORDERED_NAME_DICTIONARY_TYPE:
568 case NAME_DICTIONARY_TYPE:
569 case GLOBAL_DICTIONARY_TYPE:
570 case NUMBER_DICTIONARY_TYPE:
571 case SIMPLE_NUMBER_DICTIONARY_TYPE:
572 case HEAP_NUMBER_TYPE:
573 case JS_BOUND_FUNCTION_TYPE:
574 case JS_GLOBAL_OBJECT_TYPE:
575 case JS_GLOBAL_PROXY_TYPE:
576 case JS_PROXY_TYPE:
577 case MAP_TYPE:
578 case ODDBALL_TYPE:
579 case PROPERTY_CELL_TYPE:
580 case SHARED_FUNCTION_INFO_TYPE:
581 case SYMBOL_TYPE:
582 case ALLOCATION_SITE_TYPE:
583
584 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
585 case FIXED_##TYPE##_ARRAY_TYPE:
586 #undef TYPED_ARRAY_CASE
587
588 #define MAKE_STRUCT_CASE(TYPE, Name, name) case TYPE:
589 STRUCT_LIST(MAKE_STRUCT_CASE)
590 #undef MAKE_STRUCT_CASE
591 // We must not end up here for these instance types at all.
592 UNREACHABLE();
593 // Fall through.
594 default:
595 return false;
596 }
597 }
598 #endif // DEBUG
599
FastInitializeDerivedMap(Isolate * isolate,Handle<JSFunction> new_target,Handle<JSFunction> constructor,Handle<Map> constructor_initial_map)600 bool FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
601 Handle<JSFunction> constructor,
602 Handle<Map> constructor_initial_map) {
603 // Use the default intrinsic prototype instead.
604 if (!new_target->has_prototype_slot()) return false;
605 // Check that |function|'s initial map still in sync with the |constructor|,
606 // otherwise we must create a new initial map for |function|.
607 if (new_target->has_initial_map() &&
608 new_target->initial_map().GetConstructor() == *constructor) {
609 DCHECK(new_target->instance_prototype().IsJSReceiver());
610 return true;
611 }
612 InstanceType instance_type = constructor_initial_map->instance_type();
613 DCHECK(CanSubclassHaveInobjectProperties(instance_type));
614 // Create a new map with the size and number of in-object properties
615 // suggested by |function|.
616
617 // Link initial map and constructor function if the new.target is actually a
618 // subclass constructor.
619 if (!IsDerivedConstructor(new_target->shared().kind())) return false;
620
621 int instance_size;
622 int in_object_properties;
623 int embedder_fields =
624 JSObject::GetEmbedderFieldCount(*constructor_initial_map);
625 // Constructor expects certain number of in-object properties to be in the
626 // object. However, CalculateExpectedNofProperties() may return smaller value
627 // if 1) the constructor is not in the prototype chain of new_target, or
628 // 2) the prototype chain is modified during iteration, or 3) compilation
629 // failure occur during prototype chain iteration.
630 // So we take the maximum of two values.
631 int expected_nof_properties = std::max(
632 static_cast<int>(constructor->shared().expected_nof_properties()),
633 JSFunction::CalculateExpectedNofProperties(isolate, new_target));
634 JSFunction::CalculateInstanceSizeHelper(
635 instance_type, true, embedder_fields, expected_nof_properties,
636 &instance_size, &in_object_properties);
637
638 int pre_allocated = constructor_initial_map->GetInObjectProperties() -
639 constructor_initial_map->UnusedPropertyFields();
640 CHECK_LE(constructor_initial_map->UsedInstanceSize(), instance_size);
641 int unused_property_fields = in_object_properties - pre_allocated;
642 Handle<Map> map =
643 Map::CopyInitialMap(isolate, constructor_initial_map, instance_size,
644 in_object_properties, unused_property_fields);
645 map->set_new_target_is_base(false);
646 Handle<HeapObject> prototype(new_target->instance_prototype(), isolate);
647 JSFunction::SetInitialMap(new_target, map, prototype);
648 DCHECK(new_target->instance_prototype().IsJSReceiver());
649 map->SetConstructor(*constructor);
650 map->set_construction_counter(Map::kNoSlackTracking);
651 map->StartInobjectSlackTracking();
652 return true;
653 }
654
655 } // namespace
656
657 // static
GetDerivedMap(Isolate * isolate,Handle<JSFunction> constructor,Handle<JSReceiver> new_target)658 MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
659 Handle<JSFunction> constructor,
660 Handle<JSReceiver> new_target) {
661 EnsureHasInitialMap(constructor);
662
663 Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
664 if (*new_target == *constructor) return constructor_initial_map;
665
666 Handle<Map> result_map;
667 // Fast case, new.target is a subclass of constructor. The map is cacheable
668 // (and may already have been cached). new.target.prototype is guaranteed to
669 // be a JSReceiver.
670 if (new_target->IsJSFunction()) {
671 Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
672 if (FastInitializeDerivedMap(isolate, function, constructor,
673 constructor_initial_map)) {
674 return handle(function->initial_map(), isolate);
675 }
676 }
677
678 // Slow path, new.target is either a proxy or can't cache the map.
679 // new.target.prototype is not guaranteed to be a JSReceiver, and may need to
680 // fall back to the intrinsicDefaultProto.
681 Handle<Object> prototype;
682 if (new_target->IsJSFunction()) {
683 Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
684 if (function->has_prototype_slot()) {
685 // Make sure the new.target.prototype is cached.
686 EnsureHasInitialMap(function);
687 prototype = handle(function->prototype(), isolate);
688 } else {
689 // No prototype property, use the intrinsict default proto further down.
690 prototype = isolate->factory()->undefined_value();
691 }
692 } else {
693 Handle<String> prototype_string = isolate->factory()->prototype_string();
694 ASSIGN_RETURN_ON_EXCEPTION(
695 isolate, prototype,
696 JSReceiver::GetProperty(isolate, new_target, prototype_string), Map);
697 // The above prototype lookup might change the constructor and its
698 // prototype, hence we have to reload the initial map.
699 EnsureHasInitialMap(constructor);
700 constructor_initial_map = handle(constructor->initial_map(), isolate);
701 }
702
703 // If prototype is not a JSReceiver, fetch the intrinsicDefaultProto from the
704 // correct realm. Rather than directly fetching the .prototype, we fetch the
705 // constructor that points to the .prototype. This relies on
706 // constructor.prototype being FROZEN for those constructors.
707 if (!prototype->IsJSReceiver()) {
708 Handle<Context> context;
709 ASSIGN_RETURN_ON_EXCEPTION(isolate, context,
710 JSReceiver::GetFunctionRealm(new_target), Map);
711 DCHECK(context->IsNativeContext());
712 Handle<Object> maybe_index = JSReceiver::GetDataProperty(
713 constructor, isolate->factory()->native_context_index_symbol());
714 int index = maybe_index->IsSmi() ? Smi::ToInt(*maybe_index)
715 : Context::OBJECT_FUNCTION_INDEX;
716 Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index)),
717 isolate);
718 prototype = handle(realm_constructor->prototype(), isolate);
719 }
720
721 Handle<Map> map = Map::CopyInitialMap(isolate, constructor_initial_map);
722 map->set_new_target_is_base(false);
723 CHECK(prototype->IsJSReceiver());
724 if (map->prototype() != *prototype)
725 Map::SetPrototype(isolate, map, Handle<HeapObject>::cast(prototype));
726 map->SetConstructor(*constructor);
727 return map;
728 }
729
ComputeInstanceSizeWithMinSlack(Isolate * isolate)730 int JSFunction::ComputeInstanceSizeWithMinSlack(Isolate* isolate) {
731 CHECK(has_initial_map());
732 if (initial_map().IsInobjectSlackTrackingInProgress()) {
733 int slack = initial_map().ComputeMinObjectSlack(isolate);
734 return initial_map().InstanceSizeFromSlack(slack);
735 }
736 return initial_map().instance_size();
737 }
738
PrintName(FILE * out)739 void JSFunction::PrintName(FILE* out) {
740 std::unique_ptr<char[]> name = shared().DebugName().ToCString();
741 PrintF(out, "%s", name.get());
742 }
743
GetName(Handle<JSFunction> function)744 Handle<String> JSFunction::GetName(Handle<JSFunction> function) {
745 Isolate* isolate = function->GetIsolate();
746 Handle<Object> name =
747 JSReceiver::GetDataProperty(function, isolate->factory()->name_string());
748 if (name->IsString()) return Handle<String>::cast(name);
749 return handle(function->shared().DebugName(), isolate);
750 }
751
GetDebugName(Handle<JSFunction> function)752 Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
753 Isolate* isolate = function->GetIsolate();
754 Handle<Object> name = JSReceiver::GetDataProperty(
755 function, isolate->factory()->display_name_string());
756 if (name->IsString()) return Handle<String>::cast(name);
757 return JSFunction::GetName(function);
758 }
759
SetName(Handle<JSFunction> function,Handle<Name> name,Handle<String> prefix)760 bool JSFunction::SetName(Handle<JSFunction> function, Handle<Name> name,
761 Handle<String> prefix) {
762 Isolate* isolate = function->GetIsolate();
763 Handle<String> function_name;
764 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, function_name,
765 Name::ToFunctionName(isolate, name), false);
766 if (prefix->length() > 0) {
767 IncrementalStringBuilder builder(isolate);
768 builder.AppendString(prefix);
769 builder.AppendCharacter(' ');
770 builder.AppendString(function_name);
771 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, function_name, builder.Finish(),
772 false);
773 }
774 RETURN_ON_EXCEPTION_VALUE(
775 isolate,
776 JSObject::DefinePropertyOrElementIgnoreAttributes(
777 function, isolate->factory()->name_string(), function_name,
778 static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY)),
779 false);
780 return true;
781 }
782
783 namespace {
784
NativeCodeFunctionSourceString(Handle<SharedFunctionInfo> shared_info)785 Handle<String> NativeCodeFunctionSourceString(
786 Handle<SharedFunctionInfo> shared_info) {
787 Isolate* const isolate = shared_info->GetIsolate();
788 IncrementalStringBuilder builder(isolate);
789 builder.AppendCString("function ");
790 builder.AppendString(handle(shared_info->Name(), isolate));
791 builder.AppendCString("() { [native code] }");
792 return builder.Finish().ToHandleChecked();
793 }
794
795 } // namespace
796
797 // static
ToString(Handle<JSFunction> function)798 Handle<String> JSFunction::ToString(Handle<JSFunction> function) {
799 Isolate* const isolate = function->GetIsolate();
800 Handle<SharedFunctionInfo> shared_info(function->shared(), isolate);
801
802 // Check if {function} should hide its source code.
803 if (!shared_info->IsUserJavaScript()) {
804 return NativeCodeFunctionSourceString(shared_info);
805 }
806
807 // Check if we should print {function} as a class.
808 Handle<Object> maybe_class_positions = JSReceiver::GetDataProperty(
809 function, isolate->factory()->class_positions_symbol());
810 if (maybe_class_positions->IsClassPositions()) {
811 ClassPositions class_positions =
812 ClassPositions::cast(*maybe_class_positions);
813 int start_position = class_positions.start();
814 int end_position = class_positions.end();
815 Handle<String> script_source(
816 String::cast(Script::cast(shared_info->script()).source()), isolate);
817 return isolate->factory()->NewSubString(script_source, start_position,
818 end_position);
819 }
820
821 // Check if we have source code for the {function}.
822 if (!shared_info->HasSourceCode()) {
823 return NativeCodeFunctionSourceString(shared_info);
824 }
825
826 // If this function was compiled from asm.js, use the recorded offset
827 // information.
828 if (shared_info->HasWasmExportedFunctionData()) {
829 Handle<WasmExportedFunctionData> function_data(
830 shared_info->wasm_exported_function_data(), isolate);
831 const wasm::WasmModule* module = function_data->instance().module();
832 if (is_asmjs_module(module)) {
833 std::pair<int, int> offsets =
834 module->asm_js_offset_information->GetFunctionOffsets(
835 declared_function_index(module, function_data->function_index()));
836 Handle<String> source(
837 String::cast(Script::cast(shared_info->script()).source()), isolate);
838 return isolate->factory()->NewSubString(source, offsets.first,
839 offsets.second);
840 }
841 }
842
843 if (shared_info->function_token_position() == kNoSourcePosition) {
844 // If the function token position isn't valid, return [native code] to
845 // ensure calling eval on the returned source code throws rather than
846 // giving inconsistent call behaviour.
847 isolate->CountUsage(
848 v8::Isolate::UseCounterFeature::kFunctionTokenOffsetTooLongForToString);
849 return NativeCodeFunctionSourceString(shared_info);
850 }
851 return Handle<String>::cast(
852 SharedFunctionInfo::GetSourceCodeHarmony(shared_info));
853 }
854
855 // static
CalculateExpectedNofProperties(Isolate * isolate,Handle<JSFunction> function)856 int JSFunction::CalculateExpectedNofProperties(Isolate* isolate,
857 Handle<JSFunction> function) {
858 int expected_nof_properties = 0;
859 for (PrototypeIterator iter(isolate, function, kStartAtReceiver);
860 !iter.IsAtEnd(); iter.Advance()) {
861 Handle<JSReceiver> current =
862 PrototypeIterator::GetCurrent<JSReceiver>(iter);
863 if (!current->IsJSFunction()) break;
864 Handle<JSFunction> func = Handle<JSFunction>::cast(current);
865 // The super constructor should be compiled for the number of expected
866 // properties to be available.
867 Handle<SharedFunctionInfo> shared(func->shared(), isolate);
868 IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate));
869 if (is_compiled_scope.is_compiled() ||
870 Compiler::Compile(func, Compiler::CLEAR_EXCEPTION,
871 &is_compiled_scope)) {
872 DCHECK(shared->is_compiled());
873 int count = shared->expected_nof_properties();
874 // Check that the estimate is sensible.
875 if (expected_nof_properties <= JSObject::kMaxInObjectProperties - count) {
876 expected_nof_properties += count;
877 } else {
878 return JSObject::kMaxInObjectProperties;
879 }
880 } else {
881 // In case there was a compilation error proceed iterating in case there
882 // will be a builtin function in the prototype chain that requires
883 // certain number of in-object properties.
884 continue;
885 }
886 }
887 // Inobject slack tracking will reclaim redundant inobject space
888 // later, so we can afford to adjust the estimate generously,
889 // meaning we over-allocate by at least 8 slots in the beginning.
890 if (expected_nof_properties > 0) {
891 expected_nof_properties += 8;
892 if (expected_nof_properties > JSObject::kMaxInObjectProperties) {
893 expected_nof_properties = JSObject::kMaxInObjectProperties;
894 }
895 }
896 return expected_nof_properties;
897 }
898
899 // static
CalculateInstanceSizeHelper(InstanceType instance_type,bool has_prototype_slot,int requested_embedder_fields,int requested_in_object_properties,int * instance_size,int * in_object_properties)900 void JSFunction::CalculateInstanceSizeHelper(InstanceType instance_type,
901 bool has_prototype_slot,
902 int requested_embedder_fields,
903 int requested_in_object_properties,
904 int* instance_size,
905 int* in_object_properties) {
906 DCHECK_LE(static_cast<unsigned>(requested_embedder_fields),
907 JSObject::kMaxEmbedderFields);
908 int header_size = JSObject::GetHeaderSize(instance_type, has_prototype_slot);
909 if (requested_embedder_fields) {
910 // If there are embedder fields, then the embedder fields start offset must
911 // be properly aligned (embedder fields are located between object header
912 // and inobject fields).
913 header_size = RoundUp<kSystemPointerSize>(header_size);
914 requested_embedder_fields *= kEmbedderDataSlotSizeInTaggedSlots;
915 }
916 int max_nof_fields =
917 (JSObject::kMaxInstanceSize - header_size) >> kTaggedSizeLog2;
918 CHECK_LE(max_nof_fields, JSObject::kMaxInObjectProperties);
919 CHECK_LE(static_cast<unsigned>(requested_embedder_fields),
920 static_cast<unsigned>(max_nof_fields));
921 *in_object_properties = std::min(requested_in_object_properties,
922 max_nof_fields - requested_embedder_fields);
923 *instance_size =
924 header_size +
925 ((requested_embedder_fields + *in_object_properties) << kTaggedSizeLog2);
926 CHECK_EQ(*in_object_properties,
927 ((*instance_size - header_size) >> kTaggedSizeLog2) -
928 requested_embedder_fields);
929 CHECK_LE(static_cast<unsigned>(*instance_size),
930 static_cast<unsigned>(JSObject::kMaxInstanceSize));
931 }
932
ClearTypeFeedbackInfo()933 void JSFunction::ClearTypeFeedbackInfo() {
934 ResetIfBytecodeFlushed();
935 if (has_feedback_vector()) {
936 FeedbackVector vector = feedback_vector();
937 Isolate* isolate = GetIsolate();
938 if (vector.ClearSlots(isolate)) {
939 IC::OnFeedbackChanged(isolate, vector, FeedbackSlot::Invalid(),
940 "ClearTypeFeedbackInfo");
941 }
942 }
943 }
944
945 } // namespace internal
946 } // namespace v8
947
948 #include "src/objects/object-macros-undef.h"
949