js/src/vm/SavedStacks.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:6b4e1d18403c
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 #include "vm/SavedStacks.h"
9
10 #include "jscompartment.h"
11 #include "jsnum.h"
12
13 #include "vm/GlobalObject.h"
14 #include "vm/StringBuffer.h"
15
16 #include "jsobjinlines.h"
17
18 using mozilla::AddToHash;
19 using mozilla::HashString;
20
21 namespace js {
22
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 }
33
34 /* static */ bool
35 SavedFrame::HashPolicy::match(SavedFrame *existing, const Lookup &lookup)
36 {
37 if (existing->getLine() != lookup.line)
38 return false;
39
40 if (existing->getColumn() != lookup.column)
41 return false;
42
43 if (existing->getParent() != lookup.parent)
44 return false;
45
46 if (existing->getPrincipals() != lookup.principals)
47 return false;
48
49 JSAtom *source = existing->getSource();
50 if (source->length() != lookup.source->length())
51 return false;
52 if (source != lookup.source)
53 return false;
54
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 }
66
67 return true;
68 }
69
70 /* static */ void
71 SavedFrame::HashPolicy::rekey(Key &key, const Key &newKey)
72 {
73 key = newKey;
74 }
75
76 /* static */ const Class SavedFrame::class_ = {
77 "SavedFrame",
78 JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
79 JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT),
80
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
88
89 SavedFrame::finalize // finalize
90 };
91
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 }
101
102 JSAtom *
103 SavedFrame::getSource()
104 {
105 const Value &v = getReservedSlot(JSSLOT_SOURCE);
106 JSString *s = v.toString();
107 return &s->asAtom();
108 }
109
110 size_t
111 SavedFrame::getLine()
112 {
113 const Value &v = getReservedSlot(JSSLOT_LINE);
114 return v.toInt32();
115 }
116
117 size_t
118 SavedFrame::getColumn()
119 {
120 const Value &v = getReservedSlot(JSSLOT_COLUMN);
121 return v.toInt32();
122 }
123
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 }
133
134 SavedFrame *
135 SavedFrame::getParent()
136 {
137 const Value &v = getReservedSlot(JSSLOT_PARENT);
138 return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr;
139 }
140
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 }
149
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));
156
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));
165
166 JS_ASSERT(getReservedSlot(JSSLOT_PRINCIPALS).isUndefined());
167 if (lookup.principals)
168 JS_HoldPrincipals(lookup.principals);
169 setReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(lookup.principals));
170 }
171
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 }
179
180 void
181 SavedFrame::updatePrivateParent()
182 {
183 setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(getParent()));
184 }
185
186 bool
187 SavedFrame::isSelfHosted()
188 {
189 JSAtom *source = getSource();
190 return StringEqualsAscii(source, "self-hosted");
191 }
192
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 }
200
201 /* static */ SavedFrame *
202 SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
203 {
204 const Value &thisValue = args.thisv();
205
206 if (!thisValue.isObject()) {
207 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
208 return nullptr;
209 }
210
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 }
217
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 }
226
227 return &thisObject.as<SavedFrame>();
228 }
229
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
246
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 }
254
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 }
263
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 }
272
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 }
284
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;
291
292 do
293 frame = frame->getParent();
294 while (frame && principals && subsumes &&
295 !subsumes(principals, frame->getPrincipals()));
296
297 args.rval().setObjectOrNull(frame);
298 return true;
299 }
300
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 };
309
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;
317
318 do {
319 if (principals && subsumes && !subsumes(principals, frame->getPrincipals()))
320 continue;
321 if (frame->isSelfHosted())
322 continue;
323
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()));
336
337 args.rval().setString(sb.finishString());
338 return true;
339 }
340
341 /* static */ const JSFunctionSpec SavedFrame::methods[] = {
342 JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
343 JS_FS_END
344 };
345
346 bool
347 SavedStacks::init()
348 {
349 return frames.init();
350 }
351
352 bool
353 SavedStacks::saveCurrentStack(JSContext *cx, MutableHandle<SavedFrame*> frame)
354 {
355 JS_ASSERT(initialized());
356 JS_ASSERT(&cx->compartment()->savedStacks() == this);
357
358 ScriptFrameIter iter(cx);
359 return insertFrames(cx, iter, frame);
360 }
361
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;
369
370 if (IsObjectAboutToBeFinalized(&obj)) {
371 e.removeFront();
372 } else {
373 SavedFrame *frame = &obj->as<SavedFrame>();
374 bool parentMoved = frame->parentMoved();
375
376 if (parentMoved) {
377 frame->updatePrivateParent();
378 }
379
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 }
393
394 if (savedFrameProto && IsObjectAboutToBeFinalized(&savedFrameProto)) {
395 savedFrameProto = nullptr;
396 }
397 }
398
399 uint32_t
400 SavedStacks::count()
401 {
402 JS_ASSERT(initialized());
403 return frames.count();
404 }
405
406 void
407 SavedStacks::clear()
408 {
409 frames.clear();
410 }
411
412 size_t
413 SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
414 {
415 return frames.sizeOfExcludingThis(mallocSizeOf);
416 }
417
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 }
425
426 ScriptFrameIter thisFrame(iter);
427 Rooted<SavedFrame*> parentFrame(cx);
428 if (!insertFrames(cx, ++iter, &parentFrame))
429 return false;
430
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);
439
440 SavedFrame::Lookup lookup(source,
441 line,
442 column,
443 callee ? callee->displayAtom() : nullptr,
444 parentFrame,
445 thisFrame.compartment()->principals);
446
447 frame.set(getOrCreateSavedFrame(cx, lookup));
448 return frame.address() != nullptr;
449 }
450
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;
457
458 Rooted<SavedFrame *> frame(cx, createFrameFromLookup(cx, lookup));
459 if (!frame)
460 return nullptr;
461
462 if (!frames.relookupOrAdd(p, lookup, frame))
463 return nullptr;
464
465 return frame;
466 }
467
468 JSObject *
469 SavedStacks::getOrCreateSavedFramePrototype(JSContext *cx)
470 {
471 if (savedFrameProto)
472 return savedFrameProto;
473
474 Rooted<GlobalObject *> global(cx, cx->compartment()->maybeGlobal());
475 if (!global)
476 return nullptr;
477
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 }
486
487 SavedFrame *
488 SavedStacks::createFrameFromLookup(JSContext *cx, SavedFrame::Lookup &lookup)
489 {
490 RootedObject proto(cx, getOrCreateSavedFramePrototype(cx));
491 if (!proto)
492 return nullptr;
493
494 JS_ASSERT(proto->compartment() == cx->compartment());
495
496 RootedObject global(cx, cx->compartment()->maybeGlobal());
497 if (!global)
498 return nullptr;
499
500 JS_ASSERT(global->compartment() == cx->compartment());
501
502 RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto, global));
503 if (!frameObj)
504 return nullptr;
505
506 SavedFrame &f = frameObj->as<SavedFrame>();
507 f.initFromLookup(lookup);
508
509 return &f;
510 }
511
512 } /* namespace js */

mercurial