Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map/Set iterator #387

Closed
Xotic750 opened this issue Dec 16, 2015 · 10 comments
Closed

Map/Set iterator #387

Xotic750 opened this issue Dec 16, 2015 · 10 comments

Comments

@Xotic750
Copy link
Contributor

I am detecting MapIterator and SetIterator for my inpect-x module. On native ES6 Map and Set, the following code will alert true and TypeError: Method Map Iterator.prototype.next called on incompatible receiver.

var m = new Map();
var im = m.values();
var nm = im.next;
try {
  alert(nm.call(new Map([
    [1, true]
  ]).values()).value);
} catch (e) {
  alert(e);
}
try {
  alert(nm.call(new Set([true]).values()).value);
} catch (e) {
  alert(e);
}

http://jsfiddle.net/Xotic750/xw2Lpbwz/

But on es6-shim iterators it alerts true and true.

(I think it is the shim and not the native versions, but I have not tested thoroughly yet)

Safari8, FF26, IE10, node0.10 and 0.8 are examples.

I just did a toString of the next function and it is the shim version.

@ljharb
Copy link
Collaborator

ljharb commented Dec 16, 2015

You'd need to do Function.toString.call to get the actual source of the function, just in case - the es6-shim definitely modifies the toString of many of its shims.

@Xotic750
Copy link
Contributor Author

Ok, but I didn't think that node 0.10 and 0.8 had native Map/Set.

@ljharb
Copy link
Collaborator

ljharb commented Dec 16, 2015

They don't :-) that comes in 0.12.

@Xotic750
Copy link
Contributor Author

cscott added a commit to cscott/es6-shim that referenced this issue Dec 16, 2015
@cscott
Copy link
Collaborator

cscott commented Dec 16, 2015

The code in question seems a very strange way to detect the presence of MapIterator and SetIterator. But regardless, #391 contains a fix.

@Xotic750
Copy link
Contributor Author

Thanks, I haven't tested it yet. The code I posted was just to demonstrate what I was seeing. The actual code is more like

var SET = typeof Set === 'function' && isSet(new Set()) && Set;
var testSet = SET && new SET(['SetSentinel']);
var sValues = SET && SET.prototype.values;

function isSetIterator(value) {
  if (!SET || !isObjectLike(value) || !ES.IsCallable(value.next)) {
    return false;
  }
  try {
    return ES.Call(
      value.next,
      ES.Call(sValues, testSet)
    ).value === 'SetSentinel';
  } catch (ignore) {}
  return false;
}

It's fairly expensive, I know, but accuracy is more important than performance for me and I couldn't figure out a better cross environment/realm way of doing it?

@cscott
Copy link
Collaborator

cscott commented Dec 17, 2015

This seems like it would be sufficient:

var SetIteratorProto = Object.getPrototypeOf(new Set().values());
var isSetIterator(value) { return Object.getPrototypeOf(value) === SetIteratorProto; }

@Xotic750
Copy link
Contributor Author

Does not work cross frame.

function getXSet() {
  var iframe = document.createElement('iframe');
  var parent = document.body || document.documentElement;
  var set;
  iframe.style.display = 'none';
  parent.appendChild(iframe);
  iframe.src = 'javascript:';
  set = iframe.contentWindow.Set;
  parent.removeChild(iframe);
  iframe = null;
  return set;
};

var XSet = getXSet();

var SetIteratorProto = Object.getPrototypeOf(new Set().values());

function isSetIterator1(value) {
  return Object.getPrototypeOf(value) === SetIteratorProto;
}

var testSet = new Set(['SetSentinel']);
var sValues = Set.prototype.values;

function isSetIterator2(value) {
  if (typeof value !== 'object' || typeof value.next !== 'function') {
    return false;
  }
  try {
    return value.next.call(sValues.call(testSet)).value === 'SetSentinel';
  } catch (ignore) {}
  return false;
}

var setIt1 = new Set().values();
var setIt2 = new XSet().values();

console.log(setIt1, setIt2);
console.log(isSetIterator1(setIt1), isSetIterator1(setIt2));
console.log(isSetIterator2(setIt1), isSetIterator2(setIt2));

https://jsfiddle.net/Xotic750/prvLzvm2/

@ljharb
Copy link
Collaborator

ljharb commented Dec 17, 2015

@Xotic750 there's simply not enough machinery on non-globally-exposed items, like GeneratorFunction, or IteratorPrototype, etc, for you to determine them cross-realm. They don't have methods that pivot on internal slots nor is toString reliable.

@Xotic750
Copy link
Contributor Author

Is there a situation where isSetIterator2 gets it wrong (I guess something could be fabricated to make it fail, but you'd really be wanting to), I haven't found one yet?
Node uses some internal machinery for its inspect module, but of course that is not available cross-browser for my inspect-x. This was the best that I could come up with, and it has passed every test that I can throw at it, so far (until adding es6-shim). Otherwise I may as well go back to toString.call(value)==="[object Set Iterator]", which also works cross-frame on all native implementations that I have tested, but es6-shim will also fail, and it could be a modified value. Of course I could leave the detection and tests out completely, but I'd like to port it as faithfully as I can and would like it to work with es6-shim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants