js/src/vm/SavedStacks.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 */

mercurial