LCOV - code coverage report
Current view: top level - js/src/vm - UbiNode.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 206 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 75 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "js/UbiNode.h"
       8             : 
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/Attributes.h"
      11             : #include "mozilla/Range.h"
      12             : #include "mozilla/Scoped.h"
      13             : 
      14             : #include <algorithm>
      15             : 
      16             : #include "jscntxt.h"
      17             : #include "jsobj.h"
      18             : #include "jsscript.h"
      19             : #include "jsstr.h"
      20             : 
      21             : #include "jit/IonCode.h"
      22             : #include "js/Debug.h"
      23             : #include "js/TracingAPI.h"
      24             : #include "js/TypeDecls.h"
      25             : #include "js/Utility.h"
      26             : #include "js/Vector.h"
      27             : #include "vm/Debugger.h"
      28             : #include "vm/EnvironmentObject.h"
      29             : #include "vm/GlobalObject.h"
      30             : #include "vm/Scope.h"
      31             : #include "vm/Shape.h"
      32             : #include "vm/String.h"
      33             : #include "vm/Symbol.h"
      34             : 
      35             : #include "jsobjinlines.h"
      36             : #include "vm/Debugger-inl.h"
      37             : 
      38             : using namespace js;
      39             : 
      40             : using mozilla::Some;
      41             : using mozilla::RangedPtr;
      42             : using JS::DispatchTyped;
      43             : using JS::HandleValue;
      44             : using JS::Value;
      45             : using JS::ZoneSet;
      46             : using JS::ubi::AtomOrTwoByteChars;
      47             : using JS::ubi::CoarseType;
      48             : using JS::ubi::Concrete;
      49             : using JS::ubi::Edge;
      50             : using JS::ubi::EdgeRange;
      51             : using JS::ubi::Node;
      52             : using JS::ubi::EdgeVector;
      53             : using JS::ubi::StackFrame;
      54             : using JS::ubi::TracerConcrete;
      55             : using JS::ubi::TracerConcreteWithCompartment;
      56             : 
      57             : struct CopyToBufferMatcher
      58             : {
      59             :     RangedPtr<char16_t> destination;
      60             :     size_t              maxLength;
      61             : 
      62           0 :     CopyToBufferMatcher(RangedPtr<char16_t> destination, size_t maxLength)
      63           0 :       : destination(destination)
      64           0 :       , maxLength(maxLength)
      65           0 :     { }
      66             : 
      67             :     template<typename CharT>
      68             :     static size_t
      69           0 :     copyToBufferHelper(const CharT* src, RangedPtr<char16_t> dest, size_t length)
      70             :     {
      71           0 :         size_t i = 0;
      72           0 :         for ( ; i < length; i++)
      73           0 :             dest[i] = src[i];
      74           0 :         return i;
      75             :     }
      76             : 
      77             :     size_t
      78           0 :     match(JSAtom* atom)
      79             :     {
      80           0 :         if (!atom)
      81           0 :             return 0;
      82             : 
      83           0 :         size_t length = std::min(atom->length(), maxLength);
      84           0 :         JS::AutoCheckCannotGC noGC;
      85           0 :         return atom->hasTwoByteChars()
      86           0 :             ? copyToBufferHelper(atom->twoByteChars(noGC), destination, length)
      87           0 :             : copyToBufferHelper(atom->latin1Chars(noGC), destination, length);
      88             :     }
      89             : 
      90             :     size_t
      91           0 :     match(const char16_t* chars)
      92             :     {
      93           0 :         if (!chars)
      94           0 :             return 0;
      95             : 
      96           0 :         size_t length = std::min(js_strlen(chars), maxLength);
      97           0 :         return copyToBufferHelper(chars, destination, length);
      98             :     }
      99             : };
     100             : 
     101             : size_t
     102           0 : JS::ubi::AtomOrTwoByteChars::copyToBuffer(RangedPtr<char16_t> destination, size_t length)
     103             : {
     104           0 :     CopyToBufferMatcher m(destination, length);
     105           0 :     return match(m);
     106             : }
     107             : 
     108             : struct LengthMatcher
     109             : {
     110             :     size_t
     111           0 :     match(JSAtom* atom)
     112             :     {
     113           0 :         return atom ? atom->length() : 0;
     114             :     }
     115             : 
     116             :     size_t
     117           0 :     match(const char16_t* chars)
     118             :     {
     119           0 :         return chars ? js_strlen(chars) : 0;
     120             :     }
     121             : };
     122             : 
     123             : size_t
     124           0 : JS::ubi::AtomOrTwoByteChars::length()
     125             : {
     126             :     LengthMatcher m;
     127           0 :     return match(m);
     128             : }
     129             : 
     130             : size_t
     131           0 : StackFrame::source(RangedPtr<char16_t> destination, size_t length) const
     132             : {
     133           0 :     auto s = source();
     134           0 :     return s.copyToBuffer(destination, length);
     135             : }
     136             : 
     137             : size_t
     138           0 : StackFrame::functionDisplayName(RangedPtr<char16_t> destination, size_t length) const
     139             : {
     140           0 :     auto name = functionDisplayName();
     141           0 :     return name.copyToBuffer(destination, length);
     142             : }
     143             : 
     144             : size_t
     145           0 : StackFrame::sourceLength()
     146             : {
     147           0 :     return source().length();
     148             : }
     149             : 
     150             : size_t
     151           0 : StackFrame::functionDisplayNameLength()
     152             : {
     153           0 :     return functionDisplayName().length();
     154             : }
     155             : 
     156             : // All operations on null ubi::Nodes crash.
     157           0 : CoarseType Concrete<void>::coarseType() const      { MOZ_CRASH("null ubi::Node"); }
     158           0 : const char16_t* Concrete<void>::typeName() const   { MOZ_CRASH("null ubi::Node"); }
     159           0 : JS::Zone* Concrete<void>::zone() const             { MOZ_CRASH("null ubi::Node"); }
     160           0 : JSCompartment* Concrete<void>::compartment() const { MOZ_CRASH("null ubi::Node"); }
     161             : 
     162             : UniquePtr<EdgeRange>
     163           0 : Concrete<void>::edges(JSContext*, bool) const {
     164           0 :     MOZ_CRASH("null ubi::Node");
     165             : }
     166             : 
     167             : Node::Size
     168           0 : Concrete<void>::size(mozilla::MallocSizeOf mallocSizeof) const
     169             : {
     170           0 :     MOZ_CRASH("null ubi::Node");
     171             : }
     172             : 
     173             : struct Node::ConstructFunctor : public js::BoolDefaultAdaptor<Value, false> {
     174           0 :     template <typename T> bool operator()(T* t, Node* node) { node->construct(t); return true; }
     175             : };
     176             : 
     177           0 : Node::Node(const JS::GCCellPtr &thing)
     178             : {
     179           0 :     DispatchTyped(ConstructFunctor(), thing, this);
     180           0 : }
     181             : 
     182           0 : Node::Node(HandleValue value)
     183             : {
     184           0 :     if (!DispatchTyped(ConstructFunctor(), value, this))
     185           0 :         construct<void>(nullptr);
     186           0 : }
     187             : 
     188             : Value
     189           0 : Node::exposeToJS() const
     190             : {
     191           0 :     Value v;
     192             : 
     193           0 :     if (is<JSObject>()) {
     194           0 :         JSObject& obj = *as<JSObject>();
     195           0 :         if (obj.is<js::EnvironmentObject>()) {
     196           0 :             v.setUndefined();
     197           0 :         } else if (obj.is<JSFunction>() && js::IsInternalFunctionObject(obj)) {
     198           0 :             v.setUndefined();
     199             :         } else {
     200           0 :             v.setObject(obj);
     201             :         }
     202           0 :     } else if (is<JSString>()) {
     203           0 :         v.setString(as<JSString>());
     204           0 :     } else if (is<JS::Symbol>()) {
     205           0 :         v.setSymbol(as<JS::Symbol>());
     206             :     } else {
     207           0 :         v.setUndefined();
     208             :     }
     209             : 
     210           0 :     ExposeValueToActiveJS(v);
     211             : 
     212           0 :     return v;
     213             : }
     214             : 
     215             : 
     216             : // A JS::CallbackTracer subclass that adds a Edge to a Vector for each
     217             : // edge on which it is invoked.
     218             : class EdgeVectorTracer : public JS::CallbackTracer {
     219             :     // The vector to which we add Edges.
     220             :     EdgeVector* vec;
     221             : 
     222             :     // True if we should populate the edge's names.
     223             :     bool wantNames;
     224             : 
     225           0 :     void onChild(const JS::GCCellPtr& thing) override {
     226           0 :         if (!okay)
     227           0 :             return;
     228             : 
     229             :         // Don't trace permanent atoms and well-known symbols that are owned by
     230             :         // a parent JSRuntime.
     231           0 :         if (thing.is<JSString>() && thing.as<JSString>().isPermanentAtom())
     232           0 :             return;
     233           0 :         if (thing.is<JS::Symbol>() && thing.as<JS::Symbol>().isWellKnownSymbol())
     234           0 :             return;
     235             : 
     236           0 :         char16_t* name16 = nullptr;
     237           0 :         if (wantNames) {
     238             :             // Ask the tracer to compute an edge name for us.
     239             :             char buffer[1024];
     240           0 :             getTracingEdgeName(buffer, sizeof(buffer));
     241           0 :             const char* name = buffer;
     242             : 
     243             :             // Convert the name to char16_t characters.
     244           0 :             name16 = js_pod_malloc<char16_t>(strlen(name) + 1);
     245           0 :             if (!name16) {
     246           0 :                 okay = false;
     247           0 :                 return;
     248             :             }
     249             : 
     250             :             size_t i;
     251           0 :             for (i = 0; name[i]; i++)
     252           0 :                 name16[i] = name[i];
     253           0 :             name16[i] = '\0';
     254             :         }
     255             : 
     256             :         // The simplest code is correct! The temporary Edge takes
     257             :         // ownership of name; if the append succeeds, the vector element
     258             :         // then takes ownership; if the append fails, then the temporary
     259             :         // retains it, and its destructor will free it.
     260           0 :         if (!vec->append(mozilla::Move(Edge(name16, Node(thing))))) {
     261           0 :             okay = false;
     262           0 :             return;
     263             :         }
     264             :     }
     265             : 
     266             :   public:
     267             :     // True if no errors (OOM, say) have yet occurred.
     268             :     bool okay;
     269             : 
     270           0 :     EdgeVectorTracer(JSRuntime* rt, EdgeVector* vec, bool wantNames)
     271           0 :       : JS::CallbackTracer(rt),
     272             :         vec(vec),
     273             :         wantNames(wantNames),
     274           0 :         okay(true)
     275           0 :     { }
     276             : };
     277             : 
     278             : 
     279             : // An EdgeRange concrete class that simply holds a vector of Edges,
     280             : // populated by the init method.
     281           0 : class SimpleEdgeRange : public EdgeRange {
     282             :     EdgeVector edges;
     283             :     size_t i;
     284             : 
     285           0 :     void settle() {
     286           0 :         front_ = i < edges.length() ? &edges[i] : nullptr;
     287           0 :     }
     288             : 
     289             :   public:
     290           0 :     explicit SimpleEdgeRange() : edges(), i(0) { }
     291             : 
     292           0 :     bool init(JSRuntime* rt, void* thing, JS::TraceKind kind, bool wantNames = true) {
     293           0 :         EdgeVectorTracer tracer(rt, &edges, wantNames);
     294           0 :         js::TraceChildren(&tracer, thing, kind);
     295           0 :         settle();
     296           0 :         return tracer.okay;
     297             :     }
     298             : 
     299           0 :     void popFront() override { i++; settle(); }
     300             : };
     301             : 
     302             : 
     303             : template<typename Referent>
     304             : JS::Zone*
     305           0 : TracerConcrete<Referent>::zone() const
     306             : {
     307           0 :     return get().zoneFromAnyThread();
     308             : }
     309             : 
     310             : template JS::Zone* TracerConcrete<JSScript>::zone() const;
     311             : template JS::Zone* TracerConcrete<js::LazyScript>::zone() const;
     312             : template JS::Zone* TracerConcrete<js::Shape>::zone() const;
     313             : template JS::Zone* TracerConcrete<js::BaseShape>::zone() const;
     314             : template JS::Zone* TracerConcrete<js::ObjectGroup>::zone() const;
     315             : template JS::Zone* TracerConcrete<js::RegExpShared>::zone() const;
     316             : template JS::Zone* TracerConcrete<js::Scope>::zone() const;
     317             : template JS::Zone* TracerConcrete<JS::Symbol>::zone() const;
     318             : template JS::Zone* TracerConcrete<JSString>::zone() const;
     319             : 
     320             : template<typename Referent>
     321             : UniquePtr<EdgeRange>
     322           0 : TracerConcrete<Referent>::edges(JSContext* cx, bool wantNames) const {
     323           0 :     UniquePtr<SimpleEdgeRange, JS::DeletePolicy<SimpleEdgeRange>> range(js_new<SimpleEdgeRange>());
     324           0 :     if (!range)
     325           0 :         return nullptr;
     326             : 
     327           0 :     if (!range->init(cx->runtime(), ptr, JS::MapTypeToTraceKind<Referent>::kind, wantNames))
     328           0 :         return nullptr;
     329             : 
     330           0 :     return UniquePtr<EdgeRange>(range.release());
     331             : }
     332             : 
     333             : template UniquePtr<EdgeRange> TracerConcrete<JSScript>::edges(JSContext* cx, bool wantNames) const;
     334             : template UniquePtr<EdgeRange> TracerConcrete<js::LazyScript>::edges(JSContext* cx, bool wantNames) const;
     335             : template UniquePtr<EdgeRange> TracerConcrete<js::Shape>::edges(JSContext* cx, bool wantNames) const;
     336             : template UniquePtr<EdgeRange> TracerConcrete<js::BaseShape>::edges(JSContext* cx, bool wantNames) const;
     337             : template UniquePtr<EdgeRange> TracerConcrete<js::ObjectGroup>::edges(JSContext* cx, bool wantNames) const;
     338             : template UniquePtr<EdgeRange> TracerConcrete<js::RegExpShared>::edges(JSContext* cx, bool wantNames) const;
     339             : template UniquePtr<EdgeRange> TracerConcrete<js::Scope>::edges(JSContext* cx, bool wantNames) const;
     340             : template UniquePtr<EdgeRange> TracerConcrete<JS::Symbol>::edges(JSContext* cx, bool wantNames) const;
     341             : template UniquePtr<EdgeRange> TracerConcrete<JSString>::edges(JSContext* cx, bool wantNames) const;
     342             : 
     343             : template<typename Referent>
     344             : JSCompartment*
     345           0 : TracerConcreteWithCompartment<Referent>::compartment() const
     346             : {
     347           0 :     return TracerBase::get().compartment();
     348             : }
     349             : 
     350             : template JSCompartment* TracerConcreteWithCompartment<JSScript>::compartment() const;
     351             : 
     352             : bool
     353           0 : Concrete<JSObject>::hasAllocationStack() const
     354             : {
     355           0 :     return !!js::Debugger::getObjectAllocationSite(get());
     356             : }
     357             : 
     358             : StackFrame
     359           0 : Concrete<JSObject>::allocationStack() const
     360             : {
     361           0 :     MOZ_ASSERT(hasAllocationStack());
     362           0 :     return StackFrame(js::Debugger::getObjectAllocationSite(get()));
     363             : }
     364             : 
     365             : const char*
     366           0 : Concrete<JSObject>::jsObjectClassName() const
     367             : {
     368           0 :     return Concrete::get().getClass()->name;
     369             : }
     370             : 
     371             : bool
     372           0 : Concrete<JSObject>::jsObjectConstructorName(JSContext* cx, UniqueTwoByteChars& outName) const
     373             : {
     374           0 :     JSAtom* name = Concrete::get().maybeConstructorDisplayAtom();
     375           0 :     if (!name) {
     376           0 :         outName.reset(nullptr);
     377           0 :         return true;
     378             :     }
     379             : 
     380           0 :     auto len = JS_GetStringLength(name);
     381           0 :     auto size = len + 1;
     382             : 
     383           0 :     outName.reset(cx->pod_malloc<char16_t>(size * sizeof(char16_t)));
     384           0 :     if (!outName)
     385           0 :         return false;
     386             : 
     387           0 :     mozilla::Range<char16_t> chars(outName.get(), size);
     388           0 :     if (!JS_CopyStringChars(cx, chars, name))
     389           0 :         return false;
     390             : 
     391           0 :     outName[len] = '\0';
     392           0 :     return true;
     393             : }
     394             : 
     395             : const char16_t Concrete<JS::Symbol>::concreteTypeName[] = u"JS::Symbol";
     396             : const char16_t Concrete<JSScript>::concreteTypeName[] = u"JSScript";
     397             : const char16_t Concrete<js::LazyScript>::concreteTypeName[] = u"js::LazyScript";
     398             : const char16_t Concrete<js::jit::JitCode>::concreteTypeName[] = u"js::jit::JitCode";
     399             : const char16_t Concrete<js::Shape>::concreteTypeName[] = u"js::Shape";
     400             : const char16_t Concrete<js::BaseShape>::concreteTypeName[] = u"js::BaseShape";
     401             : const char16_t Concrete<js::ObjectGroup>::concreteTypeName[] = u"js::ObjectGroup";
     402             : const char16_t Concrete<js::Scope>::concreteTypeName[] = u"js::Scope";
     403             : const char16_t Concrete<js::RegExpShared>::concreteTypeName[] = u"js::RegExpShared";
     404             : 
     405             : namespace JS {
     406             : namespace ubi {
     407             : 
     408           0 : RootList::RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC, bool wantNames /* = false */)
     409             :   : noGC(noGC),
     410             :     cx(cx),
     411             :     edges(),
     412           0 :     wantNames(wantNames)
     413           0 : { }
     414             : 
     415             : 
     416             : bool
     417           0 : RootList::init()
     418             : {
     419           0 :     EdgeVectorTracer tracer(cx->runtime(), &edges, wantNames);
     420           0 :     js::TraceRuntime(&tracer);
     421           0 :     if (!tracer.okay)
     422           0 :         return false;
     423           0 :     noGC.emplace();
     424           0 :     return true;
     425             : }
     426             : 
     427             : bool
     428           0 : RootList::init(CompartmentSet& debuggees)
     429             : {
     430           0 :     EdgeVector allRootEdges;
     431           0 :     EdgeVectorTracer tracer(cx->runtime(), &allRootEdges, wantNames);
     432             : 
     433           0 :     ZoneSet debuggeeZones;
     434           0 :     if (!debuggeeZones.init())
     435           0 :         return false;
     436           0 :     for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
     437           0 :         if (!debuggeeZones.put(range.front()->zone()))
     438           0 :             return false;
     439             :     }
     440             : 
     441           0 :     js::TraceRuntime(&tracer);
     442           0 :     if (!tracer.okay)
     443           0 :         return false;
     444           0 :     TraceIncomingCCWs(&tracer, debuggees);
     445           0 :     if (!tracer.okay)
     446           0 :         return false;
     447             : 
     448           0 :     for (EdgeVector::Range r = allRootEdges.all(); !r.empty(); r.popFront()) {
     449           0 :         Edge& edge = r.front();
     450             : 
     451           0 :         JSCompartment* compartment = edge.referent.compartment();
     452           0 :         if (compartment && !debuggees.has(compartment))
     453           0 :             continue;
     454             : 
     455           0 :         Zone* zone = edge.referent.zone();
     456           0 :         if (zone && !debuggeeZones.has(zone))
     457           0 :             continue;
     458             : 
     459           0 :         if (!edges.append(mozilla::Move(edge)))
     460           0 :             return false;
     461             :     }
     462             : 
     463           0 :     noGC.emplace();
     464           0 :     return true;
     465             : }
     466             : 
     467             : bool
     468           0 : RootList::init(HandleObject debuggees)
     469             : {
     470           0 :     MOZ_ASSERT(debuggees && JS::dbg::IsDebugger(*debuggees));
     471           0 :     js::Debugger* dbg = js::Debugger::fromJSObject(debuggees.get());
     472             : 
     473           0 :     CompartmentSet debuggeeCompartments;
     474           0 :     if (!debuggeeCompartments.init())
     475           0 :         return false;
     476             : 
     477           0 :     for (js::WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
     478           0 :         if (!debuggeeCompartments.put(r.front()->compartment()))
     479           0 :             return false;
     480             :     }
     481             : 
     482           0 :     if (!init(debuggeeCompartments))
     483           0 :         return false;
     484             : 
     485             :     // Ensure that each of our debuggee globals are in the root list.
     486           0 :     for (js::WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
     487           0 :         if (!addRoot(JS::ubi::Node(static_cast<JSObject*>(r.front())),
     488             :                      u"debuggee global"))
     489             :         {
     490           0 :             return false;
     491             :         }
     492             :     }
     493             : 
     494           0 :     return true;
     495             : }
     496             : 
     497             : bool
     498           0 : RootList::addRoot(Node node, const char16_t* edgeName)
     499             : {
     500           0 :     MOZ_ASSERT(noGC.isSome());
     501           0 :     MOZ_ASSERT_IF(wantNames, edgeName);
     502             : 
     503           0 :     UniqueTwoByteChars name;
     504           0 :     if (edgeName) {
     505           0 :         name = js::DuplicateString(edgeName);
     506           0 :         if (!name)
     507           0 :             return false;
     508             :     }
     509             : 
     510           0 :     return edges.append(mozilla::Move(Edge(name.release(), node)));
     511             : }
     512             : 
     513             : const char16_t Concrete<RootList>::concreteTypeName[] = u"JS::ubi::RootList";
     514             : 
     515             : UniquePtr<EdgeRange>
     516           0 : Concrete<RootList>::edges(JSContext* cx, bool wantNames) const {
     517           0 :     MOZ_ASSERT_IF(wantNames, get().wantNames);
     518           0 :     return UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges));
     519             : }
     520             : 
     521             : } // namespace ubi
     522             : } // namespace JS

Generated by: LCOV version 1.13