1// Copyright 2016 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#ifndef V8_BUILTINS_BUILTINS_UTILS_H_
6#define V8_BUILTINS_BUILTINS_UTILS_H_
7
8#include "src/base/logging.h"
9#include "src/builtins/builtins.h"
10#include "src/execution/arguments.h"
11#include "src/execution/isolate.h"
12#include "src/heap/factory.h"
13#include "src/logging/runtime-call-stats-scope.h"
14
15namespace v8 {
16namespace internal {
17
18// Arguments object passed to C++ builtins.
19class BuiltinArguments : public JavaScriptArguments {
20 public:
21  BuiltinArguments(int length, Address* arguments)
22      : Arguments(length, arguments) {
23    // Check we have at least the receiver.
24    DCHECK_LE(1, this->length());
25  }
26
27  Object operator[](int index) const {
28    DCHECK_LT(index, length());
29    return Object(*address_of_arg_at(index + kArgsOffset));
30  }
31
32  template <class S = Object>
33  Handle<S> at(int index) const {
34    DCHECK_LT(index, length());
35    return Handle<S>(address_of_arg_at(index + kArgsOffset));
36  }
37
38  inline void set_at(int index, Object value) {
39    DCHECK_LT(index, length());
40    *address_of_arg_at(index + kArgsOffset) = value.ptr();
41  }
42
43  // Note: this should return the address after the receiver,
44  // even when length() == 1.
45  inline Address* address_of_first_argument() const {
46    return address_of_arg_at(kArgsOffset + 1);  // Skips receiver.
47  }
48
49  static constexpr int kNewTargetOffset = 0;
50  static constexpr int kTargetOffset = 1;
51  static constexpr int kArgcOffset = 2;
52  static constexpr int kPaddingOffset = 3;
53
54  static constexpr int kNumExtraArgs = 4;
55  static constexpr int kNumExtraArgsWithReceiver = 5;
56  static constexpr int kArgsOffset = 4;
57
58  inline Handle<Object> atOrUndefined(Isolate* isolate, int index) const;
59  inline Handle<Object> receiver() const;
60  inline Handle<JSFunction> target() const;
61  inline Handle<HeapObject> new_target() const;
62
63  // Gets the total number of arguments including the receiver (but
64  // excluding extra arguments).
65  int length() const { return Arguments::length() - kNumExtraArgs; }
66};
67
68// ----------------------------------------------------------------------------
69// Support macro for defining builtins in C++.
70// ----------------------------------------------------------------------------
71//
72// A builtin function is defined by writing:
73//
74//   BUILTIN(name) {
75//     ...
76//   }
77//
78// In the body of the builtin function the arguments can be accessed
79// through the BuiltinArguments object args.
80// TODO(cbruni): add global flag to check whether any tracing events have been
81// enabled.
82#define BUILTIN_RCS(name)                                                   \
83  V8_WARN_UNUSED_RESULT static Object Builtin_Impl_##name(                  \
84      BuiltinArguments args, Isolate* isolate);                             \
85                                                                            \
86  V8_NOINLINE static Address Builtin_Impl_Stats_##name(                     \
87      int args_length, Address* args_object, Isolate* isolate) {            \
88    BuiltinArguments args(args_length, args_object);                        \
89    RCS_SCOPE(isolate, RuntimeCallCounterId::kBuiltin_##name);              \
90    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.runtime"),                   \
91                 "V8.Builtin_" #name);                                      \
92    return CONVERT_OBJECT(Builtin_Impl_##name(args, isolate));              \
93  }                                                                         \
94                                                                            \
95  V8_WARN_UNUSED_RESULT Address Builtin_##name(                             \
96      int args_length, Address* args_object, Isolate* isolate) {            \
97    DCHECK(isolate->context().is_null() || isolate->context().IsContext()); \
98    if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) {            \
99      return Builtin_Impl_Stats_##name(args_length, args_object, isolate);  \
100    }                                                                       \
101    BuiltinArguments args(args_length, args_object);                        \
102    return CONVERT_OBJECT(Builtin_Impl_##name(args, isolate));              \
103  }                                                                         \
104                                                                            \
105  V8_WARN_UNUSED_RESULT static Object Builtin_Impl_##name(                  \
106      BuiltinArguments args, Isolate* isolate)
107
108#define BUILTIN_NO_RCS(name)                                                \
109  V8_WARN_UNUSED_RESULT static Object Builtin_Impl_##name(                  \
110      BuiltinArguments args, Isolate* isolate);                             \
111                                                                            \
112  V8_WARN_UNUSED_RESULT Address Builtin_##name(                             \
113      int args_length, Address* args_object, Isolate* isolate) {            \
114    DCHECK(isolate->context().is_null() || isolate->context().IsContext()); \
115    BuiltinArguments args(args_length, args_object);                        \
116    return CONVERT_OBJECT(Builtin_Impl_##name(args, isolate));              \
117  }                                                                         \
118                                                                            \
119  V8_WARN_UNUSED_RESULT static Object Builtin_Impl_##name(                  \
120      BuiltinArguments args, Isolate* isolate)
121
122#ifdef V8_RUNTIME_CALL_STATS
123#define BUILTIN(name) BUILTIN_RCS(name)
124#else  // V8_RUNTIME_CALL_STATS
125#define BUILTIN(name) BUILTIN_NO_RCS(name)
126#endif  // V8_RUNTIME_CALL_STATS
127// ----------------------------------------------------------------------------
128
129#define CHECK_RECEIVER(Type, name, method)                                  \
130  if (!args.receiver()->Is##Type()) {                                       \
131    THROW_NEW_ERROR_RETURN_FAILURE(                                         \
132        isolate,                                                            \
133        NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,          \
134                     isolate->factory()->NewStringFromAsciiChecked(method), \
135                     args.receiver()));                                     \
136  }                                                                         \
137  Handle<Type> name = Handle<Type>::cast(args.receiver())
138
139// Throws a TypeError for {method} if the receiver is not coercible to Object,
140// or converts the receiver to a String otherwise and assigns it to a new var
141// with the given {name}.
142#define TO_THIS_STRING(name, method)                                          \
143  if (args.receiver()->IsNullOrUndefined(isolate)) {                          \
144    THROW_NEW_ERROR_RETURN_FAILURE(                                           \
145        isolate,                                                              \
146        NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,               \
147                     isolate->factory()->NewStringFromAsciiChecked(method))); \
148  }                                                                           \
149  Handle<String> name;                                                        \
150  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(                                         \
151      isolate, name, Object::ToString(isolate, args.receiver()))
152
153}  // namespace internal
154}  // namespace v8
155
156#endif  // V8_BUILTINS_BUILTINS_UTILS_H_
157