v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-console.cc
Go to the documentation of this file.
1// Copyright 2017 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 <stack>
6
7#include "src/api/api-inl.h"
12#include "src/logging/log.h"
14
15namespace v8 {
16namespace internal {
17
18// -----------------------------------------------------------------------------
19// Console
20
21#define CONSOLE_METHOD_LIST(V) \
22 V(Dir, dir) \
23 V(DirXml, dirXml) \
24 V(Table, table) \
25 V(GroupEnd, groupEnd) \
26 V(Clear, clear) \
27 V(Count, count) \
28 V(CountReset, countReset) \
29 V(Profile, profile) \
30 V(ProfileEnd, profileEnd)
31
32#define CONSOLE_METHOD_WITH_FORMATTER_LIST(V) \
33 V(Debug, debug, 1) \
34 V(Error, error, 1) \
35 V(Info, info, 1) \
36 V(Log, log, 1) \
37 V(Warn, warn, 1) \
38 V(Trace, trace, 1) \
39 V(Group, group, 1) \
40 V(GroupCollapsed, groupCollapsed, 1) \
41 V(Assert, assert, 2)
42
43namespace {
44
45// 2.2 Formatter(args) [https://console.spec.whatwg.org/#formatter]
46//
47// This implements the formatter operation defined in the Console
48// specification to the degree that it makes sense for V8. That
49// means we primarily deal with %s, %i, %f, and %d, and any side
50// effects caused by the type conversions, and we preserve the %o,
51// %c, and %O specifiers and their parameters unchanged, and instead
52// leave it to the debugger front-end to make sense of those.
53//
54// Chrome also supports the non-standard bypass format specifier %_
55// which just skips over the parameter.
56//
57// This implementation updates the |args| in-place with the results
58// from the conversion.
59//
60// The |index| describes the position of the format string within,
61// |args| (starting with 1, since |args| also includes the receiver),
62// which is different for example in case of `console.log` where it
63// is 1 compared to `console.assert` where it is 2.
64bool Formatter(Isolate* isolate, BuiltinArguments& args, int index) {
65 if (args.length() < index + 2 || !IsString(args[index])) {
66 return true;
67 }
68 struct State {
70 int off;
71 };
72 std::stack<State> states;
73 HandleScope scope(isolate);
74 auto percent = isolate->factory()->percent_sign_string();
75 states.push({args.at<String>(index++), 0});
76 while (!states.empty() && index < args.length()) {
77 State& state = states.top();
78 state.off = String::IndexOf(isolate, state.str, percent, state.off);
79 if (state.off < 0 ||
80 state.off == static_cast<int>(state.str->length()) - 1) {
81 states.pop();
82 continue;
83 }
84 IndirectHandle<Object> current = args.at(index);
85 uint16_t specifier = state.str->Get(state.off + 1, isolate);
86 if (specifier == 'd' || specifier == 'f' || specifier == 'i') {
87 if (IsSymbol(*current)) {
88 current = isolate->factory()->nan_value();
89 } else {
90 DirectHandle<Object> params[] = {
91 current, isolate->factory()->NewNumberFromInt(10)};
92 auto builtin = specifier == 'f' ? isolate->global_parse_float_fun()
93 : isolate->global_parse_int_fun();
94 if (!Execution::CallBuiltin(isolate, builtin,
95 isolate->factory()->undefined_value(),
96 base::VectorOf(params))
97 .ToHandle(&current)) {
98 return false;
99 }
100 }
101 } else if (specifier == 's') {
102 DirectHandle<Object> params[] = {current};
103 if (!Execution::CallBuiltin(isolate, isolate->string_function(),
104 isolate->factory()->undefined_value(),
105 base::VectorOf(params))
106 .ToHandle(&current)) {
107 return false;
108 }
109
110 // Recurse into string results from type conversions, as they
111 // can themselves contain formatting specifiers.
112 states.push({Cast<String>(current), 0});
113 } else if (specifier == 'c' || specifier == 'o' || specifier == 'O' ||
114 specifier == '_') {
115 // We leave the interpretation of %c (CSS), %o (optimally useful
116 // formatting), and %O (generic JavaScript object formatting) as
117 // well as the non-standard %_ (bypass formatter in Chrome) to
118 // the debugger front-end, and preserve these specifiers as well
119 // as their arguments verbatim.
120 index++;
121 state.off += 2;
122 continue;
123 } else if (specifier == '%') {
124 // Chrome also supports %% as a way to generate a single % in the
125 // output.
126 state.off += 2;
127 continue;
128 } else {
129 state.off++;
130 continue;
131 }
132
133 // Replace the |specifier| (including the '%' character) in |target|
134 // with the |current| value. We perform the replacement only morally
135 // by updating the argument to the conversion result, but leave it to
136 // the debugger front-end to perform the actual substitution.
137 args.set_at(index++, *current);
138 state.off += 2;
139 }
140 return true;
141}
142
143// The closures installed on objects returned from `console.context()`
144// get a special builtin context with 2 slots, to hold the unique ID of
145// the console context and its name.
146enum {
147 CONSOLE_CONTEXT_ID_INDEX = Context::MIN_CONTEXT_SLOTS,
148 CONSOLE_CONTEXT_NAME_INDEX,
149 CONSOLE_CONTEXT_SLOTS,
150};
151
152void ConsoleCall(
153 Isolate* isolate, const internal::BuiltinArguments& args,
154 void (debug::ConsoleDelegate::*func)(const v8::debug::ConsoleCallArguments&,
156 if (isolate->is_execution_terminating()) return;
157 CHECK(!isolate->has_exception());
158 if (!isolate->console_delegate()) return;
159 HandleScope scope(isolate);
160 int context_id = 0;
161 DirectHandle<String> context_name = isolate->factory()->anonymous_string();
162 if (!IsNativeContext(args.target()->context())) {
163 DirectHandle<Context> context(args.target()->context(), isolate);
164 CHECK_EQ(CONSOLE_CONTEXT_SLOTS, context->length());
165 context_id = Cast<Smi>(context->get(CONSOLE_CONTEXT_ID_INDEX)).value();
166 context_name = direct_handle(
167 Cast<String>(context->get(CONSOLE_CONTEXT_NAME_INDEX)), isolate);
168 }
169 (isolate->console_delegate()->*func)(
170 debug::ConsoleCallArguments(isolate, args),
171 v8::debug::ConsoleContext(context_id, Utils::ToLocal(context_name)));
172}
173
174void LogTimerEvent(Isolate* isolate, BuiltinArguments args,
176 if (!v8_flags.log_timer_events) return;
177 HandleScope scope(isolate);
178 std::unique_ptr<char[]> name;
179 const char* raw_name = "default";
180 if (args.length() > 1 && IsString(args[1])) {
181 // Try converting the first argument to a string.
182 name = args.at<String>(1)->ToCString();
183 raw_name = name.get();
184 }
185 LOG(isolate, TimerEvent(se, raw_name));
186}
187
188} // namespace
189
190#define CONSOLE_BUILTIN_IMPLEMENTATION(call, name) \
191 BUILTIN(Console##call) { \
192 ConsoleCall(isolate, args, &debug::ConsoleDelegate::call); \
193 RETURN_FAILURE_IF_EXCEPTION(isolate); \
194 return ReadOnlyRoots(isolate).undefined_value(); \
195 }
197#undef CONSOLE_BUILTIN_IMPLEMENTATION
198
199#define CONSOLE_BUILTIN_IMPLEMENTATION(call, name, index) \
200 BUILTIN(Console##call) { \
201 if (!Formatter(isolate, args, index)) { \
202 return ReadOnlyRoots(isolate).exception(); \
203 } \
204 ConsoleCall(isolate, args, &debug::ConsoleDelegate::call); \
205 RETURN_FAILURE_IF_EXCEPTION(isolate); \
206 return ReadOnlyRoots(isolate).undefined_value(); \
207 }
209#undef CONSOLE_BUILTIN_IMPLEMENTATION
210
211BUILTIN(ConsoleTime) {
212 LogTimerEvent(isolate, args, v8::LogEventStatus::kStart);
213 ConsoleCall(isolate, args, &debug::ConsoleDelegate::Time);
215 return ReadOnlyRoots(isolate).undefined_value();
216}
217
218BUILTIN(ConsoleTimeEnd) {
219 LogTimerEvent(isolate, args, v8::LogEventStatus::kEnd);
220 ConsoleCall(isolate, args, &debug::ConsoleDelegate::TimeEnd);
222 return ReadOnlyRoots(isolate).undefined_value();
223}
224
225BUILTIN(ConsoleTimeLog) {
226 LogTimerEvent(isolate, args, v8::LogEventStatus::kLog);
227 ConsoleCall(isolate, args, &debug::ConsoleDelegate::TimeLog);
229 return ReadOnlyRoots(isolate).undefined_value();
230}
231
232BUILTIN(ConsoleTimeStamp) {
233 ConsoleCall(isolate, args, &debug::ConsoleDelegate::TimeStamp);
235 return ReadOnlyRoots(isolate).undefined_value();
236}
237
238namespace {
239
240void InstallContextFunction(Isolate* isolate, DirectHandle<JSObject> target,
241 const char* name, Builtin builtin,
242 DirectHandle<Context> context) {
243 Factory* const factory = isolate->factory();
244
245 DirectHandle<Map> map = isolate->sloppy_function_without_prototype_map();
246
247 DirectHandle<String> name_string = factory->InternalizeUtf8String(name);
248
249 DirectHandle<SharedFunctionInfo> info =
250 factory->NewSharedFunctionInfoForBuiltin(name_string, builtin, 1,
251 kDontAdapt);
252 info->set_language_mode(LanguageMode::kSloppy);
253 info->set_native(true);
254
255 DirectHandle<JSFunction> fun =
256 Factory::JSFunctionBuilder{isolate, info, context}.set_map(map).Build();
257
258 JSObject::AddProperty(isolate, target, name_string, fun, NONE);
259}
260
261} // namespace
262
263BUILTIN(ConsoleContext) {
264 HandleScope scope(isolate);
265 Factory* const factory = isolate->factory();
266
268
269 // Generate a unique ID for the new `console.context`
270 // and convert the parameter to a string (defaults to
271 // 'anonymous' if unspecified).
272 DirectHandle<String> context_name = factory->anonymous_string();
273 if (args.length() > 1) {
274 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, context_name,
275 Object::ToString(isolate, args.at(1)));
276 }
277 int context_id = isolate->last_console_context_id() + 1;
278 isolate->set_last_console_context_id(context_id);
279
282 factory->InternalizeUtf8String("Context"), Builtin::kIllegal, 0,
283 kDontAdapt);
284 info->set_language_mode(LanguageMode::kSloppy);
285
288 .Build();
289
290 DirectHandle<JSObject> prototype =
291 factory->NewJSObject(isolate->object_function());
292 JSFunction::SetPrototype(cons, prototype);
293
294 DirectHandle<JSObject> console_context =
295 factory->NewJSObject(cons, AllocationType::kOld);
296 DCHECK(IsJSObject(*console_context));
297
298 DirectHandle<Context> context = factory->NewBuiltinContext(
299 isolate->native_context(), CONSOLE_CONTEXT_SLOTS);
300 context->set(CONSOLE_CONTEXT_ID_INDEX, Smi::FromInt(context_id));
301 context->set(CONSOLE_CONTEXT_NAME_INDEX, *context_name);
302
303#define CONSOLE_BUILTIN_SETUP(call, name, ...) \
304 InstallContextFunction(isolate, console_context, #name, \
305 Builtin::kConsole##call, context);
308 CONSOLE_BUILTIN_SETUP(Time, time)
309 CONSOLE_BUILTIN_SETUP(TimeLog, timeLog)
310 CONSOLE_BUILTIN_SETUP(TimeEnd, timeEnd)
311 CONSOLE_BUILTIN_SETUP(TimeStamp, timeStamp)
312#undef CONSOLE_BUILTIN_SETUP
313
314 return *console_context;
315}
316
317#undef CONSOLE_METHOD_LIST
318
319} // namespace internal
320} // namespace v8
#define CONSOLE_BUILTIN_IMPLEMENTATION(call, name)
#define CONSOLE_BUILTIN_SETUP(call, name,...)
#define CONSOLE_METHOD_WITH_FORMATTER_LIST(V)
#define CONSOLE_METHOD_LIST(V)
#define BUILTIN(name)
virtual void TimeLog(const ConsoleCallArguments &args, const ConsoleContext &context)
virtual void TimeEnd(const ConsoleCallArguments &args, const ConsoleContext &context)
virtual void TimeStamp(const ConsoleCallArguments &args, const ConsoleContext &context)
virtual void Time(const ConsoleCallArguments &args, const ConsoleContext &context)
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > CallBuiltin(Isolate *isolate, DirectHandle< JSFunction > builtin, DirectHandle< Object > receiver, base::Vector< const DirectHandle< Object > > args)
Definition execution.cc:545
V8_WARN_UNUSED_RESULT Handle< JSFunction > Build()
Definition factory.cc:4732
Handle< SharedFunctionInfo > NewSharedFunctionInfoForBuiltin(MaybeDirectHandle< String > name, Builtin builtin, int len, AdaptArguments adapt, FunctionKind kind=FunctionKind::kNormalFunction)
Definition factory.cc:3904
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
DirectHandle< Context > NewBuiltinContext(DirectHandle< NativeContext > native_context, int length)
Definition factory.cc:1490
Handle< String > InternalizeUtf8String(base::Vector< const char > str)
Definition factory.cc:608
Handle< NativeContext > native_context()
Definition isolate-inl.h:48
static void SetPrototype(DirectHandle< JSFunction > function, DirectHandle< Object > value)
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
static V8_WARN_UNUSED_RESULT HandleType< String >::MaybeType ToString(Isolate *isolate, HandleType< T > input)
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static Tagged< Object > IndexOf(Isolate *isolate, DirectHandle< Object > receiver, DirectHandle< Object > search, DirectHandle< Object > position)
Definition string.cc:1426
Handle< SharedFunctionInfo > info
LineAndColumn current
#define RETURN_FAILURE_IF_EXCEPTION(isolate)
Definition isolate.h:205
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:284
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
Isolate * isolate
TNode< Context > context
#define LOG(isolate, Call)
Definition log.h:78
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
Handle< T > IndirectHandle
Definition globals.h:1086
constexpr AdaptArguments kDontAdapt
Definition globals.h:2776
V8_EXPORT_PRIVATE FlagValues v8_flags
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
LogEventStatus
@ kStart
#define CHECK(condition)
Definition logging.h:124
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482