Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
8 #include "vm/SavedStacks.h"
10 #include "jscompartment.h"
11 #include "jsnum.h"
13 #include "vm/GlobalObject.h"
14 #include "vm/StringBuffer.h"
16 #include "jsobjinlines.h"
18 using mozilla::AddToHash;
19 using mozilla::HashString;
21 namespace js {
23 /* static */ HashNumber
24 SavedFrame::HashPolicy::hash(const Lookup &lookup)
25 {
26 return AddToHash(HashString(lookup.source->chars(), lookup.source->length()),
27 lookup.line,
28 lookup.column,
29 lookup.functionDisplayName,
30 SavedFramePtrHasher::hash(lookup.parent),
31 JSPrincipalsPtrHasher::hash(lookup.principals));
32 }
34 /* static */ bool
35 SavedFrame::HashPolicy::match(SavedFrame *existing, const Lookup &lookup)
36 {
37 if (existing->getLine() != lookup.line)
38 return false;
40 if (existing->getColumn() != lookup.column)
41 return false;
43 if (existing->getParent() != lookup.parent)
44 return false;
46 if (existing->getPrincipals() != lookup.principals)
47 return false;
49 JSAtom *source = existing->getSource();
50 if (source->length() != lookup.source->length())
51 return false;
52 if (source != lookup.source)
53 return false;
55 JSAtom *functionDisplayName = existing->getFunctionDisplayName();
56 if (functionDisplayName) {
57 if (!lookup.functionDisplayName)
58 return false;
59 if (functionDisplayName->length() != lookup.functionDisplayName->length())
60 return false;
61 if (0 != CompareAtoms(functionDisplayName, lookup.functionDisplayName))
62 return false;
63 } else if (lookup.functionDisplayName) {
64 return false;
65 }
67 return true;
68 }
70 /* static */ void
71 SavedFrame::HashPolicy::rekey(Key &key, const Key &newKey)
72 {
73 key = newKey;
74 }
76 /* static */ const Class SavedFrame::class_ = {
77 "SavedFrame",
78 JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
79 JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT),
81 JS_PropertyStub, // addProperty
82 JS_DeletePropertyStub, // delProperty
83 JS_PropertyStub, // getProperty
84 JS_StrictPropertyStub, // setProperty
85 JS_EnumerateStub, // enumerate
86 JS_ResolveStub, // resolve
87 JS_ConvertStub, // convert
89 SavedFrame::finalize // finalize
90 };
92 /* static */ void
93 SavedFrame::finalize(FreeOp *fop, JSObject *obj)
94 {
95 JSPrincipals *p = obj->as<SavedFrame>().getPrincipals();
96 if (p) {
97 JSRuntime *rt = obj->runtimeFromMainThread();
98 JS_DropPrincipals(rt, p);
99 }
100 }
102 JSAtom *
103 SavedFrame::getSource()
104 {
105 const Value &v = getReservedSlot(JSSLOT_SOURCE);
106 JSString *s = v.toString();
107 return &s->asAtom();
108 }
110 size_t
111 SavedFrame::getLine()
112 {
113 const Value &v = getReservedSlot(JSSLOT_LINE);
114 return v.toInt32();
115 }
117 size_t
118 SavedFrame::getColumn()
119 {
120 const Value &v = getReservedSlot(JSSLOT_COLUMN);
121 return v.toInt32();
122 }
124 JSAtom *
125 SavedFrame::getFunctionDisplayName()
126 {
127 const Value &v = getReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME);
128 if (v.isNull())
129 return nullptr;
130 JSString *s = v.toString();
131 return &s->asAtom();
132 }
134 SavedFrame *
135 SavedFrame::getParent()
136 {
137 const Value &v = getReservedSlot(JSSLOT_PARENT);
138 return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr;
139 }
141 JSPrincipals *
142 SavedFrame::getPrincipals()
143 {
144 const Value &v = getReservedSlot(JSSLOT_PRINCIPALS);
145 if (v.isUndefined())
146 return nullptr;
147 return static_cast<JSPrincipals *>(v.toPrivate());
148 }
150 void
151 SavedFrame::initFromLookup(Lookup &lookup)
152 {
153 JS_ASSERT(lookup.source);
154 JS_ASSERT(getReservedSlot(JSSLOT_SOURCE).isUndefined());
155 setReservedSlot(JSSLOT_SOURCE, StringValue(lookup.source));
157 setReservedSlot(JSSLOT_LINE, NumberValue(lookup.line));
158 setReservedSlot(JSSLOT_COLUMN, NumberValue(lookup.column));
159 setReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME,
160 lookup.functionDisplayName
161 ? StringValue(lookup.functionDisplayName)
162 : NullValue());
163 setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup.parent));
164 setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(lookup.parent));
166 JS_ASSERT(getReservedSlot(JSSLOT_PRINCIPALS).isUndefined());
167 if (lookup.principals)
168 JS_HoldPrincipals(lookup.principals);
169 setReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(lookup.principals));
170 }
172 bool
173 SavedFrame::parentMoved()
174 {
175 const Value &v = getReservedSlot(JSSLOT_PRIVATE_PARENT);
176 JSObject *p = static_cast<JSObject *>(v.toPrivate());
177 return p == getParent();
178 }
180 void
181 SavedFrame::updatePrivateParent()
182 {
183 setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(getParent()));
184 }
186 bool
187 SavedFrame::isSelfHosted()
188 {
189 JSAtom *source = getSource();
190 return StringEqualsAscii(source, "self-hosted");
191 }
193 /* static */ bool
194 SavedFrame::construct(JSContext *cx, unsigned argc, Value *vp)
195 {
196 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
197 "SavedFrame");
198 return false;
199 }
201 /* static */ SavedFrame *
202 SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
203 {
204 const Value &thisValue = args.thisv();
206 if (!thisValue.isObject()) {
207 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
208 return nullptr;
209 }
211 JSObject &thisObject = thisValue.toObject();
212 if (!thisObject.is<SavedFrame>()) {
213 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
214 SavedFrame::class_.name, fnName, thisObject.getClass()->name);
215 return nullptr;
216 }
218 // Check for SavedFrame.prototype, which has the same class as SavedFrame
219 // instances, however doesn't actually represent a captured stack frame. It
220 // is the only object that is<SavedFrame>() but doesn't have a source.
221 if (thisObject.getReservedSlot(JSSLOT_SOURCE).isNull()) {
222 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
223 SavedFrame::class_.name, fnName, "prototype object");
224 return nullptr;
225 }
227 return &thisObject.as<SavedFrame>();
228 }
230 // Get the SavedFrame * from the current this value and handle any errors that
231 // might occur therein.
232 //
233 // These parameters must already exist when calling this macro:
234 // - JSContext *cx
235 // - unsigned argc
236 // - Value *vp
237 // - const char *fnName
238 // These parameters will be defined after calling this macro:
239 // - CallArgs args
240 // - Rooted<SavedFrame *> frame (will be non-null)
241 #define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame) \
242 CallArgs args = CallArgsFromVp(argc, vp); \
243 Rooted<SavedFrame *> frame(cx, checkThis(cx, args, fnName)); \
244 if (!frame) \
245 return false
247 /* static */ bool
248 SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp)
249 {
250 THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
251 args.rval().setString(frame->getSource());
252 return true;
253 }
255 /* static */ bool
256 SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp)
257 {
258 THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
259 uint32_t line = frame->getLine();
260 args.rval().setNumber(line);
261 return true;
262 }
264 /* static */ bool
265 SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp)
266 {
267 THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
268 uint32_t column = frame->getColumn();
269 args.rval().setNumber(column);
270 return true;
271 }
273 /* static */ bool
274 SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
275 {
276 THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
277 RootedAtom name(cx, frame->getFunctionDisplayName());
278 if (name)
279 args.rval().setString(name);
280 else
281 args.rval().setNull();
282 return true;
283 }
285 /* static */ bool
286 SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
287 {
288 THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
289 JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
290 JSPrincipals *principals = cx->compartment()->principals;
292 do
293 frame = frame->getParent();
294 while (frame && principals && subsumes &&
295 !subsumes(principals, frame->getPrincipals()));
297 args.rval().setObjectOrNull(frame);
298 return true;
299 }
301 /* static */ const JSPropertySpec SavedFrame::properties[] = {
302 JS_PSG("source", SavedFrame::sourceProperty, 0),
303 JS_PSG("line", SavedFrame::lineProperty, 0),
304 JS_PSG("column", SavedFrame::columnProperty, 0),
305 JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
306 JS_PSG("parent", SavedFrame::parentProperty, 0),
307 JS_PS_END
308 };
310 /* static */ bool
311 SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
312 {
313 THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
314 StringBuffer sb(cx);
315 JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
316 JSPrincipals *principals = cx->compartment()->principals;
318 do {
319 if (principals && subsumes && !subsumes(principals, frame->getPrincipals()))
320 continue;
321 if (frame->isSelfHosted())
322 continue;
324 RootedAtom name(cx, frame->getFunctionDisplayName());
325 if ((name && !sb.append(name))
326 || !sb.append('@')
327 || !sb.append(frame->getSource())
328 || !sb.append(':')
329 || !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
330 || !sb.append(':')
331 || !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
332 || !sb.append('\n')) {
333 return false;
334 }
335 } while ((frame = frame->getParent()));
337 args.rval().setString(sb.finishString());
338 return true;
339 }
341 /* static */ const JSFunctionSpec SavedFrame::methods[] = {
342 JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
343 JS_FS_END
344 };
346 bool
347 SavedStacks::init()
348 {
349 return frames.init();
350 }
352 bool
353 SavedStacks::saveCurrentStack(JSContext *cx, MutableHandle<SavedFrame*> frame)
354 {
355 JS_ASSERT(initialized());
356 JS_ASSERT(&cx->compartment()->savedStacks() == this);
358 ScriptFrameIter iter(cx);
359 return insertFrames(cx, iter, frame);
360 }
362 void
363 SavedStacks::sweep(JSRuntime *rt)
364 {
365 if (frames.initialized()) {
366 for (SavedFrame::Set::Enum e(frames); !e.empty(); e.popFront()) {
367 JSObject *obj = static_cast<JSObject *>(e.front());
368 JSObject *temp = obj;
370 if (IsObjectAboutToBeFinalized(&obj)) {
371 e.removeFront();
372 } else {
373 SavedFrame *frame = &obj->as<SavedFrame>();
374 bool parentMoved = frame->parentMoved();
376 if (parentMoved) {
377 frame->updatePrivateParent();
378 }
380 if (obj != temp || parentMoved) {
381 Rooted<SavedFrame*> parent(rt, frame->getParent());
382 e.rekeyFront(SavedFrame::Lookup(frame->getSource(),
383 frame->getLine(),
384 frame->getColumn(),
385 frame->getFunctionDisplayName(),
386 parent,
387 frame->getPrincipals()),
388 frame);
389 }
390 }
391 }
392 }
394 if (savedFrameProto && IsObjectAboutToBeFinalized(&savedFrameProto)) {
395 savedFrameProto = nullptr;
396 }
397 }
399 uint32_t
400 SavedStacks::count()
401 {
402 JS_ASSERT(initialized());
403 return frames.count();
404 }
406 void
407 SavedStacks::clear()
408 {
409 frames.clear();
410 }
412 size_t
413 SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
414 {
415 return frames.sizeOfExcludingThis(mallocSizeOf);
416 }
418 bool
419 SavedStacks::insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandle<SavedFrame*> frame)
420 {
421 if (iter.done()) {
422 frame.set(nullptr);
423 return true;
424 }
426 ScriptFrameIter thisFrame(iter);
427 Rooted<SavedFrame*> parentFrame(cx);
428 if (!insertFrames(cx, ++iter, &parentFrame))
429 return false;
431 RootedScript script(cx, thisFrame.script());
432 RootedFunction callee(cx, thisFrame.maybeCallee());
433 const char *filename = script->filename();
434 RootedAtom source(cx, Atomize(cx, filename, strlen(filename)));
435 if (!source)
436 return false;
437 uint32_t column;
438 uint32_t line = PCToLineNumber(script, thisFrame.pc(), &column);
440 SavedFrame::Lookup lookup(source,
441 line,
442 column,
443 callee ? callee->displayAtom() : nullptr,
444 parentFrame,
445 thisFrame.compartment()->principals);
447 frame.set(getOrCreateSavedFrame(cx, lookup));
448 return frame.address() != nullptr;
449 }
451 SavedFrame *
452 SavedStacks::getOrCreateSavedFrame(JSContext *cx, SavedFrame::Lookup &lookup)
453 {
454 SavedFrame::Set::AddPtr p = frames.lookupForAdd(lookup);
455 if (p)
456 return *p;
458 Rooted<SavedFrame *> frame(cx, createFrameFromLookup(cx, lookup));
459 if (!frame)
460 return nullptr;
462 if (!frames.relookupOrAdd(p, lookup, frame))
463 return nullptr;
465 return frame;
466 }
468 JSObject *
469 SavedStacks::getOrCreateSavedFramePrototype(JSContext *cx)
470 {
471 if (savedFrameProto)
472 return savedFrameProto;
474 Rooted<GlobalObject *> global(cx, cx->compartment()->maybeGlobal());
475 if (!global)
476 return nullptr;
478 savedFrameProto = js_InitClass(cx, global, global->getOrCreateObjectPrototype(cx),
479 &SavedFrame::class_, SavedFrame::construct, 0,
480 SavedFrame::properties, SavedFrame::methods, nullptr, nullptr);
481 // The only object with the SavedFrame::class_ that doesn't have a source
482 // should be the prototype.
483 savedFrameProto->setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
484 return savedFrameProto;
485 }
487 SavedFrame *
488 SavedStacks::createFrameFromLookup(JSContext *cx, SavedFrame::Lookup &lookup)
489 {
490 RootedObject proto(cx, getOrCreateSavedFramePrototype(cx));
491 if (!proto)
492 return nullptr;
494 JS_ASSERT(proto->compartment() == cx->compartment());
496 RootedObject global(cx, cx->compartment()->maybeGlobal());
497 if (!global)
498 return nullptr;
500 JS_ASSERT(global->compartment() == cx->compartment());
502 RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto, global));
503 if (!frameObj)
504 return nullptr;
506 SavedFrame &f = frameObj->as<SavedFrame>();
507 f.initFromLookup(lookup);
509 return &f;
510 }
512 } /* namespace js */