mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 06:48:38 +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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
BytecodeEmitter* bce_;
|
||||
|
|
@ -6073,57 +6385,28 @@ BytecodeEmitter::emitCatch(ParseNode* pn)
|
|||
MOZ_NEVER_INLINE bool
|
||||
BytecodeEmitter::emitTry(ParseNode* pn)
|
||||
{
|
||||
// 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.
|
||||
//
|
||||
TryFinallyControl controlInfo(this, pn->pn_kid3 ? StatementKind::Finally : StatementKind::Try);
|
||||
ParseNode* catchList = pn->pn_kid2;
|
||||
ParseNode* finallyNode = pn->pn_kid3;
|
||||
|
||||
// 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.
|
||||
//
|
||||
int depth = stackDepth;
|
||||
|
||||
// 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::Kind kind;
|
||||
if (catchList) {
|
||||
if (finallyNode)
|
||||
kind = TryEmitter::TryCatchFinally;
|
||||
else
|
||||
kind = TryEmitter::TryCatch;
|
||||
} else {
|
||||
MOZ_ASSERT(finallyNode);
|
||||
kind = TryEmitter::TryFinally;
|
||||
}
|
||||
TryEmitter tryCatch(this, kind);
|
||||
|
||||
// Source note points to the jump at the end of the try block.
|
||||
if (!setSrcNoteOffset(noteIndex, 0, offset() - tryStart + JSOP_TRY_LENGTH))
|
||||
if (!tryCatch.emitTry())
|
||||
return false;
|
||||
|
||||
// Emit jump over catch and/or finally.
|
||||
JumpList catchJump;
|
||||
if (!emitJump(JSOP_GOTO, &catchJump))
|
||||
return false;
|
||||
|
||||
JumpTarget tryEnd;
|
||||
if (!emitJumpTarget(&tryEnd))
|
||||
if (!emitTree(pn->pn_kid1))
|
||||
return false;
|
||||
|
||||
// If this try has a catch block, emit it.
|
||||
ParseNode* catchList = pn->pn_kid2;
|
||||
if (catchList) {
|
||||
MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
|
||||
|
||||
|
|
@ -6153,110 +6436,26 @@ BytecodeEmitter::emitTry(ParseNode* pn)
|
|||
// capturing exceptions thrown from catch{} blocks.
|
||||
//
|
||||
for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
|
||||
MOZ_ASSERT(this->stackDepth == depth);
|
||||
|
||||
// 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))
|
||||
if (!tryCatch.emitCatch())
|
||||
return false;
|
||||
|
||||
// Emit the lexical scope and catch body.
|
||||
MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
|
||||
if (!emitTree(pn3))
|
||||
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.
|
||||
JumpTarget finallyStart{ 0 };
|
||||
if (pn->pn_kid3) {
|
||||
if (!emitJumpTarget(&finallyStart))
|
||||
if (finallyNode) {
|
||||
if (!tryCatch.emitFinally(Some(finallyNode->pn_pos.begin)))
|
||||
return false;
|
||||
|
||||
// Fix up the gosubs that might have been emitted before non-local
|
||||
// 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))
|
||||
if (!emitTree(finallyNode))
|
||||
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 (!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))
|
||||
if (!tryCatch.emitEnd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -8040,17 +8239,15 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
|
|||
int depth = stackDepth;
|
||||
MOZ_ASSERT(depth >= 2);
|
||||
|
||||
JumpList send;
|
||||
if (!emitJump(JSOP_GOTO, &send)) // goto send
|
||||
TryEmitter tryCatch(this, TryEmitter::TryCatchFinally, TryEmitter::DontUseRetVal,
|
||||
TryEmitter::DontUseControl);
|
||||
if (!tryCatch.emitJumpOverCatchAndFinally()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Try prologue. // ITER RESULT
|
||||
unsigned noteIndex;
|
||||
if (!newSrcNote(SRC_TRY, ¬eIndex))
|
||||
return false;
|
||||
JumpTarget tryStart{ offset() };
|
||||
if (!emit1(JSOP_TRY)) // tryStart:
|
||||
if (!tryCatch.emitTry()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(this->stackDepth == depth);
|
||||
|
||||
// Load the generator object.
|
||||
|
|
@ -8061,17 +8258,9 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
|
|||
if (!emitYieldOp(JSOP_YIELD)) // ITER RECEIVED
|
||||
return false;
|
||||
|
||||
// Try epilogue.
|
||||
if (!setSrcNoteOffset(noteIndex, 0, offset() - tryStart.offset))
|
||||
return false;
|
||||
if (!emitJump(JSOP_GOTO, &send)) // goto send
|
||||
if (!tryCatch.emitCatch()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
JumpTarget tryEnd;
|
||||
if (!emitJumpTarget(&tryEnd)) // tryEnd:
|
||||
return false;
|
||||
|
||||
// Catch location.
|
||||
stackDepth = uint32_t(depth); // ITER RESULT
|
||||
if (!emit1(JSOP_EXCEPTION)) // ITER RESULT EXCEPTION
|
||||
return false;
|
||||
|
|
@ -8118,36 +8307,19 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
|
|||
if (!emitJump(JSOP_GOTO, &checkResult)) // goto checkResult
|
||||
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
|
||||
return false;
|
||||
if (!emitIteratorClose(Some(tryStart))) // ITER RESULT FTYPE FVALUE
|
||||
return false;
|
||||
if (!emit1(JSOP_RETSUB)) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Catch and finally epilogue.
|
||||
|
||||
// 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))
|
||||
if (!tryCatch.emitEnd())
|
||||
return false;
|
||||
|
||||
// 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
|
||||
if (!emit1(JSOP_SWAP)) // RECEIVED ITER
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue