# Copyright 2005-2007 Kevin Reid, under the terms of the MIT X license # found at http://www.opensource.org/licenses/mit-license.html ................ auditors actually invoked ? var timesCalled := 0 # value: 0 ? def approver { > to audit(audition) :any { > timesCalled += 1 > return true > } > } # value: ? def auditSample implements approver { > } # value: ? timesCalled # value: 1 ? def auditSample_as as approver { > } # value: ? timesCalled # value: 2 objectExpr ? def exprTest { > to audit(audition) :any { > print(audition.getObjectExpr().getDocComment()) > return false > } > } # value: ? /** the quick brown fox */ > def x1 implements exprTest { > to __printOn(out :TextWriter) :void { > out.print("jumped over the lazy dog") > } > } # stdout: the quick brown fox # value: jumped over the lazy dog __auditedBy ? __auditedBy # value: __auditedBy ? __auditedBy(approver, 1) # value: false ? __auditedBy(approver, auditSample) # value: true ? __auditedBy(approver, auditSample_as) # value: true ? __auditedBy(x1, exprTest) # value: false check that an intervening near ref doesn't interfere ' ? {def r; bind r := auditSample; __auditedBy(approver, r)} # value: true auditor that returns false ? def noop { > to audit(_) :boolean { > return false > } > } # value: ? __auditedBy(noop, def x5 implements noop {}) # value: false ? __auditedBy(noop, def x5_as as noop {}) # value: false auditor that returns funny value ? def brokenAuditor { > to audit(_) :boolean { > return 43 > } > } # value: ? def x7 implements brokenAuditor {} # problem: the int 43 doesn't coerce to a boolean matcher can't see audit ' ? def x2 { > match msg { > println(msg) > 43 > } > } # value: ? __auditedBy(approver, x2) # value: false plumbing can't see audit ' ? def x6 match msg { println(msg); E.callWithPair(auditSample, msg) }; null ? x6.__getAllegedType() # stdout: ["__getAllegedType", []] # # value: AuditSample ? __auditedBy(approver, x6) # value: false e[-named]-lambda can't see audit ' ? __auditedBy(approver, __loop) # value: false audition.ask ? def delegatingAuditor { > to audit(audition) :boolean { > audition.ask(approver) > return false > } > } # value: ? def x3 implements delegatingAuditor {} # value: ? __auditedBy(delegatingAuditor, x3) # value: false ? __auditedBy(approver, x3) # value: true audition.getGuard This section includes tests that the proper guards appear on each of the various binding types in the compiler. ? def CheckGuard { > to get(noun, guard) { > return def guardCheckingAuditor { > to audit(audition) :boolean { > if (audition.getGuard(noun) == guard) { > return true > } else { > throw(`$noun: expected $guard, got ${audition.getGuard(noun)}`) > } > } > } > } > } # value: ? def FinalSlot := .asType(); null ? def VarSlot := .asType(); null ? def GuardedSlot := .asType(); null ? {def x := 1; def doesNotGuardX implements CheckGuard["x", FinalSlot[int]] { to f() { return x } }} # problem: x: expected FinalSlot[int], got FinalSlot[any] ? {def x :int := 1; def guardsX implements CheckGuard["x", FinalSlot[int]] { to f() { return x } }} # value: ? {var x := 1; def varX implements CheckGuard["x", VarSlot] { to f() { return x } }} # value: x # ? {var x :int := 1; def guardsVarX implements CheckGuard["x", GuardedSlot[int]] { to f() { return x } }} x # value: XXX the above should work, but we don't have parameterization for GuardedSlot yet. Interim: ? {var x :int := 1; def guardedX implements CheckGuard["x", GuardedSlot] { to f() { return x } }} # value: ? def doesNotMentionX implements CheckGuard["x", FinalSlot[int]] {} # problem: "x" is not a free variable in "__main$doesNotMentionX" ? {def x {}; def objDefX implements CheckGuard["x", FinalSlot[any]] { to f() { return x } }} # value: ? {def x as approver {}; def asBoundX implements CheckGuard["x", FinalSlot[approver]] { to f() { return x } }} # value: ? {def &x := "foo"; def defaultSlotGuardX implements CheckGuard["x", any] { to f() { return x } }} # value: ? {def &x :(def g extends any {}) := "foo"; def slotGuardX implements CheckGuard["x", g] { to f() { return x } }} # value: ? def outerTestScope := safeScope.with("CheckGuard", CheckGuard) \ > .with("FinalSlot", FinalSlot) \ > .with("x", 1).withSlot("y", 2) > null ? e`def outerValueGuardX implements CheckGuard["x", any] { to f() { return x } }`.eval(outerTestScope) # value: ? e`def outerSlotGuardX implements CheckGuard["x", any] { to f() { return x } }`.eval(outerTestScope) # value: XXX should test scope with guards, but that isn't implemented yet. --- Audition guard, and usage example ? def gcStamp { to audit(_) :boolean { return true } } > def gcAuditor { > to audit(audition :EAudition) :boolean { > # XXX this will eventually need to be =~ (which isn't implemented yet) > if (audition.getObjectExpr().getPattern() == epatt`gcPass`) { > audition.ask(gcStamp) > } > return false > } > } # value: ? __auditedBy(gcStamp, def gcPass implements gcAuditor {}) # value: true ? __auditedBy(gcStamp, def gcFail implements gcAuditor {}) # value: false ? gcAuditor.audit(def gcFalseAudition { > to getObjectExpr() :any { return e`def gcPass {}` } > to ask(gcStolenStamp) :void { > def gcFalseApproved implements gcStolenStamp {} > require(!__auditedBy(gcStamp, gcFalseApproved), "shouldn't happen") > } > }) # problem: is not an E Audition --- check that audition can't be asked after the object is constructed ' ? def capturedAudition # value: ? def captureAuditor { > to audit(bind capturedAudition) :any { > print(capturedAudition) > return true > } > } # value: ? capturedAudition # value: ? def exampleObject implements captureAuditor { } # stdout: # value: ? capturedAudition # value: ? __auditedBy(approver, exampleObject) # value: false ? capturedAudition.ask(approver) # problem: is out of scope ? __auditedBy(approver, exampleObject) # value: false not actually related, but using a handy witness object ? help(capturedAudition) # value: interface "org.erights.e.elang.evm.Audition" { # /** The fully-qualified name of the object under audit. */ # to getFQName() # /** The ObjectExpr defining the object under audit. */ # to getObjectExpr() # /** Audits the object with the given auditor. XXX describe behavior upon false/throw returns from auditor */ # to ask(auditor) # /** Returns a guard which the named slot in the audited object's environment has passed. */ # to getGuard(noun :String) # } --- Old auditing protocol The original auditing protocol consisted of (objectExpr, witness), where a Witness is what is now called an Audition. The problem with it is that the auditor cannot know that the supplied witness matches the objectExpr, so if an auditor checks the objectExpr and then does witness.ask(SomeRubberStamp), it could be fooled into approving an object it should not. This could be avoided by each auditor by doing to audit(_, witness :EWitness) { witness.ask(def realAuditor { to audit(expr, witness) { ... } }) } but that would be silly. ? def oldAuditor { > to audit(objectExpr, witness) :any { > return objectExpr.getPattern() == epatt`oldAuditSample` > } > } # value: ? def oldAuditSample implements oldAuditor { > } # value: ? __auditedBy(oldAuditor, oldAuditSample) # value: true ? __auditedBy(oldAuditor, def oldFailure implements oldAuditor {}) # value: false ? def mixedAuditor { > to audit(objectExpr, witness) :any { > return false > } > to audit(audition) :any { > return true > } > } # value: ? __auditedBy(mixedAuditor, def priorityTest implements mixedAuditor {}) # value: true xxx specific test for objectExpr being actually the expression of the object under audit / what transformation stage (if any) it's at ' xxx auditors must be deep frozen?