V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
builtins.cc
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/builtins.h"
6 
7 #include "src/api-inl.h"
8 #include "src/assembler-inl.h"
9 #include "src/builtins/builtins-descriptors.h"
10 #include "src/callable.h"
11 #include "src/code-tracer.h"
12 #include "src/isolate.h"
13 #include "src/macro-assembler.h"
14 #include "src/objects-inl.h"
15 #include "src/objects/fixed-array.h"
16 #include "src/snapshot/embedded-data.h"
17 #include "src/visitors.h"
18 
19 namespace v8 {
20 namespace internal {
21 
22 // Forward declarations for C++ builtins.
23 #define FORWARD_DECLARE(Name) \
24  Object* Builtin_##Name(int argc, Address* args, Isolate* isolate);
25 BUILTIN_LIST_C(FORWARD_DECLARE)
26 #undef FORWARD_DECLARE
27 
28 namespace {
29 
30 // TODO(jgruber): Pack in CallDescriptors::Key.
31 struct BuiltinMetadata {
32  const char* name;
33  Builtins::Kind kind;
34  union {
35  Address cpp_entry; // For CPP and API builtins.
36  int8_t parameter_count; // For TFJ builtins.
37  } kind_specific_data;
38 };
39 
40 // clang-format off
41 #define DECL_CPP(Name, ...) { #Name, Builtins::CPP, \
42  { FUNCTION_ADDR(Builtin_##Name) }},
43 #define DECL_API(Name, ...) { #Name, Builtins::API, \
44  { FUNCTION_ADDR(Builtin_##Name) }},
45 #ifdef V8_TARGET_BIG_ENDIAN
46 #define DECL_TFJ(Name, Count, ...) { #Name, Builtins::TFJ, \
47  { static_cast<Address>(static_cast<uintptr_t>( \
48  Count) << (kBitsPerByte * (kPointerSize - 1))) }},
49 #else
50 #define DECL_TFJ(Name, Count, ...) { #Name, Builtins::TFJ, \
51  { static_cast<Address>(Count) }},
52 #endif
53 #define DECL_TFC(Name, ...) { #Name, Builtins::TFC, {} },
54 #define DECL_TFS(Name, ...) { #Name, Builtins::TFS, {} },
55 #define DECL_TFH(Name, ...) { #Name, Builtins::TFH, {} },
56 #define DECL_BCH(Name, ...) { #Name, Builtins::BCH, {} },
57 #define DECL_ASM(Name, ...) { #Name, Builtins::ASM, {} },
58 const BuiltinMetadata builtin_metadata[] = {
59  BUILTIN_LIST(DECL_CPP, DECL_API, DECL_TFJ, DECL_TFC, DECL_TFS, DECL_TFH,
60  DECL_BCH, DECL_ASM)
61 };
62 #undef DECL_CPP
63 #undef DECL_API
64 #undef DECL_TFJ
65 #undef DECL_TFC
66 #undef DECL_TFS
67 #undef DECL_TFH
68 #undef DECL_BCH
69 #undef DECL_ASM
70 // clang-format on
71 
72 } // namespace
73 
74 BailoutId Builtins::GetContinuationBailoutId(Name name) {
75  DCHECK(Builtins::KindOf(name) == TFJ || Builtins::KindOf(name) == TFC);
76  return BailoutId(BailoutId::kFirstBuiltinContinuationId + name);
77 }
78 
79 Builtins::Name Builtins::GetBuiltinFromBailoutId(BailoutId id) {
80  int builtin_index = id.ToInt() - BailoutId::kFirstBuiltinContinuationId;
81  DCHECK(Builtins::KindOf(builtin_index) == TFJ ||
82  Builtins::KindOf(builtin_index) == TFC);
83  return static_cast<Name>(builtin_index);
84 }
85 
86 void Builtins::TearDown() { initialized_ = false; }
87 
88 const char* Builtins::Lookup(Address pc) {
89  // Off-heap pc's can be looked up through binary search.
90  if (FLAG_embedded_builtins) {
91  Code maybe_builtin = InstructionStream::TryLookupCode(isolate_, pc);
92  if (!maybe_builtin.is_null()) return name(maybe_builtin->builtin_index());
93  }
94 
95  // May be called during initialization (disassembler).
96  if (initialized_) {
97  for (int i = 0; i < builtin_count; i++) {
98  if (isolate_->heap()->builtin(i)->contains(pc)) return name(i);
99  }
100  }
101  return nullptr;
102 }
103 
104 Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) {
105  switch (scope_type) {
106  case ScopeType::EVAL_SCOPE:
107  return builtin_handle(kFastNewFunctionContextEval);
108  case ScopeType::FUNCTION_SCOPE:
109  return builtin_handle(kFastNewFunctionContextFunction);
110  default:
111  UNREACHABLE();
112  }
113  return Handle<Code>::null();
114 }
115 
116 Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) {
117  switch (hint) {
118  case ToPrimitiveHint::kDefault:
119  return builtin_handle(kNonPrimitiveToPrimitive_Default);
120  case ToPrimitiveHint::kNumber:
121  return builtin_handle(kNonPrimitiveToPrimitive_Number);
122  case ToPrimitiveHint::kString:
123  return builtin_handle(kNonPrimitiveToPrimitive_String);
124  }
125  UNREACHABLE();
126 }
127 
128 Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) {
129  switch (hint) {
130  case OrdinaryToPrimitiveHint::kNumber:
131  return builtin_handle(kOrdinaryToPrimitive_Number);
132  case OrdinaryToPrimitiveHint::kString:
133  return builtin_handle(kOrdinaryToPrimitive_String);
134  }
135  UNREACHABLE();
136 }
137 
138 void Builtins::set_builtin(int index, Code builtin) {
139  isolate_->heap()->set_builtin(index, builtin);
140 }
141 
142 Code Builtins::builtin(int index) { return isolate_->heap()->builtin(index); }
143 
144 Handle<Code> Builtins::builtin_handle(int index) {
145  DCHECK(IsBuiltinId(index));
146  return Handle<Code>(
147  reinterpret_cast<Address*>(isolate_->heap()->builtin_address(index)));
148 }
149 
150 // static
151 int Builtins::GetStackParameterCount(Name name) {
152  DCHECK(Builtins::KindOf(name) == TFJ);
153  return builtin_metadata[name].kind_specific_data.parameter_count;
154 }
155 
156 // static
157 Callable Builtins::CallableFor(Isolate* isolate, Name name) {
158  Handle<Code> code = isolate->builtins()->builtin_handle(name);
159  CallDescriptors::Key key;
160  switch (name) {
161 // This macro is deliberately crafted so as to emit very little code,
162 // in order to keep binary size of this function under control.
163 #define CASE_OTHER(Name, ...) \
164  case k##Name: { \
165  key = Builtin_##Name##_InterfaceDescriptor::key(); \
166  break; \
167  }
168  BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE_OTHER,
169  CASE_OTHER, CASE_OTHER, IGNORE_BUILTIN, IGNORE_BUILTIN)
170 #undef CASE_OTHER
171  default:
172  Builtins::Kind kind = Builtins::KindOf(name);
173  DCHECK_NE(BCH, kind);
174  if (kind == TFJ || kind == CPP) {
175  return Callable(code, JSTrampolineDescriptor{});
176  }
177  UNREACHABLE();
178  }
179  CallInterfaceDescriptor descriptor(key);
180  return Callable(code, descriptor);
181 }
182 
183 // static
184 const char* Builtins::name(int index) {
185  DCHECK(IsBuiltinId(index));
186  return builtin_metadata[index].name;
187 }
188 
189 void Builtins::PrintBuiltinCode() {
190  DCHECK(FLAG_print_builtin_code);
191 #ifdef ENABLE_DISASSEMBLER
192  for (int i = 0; i < builtin_count; i++) {
193  const char* builtin_name = name(i);
194  Handle<Code> code = builtin_handle(i);
195  if (PassesFilter(CStrVector(builtin_name),
196  CStrVector(FLAG_print_builtin_code_filter))) {
197  CodeTracer::Scope trace_scope(isolate_->GetCodeTracer());
198  OFStream os(trace_scope.file());
199  code->Disassemble(builtin_name, os);
200  os << "\n";
201  }
202  }
203 #endif
204 }
205 
206 void Builtins::PrintBuiltinSize() {
207  DCHECK(FLAG_print_builtin_size);
208  for (int i = 0; i < builtin_count; i++) {
209  const char* builtin_name = name(i);
210  const char* kind = KindNameOf(i);
211  Code code = builtin(i);
212  PrintF(stdout, "%s Builtin, %s, %d\n", kind, builtin_name,
213  code->InstructionSize());
214  }
215 }
216 
217 // static
218 Address Builtins::CppEntryOf(int index) {
219  DCHECK(Builtins::HasCppImplementation(index));
220  return builtin_metadata[index].kind_specific_data.cpp_entry;
221 }
222 
223 // static
224 bool Builtins::IsBuiltin(const Code code) {
225  return Builtins::IsBuiltinId(code->builtin_index());
226 }
227 
228 bool Builtins::IsBuiltinHandle(Handle<HeapObject> maybe_code,
229  int* index) const {
230  Heap* heap = isolate_->heap();
231  Address handle_location = maybe_code.address();
232  Address start = heap->builtin_address(0);
233  Address end = heap->builtin_address(Builtins::builtin_count);
234  if (handle_location >= end) return false;
235  if (handle_location < start) return false;
236  *index = static_cast<int>(handle_location - start) >> kPointerSizeLog2;
237  DCHECK(Builtins::IsBuiltinId(*index));
238  return true;
239 }
240 
241 // static
242 bool Builtins::IsIsolateIndependentBuiltin(const Code code) {
243  if (FLAG_embedded_builtins) {
244  const int builtin_index = code->builtin_index();
245  return Builtins::IsBuiltinId(builtin_index) &&
246  Builtins::IsIsolateIndependent(builtin_index);
247  } else {
248  return false;
249  }
250 }
251 
252 // static
253 bool Builtins::IsWasmRuntimeStub(int index) {
254  DCHECK(IsBuiltinId(index));
255  switch (index) {
256 #define CASE_TRAP(Name) case kThrowWasm##Name:
257 #define CASE(Name) case k##Name:
258  WASM_RUNTIME_STUB_LIST(CASE, CASE_TRAP)
259 #undef CASE_TRAP
260 #undef CASE
261  return true;
262  default:
263  return false;
264  }
265  UNREACHABLE();
266 }
267 
268 namespace {
269 
270 class OffHeapTrampolineGenerator {
271  public:
272  explicit OffHeapTrampolineGenerator(Isolate* isolate)
273  : isolate_(isolate),
274  masm_(isolate, buffer, kBufferSize, CodeObjectRequired::kYes) {}
275 
276  CodeDesc Generate(Address off_heap_entry) {
277  // Generate replacement code that simply tail-calls the off-heap code.
278  DCHECK(!masm_.has_frame());
279  {
280  FrameScope scope(&masm_, StackFrame::NONE);
281  masm_.JumpToInstructionStream(off_heap_entry);
282  }
283 
284  CodeDesc desc;
285  masm_.GetCode(isolate_, &desc);
286  return desc;
287  }
288 
289  Handle<HeapObject> CodeObject() { return masm_.CodeObject(); }
290 
291  private:
292  Isolate* isolate_;
293  // Enough to fit the single jmp.
294  static constexpr size_t kBufferSize = 256;
295  byte buffer[kBufferSize];
296  MacroAssembler masm_;
297 };
298 
299 } // namespace
300 
301 // static
302 Handle<Code> Builtins::GenerateOffHeapTrampolineFor(Isolate* isolate,
303  Address off_heap_entry) {
304  DCHECK_NOT_NULL(isolate->embedded_blob());
305  DCHECK_NE(0, isolate->embedded_blob_size());
306 
307  OffHeapTrampolineGenerator generator(isolate);
308  CodeDesc desc = generator.Generate(off_heap_entry);
309 
310  return isolate->factory()->NewCode(desc, Code::BUILTIN,
311  generator.CodeObject());
312 }
313 
314 // static
315 Handle<ByteArray> Builtins::GenerateOffHeapTrampolineRelocInfo(
316  Isolate* isolate) {
317  OffHeapTrampolineGenerator generator(isolate);
318  // Generate a jump to a dummy address as we're not actually interested in the
319  // generated instruction stream.
320  CodeDesc desc = generator.Generate(kNullAddress);
321 
322  Handle<ByteArray> reloc_info =
323  isolate->factory()->NewByteArray(desc.reloc_size, TENURED_READ_ONLY);
324  Code::CopyRelocInfoToByteArray(*reloc_info, desc);
325 
326  return reloc_info;
327 }
328 
329 // static
330 Builtins::Kind Builtins::KindOf(int index) {
331  DCHECK(IsBuiltinId(index));
332  return builtin_metadata[index].kind;
333 }
334 
335 // static
336 const char* Builtins::KindNameOf(int index) {
337  Kind kind = Builtins::KindOf(index);
338  // clang-format off
339  switch (kind) {
340  case CPP: return "CPP";
341  case API: return "API";
342  case TFJ: return "TFJ";
343  case TFC: return "TFC";
344  case TFS: return "TFS";
345  case TFH: return "TFH";
346  case BCH: return "BCH";
347  case ASM: return "ASM";
348  }
349  // clang-format on
350  UNREACHABLE();
351 }
352 
353 // static
354 bool Builtins::IsCpp(int index) { return Builtins::KindOf(index) == CPP; }
355 
356 // static
357 bool Builtins::HasCppImplementation(int index) {
358  Kind kind = Builtins::KindOf(index);
359  return (kind == CPP || kind == API);
360 }
361 
362 // static
363 bool Builtins::AllowDynamicFunction(Isolate* isolate, Handle<JSFunction> target,
364  Handle<JSObject> target_global_proxy) {
365  if (FLAG_allow_unsafe_function_constructor) return true;
366  HandleScopeImplementer* impl = isolate->handle_scope_implementer();
367  Handle<Context> responsible_context = impl->LastEnteredOrMicrotaskContext();
368  // TODO(jochen): Remove this.
369  if (responsible_context.is_null()) {
370  return true;
371  }
372  if (*responsible_context == target->context()) return true;
373  return isolate->MayAccess(responsible_context, target_global_proxy);
374 }
375 
376 Builtins::Name ExampleBuiltinForTorqueFunctionPointerType(
377  size_t function_pointer_type_id) {
378  switch (function_pointer_type_id) {
379 #define FUNCTION_POINTER_ID_CASE(id, name) \
380  case id: \
381  return Builtins::k##name;
382  TORQUE_FUNCTION_POINTER_TYPE_TO_BUILTIN_MAP(FUNCTION_POINTER_ID_CASE)
383 #undef FUNCTION_POINTER_ID_CASE
384  default:
385  UNREACHABLE();
386  }
387 }
388 
389 } // namespace internal
390 } // namespace v8
Definition: libplatform.h:13