mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-20 22:27:31 +09:00
parent
62a9d049ec
commit
3bd6ad26e7
1 changed files with 341 additions and 169 deletions
|
|
@ -1563,6 +1563,318 @@ BytecodeEmitter::TDZCheckCache::noteTDZCheck(BytecodeEmitter* bce, JSAtom* name,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MOZ_STACK_CLASS TryEmitter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Kind {
|
||||||
|
TryCatch,
|
||||||
|
TryCatchFinally,
|
||||||
|
TryFinally
|
||||||
|
};
|
||||||
|
enum ShouldUseRetVal {
|
||||||
|
UseRetVal,
|
||||||
|
DontUseRetVal
|
||||||
|
};
|
||||||
|
enum ShouldUseControl {
|
||||||
|
UseControl,
|
||||||
|
DontUseControl,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
BytecodeEmitter* bce_;
|
||||||
|
Kind kind_;
|
||||||
|
ShouldUseRetVal retValKind_;
|
||||||
|
|
||||||
|
// Track jumps-over-catches and gosubs-to-finally for later fixup.
|
||||||
|
//
|
||||||
|
// When a finally block is active, non-local jumps (including
|
||||||
|
// jumps-over-catches) result in a GOSUB being written into the bytecode
|
||||||
|
// stream and fixed-up later.
|
||||||
|
//
|
||||||
|
// If ShouldUseControl is DontUseControl, all that handling is skipped.
|
||||||
|
// DontUseControl is used by yield*, that matches to the following:
|
||||||
|
// * has only one catch block
|
||||||
|
// * has no catch guard
|
||||||
|
// * has JSOP_GOTO at the end of catch-block
|
||||||
|
// * has no non-local-jump
|
||||||
|
// * doesn't use finally block for normal completion of try-block and
|
||||||
|
// catch-block
|
||||||
|
Maybe<TryFinallyControl> controlInfo_;
|
||||||
|
|
||||||
|
int depth_;
|
||||||
|
unsigned noteIndex_;
|
||||||
|
ptrdiff_t tryStart_;
|
||||||
|
JumpList catchAndFinallyJump_;
|
||||||
|
JumpTarget tryEnd_;
|
||||||
|
JumpTarget finallyStart_;
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
Start,
|
||||||
|
Try,
|
||||||
|
TryEnd,
|
||||||
|
Catch,
|
||||||
|
CatchEnd,
|
||||||
|
Finally,
|
||||||
|
FinallyEnd,
|
||||||
|
End
|
||||||
|
};
|
||||||
|
State state_;
|
||||||
|
|
||||||
|
bool hasCatch() const {
|
||||||
|
return kind_ == TryCatch || kind_ == TryCatchFinally;
|
||||||
|
}
|
||||||
|
bool hasFinally() const {
|
||||||
|
return kind_ == TryCatchFinally || kind_ == TryFinally;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TryEmitter(BytecodeEmitter* bce, Kind kind, ShouldUseRetVal retValKind = UseRetVal,
|
||||||
|
ShouldUseControl controlKind = UseControl)
|
||||||
|
: bce_(bce),
|
||||||
|
kind_(kind),
|
||||||
|
retValKind_(retValKind),
|
||||||
|
depth_(0),
|
||||||
|
noteIndex_(0),
|
||||||
|
tryStart_(0),
|
||||||
|
state_(Start)
|
||||||
|
{
|
||||||
|
if (controlKind == UseControl)
|
||||||
|
controlInfo_.emplace(bce_, hasFinally() ? StatementKind::Finally : StatementKind::Try);
|
||||||
|
finallyStart_.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool emitJumpOverCatchAndFinally() {
|
||||||
|
if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool emitTry() {
|
||||||
|
MOZ_ASSERT(state_ == Start);
|
||||||
|
|
||||||
|
// Since an exception can be thrown at any place inside the try block,
|
||||||
|
// we need to restore the stack and the scope chain before we transfer
|
||||||
|
// the control to the exception handler.
|
||||||
|
//
|
||||||
|
// For that we store in a try note associated with the catch or
|
||||||
|
// finally block the stack depth upon the try entry. The interpreter
|
||||||
|
// uses this depth to properly unwind the stack and the scope chain.
|
||||||
|
depth_ = bce_->stackDepth;
|
||||||
|
|
||||||
|
// Record the try location, then emit the try block.
|
||||||
|
if (!bce_->newSrcNote(SRC_TRY, ¬eIndex_))
|
||||||
|
return false;
|
||||||
|
if (!bce_->emit1(JSOP_TRY))
|
||||||
|
return false;
|
||||||
|
tryStart_ = bce_->offset();
|
||||||
|
|
||||||
|
state_ = Try;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool emitTryEnd() {
|
||||||
|
MOZ_ASSERT(state_ == Try);
|
||||||
|
MOZ_ASSERT(depth_ == bce_->stackDepth);
|
||||||
|
|
||||||
|
// GOSUB to finally, if present.
|
||||||
|
if (hasFinally() && controlInfo_) {
|
||||||
|
if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source note points to the jump at the end of the try block.
|
||||||
|
if (!bce_->setSrcNoteOffset(noteIndex_, 0, bce_->offset() - tryStart_ + JSOP_TRY_LENGTH))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Emit jump over catch and/or finally.
|
||||||
|
if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!bce_->emitJumpTarget(&tryEnd_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool emitCatch() {
|
||||||
|
if (state_ == Try) {
|
||||||
|
if (!emitTryEnd())
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(state_ == Catch);
|
||||||
|
if (!emitCatchEnd(true))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(bce_->stackDepth == depth_);
|
||||||
|
|
||||||
|
if (retValKind_ == UseRetVal) {
|
||||||
|
// Clear the frame's return value that might have been set by the
|
||||||
|
// try block:
|
||||||
|
//
|
||||||
|
// eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
|
||||||
|
if (!bce_->emit1(JSOP_UNDEFINED))
|
||||||
|
return false;
|
||||||
|
if (!bce_->emit1(JSOP_SETRVAL))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ = Catch;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool emitCatchEnd(bool hasNext) {
|
||||||
|
MOZ_ASSERT(state_ == Catch);
|
||||||
|
|
||||||
|
if (!controlInfo_)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// gosub <finally>, if required.
|
||||||
|
if (hasFinally()) {
|
||||||
|
if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
|
||||||
|
return false;
|
||||||
|
MOZ_ASSERT(bce_->stackDepth == depth_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump over the remaining catch blocks. This will get fixed
|
||||||
|
// up to jump to after catch/finally.
|
||||||
|
if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If this catch block had a guard clause, patch the guard jump to
|
||||||
|
// come here.
|
||||||
|
if (controlInfo_->guardJump.offset != -1) {
|
||||||
|
if (!bce_->emitJumpTargetAndPatch(controlInfo_->guardJump))
|
||||||
|
return false;
|
||||||
|
controlInfo_->guardJump.offset = -1;
|
||||||
|
|
||||||
|
// If this catch block is the last one, rethrow, delegating
|
||||||
|
// execution of any finally block to the exception handler.
|
||||||
|
if (!hasNext) {
|
||||||
|
if (!bce_->emit1(JSOP_EXCEPTION))
|
||||||
|
return false;
|
||||||
|
if (!bce_->emit1(JSOP_THROW))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool emitFinally(Maybe<uint32_t> finallyPos = Nothing()) {
|
||||||
|
MOZ_ASSERT(hasFinally());
|
||||||
|
|
||||||
|
if (state_ == Try) {
|
||||||
|
if (!emitTryEnd())
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(state_ == Catch);
|
||||||
|
if (!emitCatchEnd(false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(bce_->stackDepth == depth_);
|
||||||
|
|
||||||
|
if (!bce_->emitJumpTarget(&finallyStart_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (controlInfo_) {
|
||||||
|
// Fix up the gosubs that might have been emitted before non-local
|
||||||
|
// jumps to the finally code.
|
||||||
|
bce_->patchJumpsToTarget(controlInfo_->gosubs, finallyStart_);
|
||||||
|
|
||||||
|
// Indicate that we're emitting a subroutine body.
|
||||||
|
controlInfo_->setEmittingSubroutine();
|
||||||
|
}
|
||||||
|
if (finallyPos) {
|
||||||
|
if (!bce_->updateSourceCoordNotes(finallyPos.value()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_FINALLY))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (retValKind_ == UseRetVal) {
|
||||||
|
if (!bce_->emit1(JSOP_GETRVAL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Clear the frame's return value to make break/continue return
|
||||||
|
// correct value even if there's no other statement before them:
|
||||||
|
//
|
||||||
|
// eval("x: try { 1 } finally { break x; }"); // undefined, not 1
|
||||||
|
if (!bce_->emit1(JSOP_UNDEFINED))
|
||||||
|
return false;
|
||||||
|
if (!bce_->emit1(JSOP_SETRVAL))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ = Finally;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool emitFinallyEnd() {
|
||||||
|
MOZ_ASSERT(state_ == Finally);
|
||||||
|
|
||||||
|
if (retValKind_ == UseRetVal) {
|
||||||
|
if (!bce_->emit1(JSOP_SETRVAL))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bce_->emit1(JSOP_RETSUB))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bce_->hasTryFinally = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool emitEnd() {
|
||||||
|
if (state_ == Catch) {
|
||||||
|
MOZ_ASSERT(!hasFinally());
|
||||||
|
if (!emitCatchEnd(false))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(state_ == Finally);
|
||||||
|
MOZ_ASSERT(hasFinally());
|
||||||
|
if (!emitFinallyEnd())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(bce_->stackDepth == depth_);
|
||||||
|
|
||||||
|
// ReconstructPCStack needs a NOP here to mark the end of the last
|
||||||
|
// catch block.
|
||||||
|
if (!bce_->emit1(JSOP_NOP))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Fix up the end-of-try/catch jumps to come here.
|
||||||
|
if (!bce_->emitJumpTargetAndPatch(catchAndFinallyJump_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Add the try note last, to let post-order give us the right ordering
|
||||||
|
// (first to last for a given nesting level, inner to outer by level).
|
||||||
|
if (hasCatch()) {
|
||||||
|
if (!bce_->tryNoteList.append(JSTRY_CATCH, depth_, tryStart_, tryEnd_.offset))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've got a finally, mark try+catch region with additional
|
||||||
|
// trynote to catch exceptions (re)thrown from a catch block or
|
||||||
|
// for the try{}finally{} case.
|
||||||
|
if (hasFinally()) {
|
||||||
|
if (!bce_->tryNoteList.append(JSTRY_FINALLY, depth_, tryStart_, finallyStart_.offset))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state_ = End;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class MOZ_STACK_CLASS IfThenElseEmitter
|
class MOZ_STACK_CLASS IfThenElseEmitter
|
||||||
{
|
{
|
||||||
BytecodeEmitter* bce_;
|
BytecodeEmitter* bce_;
|
||||||
|
|
@ -6073,57 +6385,28 @@ BytecodeEmitter::emitCatch(ParseNode* pn)
|
||||||
MOZ_NEVER_INLINE bool
|
MOZ_NEVER_INLINE bool
|
||||||
BytecodeEmitter::emitTry(ParseNode* pn)
|
BytecodeEmitter::emitTry(ParseNode* pn)
|
||||||
{
|
{
|
||||||
// Track jumps-over-catches and gosubs-to-finally for later fixup.
|
ParseNode* catchList = pn->pn_kid2;
|
||||||
//
|
ParseNode* finallyNode = pn->pn_kid3;
|
||||||
// When a finally block is active, non-local jumps (including
|
|
||||||
// jumps-over-catches) result in a GOSUB being written into the bytecode
|
|
||||||
// stream and fixed-up later.
|
|
||||||
//
|
|
||||||
TryFinallyControl controlInfo(this, pn->pn_kid3 ? StatementKind::Finally : StatementKind::Try);
|
|
||||||
|
|
||||||
// Since an exception can be thrown at any place inside the try block,
|
TryEmitter::Kind kind;
|
||||||
// we need to restore the stack and the scope chain before we transfer
|
if (catchList) {
|
||||||
// the control to the exception handler.
|
if (finallyNode)
|
||||||
//
|
kind = TryEmitter::TryCatchFinally;
|
||||||
// For that we store in a try note associated with the catch or
|
else
|
||||||
// finally block the stack depth upon the try entry. The interpreter
|
kind = TryEmitter::TryCatch;
|
||||||
// uses this depth to properly unwind the stack and the scope chain.
|
} else {
|
||||||
//
|
MOZ_ASSERT(finallyNode);
|
||||||
int depth = stackDepth;
|
kind = TryEmitter::TryFinally;
|
||||||
|
|
||||||
// Record the try location, then emit the try block.
|
|
||||||
unsigned noteIndex;
|
|
||||||
if (!newSrcNote(SRC_TRY, ¬eIndex))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_TRY))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ptrdiff_t tryStart = offset();
|
|
||||||
if (!emitTree(pn->pn_kid1))
|
|
||||||
return false;
|
|
||||||
MOZ_ASSERT(depth == stackDepth);
|
|
||||||
|
|
||||||
// GOSUB to finally, if present.
|
|
||||||
if (pn->pn_kid3) {
|
|
||||||
if (!emitJump(JSOP_GOSUB, &controlInfo.gosubs))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
TryEmitter tryCatch(this, kind);
|
||||||
|
|
||||||
// Source note points to the jump at the end of the try block.
|
if (!tryCatch.emitTry())
|
||||||
if (!setSrcNoteOffset(noteIndex, 0, offset() - tryStart + JSOP_TRY_LENGTH))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Emit jump over catch and/or finally.
|
if (!emitTree(pn->pn_kid1))
|
||||||
JumpList catchJump;
|
|
||||||
if (!emitJump(JSOP_GOTO, &catchJump))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
JumpTarget tryEnd;
|
|
||||||
if (!emitJumpTarget(&tryEnd))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If this try has a catch block, emit it.
|
// If this try has a catch block, emit it.
|
||||||
ParseNode* catchList = pn->pn_kid2;
|
|
||||||
if (catchList) {
|
if (catchList) {
|
||||||
MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
|
MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
|
||||||
|
|
||||||
|
|
@ -6153,110 +6436,26 @@ BytecodeEmitter::emitTry(ParseNode* pn)
|
||||||
// capturing exceptions thrown from catch{} blocks.
|
// capturing exceptions thrown from catch{} blocks.
|
||||||
//
|
//
|
||||||
for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
|
for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
|
||||||
MOZ_ASSERT(this->stackDepth == depth);
|
if (!tryCatch.emitCatch())
|
||||||
|
|
||||||
// Clear the frame's return value that might have been set by the
|
|
||||||
// try block:
|
|
||||||
//
|
|
||||||
// eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
|
|
||||||
if (!emit1(JSOP_UNDEFINED))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_SETRVAL))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Emit the lexical scope and catch body.
|
// Emit the lexical scope and catch body.
|
||||||
MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
|
MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
|
||||||
if (!emitTree(pn3))
|
if (!emitTree(pn3))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// gosub <finally>, if required.
|
|
||||||
if (pn->pn_kid3) {
|
|
||||||
if (!emitJump(JSOP_GOSUB, &controlInfo.gosubs))
|
|
||||||
return false;
|
|
||||||
MOZ_ASSERT(this->stackDepth == depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump over the remaining catch blocks. This will get fixed
|
|
||||||
// up to jump to after catch/finally.
|
|
||||||
if (!emitJump(JSOP_GOTO, &catchJump))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If this catch block had a guard clause, patch the guard jump to
|
|
||||||
// come here.
|
|
||||||
if (controlInfo.guardJump.offset != -1) {
|
|
||||||
if (!emitJumpTargetAndPatch(controlInfo.guardJump))
|
|
||||||
return false;
|
|
||||||
controlInfo.guardJump.offset = -1;
|
|
||||||
|
|
||||||
// If this catch block is the last one, rethrow, delegating
|
|
||||||
// execution of any finally block to the exception handler.
|
|
||||||
if (!pn3->pn_next) {
|
|
||||||
if (!emit1(JSOP_EXCEPTION))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_THROW))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(this->stackDepth == depth);
|
|
||||||
|
|
||||||
// Emit the finally handler, if there is one.
|
// Emit the finally handler, if there is one.
|
||||||
JumpTarget finallyStart{ 0 };
|
if (finallyNode) {
|
||||||
if (pn->pn_kid3) {
|
if (!tryCatch.emitFinally(Some(finallyNode->pn_pos.begin)))
|
||||||
if (!emitJumpTarget(&finallyStart))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Fix up the gosubs that might have been emitted before non-local
|
if (!emitTree(finallyNode))
|
||||||
// jumps to the finally code.
|
|
||||||
patchJumpsToTarget(controlInfo.gosubs, finallyStart);
|
|
||||||
|
|
||||||
// Indicate that we're emitting a subroutine body.
|
|
||||||
controlInfo.setEmittingSubroutine();
|
|
||||||
if (!updateSourceCoordNotes(pn->pn_kid3->pn_pos.begin))
|
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_FINALLY))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_GETRVAL))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Clear the frame's return value to make break/continue return
|
|
||||||
// correct value even if there's no other statement before them:
|
|
||||||
//
|
|
||||||
// eval("x: try { 1 } finally { break x; }"); // undefined, not 1
|
|
||||||
if (!emit1(JSOP_UNDEFINED))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_SETRVAL))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!emitTree(pn->pn_kid3))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_SETRVAL))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_RETSUB))
|
|
||||||
return false;
|
|
||||||
hasTryFinally = true;
|
|
||||||
MOZ_ASSERT(this->stackDepth == depth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReconstructPCStack needs a NOP here to mark the end of the last catch block.
|
if (!tryCatch.emitEnd())
|
||||||
if (!emit1(JSOP_NOP))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Fix up the end-of-try/catch jumps to come here.
|
|
||||||
if (!emitJumpTargetAndPatch(catchJump))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Add the try note last, to let post-order give us the right ordering
|
|
||||||
// (first to last for a given nesting level, inner to outer by level).
|
|
||||||
if (catchList && !tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd.offset))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If we've got a finally, mark try+catch region with additional
|
|
||||||
// trynote to catch exceptions (re)thrown from a catch block or
|
|
||||||
// for the try{}finally{} case.
|
|
||||||
if (pn->pn_kid3 && !tryNoteList.append(JSTRY_FINALLY, depth, tryStart, finallyStart.offset))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -8040,17 +8239,15 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
|
||||||
int depth = stackDepth;
|
int depth = stackDepth;
|
||||||
MOZ_ASSERT(depth >= 2);
|
MOZ_ASSERT(depth >= 2);
|
||||||
|
|
||||||
JumpList send;
|
TryEmitter tryCatch(this, TryEmitter::TryCatchFinally, TryEmitter::DontUseRetVal,
|
||||||
if (!emitJump(JSOP_GOTO, &send)) // goto send
|
TryEmitter::DontUseControl);
|
||||||
|
if (!tryCatch.emitJumpOverCatchAndFinally()) // ITER RESULT
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Try prologue. // ITER RESULT
|
|
||||||
unsigned noteIndex;
|
|
||||||
if (!newSrcNote(SRC_TRY, ¬eIndex))
|
|
||||||
return false;
|
|
||||||
JumpTarget tryStart{ offset() };
|
JumpTarget tryStart{ offset() };
|
||||||
if (!emit1(JSOP_TRY)) // tryStart:
|
if (!tryCatch.emitTry()) // ITER RESULT
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MOZ_ASSERT(this->stackDepth == depth);
|
MOZ_ASSERT(this->stackDepth == depth);
|
||||||
|
|
||||||
// Load the generator object.
|
// Load the generator object.
|
||||||
|
|
@ -8061,17 +8258,9 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
|
||||||
if (!emitYieldOp(JSOP_YIELD)) // ITER RECEIVED
|
if (!emitYieldOp(JSOP_YIELD)) // ITER RECEIVED
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Try epilogue.
|
if (!tryCatch.emitCatch()) // ITER RESULT
|
||||||
if (!setSrcNoteOffset(noteIndex, 0, offset() - tryStart.offset))
|
|
||||||
return false;
|
|
||||||
if (!emitJump(JSOP_GOTO, &send)) // goto send
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JumpTarget tryEnd;
|
|
||||||
if (!emitJumpTarget(&tryEnd)) // tryEnd:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Catch location.
|
|
||||||
stackDepth = uint32_t(depth); // ITER RESULT
|
stackDepth = uint32_t(depth); // ITER RESULT
|
||||||
if (!emit1(JSOP_EXCEPTION)) // ITER RESULT EXCEPTION
|
if (!emit1(JSOP_EXCEPTION)) // ITER RESULT EXCEPTION
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -8118,36 +8307,19 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
|
||||||
if (!emitJump(JSOP_GOTO, &checkResult)) // goto checkResult
|
if (!emitJump(JSOP_GOTO, &checkResult)) // goto checkResult
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The finally block, IteratorClose logic.
|
// IteratorClose logic.
|
||||||
|
if (!tryCatch.emitFinally())
|
||||||
|
return false;
|
||||||
|
|
||||||
JumpTarget finallyStart{ 0 };
|
|
||||||
if (!emitJumpTarget(&finallyStart))
|
|
||||||
return false;
|
|
||||||
if (!emit1(JSOP_FINALLY)) // ITER RESULT FTYPE FVALUE
|
|
||||||
return false;
|
|
||||||
if (!emitDupAt(3)) // ITER RESULT FTYPE FVALUE ITER
|
if (!emitDupAt(3)) // ITER RESULT FTYPE FVALUE ITER
|
||||||
return false;
|
return false;
|
||||||
if (!emitIteratorClose(Some(tryStart))) // ITER RESULT FTYPE FVALUE
|
if (!emitIteratorClose(Some(tryStart))) // ITER RESULT FTYPE FVALUE
|
||||||
return false;
|
return false;
|
||||||
if (!emit1(JSOP_RETSUB)) // ITER RESULT
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Catch and finally epilogue.
|
if (!tryCatch.emitEnd())
|
||||||
|
|
||||||
// This is a peace offering to ReconstructPCStack. See the note in EmitTry.
|
|
||||||
if (!emit1(JSOP_NOP))
|
|
||||||
return false;
|
|
||||||
size_t tryStartOffset = tryStart.offset + JSOP_TRY_LENGTH;
|
|
||||||
if (!tryNoteList.append(JSTRY_CATCH, depth, tryStartOffset, tryEnd.offset))
|
|
||||||
return false;
|
|
||||||
if (!tryNoteList.append(JSTRY_FINALLY, depth, tryStartOffset, finallyStart.offset))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// After the try-catch-finally block: send the received value to the iterator.
|
// After the try-catch-finally block: send the received value to the iterator.
|
||||||
if (!emitJumpTargetAndPatch(send)) // send:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Send location.
|
|
||||||
// result = iter.next(received) // ITER RECEIVED
|
// result = iter.next(received) // ITER RECEIVED
|
||||||
if (!emit1(JSOP_SWAP)) // RECEIVED ITER
|
if (!emit1(JSOP_SWAP)) // RECEIVED ITER
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue