File: Debug.h

package info (click to toggle)
firefox 147.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,683,484 kB
  • sloc: cpp: 7,607,246; javascript: 6,533,185; ansic: 3,775,227; python: 1,415,393; xml: 634,561; asm: 438,951; java: 186,241; sh: 62,752; makefile: 18,079; objc: 13,092; perl: 12,808; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; php: 436; lisp: 258; awk: 247; sql: 66; sed: 54; csh: 10; exp: 6
file content (814 lines) | stat: -rw-r--r-- 32,351 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Interfaces by which the embedding can interact with the Debugger API.

#ifndef js_Debug_h
#define js_Debug_h

#include "mozilla/Assertions.h"
#include "mozilla/BaseProfilerUtils.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Vector.h"

#include "jstypes.h"

#include "js/GCAPI.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "js/Value.h"

namespace js {
class Debugger;
}  // namespace js

/* Defined in vm/Debugger.cpp. */
extern JS_PUBLIC_API bool JS_DefineDebuggerObject(JSContext* cx,
                                                  JS::HandleObject obj);

// If the JS execution tracer is running, this will generate a
// ENTRY_KIND_LABEL_ENTER entry with the specified label.
// The consumer of the trace can then, for instance, correlate all code running
// after this entry and before the corresponding ENTRY_KIND_LABEL_LEAVE with the
// provided label.
// If the tracer is not running, this does nothing.
extern JS_PUBLIC_API void JS_TracerEnterLabelLatin1(JSContext* cx,
                                                    const char* label);
extern JS_PUBLIC_API void JS_TracerEnterLabelTwoByte(JSContext* cx,
                                                     const char16_t* label);

extern JS_PUBLIC_API bool JS_TracerIsTracing(JSContext* cx);

// If the JS execution tracer is running, this will generate a
// ENTRY_KIND_LABEL_LEAVE entry with the specified label.
// It is up to the consumer to decide what to do with a ENTRY_KIND_LABEL_LEAVE
// entry is encountered without a corresponding ENTRY_KIND_LABEL_ENTER.
// If the tracer is not running, this does nothing.
extern JS_PUBLIC_API void JS_TracerLeaveLabelLatin1(JSContext* cx,
                                                    const char* label);
extern JS_PUBLIC_API void JS_TracerLeaveLabelTwoByte(JSContext* cx,
                                                     const char16_t* label);

#ifdef MOZ_EXECUTION_TRACING

// This will begin execution tracing for the JSContext, i.e., this will begin
// recording every entrance into / exit from a function for the given context.
// The trace can be read via JS_TracerSnapshotTrace, and populates the
// ExecutionTrace struct defined below.
//
// This throws if the code coverage is active for any realm in the context.
extern JS_PUBLIC_API bool JS_TracerBeginTracing(JSContext* cx);

// This ends execution tracing for the JSContext, discards the tracing
// buffers, and clears some caches used for tracing. JS_TracerSnapshotTrace
// should be called *before* JS_TracerEndTracing if you want to read the trace
// data for this JSContext.
extern JS_PUBLIC_API bool JS_TracerEndTracing(JSContext* cx);

namespace JS {

// Encoding values used for strings recorded via the tracer.
enum class TracerStringEncoding {
  Latin1,
  TwoByte,
  UTF8,
};

// Value Summary
//
// Value summaries are intended as a best effort, minimal representation of
// values, for the purpose of understanding/debugging an application from a
// recorded trace. At present, we record value summaries for the first
// MAX_ARGUMENTS_TO_RECORD arguments of every function call we record when
// tracing is enabled via JS_TracerBeginTracing above. Value summaries are
// surfaced as a contiguous buffer which is intended to be read as needed
// by looking up values as needed via the index in the `values` field of
// FunctionEnter events in the recorded trace. There is a reader in the
// Firefox Profiler frontend which unpacks the binary representation into
// more easily understandable objects.
//
// Value Summary Types
//
// (NOTE: All values listed below use little-endian byte ordering)
//
// - List<T> - A list of at most MAX_COLLECTION_VALUES items and structured as
//   follows:
//      length:   uint32_t
//      values:   T[min(length, MAX_COLLECTION_VALUES)]
//
// - NestedList<T> - If this is a field of ValueSummary which is not itself
//   nested inside another ValueSummary, this will be the same as a List<T>.
//   However, if it *is* nested, it will contain only the length:
//      length:     uint32_t
//      if not inside another ValueSummary ->
//        values:   T[min(length, MAX_COLLECTION_VALUES)]
//
// - SmallString - a string limited to a length of SMALL_STRING_LENGTH_LIMIT,
//   with the following structure:
//      encodingAndLength:  uint16_t (encoding << 14 | length)
//      payload:            CharT[length]
//   The encoding is one of the values in TracerStringEncoding, and CharT is
//   a char for Latin1 and UTF8, and a char16_t for TwoByte. It should be
//   noted that the original string length before truncation to
//   SMALL_STRING_LENGTH_LIMIT is not written, so it is not possible to
//   distinguish between cases where a string had a true length of
//   SMALL_STRING_LENGTH_LIMIT vs cases where a string was truncated.
//
// - Pair<T,U> - A pairing of a T followed immediately by a U
//      first: T
//      second: U
//
// Value Summary Structure
//
// (NOTE: Here and below, see Value Summary Types for more on what
// the type annotations mean.)
//
//    typeAndFlags:   uint8_t (type << 4 | flags)
//    payload:        see below
//
// The value payload's structure depends on the type and the flags:
//
//    JS::ValueType::Undefined ->       nothing
//    JS::ValueType::Null ->            nothing
//    JS::ValueType::Magic ->           nothing
//      NOTE: JS::ValueType::Magic is only used for dense element holes.
//    JS::ValueType::Boolean ->         nothing
//      NOTE: For a JS::ValueType::Boolean, `flags` will hold `1` for `true`,
//      and `0` for `false`.
//    JS::ValueType::PrivateGCThing ->  unused
//    JS::ValueType::BigInt ->          SmallString
//    JS::ValueType::BigInt ->          SmallString
//
//    JS::ValueType::Int32:
//      if flags != NUMBER_IS_OUT_OF_LINE_MAGIC -> nothing (see MIN_INLINE_INT)
//      else ->                                    int32_t
//
//    JS::ValueType::Double:
//      if flags != NUMBER_IS_OUT_OF_LINE_MAGIC -> nothing (value is +0)
//      else ->                                    double
//
//    JS::ValueType::Symbol:
//      if flags != SYMBOL_NO_DESCRIPTION ->       nothing
//      else ->                                    SmallString
//
//    JS::ValueType::Object:
//      See ObjectSummary
struct ValueSummary {
  enum Flags : uint8_t {
    // If this is set, the object has an array of dense elements right
    // after the shape summary id, which are implicitly keyed as the
    // indices within the array.
    GENERIC_OBJECT_HAS_DENSE_ELEMENTS = 1,

    // If a symbol does not have a description, this is set.
    SYMBOL_NO_DESCRIPTION = 1,

    // If the type is numeric and the flags are equal to this, the value is
    // stored immediately after the header. Otherwise, the value is stored
    // directly in the flags. (See MIN_INLINE_INT)
    NUMBER_IS_OUT_OF_LINE_MAGIC = 0xf,
  };

  // This value is written to the start of the value summaries buffer (see
  // TracedJSContext::valueBuffer), and should be bumped every time the format
  // is changed.
  //
  // Keep in mind to update
  // js/src/jit-test/tests/debug/ExecutionTracer-traced-values.js
  // VALUE_SUMMARY_VERSION value.
  static const uint32_t VERSION = 2;

  // If the type is an int and flags != Flags::NUMBER_IS_OUT_OF_LINE_MAGIC,
  // the value is MIN_INLINE_INT + flags.
  static const int32_t MIN_INLINE_INT = -1;
  static const int32_t MAX_INLINE_INT = 13;

  // Limit on the length of strings in traced value summaries.
  static const size_t SMALL_STRING_LENGTH_LIMIT = 512;

  // The max number of entries to record for general collection objects, such
  // as arrays, sets, and maps. Additionally limits the number of indexed
  // properties recorded for objects. This also limits the number of parameter
  // names to record for Function objects.
  static const size_t MAX_COLLECTION_VALUES = 16;

  // The actual JS Value type.
  JS::ValueType type : 4;

  // See the Flags enum.
  uint8_t flags : 4;

  // A variable length payload may trail the type and flags. See the comment
  // above this class.
};

// An ObjectSummary has the following structure:
//
//    kind:     uint8_t
//    payload:  see below
//
// a structure determined by that kind and by the flags on the ValueSummary
// The structure is as follows:
//
//    Kind::NotImplemented ->
//      shapeSummaryId:     uint32_t (summary will only contain class name)
//        NOTE - above, and where noted below, `shapeSummaryId` is included for
//        the class name, but no property values corresponding to the
//        shapeSummary's property names are present in `values`.
//    Kind::ArrayLike ->
//      shapeSummaryId:     uint32_t (summary will only contain class name)
//      values:             NestedList<ValueSummary>
//        NOTE - at present, ArrayObjects as well as SetObjects are serialized
//        using the ArrayLike structure.
//    Kind::MapLike ->
//      shapeSummaryId:     uint32_t (summary will only contain class name)
//      values:             NestedList<Pair<SmallString, ValueSummary>>
//        NOTE - similar to ArrayLike, the property values noted by the shape
//        are not present here.
//    Kind::Function ->
//      functionName:       SmallString
//      parameterNames:
//        values:           List<SmallString>
//        NOTE - destructuring parameters become an empty string
//    Kind::WrappedPrimitiveObject ->
//      wrappedValue:       ValueSummary
//      object:             same as GenericObject (shapeSummaryId, props, etc.)
//    Kind::GenericObject ->
//      shapeSummaryId:     uint32_t
//      props:              NestedList<PropertySummary> (see below)
//      if flags & GENERIC_OBJECT_HAS_DENSE_ELEMENTS ->
//        denseElements:    NestedList<Pair<SmallString, ValueSummary>>
//    Kind::External ->
//      shapeSummaryId:     uint32_t (summary will only contain class name)
//      externalSize:       uint32_t
//      payload:            (defined by embeddings)
//      The structure for Kind::External entries is defined by embeddings.
//      Embedders can use the JS_SetCustomObjectSummaryCallback, which will
//      define a callback for the tracer to call when tracing objects whose
//      classes have the JSCLASS_IS_DOMJSCLASS flag. From within this callback
//      the embedder should use the JS_TracerSummaryWriter interface to write
//      the data however they see fit. SpiderMonkey will then populate the
//      externalSize field with the amount written.
//      NOTE: it is the embedders' responsibility to manage the versioning of
//      their format.
//    Kind::Error ->
//      shapeSummaryId:     uint32_t (summary will only contain class name)
//      name:               SmallString
//      message:            SmallString
//      stack:              SmallString
//      filename:           SmallString
//      lineNumber:         uint32_t
//      columnNumber        uint32_t
//
// WrappedPrimitiveObjects and GenericObjects make use of a PropertySummary
// type, defined here:
//
// - PropertySummary - A union of either a ValueSummary or the value
//   GETTER_SETTER_MAGIC followed by two value summaries. I.e.:
//      if the current byte in the stream is GETTER_SETTER_MAGIC ->
//        magic:  uint8_t  (GETTER_SETTER_MAGIC)
//        getter: ValueSummary
//        setter: ValueSummary
//      else ->
//        value:  ValueSummary
struct ObjectSummary {
  // This is a special value for ValueSummary::typeAndFlags. It should be noted
  // that this only works as long as 0xf is not a valid JS::ValueType.
  static const uint8_t GETTER_SETTER_MAGIC = 0x0f;

  enum class Kind : uint8_t {
    NotImplemented,
    ArrayLike,
    MapLike,
    Function,
    WrappedPrimitiveObject,
    GenericObject,
    ProxyObject,
    External,
    Error,
  };

  Kind kind;

  // A variable length payload may trail the kind. See the comment above this
  // class.
};

// This is populated by JS_TracerSnapshotTrace and just represent a minimal
// structure for natively representing an execution trace across a range of
// JSContexts (see below). The core of the trace is an array of events, each of
// which is a tagged union with data corresponding to that event. Events can
// also point into various tables, and store all of their string data in a
// contiguous UTF-8 stringBuffer (each string is null-terminated within the
// buffer.)
struct ExecutionTrace {
  enum class EventKind : uint8_t {
    FunctionEnter = 0,
    FunctionLeave = 1,
    LabelEnter = 2,
    LabelLeave = 3,

    // NOTE: the `Error` event has no TracedEvent payload, and will always
    // represent the end of the trace when encountered.
    Error = 4,
  };

  enum class ImplementationType : uint8_t {
    Interpreter = 0,
    Baseline = 1,
    Ion = 2,
    Wasm = 3,
  };

  // See the comment above the `values` field of TracedEvent::functionEvent
  // for an explanation of how these constants apply.
  static const uint32_t MAX_ARGUMENTS_TO_RECORD = 4;
  static const int32_t ZERO_ARGUMENTS_MAGIC = -2;
  static const int32_t EXPIRED_VALUES_MAGIC = -1;
  static const int32_t FUNCTION_LEAVE_VALUES = -1;

  struct TracedEvent {
    EventKind kind;
    union {
      // For FunctionEnter / FunctionLeave
      struct {
        ImplementationType implementation;

        // 1-origin line number of the function
        uint32_t lineNumber;

        // 1-origin column of the function
        uint32_t column;

        // Keys into the thread's scriptUrls HashMap. This key can be missing
        // from the HashMap, although ideally that situation is rare (it is
        // more likely in long running traces with *many* unique functions
        // and/or scripts)
        uint32_t scriptId;

        // ID to the realm that the frame was in. It's used for finding which
        // frame comes from which window/page.
        uint64_t realmID;

        // Keys into the thread's atoms HashMap. This key can be missing from
        // the HashMap as well (see comment above scriptId)
        uint32_t functionNameId;

        // If this value is negative,
        //    ZERO_ARGUMENTS_MAGIC indicates the function call had no arguments
        //    EXPIRED_VALUES_MAGIC indicates the argument values have been
        //      overwritten in the ring buffer.
        //    FUNCTION_LEAVE_VALUES is simply a placeholder value for if this
        //      functionEvent is a FunctionLeave
        //      (TODO: we leave this here because we want to record return
        //      values here, but this is not implemented yet.)
        //
        // If this value is non-negative, this is an index into the
        // TracedJSContext::valueBuffer. At the specified index, if
        // kind == EventKind::FunctionEnter, there will be a uint32_t
        // containing the argument count of the function call (argc), followed
        // by min(argc, MAX_ARGUMENTS_TO_RECORD) ValueSummary entries.
        int32_t values;
      } functionEvent;

      // For LabelEnter / LabelLeave
      struct {
        size_t label;  // Indexes directly into the trace's stringBuffer
      } labelEvent;
    };
    // Milliseconds since process creation
    double time;
  };

  // Represents the shape of a traced native object. Essentially this lets us
  // deduplicate the property key array to one location and only store the
  // dense array of property values for each object instance.
  struct ShapeSummary {
    // An identifier for the shape summary, which is referenced by object
    // summaries recorded in the TracedJSContext::valueBuffer.
    uint32_t id;

    // This is the total number of properties for the shape excluding any
    // dense elements on the object.
    uint32_t numProperties;

    // An index into the stringBuffer containing an array, beginning with the
    // class name followed by the array of properties, which will have a length
    // of min(numProperties, MAX_COLLECTION_VALUES). The property keys are for
    // best effort end user comprehension, so for simplicity's sake we just
    // represent all keys as strings, with symbols becoming
    // "Symbol(<description>)". Note that this can result in duplicate keys in
    // the array, when the keys are not actually duplicated on the underlying
    // objects.
    size_t stringBufferOffset;

    // Consider the following example object:
    //
    // {
    //    "0": 0,
    //    "1": 0,
    //    "2": 0,
    //    [Symbol.for("prop1")]: 0,
    //    "prop2": 0,
    //    ...
    //    "prop19": 0,
    //    "prop20": 0,
    // }
    //
    // This will result in a ShapeSummary with numProperties of 20, since "0",
    // "1", and "2" are dense elements, and an array at `stringBufferOffset`
    // looking something like:
    //
    // [
    //    "Object", // The class name
    //    "Symbol(prop1)",
    //    "prop2",
    //    ...
    //    "prop15",
    //    "prop16", // The sequence ends at MAX_COLLECTION_VALUES (16)
    // ]
  };

  struct TracedJSContext {
    mozilla::baseprofiler::BaseProfilerThreadId id;

    // Maps ids to indices into the trace's stringBuffer
    mozilla::HashMap<uint32_t, size_t> scriptUrls;

    // Similar to scriptUrls
    mozilla::HashMap<uint32_t, size_t> atoms;

    // Holds any traced values, in the format defined above (See the
    // ValueSummary type). The first 4 bytes of this buffer will contain
    // the VERSION constant defined above.
    mozilla::Vector<uint8_t> valueBuffer;

    // Holds shape information for objects traced in the valueBuffer
    mozilla::Vector<ShapeSummary> shapeSummaries;

    mozilla::Vector<TracedEvent> events;
  };

  mozilla::Vector<char> stringBuffer;

  // This will be populated with an entry for each context which had tracing
  // enabled via JS_TracerBeginTracing.
  mozilla::Vector<TracedJSContext> contexts;
};
}  // namespace JS

// Captures the trace for all JSContexts in the process which are currently
// tracing.
extern JS_PUBLIC_API bool JS_TracerSnapshotTrace(JS::ExecutionTrace& trace);

// Given that embeddings may want to add support for serializing their own
// types, we expose here a means of registering a callback for serializing
// them. The JS_TracerSummaryWriter exposes a means of writing common types
// to the tracer's value ring buffer, and JS_SetCustomObjectSummaryCallback
// sets a callback on the JSContext
struct JS_TracerSummaryWriterImpl;

struct JS_PUBLIC_API JS_TracerSummaryWriter {
  JS_TracerSummaryWriterImpl* impl;

  void writeUint8(uint8_t val);
  void writeUint16(uint16_t val);
  void writeUint32(uint32_t val);
  void writeUint64(uint64_t val);

  void writeInt8(int8_t val);
  void writeInt16(int16_t val);
  void writeInt32(int32_t val);
  void writeInt64(int64_t val);

  void writeUTF8String(const char* val);
  void writeTwoByteString(const char16_t* val);

  bool writeValue(JSContext* cx, JS::Handle<JS::Value> val);
};

// - `obj` is the object intended to be summarized.
// - `nested` is true if this object is a nested property of another
//   JS::ValueSummary being written.
// - `writer` is an interface which should be used to write the serialized
//   summary.
using CustomObjectSummaryCallback = bool (*)(JSContext*,
                                             JS::Handle<JSObject*> obj,
                                             bool nested,
                                             JS_TracerSummaryWriter* writer);

extern JS_PUBLIC_API void JS_SetCustomObjectSummaryCallback(
    JSContext* cx, CustomObjectSummaryCallback callback);

#endif /* MOZ_EXECUTION_TRACING */

namespace JS {
namespace dbg {

// [SMDOC] Debugger builder API
//
// Helping embedding code build objects for Debugger
// -------------------------------------------------
//
// Some Debugger API features lean on the embedding application to construct
// their result values. For example, Debugger.Frame.prototype.scriptEntryReason
// calls hooks provided by the embedding to construct values explaining why it
// invoked JavaScript; if F is a frame called from a mouse click event handler,
// F.scriptEntryReason would return an object of the form:
//
//   { eventType: "mousedown", event: <object> }
//
// where <object> is a Debugger.Object whose referent is the event being
// dispatched.
//
// However, Debugger implements a trust boundary. Debuggee code may be
// considered untrusted; debugger code needs to be protected from debuggee
// getters, setters, proxies, Object.watch watchpoints, and any other feature
// that might accidentally cause debugger code to set the debuggee running. The
// Debugger API tries to make it easy to write safe debugger code by only
// offering access to debuggee objects via Debugger.Object instances, which
// ensure that only those operations whose explicit purpose is to invoke
// debuggee code do so. But this protective membrane is only helpful if we
// interpose Debugger.Object instances in all the necessary spots.
//
// SpiderMonkey's compartment system also implements a trust boundary. The
// debuggee and debugger are always in different compartments. Inter-compartment
// work requires carefully tracking which compartment each JSObject or JS::Value
// belongs to, and ensuring that is is correctly wrapped for each operation.
//
// It seems precarious to expect the embedding's hooks to implement these trust
// boundaries. Instead, the JS::dbg::Builder API segregates the code which
// constructs trusted objects from that which deals with untrusted objects.
// Trusted objects have an entirely different C++ type, so code that improperly
// mixes trusted and untrusted objects is caught at compile time.
//
// In the structure shown above, there are two trusted objects, and one
// untrusted object:
//
// - The overall object, with the 'eventType' and 'event' properties, is a
//   trusted object. We're going to return it to D.F.p.scriptEntryReason's
//   caller, which will handle it directly.
//
// - The Debugger.Object instance appearing as the value of the 'event' property
//   is a trusted object. It belongs to the same Debugger instance as the
//   Debugger.Frame instance whose scriptEntryReason accessor was called, and
//   presents a safe reflection-oriented API for inspecting its referent, which
//   is:
//
// - The actual event object, an untrusted object, and the referent of the
//   Debugger.Object above. (Content can do things like replacing accessors on
//   Event.prototype.)
//
// Using JS::dbg::Builder, all objects and values the embedding deals with
// directly are considered untrusted, and are assumed to be debuggee values. The
// only way to construct trusted objects is to use Builder's own methods, which
// return a separate Object type. The only way to set a property on a trusted
// object is through that Object type. The actual trusted object is never
// exposed to the embedding.
//
// So, for example, the embedding might use code like the following to construct
// the object shown above, given a Builder passed to it by Debugger:
//
//    bool
//    MyScriptEntryReason::explain(JSContext* cx,
//                                 Builder& builder,
//                                 Builder::Object& result)
//    {
//        JSObject* eventObject = ... obtain debuggee event object somehow ...;
//        if (!eventObject) {
//            return false;
//        }
//        result = builder.newObject(cx);
//        return result &&
//               result.defineProperty(cx, "eventType",
//                                     SafelyFetchType(eventObject)) &&
//               result.defineProperty(cx, "event", eventObject);
//    }
//
//
// Object::defineProperty also accepts an Object as the value to store on the
// property. By its type, we know that the value is trusted, so we set it
// directly as the property's value, without interposing a Debugger.Object
// wrapper. This allows the embedding to builted nested structures of trusted
// objects.
//
// The Builder and Builder::Object methods take care of doing whatever
// compartment switching and wrapping are necessary to construct the trusted
// values in the Debugger's compartment.
//
// The Object type is self-rooting. Construction, assignment, and destruction
// all properly root the referent object.

class BuilderOrigin;

class Builder {
  // The Debugger instance whose client we are building a value for. We build
  // objects in this object's compartment.
  PersistentRootedObject debuggerObject;

  // debuggerObject's Debugger structure, for convenience.
  js::Debugger* debugger;

  // Check that |thing| is in the same compartment as our debuggerObject. Used
  // for assertions when constructing BuiltThings. We can overload this as we
  // add more instantiations of BuiltThing.
#ifdef DEBUG
  void assertBuilt(JSObject* obj);
#else
  void assertBuilt(JSObject* obj) {}
#endif

 protected:
  // A reference to a trusted object or value. At the moment, we only use it
  // with JSObject*.
  template <typename T>
  class BuiltThing {
    friend class BuilderOrigin;

   protected:
    // The Builder to which this trusted thing belongs.
    Builder& owner;

    // A rooted reference to our value.
    PersistentRooted<T> value;

    BuiltThing(JSContext* cx, Builder& owner_,
               T value_ = SafelyInitialized<T>::create())
        : owner(owner_), value(cx, value_) {
      owner.assertBuilt(value_);
    }

    // Forward some things from our owner, for convenience.
    js::Debugger* debugger() const { return owner.debugger; }
    JSObject* debuggerObject() const { return owner.debuggerObject; }

   public:
    BuiltThing(const BuiltThing& rhs) : owner(rhs.owner), value(rhs.value) {}
    BuiltThing& operator=(const BuiltThing& rhs) {
      MOZ_ASSERT(&owner == &rhs.owner);
      owner.assertBuilt(rhs.value);
      value = rhs.value;
      return *this;
    }

    explicit operator bool() const {
      // If we ever instantiate BuiltThing<Value>, this might not suffice.
      return value;
    }

   private:
    BuiltThing() = delete;
  };

 public:
  // A reference to a trusted object, possibly null. Instances of Object are
  // always properly rooted. They can be copied and assigned, as if they were
  // pointers.
  class Object : private BuiltThing<JSObject*> {
    friend class Builder;        // for construction
    friend class BuilderOrigin;  // for unwrapping

    typedef BuiltThing<JSObject*> Base;

    // This is private, because only Builders can create Objects that
    // actually point to something (hence the 'friend' declaration).
    Object(JSContext* cx, Builder& owner_, HandleObject obj)
        : Base(cx, owner_, obj.get()) {}

    bool definePropertyToTrusted(JSContext* cx, const char* name,
                                 JS::MutableHandleValue value);

   public:
    Object(JSContext* cx, Builder& owner_) : Base(cx, owner_, nullptr) {}
    Object(const Object& rhs) = default;

    // Our automatically-generated assignment operator can see our base
    // class's assignment operator, so we don't need to write one out here.

    // Set the property named |name| on this object to |value|.
    //
    // If |value| is a string or primitive, re-wrap it for the debugger's
    // compartment.
    //
    // If |value| is an object, assume it is a debuggee object and make a
    // Debugger.Object instance referring to it. Set that as the propery's
    // value.
    //
    // If |value| is another trusted object, store it directly as the
    // property's value.
    //
    // On error, report the problem on cx and return false.
    bool defineProperty(JSContext* cx, const char* name, JS::HandleValue value);
    bool defineProperty(JSContext* cx, const char* name,
                        JS::HandleObject value);
    bool defineProperty(JSContext* cx, const char* name, Object& value);

    using Base::operator bool;
  };

  // Build an empty object for direct use by debugger code, owned by this
  // Builder. If an error occurs, report it on cx and return a false Object.
  Object newObject(JSContext* cx);

 protected:
  Builder(JSContext* cx, js::Debugger* debugger);
};

// Debugger itself instantiates this subclass of Builder, which can unwrap
// BuiltThings that belong to it.
class BuilderOrigin : public Builder {
  template <typename T>
  T unwrapAny(const BuiltThing<T>& thing) {
    MOZ_ASSERT(&thing.owner == this);
    return thing.value.get();
  }

 public:
  BuilderOrigin(JSContext* cx, js::Debugger* debugger_)
      : Builder(cx, debugger_) {}

  JSObject* unwrap(Object& object) { return unwrapAny(object); }
};

// Finding the size of blocks allocated with malloc
// ------------------------------------------------
//
// Debugger.Memory wants to be able to report how many bytes items in memory are
// consuming. To do this, it needs a function that accepts a pointer to a block,
// and returns the number of bytes allocated to that block. SpiderMonkey itself
// doesn't know which function is appropriate to use, but the embedding does.

// Tell Debuggers in |cx| to use |mallocSizeOf| to find the size of
// malloc'd blocks.
JS_PUBLIC_API void SetDebuggerMallocSizeOf(JSContext* cx,
                                           mozilla::MallocSizeOf mallocSizeOf);

// Get the MallocSizeOf function that the given context is using to find the
// size of malloc'd blocks.
JS_PUBLIC_API mozilla::MallocSizeOf GetDebuggerMallocSizeOf(JSContext* cx);

// Debugger and Garbage Collection Events
// --------------------------------------
//
// The Debugger wants to report about its debuggees' GC cycles, however entering
// JS after a GC is troublesome since SpiderMonkey will often do something like
// force a GC and then rely on the nursery being empty. If we call into some
// Debugger's hook after the GC, then JS runs and the nursery won't be
// empty. Instead, we rely on embedders to call back into SpiderMonkey after a
// GC and notify Debuggers to call their onGarbageCollection hook.

// Determine whether it's necessary to call FireOnGarbageCollectionHook() after
// a GC. This is only required if there are debuggers with an
// onGarbageCollection hook observing a global in the set of collected zones.
JS_PUBLIC_API bool FireOnGarbageCollectionHookRequired(JSContext* cx);

// For each Debugger that observed a debuggee involved in the given GC event,
// call its `onGarbageCollection` hook.
JS_PUBLIC_API bool FireOnGarbageCollectionHook(
    JSContext* cx, GarbageCollectionEvent::Ptr&& data);

// Return true if the given value is a Debugger object, false otherwise.
JS_PUBLIC_API bool IsDebugger(JSObject& obj);

// Append each of the debuggee global objects observed by the Debugger object
// |dbgObj| to |vector|. Returns true on success, false on failure.
JS_PUBLIC_API bool GetDebuggeeGlobals(JSContext* cx, JSObject& dbgObj,
                                      MutableHandleObjectVector vector);

// Returns true if there's any debugger attached to the given context where
// the debugger's "shouldAvoidSideEffects" property is true.
//
// This is supposed to be used by native code that performs side-effectful
// operations where the debugger cannot hook it.
//
// If this function returns true, the native function should throw an
// uncatchable exception by returning `false` without setting any pending
// exception. The debugger will handle this exception by aborting the eager
// evaluation.
//
// The native code can opt into this behavior to help the debugger performing
// the side-effect-free evaluation.
//
// Expected consumers of this API include JSClassOps.resolve hooks which have
// any side-effect other than just resolving the property.
//
// Example:
//   static bool ResolveHook(JSContext* cx, HandleObject obj, HandleId id,
//                           bool* resolvedp) {
//     *resolvedp = false;
//     if (JS::dbg::ShouldAvoidSideEffects()) {
//       return false;
//     }
//     // Resolve the property with the side-effect.
//     ...
//     return true;
//   }
bool ShouldAvoidSideEffects(JSContext* cx);

}  // namespace dbg
}  // namespace JS

#endif /* js_Debug_h */