| /* |
| * 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 "GenericArguments.h" |
| #include "JSLexicalEnvironment.h" |
| |
| namespace JSC { |
| |
| // This is an Arguments-class object that we create when you say "arguments" inside a function, |
| // and one or more of the arguments may be captured in the function's activation. The function |
| // will copy its formally declared arguments into the activation and then create this object. This |
| // object will store the overflow arguments, if there are any. This object will use the symbol |
| // table's ScopedArgumentsTable and the activation, or its overflow storage, to handle all indexed |
| // lookups. |
| class ScopedArguments final : public GenericArguments<ScopedArguments> { |
| private: |
| ScopedArguments(VM&, Structure*, WriteBarrier<Unknown>* storage); |
| void finishCreation(VM&, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*); |
| using Base = GenericArguments<ScopedArguments>; |
| |
| public: |
| template<typename CellType, SubspaceAccess> |
| static CompleteSubspace* subspaceFor(VM& vm) |
| { |
| static_assert(!CellType::needsDestruction, ""); |
| return &vm.variableSizedCellSpace; |
| } |
| |
| // Creates an arguments object but leaves it uninitialized. This is dangerous if we GC right |
| // after allocation. |
| static ScopedArguments* createUninitialized(VM&, Structure*, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*, unsigned totalLength); |
| |
| // Creates an arguments object and initializes everything to the empty value. Use this if you |
| // cannot guarantee that you'll immediately initialize all of the elements. |
| static ScopedArguments* create(VM&, Structure*, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*, unsigned totalLength); |
| |
| // Creates an arguments object by copying the arguments from the stack. |
| static ScopedArguments* createByCopying(JSGlobalObject*, CallFrame*, ScopedArgumentsTable*, JSLexicalEnvironment*); |
| |
| // Creates an arguments object by copying the arguments from a well-defined stack location. |
| static ScopedArguments* createByCopyingFrom(VM&, Structure*, Register* argumentsStart, unsigned totalLength, JSFunction* callee, ScopedArgumentsTable*, JSLexicalEnvironment*); |
| |
| static void visitChildren(JSCell*, SlotVisitor&); |
| |
| uint32_t internalLength() const |
| { |
| return storageHeader().totalLength; |
| } |
| |
| uint32_t length(JSGlobalObject* globalObject) const |
| { |
| VM& vm = getVM(globalObject); |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| if (UNLIKELY(storageHeader().overrodeThings)) { |
| auto value = get(globalObject, vm.propertyNames->length); |
| RETURN_IF_EXCEPTION(scope, 0); |
| RELEASE_AND_RETURN(scope, value.toUInt32(globalObject)); |
| } |
| return internalLength(); |
| } |
| |
| bool isMappedArgument(uint32_t i) const |
| { |
| WriteBarrier<Unknown>* storage = overflowStorage(); |
| if (i >= storageHeader(storage).totalLength) |
| return false; |
| unsigned namedLength = m_table->length(); |
| if (i < namedLength) |
| return !!m_table->get(i); |
| return !!storage[i - namedLength].get(); |
| } |
| |
| bool isMappedArgumentInDFG(uint32_t i) const |
| { |
| return isMappedArgument(i); |
| } |
| |
| JSValue getIndexQuickly(uint32_t i) const |
| { |
| ASSERT_WITH_SECURITY_IMPLICATION(isMappedArgument(i)); |
| WriteBarrier<Unknown>* storage = overflowStorage(); |
| unsigned totalLength = storageHeader(storage).totalLength; |
| unsigned namedLength = m_table->length(); |
| if (i < namedLength) |
| return preciseIndexMaskPtr(i, totalLength, &m_scope->variableAt(m_table->get(i)))->get(); |
| return preciseIndexMaskPtr(i, totalLength, storage + (i - namedLength))->get(); |
| } |
| |
| void setIndexQuickly(VM& vm, uint32_t i, JSValue value) |
| { |
| ASSERT_WITH_SECURITY_IMPLICATION(isMappedArgument(i)); |
| WriteBarrier<Unknown>* storage = overflowStorage(); |
| unsigned totalLength = storageHeader(storage).totalLength; |
| unsigned namedLength = m_table->length(); |
| if (i < namedLength) |
| preciseIndexMaskPtr(i, totalLength, &m_scope->variableAt(m_table->get(i)))->set(vm, m_scope.get(), value); |
| else |
| preciseIndexMaskPtr(i, totalLength, storage + (i - namedLength))->set(vm, this, value); |
| } |
| |
| JSFunction* callee() |
| { |
| return m_callee.get(); |
| } |
| |
| bool overrodeThings() const { return storageHeader().overrodeThings; } |
| void overrideThings(VM&); |
| void overrideThingsIfNecessary(VM&); |
| void unmapArgument(VM&, uint32_t index); |
| |
| void initModifiedArgumentsDescriptorIfNecessary(VM& vm) |
| { |
| GenericArguments<ScopedArguments>::initModifiedArgumentsDescriptorIfNecessary(vm, m_table->length()); |
| } |
| |
| void setModifiedArgumentDescriptor(VM& vm, unsigned index) |
| { |
| GenericArguments<ScopedArguments>::setModifiedArgumentDescriptor(vm, index, m_table->length()); |
| } |
| |
| bool isModifiedArgumentDescriptor(unsigned index) |
| { |
| return GenericArguments<ScopedArguments>::isModifiedArgumentDescriptor(index, m_table->length()); |
| } |
| |
| void copyToArguments(JSGlobalObject*, CallFrame*, VirtualRegister firstElementDest, unsigned offset, unsigned length); |
| |
| DECLARE_INFO; |
| |
| static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); |
| |
| static ptrdiff_t offsetOfStorage() { return OBJECT_OFFSETOF(ScopedArguments, m_storage); } |
| static ptrdiff_t offsetOfOverrodeThingsInStorage() { return OBJECT_OFFSETOF(StorageHeader, overrodeThings) - sizeof(WriteBarrier<Unknown>); } |
| static ptrdiff_t offsetOfTotalLengthInStorage() { return OBJECT_OFFSETOF(StorageHeader, totalLength) - sizeof(WriteBarrier<Unknown>); } |
| static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(ScopedArguments, m_table); } |
| static ptrdiff_t offsetOfScope() { return OBJECT_OFFSETOF(ScopedArguments, m_scope); } |
| |
| static size_t allocationSize(size_t inlineSize) |
| { |
| RELEASE_ASSERT(!inlineSize); |
| return sizeof(ScopedArguments); |
| } |
| |
| static size_t storageSize(Checked<size_t> capacity) |
| { |
| return (sizeof(WriteBarrier<Unknown>) * (capacity + static_cast<size_t>(1))).unsafeGet(); |
| } |
| |
| static size_t storageHeaderSize() { return sizeof(WriteBarrier<Unknown>); } |
| |
| private: |
| struct StorageHeader { |
| unsigned totalLength; |
| bool overrodeThings; // True if length, callee, and caller are fully materialized in the object. |
| }; |
| |
| WriteBarrier<Unknown>* overflowStorage() const |
| { |
| return m_storage.get(); |
| } |
| |
| static StorageHeader& storageHeader(WriteBarrier<Unknown>* storage) |
| { |
| static_assert(sizeof(StorageHeader) <= sizeof(WriteBarrier<Unknown>), "StorageHeader needs to be no bigger than a JSValue"); |
| return *bitwise_cast<StorageHeader*>(storage - 1); |
| } |
| |
| StorageHeader& storageHeader() const |
| { |
| return storageHeader(overflowStorage()); |
| } |
| |
| WriteBarrier<JSFunction> m_callee; |
| WriteBarrier<ScopedArgumentsTable> m_table; |
| WriteBarrier<JSLexicalEnvironment> m_scope; |
| |
| AuxiliaryBarrier<WriteBarrier<Unknown>*> m_storage; |
| }; |
| |
| } // namespace JSC |