# Copyright 2002 Combex, Inc. under the terms of the MIT X license # found at http://www.opensource.org/licenses/mit-license.html ............... pragma.syntax("0.9") def makeValueThunk :DeepFrozen := def makeFlexSet :DeepFrozen := def OneArgFunc :DeepFrozen := /** * A weak-value table mapping from SwissNumbers to references. *

* There are two cases: 1) NEAR references to Selfish objects. 2) Everything * else. For case #1, a backwards weak-key table is also maintained, such that * multiple registrations of a NEAR Selfish object will always yield the same * SwissNumber. This SwissNumber can then be (and is) used remotely to * represent the sameness identity of resolved references to this Selfish * object. Case #1 is used for both live and sturdy references. *

* Case #2 is used only for sturdy references. The table only maps from * SwissNumbers to references, not vice versa, so each registration assigns a * new SwissNumber. * * @author Mark S. Miller */ def makeSwissTable(entropy, makeWeakKeyMap, makeWeakValueMap) implements DeepFrozen { # Change note for E-on-CL: changed the messages below from run/2 to fromTypes/2, for uniformity with the makeFlexMap interface -- kpreid 2007-07-26 # Maps from NEAR Selfish objects to SwissNumbers. def mySelfishToSwiss := makeWeakKeyMap.fromTypes(any, any) # Maps from SwissNumber to anything. # # Note: can't handle null values. def mySwissToRef := makeWeakValueMap.fromTypes(int, any) # OneArgFuncs that handle lookup faulting. def mySwissDBs := makeFlexSet.fromType(OneArgFunc) def swissTable { /** * Lookup an object by SwissNumber.

*

* If not found, throw an IndexOutOfBoundsException. This is necessary * since null is a valid return value. (By decree, the SwissNumber 0 * designates null.) */ to lookupSwiss(swissNum :int) :any { if (0 == swissNum.signum()) { #Since Weak*Maps can't handle nulls, we handle it ourselves. return null; } escape fault { return mySwissToRef.fetch(swissNum, fault) } def swissHash := swissNum.cryptoHash(); for db in mySwissDBs { #give each fault handler a chance db.run(swissHash); } #try one more time return mySwissToRef.get(swissNum); } /** * A SwissDB is able to supplement the SwissTable's internal mySwissToRef * table with further storage that gets faulted on demand.

*

* When the SwissTable's lookupSwiss fails to find the swissNum in the * internal table, it invokes each of its registered swissDBs with a hash * of the swissNumber being looked up. This is known as a swissHash, and * represents the identity of the object without providing any authority to * access the object. A swissDB which has stored a representation of the * object elsewhere should then register the object using registerIdentity * or registerSwiss, both of which require the swissBase -- the archash of * the swissNumber being looked up. In other words, *

         *     swissBase cryptoHash() -> swissNum
         *     swissNum crytoHash()   -> swissHash
         * 

*

* If an already registered swissDB is re-registered, an exception is * thrown. */ to addFaultHandler(swissDB :OneArgFunc) :void { mySwissDBs.addElement(swissDB, true); } /** * Removes a registered (by addFaultHandler) swissDB.

*

* If not there, this method does nothing. */ to removeFaultHandler(swissDB :OneArgFunc) :void { mySwissDBs.remove(swissDB); } /** * Returns the SwissNumber which represents the identity of this near * Selfish object in this vat. *

* If not 'Ref.isSelfish(obj)", then this will throw an Exception. *

* This returns the unique SwissNumber which represents the designated near * selfish object's unique identity within this vat. If the object wasn't * yet associated with a SwissNumber, it will be now. */ to getIdentity(var obj :any) :int { obj := Ref.resolution(obj); if (!Ref.isSelfish(obj)) { throw("Not Selfish: " + E.toQuote(obj)); } var result := mySelfishToSwiss.fetch(obj, makeValueThunk.getNULL_THUNK()); if (null == result) { result := entropy.nextSwiss(); mySwissToRef.put(result, obj); mySelfishToSwiss.put(obj, result); } return result; } /** * Returns a SwissNumber with which this ref can be looked up. *

* This method always assigns and returns a new unique SwissNumber (an * integer of some type), even for NEAR Selfish objects that already have * one, with one exception. The swissNumber for null is always 0. * * XXX the above seems to be false since the current implementation uses getIdentity */ to getNewSwiss(var ref :any) :int { ref := Ref.resolution(ref) if (__equalizer.sameYet(null, ref)) { return 0 } if (Ref.isSelfish(ref)) { return swissTable.getIdentity(ref) } def result := entropy.nextSwiss() mySwissToRef.put(result, ref) return result; } /** * Registers obj to have the identity 'swissBase.cryptoHash()'.

*

* The cryptoHash of a SwissBase is a SwissNumber, so we also say that the * archash of a SwissNumber is a SwissBase. (Of course, our security rests * on the assumption that the archash is infeasible to compute.) Since an * unconfined client of an object can often get its SwissNumber, something * more is needed to establish authority to associate an object with a * SwissNumber. For this "something more", we use knowledge of the archash * of the number.

*

* The object is given the new identity 'swissBase cryptoHash()', assuming * this doesn't conflict with any existing registrations. If it does, an * exception is thrown. */ to registerIdentity(var obj :any, swissBase :int) :int { obj := Ref.resolution(obj); if (!Ref.isSelfish(obj)) { throw("Not Selfish: " + obj); } def result := swissBase.cryptoHash() def oldObj := Ref.resolution(mySwissToRef.fetch(result, makeValueThunk(obj))); require(null == oldObj || oldObj == obj, fn { "SwissNumber already identifies a different object: " + E.toQuote(result) }) def oldSwiss := mySelfishToSwiss.fetch(obj, makeValueThunk(result)); require(oldSwiss == result, fn { "Object already has a different identity: " + E.toQuote(oldSwiss) + " vs " + E.toQuote(result) }) mySelfishToSwiss.put(obj, result); mySwissToRef.put(result, obj); return result; } /** * Registers ref at 'swissBase.cryptoHash()'. *

* registerNewSwiss() is to registerIdentity() as getNewSwiss() is to * getIdentity(). 'swissBase.cryptoHash()' must not already be registered, * or an exception will be thrown. If ref is null, an exception is thrown * (since we assume its infeasible to find the archash of zero). */ to registerNewSwiss(var ref :any, swissBase :int) :int { ref := Ref.resolution(ref) def result := swissBase.cryptoHash(); if (null == ref) { #XXX In just the way the following is careful not to reveal more #than a swissHash in a thrown exception, we need to go through the #rest of the swissNumber and swissBase handling logic and make it #do likewise. XXX The preceding is not true under E-on-CL sealed exception rules. def swissHash := result.cryptoHash(); throw("May not re-register null for swissHash: " + E.toString(swissHash)); } def oldRef := mySwissToRef.fetch(result, makeValueThunk.getNULL_THUNK()); if (null == oldRef) { mySwissToRef.put(result, ref); } else if (ref == oldRef) { # Registering the same object with the same base is cool. # XXX should we use Ref.same(..) instead of == ? } else { def swissHash := result.cryptoHash(); throw("An object with swissHash " + E.toString(swissHash) + "is already registered"); } return result; } /** * A convenience method typically used to obtain new SwissBases (archashes * of SwissNumbers). *

* Since a client of SwissTable can obtain such entropy from the SwissTable * anyway, by registering objects, there's no loss of security in providing * this convenience method. */ to nextSwiss() :int { return entropy.nextSwiss(); } } return swissTable }