/* * Copyright (C) 2009-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 "ExecutableToCodeBlockEdge.h" #include "ScriptExecutable.h" #include "SourceCode.h" #include #include namespace JSC { struct FunctionOverrideInfo; class FunctionExecutable final : public ScriptExecutable { friend class JIT; friend class LLIntOffsetsExtractor; public: typedef ScriptExecutable Base; static constexpr unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; template static IsoSubspace* subspaceFor(VM& vm) { return &vm.functionExecutableSpace.space; } static FunctionExecutable* create(VM& vm, ScriptExecutable* topLevelExecutable, const SourceCode& source, UnlinkedFunctionExecutable* unlinkedExecutable, Intrinsic intrinsic, bool isInsideOrdinaryFunction) { FunctionExecutable* executable = new (NotNull, allocateCell(vm.heap)) FunctionExecutable(vm, source, unlinkedExecutable, intrinsic, isInsideOrdinaryFunction); executable->finishCreation(vm, topLevelExecutable); return executable; } static FunctionExecutable* fromGlobalCode( const Identifier& name, JSGlobalObject*, const SourceCode&, JSObject*& exception, int overrideLineNumber, Optional functionConstructorParametersEndPosition); static void destroy(JSCell*); UnlinkedFunctionExecutable* unlinkedExecutable() const { return m_unlinkedExecutable.get(); } // Returns either call or construct bytecode. This can be appropriate // for answering questions that that don't vary between call and construct -- // for example, argumentsRegister(). FunctionCodeBlock* eitherCodeBlock() { ExecutableToCodeBlockEdge* edge; if (m_codeBlockForCall) edge = m_codeBlockForCall.get(); else edge = m_codeBlockForConstruct.get(); return bitwise_cast(ExecutableToCodeBlockEdge::unwrap(edge)); } bool isGeneratedForCall() const { return !!m_codeBlockForCall; } FunctionCodeBlock* codeBlockForCall() { return bitwise_cast(ExecutableToCodeBlockEdge::unwrap(m_codeBlockForCall.get())); } bool isGeneratedForConstruct() const { return !!m_codeBlockForConstruct; } FunctionCodeBlock* codeBlockForConstruct() { return bitwise_cast(ExecutableToCodeBlockEdge::unwrap(m_codeBlockForConstruct.get())); } bool isGeneratedFor(CodeSpecializationKind kind) { if (kind == CodeForCall) return isGeneratedForCall(); ASSERT(kind == CodeForConstruct); return isGeneratedForConstruct(); } FunctionCodeBlock* codeBlockFor(CodeSpecializationKind kind) { if (kind == CodeForCall) return codeBlockForCall(); ASSERT(kind == CodeForConstruct); return codeBlockForConstruct(); } FunctionCodeBlock* baselineCodeBlockFor(CodeSpecializationKind); FunctionCodeBlock* profiledCodeBlockFor(CodeSpecializationKind kind) { return baselineCodeBlockFor(kind); } RefPtr returnStatementTypeSet() { RareData& rareData = ensureRareData(); if (!rareData.m_returnStatementTypeSet) rareData.m_returnStatementTypeSet = TypeSet::create(); return rareData.m_returnStatementTypeSet; } FunctionMode functionMode() { return m_unlinkedExecutable->functionMode(); } bool isBuiltinFunction() const { return m_unlinkedExecutable->isBuiltinFunction(); } ConstructAbility constructAbility() const { return m_unlinkedExecutable->constructAbility(); } bool isClass() const { return m_unlinkedExecutable->isClass(); } bool isArrowFunction() const { return parseMode() == SourceParseMode::ArrowFunctionMode; } bool isGetter() const { return parseMode() == SourceParseMode::GetterMode; } bool isSetter() const { return parseMode() == SourceParseMode::SetterMode; } bool isGenerator() const { return isGeneratorParseMode(parseMode()); } bool isAsyncGenerator() const { return isAsyncGeneratorParseMode(parseMode()); } bool isMethod() const { return parseMode() == SourceParseMode::MethodMode; } bool hasCallerAndArgumentsProperties() const { // Per https://tc39.github.io/ecma262/#sec-forbidden-extensions, only sloppy-mode non-builtin functions in old-style (pre-ES6) syntactic forms can contain // "caller" and "arguments". return !isInStrictContext() && parseMode() == SourceParseMode::NormalFunctionMode && !isClassConstructorFunction(); } bool hasPrototypeProperty() const { return SourceParseModeSet( SourceParseMode::NormalFunctionMode, SourceParseMode::GeneratorBodyMode, SourceParseMode::GeneratorWrapperFunctionMode, SourceParseMode::GeneratorWrapperMethodMode, SourceParseMode::AsyncGeneratorWrapperFunctionMode, SourceParseMode::AsyncGeneratorWrapperMethodMode, SourceParseMode::AsyncGeneratorBodyMode ).contains(parseMode()) || isClass(); } DerivedContextType derivedContextType() const { return m_unlinkedExecutable->derivedContextType(); } bool isClassConstructorFunction() const { return m_unlinkedExecutable->isClassConstructorFunction(); } const Identifier& name() { return m_unlinkedExecutable->name(); } const Identifier& ecmaName() { return m_unlinkedExecutable->ecmaName(); } unsigned parameterCount() const { return m_unlinkedExecutable->parameterCount(); } // Excluding 'this'! SourceParseMode parseMode() const { return m_unlinkedExecutable->parseMode(); } JSParserScriptMode scriptMode() const { return m_unlinkedExecutable->scriptMode(); } SourceCode classSource() const { return m_unlinkedExecutable->classSource(); } static void visitChildren(JSCell*, SlotVisitor&); static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(vm, globalObject, proto, TypeInfo(FunctionExecutableType, StructureFlags), info()); } void setOverrideLineNumber(int overrideLineNumber) { if (overrideLineNumber == -1) { if (UNLIKELY(m_rareData)) m_rareData->m_overrideLineNumber = WTF::nullopt; return; } ensureRareData().m_overrideLineNumber = overrideLineNumber; } Optional overrideLineNumber() const { if (UNLIKELY(m_rareData)) return m_rareData->m_overrideLineNumber; return WTF::nullopt; } int lineCount() const { if (UNLIKELY(m_rareData)) return m_rareData->m_lineCount; return m_unlinkedExecutable->lineCount(); } int endColumn() const { if (UNLIKELY(m_rareData)) return m_rareData->m_endColumn; return m_unlinkedExecutable->linkedEndColumn(m_source.startColumn().oneBasedInt()); } int firstLine() const { return source().firstLine().oneBasedInt(); } int lastLine() const { return firstLine() + lineCount(); } unsigned typeProfilingStartOffset(VM&) const { return typeProfilingStartOffset(); } unsigned typeProfilingStartOffset() const { if (UNLIKELY(m_rareData)) return m_rareData->m_typeProfilingStartOffset; return m_unlinkedExecutable->typeProfilingStartOffset(); } unsigned typeProfilingEndOffset(VM&) const { return typeProfilingEndOffset(); } unsigned typeProfilingEndOffset() const { if (UNLIKELY(m_rareData)) return m_rareData->m_typeProfilingEndOffset; return m_unlinkedExecutable->typeProfilingEndOffset(); } unsigned parametersStartOffset() const { if (UNLIKELY(m_rareData)) return m_rareData->m_parametersStartOffset; return m_unlinkedExecutable->parametersStartOffset(); } void overrideInfo(const FunctionOverrideInfo&); DECLARE_INFO; InferredValue& singleton() { return m_singleton; } void notifyCreation(VM& vm, JSFunction* function, const char* reason) { m_singleton.notifyWrite(vm, this, function, reason); } // Cached poly proto structure for the result of constructing this executable. Structure* cachedPolyProtoStructure() { if (UNLIKELY(m_rareData)) return m_rareData->m_cachedPolyProtoStructure.get(); return nullptr; } void setCachedPolyProtoStructure(VM& vm, Structure* structure) { ensureRareData().m_cachedPolyProtoStructure.set(vm, this, structure); } InlineWatchpointSet& ensurePolyProtoWatchpoint() { if (!m_polyProtoWatchpoint) m_polyProtoWatchpoint = Box::create(IsWatched); return *m_polyProtoWatchpoint; } Box sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; } ScriptExecutable* topLevelExecutable() const { return m_topLevelExecutable.get(); } TemplateObjectMap& ensureTemplateObjectMap(VM&); void finalizeUnconditionally(VM&); private: friend class ExecutableBase; FunctionExecutable(VM&, const SourceCode&, UnlinkedFunctionExecutable*, Intrinsic, bool isInsideOrdinaryFunction); void finishCreation(VM&, ScriptExecutable* topLevelExecutable); friend class ScriptExecutable; struct RareData { WTF_MAKE_STRUCT_FAST_ALLOCATED; RefPtr m_returnStatementTypeSet; unsigned m_lineCount; unsigned m_endColumn; Markable> m_overrideLineNumber; unsigned m_parametersStartOffset { 0 }; unsigned m_typeProfilingStartOffset { UINT_MAX }; unsigned m_typeProfilingEndOffset { UINT_MAX }; std::unique_ptr m_templateObjectMap; WriteBarrier m_cachedPolyProtoStructure; }; RareData& ensureRareData() { if (LIKELY(m_rareData)) return *m_rareData; return ensureRareDataSlow(); } RareData& ensureRareDataSlow(); // FIXME: We can merge rareData pointer and top-level executable pointer. First time, setting parent. // If RareData is required, materialize RareData, swap it, and store top-level executable pointer inside RareData. // https://bugs.webkit.org/show_bug.cgi?id=197625 std::unique_ptr m_rareData; WriteBarrier m_topLevelExecutable; WriteBarrier m_unlinkedExecutable; WriteBarrier m_codeBlockForCall; WriteBarrier m_codeBlockForConstruct; InferredValue m_singleton; Box m_polyProtoWatchpoint; }; } // namespace JSC