// 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/api/api-inl.h" #include "src/builtins/builtins-utils-inl.h" #include "src/builtins/builtins.h" #include "src/debug/interface-types.h" #include "src/logging/counters.h" #include "src/logging/log.h" #include "src/objects/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(Profile, profile) \ V(ProfileEnd, profileEnd) \ V(TimeLog, timeLog) namespace { void ConsoleCall( Isolate* isolate, internal::BuiltinArguments& args, // NOLINT(runtime/references) 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); // Access check. The current context has to match the context of all // arguments, otherwise the inspector might leak objects across contexts. Handle context = handle(isolate->context(), isolate); for (int i = 0; i < args.length(); ++i) { Handle argument = args.at(i); if (!argument->IsJSObject()) continue; Handle argument_obj = Handle::cast(argument); if (argument->IsAccessCheckNeeded(isolate) && !isolate->MayAccess(context, argument_obj)) { isolate->ReportFailedAccessCheck(argument_obj); return; } } debug::ConsoleCallArguments wrapper(args); Handle context_id_obj = JSObject::GetDataProperty( args.target(), isolate->factory()->console_context_id_symbol()); int context_id = context_id_obj->IsSmi() ? Handle::cast(context_id_obj)->value() : 0; Handle context_name_obj = JSObject::GetDataProperty( args.target(), isolate->factory()->console_context_name_symbol()); Handle context_name = context_name_obj->IsString() ? Handle::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 name; const char* raw_name = "default"; if (args.length() > 1 && args[1].IsString()) { // Try converting the first argument to a string. name = args.at(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 ReadOnlyRoots(isolate).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 ReadOnlyRoots(isolate).undefined_value(); } BUILTIN(ConsoleTimeEnd) { LogTimerEvent(isolate, args, Logger::END); ConsoleCall(isolate, args, &debug::ConsoleDelegate::TimeEnd); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); return ReadOnlyRoots(isolate).undefined_value(); } BUILTIN(ConsoleTimeStamp) { LogTimerEvent(isolate, args, Logger::STAMP); ConsoleCall(isolate, args, &debug::ConsoleDelegate::TimeStamp); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); return ReadOnlyRoots(isolate).undefined_value(); } namespace { void InstallContextFunction(Isolate* isolate, Handle target, const char* name, Builtins::Name builtin_id, int context_id, Handle context_name) { Factory* const factory = isolate->factory(); Handle name_string = Name::ToFunctionName(isolate, factory->InternalizeUtf8String(name)) .ToHandleChecked(); NewFunctionArgs args = NewFunctionArgs::ForBuiltinWithoutPrototype( name_string, builtin_id, i::LanguageMode::kSloppy); Handle fun = factory->NewFunction(args); fun->shared().set_native(true); fun->shared().DontAdaptArguments(); fun->shared().set_length(1); JSObject::AddProperty(isolate, fun, factory->console_context_id_symbol(), handle(Smi::FromInt(context_id), isolate), NONE); if (context_name->IsString()) { JSObject::AddProperty(isolate, fun, factory->console_context_name_symbol(), context_name, NONE); } JSObject::AddProperty(isolate, target, name_string, fun, NONE); } } // namespace BUILTIN(ConsoleContext) { HandleScope scope(isolate); Factory* const factory = isolate->factory(); Handle name = factory->InternalizeUtf8String("Context"); NewFunctionArgs arguments = NewFunctionArgs::ForFunctionWithoutCode( name, isolate->sloppy_function_map(), LanguageMode::kSloppy); Handle cons = factory->NewFunction(arguments); Handle prototype = factory->NewJSObject(isolate->object_function()); JSFunction::SetPrototype(cons, prototype); Handle context = factory->NewJSObject(cons, AllocationType::kOld); 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(isolate, context, #name, Builtins::kConsole##call, \ id, args.at(1)); CONSOLE_METHOD_LIST(CONSOLE_BUILTIN_SETUP) #undef CONSOLE_BUILTIN_SETUP InstallContextFunction(isolate, context, "time", Builtins::kConsoleTime, id, args.at(1)); InstallContextFunction(isolate, context, "timeEnd", Builtins::kConsoleTimeEnd, id, args.at(1)); InstallContextFunction(isolate, context, "timeStamp", Builtins::kConsoleTimeStamp, id, args.at(1)); return *context; } #undef CONSOLE_METHOD_LIST } // namespace internal } // namespace v8