diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h')
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h | 1017 |
1 files changed, 744 insertions, 273 deletions
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h index 4e9f213c9..06da7cb71 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008-2009, 2012-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> * Copyright (C) 2012 Igalia, S.L. * @@ -12,7 +12,7 @@ * 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -28,34 +28,32 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BytecodeGenerator_h -#define BytecodeGenerator_h +#pragma once #include "CodeBlock.h" -#include <wtf/HashTraits.h> #include "Instruction.h" +#include "Interpreter.h" +#include "JSGeneratorFunction.h" #include "Label.h" #include "LabelScope.h" -#include "Interpreter.h" +#include "Nodes.h" #include "ParserError.h" #include "RegisterID.h" -#include "SymbolTable.h" -#include "Debugger.h" -#include "Nodes.h" #include "StaticPropertyAnalyzer.h" +#include "SymbolTable.h" +#include "TemplateRegistryKey.h" #include "UnlinkedCodeBlock.h" - #include <functional> - -#include <wtf/PassRefPtr.h> +#include <wtf/CheckedArithmetic.h> +#include <wtf/HashTraits.h> #include <wtf/SegmentedVector.h> +#include <wtf/SetForScope.h> #include <wtf/Vector.h> - namespace JSC { class Identifier; - class Label; + class JSTemplateRegistryKey; enum ExpectedFunction { NoExpectedFunction, @@ -63,132 +61,334 @@ namespace JSC { ExpectArrayConstructor }; + enum class DebuggableCall { Yes, No }; + enum class ThisResolutionType { Local, Scoped }; + class CallArguments { public: CallArguments(BytecodeGenerator&, ArgumentsNode*, unsigned additionalArguments = 0); RegisterID* thisRegister() { return m_argv[0].get(); } RegisterID* argumentRegister(unsigned i) { return m_argv[i + 1].get(); } - unsigned stackOffset() { return -m_argv[0]->index() + JSStack::CallFrameHeaderSize; } + unsigned stackOffset() { return -m_argv[0]->index() + CallFrame::headerSizeInRegisters; } unsigned argumentCountIncludingThis() { return m_argv.size() - m_padding; } - RegisterID* profileHookRegister() { return m_profileHookRegister.get(); } ArgumentsNode* argumentsNode() { return m_argumentsNode; } private: - RefPtr<RegisterID> m_profileHookRegister; ArgumentsNode* m_argumentsNode; Vector<RefPtr<RegisterID>, 8, UnsafeVectorOverflow> m_argv; unsigned m_padding; }; + // https://tc39.github.io/ecma262/#sec-completion-record-specification-type + // + // For the Break and Continue cases, instead of using the Break and Continue enum values + // below, we use the unique jumpID of the break and continue statement as the encoding + // for the CompletionType value. emitFinallyCompletion() uses this jumpID value later + // to determine the appropriate jump target to jump to after executing the relevant finally + // blocks. The jumpID is computed as: + // jumpID = bytecodeOffset (of the break/continue node) + CompletionType::NumberOfTypes. + // Hence, there won't be any collision between jumpIDs and CompletionType enums. + enum class CompletionType : int { + Normal, + Break, + Continue, + Return, + Throw, + + NumberOfTypes + }; + + inline CompletionType bytecodeOffsetToJumpID(unsigned offset) + { + int jumpIDAsInt = offset + static_cast<int>(CompletionType::NumberOfTypes); + ASSERT(jumpIDAsInt >= static_cast<int>(CompletionType::NumberOfTypes)); + return static_cast<CompletionType>(jumpIDAsInt); + } + + struct FinallyJump { + FinallyJump(CompletionType jumpID, int targetLexicalScopeIndex, Label& targetLabel) + : jumpID(jumpID) + , targetLexicalScopeIndex(targetLexicalScopeIndex) + , targetLabel(targetLabel) + { } + + CompletionType jumpID; + int targetLexicalScopeIndex; + Ref<Label> targetLabel; + }; + struct FinallyContext { - StatementNode* finallyBlock; - unsigned scopeContextStackSize; - unsigned switchContextStackSize; - unsigned forInContextStackSize; - unsigned tryContextStackSize; - unsigned labelScopesSize; - int finallyDepth; - int dynamicScopeDepth; + FinallyContext() { } + FinallyContext(FinallyContext* outerContext, Label& finallyLabel) + : m_outerContext(outerContext) + , m_finallyLabel(&finallyLabel) + { + ASSERT(m_jumps.isEmpty()); + } + + FinallyContext* outerContext() const { return m_outerContext; } + Label* finallyLabel() const { return m_finallyLabel; } + + uint32_t numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues.unsafeGet(); } + void incNumberOfBreaksOrContinues() { m_numberOfBreaksOrContinues++; } + + bool handlesReturns() const { return m_handlesReturns; } + void setHandlesReturns() { m_handlesReturns = true; } + + void registerJump(CompletionType jumpID, int lexicalScopeIndex, Label& targetLabel) + { + m_jumps.append(FinallyJump(jumpID, lexicalScopeIndex, targetLabel)); + } + + size_t numberOfJumps() const { return m_jumps.size(); } + FinallyJump& jumps(size_t i) { return m_jumps[i]; } + + private: + FinallyContext* m_outerContext { nullptr }; + Label* m_finallyLabel { nullptr }; + Checked<uint32_t, WTF::CrashOnOverflow> m_numberOfBreaksOrContinues; + bool m_handlesReturns { false }; + Vector<FinallyJump> m_jumps; }; - struct ControlFlowContext { - bool isFinallyBlock; + struct ControlFlowScope { + typedef uint8_t Type; + enum { + Label, + Finally + }; + ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext&& finallyContext = FinallyContext()) + : type(type) + , lexicalScopeIndex(lexicalScopeIndex) + , finallyContext(std::forward<FinallyContext>(finallyContext)) + { } + + bool isLabelScope() const { return type == Label; } + bool isFinallyScope() const { return type == Finally; } + + Type type; + int lexicalScopeIndex; FinallyContext finallyContext; }; - struct ForInContext { - RefPtr<RegisterID> expectedSubscriptRegister; - RefPtr<RegisterID> iterRegister; - RefPtr<RegisterID> indexRegister; - RefPtr<RegisterID> propertyRegister; + class ForInContext : public RefCounted<ForInContext> { + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(ForInContext); + public: + ForInContext(RegisterID* localRegister) + : m_localRegister(localRegister) + , m_isValid(true) + { + } + + virtual ~ForInContext() + { + } + + bool isValid() const { return m_isValid; } + void invalidate() { m_isValid = false; } + + enum ForInContextType { + StructureForInContextType, + IndexedForInContextType + }; + virtual ForInContextType type() const = 0; + + RegisterID* local() const { return m_localRegister.get(); } + + private: + RefPtr<RegisterID> m_localRegister; + bool m_isValid; + }; + + class StructureForInContext : public ForInContext { + public: + StructureForInContext(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister) + : ForInContext(localRegister) + , m_indexRegister(indexRegister) + , m_propertyRegister(propertyRegister) + , m_enumeratorRegister(enumeratorRegister) + { + } + + ForInContextType type() const override + { + return StructureForInContextType; + } + + RegisterID* index() const { return m_indexRegister.get(); } + RegisterID* property() const { return m_propertyRegister.get(); } + RegisterID* enumerator() const { return m_enumeratorRegister.get(); } + + private: + RefPtr<RegisterID> m_indexRegister; + RefPtr<RegisterID> m_propertyRegister; + RefPtr<RegisterID> m_enumeratorRegister; + }; + + class IndexedForInContext : public ForInContext { + public: + IndexedForInContext(RegisterID* localRegister, RegisterID* indexRegister) + : ForInContext(localRegister) + , m_indexRegister(indexRegister) + { + } + + ForInContextType type() const override + { + return IndexedForInContextType; + } + + RegisterID* index() const { return m_indexRegister.get(); } + + private: + RefPtr<RegisterID> m_indexRegister; }; struct TryData { - RefPtr<Label> target; - unsigned targetScopeDepth; + Ref<Label> target; + HandlerType handlerType; }; struct TryContext { - RefPtr<Label> start; + Ref<Label> start; TryData* tryData; }; - enum CaptureMode { - NotCaptured, - IsCaptured - }; - - class Local { + class Variable { public: - Local() - : m_local(0) + enum VariableKind { NormalVariable, SpecialVariable }; + + Variable() + : m_offset() + , m_local(nullptr) + , m_attributes(0) + , m_kind(NormalVariable) + , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable. + , m_isLexicallyScoped(false) + { + } + + Variable(const Identifier& ident) + : m_ident(ident) + , m_local(nullptr) , m_attributes(0) + , m_kind(NormalVariable) // This is somewhat meaningless here for this kind of Variable. + , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable. + , m_isLexicallyScoped(false) { } - Local(RegisterID* local, unsigned attributes, CaptureMode captureMode) - : m_local(local) + Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind, int symbolTableConstantIndex, bool isLexicallyScoped) + : m_ident(ident) + , m_offset(offset) + , m_local(local) , m_attributes(attributes) - , m_isCaptured(captureMode == IsCaptured) + , m_kind(kind) + , m_symbolTableConstantIndex(symbolTableConstantIndex) + , m_isLexicallyScoped(isLexicallyScoped) { } - operator bool() { return m_local; } + // If it's unset, then it is a non-locally-scoped variable. If it is set, then it could be + // a stack variable, a scoped variable in a local scope, or a variable captured in the + // direct arguments object. + bool isResolved() const { return !!m_offset; } + int symbolTableConstantIndex() const { ASSERT(isResolved() && !isSpecial()); return m_symbolTableConstantIndex; } + + const Identifier& ident() const { return m_ident; } + + VarOffset offset() const { return m_offset; } + bool isLocal() const { return m_offset.isStack(); } + RegisterID* local() const { return m_local; } - RegisterID* get() { return m_local; } + bool isReadOnly() const { return m_attributes & ReadOnly; } + bool isSpecial() const { return m_kind != NormalVariable; } + bool isConst() const { return isReadOnly() && m_isLexicallyScoped; } + void setIsReadOnly() { m_attributes |= ReadOnly; } - bool isReadOnly() { return m_attributes & ReadOnly; } - - bool isCaptured() { return m_isCaptured; } - CaptureMode captureMode() { return isCaptured() ? IsCaptured : NotCaptured; } + void dump(PrintStream&) const; private: + Identifier m_ident; + VarOffset m_offset; RegisterID* m_local; unsigned m_attributes; - bool m_isCaptured; + VariableKind m_kind; + int m_symbolTableConstantIndex; + bool m_isLexicallyScoped; }; struct TryRange { - RefPtr<Label> start; - RefPtr<Label> end; + Ref<Label> start; + Ref<Label> end; TryData* tryData; }; + enum ProfileTypeBytecodeFlag { + ProfileTypeBytecodeClosureVar, + ProfileTypeBytecodeLocallyResolved, + ProfileTypeBytecodeDoesNotHaveGlobalID, + ProfileTypeBytecodeFunctionArgument, + ProfileTypeBytecodeFunctionReturnStatement + }; + class BytecodeGenerator { WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(BytecodeGenerator); public: - typedef DeclarationStacks::VarStack VarStack; typedef DeclarationStacks::FunctionStack FunctionStack; - BytecodeGenerator(VM&, ProgramNode*, UnlinkedProgramCodeBlock*, DebuggerMode, ProfilerMode); - BytecodeGenerator(VM&, FunctionBodyNode*, UnlinkedFunctionCodeBlock*, DebuggerMode, ProfilerMode); - BytecodeGenerator(VM&, EvalNode*, UnlinkedEvalCodeBlock*, DebuggerMode, ProfilerMode); + BytecodeGenerator(VM&, ProgramNode*, UnlinkedProgramCodeBlock*, DebuggerMode, const VariableEnvironment*); + BytecodeGenerator(VM&, FunctionNode*, UnlinkedFunctionCodeBlock*, DebuggerMode, const VariableEnvironment*); + BytecodeGenerator(VM&, EvalNode*, UnlinkedEvalCodeBlock*, DebuggerMode, const VariableEnvironment*); + BytecodeGenerator(VM&, ModuleProgramNode*, UnlinkedModuleProgramCodeBlock*, DebuggerMode, const VariableEnvironment*); ~BytecodeGenerator(); VM* vm() const { return m_vm; } + ParserArena& parserArena() const { return m_scopeNode->parserArena(); } const CommonIdentifiers& propertyNames() const { return *m_vm->propertyNames; } - bool isConstructor() { return m_codeBlock->isConstructor(); } - - ParserError generate(); + bool isConstructor() const { return m_codeBlock->isConstructor(); } + DerivedContextType derivedContextType() const { return m_derivedContextType; } + bool usesArrowFunction() const { return m_scopeNode->usesArrowFunction(); } + bool needsToUpdateArrowFunctionContext() const { return m_needsToUpdateArrowFunctionContext; } + bool usesEval() const { return m_scopeNode->usesEval(); } + bool usesThis() const { return m_scopeNode->usesThis(); } + ConstructorKind constructorKind() const { return m_codeBlock->constructorKind(); } + SuperBinding superBinding() const { return m_codeBlock->superBinding(); } + JSParserScriptMode scriptMode() const { return m_codeBlock->scriptMode(); } + + template<typename... Args> + static ParserError generate(VM& vm, Args&& ...args) + { + DeferGC deferGC(vm.heap); + auto bytecodeGenerator = std::make_unique<BytecodeGenerator>(vm, std::forward<Args>(args)...); + return bytecodeGenerator->generate(); + } bool isArgumentNumber(const Identifier&, int); - void setIsNumericCompareFunction(bool isNumericCompareFunction); - - bool willResolveToArguments(const Identifier&); - RegisterID* uncheckedRegisterForArguments(); - - bool isCaptured(int operand); - CaptureMode captureMode(int operand) { return isCaptured(operand) ? IsCaptured : NotCaptured; } + Variable variable(const Identifier&, ThisResolutionType = ThisResolutionType::Local); + + enum ExistingVariableMode { VerifyExisting, IgnoreExisting }; + void createVariable(const Identifier&, VarKind, SymbolTable*, ExistingVariableMode = VerifyExisting); // Creates the variable, or asserts that the already-created variable is sufficiently compatible. - Local local(const Identifier&); - Local constLocal(const Identifier&); - // Returns the register storing "this" RegisterID* thisRegister() { return &m_thisRegister; } + RegisterID* argumentsRegister() { return m_argumentsRegister; } + RegisterID* newTarget() + { + return m_newTargetRegister; + } + + RegisterID* scopeRegister() { return m_scopeRegister; } + + RegisterID* generatorRegister() { return m_generatorRegister; } + + RegisterID* promiseCapabilityRegister() { return m_promiseCapabilityRegister; } // Returns the next available temporary register. Registers returned by // newTemporary require a modified form of reference counting: any @@ -206,6 +406,13 @@ namespace JSC { RegisterID* ignoredResult() { return &m_ignoredResultRegister; } + // This will be allocated in the temporary region of registers, but it will + // not be marked as a temporary. This will ensure that finalDestination() does + // not overwrite a block scope variable that it mistakes as a temporary. These + // registers can be (and are) reclaimed when the lexical scope they belong to + // is no longer on the symbol table stack. + RegisterID* newBlockScopeVariable(); + // Returns a place to write intermediate values of an operation // which reuses dst if it is safe to do so. RegisterID* tempDestination(RegisterID* dst) @@ -226,7 +433,7 @@ namespace JSC { RegisterID* destinationForAssignResult(RegisterID* dst) { - if (dst && dst != ignoredResult() && m_codeBlock->needsFullScopeChain()) + if (dst && dst != ignoredResult()) return dst->isTemporary() ? dst : newTemporary(); return 0; } @@ -238,49 +445,71 @@ namespace JSC { } LabelScopePtr newLabelScope(LabelScope::Type, const Identifier* = 0); - PassRefPtr<Label> newLabel(); + Ref<Label> newLabel(); + Ref<Label> newEmittedLabel(); void emitNode(RegisterID* dst, StatementNode* n) { + SetForScope<bool> tailPositionPoisoner(m_inTailPosition, false); + return emitNodeInTailPosition(dst, n); + } + + void emitNodeInTailPosition(RegisterID* dst, StatementNode* n) + { // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); - // Should never store directly into a captured variable. - ASSERT(!dst || dst == ignoredResult() || !isCaptured(dst->index())); - if (!m_vm->isSafeToRecurse()) { + if (UNLIKELY(!m_vm->isSafeToRecurse())) { emitThrowExpressionTooDeepException(); return; } + if (UNLIKELY(n->needsDebugHook())) + emitDebugHook(n); n->emitBytecode(*this, dst); } void emitNode(StatementNode* n) { - emitNode(0, n); + emitNode(nullptr, n); + } + + void emitNodeInTailPosition(StatementNode* n) + { + emitNodeInTailPosition(nullptr, n); } RegisterID* emitNode(RegisterID* dst, ExpressionNode* n) { + SetForScope<bool> tailPositionPoisoner(m_inTailPosition, false); + return emitNodeInTailPosition(dst, n); + } + + RegisterID* emitNodeInTailPosition(RegisterID* dst, ExpressionNode* n) + { // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); - // Should never store directly into a captured variable. - ASSERT(!dst || dst == ignoredResult() || !isCaptured(dst->index())); - if (!m_vm->isSafeToRecurse()) + if (UNLIKELY(!m_vm->isSafeToRecurse())) return emitThrowExpressionTooDeepException(); + if (UNLIKELY(n->needsDebugHook())) + emitDebugHook(n); return n->emitBytecode(*this, dst); } RegisterID* emitNode(ExpressionNode* n) { - return emitNode(0, n); + return emitNode(nullptr, n); + } + + RegisterID* emitNodeInTailPosition(ExpressionNode* n) + { + return emitNodeInTailPosition(nullptr, n); } - void emitNodeInConditionContext(ExpressionNode* n, Label* trueTarget, Label* falseTarget, FallThroughMode fallThroughMode) + void emitNodeInConditionContext(ExpressionNode* n, Label& trueTarget, Label& falseTarget, FallThroughMode fallThroughMode) { - if (!m_vm->isSafeToRecurse()) { + if (UNLIKELY(!m_vm->isSafeToRecurse())) { emitThrowExpressionTooDeepException(); return; } - n->emitBytecodeInConditionContext(*this, trueTarget, falseTarget, fallThroughMode); } @@ -290,7 +519,7 @@ namespace JSC { ASSERT(divotEnd.offset >= divot.offset); int sourceOffset = m_scopeNode->source().startOffset(); - unsigned firstLine = m_scopeNode->source().firstLine(); + unsigned firstLine = m_scopeNode->source().firstLine().oneBasedInt(); int divotOffset = divot.offset - sourceOffset; int startOffset = divot.offset - divotStart.offset; @@ -312,18 +541,20 @@ namespace JSC { unsigned column = divotOffset - lineStart; unsigned instructionOffset = instructions().size(); - m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column); + if (!m_isBuiltinFunction) + m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column); } + ALWAYS_INLINE bool leftHandSideNeedsCopy(bool rightHasAssignments, bool rightIsPure) { - return (m_codeType != FunctionCode || m_codeBlock->needsFullScopeChain() || rightHasAssignments) && !rightIsPure; + return (m_codeType != FunctionCode || rightHasAssignments) && !rightIsPure; } - ALWAYS_INLINE PassRefPtr<RegisterID> emitNodeForLeftHandSide(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure) + ALWAYS_INLINE RefPtr<RegisterID> emitNodeForLeftHandSide(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure) { if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) { - PassRefPtr<RegisterID> dst = newTemporary(); + RefPtr<RegisterID> dst = newTemporary(); emitNode(dst.get(), n); return dst; } @@ -331,62 +562,125 @@ namespace JSC { return emitNode(n); } + void hoistSloppyModeFunctionIfNecessary(const Identifier& functionName); + + private: + void emitTypeProfilerExpressionInfo(const JSTextPosition& startDivot, const JSTextPosition& endDivot); + public: + + // This doesn't emit expression info. If using this, make sure you shouldn't be emitting text offset. + void emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag); + // These variables are associated with variables in a program. They could be Locals, LocalClosureVar, or ClosureVar. + void emitProfileType(RegisterID* registerToProfile, const Variable&, const JSTextPosition& startDivot, const JSTextPosition& endDivot); + + void emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag, const JSTextPosition& startDivot, const JSTextPosition& endDivot); + // These are not associated with variables and don't have a global id. + void emitProfileType(RegisterID* registerToProfile, const JSTextPosition& startDivot, const JSTextPosition& endDivot); + + void emitProfileControlFlow(int); + + RegisterID* emitLoadArrowFunctionLexicalEnvironment(const Identifier&); + RegisterID* ensureThis(); + void emitLoadThisFromArrowFunctionLexicalEnvironment(); + RegisterID* emitLoadNewTargetFromArrowFunctionLexicalEnvironment(); + RegisterID* emitLoad(RegisterID* dst, bool); - RegisterID* emitLoad(RegisterID* dst, double); RegisterID* emitLoad(RegisterID* dst, const Identifier&); - RegisterID* emitLoad(RegisterID* dst, JSValue); + RegisterID* emitLoad(RegisterID* dst, JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other); RegisterID* emitLoadGlobalObject(RegisterID* dst); RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src); + RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src, OperandTypes); + RegisterID* emitUnaryOpProfiled(OpcodeID, RegisterID* dst, RegisterID* src); RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes); RegisterID* emitEqualityOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2); RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src); RegisterID* emitCreateThis(RegisterID* dst); + void emitTDZCheck(RegisterID* target); + bool needsTDZCheck(const Variable&); + void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope); + void liftTDZCheckIfPossible(const Variable&); RegisterID* emitNewObject(RegisterID* dst); RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision - - RegisterID* emitNewFunction(RegisterID* dst, CaptureMode, FunctionBodyNode*); - RegisterID* emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* body); - RegisterID* emitNewFunctionInternal(RegisterID* dst, CaptureMode, unsigned index, bool shouldNullCheck); - RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func); + RegisterID* emitNewArrayWithSpread(RegisterID* dst, ElementNode*); + RegisterID* emitNewArrayWithSize(RegisterID* dst, RegisterID* length); + + RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*); + RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*); + RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource); + RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*); + RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*); RegisterID* emitNewRegExp(RegisterID* dst, RegExp*); - RegisterID* emitMove(RegisterID* dst, CaptureMode, RegisterID* src); + void emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name); + + RegisterID* emitMoveLinkTimeConstant(RegisterID* dst, LinkTimeConstant); + RegisterID* emitMoveEmptyValue(RegisterID* dst); RegisterID* emitMove(RegisterID* dst, RegisterID* src); - RegisterID* emitToNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_number, dst, src); } + RegisterID* emitToNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOpProfiled(op_to_number, dst, src); } + RegisterID* emitToString(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_string, dst, src); } RegisterID* emitInc(RegisterID* srcDst); RegisterID* emitDec(RegisterID* srcDst); - void emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target); + RegisterID* emitOverridesHasInstance(RegisterID* dst, RegisterID* constructor, RegisterID* hasInstanceValue); RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* basePrototype); + RegisterID* emitInstanceOfCustom(RegisterID* dst, RegisterID* value, RegisterID* constructor, RegisterID* hasInstanceValue); RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); } - RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); } - - RegisterID* emitInitGlobalConst(const Identifier&, RegisterID* value); + RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base); + RegisterID* emitTryGetById(RegisterID* dst, RegisterID* base, const Identifier& property); RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property); - RegisterID* emitGetArgumentsLength(RegisterID* dst, RegisterID* base); + RegisterID* emitGetById(RegisterID* dst, RegisterID* base, RegisterID* thisVal, const Identifier& property); RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value); - RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value); + RegisterID* emitPutById(RegisterID* base, RegisterID* thisValue, const Identifier& property, RegisterID* value); + RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType); RegisterID* emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier&); RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property); - RegisterID* emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property); + RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* thisValue, RegisterID* property); RegisterID* emitPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); + RegisterID* emitPutByVal(RegisterID* base, RegisterID* thisValue, RegisterID* property, RegisterID* value); RegisterID* emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property); RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value); - void emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter); - - ExpectedFunction expectedFunctionForIdentifier(const Identifier&); - RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); - RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); - RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); - void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack); - - RegisterID* emitReturn(RegisterID* src); + RegisterID* emitAssert(RegisterID* condition, int line); + + void emitPutGetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* getter); + void emitPutSetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* setter); + void emitPutGetterSetter(RegisterID* base, const Identifier& property, unsigned attributes, RegisterID* getter, RegisterID* setter); + void emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* getter); + void emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* setter); + + RegisterID* emitGetArgument(RegisterID* dst, int32_t index); + + // Initialize object with generator fields (@generatorThis, @generatorNext, @generatorState, @generatorFrame) + void emitPutGeneratorFields(RegisterID* nextFunction); + + ExpectedFunction expectedFunctionForIdentifier(const Identifier&); + RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + RegisterID* emitCallInTailPosition(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + RegisterID* emitCallVarargsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + RegisterID* emitCallForwardArgumentsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + + enum PropertyDescriptorOption { + PropertyConfigurable = 1, + PropertyWritable = 1 << 1, + PropertyEnumerable = 1 << 2, + }; + void emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister, + RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition&); + + void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, ForOfNode* = nullptr, RegisterID* forLoopSymbolTable = nullptr); + + RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*); + RegisterID* emitGetGlobalPrivate(RegisterID* dst, const Identifier& property); + + enum class ReturnFrom { Normal, Finally }; + RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal); RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); } RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); @@ -394,28 +688,80 @@ namespace JSC { void emitToPrimitive(RegisterID* dst, RegisterID* src); ResolveType resolveType(); - RegisterID* emitResolveScope(RegisterID* dst, const Identifier&); - RegisterID* emitGetFromScope(RegisterID* dst, RegisterID* scope, const Identifier&, ResolveMode); - RegisterID* emitPutToScope(RegisterID* scope, const Identifier&, RegisterID* value, ResolveMode); + RegisterID* emitResolveConstantLocal(RegisterID* dst, const Variable&); + RegisterID* emitResolveScope(RegisterID* dst, const Variable&); + RegisterID* emitGetFromScope(RegisterID* dst, RegisterID* scope, const Variable&, ResolveMode); + RegisterID* emitPutToScope(RegisterID* scope, const Variable&, RegisterID* value, ResolveMode, InitializationMode); + RegisterID* initializeVariable(const Variable&, RegisterID* value); - PassRefPtr<Label> emitLabel(Label*); + void emitLabel(Label&); void emitLoopHint(); - PassRefPtr<Label> emitJump(Label* target); - PassRefPtr<Label> emitJumpIfTrue(RegisterID* cond, Label* target); - PassRefPtr<Label> emitJumpIfFalse(RegisterID* cond, Label* target); - PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target); - PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target); - void emitPopScopes(int targetScopeDepth); - - RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget); - RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target); - - void emitReadOnlyExceptionIfNeeded(); + void emitJump(Label& target); + void emitJumpIfTrue(RegisterID* cond, Label& target); + void emitJumpIfFalse(RegisterID* cond, Label& target); + void emitJumpIfNotFunctionCall(RegisterID* cond, Label& target); + void emitJumpIfNotFunctionApply(RegisterID* cond, Label& target); + + void emitEnter(); + void emitWatchdog(); + + RegisterID* emitHasIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); + RegisterID* emitHasStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator); + RegisterID* emitHasGenericProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); + RegisterID* emitGetPropertyEnumerator(RegisterID* dst, RegisterID* base); + RegisterID* emitGetEnumerableLength(RegisterID* dst, RegisterID* base); + RegisterID* emitGetStructurePropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length); + RegisterID* emitGetGenericPropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length, RegisterID* structureEnumerator); + RegisterID* emitEnumeratorStructurePropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index); + RegisterID* emitEnumeratorGenericPropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index); + RegisterID* emitToIndexString(RegisterID* dst, RegisterID* index); + + RegisterID* emitIsCellWithType(RegisterID* dst, RegisterID* src, JSType); + RegisterID* emitIsJSArray(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, ArrayType); } + RegisterID* emitIsProxyObject(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, ProxyObjectType); } + RegisterID* emitIsRegExpObject(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, RegExpObjectType); } + RegisterID* emitIsMap(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSMapType); } + RegisterID* emitIsSet(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSSetType); } + RegisterID* emitIsObject(RegisterID* dst, RegisterID* src); + RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src); + RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src); + RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src); + RegisterID* emitIsDerivedArray(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, DerivedArrayType); } + void emitRequireObjectCoercible(RegisterID* value, const String& error); + + RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node); + RegisterID* emitIteratorNextWithValue(RegisterID* dst, RegisterID* iterator, RegisterID* value, const ThrowableExpressionData* node); + void emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node); + + RegisterID* emitRestParameter(RegisterID* result, unsigned numParametersToSkip); + + bool emitReadOnlyExceptionIfNeeded(const Variable&); // Start a try block. 'start' must have been emitted. - TryData* pushTry(Label* start); + TryData* pushTry(Label& start, Label& handlerLabel, HandlerType); // End a try block. 'end' must have been emitted. - RegisterID* popTryAndEmitCatch(TryData*, RegisterID* targetRegister, Label* end); + void popTry(TryData*, Label& end); + void emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister); + + private: + static const int CurrentLexicalScopeIndex = -2; + static const int OutermostLexicalScopeIndex = -1; + + int currentLexicalScopeIndex() const + { + int size = static_cast<int>(m_lexicalScopeStack.size()); + ASSERT(static_cast<size_t>(size) == m_lexicalScopeStack.size()); + ASSERT(size >= 0); + if (!size) + return OutermostLexicalScopeIndex; + return size - 1; + } + + public: + void restoreScopeRegister(); + void restoreScopeRegister(int lexicalScopeIndex); + + int labelScopeDepthToLexicalScopeIndex(int labelScopeDepth); void emitThrow(RegisterID* exc) { @@ -423,48 +769,157 @@ namespace JSC { emitUnaryNoDstOp(op_throw, exc); } + void emitThrowStaticError(ErrorType, RegisterID*); + void emitThrowStaticError(ErrorType, const Identifier& message); void emitThrowReferenceError(const String& message); + void emitThrowTypeError(const String& message); + void emitThrowTypeError(const Identifier& message); + void emitThrowRangeError(const Identifier& message); + void emitThrowOutOfMemoryError(); + + void emitPushCatchScope(VariableEnvironment&); + void emitPopCatchScope(VariableEnvironment&); + + void emitGetScope(); + RegisterID* emitPushWithScope(RegisterID* objectScope); + void emitPopWithScope(); + void emitPutThisToArrowFunctionContextScope(); + void emitPutNewTargetToArrowFunctionContextScope(); + void emitPutDerivedConstructorToArrowFunctionContextScope(); + RegisterID* emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); + + void emitDebugHook(DebugHookType, const JSTextPosition&); + void emitDebugHook(DebugHookType, unsigned line, unsigned charOffset, unsigned lineStart); + void emitDebugHook(StatementNode*); + void emitDebugHook(ExpressionNode*); + void emitWillLeaveCallFrameDebugHook(); + + class CompletionRecordScope { + public: + CompletionRecordScope(BytecodeGenerator& generator, bool needCompletionRecordRegisters = true) + : m_generator(generator) + { + if (needCompletionRecordRegisters && m_generator.allocateCompletionRecordRegisters()) + m_needToReleaseOnDestruction = true; + } + ~CompletionRecordScope() + { + if (m_needToReleaseOnDestruction) + m_generator.releaseCompletionRecordRegisters(); + } - void emitPushNameScope(const Identifier& property, RegisterID* value, unsigned attributes); - - RegisterID* emitPushWithScope(RegisterID* scope); - void emitPopScope(); - - void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart); - - int scopeDepth() { return m_localScopeDepth + m_finallyDepth; } - bool hasFinaliser() { return m_finallyDepth != 0; } - - void pushFinallyContext(StatementNode* finallyBlock); - void popFinallyContext(); + private: + BytecodeGenerator& m_generator; + bool m_needToReleaseOnDestruction { false }; + }; - void pushOptimisedForIn(RegisterID* expectedSubscript, RegisterID* iter, RegisterID* index, RegisterID* propertyRegister) + RegisterID* completionTypeRegister() const { - ForInContext context = { expectedSubscript, iter, index, propertyRegister }; - m_forInContextStack.append(context); + ASSERT(m_completionTypeRegister); + return m_completionTypeRegister.get(); + } + RegisterID* completionValueRegister() const + { + ASSERT(m_completionValueRegister); + return m_completionValueRegister.get(); } - void popOptimisedForIn() + void emitSetCompletionType(CompletionType type) + { + emitLoad(completionTypeRegister(), JSValue(static_cast<int>(type))); + } + void emitSetCompletionValue(RegisterID* reg) { - m_forInContextStack.removeLast(); + emitMove(completionValueRegister(), reg); } - LabelScope* breakTarget(const Identifier&); - LabelScope* continueTarget(const Identifier&); + void emitJumpIf(OpcodeID compareOpcode, RegisterID* completionTypeRegister, CompletionType, Label& jumpTarget); + + bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label& jumpTarget); + bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister); + void emitFinallyCompletion(FinallyContext&, RegisterID* completionTypeRegister, Label& normalCompletionLabel); + + private: + bool allocateCompletionRecordRegisters(); + void releaseCompletionRecordRegisters(); + + public: + FinallyContext* pushFinallyControlFlowScope(Label& finallyLabel); + FinallyContext popFinallyControlFlowScope(); + + void pushIndexedForInScope(RegisterID* local, RegisterID* index); + void popIndexedForInScope(RegisterID* local); + void pushStructureForInScope(RegisterID* local, RegisterID* index, RegisterID* property, RegisterID* enumerator); + void popStructureForInScope(RegisterID* local); + void invalidateForInContextForLocal(RegisterID* local); + + LabelScopePtr breakTarget(const Identifier&); + LabelScopePtr continueTarget(const Identifier&); void beginSwitch(RegisterID*, SwitchInfo::SwitchType); - void endSwitch(uint32_t clauseCount, RefPtr<Label>*, ExpressionNode**, Label* defaultLabel, int32_t min, int32_t range); + void endSwitch(uint32_t clauseCount, const Vector<Ref<Label>, 8>&, ExpressionNode**, Label& defaultLabel, int32_t min, int32_t range); + + void emitYieldPoint(RegisterID*); + + void emitGeneratorStateLabel(); + void emitGeneratorStateChange(int32_t state); + RegisterID* emitYield(RegisterID* argument); + RegisterID* emitDelegateYield(RegisterID* argument, ThrowableExpressionData*); + RegisterID* generatorStateRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::State)]; } + RegisterID* generatorValueRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::Value)]; } + RegisterID* generatorResumeModeRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::ResumeMode)]; } + RegisterID* generatorFrameRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::Frame)]; } CodeType codeType() const { return m_codeType; } - bool shouldEmitProfileHooks() { return m_shouldEmitProfileHooks; } bool shouldEmitDebugHooks() { return m_shouldEmitDebugHooks; } bool isStrictMode() const { return m_codeBlock->isStrictMode(); } + SourceParseMode parseMode() const { return m_codeBlock->parseMode(); } + + bool isBuiltinFunction() const { return m_isBuiltinFunction; } + + OpcodeID lastOpcodeID() const { return m_lastOpcodeID; } + + bool isDerivedConstructorContext() { return m_derivedContextType == DerivedContextType::DerivedConstructorContext; } + bool isDerivedClassContext() { return m_derivedContextType == DerivedContextType::DerivedMethodContext; } + bool isArrowFunction() { return m_codeBlock->isArrowFunction(); } + + enum class TDZCheckOptimization { Optimize, DoNotOptimize }; + enum class NestedScopeType { IsNested, IsNotNested }; private: - friend class Label; + enum class TDZRequirement { UnderTDZ, NotUnderTDZ }; + enum class ScopeType { CatchScope, LetConstScope, FunctionNameScope }; + enum class ScopeRegisterType { Var, Block }; + void pushLexicalScopeInternal(VariableEnvironment&, TDZCheckOptimization, NestedScopeType, RegisterID** constantSymbolTableResult, TDZRequirement, ScopeType, ScopeRegisterType); + void initializeBlockScopedFunctions(VariableEnvironment&, FunctionStack&, RegisterID* constantSymbolTable); + void popLexicalScopeInternal(VariableEnvironment&); + template<typename LookUpVarKindFunctor> + bool instantiateLexicalVariables(const VariableEnvironment&, SymbolTable*, ScopeRegisterType, LookUpVarKindFunctor); + void emitPrefillStackTDZVariables(const VariableEnvironment&, SymbolTable*); + void emitPopScope(RegisterID* dst, RegisterID* scope); + RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope); + void emitPushFunctionNameScope(const Identifier& property, RegisterID* value, bool isCaptured); + void emitNewFunctionExpressionCommon(RegisterID*, FunctionMetadataNode*); + bool isNewTargetUsedInInnerArrowFunction(); + bool isArgumentsUsedInInnerArrowFunction(); + + public: + bool isSuperUsedInInnerArrowFunction(); + bool isSuperCallUsedInInnerArrowFunction(); + bool isThisUsedInInnerArrowFunction(); + void pushLexicalScope(VariableEnvironmentNode*, TDZCheckOptimization, NestedScopeType = NestedScopeType::IsNotNested, RegisterID** constantSymbolTableResult = nullptr, bool shouldInitializeBlockScopedFunctions = true); + void popLexicalScope(VariableEnvironmentNode*); + void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable); + int labelScopeDepth() const; + + private: + ParserError generate(); + void reclaimFreeRegisters(); + Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&, int symbolTableConstantIndex, bool isLexicallyScoped); + void emitOpcode(OpcodeID); UnlinkedArrayAllocationProfile newArrayAllocationProfile(); UnlinkedObjectAllocationProfile newObjectAllocationProfile(); @@ -482,203 +937,219 @@ namespace JSC { ALWAYS_INLINE void rewindBinaryOp(); ALWAYS_INLINE void rewindUnaryOp(); - void emitComplexPopScopes(ControlFlowContext* topScope, ControlFlowContext* bottomScope); + void allocateCalleeSaveSpace(); + void allocateAndEmitScope(); typedef HashMap<double, JSValue> NumberMap; - typedef HashMap<StringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap; + typedef HashMap<UniquedStringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap; + typedef HashMap<Ref<TemplateRegistryKey>, JSTemplateRegistryKey*> TemplateRegistryKeyMap; // Helper for emitCall() and emitConstruct(). This works because the set of // expected functions have identical behavior for both call and construct // (i.e. "Object()" is identical to "new Object()"). - ExpectedFunction emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, Label* done); + ExpectedFunction emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, Label& done); - RegisterID* emitCall(OpcodeID, RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + RegisterID* emitCall(OpcodeID, RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); RegisterID* newRegister(); - // Adds a var slot and maps it to the name ident in symbolTable(). - enum WatchMode { IsWatchable, NotWatchable }; - RegisterID* addVar(const Identifier& ident, ConstantMode constantMode, WatchMode watchMode) - { - RegisterID* local; - addVar(ident, constantMode, watchMode, local); - return local; - } - - // Ditto. Returns true if a new RegisterID was added, false if a pre-existing RegisterID was re-used. - bool addVar(const Identifier&, ConstantMode, WatchMode, RegisterID*&); - - // Adds an anonymous var slot. To give this slot a name, add it to symbolTable(). + // Adds an anonymous local var slot. To give this slot a name, add it to symbolTable(). RegisterID* addVar() { ++m_codeBlock->m_numVars; - return newRegister(); + RegisterID* result = newRegister(); + ASSERT(VirtualRegister(result->index()).toLocal() == m_codeBlock->m_numVars - 1); + result->ref(); // We should never free this slot. + return result; } - // Returns the index of the added var. - void addParameter(const Identifier&, int parameterIndex); - RegisterID* resolveCallee(FunctionBodyNode*); - void addCallee(FunctionBodyNode*, RegisterID*); - - void preserveLastVar(); - - RegisterID& registerFor(int index) + // Initializes the stack form the parameter; does nothing for the symbol table. + RegisterID* initializeNextParameter(); + UniquedStringImpl* visibleNameForParameter(DestructuringPatternNode*); + + RegisterID& registerFor(VirtualRegister reg) { - if (operandIsLocal(index)) - return m_calleeRegisters[VirtualRegister(index).toLocal()]; + if (reg.isLocal()) + return m_calleeLocals[reg.toLocal()]; - if (index == JSStack::Callee) + if (reg.offset() == CallFrameSlot::callee) return m_calleeRegister; ASSERT(m_parameters.size()); - return m_parameters[VirtualRegister(index).toArgument()]; + return m_parameters[reg.toArgument()]; } + bool hasConstant(const Identifier&) const; unsigned addConstant(const Identifier&); - RegisterID* addConstantValue(JSValue); + RegisterID* addConstantValue(JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other); RegisterID* addConstantEmptyValue(); unsigned addRegExp(RegExp*); unsigned addConstantBuffer(unsigned length); - UnlinkedFunctionExecutable* makeFunction(FunctionBodyNode* body) + UnlinkedFunctionExecutable* makeFunction(FunctionMetadataNode* metadata) { - return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), body); - } - - RegisterID* emitInitLazyRegister(RegisterID*); + DerivedContextType newDerivedContextType = DerivedContextType::None; - public: - JSString* addStringConstant(const Identifier&); - - Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; } + if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(metadata->parseMode())) { + if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) + newDerivedContextType = DerivedContextType::DerivedConstructorContext; + else if (m_codeBlock->isClassContext() || isDerivedClassContext()) + newDerivedContextType = DerivedContextType::DerivedMethodContext; + } - SymbolTable& symbolTable() { return *m_symbolTable; } + VariableEnvironment variablesUnderTDZ; + getVariablesUnderTDZ(variablesUnderTDZ); - bool shouldOptimizeLocals() - { - if (m_codeType != FunctionCode) - return false; + // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized. + // https://bugs.webkit.org/show_bug.cgi?id=151547 + SourceParseMode parseMode = metadata->parseMode(); + ConstructAbility constructAbility = constructAbilityForParseMode(parseMode); + if (parseMode == SourceParseMode::MethodMode && metadata->constructorKind() != ConstructorKind::None) + constructAbility = ConstructAbility::CanConstruct; - if (m_localScopeDepth) - return false; - - return true; + return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), variablesUnderTDZ, newDerivedContextType); } - bool canOptimizeNonLocals() - { - if (m_localScopeDepth) - return false; + void getVariablesUnderTDZ(VariableEnvironment&); - if (m_codeType == EvalCode) - return false; + RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + RegisterID* emitCallVarargs(OpcodeID, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); + + void emitLogShadowChickenPrologueIfNecessary(); + void emitLogShadowChickenTailIfNecessary(); - if (m_codeType == FunctionCode && m_codeBlock->usesEval()) - return false; + void initializeParameters(FunctionParameters&); + void initializeVarLexicalEnvironment(int symbolTableConstantIndex, SymbolTable* functionSymbolTable, bool hasCapturedVariables); + void initializeDefaultParameterValuesAndSetupFunctionScopeStack(FunctionParameters&, bool isSimpleParameterList, FunctionNode*, SymbolTable*, int symbolTableConstantIndex, const std::function<bool (UniquedStringImpl*)>& captures, bool shouldCreateArgumentsVariableInParameterScope); + void initializeArrowFunctionContextScopeIfNeeded(SymbolTable* functionSymbolTable = nullptr, bool canReuseLexicalEnvironment = false); + bool needsDerivedConstructorInArrowFunctionLexicalEnvironment(); - return true; - } + public: + JSString* addStringConstant(const Identifier&); + JSTemplateRegistryKey* addTemplateRegistryKeyConstant(Ref<TemplateRegistryKey>&&); - bool shouldTearOffArgumentsEagerly() - { - return m_codeType == FunctionCode && isStrictMode() && m_scopeNode->modifiesParameter(); - } + Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; } RegisterID* emitThrowExpressionTooDeepException(); - void createArgumentsIfNecessary(); - void createActivationIfNecessary(); - RegisterID* createLazyRegisterIfNecessary(RegisterID*); - - unsigned watchableVariable(int operand) - { - VirtualRegister reg(operand); - if (!reg.isLocal()) - return UINT_MAX; - if (static_cast<size_t>(reg.toLocal()) >= m_watchableVariables.size()) - return UINT_MAX; - Identifier& ident = m_watchableVariables[reg.toLocal()]; - if (ident.isNull()) - return UINT_MAX; - return addConstant(ident); - } - - bool hasWatchableVariable(int operand) - { - return watchableVariable(operand) != UINT_MAX; - } - + private: Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow> m_instructions; bool m_shouldEmitDebugHooks; - bool m_shouldEmitProfileHooks; - - SymbolTable* m_symbolTable; - ScopeNode* m_scopeNode; + struct LexicalScopeStackEntry { + SymbolTable* m_symbolTable; + RegisterID* m_scope; + bool m_isWithScope; + int m_symbolTableConstantIndex; + }; + Vector<LexicalScopeStackEntry> m_lexicalScopeStack; + enum class TDZNecessityLevel { + NotNeeded, + Optimize, + DoNotOptimize + }; + typedef HashMap<RefPtr<UniquedStringImpl>, TDZNecessityLevel, IdentifierRepHash> TDZMap; + Vector<TDZMap> m_TDZStack; + std::optional<size_t> m_varScopeLexicalScopeStackIndex; + void pushTDZVariables(const VariableEnvironment&, TDZCheckOptimization, TDZRequirement); + + ScopeNode* const m_scopeNode; Strong<UnlinkedCodeBlock> m_codeBlock; // Some of these objects keep pointers to one another. They are arranged // to ensure a sane destruction order that avoids references to freed memory. - HashSet<RefPtr<StringImpl>, IdentifierRepHash> m_functions; + HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> m_functions; RegisterID m_ignoredResultRegister; RegisterID m_thisRegister; RegisterID m_calleeRegister; - RegisterID* m_activationRegister; - RegisterID* m_emptyValueRegister; - RegisterID* m_globalObjectRegister; - Vector<Identifier, 16> m_watchableVariables; + RegisterID* m_scopeRegister { nullptr }; + RegisterID* m_topMostScope { nullptr }; + RegisterID* m_argumentsRegister { nullptr }; + RegisterID* m_lexicalEnvironmentRegister { nullptr }; + RegisterID* m_generatorRegister { nullptr }; + RegisterID* m_emptyValueRegister { nullptr }; + RegisterID* m_globalObjectRegister { nullptr }; + RegisterID* m_newTargetRegister { nullptr }; + RegisterID* m_isDerivedConstuctor { nullptr }; + RegisterID* m_linkTimeConstantRegisters[LinkTimeConstantCount]; + RegisterID* m_arrowFunctionContextLexicalEnvironmentRegister { nullptr }; + RegisterID* m_promiseCapabilityRegister { nullptr }; + + RefPtr<RegisterID> m_completionTypeRegister; + RefPtr<RegisterID> m_completionValueRegister; + + FinallyContext* m_currentFinallyContext { nullptr }; + + SegmentedVector<RegisterID*, 16> m_localRegistersForCalleeSaveRegisters; SegmentedVector<RegisterID, 32> m_constantPoolRegisters; - SegmentedVector<RegisterID, 32> m_calleeRegisters; + SegmentedVector<RegisterID, 32> m_calleeLocals; SegmentedVector<RegisterID, 32> m_parameters; SegmentedVector<Label, 32> m_labels; LabelScopeStore m_labelScopes; - RefPtr<RegisterID> m_lastVar; - int m_finallyDepth; - int m_localScopeDepth; - CodeType m_codeType; + unsigned m_finallyDepth { 0 }; + int m_localScopeDepth { 0 }; + const CodeType m_codeType; + + int localScopeDepth() const; + void pushLocalControlFlowScope(); + void popLocalControlFlowScope(); - Vector<ControlFlowContext, 0, UnsafeVectorOverflow> m_scopeContextStack; + // FIXME: Restore overflow checking with UnsafeVectorOverflow once SegmentVector supports it. + // https://bugs.webkit.org/show_bug.cgi?id=165980 + SegmentedVector<ControlFlowScope, 16> m_controlFlowScopeStack; Vector<SwitchInfo> m_switchContextStack; - Vector<ForInContext> m_forInContextStack; + Vector<Ref<ForInContext>> m_forInContextStack; Vector<TryContext> m_tryContextStack; + unsigned m_yieldPoints { 0 }; + + Strong<SymbolTable> m_generatorFrameSymbolTable; + int m_generatorFrameSymbolTableIndex { 0 }; + + enum FunctionVariableType : uint8_t { NormalFunctionVariable, GlobalFunctionVariable }; + Vector<std::pair<FunctionMetadataNode*, FunctionVariableType>> m_functionsToInitialize; + bool m_needToInitializeArguments { false }; + RestParameterNode* m_restParameter { nullptr }; Vector<TryRange> m_tryRanges; SegmentedVector<TryData, 8> m_tryData; - int m_firstConstantIndex; - int m_nextConstantOffset; - unsigned m_globalConstantIndex; - - int m_globalVarStorageOffset; + int m_nextConstantOffset { 0 }; - bool m_hasCreatedActivation; - int m_firstLazyFunction; - int m_lastLazyFunction; - HashMap<unsigned int, FunctionBodyNode*, WTF::IntHash<unsigned int>, WTF::UnsignedWithZeroKeyHashTraits<unsigned int>> m_lazyFunctions; - typedef HashMap<FunctionBodyNode*, unsigned> FunctionOffsetMap; + typedef HashMap<FunctionMetadataNode*, unsigned> FunctionOffsetMap; FunctionOffsetMap m_functionOffsets; // Constant pool IdentifierMap m_identifierMap; + + typedef HashMap<EncodedJSValueWithRepresentation, unsigned, EncodedJSValueWithRepresentationHash, EncodedJSValueWithRepresentationHashTraits> JSValueMap; JSValueMap m_jsValueMap; - NumberMap m_numberMap; IdentifierStringMap m_stringMap; + TemplateRegistryKeyMap m_templateRegistryKeyMap; - StaticPropertyAnalyzer m_staticPropertyAnalyzer; + StaticPropertyAnalyzer m_staticPropertyAnalyzer { &m_instructions }; VM* m_vm; - OpcodeID m_lastOpcodeID; + OpcodeID m_lastOpcodeID = op_end; #ifndef NDEBUG - size_t m_lastOpcodePosition; + size_t m_lastOpcodePosition { 0 }; #endif - bool m_usesExceptions; - bool m_expressionTooDeep; + bool m_usesExceptions { false }; + bool m_expressionTooDeep { false }; + bool m_isBuiltinFunction { false }; + bool m_usesNonStrictEval { false }; + bool m_inTailPosition { false }; + bool m_needsToUpdateArrowFunctionContext; + DerivedContextType m_derivedContextType { DerivedContextType::None }; }; -} +} // namespace JSC + +namespace WTF { + +void printInternal(PrintStream&, JSC::Variable::VariableKind); -#endif // BytecodeGenerator_h +} // namespace WTF |