Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/CallArgs.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * vim: set ts=8 sts=4 et sw=4 tw=99:
3
 * This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
 * [SMDOC] JS::CallArgs API
9
 *
10
 * Helper classes encapsulating access to the callee, |this| value, arguments,
11
 * and argument count for a call/construct operation.
12
 *
13
 * JS::CallArgs encapsulates access to a JSNative's un-abstracted
14
 * |unsigned argc, Value* vp| arguments.  The principal way to create a
15
 * JS::CallArgs is using JS::CallArgsFromVp:
16
 *
17
 *   // If provided no arguments or a non-numeric first argument, return zero.
18
 *   // Otherwise return |this| exactly as given, without boxing.
19
 *   static bool
20
 *   Func(JSContext* cx, unsigned argc, JS::Value* vp)
21
 *   {
22
 *       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
23
 *
24
 *       // Guard against no arguments or a non-numeric arg0.
25
 *       if (args.length() == 0 || !args[0].isNumber()) {
26
 *           args.rval().setInt32(0);
27
 *           return true;
28
 *       }
29
 *
30
 *       // Access to the callee must occur before accessing/setting
31
 *       // the return value.
32
 *       JSObject& callee = args.callee();
33
 *       args.rval().setObject(callee);
34
 *
35
 *       // callee() and calleev() will now assert.
36
 *
37
 *       // It's always fine to access thisv().
38
 *       HandleValue thisv = args.thisv();
39
 *       args.rval().set(thisv);
40
 *
41
 *       // As the return value was last set to |this|, returns |this|.
42
 *       return true;
43
 *   }
44
 *
45
 * CallArgs is exposed publicly and used internally.  Not all parts of its
46
 * public interface are meant to be used by embedders!  See inline comments to
47
 * for details.
48
 *
49
 * It's possible (albeit deprecated) to manually index into |vp| to access the
50
 * callee, |this|, and arguments of a function, and to set its return value.
51
 * This does not have the error-handling or moving-GC correctness of CallArgs.
52
 * New code should use CallArgs instead whenever possible.
53
 *
54
 * The eventual plan is to change JSNative to take |const CallArgs&| directly,
55
 * for automatic assertion of correct use and to make calling functions more
56
 * efficient.  Embedders should start internally switching away from using
57
 * |argc| and |vp| directly, except to create a |CallArgs|.  Then, when an
58
 * eventual release making that change occurs, porting efforts will require
59
 * changing methods' signatures but won't require invasive changes to the
60
 * methods' implementations, potentially under time pressure.
61
 */
62
63
#ifndef js_CallArgs_h
64
#define js_CallArgs_h
65
66
#include "mozilla/Assertions.h"
67
#include "mozilla/Attributes.h"
68
#include "mozilla/TypeTraits.h"
69
70
#include "jstypes.h"
71
72
#include "js/RootingAPI.h"
73
#include "js/Value.h"
74
75
/* Typedef for native functions called by the JS VM. */
76
typedef bool
77
(* JSNative)(JSContext* cx, unsigned argc, JS::Value* vp);
78
79
namespace JS {
80
81
extern JS_PUBLIC_DATA(const HandleValue) UndefinedHandleValue;
82
83
namespace detail {
84
85
/*
86
 * Compute |this| for the |vp| inside a JSNative, either boxing primitives or
87
 * replacing with the global object as necessary.
88
 */
89
extern JS_PUBLIC_API(bool)
90
ComputeThis(JSContext* cx, JS::Value* vp, MutableHandleObject thisObject);
91
92
#ifdef JS_DEBUG
93
extern JS_PUBLIC_API(void)
94
CheckIsValidConstructible(const Value& v);
95
#endif
96
97
class MOZ_STACK_CLASS IncludeUsedRval
98
{
99
    mutable bool usedRval_;
100
101
  public:
102
0
    bool usedRval() const { return usedRval_; }
103
0
    void setUsedRval() const { usedRval_ = true; }
104
0
    void clearUsedRval() const { usedRval_ = false; }
105
0
    void assertUnusedRval() const { MOZ_ASSERT(!usedRval_); }
106
};
107
108
class MOZ_STACK_CLASS NoUsedRval
109
{
110
  public:
111
0
    bool usedRval() const { return false; }
112
0
    void setUsedRval() const {}
113
0
    void clearUsedRval() const {}
114
0
    void assertUnusedRval() const {}
115
};
116
117
template<class WantUsedRval>
118
class MOZ_STACK_CLASS CallArgsBase
119
{
120
    static_assert(mozilla::IsSame<WantUsedRval, IncludeUsedRval>::value ||
121
                  mozilla::IsSame<WantUsedRval, NoUsedRval>::value,
122
                  "WantUsedRval can only be IncludeUsedRval or NoUsedRval");
123
124
  protected:
125
    Value* argv_;
126
    unsigned argc_;
127
    bool constructing_:1;
128
129
    // True if the caller does not use the return value.
130
    bool ignoresReturnValue_:1;
131
132
#ifdef JS_DEBUG
133
    WantUsedRval wantUsedRval_;
134
    bool usedRval() const { return wantUsedRval_.usedRval(); }
135
    void setUsedRval() const { wantUsedRval_.setUsedRval(); }
136
    void clearUsedRval() const { wantUsedRval_.clearUsedRval(); }
137
    void assertUnusedRval() const { wantUsedRval_.assertUnusedRval(); }
138
#else
139
    bool usedRval() const { return false; }
140
12.9M
    void setUsedRval() const {}
JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::setUsedRval() const
Line
Count
Source
140
12.9M
    void setUsedRval() const {}
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::setUsedRval() const
141
19.4M
    void clearUsedRval() const {}
142
40.5M
    void assertUnusedRval() const {}
143
#endif
144
145
  public:
146
    // CALLEE ACCESS
147
148
    /*
149
     * Returns the function being called, as a value.  Must not be called after
150
     * rval() has been used!
151
     */
152
40.5M
    HandleValue calleev() const {
153
40.5M
        this->assertUnusedRval();
154
40.5M
        return HandleValue::fromMarkedLocation(&argv_[-2]);
155
40.5M
    }
156
157
    /*
158
     * Returns the function being called, as an object.  Must not be called
159
     * after rval() has been used!
160
     */
161
27.5M
    JSObject& callee() const {
162
27.5M
        return calleev().toObject();
163
27.5M
    }
164
165
    // CALLING/CONSTRUCTING-DIFFERENTIATIONS
166
167
0
    bool isConstructing() const {
168
0
        if (!argv_[-1].isMagic()) {
169
0
            return false;
170
0
        }
171
0
172
#ifdef JS_DEBUG
173
        if (!this->usedRval()) {
174
            CheckIsValidConstructible(calleev());
175
        }
176
#endif
177
178
0
        return true;
179
0
    }
180
181
4.86M
    bool ignoresReturnValue() const {
182
4.86M
        return ignoresReturnValue_;
183
4.86M
    }
184
185
10
    MutableHandleValue newTarget() const {
186
10
        MOZ_ASSERT(constructing_);
187
10
        return MutableHandleValue::fromMarkedLocation(&this->argv_[argc_]);
188
10
    }
189
190
    /*
191
     * Returns the |this| value passed to the function.  This method must not
192
     * be called when the function is being called as a constructor via |new|.
193
     * The value may or may not be an object: it is the individual function's
194
     * responsibility to box the value if needed.
195
     */
196
25.9M
    HandleValue thisv() const {
197
25.9M
        // Some internal code uses thisv() in constructing cases, so don't do
198
25.9M
        // this yet.
199
25.9M
        // MOZ_ASSERT(!argv_[-1].isMagic(JS_IS_CONSTRUCTING));
200
25.9M
        return HandleValue::fromMarkedLocation(&argv_[-1]);
201
25.9M
    }
202
203
6.49M
    bool computeThis(JSContext* cx, MutableHandleObject thisObject) const {
204
6.49M
        if (thisv().isObject()) {
205
6.49M
            thisObject.set(&thisv().toObject());
206
6.49M
            return true;
207
6.49M
        }
208
0
209
0
        return ComputeThis(cx, base(), thisObject);
210
0
    }
211
212
    // ARGUMENTS
213
214
        /* Returns the number of arguments. */
215
21.0M
    unsigned length() const { return argc_; }
JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::length() const
Line
Count
Source
215
21.0M
    unsigned length() const { return argc_; }
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::length() const
216
217
    /* Returns the i-th zero-indexed argument. */
218
8.09M
    MutableHandleValue operator[](unsigned i) const {
219
8.09M
        MOZ_ASSERT(i < argc_);
220
8.09M
        return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
221
8.09M
    }
JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::operator[](unsigned int) const
Line
Count
Source
218
8.09M
    MutableHandleValue operator[](unsigned i) const {
219
8.09M
        MOZ_ASSERT(i < argc_);
220
8.09M
        return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
221
8.09M
    }
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::operator[](unsigned int) const
222
223
    /*
224
     * Returns the i-th zero-indexed argument, or |undefined| if there's no
225
     * such argument.
226
     */
227
6
    HandleValue get(unsigned i) const {
228
6
        return i < length()
229
6
               ? HandleValue::fromMarkedLocation(&this->argv_[i])
230
6
               : UndefinedHandleValue;
231
6
    }
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::get(unsigned int) const
JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::get(unsigned int) const
Line
Count
Source
227
6
    HandleValue get(unsigned i) const {
228
6
        return i < length()
229
6
               ? HandleValue::fromMarkedLocation(&this->argv_[i])
230
6
               : UndefinedHandleValue;
231
6
    }
232
233
    /*
234
     * Returns true if the i-th zero-indexed argument is present and is not
235
     * |undefined|.
236
     */
237
17
    bool hasDefined(unsigned i) const {
238
17
        return i < argc_ && !this->argv_[i].isUndefined();
239
17
    }
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::hasDefined(unsigned int) const
JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::hasDefined(unsigned int) const
Line
Count
Source
237
17
    bool hasDefined(unsigned i) const {
238
17
        return i < argc_ && !this->argv_[i].isUndefined();
239
17
    }
240
241
    // RETURN VALUE
242
243
    /*
244
     * Returns the currently-set return value.  The initial contents of this
245
     * value are unspecified.  Once this method has been called, callee() and
246
     * calleev() can no longer be used.  (If you're compiling against a debug
247
     * build of SpiderMonkey, these methods will assert to aid debugging.)
248
     *
249
     * If the method you're implementing succeeds by returning true, you *must*
250
     * set this.  (SpiderMonkey doesn't currently assert this, but it will do
251
     * so eventually.)  You don't need to use or change this if your method
252
     * fails.
253
     */
254
12.9M
    MutableHandleValue rval() const {
255
12.9M
        this->setUsedRval();
256
12.9M
        return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
257
12.9M
    }
JS::detail::CallArgsBase<JS::detail::IncludeUsedRval>::rval() const
Line
Count
Source
254
12.9M
    MutableHandleValue rval() const {
255
12.9M
        this->setUsedRval();
256
12.9M
        return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
257
12.9M
    }
Unexecuted instantiation: JS::detail::CallArgsBase<JS::detail::NoUsedRval>::rval() const
258
259
  public:
260
    // These methods are publicly exposed, but they are *not* to be used when
261
    // implementing a JSNative method and encapsulating access to |vp| within
262
    // it.  You probably don't want to use these!
263
264
6.45M
    void setCallee(const Value& aCalleev) const {
265
6.45M
        this->clearUsedRval();
266
6.45M
        argv_[-2] = aCalleev;
267
6.45M
    }
268
269
6.45M
    void setThis(const Value& aThisv) const {
270
6.45M
        argv_[-1] = aThisv;
271
6.45M
    }
272
273
6.48M
    MutableHandleValue mutableThisv() const {
274
6.48M
        return MutableHandleValue::fromMarkedLocation(&argv_[-1]);
275
6.48M
    }
276
277
  public:
278
    // These methods are publicly exposed, but we're unsure of the interfaces
279
    // (because they're hackish and drop assertions).  Avoid using these if you
280
    // can.
281
282
8.11M
    Value* array() const { return argv_; }
283
17.8M
    Value* end() const { return argv_ + argc_ + constructing_; }
284
285
  public:
286
    // These methods are only intended for internal use.  Embedders shouldn't
287
    // use them!
288
289
9.72M
    Value* base() const { return argv_ - 2; }
290
291
511
    Value* spAfterCall() const {
292
511
        this->setUsedRval();
293
511
        return argv_ - 1;
294
511
    }
295
};
296
297
} // namespace detail
298
299
class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval>
300
{
301
  private:
302
    friend CallArgs CallArgsFromVp(unsigned argc, Value* vp);
303
    friend CallArgs CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing,
304
                                   bool ignoresReturnValue);
305
306
    static CallArgs create(unsigned argc, Value* argv, bool constructing,
307
12.9M
                           bool ignoresReturnValue = false) {
308
12.9M
        CallArgs args;
309
12.9M
        args.clearUsedRval();
310
12.9M
        args.argv_ = argv;
311
12.9M
        args.argc_ = argc;
312
12.9M
        args.constructing_ = constructing;
313
12.9M
        args.ignoresReturnValue_ = ignoresReturnValue;
314
#ifdef DEBUG
315
        MOZ_ASSERT(ValueIsNotGray(args.thisv()));
316
        MOZ_ASSERT(ValueIsNotGray(args.calleev()));
317
        for (unsigned i = 0; i < argc; ++i) {
318
            MOZ_ASSERT(ValueIsNotGray(argv[i]));
319
        }
320
#endif
321
        return args;
322
12.9M
    }
323
324
  public:
325
    /*
326
     * Returns true if there are at least |required| arguments passed in. If
327
     * false, it reports an error message on the context.
328
     */
329
    JS_PUBLIC_API(bool) requireAtLeast(JSContext* cx, const char* fnname, unsigned required) const;
330
331
};
332
333
MOZ_ALWAYS_INLINE CallArgs
334
CallArgsFromVp(unsigned argc, Value* vp)
335
12.9M
{
336
12.9M
    return CallArgs::create(argc, vp + 2, vp[1].isMagic(JS_IS_CONSTRUCTING));
337
12.9M
}
338
339
// This method is only intended for internal use in SpiderMonkey.  We may
340
// eventually move it to an internal header.  Embedders should use
341
// JS::CallArgsFromVp!
342
MOZ_ALWAYS_INLINE CallArgs
343
CallArgsFromSp(unsigned stackSlots, Value* sp, bool constructing = false,
344
               bool ignoresReturnValue = false)
345
31.6k
{
346
31.6k
    return CallArgs::create(stackSlots - constructing, sp - stackSlots, constructing,
347
31.6k
                            ignoresReturnValue);
348
31.6k
}
349
350
} // namespace JS
351
352
#endif /* js_CallArgs_h */