/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* JS bytecode generation. */ #ifndef frontend_BytecodeEmitter_h #define frontend_BytecodeEmitter_h #include "mozilla/Assertions.h" // MOZ_ASSERT #include "mozilla/Attributes.h" // MOZ_STACK_CLASS, MOZ_MUST_USE, MOZ_ALWAYS_INLINE, MOZ_NEVER_INLINE, MOZ_RAII #include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Some #include "mozilla/Span.h" // mozilla::Span #include "mozilla/Vector.h" // mozilla::Vector #include // std::function #include // ptrdiff_t #include // uint16_t, uint32_t #include "jsapi.h" // CompletionKind #include "frontend/AbstractScopePtr.h" // ScopeIndex #include "frontend/BCEParserHandle.h" // BCEParserHandle #include "frontend/BytecodeControlStructures.h" // NestableControl #include "frontend/BytecodeOffset.h" // BytecodeOffset #include "frontend/BytecodeSection.h" // BytecodeSection, PerScriptData, CGScopeList #include "frontend/DestructuringFlavor.h" // DestructuringFlavor #include "frontend/EitherParser.h" // EitherParser #include "frontend/ErrorReporter.h" // ErrorReporter #include "frontend/FullParseHandler.h" // FullParseHandler #include "frontend/IteratorKind.h" // IteratorKind #include "frontend/JumpList.h" // JumpList, JumpTarget #include "frontend/NameAnalysisTypes.h" // NameLocation #include "frontend/NameCollections.h" // AtomIndexMap #include "frontend/ParseNode.h" // ParseNode and subclasses #include "frontend/Parser.h" // Parser, PropListType #include "frontend/ScriptIndex.h" // ScriptIndex #include "frontend/SharedContext.h" // SharedContext, TopLevelFunction #include "frontend/SourceNotes.h" // SrcNoteType #include "frontend/TokenStream.h" // TokenPos #include "frontend/ValueUsage.h" // ValueUsage #include "js/RootingAPI.h" // JS::Rooted, JS::Handle #include "js/TypeDecls.h" // jsbytecode #include "vm/BuiltinObjectKind.h" // BuiltinObjectKind #include "vm/BytecodeUtil.h" // JSOp #include "vm/CheckIsObjectKind.h" // CheckIsObjectKind #include "vm/FunctionPrefixKind.h" // FunctionPrefixKind #include "vm/GeneratorResumeKind.h" // GeneratorResumeKind #include "vm/Instrumentation.h" // InstrumentationKind #include "vm/JSFunction.h" // JSFunction #include "vm/JSScript.h" // JSScript, BaseScript, MemberInitializers #include "vm/Runtime.h" // ReportOutOfMemory #include "vm/SharedStencil.h" // GCThingIndex #include "vm/StencilEnums.h" // TryNoteKind #include "vm/StringType.h" // JSAtom #include "vm/ThrowMsgKind.h" // ThrowMsgKind, ThrowCondition namespace js { namespace frontend { class CallOrNewEmitter; class ClassEmitter; class ElemOpEmitter; class EmitterScope; class NestableControl; class PropertyEmitter; class PropOpEmitter; class OptionalEmitter; class TDZCheckCache; class TryEmitter; class ScriptStencil; enum class ValueIsOnStack { Yes, No }; struct MOZ_STACK_CLASS BytecodeEmitter { // Context shared between parsing and bytecode generation. SharedContext* const sc = nullptr; JSContext* const cx = nullptr; // Enclosing function or global context. BytecodeEmitter* const parent = nullptr; BytecodeSection bytecodeSection_; public: BytecodeSection& bytecodeSection() { return bytecodeSection_; } const BytecodeSection& bytecodeSection() const { return bytecodeSection_; } private: PerScriptData perScriptData_; public: PerScriptData& perScriptData() { return perScriptData_; } const PerScriptData& perScriptData() const { return perScriptData_; } private: // switchToMain sets this to the bytecode offset of the main section. mozilla::Maybe mainOffset_ = {}; public: // Private storage for parser wrapper. DO NOT REFERENCE INTERNALLY. May not be // initialized. Use |parser| instead. mozilla::Maybe ep_ = {}; BCEParserHandle* parser = nullptr; CompilationStencil& stencil; CompilationState& compilationState; uint32_t maxFixedSlots = 0; /* maximum number of fixed frame slots so far */ // Index into scopeList of the body scope. GCThingIndex bodyScopeIndex = ScopeNote::NoScopeIndex; EmitterScope* varEmitterScope = nullptr; NestableControl* innermostNestableControl = nullptr; EmitterScope* innermostEmitterScope_ = nullptr; TDZCheckCache* innermostTDZCheckCache = nullptr; #ifdef DEBUG bool unstableEmitterScope = false; friend class AutoCheckUnstableEmitterScope; #endif EmitterScope* innermostEmitterScope() const { MOZ_ASSERT(!unstableEmitterScope); return innermostEmitterScopeNoCheck(); } EmitterScope* innermostEmitterScopeNoCheck() const { return innermostEmitterScope_; } // Script contains finally block. bool hasTryFinally = false; enum EmitterMode { Normal, // Emit JSOp::GetIntrinsic instead of JSOp::GetName and assert that // JSOp::GetName and JSOp::*GName don't ever get emitted. See the comment // for the field |selfHostingMode| in Parser.h for details. SelfHosting, // Check the static scope chain of the root function for resolving free // variable accesses in the script. LazyFunction }; const EmitterMode emitterMode = Normal; mozilla::Maybe scriptStartOffset = {}; // The end location of a function body that is being emitted. mozilla::Maybe functionBodyEndPos = {}; // Mask of operation kinds which need instrumentation. This is obtained from // the compile options and copied here for efficiency. uint32_t instrumentationKinds = 0; /* * Note that BytecodeEmitters are magic: they own the arena "top-of-stack" * space above their tempMark points. This means that you cannot alloc from * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter * destruction. */ private: // Internal constructor, for delegation use only. BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc, CompilationStencil& stencil, CompilationState& compilationState, EmitterMode emitterMode); void initFromBodyPosition(TokenPos bodyPosition); /* * Helper for reporting that we have insufficient args. pluralizer * should be "s" if requiredArgs is anything other than "1" and "" * if requiredArgs is "1". */ void reportNeedMoreArgsError(ParseNode* pn, const char* errorName, const char* requiredArgs, const char* pluralizer, const ListNode* argsList); public: BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* handle, SharedContext* sc, CompilationStencil& stencil, CompilationState& compilationState, EmitterMode emitterMode = Normal); BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc, CompilationStencil& stencil, CompilationState& compilationState, EmitterMode emitterMode = Normal); template BytecodeEmitter(BytecodeEmitter* parent, Parser* parser, SharedContext* sc, CompilationStencil& stencil, CompilationState& compilationState, EmitterMode emitterMode = Normal) : BytecodeEmitter(parent, EitherParser(parser), sc, stencil, compilationState, emitterMode) {} MOZ_MUST_USE bool init(); MOZ_MUST_USE bool init(TokenPos bodyPosition); template T* findInnermostNestableControl() const; template bool */> T* findInnermostNestableControl(Predicate predicate) const; NameLocation lookupName(const ParserAtom* name); // To implement Annex B and the formal parameter defaults scope semantics // requires accessing names that would otherwise be shadowed. This method // returns the access location of a name that is known to be bound in a // target scope. mozilla::Maybe locationOfNameBoundInScope( const ParserAtom* name, EmitterScope* target); // Get the location of a name known to be bound in a given scope, // starting at the source scope. template mozilla::Maybe locationOfNameBoundInScopeType( const ParserAtom* name, EmitterScope* source); // Get the location of a name known to be bound in the function scope, // starting at the source scope. mozilla::Maybe locationOfNameBoundInFunctionScope( const ParserAtom* name) { return locationOfNameBoundInScopeType( name, innermostEmitterScope()); } void setVarEmitterScope(EmitterScope* emitterScope) { MOZ_ASSERT(emitterScope); MOZ_ASSERT(!varEmitterScope); varEmitterScope = emitterScope; } AbstractScopePtr outermostScope() const { return perScriptData().gcThingList().firstScope(); } AbstractScopePtr innermostScope() const; ScopeIndex innermostScopeIndex() const; MOZ_ALWAYS_INLINE MOZ_MUST_USE bool makeAtomIndex(const ParserAtom* atom, GCThingIndex* indexp) { MOZ_ASSERT(perScriptData().atomIndices()); AtomIndexMap::AddPtr p = perScriptData().atomIndices()->lookupForAdd(atom); if (p) { *indexp = GCThingIndex(p->value()); return true; } GCThingIndex index; if (!perScriptData().gcThingList().append(atom, &index)) { return false; } // `atomIndices()` uses uint32_t instead of GCThingIndex, because // GCThingIndex isn't trivial type. if (!perScriptData().atomIndices()->add(p, atom, index.index)) { ReportOutOfMemory(cx); return false; } *indexp = index; return true; } bool isInLoop(); MOZ_MUST_USE bool checkSingletonContext(); bool needsImplicitThis(); size_t countThisEnvironmentHops(); MOZ_MUST_USE bool emitThisEnvironmentCallee(); MOZ_MUST_USE bool emitSuperBase(); uint32_t mainOffset() const { return *mainOffset_; } bool inPrologue() const { return mainOffset_.isNothing(); } MOZ_MUST_USE bool switchToMain() { MOZ_ASSERT(inPrologue()); mainOffset_.emplace(bytecodeSection().code().length()); return emitInstrumentation(InstrumentationKind::Main); } void setFunctionBodyEndPos(uint32_t pos) { functionBodyEndPos = mozilla::Some(pos); } void setScriptStartOffsetIfUnset(uint32_t pos) { if (scriptStartOffset.isNothing()) { scriptStartOffset = mozilla::Some(pos); } } void reportError(ParseNode* pn, unsigned errorNumber, ...); void reportError(const mozilla::Maybe& maybeOffset, unsigned errorNumber, ...); // Fill in a ScriptStencil using this BCE data. bool intoScriptStencil(ScriptIndex scriptIndex); // If pn contains a useful expression, return true with *answer set to true. // If pn contains a useless expression, return true with *answer set to // false. Return false on error. // // The caller should initialize *answer to false and invoke this function on // an expression statement or similar subtree to decide whether the tree // could produce code that has any side effects. For an expression // statement, we define useless code as code with no side effects, because // the main effect, the value left on the stack after the code executes, // will be discarded by a pop bytecode. MOZ_MUST_USE bool checkSideEffects(ParseNode* pn, bool* answer); #ifdef DEBUG MOZ_MUST_USE bool checkStrictOrSloppy(JSOp op); #endif // Add TryNote to the tryNoteList array. The start and end offset are // relative to current section. MOZ_MUST_USE bool addTryNote(TryNoteKind kind, uint32_t stackDepth, BytecodeOffset start, BytecodeOffset end); // Indicates the emitter should not generate location or debugger source // notes. This lets us avoid generating notes for non-user code. bool skipLocationSrcNotes() const { return inPrologue() || (emitterMode == EmitterMode::SelfHosting); } bool skipBreakpointSrcNotes() const { return inPrologue() || (emitterMode == EmitterMode::SelfHosting); } // Append a new source note of the given type (and therefore size) to the // notes dynamic array, updating noteCount. Return the new note's index // within the array pointed at by current->notes as outparam. MOZ_MUST_USE bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr); MOZ_MUST_USE bool newSrcNote2(SrcNoteType type, ptrdiff_t operand, unsigned* indexp = nullptr); MOZ_MUST_USE bool newSrcNoteOperand(ptrdiff_t operand); // Control whether emitTree emits a line number note. enum EmitLineNumberNote { EMIT_LINENOTE, SUPPRESS_LINENOTE }; // Emit code for the tree rooted at pn. MOZ_MUST_USE bool emitTree(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue, EmitLineNumberNote emitLineNote = EMIT_LINENOTE); MOZ_MUST_USE bool emitOptionalTree( ParseNode* pn, OptionalEmitter& oe, ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool emitDeclarationInstantiation(ParseNode* body); // Emit global, eval, or module code for tree rooted at body. Always // encompasses the entire source. MOZ_MUST_USE bool emitScript(ParseNode* body); // Calculate the `nslots` value for BCEScriptStencil constructor parameter. // Fails if it overflows. MOZ_MUST_USE bool getNslots(uint32_t* nslots); // Emit function code for the tree rooted at body. MOZ_MUST_USE bool emitFunctionScript(FunctionNode* funNode); MOZ_MUST_USE bool markStepBreakpoint(); MOZ_MUST_USE bool markSimpleBreakpoint(); MOZ_MUST_USE bool updateLineNumberNotes(uint32_t offset); MOZ_MUST_USE bool updateSourceCoordNotes(uint32_t offset); JSOp strictifySetNameOp(JSOp op); MOZ_MUST_USE bool emitCheck(JSOp op, ptrdiff_t delta, BytecodeOffset* offset); // Emit one bytecode. MOZ_MUST_USE bool emit1(JSOp op); // Emit two bytecodes, an opcode (op) with a byte of immediate operand // (op1). MOZ_MUST_USE bool emit2(JSOp op, uint8_t op1); // Emit three bytecodes, an opcode with two bytes of immediate operands. MOZ_MUST_USE bool emit3(JSOp op, jsbytecode op1, jsbytecode op2); // Helper to duplicate one or more stack values. |slotFromTop| is the value's // depth on the JS stack, as measured from the top. |count| is the number of // values to duplicate, in theiro original order. MOZ_MUST_USE bool emitDupAt(unsigned slotFromTop, unsigned count = 1); // Helper to emit JSOp::Pop or JSOp::PopN. MOZ_MUST_USE bool emitPopN(unsigned n); // Helper to emit JSOp::Swap or JSOp::Pick. MOZ_MUST_USE bool emitPickN(uint8_t n); // Helper to emit JSOp::Swap or JSOp::Unpick. MOZ_MUST_USE bool emitUnpickN(uint8_t n); // Helper to emit JSOp::CheckIsObj. MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind); // Helper to emit JSOp::BuiltinObject. MOZ_MUST_USE bool emitBuiltinObject(BuiltinObjectKind kind); // Push whether the value atop of the stack is non-undefined and non-null. MOZ_MUST_USE bool emitPushNotUndefinedOrNull(); // Emit a bytecode followed by an uint16 immediate operand stored in // big-endian order. MOZ_MUST_USE bool emitUint16Operand(JSOp op, uint32_t operand); // Emit a bytecode followed by an uint32 immediate operand. MOZ_MUST_USE bool emitUint32Operand(JSOp op, uint32_t operand); // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. MOZ_MUST_USE bool emitN(JSOp op, size_t extra, BytecodeOffset* offset = nullptr); MOZ_MUST_USE bool emitDouble(double dval); MOZ_MUST_USE bool emitNumberOp(double dval); MOZ_MUST_USE bool emitBigIntOp(BigIntLiteral* bigint); MOZ_MUST_USE bool emitThisLiteral(ThisLiteral* pn); MOZ_MUST_USE bool emitGetFunctionThis(NameNode* thisName); MOZ_MUST_USE bool emitGetFunctionThis(const mozilla::Maybe& offset); MOZ_MUST_USE bool emitGetThisForSuperBase(UnaryNode* superBase); MOZ_MUST_USE bool emitSetThis(BinaryNode* setThisNode); MOZ_MUST_USE bool emitCheckDerivedClassConstructorReturn(); // Handle jump opcodes and jump targets. MOZ_MUST_USE bool emitJumpTargetOp(JSOp op, BytecodeOffset* off); MOZ_MUST_USE bool emitJumpTarget(JumpTarget* target); MOZ_MUST_USE bool emitJumpNoFallthrough(JSOp op, JumpList* jump); MOZ_MUST_USE bool emitJump(JSOp op, JumpList* jump); void patchJumpsToTarget(JumpList jump, JumpTarget target); MOZ_MUST_USE bool emitJumpTargetAndPatch(JumpList jump); MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc, const mozilla::Maybe& sourceCoordOffset); MOZ_MUST_USE bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr); MOZ_MUST_USE bool emitCallIncDec(UnaryNode* incDec); mozilla::Maybe getOffsetForLoop(ParseNode* nextpn); enum class GotoKind { Break, Continue }; MOZ_MUST_USE bool emitGoto(NestableControl* target, JumpList* jumplist, GotoKind kind); MOZ_MUST_USE bool emitGCIndexOp(JSOp op, GCThingIndex index); MOZ_MUST_USE bool emitAtomOp( JSOp op, const ParserAtom* atom, ShouldInstrument shouldInstrument = ShouldInstrument::No); MOZ_MUST_USE bool emitAtomOp( JSOp op, GCThingIndex atomIndex, ShouldInstrument shouldInstrument = ShouldInstrument::No); MOZ_MUST_USE bool emitArrayLiteral(ListNode* array); MOZ_MUST_USE bool emitArray(ParseNode* arrayHead, uint32_t count); MOZ_MUST_USE bool emitInternedScopeOp(GCThingIndex index, JSOp op); MOZ_MUST_USE bool emitInternedObjectOp(GCThingIndex index, JSOp op); MOZ_MUST_USE bool emitObjectPairOp(GCThingIndex index1, GCThingIndex index2, JSOp op); MOZ_MUST_USE bool emitRegExp(GCThingIndex index); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction( FunctionNode* funNode, bool needsProto = false, ListNode* classContentsIfConstructor = nullptr); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ListNode* objNode); MOZ_MUST_USE bool emitHoistedFunctionsInList(ListNode* stmtList); // Can we use the object-literal writer either in singleton-object mode (with // values) or in template mode (field names only, no values) for the property // list? void isPropertyListObjLiteralCompatible(ListNode* obj, bool* withValues, bool* withoutValues); bool isArrayObjLiteralCompatible(ParseNode* arrayHead); MOZ_MUST_USE bool emitPropertyList(ListNode* obj, PropertyEmitter& pe, PropListType type); MOZ_MUST_USE bool emitPropertyListObjLiteral(ListNode* obj, ObjLiteralFlags flags, bool useObjLiteralValues); MOZ_MUST_USE bool emitDestructuringRestExclusionSetObjLiteral( ListNode* pattern); MOZ_MUST_USE bool emitObjLiteralArray(ParseNode* arrayHead); // Is a field value OBJLITERAL-compatible? MOZ_MUST_USE bool isRHSObjLiteralCompatible(ParseNode* value); MOZ_MUST_USE bool emitObjLiteralValue(ObjLiteralWriter& writer, ParseNode* value); enum class FieldPlacement { Instance, Static }; mozilla::Maybe setupMemberInitializers( ListNode* classMembers, FieldPlacement placement); MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj, FieldPlacement placement); MOZ_MUST_USE bool emitCreateMemberInitializers(ClassEmitter& ce, ListNode* obj, FieldPlacement placement); const MemberInitializers& findMemberInitializersForCall(); MOZ_MUST_USE bool emitInitializeInstanceMembers(); MOZ_MUST_USE bool emitInitializeStaticFields(ListNode* classMembers); MOZ_MUST_USE bool emitPrivateMethodInitializers(ClassEmitter& ce, ListNode* obj); MOZ_MUST_USE bool emitPrivateMethodInitializer( ClassEmitter& ce, ParseNode* prop, ParseNode* propName, const ParserAtom* storedMethodAtom, AccessorType accessorType); // To catch accidental misuse, emitUint16Operand/emit3 assert that they are // not used to unconditionally emit JSOp::GetLocal. Variable access should // instead be emitted using EmitVarOp. In special cases, when the caller // definitely knows that a given local slot is unaliased, this function may be // used as a non-asserting version of emitUint16Operand. MOZ_MUST_USE bool emitLocalOp(JSOp op, uint32_t slot); MOZ_MUST_USE bool emitArgOp(JSOp op, uint16_t slot); MOZ_MUST_USE bool emitEnvCoordOp(JSOp op, EnvironmentCoordinate ec); MOZ_MUST_USE bool emitGetNameAtLocation(const ParserAtom* name, const NameLocation& loc); MOZ_MUST_USE bool emitGetName(const ParserAtom* name) { return emitGetNameAtLocation(name, lookupName(name)); } MOZ_MUST_USE bool emitGetName(NameNode* name); MOZ_MUST_USE bool emitGetPrivateName(NameNode* name); MOZ_MUST_USE bool emitGetPrivateName(const ParserAtom* name); MOZ_MUST_USE bool emitTDZCheckIfNeeded(const ParserAtom* name, const NameLocation& loc, ValueIsOnStack isOnStack); MOZ_MUST_USE bool emitNameIncDec(UnaryNode* incDec); MOZ_MUST_USE bool emitDeclarationList(ListNode* declList); MOZ_MUST_USE bool emitSingleDeclaration(ListNode* declList, NameNode* decl, ParseNode* initializer); MOZ_MUST_USE bool emitAssignmentRhs(ParseNode* rhs, const ParserAtom* anonFunctionName); MOZ_MUST_USE bool emitAssignmentRhs(uint8_t offset); MOZ_MUST_USE bool emitPrepareIteratorResult(); MOZ_MUST_USE bool emitFinishIteratorResult(bool done); MOZ_MUST_USE bool iteratorResultShape(GCThingIndex* outShape); // Convert and add `writer` data to stencil. // Iff it suceeds, `outIndex` out parameter is initialized to the index of the // object in GC things vector. MOZ_MUST_USE bool addObjLiteralData(ObjLiteralWriter& writer, GCThingIndex* outIndex); MOZ_MUST_USE bool emitGetDotGeneratorInInnermostScope() { return emitGetDotGeneratorInScope(*innermostEmitterScope()); } MOZ_MUST_USE bool emitGetDotGeneratorInScope(EmitterScope& currentScope); MOZ_MUST_USE bool allocateResumeIndex(BytecodeOffset offset, uint32_t* resumeIndex); MOZ_MUST_USE bool allocateResumeIndexRange( mozilla::Span offsets, uint32_t* firstResumeIndex); MOZ_MUST_USE bool emitInitialYield(UnaryNode* yieldNode); MOZ_MUST_USE bool emitYield(UnaryNode* yieldNode); MOZ_MUST_USE bool emitYieldOp(JSOp op); MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); MOZ_MUST_USE bool emitAwaitInInnermostScope() { return emitAwaitInScope(*innermostEmitterScope()); } MOZ_MUST_USE bool emitAwaitInInnermostScope(UnaryNode* awaitNode); MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope); MOZ_MUST_USE bool emitPushResumeKind(GeneratorResumeKind kind); MOZ_MUST_USE bool emitPropLHS(PropertyAccess* prop); MOZ_MUST_USE bool emitPropIncDec(UnaryNode* incDec); MOZ_MUST_USE bool emitComputedPropertyName(UnaryNode* computedPropName); // Emit bytecode to put operands for a JSOp::GetElem/CallElem/SetElem/DelElem // opcode onto the stack in the right order. In the case of SetElem, the // value to be assigned must already be pushed. enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref }; MOZ_MUST_USE bool emitElemOperands(PropertyByValue* elem, EmitElemOption opts); MOZ_MUST_USE bool emitElemObjAndKey(PropertyByValue* elem, bool isSuper, ElemOpEmitter& eoe); MOZ_MUST_USE bool emitElemOpBase( JSOp op, ShouldInstrument shouldInstrument = ShouldInstrument::No); MOZ_MUST_USE bool emitElemOp(PropertyByValue* elem, JSOp op); MOZ_MUST_USE bool emitElemIncDec(UnaryNode* incDec); MOZ_MUST_USE bool emitCatch(BinaryNode* catchClause); MOZ_MUST_USE bool emitIf(TernaryNode* ifNode); MOZ_MUST_USE bool emitWith(BinaryNode* withNode); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement( const LabeledStatement* labeledStmt); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope( LexicalScopeNode* lexicalScope); MOZ_MUST_USE bool emitLexicalScopeBody( ParseNode* body, EmitLineNumberNote emitLineNote = EMIT_LINENOTE); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* switchStmt); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(TryNode* tryNode); MOZ_MUST_USE bool emitGoSub(JumpList* jump); // emitDestructuringLHSRef emits the lhs expression's reference. // If the lhs expression is object property |OBJ.prop|, it emits |OBJ|. // If it's object element |OBJ[ELEM]|, it emits |OBJ| and |ELEM|. // If there's nothing to evaluate for the reference, it emits nothing. // |emitted| parameter receives the number of values pushed onto the stack. MOZ_MUST_USE bool emitDestructuringLHSRef(ParseNode* target, size_t* emitted); // emitSetOrInitializeDestructuring assumes the lhs expression's reference // and the to-be-destructured value has been pushed on the stack. It emits // code to destructure a single lhs expression (either a name or a compound // []/{} expression). MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav); // emitDestructuringObjRestExclusionSet emits the property exclusion set // for the rest-property in an object pattern. MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ListNode* pattern); // emitDestructuringOps assumes the to-be-destructured value has been // pushed on the stack and emits code to destructure each part of a [] or // {} lhs expression. MOZ_MUST_USE bool emitDestructuringOps(ListNode* pattern, DestructuringFlavor flav); MOZ_MUST_USE bool emitDestructuringOpsArray(ListNode* pattern, DestructuringFlavor flav); MOZ_MUST_USE bool emitDestructuringOpsObject(ListNode* pattern, DestructuringFlavor flav); enum class CopyOption { Filtered, Unfiltered }; // Calls either the |CopyDataProperties| or the // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or // two in the latter case) elements from the stack. MOZ_MUST_USE bool emitCopyDataProperties(CopyOption option); // emitIterator expects the iterable to already be on the stack. // It will replace that stack value with the corresponding iterator MOZ_MUST_USE bool emitIterator(); MOZ_MUST_USE bool emitAsyncIterator(); // Pops iterator from the top of the stack. Pushes the result of |.next()| // onto the stack. MOZ_MUST_USE bool emitIteratorNext( const mozilla::Maybe& callSourceCoordOffset, IteratorKind kind = IteratorKind::Sync, bool allowSelfHosted = false); MOZ_MUST_USE bool emitIteratorCloseInScope( EmitterScope& currentScope, IteratorKind iterKind = IteratorKind::Sync, CompletionKind completionKind = CompletionKind::Normal, bool allowSelfHosted = false); MOZ_MUST_USE bool emitIteratorCloseInInnermostScope( IteratorKind iterKind = IteratorKind::Sync, CompletionKind completionKind = CompletionKind::Normal, bool allowSelfHosted = false) { return emitIteratorCloseInScope(*innermostEmitterScope(), iterKind, completionKind, allowSelfHosted); } template MOZ_MUST_USE bool wrapWithDestructuringTryNote(int32_t iterDepth, InnerEmitter emitter); MOZ_MUST_USE bool defineHoistedTopLevelFunctions(ParseNode* body); // Check if the value on top of the stack is "undefined". If so, replace // that value on the stack with the value defined by |defaultExpr|. // |pattern| is a lhs node of the default expression. If it's an // identifier and |defaultExpr| is an anonymous function, |SetFunctionName| // is called at compile time. MOZ_MUST_USE bool emitDefault(ParseNode* defaultExpr, ParseNode* pattern); MOZ_MUST_USE bool emitAnonymousFunctionWithName(ParseNode* node, const ParserAtom* name); MOZ_MUST_USE bool emitAnonymousFunctionWithComputedName( ParseNode* node, FunctionPrefixKind prefixKind); MOZ_MUST_USE bool setFunName(FunctionBox* fun, const ParserAtom* name); MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern); MOZ_MUST_USE bool emitCallSiteObjectArray(ListNode* cookedOrRaw, GCThingIndex* outArrayIndex); MOZ_MUST_USE bool emitCallSiteObject(CallSiteNode* callSiteObj); MOZ_MUST_USE bool emitTemplateString(ListNode* templateString); MOZ_MUST_USE bool emitAssignmentOrInit(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs); MOZ_MUST_USE bool emitShortCircuitAssignment(AssignmentNode* node); MOZ_MUST_USE bool emitReturn(UnaryNode* returnNode); MOZ_MUST_USE bool emitExpressionStatement(UnaryNode* exprStmt); MOZ_MUST_USE bool emitStatementList(ListNode* stmtList); MOZ_MUST_USE bool emitDeleteName(UnaryNode* deleteNode); MOZ_MUST_USE bool emitDeleteProperty(UnaryNode* deleteNode); MOZ_MUST_USE bool emitDeleteElement(UnaryNode* deleteNode); MOZ_MUST_USE bool emitDeleteExpression(UnaryNode* deleteNode); // Optional methods which emit Optional Jump Target MOZ_MUST_USE bool emitOptionalChain(UnaryNode* expr, ValueUsage valueUsage); MOZ_MUST_USE bool emitCalleeAndThisForOptionalChain(UnaryNode* expr, CallNode* callNode, CallOrNewEmitter& cone); MOZ_MUST_USE bool emitDeleteOptionalChain(UnaryNode* deleteNode); // Optional methods which emit a shortCircuit jump. They need to be called by // a method which emits an Optional Jump Target, see below. MOZ_MUST_USE bool emitOptionalDotExpression(PropertyAccessBase* expr, PropOpEmitter& poe, bool isSuper, OptionalEmitter& oe); MOZ_MUST_USE bool emitOptionalElemExpression(PropertyByValueBase* elem, ElemOpEmitter& poe, bool isSuper, OptionalEmitter& oe); MOZ_MUST_USE bool emitOptionalCall(CallNode* callNode, OptionalEmitter& oe, ValueUsage valueUsage); MOZ_MUST_USE bool emitDeletePropertyInOptChain(PropertyAccessBase* propExpr, OptionalEmitter& oe); MOZ_MUST_USE bool emitDeleteElementInOptChain(PropertyByValueBase* elemExpr, OptionalEmitter& oe); // |op| must be JSOp::Typeof or JSOp::TypeofExpr. MOZ_MUST_USE bool emitTypeof(UnaryNode* typeofNode, JSOp op); MOZ_MUST_USE bool emitUnary(UnaryNode* unaryNode); MOZ_MUST_USE bool emitRightAssociative(ListNode* node); MOZ_MUST_USE bool emitLeftAssociative(ListNode* node); MOZ_MUST_USE bool emitShortCircuit(ListNode* node); MOZ_MUST_USE bool emitSequenceExpr( ListNode* node, ValueUsage valueUsage = ValueUsage::WantValue); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(UnaryNode* incDec); MOZ_MUST_USE bool emitConditionalExpression( ConditionalExpression& conditional, ValueUsage valueUsage = ValueUsage::WantValue); bool isRestParameter(ParseNode* expr); MOZ_MUST_USE ParseNode* getCoordNode(ParseNode* callNode, ParseNode* calleeNode, JSOp op, ListNode* argsList); MOZ_MUST_USE bool emitArguments(ListNode* argsList, bool isCall, bool isSpread, CallOrNewEmitter& cone); MOZ_MUST_USE bool emitCallOrNew( CallNode* callNode, ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool emitSelfHostedCallFunction(CallNode* callNode); MOZ_MUST_USE bool emitSelfHostedResumeGenerator(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedForceInterpreter(); MOZ_MUST_USE bool emitSelfHostedAllowContentIter(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedGetPropertySuper(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedHasOwn(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedToNumeric(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedToString(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedGetBuiltinConstructor(BinaryNode* callNode); MOZ_MUST_USE bool emitSelfHostedGetBuiltinPrototype(BinaryNode* callNode); #ifdef DEBUG MOZ_MUST_USE bool checkSelfHostedUnsafeGetReservedSlot(BinaryNode* callNode); MOZ_MUST_USE bool checkSelfHostedUnsafeSetReservedSlot(BinaryNode* callNode); #endif MOZ_MUST_USE bool emitDo(BinaryNode* doNode); MOZ_MUST_USE bool emitWhile(BinaryNode* whileNode); MOZ_MUST_USE bool emitFor( ForNode* forNode, const EmitterScope* headLexicalEmitterScope = nullptr); MOZ_MUST_USE bool emitCStyleFor(ForNode* forNode, const EmitterScope* headLexicalEmitterScope); MOZ_MUST_USE bool emitForIn(ForNode* forNode, const EmitterScope* headLexicalEmitterScope); MOZ_MUST_USE bool emitForOf(ForNode* forNode, const EmitterScope* headLexicalEmitterScope); MOZ_MUST_USE bool emitInitializeForInOrOfTarget(TernaryNode* forHead); MOZ_MUST_USE bool emitBreak(const ParserName* label); MOZ_MUST_USE bool emitContinue(const ParserName* label); MOZ_MUST_USE bool emitFunctionFormalParameters(ListNode* paramsBody); MOZ_MUST_USE bool emitInitializeFunctionSpecialNames(); MOZ_MUST_USE bool emitLexicalInitialization(NameNode* name); MOZ_MUST_USE bool emitLexicalInitialization(const ParserAtom* name); // Emit bytecode for the spread operator. // // emitSpread expects the current index (I) of the array, the array itself // and the iterator to be on the stack in that order (iterator on the bottom). // It will pop the iterator and I, then iterate over the iterator by calling // |.next()| and put the results into the I-th element of array with // incrementing I, then push the result I (it will be original I + // iteration count). The stack after iteration will look like |ARRAY INDEX|. MOZ_MUST_USE bool emitSpread(bool allowSelfHosted = false); enum class ClassNameKind { // The class name is defined through its BindingIdentifier, if present. BindingName, // The class is anonymous and has a statically inferred name. InferredName, // The class is anonymous and has a dynamically computed name. ComputedName }; MOZ_MUST_USE bool emitClass( ClassNode* classNode, ClassNameKind nameKind = ClassNameKind::BindingName, const ParserAtom* nameForAnonymousClass = nullptr); MOZ_MUST_USE bool emitSuperElemOperands( PropertyByValue* elem, EmitElemOption opts = EmitElemOption::Get); MOZ_MUST_USE bool emitSuperGetElem(PropertyByValue* elem, bool isCall = false); MOZ_MUST_USE bool emitCalleeAndThis(ParseNode* callee, ParseNode* call, CallOrNewEmitter& cone); MOZ_MUST_USE bool emitOptionalCalleeAndThis(ParseNode* callee, CallNode* call, CallOrNewEmitter& cone, OptionalEmitter& oe); MOZ_MUST_USE bool emitPipeline(ListNode* node); MOZ_MUST_USE bool emitExportDefault(BinaryNode* exportNode); MOZ_MUST_USE bool emitReturnRval() { return emitInstrumentation(InstrumentationKind::Exit) && emit1(JSOp::RetRval); } MOZ_MUST_USE bool emitCheckPrivateField(ThrowCondition throwCondition, ThrowMsgKind msgKind) { return emit3(JSOp::CheckPrivateField, uint8_t(throwCondition), uint8_t(msgKind)); } template MOZ_MUST_USE bool emitNewPrivateNames(ListNode* classMembers); MOZ_MUST_USE bool emitInstrumentation(InstrumentationKind kind, uint32_t npopped = 0) { return MOZ_LIKELY(!instrumentationKinds) || emitInstrumentationSlow(kind, std::function()); } MOZ_MUST_USE bool emitInstrumentationForOpcode(JSOp op, GCThingIndex atomIndex) { return MOZ_LIKELY(!instrumentationKinds) || emitInstrumentationForOpcodeSlow(op, atomIndex); } MOZ_MUST_USE js::UniquePtr createImmutableScriptData( JSContext* cx); private: MOZ_MUST_USE bool emitInstrumentationSlow( InstrumentationKind kind, const std::function& pushOperandsCallback); MOZ_MUST_USE bool emitInstrumentationForOpcodeSlow(JSOp op, GCThingIndex atomIndex); MOZ_MUST_USE bool allowSelfHostedIter(ParseNode* parseNode); MOZ_MUST_USE bool emitSelfHostedGetBuiltinConstructorOrPrototype( BinaryNode* callNode, bool isConstructor); }; class MOZ_RAII AutoCheckUnstableEmitterScope { #ifdef DEBUG bool prev_; BytecodeEmitter* bce_; #endif public: AutoCheckUnstableEmitterScope() = delete; explicit AutoCheckUnstableEmitterScope(BytecodeEmitter* bce) #ifdef DEBUG : bce_(bce) #endif { #ifdef DEBUG prev_ = bce_->unstableEmitterScope; bce_->unstableEmitterScope = true; #endif } ~AutoCheckUnstableEmitterScope() { #ifdef DEBUG bce_->unstableEmitterScope = prev_; #endif } }; } /* namespace frontend */ } /* namespace js */ #endif /* frontend_BytecodeEmitter_h */