text: // Copyright 2012 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/builtins/builtins.h"
  #include "src/api.h"
  #include "src/assembler-inl.h"
  #include "src/builtins/builtins-descriptors.h"
  #include "src/callable.h"
  #include "src/isolate.h"
  #include "src/macro-assembler.h"
  #include "src/objects-inl.h"
  #include "src/visitors.h"
  
  namespace v8 {
  namespace internal {
  
  // Forward declarations for C++ builtins.
  #define FORWARD_DECLARE(Name) \
    Object* Builtin_##Name(int argc, Object** args, Isolate* isolate);
  BUILTIN_LIST_C(FORWARD_DECLARE)
  #undef FORWARD_DECLARE
  
  namespace {
  
  // TODO(jgruber): Pack in CallDescriptors::Key.
  struct BuiltinMetadata {
    const char* name;
    Builtins::Kind kind;
    union {
      Address cpp_entry;       // For CPP and API builtins.
      int8_t parameter_count;  // For TFJ builtins.
    } kind_specific_data;
  };
  
  // clang-format off
  #define DECL_CPP(Name, ...) { #Name, Builtins::CPP, \
                                { FUNCTION_ADDR(Builtin_##Name) }},
  #define DECL_API(Name, ...) { #Name, Builtins::API, \
                                { FUNCTION_ADDR(Builtin_##Name) }},
  #ifdef V8_TARGET_BIG_ENDIAN
  #define DECL_TFJ(Name, Count, ...) { #Name, Builtins::TFJ, \
    { static_cast<Address>(static_cast<uintptr_t>(           \
                                Count) << (kBitsPerByte * (kPointerSize - 1))) }},
  #else
  #define DECL_TFJ(Name, Count, ...) { #Name, Builtins::TFJ, \
                                { static_cast<Address>(Count) }},
  #endif
  #define DECL_TFC(Name, ...) { #Name, Builtins::TFC, {} },
  #define DECL_TFS(Name, ...) { #Name, Builtins::TFS, {} },
  #define DECL_TFH(Name, ...) { #Name, Builtins::TFH, {} },
  #define DECL_ASM(Name, ...) { #Name, Builtins::ASM, {} },
  const BuiltinMetadata builtin_metadata[] = {
    BUILTIN_LIST(DECL_CPP, DECL_API, DECL_TFJ, DECL_TFC, DECL_TFS, DECL_TFH,
                 DECL_ASM)
  };
  #undef DECL_CPP
  #undef DECL_API
  #undef DECL_TFJ
  #undef DECL_TFC
  #undef DECL_TFS
  #undef DECL_TFH
  #undef DECL_ASM
  // clang-format on
  
  }  // namespace
  
  Builtins::Builtins() : initialized_(false) {
    memset(builtins_, 0, sizeof(builtins_[0]) * builtin_count);
  }
  
  Builtins::~Builtins() {}
  
  BailoutId Builtins::GetContinuationBailoutId(Name name) {
    DCHECK(Builtins::KindOf(name) == TFJ || Builtins::KindOf(name) == TFC);
    return BailoutId(BailoutId::kFirstBuiltinContinuationId + name);
  }
  
  Builtins::Name Builtins::GetBuiltinFromBailoutId(BailoutId id) {
    int builtin_index = id.ToInt() - BailoutId::kFirstBuiltinContinuationId;
    DCHECK(Builtins::KindOf(builtin_index) == TFJ ||
           Builtins::KindOf(builtin_index) == TFC);
    return static_cast<Name>(builtin_index);
  }
  
  void Builtins::TearDown() { initialized_ = false; }
  
  void Builtins::IterateBuiltins(RootVisitor* v) {
    for (int i = 0; i < builtin_count; i++) {
      v->VisitRootPointer(Root::kBuiltins, name(i), &builtins_[i]);
    }
  }
  
  const char* Builtins::Lookup(Address pc) {
    // may be called during initialization (disassembler!)
    if (initialized_) {
      for (int i = 0; i < builtin_count; i++) {
        Code* entry = Code::cast(builtins_[i]);
        if (entry->contains(pc)) return name(i);
      }
    }
    return nullptr;
  }
  
  Handle<Code> Builtins::NewFunctionContext(ScopeType scope_type) {
    switch (scope_type) {
      case ScopeType::EVAL_SCOPE:
        return builtin_handle(kFastNewFunctionContextEval);
      case ScopeType::FUNCTION_SCOPE:
        return builtin_handle(kFastNewFunctionContextFunction);
      default:
        UNREACHABLE();
    }
    return Handle<Code>::null();
  }
  
  Handle<Code> Builtins::NonPrimitiveToPrimitive(ToPrimitiveHint hint) {
    switch (hint) {
      case ToPrimitiveHint::kDefault:
        return builtin_handle(kNonPrimitiveToPrimitive_Default);
      case ToPrimitiveHint::kNumber:
        return builtin_handle(kNonPrimitiveToPrimitive_Number);
      case ToPrimitiveHint::kString:
        return builtin_handle(kNonPrimitiveToPrimitive_String);
    }
    UNREACHABLE();
  }
  
  Handle<Code> Builtins::OrdinaryToPrimitive(OrdinaryToPrimitiveHint hint) {
    switch (hint) {
      case OrdinaryToPrimitiveHint::kNumber:
        return builtin_handle(kOrdinaryToPrimitive_Number);
      case OrdinaryToPrimitiveHint::kString:
        return builtin_handle(kOrdinaryToPrimitive_String);
    }
    UNREACHABLE();
  }
  
  void Builtins::set_builtin(int index, HeapObject* builtin) {
    DCHECK(Builtins::IsBuiltinId(index));
    DCHECK(Internals::HasHeapObjectTag(builtin));
    // The given builtin may be completely uninitialized thus we cannot check its
    // type here.
    builtins_[index] = builtin;
  }
  
  Handle<Code> Builtins::builtin_handle(int index) {
    DCHECK(IsBuiltinId(index));
    return Handle<Code>(reinterpret_cast<Code**>(builtin_address(index)));
  }
  
  // static
  int Builtins::GetStackParameterCount(Name name) {
    DCHECK(Builtins::KindOf(name) == TFJ);
    return builtin_metadata[name].kind_specific_data.parameter_count;
  }
  
  // static
  Callable Builtins::CallableFor(Isolate* isolate, Name name) {
    Handle<Code> code(
        reinterpret_cast<Code**>(isolate->builtins()->builtin_address(name)));
    CallDescriptors::Key key;
    switch (name) {
  // This macro is deliberately crafted so as to emit very little code,
  // in order to keep binary size of this function under control.
  #define CASE_OTHER(Name, ...)                          \
    case k##Name: {                                      \
      key = Builtin_##Name##_InterfaceDescriptor::key(); \
      break;                                             \
    }
      BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN, IGNORE_BUILTIN, CASE_OTHER,
                   CASE_OTHER, CASE_OTHER, IGNORE_BUILTIN)
  #undef CASE_OTHER
      default:
        Builtins::Kind kind = Builtins::KindOf(name);
        if (kind == TFJ || kind == CPP) {
          return Callable(code, BuiltinDescriptor(isolate));
        }
        UNREACHABLE();
    }
    CallInterfaceDescriptor descriptor(isolate, key);
    return Callable(code, descriptor);
  }
  
  // static
  const char* Builtins::name(int index) {
    DCHECK(IsBuiltinId(index));
    return builtin_metadata[index].name;
  }
  
  // static
  Address Builtins::CppEntryOf(int index) {
    DCHECK(Builtins::HasCppImplementation(index));
    return builtin_metadata[index].kind_specific_data.cpp_entry;
  }
  
  // static
  bool Builtins::IsBuiltin(const Code* code) {
    return Builtins::IsBuiltinId(code->builtin_index());
  }
  
  // static
  bool Builtins::IsEmbeddedBuiltin(const Code* code) {
  #ifdef V8_EMBEDDED_BUILTINS
    return Builtins::IsBuiltinId(code->builtin_index()) &&
           Builtins::IsIsolateIndependent(code->builtin_index());
  #else
    return false;
  #endif
  }
  
  // static
  bool Builtins::IsLazy(int index) {
    DCHECK(IsBuiltinId(index));
  
  #ifdef V8_EMBEDDED_BUILTINS
    // We don't want to lazy-deserialize off-heap builtins.
    if (Builtins::IsIsolateIndependent(index)) return false;
  #endif
  
    // There are a couple of reasons that builtins can require eager-loading,
    // i.e. deserialization at isolate creation instead of on-demand. For
    // instance:
    // * DeserializeLazy implements lazy loading.
    // * Immovability requirement. This can only conveniently be guaranteed at
    //   isolate creation (at runtime, we'd have to allocate in LO space).
    // * To avoid conflicts in SharedFunctionInfo::function_data (Illegal,
    //   HandleApiCall, interpreter entry trampolines).
    // * Frequent use makes lazy loading unnecessary (CompileLazy).
    // TODO(wasm): Remove wasm builtins once immovability is no longer required.
    switch (index) {
      case kAbort:  // Required by wasm.
      case kArrayEveryLoopEagerDeoptContinuation:
      case kArrayEveryLoopLazyDeoptContinuation:
      case kArrayFilterLoopEagerDeoptContinuation:
      case kArrayFilterLoopLazyDeoptContinuation:
      case kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation:
      case kArrayFindIndexLoopEagerDeoptContinuation:
      case kArrayFindIndexLoopLazyDeoptContinuation:
      case kArrayFindLoopAfterCallbackLazyDeoptContinuation:
      case kArrayFindLoopEagerDeoptContinuation:
      case kArrayFindLoopLazyDeoptContinuation:
      case kArrayForEachLoopEagerDeoptContinuation:
      case kArrayForEachLoopLazyDeoptContinuation:
      case kArrayMapLoopEagerDeoptContinuation:
      case kArrayMapLoopLazyDeoptContinuation:
      case kArrayReduceLoopEagerDeoptContinuation:
      case kArrayReduceLoopLazyDeoptContinuation:
      case kArrayReducePreLoopEagerDeoptContinuation:
      case kArrayReduceRightLoopEagerDeoptContinuation:
      case kArrayReduceRightLoopLazyDeoptContinuation:
      case kArrayReduceRightPreLoopEagerDeoptContinuation:
      case kArraySomeLoopEagerDeoptContinuation:
      case kArraySomeLoopLazyDeoptContinuation:
      case kAsyncGeneratorAwaitCaught:            // https://crbug.com/v8/6786.
      case kAsyncGeneratorAwaitUncaught:          // https://crbug.com/v8/6786.
      // CEntry variants must be immovable, whereas lazy deserialization allocates
      // movable code.
      case kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
      case kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
      case kCEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
      case kCEntry_Return1_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
      case kCEntry_Return1_SaveFPRegs_ArgvOnStack_BuiltinExit:
      case kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit:
      case kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_BuiltinExit:
      case kCEntry_Return2_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit:
      case kCEntry_Return2_SaveFPRegs_ArgvOnStack_NoBuiltinExit:
      case kCEntry_Return2_SaveFPRegs_ArgvOnStack_BuiltinExit:
      case kCompileLazy:
      case kDebugBreakTrampoline:
      case kDeserializeLazy:
      case kFunctionPrototypeHasInstance:  // https://crbug.com/v8/6786.
      case kHandleApiCall:
      case kIllegal:
      case kInstantiateAsmJs:
      case kInterpreterEnterBytecodeAdvance:
      case kInterpreterEnterBytecodeDispatch:
      case kInterpreterEntryTrampoline:
      case kPromiseConstructorLazyDeoptContinuation:
      case kRecordWrite:  // https://crbug.com/chromium/765301.
      case kThrowWasmTrapDivByZero:             // Required by wasm.
      case kThrowWasmTrapDivUnrepresentable:    // Required by wasm.
      case kThrowWasmTrapFloatUnrepresentable:  // Required by wasm.
      case kThrowWasmTrapFuncInvalid:           // Required by wasm.
      case kThrowWasmTrapFuncSigMismatch:       // Required by wasm.
      case kThrowWasmTrapMemOutOfBounds:        // Required by wasm.
      case kThrowWasmTrapRemByZero:             // Required by wasm.
      case kThrowWasmTrapUnreachable:           // Required by wasm.
      case kToBooleanLazyDeoptContinuation:
      case kToNumber:                           // Required by wasm.
      case kTypedArrayConstructorLazyDeoptContinuation:
      case kWasmCompileLazy:                    // Required by wasm.
      case kWasmStackGuard:                     // Required by wasm.
        return false;
      default:
        // TODO(6624): Extend to other kinds.
        return KindOf(index) == TFJ;
    }
    UNREACHABLE();
  }
  
  // static
  bool Builtins::IsIsolateIndependent(int index) {
    DCHECK(IsBuiltinId(index));
    // TODO(jgruber): There's currently two blockers for moving
    // InterpreterEntryTrampoline into the binary:
    // 1. InterpreterEnterBytecode calculates a pointer into the middle of
    //    InterpreterEntryTrampoline (see interpreter_entry_return_pc_offset).
    //    When the builtin is embedded, the pointer would need to be calculated
    //    at an offset from the embedded instruction stream (instead of the
    //    trampoline code object).
    // 2. We create distinct copies of the trampoline to make it possible to
    //    attribute ticks in the interpreter to individual JS functions.
    //    See https://crrev.com/c/959081 and InstallBytecodeArray. When the
    //    trampoline is embedded, we need to ensure that CopyCode creates a copy
    //    of the builtin itself (and not just the trampoline).
    return index != kInterpreterEntryTrampoline;
  }
  
  #ifdef V8_EMBEDDED_BUILTINS
  // static
  Handle<Code> Builtins::GenerateOffHeapTrampolineFor(Isolate* isolate,
                                                      Address off_heap_entry) {
    DCHECK(isolate->serializer_enabled());
    DCHECK_NOT_NULL(isolate->embedded_blob());
    DCHECK_NE(0, isolate->embedded_blob_size());
  
    constexpr size_t buffer_size = 256;  // Enough to fit the single jmp.
    byte buffer[buffer_size];            // NOLINT(runtime/arrays)
  
    // Generate replacement code that simply tail-calls the off-heap code.
    MacroAssembler masm(isolate, buffer, buffer_size, CodeObjectRequired::kYes);
    DCHECK(!masm.has_frame());
    {
      FrameScope scope(&masm, StackFrame::NONE);
      masm.JumpToInstructionStream(off_heap_entry);
    }
  
    CodeDesc desc;
    masm.GetCode(isolate, &desc);
  
    return isolate->factory()->NewCode(desc, Code::BUILTIN, masm.CodeObject());
  }
  #endif  // V8_EMBEDDED_BUILTINS
  
  // static
  Builtins::Kind Builtins::KindOf(int index) {
    DCHECK(IsBuiltinId(index));
    return builtin_metadata[index].kind;
  }
  
  // static
  const char* Builtins::KindNameOf(int index) {
    Kind kind = Builtins::KindOf(index);
    // clang-format off
    switch (kind) {
      case CPP: return "CPP";
      case API: return "API";
      case TFJ: return "TFJ";
      case TFC: return "TFC";
      case TFS: return "TFS";
      case TFH: return "TFH";
      case ASM: return "ASM";
    }
    // clang-format on
    UNREACHABLE();
  }
  
  // static
  bool Builtins::IsCpp(int index) { return Builtins::KindOf(index) == CPP; }
  
  // static
  bool Builtins::HasCppImplementation(int index) {
    Kind kind = Builtins::KindOf(index);
    return (kind == CPP || kind == API);
  }
  
  // static
  bool Builtins::AllowDynamicFunction(Isolate* isolate, Handle<JSFunction> target,
                                      Handle<JSObject> target_global_proxy) {
    if (FLAG_allow_unsafe_function_constructor) return true;
    HandleScopeImplementer* impl = isolate->handle_scope_implementer();
    Handle<Context> responsible_context =
        impl->MicrotaskContextIsLastEnteredContext() ? impl->MicrotaskContext()
                                                     : impl->LastEnteredContext();
    // TODO(jochen): Remove this.
    if (responsible_context.is_null()) {
      return true;
    }
    if (*responsible_context == target->context()) return true;
    return isolate->MayAccess(responsible_context, target_global_proxy);
  }
  
  }  // namespace internal
  }  // namespace v8
  