Oct 1 meeting notes

Waldemar Horwat waldemar at google.com
Fri Oct 1 16:38:28 PDT 2010


Here are my raw notes from our second day.

    Waldemar


Catch guard proposal, resurrected from 1998.  Well-liked all around.
Proposed to move into Harmony if there are no objections by the next
meeting.

MarkM speculating: Could the if syntax be extended to pattern
matching?  Not easily.  Pattern matching doesn't have a concept of
failing; also, semicolon insertion would change the meaning of
existing programs.

Unique names
Question:  In what ways does this differ from weak tables?
- A client can look up a property p using a[p] without knowing whether
p is a traditional string or a unique name.

Debate about private names leaking via proxies.  It's trivial to get
at private names by passing a proxy to code that accesses them.

It's not clear what private names are trying to do well.  If they want
to provide privacy, they can't be visible to proxies.  If they want to
provide extension without collision (i.e. namespacing), they should be
visible to Object.keys, enumeration, etc.

Allen:  private syntax is the primary usage of this proposal.  Debate
over whether there should be two independent concepts of scope or one
with a flag.

Brendan: list of questions:
1. Private for sure?
  No: unique name weak encapsulation
  Yes: Private name strong encapsulation
2. Visible via for-in?
  Object.defineProperty
  Assignment via private
3. Visible via Object.keys/getOwnPropertyNames?
4. Proxy: visible as property name?
  Yes: Leaks names.  Membranes can be done using proxies.
  No: Doesn't leak names.  Membranes can be done using proxies using
the technique below.

Philosophical debate about weak encapsulation:
Allen:  Weak encapsulation is good enough for most use cases
Waldemar:  Encapsulation that almost works is worse than either strong
encapsulation or no encapsulation.  The reason is that folks will code
until the code appears to work and then ship with vulnerabilities.
This leads to most of the web security attacks.
Allen:  Functions and objects are roughly dual mechanisms.  Why do you
need both?  Functions provide strong encapsulation; objects provide
weak encapsulation.
Waldemar:  Why does executability have to imply the need for strong
encapsulation and vice versa?

MarkM: Desugar
  private n;
  x.n = ...
  x.n()
into:
  const n = SoftField();
  n.set(x, ...);
  n.get(x).call(x);

Analogously, desugar
  unique n;
  x.n = ...  // Maybe make it nonenumerable by default?
  x.n()
into:
  const n = Name();
  x[n] = ...
  x[n]()

It's useful to view soft-fields as a kind of property names, but then
have to address what happens on preventExtensions, freeze, etc.  If
you think of these private fields as properties then they should be
subject to freezing and such.

A proxy having the right to get at an object's private field names is
equivalent to a proxy having the right to obtain all weak maps for
which the object is the key.  The security implications are the same.
If a proxy can do a faithful membrane without one of these rights, it
can do a faithful membrane without the other of these rights.  If a
proxy has no rights to get at an object's private field names, the
membrane will still work as follows:

proxy[name] does not trap
object[proxy] calls a trap on the proxy
When a name object itself (not merely an object merely containing
privately named fields) is passed through a membrane, the membrane
wraps the name in a proxy p and then uses object[p] traps to maintain
the membrane.  Private names would be used completely on either the
wet or dry side of the membrane, so they'd stay private.  Public names
that cross the membrane get proxied by the membrane.


Binary data

Given
const Point2D = new StructType({ x: uint32, y: uint32 });
const Color = new StructType({ r: uint8, g: uint8, b: uint8 });
const Pixel = new StructType({ point: Point2D, color: Color });
p = new Pixel({point: {x: 3, y:8}, color:{r: 100, g: 50, b: 0}});

p.point.y returns the primitive integer 8
p.point returns an alias (lvalue) to the Point2D substructure (block)
of p.  Modifying data visible via such aliasing is visible to all
aliases.

Debate over whether values such as 3.8 or -1 should be assignable to a
uint32 field.  Current proposal says no and provices a uint32(x)
coercer method: uint32(-1) = 0xFFFFFFFF.

Does calling p.point twice return block references that are === to
each other?  Dave says he's agnostic.  Produced a bit of debate.

Special updateRef feature to avoid creating lots of block wrapper
objects (the blocks themselves are aliased so are less of an issue):

var T = new StructType(...);
var A = new ArrayType(T, 1000000);
t = T.ref();  // allocate a homeless struct object
for (i = 0; i < 1000000; i++) {
  t.updateRef(a, i);  // no allocation of struct wrapper
}

Given this feature, it's no longer possible to be agnostic on ===.
One can't make block references a === b if and only if a and b are
aliases of the same block.

One way out would be to have T.ref() return a separate updatable block
type, while p.point or a[3] would produce nonupdatable block type
objects.  Nonupdatable block type objects would have the above natural
definition of === and not support updateRef.

The counterpoint to the above is the creation of two kinds of block
types, similar to each other.

Debated desire for having blocks inherit from Array.prototype.
Debated desire for having some Array generic methods on blocks.

Endianness is invisible to users except when using blocks for file
i/o, in which case the routines doing the i/o will take a parameter
specifying the endianness of the file and convert as needed.

How does one represent variable-length strings?

Why no character types for fixed-size arrays?  Punted from proposal
for time reasons for now, but are desirable.

When using this for file i/o, handling of variable-length strings will
become important.  How to do this?  No specific design, but some ideas
are:

Given struct {x: uint32, y: uint32, z: uint32, name: String}, an idea would be:
string = new Layout({
  length: uint16,
  contents: function(me) {return new ArrayType(uint8, me.length)}
});
color = new Layout({
  x: uint32,
  y: uint32,
  z: uint32,
  name: string
})

How to store these in memory?  One can't have a pointer to a string
inside a block.

int64's:  Open issue.  Reference semantics are annoying, but what's a
realistic alternative?
int128's?  Those come up increasingly often in SSE programming.

Waldemar:  Reify uint64's as 4-char big endian strings and uint128's
as 8-char big endian strings.  A significant advantage is that these
would have value semantics, and ==, ===, <, <=, etc. would all work
correctly.  Would need to call methods to do arithmetic or signed
inequality comparisons on them.

More debate about block ===.  Reached consensus that two
(nonupdatable) block references a and b should be === if and only if
they are the same type and alias to the same data.  Updatable block
references will have separate rules.


Discussion on the use of put vs. defineOwnProperty in the ES5 spec as
they relate to proxies and library classes.  Efficiency concerns with
creating descriptors for defineOwnProperty.  Most array classes turn
out to use put for objects which can be proxied; the places where they
use defineOwnProperties tend not to be proxyable because they're done
on locally created Array objects.

Can a proxied [[construct]] return a primitive?  Yes.


Brendan: How do we do feature detection and top-level patching in ES-harmony?
if (!window.fooQuery)
  var fooQuery = ....


let clarifications:
let x and var x at same scope:  error
let x at top scope of a function or catch block with parameter x:  error
let x at script top leve:  ok
module M {
  export let x;
}
var binding that would hoist across a let binding:  error

let has a read barrier before the let statement gets executed:
{
  let x = 1;
  if (x) {
    alert(x);  // Read barrier error here because inner x hasn't been
initialized yet
    let x = 2;
  }
}

{
  x = 42;  // Write barrier error because x hasn't been initialized yet
  print(x);
  let x = 99;
}
Questions about whether
  let x;
should be treated as:
  let x = undefined;
so it has a barrier too.  Could take either position on this one.

Brendan: What if let and const don't hoist at all (as in C++ scoping)?
 This was Waldemar's position before the grand ES4 scoping compromise
a few years ago.

Mutually recursive example (wouldn't work with const without some extension):
let f, g;
f = function() {... g ...};
g = function() {... f ...};

Another alternative for mutual recursion:
let [f, g] = [function() {... g ...}, function() {... f ...}];

This assumes that f and g are visible in the initializer, which would
bring the barrier issues back.  Read barrier would be needed.  Debate
about whether write barrier would be needed, even if we have
expressions for typed/guarded let.


Discussion of pragma syntax:
use bignums;
use harmony;
use modules {A, B, C};


More information about the es-discuss mailing list