/* -*- 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.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #ifndef VERIFICATIONENV_H #define VERIFICATIONENV_H #include "LocalEnv.h" #include "Value.h" #include "ErrorHandling.h" struct BytecodeBlock; class VerificationEnv { public: typedef BytecodeBlock *Subroutine; // A subroutine is represented by a pointer to its entry BytecodeBlock enum BindingKind { bkVoid, // Undefined stack word bkReturn, // jsr/ret return value bkInt, // int value bkFloat, // float value bkAddr, // Object pointer value bkLong, // First word of a long value bkDouble, // First word of a double value bkSecondWord // Second word of a long or double value }; private: static const BindingKind valueKindBindingKinds[nValueKinds]; static const BindingKind typeKindBindingKinds[nTypeKinds]; public: static bool isOneWordKind(BindingKind kind) {return (uint)(kind - bkReturn) <= (uint)(bkAddr - bkReturn);} static bool isTwoWordKind(BindingKind kind) {return (uint)(kind - bkLong) <= (uint)(bkDouble - bkLong);} static bool isTwoOrSecondWordKind(BindingKind kind) {return (uint)(kind - bkLong) <= (uint)(bkSecondWord - bkLong);} static BindingKind valueKindToBindingKind(ValueKind kind) {return valueKindBindingKinds[kind];} static BindingKind typeKindToBindingKind(TypeKind kind) {return typeKindBindingKinds[kind];} class Binding { BindingKind kind; // Kind of value held in this stack slot union { Subroutine subroutine; // If kind is bkReturn, the subroutine that was called }; public: BindingKind getKind() {return kind;} bool hasKind(BindingKind k) const {return kind == k;} bool isOneWord() const {return isOneWordKind(kind);} bool isTwoWord() const {return isTwoWordKind(kind);} bool isTwoOrSecondWord() const {return isTwoOrSecondWordKind(kind);} bool operator==(const Binding &b2) const {return kind == b2.kind && (kind != bkReturn || subroutine == b2.subroutine);} bool operator!=(const Binding &b2) const {return !operator==(b2);} void setVoid() {kind = bkVoid;} void setReturn(Subroutine s) {kind = bkReturn; subroutine = s;} void setKind(BindingKind k) {assert(k != bkReturn); kind = k;} Subroutine getSubroutine() const {assert(kind == bkReturn); return subroutine;} }; struct InitBinding: Binding { explicit InitBinding(BindingKind kind) {setKind(kind);} }; struct Common: CommonEnv { Pool *bindingPool; // Pool used for allocating arrays of bindings and 'modified' arrays inside Contexts; nil if none Common(Pool &envPool, Pool *bindingPool, Uint32 nLocals, Uint32 stackSize): CommonEnv(envPool, 0, nLocals, stackSize), bindingPool(bindingPool) {} void setBindingPool(Pool &pool) {assert(!bindingPool); bindingPool = &pool;} void clearBindingPool() {bindingPool = 0;} // Trashes bindings arrays and 'modified' arrays inside Contexts }; private: struct Context { Context *next; // Link to next outer subroutine context or nil if none const Subroutine subroutine; // Pointer to the first instruction of a subroutine that was called and not yet returned private: char *modified; // Array of nLocals+stackSize bytes; each is set if the corresponding local variable // or stack temporary has been modified since entry to the subroutine; public: // undefined if common.bindingPool is nil. bool retReachable; // True if subroutine's ret is reachable from this block (without going through subroutine's jsr) #ifdef DEBUG const Common &common; // Backpointer to information shared among all VerificationEnvs in a graph #endif Context(Subroutine subroutine, const Common &common); Context(const Context &src, const Common &common); Context(const Context &src, const Common &common, Function1 &translator); void modify(Uint32 slot) {assert(common.bindingPool); modified[slot] = 1;} bool isModified(Uint32 slot) {assert(common.bindingPool); return modified[slot] != 0;} bool meet(const Context &src, Uint32 nSlots); static Context *find(Context *list, const Subroutine subroutine); }; Common &common; // Backpointer to information shared among all VerificationEnvs in a graph Binding *bindings; // Array of: // nLocals local variable bindings // stackSize stack temporaries' bindings; // bindings==nil means no information is available yet. // If common.bindingPool is nil, bindings is either nil or non-nil but does not point to anything. Uint32 sp; // Stack pointer (index within bindings array of first unused temporary) // Bindings at indices 0..sp-1 are valid; others are ignored Context *activeContexts; // Linked list of all currently active subroutines from inner to outer static const InitBinding secondWordBinding; // Always contains a bkSecondWord binding public: explicit VerificationEnv(Common &common): common(common), bindings(0) {} VerificationEnv(const VerificationEnv &env): common(env.common) {copyEnv(env);} VerificationEnv(const VerificationEnv &env, Function1 &translator); void operator=(const VerificationEnv &env) {assert(!live()); copyEnv(env);} void move(VerificationEnv &env); void initLive(); bool live() const {return bindings != 0;} private: void copyEnv(const VerificationEnv &env); void setSlot(Uint32 slot, const Binding &binding); public: // Local variable operations const Binding &getLocal1(Uint32 n) const; const Binding &getLocal2(Uint32 n) const; void setLocal1(Uint32 n, const Binding &binding); void setLocal2(Uint32 n, const Binding &binding); // Stack operations Uint32 getSP() const {return sp - common.stackBase;} void dropAll() {sp = common.stackBase;} const Binding &pop1(); const Binding &pop1(BindingKind bk); void pop2(Binding &binding1, Binding &binding2); const Binding &pop2(BindingKind bk); const Binding &pop1or2(BindingKind bk); void push1(const Binding &binding); void push1(BindingKind bk); void push2(const Binding &binding); void push2(BindingKind bk); void push2(const Binding &binding1, const Binding &binding2); void push1or2(BindingKind bk); void enterSubroutine(Subroutine s); void exitSubroutine(Subroutine s, const VerificationEnv &entryEnv); bool meet(const VerificationEnv &env); bool isRetReachable(Subroutine s); void setRetReachable(Subroutine s); bool mergeRetReachables(const VerificationEnv &env, Subroutine s); }; // --- INLINES ---------------------------------------------------------------- // // Destructively move the given VerificationEnv (which must have been initialized) // to this VerificationEnv, which must be uninitialized. The given VerificationEnv is // left uninitialized. // inline void VerificationEnv::move(VerificationEnv &env) { assert(!live() && env.live()); bindings = env.bindings; sp = env.sp; activeContexts = env.activeContexts; #ifdef DEBUG env.bindings = 0; env.activeContexts = 0; #endif } // // Return true if Subroutine s is one of the active contexts in this VerificationEnv // and that context's retReachable flag is true. // inline bool VerificationEnv::isRetReachable(Subroutine s) { assert(live()); Context *c = Context::find(activeContexts, s); return c && c->retReachable; } #endif