endojs / Jessie

Tiny subset of JavaScript for ocap-safe universal mobile code
Apache License 2.0
281 stars 16 forks source link

Escaping static context #16

Closed michaelfig closed 2 years ago

michaelfig commented 5 years ago

Hi, I have a request for clarification:

To enable sound static reasoning, in Jessie all objects made by literal expressions (object literals, array literals, the many forms of function literals) must be tamper-proofed with def before they can be aliased or escape from their static context of origin.

I understand "aliasing" to mean assignment to another object (array element, variable, or object property). Clearly, some ways of "escape" are being used in a return or throw statement. Do these concepts include being closed over by a subfunction?

So is the following legal Jessie?

function makeRunningTotal() {
  const totalArr = [0];
  function add(addend) {
    totalArr[0] += addend;
    return def(totalArr[0]);
  }
  return def(add);
}

I don't read anything in the current document that directly says all mutable state is to be prevented, but I am unclear if the totalArr in the above can be used as a mutable state container, or if it needs to be a direct variable reference. Or further, if this pattern of mutation is simply not allowed (i.e. closure is considered a form of escape from the static context).

Thanks: I'm working up to an understanding of what Jessie really means, Michael.

michaelfig commented 5 years ago

Pondering this further, if closure is considered aliasing (and if def(obj) makes all of obj's properties read-only as I expect), then it looks like Jessie data structures are guaranteed not to be cyclical. Interestingly (to me), a valid implementation need only reference-count its objects... full-fledged garbage collection is not necessary!

Something makes me think Jessie was designed this way.

Thanks, Michael.

michaelfig commented 5 years ago

Oh, okay now I think I got it.

Cycles can exist due to Map, Set and friends, which are mutable collections, even if literals cannot be used as such.

So this "API surface" concept exists because the only use of escaping object, function, and array literals is in defining APIs. Collections are a separate concern from that, and already have a defensive API.

Pardon my ramblings... Still chewing on this. Michael.

erights commented 5 years ago

Pardon my ramblings... Still chewing on this.

If you don't mind, I find the sequence of aha's informative. Please keep rambling!

JavaScript has several places where mutable state lurks, primarily:

Jessie has the first two (e.g., let, Map). Jessie mostly omits mutable properties because properties also serve as API surface, which the clients of an API should not be able to corrupt.

I say "mostly" because Jessie does allow mutable property updates for initialization only, of objects that are not yet otherwise reachable. That's the intention anyway. The exact rules needed to realize this have not yet been precisely worked out, so these questions are timely.

I am confused about your example:

function makeRunningTotal() {
  const totalArr = [0];
  function add(addend) {
    totalArr[0] += addend;
    return def(totalArr[0]);
  }
  return def(add);
}

Since totalArr[0] === 0 before add is called and is only updated by +=, it will always be a primitive value, so the def around def(totalArr[0]) is pointless. In any case, that def call is not relevant to Jessie's static rules. Did you mean return def(totalArr); on that line?

michaelfig commented 5 years ago

As for my example, I was trying to discern some conservative "no escape or aliasing without def" rules that don't require full data typing or semantic analysis, but it was all rather hand-wavy.