Re: E patches for review: ELoader, seedVat

Thomas Leonard <tal-v5nx5w6akNyLE8xUarVfuPLx9OUvmyODWmv/[email protected]>
Newsgroups gmane.comp.lang.e.general
Organization IT Innovation
Message-ID <[email protected]>
On Fri, 2010-05-21 at 07:25 -0400, Kevin Reid wrote:
> On May 21, 2010, at 4:59, Thomas Leonard wrote:
> 
> >> 1. Envs also have the distinction which prohibits binding a noun  
> >> twice
> >> within a single scope, and is controlled by nestOuter(). This is data
> >> a ConstMap does not have.
> >
> > Is this useful when calling eval() from E?
> 
> The intent is that it should be possible to write an evaluator using  
> Env (Scope) as the environment type. This is not possible if Env does  
> not preserve this information.
[...]
> "Scope" is an incorrect and deprecated name. It is to be renamed  
> to "Env". There is no distinction between them.

Internally, I imagine an interpreter would do something like:

def interpret(code, env :Map) {
  def [transformedE, layout, nLocals] := transform(code, env)
  def scope := makeScope(env, nLocals)
  return subEval(transformedE, scope)
}

The scope includes things like evalContext.locals[], which are needed by
the interpreter.

On the other hand, another evaluator might work by generating JVM
byte-code. In that case, no Scope object is created and there is no
locals[] array.

Currently, EExpr.eval does something like:

 public Object eval(Scope scope) {
   ScopeLayout oldLayout = scope.getScopeLayout();
   Object[] triple = transform(this, oldLayout);
   ...
   Scope newScope = scope.update(nLocals);
   ...
 }

Here, the Scope type fills both roles ("scope" and "newScope"). Some
safej rules prevent E programs from accessing "internal" details (e.g.
locals[]). This means you can't write the interpreter in E (using
Scope), because E can't access these bits even when it needs them.

If they were separate types ("Map env/Scope scope" rather than "Scope
scope/Scope newScope"), the safej restrictions could go away and normal
capability rules would apply (if you have a Scope, you can call all its
methods, whether you're E or Java).

> >> Thomas, would you be interested in working on implementing guard- 
> >> based
> >> auditing in E-on-Java? It would then be straightforward to add the
> >> DeepFrozen auditor for E code to E-on-Java, which enables a variety  
> >> of
> >> useful possibilities.
> >
> > I'm a bit confused about DeepFrozen. For example, could this be
> > DeepFrozen?

> The correct syntax is
> 
> def requireOne implements DeepFrozen {
>     to run(x) {
>         require(x == 1)
>     }
> }
> 
> > It seems not, because it mutates &require:
> >
> > ? (&require).isFinal()
> > # value: false
> > ? requireOne(1)
> > ? (&require).isFinal()
> > # value: true
> >
> > Can any useful code be DeepFrozen?
> 
> This is a mistake in the implementation of safeEnv (safeScope), not in  
> DeepFrozen. It should not be possible to observe whether a lazy eval  
> slot has been forced yet, because otherwise it leaks information among  
> users of a given safeEnv when it should not (regardless of the  
> existence of DeepFrozen).

Good point. Still, it *is* mutating the slot, even if you can't see it.
Is that a problem? BootRefHandler does this:

 if (Ref.isDeepPassByCopy(arg) /* implicit "and is thread-safe" */) {
   return arg;
 }

An E object which forces a LazyEvalSlot (even a DeepFrozen one like
require) still isn't thread-safe.

> > I'm also confused about bindings. ... Is this so that auditors don't  
> > get free access to all the values?
> 
> Yes. Auditors should be able to analyze the behavior of your code, but  
> not use the authority it bears.
> 
> > If so, why not just subclass FinalSlot, e.g.
> >
> >  class AuditableFinalSlot extends FinalSlot implements AuditableSlot
> >
> > Then only give auditors auditable slots to look at (which would  
> > include
> > everything in safeScope)?
> 
> Because this would prevent auditing from working at all on custom  
> slots unless they all carefully implemented AuditableSlot and were  
> audited to do so correctly -- and what does 'correctly' mean for a  
> sufficiently wacky slot? The binding system leaves it up to the  
> auditor to determine what an acceptable slot is.
> 
> (On the other hand, the binding system does make some annoying  
> complications, like &&foo, which are the part I like least about GBA  
> as it stands now (which is entirely my own design); so if you could  
> expand on this proposal and show it works better in practice, I'd be  
> interested to hear it.)

OK, so the problem is:

- we want to know the guard (reliably)
- we want to allow custom slots
- we don't trust custom slots to enforce their claimed guard

Perhaps we could wrap wacky slots in a known wrapper? e.g.

def &myVar := makeGuardedSlot(myWackySlot)

Where makeGuardedSlot would call myWackySlot.getGuard and store a copy
of the guard. GuardedSlot would check the value against the guard every
time you called get().

An auditor could see that the slot was a GuardedSlot, and thus trust it
to ensure the values it returned always conformed to the guard. But for
the common case (FinalSlot) it would work fine anyway without wrapping.

> >> Perhaps, instead of using the persistence sealer, use the unsafe
> >> loader, which presumably can (ought to be able to, anyway) uncall
> >> makeTraceln?
> >
> > It can, but I don't want to let people get makeTraceln from <this>,  
> > e.g.
> > with:
> >
> > def loader {
> >    to __optUncall() {
> >       return [makeLoader, "run", [sourceDir, envExtras, fqnPrefix]]
> >   }
> > }
> >
> > You could recover makeLoader with:
> >
> >  def makeLoader := <this>.__optUncall()[0]
> >
> > You can then use makeLoader to create loaders that log with any  
> > fqname,
> > or uncall it to get makeTraceln. So, I think this needs to be sealed
> > somehow.
> 
> Hold on. I'm not sure how what I proposed makes a difference here.
> 
> Could you explain the entire object graph leading up to makeTraceln?

<this> is an ELoader, created by makeELoader. It holds a reference to
makeTraceln and creates traceln instances by combining a prefix with the
filename. Like this:

? def makeELoader := <elang:interp.ELoaderAuthor>(makeTraceln)
? def <libfoo> := makeELoader(dir, [].asMap(), "libfoo$")

Any emaker loaded by libfoo gets a traceln that logs things from "libfoo
$...".

Having <libfoo> should not allow unrestricted access to makeTraceln.

> I suspect a violation of the principle that any object with private  
> state must use an amplified uncall rather than __optUncall/0.

Exactly. The question is whether to amplify using the persistence sealer
or something else.


-- 
Dr Thomas Leonard
IT Innovation Centre
2 Venture Road
Southampton
Hampshire SO16 7NP

Tel: +44 0 23 8076 0834
Fax: +44 0 23 8076 0833
mailto:tal-v5nx5w6akNyLE8xUarVfuPLx9OUvmyODWmv/[email protected]
http://www.it-innovation.soton.ac.uk
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.