text: // Copyright 2017 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-utils.h"
  #include "src/builtins/builtins.h"
  
  #include "src/api.h"
  #include "src/debug/interface-types.h"
  #include "src/objects-inl.h"
  
  namespace v8 {
  namespace internal {
  
  // -----------------------------------------------------------------------------
  // Console
  
  #define CONSOLE_METHOD_LIST(V)      \
    V(Debug, debug)                   \
    V(Error, error)                   \
    V(Info, info)                     \
    V(Log, log)                       \
    V(Warn, warn)                     \
    V(Dir, dir)                       \
    V(DirXml, dirXml)                 \
    V(Table, table)                   \
    V(Trace, trace)                   \
    V(Group, group)                   \
    V(GroupCollapsed, groupCollapsed) \
    V(GroupEnd, groupEnd)             \
    V(Clear, clear)                   \
    V(Count, count)                   \
    V(CountReset, countReset)         \
    V(Assert, assert)                 \
    V(MarkTimeline, markTimeline)     \
    V(Profile, profile)               \
    V(ProfileEnd, profileEnd)         \
    V(Timeline, timeline)             \
    V(TimelineEnd, timelineEnd)
  
  namespace {
  void ConsoleCall(
      Isolate* isolate, internal::BuiltinArguments& args,
      void (debug::ConsoleDelegate::*func)(const v8::debug::ConsoleCallArguments&,
                                           const v8::debug::ConsoleContext&)) {
    CHECK(!isolate->has_pending_exception());
    CHECK(!isolate->has_scheduled_exception());
    if (!isolate->console_delegate()) return;
    HandleScope scope(isolate);
    debug::ConsoleCallArguments wrapper(args);
    Handle<Object> context_id_obj = JSObject::GetDataProperty(
        args.target(), isolate->factory()->console_context_id_symbol());
    int context_id =
        context_id_obj->IsSmi() ? Handle<Smi>::cast(context_id_obj)->value() : 0;
    Handle<Object> context_name_obj = JSObject::GetDataProperty(
        args.target(), isolate->factory()->console_context_name_symbol());
    Handle<String> context_name = context_name_obj->IsString()
                                      ? Handle<String>::cast(context_name_obj)
                                      : isolate->factory()->anonymous_string();
    (isolate->console_delegate()->*func)(
        wrapper,
        v8::debug::ConsoleContext(context_id, Utils::ToLocal(context_name)));
  }
  
  void LogTimerEvent(Isolate* isolate, BuiltinArguments args,
                     Logger::StartEnd se) {
    if (!isolate->logger()->is_logging()) return;
    HandleScope scope(isolate);
    std::unique_ptr<char[]> name;
    const char* raw_name = "default";
    if (args.length() > 1 && args[1]->IsString()) {
      // Try converting the first argument to a string.
      name = args.at<String>(1)->ToCString();
      raw_name = name.get();
    }
    LOG(isolate, TimerEvent(se, raw_name));
  }
  }  // namespace
  
  #define CONSOLE_BUILTIN_IMPLEMENTATION(call, name)             \
    BUILTIN(Console##call) {                                     \
      ConsoleCall(isolate, args, &debug::ConsoleDelegate::call); \
      RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);            \
      return isolate->heap()->undefined_value();                 \
    }
  CONSOLE_METHOD_LIST(CONSOLE_BUILTIN_IMPLEMENTATION)
  #undef CONSOLE_BUILTIN_IMPLEMENTATION
  
  BUILTIN(ConsoleTime) {
    LogTimerEvent(isolate, args, Logger::START);
    ConsoleCall(isolate, args, &debug::ConsoleDelegate::Time);
    RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
    return isolate->heap()->undefined_value();
  }
  
  BUILTIN(ConsoleTimeEnd) {
    LogTimerEvent(isolate, args, Logger::END);
    ConsoleCall(isolate, args, &debug::ConsoleDelegate::TimeEnd);
    RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
    return isolate->heap()->undefined_value();
  }
  
  BUILTIN(ConsoleTimeStamp) {
    LogTimerEvent(isolate, args, Logger::STAMP);
    ConsoleCall(isolate, args, &debug::ConsoleDelegate::TimeStamp);
    RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
    return isolate->heap()->undefined_value();
  }
  
  namespace {
  void InstallContextFunction(Handle<JSObject> target, const char* name,
                              Builtins::Name builtin_id, int context_id,
                              Handle<Object> context_name) {
    Factory* const factory = target->GetIsolate()->factory();
  
    Handle<String> name_string =
        Name::ToFunctionName(factory->InternalizeUtf8String(name))
            .ToHandleChecked();
    NewFunctionArgs args = NewFunctionArgs::ForBuiltinWithoutPrototype(
        name_string, builtin_id, i::LanguageMode::kSloppy);
    Handle<JSFunction> fun = factory->NewFunction(args);
  
    fun->shared()->set_native(true);
    fun->shared()->DontAdaptArguments();
    fun->shared()->set_length(1);
  
    JSObject::AddProperty(fun, factory->console_context_id_symbol(),
                          handle(Smi::FromInt(context_id), target->GetIsolate()),
                          NONE);
    if (context_name->IsString()) {
      JSObject::AddProperty(fun, factory->console_context_name_symbol(),
                            context_name, NONE);
    }
    JSObject::AddProperty(target, name_string, fun, NONE);
  }
  }  // namespace
  
  BUILTIN(ConsoleContext) {
    HandleScope scope(isolate);
  
    Factory* const factory = isolate->factory();
    Handle<String> name = factory->InternalizeUtf8String("Context");
    NewFunctionArgs arguments = NewFunctionArgs::ForFunctionWithoutCode(
        name, isolate->sloppy_function_map(), LanguageMode::kSloppy);
    Handle<JSFunction> cons = factory->NewFunction(arguments);
  
    Handle<JSObject> prototype = factory->NewJSObject(isolate->object_function());
    JSFunction::SetPrototype(cons, prototype);
  
    Handle<JSObject> context = factory->NewJSObject(cons, TENURED);
    DCHECK(context->IsJSObject());
    int id = isolate->last_console_context_id() + 1;
    isolate->set_last_console_context_id(id);
  
  #define CONSOLE_BUILTIN_SETUP(call, name)                              \
    InstallContextFunction(context, #name, Builtins::kConsole##call, id, \
                           args.at(1));
    CONSOLE_METHOD_LIST(CONSOLE_BUILTIN_SETUP)
  #undef CONSOLE_BUILTIN_SETUP
    InstallContextFunction(context, "time", Builtins::kConsoleTime, id,
                           args.at(1));
    InstallContextFunction(context, "timeEnd", Builtins::kConsoleTimeEnd, id,
                           args.at(1));
    InstallContextFunction(context, "timeStamp", Builtins::kConsoleTimeStamp, id,
                           args.at(1));
  
    return *context;
  }
  
  #undef CONSOLE_METHOD_LIST
  
  }  // namespace internal
  }  // namespace v8
  