1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/SavedStacks.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,512 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 + 1.11 +#include "vm/SavedStacks.h" 1.12 + 1.13 +#include "jscompartment.h" 1.14 +#include "jsnum.h" 1.15 + 1.16 +#include "vm/GlobalObject.h" 1.17 +#include "vm/StringBuffer.h" 1.18 + 1.19 +#include "jsobjinlines.h" 1.20 + 1.21 +using mozilla::AddToHash; 1.22 +using mozilla::HashString; 1.23 + 1.24 +namespace js { 1.25 + 1.26 +/* static */ HashNumber 1.27 +SavedFrame::HashPolicy::hash(const Lookup &lookup) 1.28 +{ 1.29 + return AddToHash(HashString(lookup.source->chars(), lookup.source->length()), 1.30 + lookup.line, 1.31 + lookup.column, 1.32 + lookup.functionDisplayName, 1.33 + SavedFramePtrHasher::hash(lookup.parent), 1.34 + JSPrincipalsPtrHasher::hash(lookup.principals)); 1.35 +} 1.36 + 1.37 +/* static */ bool 1.38 +SavedFrame::HashPolicy::match(SavedFrame *existing, const Lookup &lookup) 1.39 +{ 1.40 + if (existing->getLine() != lookup.line) 1.41 + return false; 1.42 + 1.43 + if (existing->getColumn() != lookup.column) 1.44 + return false; 1.45 + 1.46 + if (existing->getParent() != lookup.parent) 1.47 + return false; 1.48 + 1.49 + if (existing->getPrincipals() != lookup.principals) 1.50 + return false; 1.51 + 1.52 + JSAtom *source = existing->getSource(); 1.53 + if (source->length() != lookup.source->length()) 1.54 + return false; 1.55 + if (source != lookup.source) 1.56 + return false; 1.57 + 1.58 + JSAtom *functionDisplayName = existing->getFunctionDisplayName(); 1.59 + if (functionDisplayName) { 1.60 + if (!lookup.functionDisplayName) 1.61 + return false; 1.62 + if (functionDisplayName->length() != lookup.functionDisplayName->length()) 1.63 + return false; 1.64 + if (0 != CompareAtoms(functionDisplayName, lookup.functionDisplayName)) 1.65 + return false; 1.66 + } else if (lookup.functionDisplayName) { 1.67 + return false; 1.68 + } 1.69 + 1.70 + return true; 1.71 +} 1.72 + 1.73 +/* static */ void 1.74 +SavedFrame::HashPolicy::rekey(Key &key, const Key &newKey) 1.75 +{ 1.76 + key = newKey; 1.77 +} 1.78 + 1.79 +/* static */ const Class SavedFrame::class_ = { 1.80 + "SavedFrame", 1.81 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | 1.82 + JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT), 1.83 + 1.84 + JS_PropertyStub, // addProperty 1.85 + JS_DeletePropertyStub, // delProperty 1.86 + JS_PropertyStub, // getProperty 1.87 + JS_StrictPropertyStub, // setProperty 1.88 + JS_EnumerateStub, // enumerate 1.89 + JS_ResolveStub, // resolve 1.90 + JS_ConvertStub, // convert 1.91 + 1.92 + SavedFrame::finalize // finalize 1.93 +}; 1.94 + 1.95 +/* static */ void 1.96 +SavedFrame::finalize(FreeOp *fop, JSObject *obj) 1.97 +{ 1.98 + JSPrincipals *p = obj->as<SavedFrame>().getPrincipals(); 1.99 + if (p) { 1.100 + JSRuntime *rt = obj->runtimeFromMainThread(); 1.101 + JS_DropPrincipals(rt, p); 1.102 + } 1.103 +} 1.104 + 1.105 +JSAtom * 1.106 +SavedFrame::getSource() 1.107 +{ 1.108 + const Value &v = getReservedSlot(JSSLOT_SOURCE); 1.109 + JSString *s = v.toString(); 1.110 + return &s->asAtom(); 1.111 +} 1.112 + 1.113 +size_t 1.114 +SavedFrame::getLine() 1.115 +{ 1.116 + const Value &v = getReservedSlot(JSSLOT_LINE); 1.117 + return v.toInt32(); 1.118 +} 1.119 + 1.120 +size_t 1.121 +SavedFrame::getColumn() 1.122 +{ 1.123 + const Value &v = getReservedSlot(JSSLOT_COLUMN); 1.124 + return v.toInt32(); 1.125 +} 1.126 + 1.127 +JSAtom * 1.128 +SavedFrame::getFunctionDisplayName() 1.129 +{ 1.130 + const Value &v = getReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME); 1.131 + if (v.isNull()) 1.132 + return nullptr; 1.133 + JSString *s = v.toString(); 1.134 + return &s->asAtom(); 1.135 +} 1.136 + 1.137 +SavedFrame * 1.138 +SavedFrame::getParent() 1.139 +{ 1.140 + const Value &v = getReservedSlot(JSSLOT_PARENT); 1.141 + return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr; 1.142 +} 1.143 + 1.144 +JSPrincipals * 1.145 +SavedFrame::getPrincipals() 1.146 +{ 1.147 + const Value &v = getReservedSlot(JSSLOT_PRINCIPALS); 1.148 + if (v.isUndefined()) 1.149 + return nullptr; 1.150 + return static_cast<JSPrincipals *>(v.toPrivate()); 1.151 +} 1.152 + 1.153 +void 1.154 +SavedFrame::initFromLookup(Lookup &lookup) 1.155 +{ 1.156 + JS_ASSERT(lookup.source); 1.157 + JS_ASSERT(getReservedSlot(JSSLOT_SOURCE).isUndefined()); 1.158 + setReservedSlot(JSSLOT_SOURCE, StringValue(lookup.source)); 1.159 + 1.160 + setReservedSlot(JSSLOT_LINE, NumberValue(lookup.line)); 1.161 + setReservedSlot(JSSLOT_COLUMN, NumberValue(lookup.column)); 1.162 + setReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME, 1.163 + lookup.functionDisplayName 1.164 + ? StringValue(lookup.functionDisplayName) 1.165 + : NullValue()); 1.166 + setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup.parent)); 1.167 + setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(lookup.parent)); 1.168 + 1.169 + JS_ASSERT(getReservedSlot(JSSLOT_PRINCIPALS).isUndefined()); 1.170 + if (lookup.principals) 1.171 + JS_HoldPrincipals(lookup.principals); 1.172 + setReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(lookup.principals)); 1.173 +} 1.174 + 1.175 +bool 1.176 +SavedFrame::parentMoved() 1.177 +{ 1.178 + const Value &v = getReservedSlot(JSSLOT_PRIVATE_PARENT); 1.179 + JSObject *p = static_cast<JSObject *>(v.toPrivate()); 1.180 + return p == getParent(); 1.181 +} 1.182 + 1.183 +void 1.184 +SavedFrame::updatePrivateParent() 1.185 +{ 1.186 + setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(getParent())); 1.187 +} 1.188 + 1.189 +bool 1.190 +SavedFrame::isSelfHosted() 1.191 +{ 1.192 + JSAtom *source = getSource(); 1.193 + return StringEqualsAscii(source, "self-hosted"); 1.194 +} 1.195 + 1.196 +/* static */ bool 1.197 +SavedFrame::construct(JSContext *cx, unsigned argc, Value *vp) 1.198 +{ 1.199 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, 1.200 + "SavedFrame"); 1.201 + return false; 1.202 +} 1.203 + 1.204 +/* static */ SavedFrame * 1.205 +SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName) 1.206 +{ 1.207 + const Value &thisValue = args.thisv(); 1.208 + 1.209 + if (!thisValue.isObject()) { 1.210 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT); 1.211 + return nullptr; 1.212 + } 1.213 + 1.214 + JSObject &thisObject = thisValue.toObject(); 1.215 + if (!thisObject.is<SavedFrame>()) { 1.216 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.217 + SavedFrame::class_.name, fnName, thisObject.getClass()->name); 1.218 + return nullptr; 1.219 + } 1.220 + 1.221 + // Check for SavedFrame.prototype, which has the same class as SavedFrame 1.222 + // instances, however doesn't actually represent a captured stack frame. It 1.223 + // is the only object that is<SavedFrame>() but doesn't have a source. 1.224 + if (thisObject.getReservedSlot(JSSLOT_SOURCE).isNull()) { 1.225 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.226 + SavedFrame::class_.name, fnName, "prototype object"); 1.227 + return nullptr; 1.228 + } 1.229 + 1.230 + return &thisObject.as<SavedFrame>(); 1.231 +} 1.232 + 1.233 +// Get the SavedFrame * from the current this value and handle any errors that 1.234 +// might occur therein. 1.235 +// 1.236 +// These parameters must already exist when calling this macro: 1.237 +// - JSContext *cx 1.238 +// - unsigned argc 1.239 +// - Value *vp 1.240 +// - const char *fnName 1.241 +// These parameters will be defined after calling this macro: 1.242 +// - CallArgs args 1.243 +// - Rooted<SavedFrame *> frame (will be non-null) 1.244 +#define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame) \ 1.245 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.246 + Rooted<SavedFrame *> frame(cx, checkThis(cx, args, fnName)); \ 1.247 + if (!frame) \ 1.248 + return false 1.249 + 1.250 +/* static */ bool 1.251 +SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp) 1.252 +{ 1.253 + THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame); 1.254 + args.rval().setString(frame->getSource()); 1.255 + return true; 1.256 +} 1.257 + 1.258 +/* static */ bool 1.259 +SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp) 1.260 +{ 1.261 + THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame); 1.262 + uint32_t line = frame->getLine(); 1.263 + args.rval().setNumber(line); 1.264 + return true; 1.265 +} 1.266 + 1.267 +/* static */ bool 1.268 +SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp) 1.269 +{ 1.270 + THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame); 1.271 + uint32_t column = frame->getColumn(); 1.272 + args.rval().setNumber(column); 1.273 + return true; 1.274 +} 1.275 + 1.276 +/* static */ bool 1.277 +SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp) 1.278 +{ 1.279 + THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame); 1.280 + RootedAtom name(cx, frame->getFunctionDisplayName()); 1.281 + if (name) 1.282 + args.rval().setString(name); 1.283 + else 1.284 + args.rval().setNull(); 1.285 + return true; 1.286 +} 1.287 + 1.288 +/* static */ bool 1.289 +SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp) 1.290 +{ 1.291 + THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame); 1.292 + JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes; 1.293 + JSPrincipals *principals = cx->compartment()->principals; 1.294 + 1.295 + do 1.296 + frame = frame->getParent(); 1.297 + while (frame && principals && subsumes && 1.298 + !subsumes(principals, frame->getPrincipals())); 1.299 + 1.300 + args.rval().setObjectOrNull(frame); 1.301 + return true; 1.302 +} 1.303 + 1.304 +/* static */ const JSPropertySpec SavedFrame::properties[] = { 1.305 + JS_PSG("source", SavedFrame::sourceProperty, 0), 1.306 + JS_PSG("line", SavedFrame::lineProperty, 0), 1.307 + JS_PSG("column", SavedFrame::columnProperty, 0), 1.308 + JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0), 1.309 + JS_PSG("parent", SavedFrame::parentProperty, 0), 1.310 + JS_PS_END 1.311 +}; 1.312 + 1.313 +/* static */ bool 1.314 +SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp) 1.315 +{ 1.316 + THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame); 1.317 + StringBuffer sb(cx); 1.318 + JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes; 1.319 + JSPrincipals *principals = cx->compartment()->principals; 1.320 + 1.321 + do { 1.322 + if (principals && subsumes && !subsumes(principals, frame->getPrincipals())) 1.323 + continue; 1.324 + if (frame->isSelfHosted()) 1.325 + continue; 1.326 + 1.327 + RootedAtom name(cx, frame->getFunctionDisplayName()); 1.328 + if ((name && !sb.append(name)) 1.329 + || !sb.append('@') 1.330 + || !sb.append(frame->getSource()) 1.331 + || !sb.append(':') 1.332 + || !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb) 1.333 + || !sb.append(':') 1.334 + || !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb) 1.335 + || !sb.append('\n')) { 1.336 + return false; 1.337 + } 1.338 + } while ((frame = frame->getParent())); 1.339 + 1.340 + args.rval().setString(sb.finishString()); 1.341 + return true; 1.342 +} 1.343 + 1.344 +/* static */ const JSFunctionSpec SavedFrame::methods[] = { 1.345 + JS_FN("toString", SavedFrame::toStringMethod, 0, 0), 1.346 + JS_FS_END 1.347 +}; 1.348 + 1.349 +bool 1.350 +SavedStacks::init() 1.351 +{ 1.352 + return frames.init(); 1.353 +} 1.354 + 1.355 +bool 1.356 +SavedStacks::saveCurrentStack(JSContext *cx, MutableHandle<SavedFrame*> frame) 1.357 +{ 1.358 + JS_ASSERT(initialized()); 1.359 + JS_ASSERT(&cx->compartment()->savedStacks() == this); 1.360 + 1.361 + ScriptFrameIter iter(cx); 1.362 + return insertFrames(cx, iter, frame); 1.363 +} 1.364 + 1.365 +void 1.366 +SavedStacks::sweep(JSRuntime *rt) 1.367 +{ 1.368 + if (frames.initialized()) { 1.369 + for (SavedFrame::Set::Enum e(frames); !e.empty(); e.popFront()) { 1.370 + JSObject *obj = static_cast<JSObject *>(e.front()); 1.371 + JSObject *temp = obj; 1.372 + 1.373 + if (IsObjectAboutToBeFinalized(&obj)) { 1.374 + e.removeFront(); 1.375 + } else { 1.376 + SavedFrame *frame = &obj->as<SavedFrame>(); 1.377 + bool parentMoved = frame->parentMoved(); 1.378 + 1.379 + if (parentMoved) { 1.380 + frame->updatePrivateParent(); 1.381 + } 1.382 + 1.383 + if (obj != temp || parentMoved) { 1.384 + Rooted<SavedFrame*> parent(rt, frame->getParent()); 1.385 + e.rekeyFront(SavedFrame::Lookup(frame->getSource(), 1.386 + frame->getLine(), 1.387 + frame->getColumn(), 1.388 + frame->getFunctionDisplayName(), 1.389 + parent, 1.390 + frame->getPrincipals()), 1.391 + frame); 1.392 + } 1.393 + } 1.394 + } 1.395 + } 1.396 + 1.397 + if (savedFrameProto && IsObjectAboutToBeFinalized(&savedFrameProto)) { 1.398 + savedFrameProto = nullptr; 1.399 + } 1.400 +} 1.401 + 1.402 +uint32_t 1.403 +SavedStacks::count() 1.404 +{ 1.405 + JS_ASSERT(initialized()); 1.406 + return frames.count(); 1.407 +} 1.408 + 1.409 +void 1.410 +SavedStacks::clear() 1.411 +{ 1.412 + frames.clear(); 1.413 +} 1.414 + 1.415 +size_t 1.416 +SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) 1.417 +{ 1.418 + return frames.sizeOfExcludingThis(mallocSizeOf); 1.419 +} 1.420 + 1.421 +bool 1.422 +SavedStacks::insertFrames(JSContext *cx, ScriptFrameIter &iter, MutableHandle<SavedFrame*> frame) 1.423 +{ 1.424 + if (iter.done()) { 1.425 + frame.set(nullptr); 1.426 + return true; 1.427 + } 1.428 + 1.429 + ScriptFrameIter thisFrame(iter); 1.430 + Rooted<SavedFrame*> parentFrame(cx); 1.431 + if (!insertFrames(cx, ++iter, &parentFrame)) 1.432 + return false; 1.433 + 1.434 + RootedScript script(cx, thisFrame.script()); 1.435 + RootedFunction callee(cx, thisFrame.maybeCallee()); 1.436 + const char *filename = script->filename(); 1.437 + RootedAtom source(cx, Atomize(cx, filename, strlen(filename))); 1.438 + if (!source) 1.439 + return false; 1.440 + uint32_t column; 1.441 + uint32_t line = PCToLineNumber(script, thisFrame.pc(), &column); 1.442 + 1.443 + SavedFrame::Lookup lookup(source, 1.444 + line, 1.445 + column, 1.446 + callee ? callee->displayAtom() : nullptr, 1.447 + parentFrame, 1.448 + thisFrame.compartment()->principals); 1.449 + 1.450 + frame.set(getOrCreateSavedFrame(cx, lookup)); 1.451 + return frame.address() != nullptr; 1.452 +} 1.453 + 1.454 +SavedFrame * 1.455 +SavedStacks::getOrCreateSavedFrame(JSContext *cx, SavedFrame::Lookup &lookup) 1.456 +{ 1.457 + SavedFrame::Set::AddPtr p = frames.lookupForAdd(lookup); 1.458 + if (p) 1.459 + return *p; 1.460 + 1.461 + Rooted<SavedFrame *> frame(cx, createFrameFromLookup(cx, lookup)); 1.462 + if (!frame) 1.463 + return nullptr; 1.464 + 1.465 + if (!frames.relookupOrAdd(p, lookup, frame)) 1.466 + return nullptr; 1.467 + 1.468 + return frame; 1.469 +} 1.470 + 1.471 +JSObject * 1.472 +SavedStacks::getOrCreateSavedFramePrototype(JSContext *cx) 1.473 +{ 1.474 + if (savedFrameProto) 1.475 + return savedFrameProto; 1.476 + 1.477 + Rooted<GlobalObject *> global(cx, cx->compartment()->maybeGlobal()); 1.478 + if (!global) 1.479 + return nullptr; 1.480 + 1.481 + savedFrameProto = js_InitClass(cx, global, global->getOrCreateObjectPrototype(cx), 1.482 + &SavedFrame::class_, SavedFrame::construct, 0, 1.483 + SavedFrame::properties, SavedFrame::methods, nullptr, nullptr); 1.484 + // The only object with the SavedFrame::class_ that doesn't have a source 1.485 + // should be the prototype. 1.486 + savedFrameProto->setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue()); 1.487 + return savedFrameProto; 1.488 +} 1.489 + 1.490 +SavedFrame * 1.491 +SavedStacks::createFrameFromLookup(JSContext *cx, SavedFrame::Lookup &lookup) 1.492 +{ 1.493 + RootedObject proto(cx, getOrCreateSavedFramePrototype(cx)); 1.494 + if (!proto) 1.495 + return nullptr; 1.496 + 1.497 + JS_ASSERT(proto->compartment() == cx->compartment()); 1.498 + 1.499 + RootedObject global(cx, cx->compartment()->maybeGlobal()); 1.500 + if (!global) 1.501 + return nullptr; 1.502 + 1.503 + JS_ASSERT(global->compartment() == cx->compartment()); 1.504 + 1.505 + RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto, global)); 1.506 + if (!frameObj) 1.507 + return nullptr; 1.508 + 1.509 + SavedFrame &f = frameObj->as<SavedFrame>(); 1.510 + f.initFromLookup(lookup); 1.511 + 1.512 + return &f; 1.513 +} 1.514 + 1.515 +} /* namespace js */