tc39 / ecma262

Status, process, and documents for ECMA-262
https://tc39.es/ecma262/
Other
15.06k stars 1.29k forks source link

Creating a %TypedArray% instance in a different realm #418

Closed annevk closed 7 years ago

annevk commented 8 years ago

For structured cloning we need a way to create a %TypedArray% instance in a different realm based on an existing %TypedArray% instance.

I can define my own abstract operation that does "Let defaultConstructor be the intrinsic object listed in column one of Table 50 for the value of exemplar's [[TypedArrayName]] internal slot." (from TypedArraySpeciesCreate, which apart from @@species does mostly what I want I think) in a different realm, but I wonder how stable that reference to Table 50 will be over time.

It's also not entirely clear to me if that constructor is guaranteed to be the built-in one or can be from userland doing something entirely different.

annevk commented 8 years ago

I think this is roughly the algorithm I need (coupled with cloning the various slots and passing those to TypedArrayCreate):

  1. Let constructor be the intrinsic object listed in column one of Table 50 for the value of input's [[TypedArrayName]] internal slot in targetRealm.
  2. Return ? TypedArrayCreate(constructor, « ... »).
domenic commented 8 years ago

I haven't looked at https://github.com/tc39/ecma262/pull/410 but based on the name it might impact things.

It's also not entirely clear to me if that constructor is guaranteed to be the built-in one or can be from userland doing something entirely different.

The defining characteristics of intrinsics is that they cannot be changed in userland.

annevk commented 8 years ago

That PR does not seem to impact this. I'm going with something homegrown for now, but would love input if we could factor out the Table 50 reference somehow.

domenic commented 8 years ago

I guess you could say "The TypedArray Constructors table" and link to #table-49. (Note: the _s in the caption are a bug in the current ES spec output that we should expect to eventually be fixed.)

annevk commented 7 years ago

This is the language we have now in HTML:

[S]et value to a new typed array object, using the constructor given by input.[[Constructor]], whose [[ViewedArrayBuffer]] internal slot value is deserializedArrayBuffer, whose [[TypedArrayName]] internal slot value is input.[[Constructor]], whose [[ByteLength]] internal slot value is serialized.[[ByteLength]], whose [[ByteOffset]] internal slot value is serialized.[[ByteOffset]], and whose [[ArrayLength]] internal slot value is serialized.[[ArrayLength]]

Which matches language we also use for other objects:

Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List.

If TC39 considers that precise enough this issue can be closed.

annevk commented 7 years ago

I guess we need to ad "in targetRealm" at the very least. Will fix that.

allenwb commented 7 years ago

The ECMAScript spec. has never defined an explicit directed association from ordinary objects to realms because it had so semantics that required such an association. The closest thing to such an association is the selection of an intrinsic object (associated with some realm) that is to be the initial value of a new object's [[Prototype]] internal slot. Most (all?) object creation algorithms in ECMA-262 select the appropriate intrinsics based upon the "current realm".

Which matches language we also use for other objects: Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List.

The above language is not precise enough, because the meaning of the phrase "a new X object in targetRealm" not precisely defined.

It seems to me, that what you need to precisely specify this is something like a realm parameterized version of OrdinaryObjectCreate. For example:

   CreateOrdinaryObjectFromRealmIntrinsicConstructor(realm, instrinsicName [,internalSlotList])

where intrinsicName is the Intrinsic Name of a constructor as listed in Table 7. Note that unlike OrdinaryCreateFromConstructor, an intrinsicDefaultProto parameter is not needed. The reason is that the prototype property of intrinsic constructors is always non-writable'non-configurable so we know that we can get the initial [[Prototype]] value from the constructor.

The specification of of this new abstract operation would be just like OrdinaryObjectCreate except that it's first step is would retrieve the appropriate intrinsic constructor from realm.[[Intrinsics]]. In step 2, null can be used in place of intrinsicDefaultProto.

Using this new abstract operation, the above quoted structured clone language would be replaced by something like:

  1. Let value be ?CreateOrdinaryObjecgtFromRealmIntrinsic(targetRealm,"%Map", « [[MapData]] »).
  2. Set value.[[MapData]] to a new empty List.

Whether this new abstract operation is part of the HTML structured clone spec or part of ECMA-262 is something for the appropriate editors to negotiate. If it was up to me I would place in the clone spec since it isn't currently needed by ECMA-262.

I haven't worked through the details for typed arrays, but the basic idea should be the same except that the intrinsicName is obtained from [[TypedArrayName]].

domenic commented 7 years ago

Yes, something like that would be reasonable for formalizing things, but I don't think we want to duplicate and keep in sync the list of internal slots. Also we don't want to have to duplicate and keep in sync the default values for the internal slots, which are currently set in the constructors. (E.g. an empty list for [[MapData]]; [[TypedArrayName]] for typed arrays.)

Currently the only ES type for which this is easy is arrays, which have ArrayCreate(length[, proto]), which we use. (And I guess ObjectCreate, which we don't use, but could.) "Properly" creating the other built-ins all involve a lot of boilerplate and error-prone duplication.

As such we went for "a new X object in targetRealm" as a signifier for "using the intrinsic from targetRealm and the defaults for the usual internal slots"; so far we have not had any interoperability issues or implementer questions.

It would be great to make it more precise if the ES spec was willing to expose the right hooks, certainly.

annevk commented 7 years ago

@allenwb I suggest that for this issue we focus exclusively on the prototype implications of the realm. #573 is about the mapping from ordinary objects to a realm, if there is to be such a thing.

allenwb commented 7 years ago

@domenic Invoking the constructor is probably the easiest way to avoid duplicating the work to establish the internal invariants of the various objects.

You could define "a new %Foo% object for targetRealm [with args]" as meaning:

  1. Let ctor be the %Foo% intrinsic constructor from targetRealm.[[Intrinsics]].
  2. If args was not included, let args be « ».
  3. return ? Construct(ctor, args)

The parameters to the constructor are the only tricky parts. For the structured clone use case an empty argument list exactly what you need. But for others, such as typed arrays you would need to supply an actual argument (for example, the length of the array).

domenic commented 7 years ago

Oh, thanks @allenwb. I'm not sure why that didn't occur to us... it seems perfectly serviceable. Maybe I was confused by all the extra steps that many constructors contain? But skimming through, they seem to be a non-issue when you pass an empty arguments list. I very much appreciate you walking us to the right conclusion here.

Let me open an issue on HTML to rigorize our definitions and then I'll close this one.

annevk commented 7 years ago

(@allenwb, by the way, wanted to thank you off-topic and wasn't quite sure where to mention it. We have revised structured cloning to be separate serialization and deserialization algorithms (object <> Record structure) as you've suggested ages ago and it's great.)