Skip to content

Commit

Permalink
Add ES.TypeIsObject, ES.ToObject, and ES.IsCallable helpers.
Browse files Browse the repository at this point in the history
In the process found a little bug in the String.raw test suite; String.raw
should throw a TypeError if the `callSite` object passed does not have an
Object value for its `raw` field. (Checked against Jan 20, 2014 spec.)

Fixes #208.
  • Loading branch information
cscott committed Feb 12, 2014
1 parent f302c03 commit 86968ce
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 48 deletions.
105 changes: 58 additions & 47 deletions es6-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,28 @@
};

var ES = {
CheckObjectCoercible: function(x) {
if (x == null) throw new TypeError('Cannot call method on ' + x);
CheckObjectCoercible: function(x, optMessage) {
if (x == null)
throw new TypeError(optMessage || ('Cannot call method on ' + x));
return x;
},

TypeIsObject: function(x) {
// this is expensive when it returns false; use this function
// when you expect it to return true in the common case.
return x != null && Object(x) === x;
},

ToObject: function(o, optMessage) {
return Object(ES.CheckObjectCoercible(o, optMessage));
},

IsCallable: function(x) {
return typeof x === 'function' &&
// some versions of IE say that typeof /abc/ === 'function'
_toString.call(x) === '[object Function]';
},

ToInt32: function(x) {
return x >> 0;
},
Expand Down Expand Up @@ -282,9 +299,9 @@
raw: function() {
var callSite = arguments.length > 0 ? arguments[0] : undefined;
var substitutions = _slice.call(arguments, 1, arguments.length);
var cooked = Object(callSite);
var cooked = ES.ToObject(callSite, 'bad callSite');
var rawValue = cooked.raw;
var raw = Object(rawValue);
var raw = ES.ToObject(rawValue, 'bad raw value');
var len = Object.keys(raw).length;
var literalsegments = ES.ToUint32(len);
if (literalsegments === 0) {
Expand Down Expand Up @@ -413,27 +430,25 @@
var mapFn = arguments.length > 1 ? arguments[1] : undefined;
var thisArg = arguments.length > 2 ? arguments[2] : undefined;

if (iterable === null || iterable === undefined) {
throw new TypeError('Array.from: null or undefined are not valid values. Use []');
} else if (mapFn !== undefined && _toString.call(mapFn) !== '[object Function]') {
var list = ES.ToObject(iterable, 'bad iterable');
if (mapFn !== undefined && !ES.IsCallable(mapFn)) {
throw new TypeError('Array.from: when provided, the second argument must be a function');
}

var list = Object(iterable);
var usingIterator = ($iterator$ in list);
// does the spec really mean that Arrays should use ArrayIterator?
// https://bugs.ecmascript.org/show_bug.cgi?id=2416
//if (Array.isArray(list)) { usingIterator=false; }
var length = usingIterator ? 0 : ES.ToUint32(list.length);
var result = typeof this === 'function' ? Object(usingIterator ? new this() : new this(length)) : new Array(length);
var result = ES.IsCallable(this) ? Object(usingIterator ? new this() : new this(length)) : new Array(length);
var it = usingIterator ? list[$iterator$]() : null;
var value;

for (var i = 0; usingIterator || (i < length); i++) {
if (usingIterator) {
value = it.next();
if (value === null || typeof value !== 'object') {
throw new TypeError("Bad iterator");
if (!ES.TypeIsObject(value)) {
throw new TypeError('bad iterator');
}
if (value.done) {
length = i;
Expand Down Expand Up @@ -498,7 +513,7 @@

defineProperties(Array.prototype, {
copyWithin: function(target, start) {
var o = Object(this);
var o = ES.ToObject(this);
var len = Math.max(ES.toInteger(o.length), 0);
var to = target < 0 ? Math.max(len + target, 0) : Math.min(target, len);
var from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
Expand Down Expand Up @@ -537,10 +552,10 @@
},

find: function(predicate) {
var list = Object(this);
var list = ES.ToObject(this);
var length = ES.ToUint32(list.length);
if (length === 0) return undefined;
if (typeof predicate !== 'function') {
if (!ES.IsCallable(predicate)) {
throw new TypeError('Array#find: predicate must be a function');
}
var thisArg = arguments.length > 1 ? arguments[1] : undefined;
Expand All @@ -552,10 +567,10 @@
},

findIndex: function(predicate) {
var list = Object(this);
var list = ES.ToObject(this);
var length = ES.ToUint32(list.length);
if (length === 0) return -1;
if (typeof predicate !== 'function') {
if (!ES.IsCallable(predicate)) {
throw new TypeError('Array#findIndex: predicate must be a function');
}
var thisArg = arguments.length > 1 ? arguments[1] : undefined;
Expand Down Expand Up @@ -616,7 +631,7 @@

defineProperties(Number.prototype, {
clz: function() {
var number = Number.prototype.valueOf.call(this) >>> 0;
var number = ES.ToUint32(Number.prototype.valueOf.call(this));
if (number === 0) {
return 32;
}
Expand Down Expand Up @@ -677,11 +692,11 @@
var set;

var checkArgs = function(O, proto) {
if (typeof O !== 'object' || O === null) {
if (!ES.TypeIsObject(O)) {
throw new TypeError('cannot set prototype on a non-object');
}
if (typeof proto !== 'object') {
throw new TypeError('can only set prototype to an object or null');
if (!(proto===null || ES.TypeIsObject(proto))) {
throw new TypeError('can only set prototype to an object or null'+proto);
}
};

Expand Down Expand Up @@ -914,7 +929,7 @@
var Promise, Promise$prototype;

ES.IsPromise = function(promise) {
if (promise === null || typeof promise !== 'object') {
if (!ES.TypeIsObject(promise)) {
return false;
}
if (!promise._promiseConstructor) {
Expand All @@ -931,7 +946,7 @@
// "PromiseCapability" in the spec is what most promise implementations
// call a "deferred".
var PromiseCapability = function(C) {
if (typeof C !== 'function') {
if (!ES.IsCallable(C)) {
throw new TypeError('bad promise constructor');
}
var capability = this;
Expand All @@ -940,25 +955,24 @@
capability.reject = reject;
};
// this is es6 'CreateFromConstructor'
if (typeof C['@@create'] === 'function') {
if (ES.IsCallable(C['@@create'])) {
capability.promise = C['@@create']();
} else {
capability.promise = Object.create(C.prototype || null);
}
var cr = C.call(capability.promise, resolver);
if (typeof capability.resolve !== 'function' ||
typeof capability.reject !== 'function') {
if (!(ES.IsCallable(capability.resolve) &&
ES.IsCallable(capability.reject))) {
throw new TypeError('bad promise constructor');
}
if ((typeof cr === 'object' || typeof cr === 'function') &&
cr !== capability.promise) {
if (ES.TypeIsObject(cr) && cr !== capability.promise) {
throw new TypeError('bad promise constructor');
}
};

// find an appropriate setImmediate-alike
var setTimeout = globals.setTimeout;
var enqueue = typeof globals.setImmediate === 'function' ?
var enqueue = ES.IsCallable(globals.setImmediate) ?
globals.setImmediate.bind(globals) :
typeof process === 'object' && process.nextTick ? process.nextTick :
function(task) { setTimeout(task, 0); }; // fallback
Expand Down Expand Up @@ -989,15 +1003,14 @@
};

var updatePromiseFromPotentialThenable = function(x, capability) {
if (x === null || // is Type(x) not Object?
(typeof x !== 'function' && typeof x !== 'object')) {
if (!ES.TypeIsObject(x)) {
return false;
}
var resolve = capability.resolve;
var reject = capability.reject;
try {
var then = x.then; // only one invocation of accessor
if (typeof then !== 'function') { return false; }
if (!ES.IsCallable(then)) { return false; }
then.call(x, resolve, reject);
} catch(e) {
reject(e);
Expand All @@ -1023,16 +1036,16 @@

Promise = function(resolver) {
var promise = this;
if (promise === null || typeof promise !== 'object') {
if (!ES.TypeIsObject(promise)) {
throw new TypeError('bad promise');
}
// es5 approximation to es6 subclass semantics: in es6, 'new Promise'
// would invoke Promise.@@create to initialize the new object.
// In es5 we just get the plain object. So if we detect an
// uninitialized promise, invoke promise.contructor.@@create
if (!promise._promiseConstructor) {
if (typeof promise.constructor !== 'function' ||
typeof promise.constructor['@@create'] !== 'function') {
if (!(ES.IsCallable(promise.constructor) &&
ES.IsCallable(promise.constructor['@@create']))) {
throw new TypeError('bad promise constructor');
}
promise = promise.constructor['@@create'](promise);
Expand All @@ -1041,7 +1054,7 @@
throw new TypeError('promise already initialized');
}
// see https://bugs.ecmascript.org/show_bug.cgi?id=2482
if (typeof resolver !== 'function') {
if (!ES.IsCallable(resolver)) {
throw new TypeError('not a valid resolver');
}
promise._status = 'unresolved';
Expand Down Expand Up @@ -1113,17 +1126,16 @@
var resolve = capability.resolve;
var reject = capability.reject;
try {
var it = typeof iterable === 'object' && iterable !== null &&
typeof iterable[$iterator$] === 'function' &&
var it = ES.TypeIsObject(iterable) &&
ES.IsCallable(iterable[$iterator$]) &&
iterable[$iterator$]();
if (it === null || typeof it !== 'object' ||
typeof it.next !== 'function') {
if (!(ES.TypeIsObject(it) && ES.IsCallable(it.next))) {
throw new TypeError('bad iterable');
}
var values = [], remaining = { count: 1 };
for (var index = 0; ; index++) {
var next = it.next();
if (next === null || typeof next !== 'object') {
if (!ES.TypeIsObject(next)) {
throw new TypeError('bad iterator');
}
if (next.done) {
Expand Down Expand Up @@ -1163,16 +1175,15 @@
var resolve = capability.resolve;
var reject = capability.reject;
try {
var it = typeof iterable === 'object' && iterable !== null &&
typeof iterable[$iterator$] === 'function' &&
var it = ES.TypeIsObject(iterable) &&
ES.IsCallable(iterable[$iterator$]) &&
iterable[$iterator$]();
if (it === null || typeof it !== 'object' ||
typeof it.next !== 'function') {
if (!(ES.TypeIsObject(it) && ES.IsCallable(it.next))) {
throw new TypeError('bad iterable');
}
while (true) {
var next = it.next();
if (next === null || typeof next !== 'object') {
if (!ES.TypeIsObject(next)) {
throw new TypeError('bad iterator');
}
if (next.done) {
Expand Down Expand Up @@ -1215,10 +1226,10 @@
if (!ES.IsPromise(promise)) { throw new TypeError('not a promise'); }
var C = this._promiseConstructor;
var capability = new PromiseCapability(C);
if (typeof onRejected !== 'function') {
if (!ES.IsCallable(onRejected)) {
onRejected = function(e) { throw e; };
}
if (typeof onFulfilled !== 'function') {
if (!ES.IsCallable(onFulfilled)) {
onFulfilled = function(x) { return x; };
}
var resolutionHandler =
Expand Down
2 changes: 1 addition & 1 deletion test/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ var runStringTests = function() {
});

it('String.raw Empty objects', function() {
var callSite = {};
var callSite = {raw:{}};
expect(String.raw(callSite,'{total}','{total * 1.01}')).to.eql('');
expect(String.raw(callSite)).to.eql('');
});
Expand Down

0 comments on commit 86968ce

Please sign in to comment.