зеркало из https://github.com/mozilla/pjs.git
658 строки
20 KiB
C++
658 строки
20 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#include "VerificationEnv.h"
|
|
|
|
|
|
const VerificationEnv::BindingKind VerificationEnv::valueKindBindingKinds[nValueKinds] =
|
|
{
|
|
bkVoid, // vkVoid
|
|
bkInt, // vkInt
|
|
bkLong, // vkLong
|
|
bkFloat, // vkFloat
|
|
bkDouble, // vkDouble
|
|
bkAddr, // vkAddr
|
|
bkVoid, // vkCond
|
|
bkVoid, // vkMemory
|
|
bkVoid // vkTuple
|
|
};
|
|
|
|
|
|
const VerificationEnv::BindingKind VerificationEnv::typeKindBindingKinds[nTypeKinds] =
|
|
{
|
|
bkVoid, // tkVoid
|
|
bkInt, // tkBoolean
|
|
bkInt, // tkUByte
|
|
bkInt, // tkByte
|
|
bkInt, // tkChar
|
|
bkInt, // tkShort
|
|
bkInt, // tkInt
|
|
bkLong, // tkLong
|
|
bkFloat, // tkFloat
|
|
bkDouble, // tkDouble
|
|
bkAddr, // tkObject
|
|
bkAddr, // tkSpecial
|
|
bkAddr, // tkArray
|
|
bkAddr // tkInterface
|
|
};
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// VerificationEnv::Context
|
|
|
|
|
|
//
|
|
// Create a new, empty Context that holds information about which variables a subroutine
|
|
// has modified. Clear the modified array (indicating that no variables have been accessed
|
|
// yet) and the link to the next context.
|
|
//
|
|
VerificationEnv::Context::Context(Subroutine subroutine, const Common &common):
|
|
next(0),
|
|
subroutine(subroutine),
|
|
retReachable(false)
|
|
#ifdef DEBUG
|
|
, common(common)
|
|
#endif
|
|
{
|
|
assert(common.bindingPool);
|
|
modified = new(*common.bindingPool) char[common.nEnvSlots];
|
|
fill(modified, modified + common.nEnvSlots, 0);
|
|
}
|
|
|
|
|
|
//
|
|
// Create a copy of the given context. Clear the link to the next context.
|
|
//
|
|
VerificationEnv::Context::Context(const Context &src, const Common &common):
|
|
next(0),
|
|
subroutine(src.subroutine),
|
|
retReachable(src.retReachable)
|
|
#ifdef DEBUG
|
|
, common(common)
|
|
#endif
|
|
{
|
|
assert(common.bindingPool);
|
|
modified = new(*common.bindingPool) char[common.nEnvSlots];
|
|
copy(src.modified, src.modified + common.nEnvSlots, modified);
|
|
}
|
|
|
|
|
|
//
|
|
// Create a partial copy of the given context. Do not copy the contents of the
|
|
// modified array. Translate the Subroutine value using the given translator.
|
|
// Clear the link to the next context.
|
|
//
|
|
inline VerificationEnv::Context::Context(const Context &src, const Common &common, Function1<Subroutine, Subroutine> &translator):
|
|
next(0),
|
|
subroutine(translator(src.subroutine)),
|
|
retReachable(src.retReachable)
|
|
#ifdef DEBUG
|
|
, common(common)
|
|
#endif
|
|
{}
|
|
|
|
|
|
//
|
|
// Merge this context with the src context. A slot in the merged context is
|
|
// set to the modified state if it was modified in either this or the src context.
|
|
// Return true if the resulting context differs from the original contents of this
|
|
// context.
|
|
//
|
|
bool VerificationEnv::Context::meet(const Context &src, Uint32 nSlots)
|
|
{
|
|
assert(subroutine == src.subroutine && common.bindingPool);
|
|
bool changed = false;
|
|
char *dstModified = modified;
|
|
const char *srcModified = src.modified;
|
|
for (Uint32 slot = 0; slot != nSlots; slot++)
|
|
if (srcModified[slot] && !dstModified[slot]) {
|
|
dstModified[slot] = true;
|
|
changed = true;
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
|
|
//
|
|
// If a context corresponding to the given subroutine is in the linked list of
|
|
// contexts, return that context. If not, return nil. list can be nil.
|
|
//
|
|
VerificationEnv::Context *VerificationEnv::Context::find(Context *list, const Subroutine subroutine)
|
|
{
|
|
while (list && list->subroutine != subroutine)
|
|
list = list->next;
|
|
return list;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// VerificationEnv
|
|
|
|
|
|
const VerificationEnv::InitBinding VerificationEnv::secondWordBinding(bkSecondWord);
|
|
|
|
|
|
//
|
|
// Assign a partial copy of the given VerificationEnv (which must have been initialized)
|
|
// to this VerificationEnv, which is known to be uninitialized. Do not copy the
|
|
// bindings nor the modified arrays inside Contexts. Translate all Subroutine values
|
|
// using the given translator.
|
|
//
|
|
VerificationEnv::VerificationEnv(const VerificationEnv &env, Function1<Subroutine, Subroutine> &translator):
|
|
common(env.common)
|
|
{
|
|
assert(env.live());
|
|
|
|
// Copy the bindings pointer (which is now only relevant to indicate whether this
|
|
// environment is live or not).
|
|
bindings = env.bindings;
|
|
sp = env.sp;
|
|
|
|
// Copy the context hierarchy.
|
|
activeContexts = 0;
|
|
Context *srcContext = env.activeContexts;
|
|
Context **dstContext = &activeContexts;
|
|
while (srcContext) {
|
|
*dstContext = new(common.envPool) Context(*srcContext, common, translator);
|
|
dstContext = &(*dstContext)->next;
|
|
srcContext = srcContext->next;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Make this VerificationEnv (which must not have been initialized) live,
|
|
// assigning bkVoid to every slot.
|
|
//
|
|
void VerificationEnv::initLive()
|
|
{
|
|
assert(!live() && common.bindingPool);
|
|
|
|
// Create and initialize the bindings.
|
|
Binding *bdgs = new(*common.bindingPool) Binding[common.nEnvSlots];
|
|
bindings = bdgs;
|
|
Binding *bdgsEnd = bdgs + common.nEnvSlots;
|
|
while (bdgs != bdgsEnd)
|
|
bdgs++->setVoid();
|
|
sp = common.stackBase;
|
|
activeContexts = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Assign a copy of the given VerificationEnv (which must have been initialized)
|
|
// to this VerificationEnv, which is known to be uninitialized.
|
|
//
|
|
void VerificationEnv::copyEnv(const VerificationEnv &env)
|
|
{
|
|
assert(env.live() && common.bindingPool);
|
|
|
|
// Copy the bindings.
|
|
bindings = new(*common.bindingPool) Binding[common.nEnvSlots];
|
|
copy(env.bindings, env.bindings + common.nEnvSlots, bindings);
|
|
sp = env.sp;
|
|
|
|
// Copy the context hierarchy.
|
|
activeContexts = 0;
|
|
Context *srcContext = env.activeContexts;
|
|
Context **dstContext = &activeContexts;
|
|
while (srcContext) {
|
|
*dstContext = new(common.envPool) Context(*srcContext, common);
|
|
dstContext = &(*dstContext)->next;
|
|
srcContext = srcContext->next;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Set the value of the given environment slot, adding that slot to
|
|
// the list of slots modified by all currently active subroutines.
|
|
//
|
|
void VerificationEnv::setSlot(Uint32 slot, const Binding &binding)
|
|
{
|
|
assert(slot < common.nEnvSlots);
|
|
bindings[slot] = binding;
|
|
for (Context *c = activeContexts; c; c = c->next)
|
|
c->modify(slot);
|
|
}
|
|
|
|
|
|
//
|
|
// Return the value of the one-word local variable in the given slot.
|
|
// Throw a verification error if the slot number is out of bounds or the value
|
|
// is undefined or a part of a two-word value.
|
|
//
|
|
const VerificationEnv::Binding &VerificationEnv::getLocal1(Uint32 n) const
|
|
{
|
|
assert(live() && common.bindingPool);
|
|
if (n >= common.nLocals)
|
|
verifyError(VerifyError::noSuchLocal);
|
|
Binding &b = bindings[n];
|
|
if (!b.isOneWord())
|
|
verifyError(VerifyError::badType);
|
|
return b;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the value of the two-word local variable in the given slot.
|
|
// Throw a verification error if the slot number is out of bounds or the value
|
|
// is undefined or not a two-word value.
|
|
//
|
|
const VerificationEnv::Binding &VerificationEnv::getLocal2(Uint32 n) const
|
|
{
|
|
assert(live() && common.bindingPool);
|
|
if (n+1 >= common.nLocals)
|
|
verifyError(VerifyError::noSuchLocal);
|
|
Binding &b = bindings[n];
|
|
if (!b.isTwoWord())
|
|
verifyError(VerifyError::badType);
|
|
assert(bindings[n+1].hasKind(bkSecondWord));
|
|
return b;
|
|
}
|
|
|
|
|
|
//
|
|
// Set the value of the one-word local variable in the given slot.
|
|
// Throw a verification error if the slot number is out of bounds.
|
|
// The given binding must have a one-word kind.
|
|
//
|
|
void VerificationEnv::setLocal1(Uint32 n, const Binding &binding)
|
|
{
|
|
assert(live() && binding.isOneWord() && common.bindingPool);
|
|
if (n >= common.nLocals)
|
|
verifyError(VerifyError::noSuchLocal);
|
|
Binding *b = &bindings[n];
|
|
|
|
// If we're writing into an existing half of a doubleword,
|
|
// invalidate the other half of that doubleword.
|
|
BindingKind bk = b->getKind();
|
|
if (isTwoOrSecondWordKind(bk))
|
|
b[bk == bkSecondWord ? -1 : 1].setVoid();
|
|
|
|
setSlot(n, binding);
|
|
}
|
|
|
|
|
|
//
|
|
// Set the value of the two-word local variable in the given slot.
|
|
// Throw a verification error if the slot number is out of bounds.
|
|
// The given binding must have a two-word kind.
|
|
//
|
|
void VerificationEnv::setLocal2(Uint32 n, const Binding &binding)
|
|
{
|
|
assert(live() && binding.isTwoWord() && common.bindingPool);
|
|
if (n+1 >= common.nLocals)
|
|
verifyError(VerifyError::noSuchLocal);
|
|
Binding *b = &bindings[n];
|
|
|
|
// If we're writing into an existing half of a doubleword,
|
|
// invalidate the other half of that doubleword.
|
|
if (b[0].hasKind(bkSecondWord))
|
|
b[-1].setVoid();
|
|
if (b[1].isTwoWord())
|
|
b[2].setVoid();
|
|
|
|
setSlot(n, binding);
|
|
setSlot(n+1, secondWordBinding);
|
|
}
|
|
|
|
|
|
//
|
|
// Pop and return a one-word value from the stack.
|
|
// Throw a verification error if the stack underflows or the value
|
|
// is undefined or a part of a two-word value.
|
|
// Note that the returned reference will be invalidated by the next push.
|
|
//
|
|
const VerificationEnv::Binding &VerificationEnv::pop1()
|
|
{
|
|
assert(live() && common.bindingPool);
|
|
if (sp == common.stackBase)
|
|
verifyError(VerifyError::bytecodeStackUnderflow);
|
|
Binding &b = bindings[--sp];
|
|
if (!b.isOneWord())
|
|
verifyError(VerifyError::badType);
|
|
return b;
|
|
}
|
|
|
|
|
|
//
|
|
// Pop and return a one-word value from the stack.
|
|
// Throw a verification error if the stack underflows or the value
|
|
// is undefined or has a kind other than the given kind.
|
|
// Note that the returned reference will be invalidated by the next push.
|
|
//
|
|
const VerificationEnv::Binding &VerificationEnv::pop1(BindingKind bk)
|
|
{
|
|
assert(live() && isOneWordKind(bk) && common.bindingPool);
|
|
if (sp == common.stackBase)
|
|
verifyError(VerifyError::bytecodeStackUnderflow);
|
|
Binding &b = bindings[--sp];
|
|
if (!b.hasKind(bk))
|
|
verifyError(VerifyError::badType);
|
|
return b;
|
|
}
|
|
|
|
|
|
//
|
|
// Pop and return a two-word value or two one-word values from the stack.
|
|
// Throw a verification error if the stack underflows or the value
|
|
// is undefined or a part of a two-word value that includes one of the two
|
|
// stack slots but not the other.
|
|
//
|
|
void VerificationEnv::pop2(Binding &binding1, Binding &binding2)
|
|
{
|
|
assert(live() && common.bindingPool);
|
|
if (sp <= common.stackBase+1)
|
|
verifyError(VerifyError::bytecodeStackUnderflow);
|
|
sp -= 2;
|
|
Binding *b = &bindings[sp];
|
|
BindingKind bk1 = b[0].getKind();
|
|
BindingKind bk2 = b[1].getKind();
|
|
if (!(isOneWordKind(bk1) && isOneWordKind(bk2) || isTwoWordKind(bk1)))
|
|
verifyError(VerifyError::badType);
|
|
assert(!isTwoWordKind(bk1) || bk2 == bkSecondWord);
|
|
binding1 = b[0];
|
|
binding2 = b[1];
|
|
}
|
|
|
|
|
|
//
|
|
// Pop and return a two-word value from the stack.
|
|
// Throw a verification error if the stack underflows or the value
|
|
// is undefined or has a kind other than the given kind.
|
|
// Note that the returned reference will be invalidated by the next push.
|
|
//
|
|
const VerificationEnv::Binding &VerificationEnv::pop2(BindingKind bk)
|
|
{
|
|
assert(live() && isTwoWordKind(bk) && common.bindingPool);
|
|
if (sp <= common.stackBase+1)
|
|
verifyError(VerifyError::bytecodeStackUnderflow);
|
|
sp -= 2;
|
|
Binding *b = &bindings[sp];
|
|
if (!b[0].hasKind(bk))
|
|
verifyError(VerifyError::badType);
|
|
assert(b[1].hasKind(bkSecondWord));
|
|
return *b;
|
|
}
|
|
|
|
|
|
//
|
|
// Pop and return a one or two-word value from the stack, depending on bk.
|
|
// Throw a verification error if the stack underflows or the value
|
|
// is undefined or has a kind other than the given kind.
|
|
// Note that the returned reference will be invalidated by the next push.
|
|
//
|
|
const VerificationEnv::Binding &VerificationEnv::pop1or2(BindingKind bk)
|
|
{
|
|
return isTwoWordKind(bk) ? pop2(bk) : pop1(bk);
|
|
}
|
|
|
|
|
|
//
|
|
// Push a one-word value onto the stack.
|
|
// Throw a verification error if the stack overflows.
|
|
// The given binding must have a one-word kind.
|
|
//
|
|
void VerificationEnv::push1(const Binding &binding)
|
|
{
|
|
assert(live() && binding.isOneWord() && common.bindingPool);
|
|
if (sp == common.nEnvSlots)
|
|
verifyError(VerifyError::bytecodeStackOverflow);
|
|
setSlot(sp++, binding);
|
|
}
|
|
|
|
|
|
//
|
|
// Push a one-word value that contains a new binding of the given kind onto the stack.
|
|
// Throw a verification error if the stack overflows.
|
|
//
|
|
void VerificationEnv::push1(BindingKind bk)
|
|
{
|
|
// Note: bkAddr will be outlawed here once we start to distinguish among types of
|
|
// pointers.
|
|
assert(bk == bkVoid || bk == bkInt || bk == bkFloat || bk == bkAddr);
|
|
InitBinding b(bk);
|
|
push1(b);
|
|
}
|
|
|
|
|
|
//
|
|
// Push a two-word value onto the stack.
|
|
// Throw a verification error if the stack overflows.
|
|
// The given binding must have a two-word kind.
|
|
//
|
|
void VerificationEnv::push2(const Binding &binding)
|
|
{
|
|
assert(live() && binding.isTwoWord() && common.bindingPool);
|
|
if (sp+1 >= common.nEnvSlots)
|
|
verifyError(VerifyError::bytecodeStackOverflow);
|
|
setSlot(sp++, binding);
|
|
setSlot(sp++, secondWordBinding);
|
|
}
|
|
|
|
|
|
//
|
|
// Push a two-word value that contains a new binding of the given kind onto the stack.
|
|
// Throw a verification error if the stack overflows.
|
|
//
|
|
void VerificationEnv::push2(BindingKind bk)
|
|
{
|
|
assert(bk == bkLong || bk == bkDouble);
|
|
InitBinding b(bk);
|
|
push2(b);
|
|
}
|
|
|
|
|
|
//
|
|
// Push a two-word value or two one-word values onto the stack.
|
|
// Throw a verification error if the stack overflows.
|
|
// The given bindings must be the two words of a two-word value
|
|
// or both be one-word values.
|
|
//
|
|
void VerificationEnv::push2(const Binding &binding1, const Binding &binding2)
|
|
{
|
|
assert(live() && (binding1.isTwoWord() && binding2.hasKind(bkSecondWord) ||
|
|
binding1.isOneWord() && binding2.isOneWord())
|
|
&& common.bindingPool);
|
|
if (sp+1 >= common.nEnvSlots)
|
|
verifyError(VerifyError::bytecodeStackOverflow);
|
|
setSlot(sp++, binding1);
|
|
setSlot(sp++, binding2);
|
|
}
|
|
|
|
|
|
//
|
|
// Push a one or two-word value that contains a new binding of the
|
|
// given kind onto the stack.
|
|
// Throw a verification error if the stack overflows.
|
|
//
|
|
void VerificationEnv::push1or2(BindingKind bk)
|
|
{
|
|
// Note: bkAddr will be outlawed here once we start to distinguish among types of
|
|
// pointers.
|
|
assert(bk == bkVoid || bk == bkInt || bk == bkFloat || bk == bkAddr || bk == bkLong || bk == bkDouble);
|
|
InitBinding b(bk);
|
|
if (isTwoWordKind(bk))
|
|
push2(b);
|
|
else
|
|
push1(b);
|
|
}
|
|
|
|
|
|
//
|
|
// Push the context of the given subroutine onto the list of currently active
|
|
// contexts. Initialize that context to record all bindings written to this
|
|
// environment after this enterSubroutine call.
|
|
//
|
|
// Throw a verification error if the given subroutine already is on the list
|
|
// of active contexts. This rejects bytecode programs that call subroutines
|
|
// recursively. It also rejects some "valid" bytecode programs that do not call
|
|
// subroutines recursively (for instance, if subroutine A exits via a jump
|
|
// instead of a ret and then calls subroutine A again), but, fortunately, the
|
|
// current definition of the Java language does not generate such programs.
|
|
// (Specifically, each Java try block has a single entry point and the finally
|
|
// handler -- say, subroutine A -- for that try block is outside that block. In
|
|
// order for subroutine A to be called again, execution must proceed again
|
|
// through the entry point of the try block, and we know that at that point
|
|
// subroutine A is not one of the active contexts.)
|
|
//
|
|
void VerificationEnv::enterSubroutine(Subroutine s)
|
|
{
|
|
assert(live() && s && common.bindingPool);
|
|
if (Context::find(activeContexts, s))
|
|
verifyError(VerifyError::jsrNestingError);
|
|
|
|
Context *c = new(common.envPool) Context(s, common);
|
|
c->next = activeContexts;
|
|
activeContexts = c;
|
|
}
|
|
|
|
|
|
//
|
|
// Pop the context of the given subroutine from the list of currently active
|
|
// contexts, and replace environment bindings that were not modified by the
|
|
// subroutine by their contenst from entryEnv, which represents the environment
|
|
// as it was just before entry to the subroutine. Also pop any contexts more
|
|
// recent than that of the given subroutine -- these subroutines apparently
|
|
// exited using a jump instead of a ret.
|
|
//
|
|
// Throw a verification error if the subroutine is not on the list of active
|
|
// contexts, which indicates that there is some program path along which this
|
|
// ret could be reached without this subroutine having been called first.
|
|
//
|
|
void VerificationEnv::exitSubroutine(Subroutine s, const VerificationEnv &entryEnv)
|
|
{
|
|
assert(live() && s && entryEnv.live() && common.bindingPool);
|
|
Context *c = Context::find(activeContexts, s);
|
|
if (!c)
|
|
verifyError(VerifyError::jsrNestingError);
|
|
activeContexts = c->next;
|
|
|
|
// Replace unmodified environment bindings.
|
|
Uint32 nSlots = sp;
|
|
const Binding *srcBindings = entryEnv.bindings;
|
|
Binding *dstBindings = bindings;
|
|
for (Uint32 slot = 0; slot != nSlots; slot++)
|
|
if (!c->isModified(slot))
|
|
dstBindings[slot] = srcBindings[slot];
|
|
}
|
|
|
|
|
|
//
|
|
// Intersect the given VerificationEnv (which must be live) into this
|
|
// VerificationEnv, which must also be live. The two environments may have
|
|
// different sets of active subroutine contexts, in which case leave the maximal
|
|
// common set (for instance, intersecting a->b with b yields b, while
|
|
// intersecting a->b->d->e with a->c->d yields a->d). Because each subroutine has
|
|
// only one entry point and cannot be recursive, we don't have to worry about
|
|
// cases such as intersecting a->b with b->a -- nesting of subroutines follows a
|
|
// partial order.
|
|
//
|
|
// Return true if this VerificationEnv changed.
|
|
//
|
|
// Throw a verification error if the environments have different stack depths.
|
|
//
|
|
bool VerificationEnv::meet(const VerificationEnv &env)
|
|
{
|
|
assert(live() && env.live() && common.bindingPool);
|
|
Uint32 nSlots = sp;
|
|
if (nSlots != env.sp)
|
|
verifyError(VerifyError::bytecodeStackDynamic);
|
|
bool changed = false;
|
|
|
|
// Merge context lists
|
|
Context **dstContextPtr = &activeContexts;
|
|
Context *srcContext = env.activeContexts;
|
|
Context *dstContext;
|
|
while ((dstContext = *dstContextPtr) != 0) {
|
|
Context *c = Context::find(srcContext, dstContext->subroutine);
|
|
if (c) {
|
|
srcContext = c;
|
|
changed |= dstContext->meet(*c, nSlots);
|
|
dstContextPtr = &dstContext->next;
|
|
} else {
|
|
// Remove this destination context.
|
|
*dstContextPtr = dstContext->next;
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
// Merge bindings
|
|
const Binding *srcBinding = env.bindings;
|
|
Binding *dstBinding = bindings;
|
|
Binding *dstBindingsEnd = dstBinding + nSlots;
|
|
while (dstBinding != dstBindingsEnd) {
|
|
if (*dstBinding != *srcBinding && !dstBinding->hasKind(bkVoid)) {
|
|
dstBinding->setVoid();
|
|
changed = true;
|
|
}
|
|
srcBinding++;
|
|
dstBinding++;
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
|
|
//
|
|
// This VerificationEnv (which must be live) is the entry environment to a ret
|
|
// bytecode that returns from subroutine s. That subroutine's context is one
|
|
// of the active contexts in this VerificationEnv. Set that context's retReachable
|
|
// flag because clearly subroutine s's ret is reachable from here.
|
|
//
|
|
void VerificationEnv::setRetReachable(Subroutine s)
|
|
{
|
|
assert(live());
|
|
Context *c = Context::find(activeContexts, s);
|
|
assert(c);
|
|
c->retReachable = true;
|
|
}
|
|
|
|
|
|
//
|
|
// Merge the retReachable flags of env into this VerificationEnv. env is the
|
|
// environment of a successor of this VerificationEnv's block. If s is non-nil,
|
|
// this VerificationEnv's block is a jsr instruction that calls subroutine s,
|
|
// and env belongs to s's first block; in this case don't set this environment's
|
|
// retReachable flag for s's context because this environment is outside s.
|
|
//
|
|
// Return true if this VerificationEnv changed.
|
|
//
|
|
bool VerificationEnv::mergeRetReachables(const VerificationEnv &env, Subroutine s)
|
|
{
|
|
assert(live() && env.live());
|
|
bool changed = false;
|
|
|
|
Context *srcContext = env.activeContexts;
|
|
Context *dstContext = activeContexts;
|
|
while (srcContext) {
|
|
if (srcContext->retReachable) {
|
|
Subroutine srcSubroutine = srcContext->subroutine;
|
|
if (srcSubroutine != s) {
|
|
dstContext = Context::find(dstContext, srcSubroutine);
|
|
assert(dstContext);
|
|
if (!dstContext->retReachable) {
|
|
dstContext->retReachable = true;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
srcContext = srcContext->next;
|
|
}
|
|
return changed;
|
|
}
|