/* * Copyright (C) 2015-2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include "InternalFunctionAllocationProfile.h" #include "JSCast.h" #include "ObjectAllocationProfile.h" #include "PackedCellPtr.h" #include "Watchpoint.h" namespace JSC { class ExecutableBase; class JSGlobalObject; class LLIntOffsetsExtractor; namespace DFG { class SpeculativeJIT; class JITCompiler; } class FunctionRareData final : public JSCell { friend class JIT; friend class DFG::SpeculativeJIT; friend class DFG::JITCompiler; friend class VM; public: typedef JSCell Base; static constexpr unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; static FunctionRareData* create(VM&, ExecutableBase*); static constexpr bool needsDestruction = true; template static IsoSubspace* subspaceFor(VM& vm) { return vm.functionRareDataSpace(); } static void destroy(JSCell*); static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); static void visitChildren(JSCell*, SlotVisitor&); DECLARE_INFO; static inline ptrdiff_t offsetOfObjectAllocationProfile() { return OBJECT_OFFSETOF(FunctionRareData, m_objectAllocationProfile); } static inline ptrdiff_t offsetOfAllocationProfileWatchpointSet() { return OBJECT_OFFSETOF(FunctionRareData, m_allocationProfileWatchpointSet); } static inline ptrdiff_t offsetOfInternalFunctionAllocationProfile() { return OBJECT_OFFSETOF(FunctionRareData, m_internalFunctionAllocationProfile); } static inline ptrdiff_t offsetOfBoundFunctionStructure() { return OBJECT_OFFSETOF(FunctionRareData, m_boundFunctionStructure); } static inline ptrdiff_t offsetOfExecutable() { return OBJECT_OFFSETOF(FunctionRareData, m_executable); } static inline ptrdiff_t offsetOfAllocationProfileClearingWatchpoint() { return OBJECT_OFFSETOF(FunctionRareData, m_allocationProfileClearingWatchpoint); } ObjectAllocationProfileWithPrototype* objectAllocationProfile() { return &m_objectAllocationProfile; } Structure* objectAllocationStructure() { return m_objectAllocationProfile.structure(); } JSObject* objectAllocationPrototype() { return m_objectAllocationProfile.prototype(); } InlineWatchpointSet& allocationProfileWatchpointSet() { return m_allocationProfileWatchpointSet; } void clear(const char* reason); void initializeObjectAllocationProfile(VM&, JSGlobalObject*, JSObject* prototype, size_t inlineCapacity, JSFunction* constructor); bool isObjectAllocationProfileInitialized() { return !m_objectAllocationProfile.isNull(); } Structure* internalFunctionAllocationStructure() { return m_internalFunctionAllocationProfile.structure(); } Structure* createInternalFunctionAllocationStructureFromBase(VM& vm, JSGlobalObject* baseGlobalObject, JSObject* prototype, Structure* baseStructure) { initializeAllocationProfileWatchpointSet(); return m_internalFunctionAllocationProfile.createAllocationStructureFromBase(vm, baseGlobalObject, this, prototype, baseStructure); } void clearInternalFunctionAllocationProfile(const char* reason) { m_internalFunctionAllocationProfile.clear(); m_allocationProfileWatchpointSet.fireAll(vm(), reason); } void initializeAllocationProfileWatchpointSet() { if (m_allocationProfileWatchpointSet.isStillValid()) m_allocationProfileWatchpointSet.startWatching(); } Structure* getBoundFunctionStructure() { return m_boundFunctionStructure.get(); } void setBoundFunctionStructure(VM& vm, Structure* structure) { m_boundFunctionStructure.set(vm, this, structure); } ExecutableBase* executable() const { return m_executable.get(); } bool hasReifiedLength() const { return m_hasReifiedLength; } void setHasReifiedLength() { m_hasReifiedLength = true; } bool hasReifiedName() const { return m_hasReifiedName; } void setHasReifiedName() { m_hasReifiedName = true; } bool hasModifiedLengthForNonHostFunction() const { return m_hasModifiedLengthForNonHostFunction; } void setHasModifiedLengthForNonHostFunction() { m_hasModifiedLengthForNonHostFunction = true; } bool hasModifiedNameForNonHostFunction() const { return m_hasModifiedNameForNonHostFunction; } void setHasModifiedNameForNonHostFunction() { m_hasModifiedNameForNonHostFunction = true; } bool hasAllocationProfileClearingWatchpoint() const { return !!m_allocationProfileClearingWatchpoint; } Watchpoint* createAllocationProfileClearingWatchpoint(); class AllocationProfileClearingWatchpoint; private: friend class LLIntOffsetsExtractor; explicit FunctionRareData(VM&, ExecutableBase*); ~FunctionRareData(); // Ideally, there would only be one allocation profile for subclassing but due to Reflect.construct we // have two. There are some pros and cons in comparison to our current system to using the same profile // for both JS constructors and subclasses of builtin constructors: // // 1) + Uses less memory. // 2) + Conceptually simplier as there is only one profile. // 3) - We would need a check in all JSFunction object creations (both with classes and without) that the // new.target's profiled structure has a JSFinalObject ClassInfo. This is needed, for example, if we have // `Reflect.construct(Array, args, myConstructor)` since myConstructor will be the new.target of Array // the Array constructor will set the allocation profile of myConstructor to hold an Array structure // // We don't really care about 1) since this memory is rare and small in total. 2) is unfortunate but is // probably outweighed by the cost of 3). ObjectAllocationProfileWithPrototype m_objectAllocationProfile; InlineWatchpointSet m_allocationProfileWatchpointSet; InternalFunctionAllocationProfile m_internalFunctionAllocationProfile; WriteBarrier m_boundFunctionStructure; WriteBarrier m_executable; std::unique_ptr m_allocationProfileClearingWatchpoint; bool m_hasReifiedLength : 1; bool m_hasReifiedName : 1; bool m_hasModifiedLengthForNonHostFunction : 1; bool m_hasModifiedNameForNonHostFunction : 1; }; class FunctionRareData::AllocationProfileClearingWatchpoint final : public Watchpoint { public: AllocationProfileClearingWatchpoint(FunctionRareData* rareData) : Watchpoint(Watchpoint::Type::FunctionRareDataAllocationProfileClearing) , m_rareData(rareData) { } void fireInternal(VM&, const FireDetail&); private: // Own destructor may not be called. Keep members trivially destructible. JSC_WATCHPOINT_FIELD(PackedCellPtr, m_rareData); }; inline Watchpoint* FunctionRareData::createAllocationProfileClearingWatchpoint() { RELEASE_ASSERT(!hasAllocationProfileClearingWatchpoint()); m_allocationProfileClearingWatchpoint = makeUnique(this); return m_allocationProfileClearingWatchpoint.get(); } } // namespace JSC