summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/XPCWrappedNativeProto.cpp
blob: 4b492bfa9ed34ef5fcb0073be77412cc96b4404c (plain)
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
/* -*- 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/. */

/* Shared proto object for XPCWrappedNative. */

#include "xpcprivate.h"
#include "js/Object.h"  // JS::SetReservedSlot
#include "pratom.h"
#include "XPCMaps.h"

using namespace mozilla;

#ifdef DEBUG
int32_t XPCWrappedNativeProto::gDEBUG_LiveProtoCount = 0;
#endif

XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
                                             nsIClassInfo* ClassInfo,
                                             RefPtr<XPCNativeSet>&& Set)
    : mScope(Scope),
      mJSProtoObject(nullptr),
      mClassInfo(ClassInfo),
      mSet(std::move(Set)) {
  // This native object lives as long as its associated JSObject - killed
  // by finalization of the JSObject (or explicitly if Init fails).

  MOZ_COUNT_CTOR(XPCWrappedNativeProto);
  MOZ_ASSERT(mScope);

#ifdef DEBUG
  gDEBUG_LiveProtoCount++;
#endif
}

XPCWrappedNativeProto::~XPCWrappedNativeProto() {
  MOZ_ASSERT(!mJSProtoObject, "JSProtoObject still alive");

  MOZ_COUNT_DTOR(XPCWrappedNativeProto);

#ifdef DEBUG
  gDEBUG_LiveProtoCount--;
#endif

  // Note that our weak ref to mScope is not to be trusted at this point.

  XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo);

  DeferredFinalize(mClassInfo.forget().take());
}

bool XPCWrappedNativeProto::Init(JSContext* cx, nsIXPCScriptable* scriptable) {
  mScriptable = scriptable;

  JS::RootedObject proto(cx, JS::GetRealmObjectPrototype(cx));
  mJSProtoObject = JS_NewObjectWithGivenProto(cx, &XPC_WN_Proto_JSClass, proto);

  bool success = !!mJSProtoObject;
  if (success) {
    JS::SetReservedSlot(mJSProtoObject, ProtoSlot, JS::PrivateValue(this));
  }

  return success;
}

void XPCWrappedNativeProto::JSProtoObjectFinalized(JS::GCContext* gcx,
                                                   JSObject* obj) {
  MOZ_ASSERT(obj == mJSProtoObject, "huh?");

#ifdef DEBUG
  // Check that this object has already been swept from the map.
  ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap();
  MOZ_ASSERT(map->Find(mClassInfo) != this);
#endif

  MOZ_ALWAYS_TRUE(GetRuntime()->GetDyingWrappedNativeProtos().append(this));
  mJSProtoObject = nullptr;
}

void XPCWrappedNativeProto::JSProtoObjectMoved(JSObject* obj,
                                               const JSObject* old) {
  // Update without triggering barriers.
  MOZ_ASSERT(mJSProtoObject == old);
  mJSProtoObject.unbarrieredSet(obj);
}

void XPCWrappedNativeProto::SystemIsBeingShutDown() {
  // Note that the instance might receive this call multiple times
  // as we walk to here from various places.

  if (mJSProtoObject) {
    // short circuit future finalization
    JS::SetReservedSlot(mJSProtoObject, ProtoSlot, JS::UndefinedValue());
    mJSProtoObject = nullptr;
  }
}

// static
XPCWrappedNativeProto* XPCWrappedNativeProto::GetNewOrUsed(
    JSContext* cx, XPCWrappedNativeScope* scope, nsIClassInfo* classInfo,
    nsIXPCScriptable* scriptable) {
  MOZ_ASSERT(scope, "bad param");
  MOZ_ASSERT(classInfo, "bad param");

  AutoMarkingWrappedNativeProtoPtr proto(cx);
  ClassInfo2WrappedNativeProtoMap* map = nullptr;

  map = scope->GetWrappedNativeProtoMap();
  proto = map->Find(classInfo);
  if (proto) {
    return proto;
  }

  RefPtr<XPCNativeSet> set = XPCNativeSet::GetNewOrUsed(cx, classInfo);
  if (!set) {
    return nullptr;
  }

  proto = new XPCWrappedNativeProto(scope, classInfo, std::move(set));

  if (!proto->Init(cx, scriptable)) {
    delete proto.get();
    return nullptr;
  }

  map->Add(classInfo, proto);

  return proto;
}

void XPCWrappedNativeProto::DebugDump(int16_t depth) {
#ifdef DEBUG
  depth--;
  XPC_LOG_ALWAYS(("XPCWrappedNativeProto @ %p", this));
  XPC_LOG_INDENT();
  XPC_LOG_ALWAYS(("gDEBUG_LiveProtoCount is %d", gDEBUG_LiveProtoCount));
  XPC_LOG_ALWAYS(("mScope @ %p", mScope));
  XPC_LOG_ALWAYS(("mJSProtoObject @ %p", mJSProtoObject.get()));
  XPC_LOG_ALWAYS(("mSet @ %p", mSet.get()));
  XPC_LOG_ALWAYS(("mScriptable @ %p", mScriptable.get()));
  if (depth && mScriptable) {
    XPC_LOG_INDENT();
    XPC_LOG_ALWAYS(("mFlags of %x", mScriptable->GetScriptableFlags()));
    XPC_LOG_ALWAYS(("mJSClass @ %p", mScriptable->GetJSClass()));
    XPC_LOG_OUTDENT();
  }
  XPC_LOG_OUTDENT();
#endif
}