<commit_before>/**
 * The default date format for dc.js
 * @name dateFormat
 * @memberof dc
 * @type {Function}
 * @default d3.timeFormat('%m/%d/%Y')
 */
dc.dateFormat = d3.timeFormat('%m/%d/%Y');

/**
 * @namespace printers
 * @memberof dc
 * @type {{}}
 */
dc.printers = {};

/**
 * Converts a list of filters into a readable string.
 * @method filters
 * @memberof dc.printers
 * @param {Array<dc.filters>} filters
 * @returns {String}
 */
dc.printers.filters = function (filters) {
    var s = '';

    for (var i = 0; i < filters.length; ++i) {
        if (i > 0) {
            s += ', ';
        }
        s += dc.printers.filter(filters[i]);
    }

    return s;
};

/**
 * Converts a filter into a readable string.
 * @method filter
 * @memberof dc.printers
 * @param {dc.filters|any|Array<any>} filter
 * @returns {String}
 */
dc.printers.filter = function (filter) {
    var s = '';

    if (typeof filter !== 'undefined' && filter !== null) {
        if (filter instanceof Array) {
            if (filter.length >= 2) {
                s = '[' + filter.map(function (e) {
                    return dc.utils.printSingleValue(e);
                }).join(' -> ') + ']';
            } else if (filter.length >= 1) {
                s = dc.utils.printSingleValue(filter[0]);
            }
        } else {
            s = dc.utils.printSingleValue(filter);
        }
    }

    return s;
};

/**
 * Returns a function that given a string property name, can be used to pluck the property off an object.  A function
 * can be passed as the second argument to also alter the data being returned.
 *
 * This can be a useful shorthand method to create accessor functions.
 * @method pluck
 * @memberof dc
 * @example
 * var xPluck = dc.pluck('x');
 * var objA = {x: 1};
 * xPluck(objA) // 1
 * @example
 * var xPosition = dc.pluck('x', function (x, i) {
 *     // `this` is the original datum,
 *     // `x` is the x property of the datum,
 *     // `i` is the position in the array
 *     return this.radius + x;
 * });
 * dc.selectAll('.circle').data(...).x(xPosition);
 * @param {String} n
 * @param {Function} [f]
 * @returns {Function}
 */
dc.pluck = function (n, f) {
    if (!f) {
        return function (d) { return d[n]; };
    }
    return function (d, i) { return f.call(d, d[n], i); };
};

/**
 * @namespace utils
 * @memberof dc
 * @type {{}}
 */
dc.utils = {};

/**
 * Print a single value filter.
 * @method printSingleValue
 * @memberof dc.utils
 * @param {any} filter
 * @returns {String}
 */
dc.utils.printSingleValue = function (filter) {
    var s = '' + filter;

    if (filter instanceof Date) {
        s = dc.dateFormat(filter);
    } else if (typeof(filter) === 'string') {
        s = filter;
    } else if (dc.utils.isFloat(filter)) {
        s = dc.utils.printSingleValue.fformat(filter);
    } else if (dc.utils.isInteger(filter)) {
        s = Math.round(filter);
    }

    return s;
};
dc.utils.printSingleValue.fformat = d3.format('.2f');

// convert 'day' to 'timeDay' and similar
dc.utils.toTimeFunc = function (t) {
    return 'time' + t.charAt(0).toUpperCase() + t.slice(1);
};

/**
 * Arbitrary add one value to another.
 *
 * If the value l is of type Date, adds r units to it. t becomes the unit.
 * For example dc.utils.add(dt, 3, 'week') will add 3 (r = 3) weeks (t= 'week') to dt.
 *
 * If l is of type numeric, t is ignored. In this case if r is of type string,
 * it is assumed to be percentage (whether or not it includes %). For example
 * dc.utils.add(30, 10) will give 40 and dc.utils.add(30, '10') will give 33.
 *
 * They also generate strange results if l is a string.
 * @method add
 * @memberof dc.utils
 * @param {Date|Number} l the value to modify
 * @param {String|Number} r the amount by which to modify the value
 * @param {Function|String} [t=d3.timeDay] if `l` is a `Date`, then this should be a
 * [d3 time interval](https://github.com/d3/d3-time/blob/master/README.md#_interval).
 * For backward compatibility with dc.js 2.0, it can also be the name of an interval, i.e.
 * 'millis', 'second', 'minute', 'hour', 'day', 'week', 'month', or 'year'
 * @returns {Date|Number}
 */
dc.utils.add = function (l, r, t) {
    if (typeof r === 'string') {
        r = r.replace('%', '');
    }

    if (l instanceof Date) {
        if (typeof r === 'string') {
            r = +r;
        }
        if (t === 'millis') {
            return new Date(l.getTime() + r);
        }
        t = t || d3.timeDay;
        if (typeof t !== 'function') {
            t = d3[dc.utils.toTimeFunc(t)];
        }
        return t.offset(l, r);
    } else if (typeof r === 'string') {
        var percentage = (+r / 100);
        return l > 0 ? l * (1 + percentage) : l * (1 - percentage);
    } else {
        return l + r;
    }
};

/**
 * Arbitrary subtract one value from another.
 *
 * If the value l is of type Date, subtracts r units from it. t becomes the unit.
 * For example dc.utils.subtract(dt, 3, 'week') will subtract 3 (r = 3) weeks (t= 'week') from dt.
 *
 * If l is of type numeric, t is ignored. In this case if r is of type string,
 * it is assumed to be percentage (whether or not it includes %). For example
 * dc.utils.subtract(30, 10) will give 20 and dc.utils.subtract(30, '10') will give 27.
 *
 * They also generate strange results if l is a string.
 * @method subtract
 * @memberof dc.utils
 * @param {Date|Number} l the value to modify
 * @param {String|Number} r the amount by which to modify the value
 * @param {Function|String} [t=d3.timeDay] if `l` is a `Date`, then this should be a
 * [d3 time interval](https://github.com/d3/d3-time/blob/master/README.md#_interval).
 * For backward compatibility with dc.js 2.0, it can also be the name of an interval, i.e.
 * 'millis', 'second', 'minute', 'hour', 'day', 'week', 'month', or 'year'
 * @returns {Date|Number}
 */
dc.utils.subtract = function (l, r, t) {
    if (typeof r === 'string') {
        r = r.replace('%', '');
    }

    if (l instanceof Date) {
        if (typeof r === 'string') {
            r = +r;
        }
        if (t === 'millis') {
            return new Date(l.getTime() - r);
        }
        t = t || d3.timeDay;
        if (typeof t !== 'function') {
            t = d3[dc.utils.toTimeFunc(t)];
        }
        return t.offset(l, -r);
    } else if (typeof r === 'string') {
        var percentage = (+r / 100);
        return l < 0 ? l * (1 + percentage) : l * (1 - percentage);
    } else {
        return l - r;
    }
};

/**
 * Is the value a number?
 * @method isNumber
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isNumber = function (n) {
    return n === +n;
};

/**
 * Is the value a float?
 * @method isFloat
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isFloat = function (n) {
    return n === +n && n !== (n | 0);
};

/**
 * Is the value an integer?
 * @method isInteger
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isInteger = function (n) {
    return n === +n && n === (n | 0);
};

/**
 * Is the value very close to zero?
 * @method isNegligible
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isNegligible = function (n) {
    return !dc.utils.isNumber(n) || (n < dc.constants.NEGLIGIBLE_NUMBER && n > -dc.constants.NEGLIGIBLE_NUMBER);
};

/**
 * Ensure the value is no greater or less than the min/max values.  If it is return the boundary value.
 * @method clamp
 * @memberof dc.utils
 * @param {any} val
 * @param {any} min
 * @param {any} max
 * @returns {any}
 */
dc.utils.clamp = function (val, min, max) {
    return val < min ? min : (val > max ? max : val);
};

/**
 * Given `x`, return a function that always returns `x`.
 *
 * {@link https://github.com/d3/d3/blob/master/CHANGES.md#internals `d3.functor` was removed in d3 version 4}.
 * This function helps to implement the replacement,
 * `typeof x === "function" ? x : dc.utils.constant(x)`
 * @method constant
 * @memberof dc.utils
 * @param {any} x
 * @returns {Function}
 */
dc.utils.constant = function (x) {
    return function () {
        return x;
    };
};

/**
 * Using a simple static counter, provide a unique integer id.
 * @method uniqueId
 * @memberof dc.utils
 * @returns {Number}
 */
var _idCounter = 0;
dc.utils.uniqueId = function () {
    return ++_idCounter;
};

/**
 * Convert a name to an ID.
 * @method nameToId
 * @memberof dc.utils
 * @param {String} name
 * @returns {String}
 */
dc.utils.nameToId = function (name) {
    return name.toLowerCase().replace(/[\s]/g, '_').replace(/[\.']/g, '');
};

/**
 * Append or select an item on a parent element.
 * @method appendOrSelect
 * @memberof dc.utils
 * @param {d3.selection} parent
 * @param {String} selector
 * @param {String} tag
 * @returns {d3.selection}
 */
dc.utils.appendOrSelect = function (parent, selector, tag) {
    tag = tag || selector;
    var element = parent.select(selector);
    if (element.empty()) {
        element = parent.append(tag);
    }
    return element;
};

/**
 * Return the number if the value is a number; else 0.
 * @method safeNumber
 * @memberof dc.utils
 * @param {Number|any} n
 * @returns {Number}
 */
dc.utils.safeNumber = function (n) { return dc.utils.isNumber(+n) ? +n : 0;};

/**
 * Return true if both arrays are equal, if both array are null these are considered equal
 * @method arraysEqual
 * @memberof dc.utils
 * @param {Array|null} a1
 * @param {Array|null} a2
 * @returns {Boolean}
 */
dc.utils.arraysEqual = function (a1, a2) {
    if (!a1 && !a2) {
        return true;
    }

    if (!a1 || !a2) {
        return false;
    }

    return a1.length === a2.length &&
        // If elements are not integers/strings, we hope that it will match because of toString
        // Test cases cover dates as well.
        a1.every(function (elem, i) {
            return elem.valueOf() === a2[i].valueOf();
        });
};

// ******** Sunburst Chart ********
dc.utils.allChildren = function (node) {
    var paths = [];
    paths.push(node.path);
    console.log('currentNode', node);
    if (node.children) {
        for (var i = 0; i < node.children.length; i++) {
            paths = paths.concat(dc.utils.allChildren(node.children[i]));
        }
    }
    return paths;
};

// builds a d3 Hierarchy from a collection
// TODO: turn this monster method something better.
dc.utils.toHierarchy = function (list, accessor) {
    var root = {'key': 'root', 'children': []};
    for (var i = 0; i < list.length; i++) {
        var data = list[i];
        var parts = data.key;
        var value = accessor(data);
        var currentNode = root;
        for (var j = 0; j < parts.length; j++) {
            var currentPath = parts.slice(0, j + 1);
            var children = currentNode.children;
            var nodeName = parts[j];
            var childNode;
            if (j + 1 < parts.length) {
                // Not yet at the end of the sequence; move down the tree.
                childNode = findChild(children, nodeName);

                // If we don't already have a child node for this branch, create it.
                if (childNode === void 0) {
                    childNode = {'key': nodeName, 'children': [], 'path': currentPath};
                    children.push(childNode);
                }
                currentNode = childNode;
            } else {
                // Reached the end of the sequence; create a leaf node.
                childNode = {'key': nodeName, 'value': value, 'data': data, 'path': currentPath};
                children.push(childNode);
            }
        }
    }
    return root;
};

function findChild (children, nodeName) {
    for (var k = 0; k < children.length; k++) {
        if (children[k].key === nodeName) {
            return children[k];
        }
    }
}

dc.utils.getAncestors = function (node) {
    var path = [];
    var current = node;
    while (current.parent) {
        path.unshift(current.name);
        current = current.parent;
    }
    return path;
};

dc.utils.arraysIdentical = function (a, b) {
    var i = a.length;
    if (i !== b.length) {
        return false;
    }
    while (i--) {
        if (a[i] !== b[i]) {
            return false;
        }
    }
    return true;
};
<commit_msg>polyfill Object.assign for IE<commit_after>/**
 * The default date format for dc.js
 * @name dateFormat
 * @memberof dc
 * @type {Function}
 * @default d3.timeFormat('%m/%d/%Y')
 */
dc.dateFormat = d3.timeFormat('%m/%d/%Y');

/**
 * @namespace printers
 * @memberof dc
 * @type {{}}
 */
dc.printers = {};

/**
 * Converts a list of filters into a readable string.
 * @method filters
 * @memberof dc.printers
 * @param {Array<dc.filters>} filters
 * @returns {String}
 */
dc.printers.filters = function (filters) {
    var s = '';

    for (var i = 0; i < filters.length; ++i) {
        if (i > 0) {
            s += ', ';
        }
        s += dc.printers.filter(filters[i]);
    }

    return s;
};

/**
 * Converts a filter into a readable string.
 * @method filter
 * @memberof dc.printers
 * @param {dc.filters|any|Array<any>} filter
 * @returns {String}
 */
dc.printers.filter = function (filter) {
    var s = '';

    if (typeof filter !== 'undefined' && filter !== null) {
        if (filter instanceof Array) {
            if (filter.length >= 2) {
                s = '[' + filter.map(function (e) {
                    return dc.utils.printSingleValue(e);
                }).join(' -> ') + ']';
            } else if (filter.length >= 1) {
                s = dc.utils.printSingleValue(filter[0]);
            }
        } else {
            s = dc.utils.printSingleValue(filter);
        }
    }

    return s;
};

/**
 * Returns a function that given a string property name, can be used to pluck the property off an object.  A function
 * can be passed as the second argument to also alter the data being returned.
 *
 * This can be a useful shorthand method to create accessor functions.
 * @method pluck
 * @memberof dc
 * @example
 * var xPluck = dc.pluck('x');
 * var objA = {x: 1};
 * xPluck(objA) // 1
 * @example
 * var xPosition = dc.pluck('x', function (x, i) {
 *     // `this` is the original datum,
 *     // `x` is the x property of the datum,
 *     // `i` is the position in the array
 *     return this.radius + x;
 * });
 * dc.selectAll('.circle').data(...).x(xPosition);
 * @param {String} n
 * @param {Function} [f]
 * @returns {Function}
 */
dc.pluck = function (n, f) {
    if (!f) {
        return function (d) { return d[n]; };
    }
    return function (d, i) { return f.call(d, d[n], i); };
};

/**
 * @namespace utils
 * @memberof dc
 * @type {{}}
 */
dc.utils = {};

/**
 * Print a single value filter.
 * @method printSingleValue
 * @memberof dc.utils
 * @param {any} filter
 * @returns {String}
 */
dc.utils.printSingleValue = function (filter) {
    var s = '' + filter;

    if (filter instanceof Date) {
        s = dc.dateFormat(filter);
    } else if (typeof(filter) === 'string') {
        s = filter;
    } else if (dc.utils.isFloat(filter)) {
        s = dc.utils.printSingleValue.fformat(filter);
    } else if (dc.utils.isInteger(filter)) {
        s = Math.round(filter);
    }

    return s;
};
dc.utils.printSingleValue.fformat = d3.format('.2f');

// convert 'day' to 'timeDay' and similar
dc.utils.toTimeFunc = function (t) {
    return 'time' + t.charAt(0).toUpperCase() + t.slice(1);
};

/**
 * Arbitrary add one value to another.
 *
 * If the value l is of type Date, adds r units to it. t becomes the unit.
 * For example dc.utils.add(dt, 3, 'week') will add 3 (r = 3) weeks (t= 'week') to dt.
 *
 * If l is of type numeric, t is ignored. In this case if r is of type string,
 * it is assumed to be percentage (whether or not it includes %). For example
 * dc.utils.add(30, 10) will give 40 and dc.utils.add(30, '10') will give 33.
 *
 * They also generate strange results if l is a string.
 * @method add
 * @memberof dc.utils
 * @param {Date|Number} l the value to modify
 * @param {String|Number} r the amount by which to modify the value
 * @param {Function|String} [t=d3.timeDay] if `l` is a `Date`, then this should be a
 * [d3 time interval](https://github.com/d3/d3-time/blob/master/README.md#_interval).
 * For backward compatibility with dc.js 2.0, it can also be the name of an interval, i.e.
 * 'millis', 'second', 'minute', 'hour', 'day', 'week', 'month', or 'year'
 * @returns {Date|Number}
 */
dc.utils.add = function (l, r, t) {
    if (typeof r === 'string') {
        r = r.replace('%', '');
    }

    if (l instanceof Date) {
        if (typeof r === 'string') {
            r = +r;
        }
        if (t === 'millis') {
            return new Date(l.getTime() + r);
        }
        t = t || d3.timeDay;
        if (typeof t !== 'function') {
            t = d3[dc.utils.toTimeFunc(t)];
        }
        return t.offset(l, r);
    } else if (typeof r === 'string') {
        var percentage = (+r / 100);
        return l > 0 ? l * (1 + percentage) : l * (1 - percentage);
    } else {
        return l + r;
    }
};

/**
 * Arbitrary subtract one value from another.
 *
 * If the value l is of type Date, subtracts r units from it. t becomes the unit.
 * For example dc.utils.subtract(dt, 3, 'week') will subtract 3 (r = 3) weeks (t= 'week') from dt.
 *
 * If l is of type numeric, t is ignored. In this case if r is of type string,
 * it is assumed to be percentage (whether or not it includes %). For example
 * dc.utils.subtract(30, 10) will give 20 and dc.utils.subtract(30, '10') will give 27.
 *
 * They also generate strange results if l is a string.
 * @method subtract
 * @memberof dc.utils
 * @param {Date|Number} l the value to modify
 * @param {String|Number} r the amount by which to modify the value
 * @param {Function|String} [t=d3.timeDay] if `l` is a `Date`, then this should be a
 * [d3 time interval](https://github.com/d3/d3-time/blob/master/README.md#_interval).
 * For backward compatibility with dc.js 2.0, it can also be the name of an interval, i.e.
 * 'millis', 'second', 'minute', 'hour', 'day', 'week', 'month', or 'year'
 * @returns {Date|Number}
 */
dc.utils.subtract = function (l, r, t) {
    if (typeof r === 'string') {
        r = r.replace('%', '');
    }

    if (l instanceof Date) {
        if (typeof r === 'string') {
            r = +r;
        }
        if (t === 'millis') {
            return new Date(l.getTime() - r);
        }
        t = t || d3.timeDay;
        if (typeof t !== 'function') {
            t = d3[dc.utils.toTimeFunc(t)];
        }
        return t.offset(l, -r);
    } else if (typeof r === 'string') {
        var percentage = (+r / 100);
        return l < 0 ? l * (1 + percentage) : l * (1 - percentage);
    } else {
        return l - r;
    }
};

/**
 * Is the value a number?
 * @method isNumber
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isNumber = function (n) {
    return n === +n;
};

/**
 * Is the value a float?
 * @method isFloat
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isFloat = function (n) {
    return n === +n && n !== (n | 0);
};

/**
 * Is the value an integer?
 * @method isInteger
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isInteger = function (n) {
    return n === +n && n === (n | 0);
};

/**
 * Is the value very close to zero?
 * @method isNegligible
 * @memberof dc.utils
 * @param {any} n
 * @returns {Boolean}
 */
dc.utils.isNegligible = function (n) {
    return !dc.utils.isNumber(n) || (n < dc.constants.NEGLIGIBLE_NUMBER && n > -dc.constants.NEGLIGIBLE_NUMBER);
};

/**
 * Ensure the value is no greater or less than the min/max values.  If it is return the boundary value.
 * @method clamp
 * @memberof dc.utils
 * @param {any} val
 * @param {any} min
 * @param {any} max
 * @returns {any}
 */
dc.utils.clamp = function (val, min, max) {
    return val < min ? min : (val > max ? max : val);
};

/**
 * Given `x`, return a function that always returns `x`.
 *
 * {@link https://github.com/d3/d3/blob/master/CHANGES.md#internals `d3.functor` was removed in d3 version 4}.
 * This function helps to implement the replacement,
 * `typeof x === "function" ? x : dc.utils.constant(x)`
 * @method constant
 * @memberof dc.utils
 * @param {any} x
 * @returns {Function}
 */
dc.utils.constant = function (x) {
    return function () {
        return x;
    };
};

/**
 * Using a simple static counter, provide a unique integer id.
 * @method uniqueId
 * @memberof dc.utils
 * @returns {Number}
 */
var _idCounter = 0;
dc.utils.uniqueId = function () {
    return ++_idCounter;
};

/**
 * Convert a name to an ID.
 * @method nameToId
 * @memberof dc.utils
 * @param {String} name
 * @returns {String}
 */
dc.utils.nameToId = function (name) {
    return name.toLowerCase().replace(/[\s]/g, '_').replace(/[\.']/g, '');
};

/**
 * Append or select an item on a parent element.
 * @method appendOrSelect
 * @memberof dc.utils
 * @param {d3.selection} parent
 * @param {String} selector
 * @param {String} tag
 * @returns {d3.selection}
 */
dc.utils.appendOrSelect = function (parent, selector, tag) {
    tag = tag || selector;
    var element = parent.select(selector);
    if (element.empty()) {
        element = parent.append(tag);
    }
    return element;
};

/**
 * Return the number if the value is a number; else 0.
 * @method safeNumber
 * @memberof dc.utils
 * @param {Number|any} n
 * @returns {Number}
 */
dc.utils.safeNumber = function (n) { return dc.utils.isNumber(+n) ? +n : 0;};

/**
 * Return true if both arrays are equal, if both array are null these are considered equal
 * @method arraysEqual
 * @memberof dc.utils
 * @param {Array|null} a1
 * @param {Array|null} a2
 * @returns {Boolean}
 */
dc.utils.arraysEqual = function (a1, a2) {
    if (!a1 && !a2) {
        return true;
    }

    if (!a1 || !a2) {
        return false;
    }

    return a1.length === a2.length &&
        // If elements are not integers/strings, we hope that it will match because of toString
        // Test cases cover dates as well.
        a1.every(function (elem, i) {
            return elem.valueOf() === a2[i].valueOf();
        });
};

// ******** Sunburst Chart ********
dc.utils.allChildren = function (node) {
    var paths = [];
    paths.push(node.path);
    console.log('currentNode', node);
    if (node.children) {
        for (var i = 0; i < node.children.length; i++) {
            paths = paths.concat(dc.utils.allChildren(node.children[i]));
        }
    }
    return paths;
};

// builds a d3 Hierarchy from a collection
// TODO: turn this monster method something better.
dc.utils.toHierarchy = function (list, accessor) {
    var root = {'key': 'root', 'children': []};
    for (var i = 0; i < list.length; i++) {
        var data = list[i];
        var parts = data.key;
        var value = accessor(data);
        var currentNode = root;
        for (var j = 0; j < parts.length; j++) {
            var currentPath = parts.slice(0, j + 1);
            var children = currentNode.children;
            var nodeName = parts[j];
            var childNode;
            if (j + 1 < parts.length) {
                // Not yet at the end of the sequence; move down the tree.
                childNode = findChild(children, nodeName);

                // If we don't already have a child node for this branch, create it.
                if (childNode === void 0) {
                    childNode = {'key': nodeName, 'children': [], 'path': currentPath};
                    children.push(childNode);
                }
                currentNode = childNode;
            } else {
                // Reached the end of the sequence; create a leaf node.
                childNode = {'key': nodeName, 'value': value, 'data': data, 'path': currentPath};
                children.push(childNode);
            }
        }
    }
    return root;
};

function findChild (children, nodeName) {
    for (var k = 0; k < children.length; k++) {
        if (children[k].key === nodeName) {
            return children[k];
        }
    }
}

dc.utils.getAncestors = function (node) {
    var path = [];
    var current = node;
    while (current.parent) {
        path.unshift(current.name);
        current = current.parent;
    }
    return path;
};

dc.utils.arraysIdentical = function (a, b) {
    var i = a.length;
    if (i !== b.length) {
        return false;
    }
    while (i--) {
        if (a[i] !== b[i]) {
            return false;
        }
    }
    return true;
};

if (typeof Object.assign !== 'function') {
    // Must be writable: true, enumerable: false, configurable: true
    Object.defineProperty(Object, 'assign', {
        value: function assign (target, varArgs) { // .length of function is 2
            'use strict';
            if (target === null) { // TypeError if undefined or null
                throw new TypeError('Cannot convert undefined or null to object');
            }

            var to = Object(target);

            for (var index = 1; index < arguments.length; index++) {
                var nextSource = arguments[index];

                if (nextSource !== null) { // Skip over if undefined or null
                    for (var nextKey in nextSource) {
                        // Avoid bugs when hasOwnProperty is shadowed
                        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                            to[nextKey] = nextSource[nextKey];
                        }
                    }
                }
            }
            return to;
        },
        writable: true,
        configurable: true
    });
}
<|endoftext|>


<commit_before>
// TextEncoder shim for older browsers and Safari.
import {TextEncoder} from 'text-encoding-utf-8';

import {scalarTypes, constantArrayType, lubType} from './type';

export function IntegralValue (type, number) {
  this.type = type;
  this.number = number | 0;
};
IntegralValue.prototype.toString = function () {
  if (this.type.repr === 'char') {
    if (this.number >= 32 && this.number < 128) {
      switch (this.number) {
        case 39: return '\\\'';
        case 92: return '\\\\';
        default: return `'${String.fromCharCode(this.number)}'`;
      }
    } else {
      switch (this.number) {
        case 0: return '\\0';
        case 8: return '\\t';
        case 10: return '\\r';
        case 13: return '\\n';
        default: {
          const n = this.number + (this.number >= 0 ? 0 : 256);
          return `'\\x${n.toString(16)}'`;
        }
      }
    }
  } else {
    return this.number.toString();
  }
};
IntegralValue.prototype.toInteger = function () {
  return this.number;
};
IntegralValue.prototype.toBool = function () {
  return 0 !== this.number;
};
IntegralValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'char':
      view.setInt8(offset, this.number);
      break;
    case 'unsigned char':
      view.setUint8(offset, this.number);
      break;
    case 'short':
      view.setInt16(offset, this.number, littleEndian);
      break;
    case 'unsigned short':
      view.setUint16(offset, this.number, littleEndian);
      break;
    case 'int':
    case 'long':
      view.setInt32(offset, this.number, littleEndian);
      break;
    case 'unsigned int':
    case 'unsigned long':
      view.setUint32(offset, this.number, littleEndian);
      break;
    default:
      throw `cannot pack integral value ${this.type.repr}`;
  }
};

export function FloatingValue (type, number) {
  this.type = type;
  this.number = type.size === 4 ? Math.fround(number) : number;
};
FloatingValue.prototype.toString = function () {
  return this.number.toString();
};
FloatingValue.prototype.toInteger = function () {
  return this.number | 0;
};
FloatingValue.prototype.toBool = function () {
  return 0 !== this.number;
};
FloatingValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'float':
      view.setFloat32(offset, this.number, littleEndian);
      break;
    case 'double':
      view.setFloat64(offset, this.number, littleEndian);
      break;
    default:
      throw `cannot pack floating value ${this.type.repr}`;
  }
};

export function PointerValue (type, address) {
  this.type = type;
  this.address = address | 0;
};
PointerValue.prototype.toString = function () {
  return `0x${this.address.toString(16)}`;
};
PointerValue.prototype.toInteger = function () {
  return this.address;
};
PointerValue.prototype.toBool = function () {
  return 0 !== this.address;
};
PointerValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint32(offset, this.address, littleEndian);
};

export function ConstantArrayValue (type, elements) {
  this.type = type;
  this.elements = elements;
};
ConstantArrayValue.prototype.toString = function () {
  return `constant array`;
};
ConstantArrayValue.prototype.pack = function (view, offset, littleEndian) {
  const elemSize = this.type.elem.size;
  this.elements.forEach(function (elem, index) {
    packValue(view, offset + index * elemSize, elem, littleEndian);
  });
};

export const packValue = function (view, offset, value, littleEndian) {
  value.pack(view, offset, littleEndian);
};

export const unpackValue = function (view, offset, type, littleEndian) {
  switch (type.kind) {
    case 'scalar':
      switch (type.repr) {
        case 'char':
          return new IntegralValue(type, view.getInt8(offset));
        case 'unsigned char':
          return new IntegralValue(type, view.getUint8(offset));
        case 'short':
          return new IntegralValue(type, view.getInt16(offset, littleEndian));
        case 'unsigned short':
          return new IntegralValue(type, view.getUint16(offset, littleEndian));
        case 'int':
        case 'long':
          return new IntegralValue(type, view.getInt32(offset, littleEndian));
        case 'unsigned int':
        case 'unsigned long':
          return new IntegralValue(type, view.getUint32(offset, littleEndian));
        case 'float':
          return new FloatingValue(type, view.getFloat32(offset, littleEndian));
        case 'double':
          return new FloatingValue(type, view.getFloat64(offset, littleEndian));
        default:
          throw `unpack scalar ${type.repr}`;
      }
    case 'constant array':
      {
        const elemType = type.elem;
        const elemSize = elemType.size;
        const elements = [];
        for (var index = 0; index < type.count; index++) {
          elements.push(unpackValue(view, offset + index * elemSize, elemType, littleEndian));
        }
        return new ConstantArrayValue(type, elements);
      }
    case 'pointer':
      return new PointerValue(type, view.getUint32(offset, littleEndian));
    default:
      throw `not implemented: unpack ${type.kind}`;
  }
};

export const stringValue = function (string) {
  const encoder = new TextEncoder('utf-8');
  const bytesArray = encoder.encode(string);
  const charType = scalarTypes['char'];
  const charLen = bytesArray.length;
  const chars = [];
  for (let charPos = 0; charPos < charLen; charPos++) {
    chars.push(new IntegralValue(charType, bytesArray[charPos]));
  }
  chars.push(new IntegralValue(charType, 0));
  const lenValue = new IntegralValue(scalarTypes['int'], chars.length);
  return new ConstantArrayValue(constantArrayType(charType, lenValue), chars);
};

const isRelational = function (op) {
  return /^(EQ|NE|LT|LE|GT|GE)$/.test(op);
};

const evalRelationalOperation = function (op, v1, v2) {
  switch (op) {
    case 'EQ':  return v1 === v2;
    case 'NE':  return v1 !== v2;
    case 'LT':  return v1 < v2;
    case 'LE':  return v1 <= v2;
    case 'GT':  return v1 > v2;
    case 'GE':  return v1 >= v2;
  }
};

const evalIntegerBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
    // TODO: check Rem results on negative values
    case 'Rem': case 'RemAssign': return v1 % v2;
    case 'And': case 'AndAssign': return v1 & v2;
    case 'Or':  case 'OrAssign':  return v1 | v2;
    case 'Xor': case 'XorAssign': return v1 ^ v2;
    case 'Shl': case 'ShlAssign': return v1 << v2;
    case 'Shr': case 'ShrAssign': return v1 >> v2;
  }
};

const evalFloatingBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
  }
};

export const evalBinaryOperation = function (opcode, lhs, rhs) {
  // Relational operators
  if (isRelational(opcode)) {
    const result = evalRelationalOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(scalarTypes['int'], result ? 1 : 0);
  }
  // Integer arithmetic
  if (lhs instanceof IntegralValue && rhs instanceof IntegralValue) {
    const result = evalIntegerBinaryOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(lubType(lhs.type, rhs.type), result);
  }
  // Float arithmetic
  if (lhs instanceof FloatingValue && rhs instanceof FloatingValue) {
    const result = evalFloatingBinaryOperation(opcode, lhs.number, rhs.number)
    return new FloatingValue(lubType(lhs.type, rhs.type), result);
  }
  // Pointer arithmetic
  if (lhs instanceof PointerValue && rhs instanceof IntegralValue) {
    if (opcode === 'Add') {
      const address = lhs.address + rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
    if (opcode === 'Sub') {
      const address = lhs.address - rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
  }
  if (lhs instanceof IntegralValue && rhs instanceof PointerValue) {
    if (opcode === 'Add') {
      const address = rhs.address + lhs.number * rhs.type.pointee.size;
      return new PointerValue(rhs.type, address);
    }
  }
  if (lhs instanceof PointerValue && rhs instanceof PointerValue) {
    if (opcode === 'Sub') {
      const offset = lhs.address - rhs.address;
      return new IntegralValue(scalarTypes['int'], offset);
    }
  }
  throw `not implemented: ${lhs} ${opcode} ${rhs}`;
};

export const evalUnaryOperation = function (opcode, operand) {
  if (operand instanceof IntegralValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new IntegralValue(operand.type, -operand.number);
      case 'LNot': return new IntegralValue(scalarTypes['int'], operand.number ? 1 : 0);
      case 'Not': return new IntegralValue(operand.type, ~operand.number);
    }
  }
  if (operand instanceof FloatingValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new FloatingValue(operand.type, -operand.number);
    }
  }
  throw `not implemented: ${opcode} ${operand}`;
};

export const evalCast = function (type, operand) {
  if (type.kind === 'scalar') {
    if (/^(unsigned )?char$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xff);
    }
    if (/^(unsigned )?short$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffff);
    }
    if (/^(unsigned )?(int|long)$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffffffff);
    }
    if (type.repr === 'float') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number)
      }
    }
    if (type.repr === 'double') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number);
      }
    }
  }
  if (type.kind === 'pointer') {
    if (operand instanceof PointerValue) {
      return new PointerValue(type, operand.address);
    }
    if (operand instanceof IntegralValue) {
      return new PointerValue(type, operand.toInteger())
    }
    if (/^(builtin|function|string)$/.test(operand[0])) {
      // XXX temporary cheat for non-addressable values.
      return operand;
    }
  }
  throw `not implemented: (${type})${operand}`;
};

export const evalPointerAdd = function (pointer, value) {
  const offset = value.toInteger() * pointer.type.pointee.size;
  return new PointerValue(pointer.type, pointer.address + offset);
};
<commit_msg>improve printing of floats<commit_after>
// TextEncoder shim for older browsers and Safari.
import {TextEncoder} from 'text-encoding-utf-8';

import {scalarTypes, constantArrayType, lubType} from './type';

export function IntegralValue (type, number) {
  this.type = type;
  if (this.type.repr.startsWith('unsigned')) {
    this.number = number >>> 0;
  } else {
    this.number = number | 0;
  }
};
IntegralValue.prototype.toString = function () {
  if (this.type.repr === 'char') {
    if (this.number >= 32 && this.number < 128) {
      switch (this.number) {
        case 39: return '\\\'';
        case 92: return '\\\\';
        default: return `'${String.fromCharCode(this.number)}'`;
      }
    } else {
      switch (this.number) {
        case 0: return '\\0';
        case 8: return '\\t';
        case 10: return '\\r';
        case 13: return '\\n';
        default: {
          const n = this.number + (this.number >= 0 ? 0 : 256);
          return `'\\x${n.toString(16)}'`;
        }
      }
    }
  } else {
    return this.number.toString();
  }
};
IntegralValue.prototype.toInteger = function () {
  return this.number;
};
IntegralValue.prototype.toBool = function () {
  return 0 !== this.number;
};
IntegralValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'char':
      view.setInt8(offset, this.number);
      break;
    case 'unsigned char':
      view.setUint8(offset, this.number);
      break;
    case 'short':
      view.setInt16(offset, this.number, littleEndian);
      break;
    case 'unsigned short':
      view.setUint16(offset, this.number, littleEndian);
      break;
    case 'int':
    case 'long':
      view.setInt32(offset, this.number, littleEndian);
      break;
    case 'unsigned int':
    case 'unsigned long':
      view.setUint32(offset, this.number, littleEndian);
      break;
    default:
      throw `cannot pack integral value ${this.type.repr}`;
  }
};

export function FloatingValue (type, number) {
  this.type = type;
  this.number = type.size === 4 ? Math.fround(number) : number;
};
FloatingValue.prototype.toString = function () {
  let str = this.number.toFixed(6);
  // Trim the trailing zeros, and the decimal point if there are no digits
  // to its right.
  str = str.replace(/(\.[0-9]*?)0+$/, function (m, n) {
    return n === '.' ? '' : n;
  });
  return str;
};
FloatingValue.prototype.toInteger = function () {
  return this.number | 0;
};
FloatingValue.prototype.toBool = function () {
  return 0 !== this.number;
};
FloatingValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'float':
      view.setFloat32(offset, this.number, littleEndian);
      break;
    case 'double':
      view.setFloat64(offset, this.number, littleEndian);
      break;
    default:
      throw `cannot pack floating value ${this.type.repr}`;
  }
};

export function PointerValue (type, address) {
  this.type = type;
  this.address = address | 0;
};
PointerValue.prototype.toString = function () {
  return `0x${this.address.toString(16)}`;
};
PointerValue.prototype.toInteger = function () {
  return this.address;
};
PointerValue.prototype.toBool = function () {
  return 0 !== this.address;
};
PointerValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint32(offset, this.address, littleEndian);
};

export function ConstantArrayValue (type, elements) {
  this.type = type;
  this.elements = elements;
};
ConstantArrayValue.prototype.toString = function () {
  return `constant array`;
};
ConstantArrayValue.prototype.pack = function (view, offset, littleEndian) {
  const elemSize = this.type.elem.size;
  this.elements.forEach(function (elem, index) {
    packValue(view, offset + index * elemSize, elem, littleEndian);
  });
};

export const packValue = function (view, offset, value, littleEndian) {
  value.pack(view, offset, littleEndian);
};

export const unpackValue = function (view, offset, type, littleEndian) {
  switch (type.kind) {
    case 'scalar':
      switch (type.repr) {
        case 'char':
          return new IntegralValue(type, view.getInt8(offset));
        case 'unsigned char':
          return new IntegralValue(type, view.getUint8(offset));
        case 'short':
          return new IntegralValue(type, view.getInt16(offset, littleEndian));
        case 'unsigned short':
          return new IntegralValue(type, view.getUint16(offset, littleEndian));
        case 'int':
        case 'long':
          return new IntegralValue(type, view.getInt32(offset, littleEndian));
        case 'unsigned int':
        case 'unsigned long':
          return new IntegralValue(type, view.getUint32(offset, littleEndian));
        case 'float':
          return new FloatingValue(type, view.getFloat32(offset, littleEndian));
        case 'double':
          return new FloatingValue(type, view.getFloat64(offset, littleEndian));
        default:
          throw `unpack scalar ${type.repr}`;
      }
    case 'constant array':
      {
        const elemType = type.elem;
        const elemSize = elemType.size;
        const elements = [];
        for (var index = 0; index < type.count; index++) {
          elements.push(unpackValue(view, offset + index * elemSize, elemType, littleEndian));
        }
        return new ConstantArrayValue(type, elements);
      }
    case 'pointer':
      return new PointerValue(type, view.getUint32(offset, littleEndian));
    default:
      throw `not implemented: unpack ${type.kind}`;
  }
};

export const stringValue = function (string) {
  const encoder = new TextEncoder('utf-8');
  const bytesArray = encoder.encode(string);
  const charType = scalarTypes['char'];
  const charLen = bytesArray.length;
  const chars = [];
  for (let charPos = 0; charPos < charLen; charPos++) {
    chars.push(new IntegralValue(charType, bytesArray[charPos]));
  }
  chars.push(new IntegralValue(charType, 0));
  const lenValue = new IntegralValue(scalarTypes['int'], chars.length);
  return new ConstantArrayValue(constantArrayType(charType, lenValue), chars);
};

const isRelational = function (op) {
  return /^(EQ|NE|LT|LE|GT|GE)$/.test(op);
};

const evalRelationalOperation = function (op, v1, v2) {
  switch (op) {
    case 'EQ':  return v1 === v2;
    case 'NE':  return v1 !== v2;
    case 'LT':  return v1 < v2;
    case 'LE':  return v1 <= v2;
    case 'GT':  return v1 > v2;
    case 'GE':  return v1 >= v2;
  }
};

const evalIntegerBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
    // TODO: check Rem results on negative values
    case 'Rem': case 'RemAssign': return v1 % v2;
    case 'And': case 'AndAssign': return v1 & v2;
    case 'Or':  case 'OrAssign':  return v1 | v2;
    case 'Xor': case 'XorAssign': return v1 ^ v2;
    case 'Shl': case 'ShlAssign': return v1 << v2;
    case 'Shr': case 'ShrAssign': return v1 >> v2;
  }
};

const evalFloatingBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
  }
};

export const evalBinaryOperation = function (opcode, lhs, rhs) {
  // Relational operators
  if (isRelational(opcode)) {
    const result = evalRelationalOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(scalarTypes['int'], result ? 1 : 0);
  }
  // Integer arithmetic
  if (lhs instanceof IntegralValue && rhs instanceof IntegralValue) {
    const result = evalIntegerBinaryOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(lubType(lhs.type, rhs.type), result);
  }
  // Float arithmetic
  if (lhs instanceof FloatingValue && rhs instanceof FloatingValue) {
    const result = evalFloatingBinaryOperation(opcode, lhs.number, rhs.number)
    return new FloatingValue(lubType(lhs.type, rhs.type), result);
  }
  // Pointer arithmetic
  if (lhs instanceof PointerValue && rhs instanceof IntegralValue) {
    if (opcode === 'Add') {
      const address = lhs.address + rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
    if (opcode === 'Sub') {
      const address = lhs.address - rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
  }
  if (lhs instanceof IntegralValue && rhs instanceof PointerValue) {
    if (opcode === 'Add') {
      const address = rhs.address + lhs.number * rhs.type.pointee.size;
      return new PointerValue(rhs.type, address);
    }
  }
  if (lhs instanceof PointerValue && rhs instanceof PointerValue) {
    if (opcode === 'Sub') {
      const offset = lhs.address - rhs.address;
      return new IntegralValue(scalarTypes['int'], offset);
    }
  }
  throw `not implemented: ${lhs} ${opcode} ${rhs}`;
};

export const evalUnaryOperation = function (opcode, operand) {
  if (operand instanceof IntegralValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new IntegralValue(operand.type, -operand.number);
      case 'LNot': return new IntegralValue(scalarTypes['int'], operand.number ? 1 : 0);
      case 'Not': return new IntegralValue(operand.type, ~operand.number);
    }
  }
  if (operand instanceof FloatingValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new FloatingValue(operand.type, -operand.number);
    }
  }
  throw `not implemented: ${opcode} ${operand}`;
};

export const evalCast = function (type, operand) {
  if (type.kind === 'scalar') {
    if (/^(unsigned )?char$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xff);
    }
    if (/^(unsigned )?short$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffff);
    }
    if (/^(unsigned )?(int|long)$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffffffff);
    }
    if (type.repr === 'float') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number)
      }
    }
    if (type.repr === 'double') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number);
      }
    }
  }
  if (type.kind === 'pointer') {
    if (operand instanceof PointerValue) {
      return new PointerValue(type, operand.address);
    }
    if (operand instanceof IntegralValue) {
      return new PointerValue(type, operand.toInteger())
    }
    if (/^(builtin|function|string)$/.test(operand[0])) {
      // XXX temporary cheat for non-addressable values.
      return operand;
    }
  }
  throw `not implemented: (${type})${operand}`;
};

export const evalPointerAdd = function (pointer, value) {
  const offset = value.toInteger() * pointer.type.pointee.size;
  return new PointerValue(pointer.type, pointer.address + offset);
};
<|endoftext|>


<commit_before>
// TextEncoder shim for older browsers and Safari.
import {TextEncoder} from 'text-encoding-utf-8';

import {builtinTypes, arrayType, lubType, decayedType, pointerType, functionType} from './type';

export function IntegralValue (type, number) {
  this.type = type;
  if (/^unsigned/.test(this.type.repr)) {
    this.number = number >>> 0;
  } else {
    this.number = number | 0;
  }
};
IntegralValue.prototype.toString = function () {
  if (this.type.repr === 'char') {
    if (this.number >= 32 && this.number < 128) {
      switch (this.number) {
        case 39: return '\\\'';
        case 92: return '\\\\';
        default: return `'${String.fromCharCode(this.number)}'`;
      }
    } else {
      switch (this.number) {
        case 0: return '\\0';
        case 8: return '\\t';
        case 10: return '\\r';
        case 13: return '\\n';
        default: {
          const n = this.number + (this.number >= 0 ? 0 : 256);
          return `'\\x${n.toString(16)}'`;
        }
      }
    }
  } else {
    return this.number.toString();
  }
};
IntegralValue.prototype.toInteger = function () {
  return this.number;
};
IntegralValue.prototype.toBool = function () {
  return 0 !== this.number;
};
IntegralValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'char':
      view.setInt8(offset, this.number);
      break;
    case 'unsigned char':
      view.setUint8(offset, this.number);
      break;
    case 'short':
      view.setInt16(offset, this.number, littleEndian);
      break;
    case 'unsigned short':
      view.setUint16(offset, this.number, littleEndian);
      break;
    case 'int':
    case 'long':
      view.setInt32(offset, this.number, littleEndian);
      break;
    case 'unsigned int':
    case 'unsigned long':
      view.setUint32(offset, this.number, littleEndian);
      break;
    default:
      throw new Error(`cannot pack integral value ${this.type.repr}`);
  }
};

export function FloatingValue (type, number) {
  this.type = type;
  this.number = type.size === 4 ? Math.fround(number) : number;
};
FloatingValue.prototype.toString = function () {
  let str = this.number.toFixed(6);
  // Trim the trailing zeros, and the decimal point if there are no digits
  // to its right.
  str = str.replace(/(\.[0-9]*?)0+$/, function (m, n) {
    return n === '.' ? '' : n;
  });
  return str;
};
FloatingValue.prototype.toInteger = function () {
  return this.number | 0;
};
FloatingValue.prototype.toBool = function () {
  return 0 !== this.number;
};
FloatingValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'float':
      view.setFloat32(offset, this.number, littleEndian);
      break;
    case 'double':
      view.setFloat64(offset, this.number, littleEndian);
      break;
    default:
      throw new Error(`cannot pack floating value ${this.type.repr}`);
  }
};

export function PointerValue (type, address) {
  this.type = type;
  this.address = address | 0;
};
PointerValue.prototype.toString = function () {
  return `0x${this.address.toString(16)}`;
};
PointerValue.prototype.toInteger = function () {
  return this.address;
};
PointerValue.prototype.toBool = function () {
  return 0 !== this.address;
};
PointerValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint32(offset, this.address, littleEndian);
};

export const makeRef = function (elemType, address) {
  let refType;
  if (elemType.kind === 'array') {
    // A array reference decays to a pointer to its first element.
    refType = decayedType(elemType);
  } else {
    refType = pointerType(elemType)
  }
  return new PointerValue(refType, address);
};

export function ArrayValue (type, elements) {
  this.type = type;
  this.elements = elements;
};
ArrayValue.prototype.toString = function () {
  return `array`;
};
ArrayValue.prototype.pack = function (view, offset, littleEndian) {
  const elemSize = this.type.elem.size;
  this.elements.forEach(function (elem, index) {
    packValue(view, offset + index * elemSize, elem, littleEndian);
  });
};

export function RecordValue (type, props) {
  this.type = type;
  this.props = props;
};
RecordValue.prototype.toString = function () {
  return `record`;
};
RecordValue.prototype.pack = function (view, offset, littleEndian) {
  const {type: {fields, fieldMap}, props} = this;
  // {offset, type, refType}
  fields.forEach(function (name) {
    const value = props[name];
    if (value) {
      const {offset: fieldOffset} = fieldMap[name];
      packValue(view, offset + fieldOffset, value, littleEndian);
    }
  });
};

export function FunctionValue (type, codePtr, name, body, decl) {
  this.type = pointerType(type);
  this.codePtr = codePtr;
  this.name = name;
  this.body = body;
  this.decl = decl;
};
FunctionValue.prototype.toString = function () {
  return `&${this.name}`;
}
FunctionValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint16(offset, this.codePtr, littleEndian);
};
FunctionValue.prototype.toInteger = function () {
  return this.codePtr;
};

export function BuiltinValue (type, codePtr, name, func) {
  this.type = type;
  this.codePtr = codePtr;
  this.name = name;
  this.func = func;
};
BuiltinValue.prototype.toString = function () {
  return `&${this.name}`;
}
BuiltinValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint16(offset, this.codePtr, littleEndian);
};
BuiltinValue.prototype.toInteger = function () {
  return this.codePtr;
};

export const badFunction = new BuiltinValue (
  functionType(builtinTypes['void'], []), 0, "bad_func",
  function (state, cont, values) {
    return {error: `bad function`};
  });

export const packValue = function (view, offset, value, littleEndian) {
  value.pack(view, offset, littleEndian);
};

export const unpackValue = function (view, offset, type, littleEndian, core) {
  switch (type.kind) {
    case 'builtin':
      switch (type.repr) {
        case 'char':
          return new IntegralValue(type, view.getInt8(offset));
        case 'unsigned char':
          return new IntegralValue(type, view.getUint8(offset));
        case 'short':
          return new IntegralValue(type, view.getInt16(offset, littleEndian));
        case 'unsigned short':
          return new IntegralValue(type, view.getUint16(offset, littleEndian));
        case 'int':
        case 'long':
          return new IntegralValue(type, view.getInt32(offset, littleEndian));
        case 'unsigned int':
        case 'unsigned long':
          return new IntegralValue(type, view.getUint32(offset, littleEndian));
        case 'float':
          return new FloatingValue(type, view.getFloat32(offset, littleEndian));
        case 'double':
          return new FloatingValue(type, view.getFloat64(offset, littleEndian));
        default:
          throw new Error(`unpack builtin ${type.repr}`);
      }
    case 'array':
      {
        const elemType = type.elem;
        const elemSize = elemType.size;
        const elements = [];
        for (var index = 0; index < type.count; index++) {
          elements.push(unpackValue(view, offset + index * elemSize, elemType, littleEndian, core));
        }
        return new ArrayValue(type, elements);
      }
    case 'record':
      {
        const props = {};
        for (let name of type.fields) {
          const {offset: fieldOffset, type: fieldType} = type.fieldMap[name];
          props[name] = unpackValue(view, offset + fieldOffset, fieldType, littleEndian, core);
        }
        return new RecordValue(type, props);
      }
    case 'pointer': {
      if (type.pointee.kind === 'function') {
        const codePtr = view.getUint16(offset, littleEndian);
        return core.functions[codePtr] || badFunction;
      } else {
        const address = view.getUint32(offset, littleEndian);
        return new PointerValue(type, address);
      }
    }
    default:
      throw new Error(`not implemented: unpack ${type.kind}`);
  }
};

export const stringValue = function (string) {
  const encoder = new TextEncoder('utf-8');
  const bytesArray = encoder.encode(string);
  const charType = builtinTypes['char'];
  const charLen = bytesArray.length;
  const chars = [];
  for (let charPos = 0; charPos < charLen; charPos++) {
    chars.push(new IntegralValue(charType, bytesArray[charPos]));
  }
  chars.push(new IntegralValue(charType, 0));
  const lenValue = new IntegralValue(builtinTypes['int'], chars.length);
  return new ArrayValue(arrayType(charType, lenValue), chars);
};

const isRelational = function (op) {
  return /^(EQ|NE|LT|LE|GT|GE)$/.test(op);
};

const evalRelationalOperation = function (op, v1, v2) {
  switch (op) {
    case 'EQ':  return v1 === v2;
    case 'NE':  return v1 !== v2;
    case 'LT':  return v1 < v2;
    case 'LE':  return v1 <= v2;
    case 'GT':  return v1 > v2;
    case 'GE':  return v1 >= v2;
  }
};

const evalIntegerBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
    case 'Rem': case 'RemAssign': return v1 % v2;
    case 'And': case 'AndAssign': return v1 & v2;
    case 'Or':  case 'OrAssign':  return v1 | v2;
    case 'Xor': case 'XorAssign': return v1 ^ v2;
    case 'Shl': case 'ShlAssign': return v1 << v2;
    case 'Shr': case 'ShrAssign': return v1 >> v2;
  }
};

const evalFloatingBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
  }
};

export const evalBinaryOperation = function (opcode, lhs, rhs) {
  // Relational operators
  if (isRelational(opcode)) {
    const result = evalRelationalOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(builtinTypes['int'], result ? 1 : 0);
  }
  // Integer arithmetic
  if (lhs instanceof IntegralValue && rhs instanceof IntegralValue) {
    const result = evalIntegerBinaryOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(lubType(lhs.type, rhs.type), result);
  }
  // Float arithmetic
  if (lhs instanceof FloatingValue && rhs instanceof FloatingValue) {
    const result = evalFloatingBinaryOperation(opcode, lhs.number, rhs.number)
    return new FloatingValue(lubType(lhs.type, rhs.type), result);
  }
  // Pointer arithmetic
  if (lhs instanceof PointerValue && rhs instanceof IntegralValue) {
    if (opcode === 'Add') {
      const address = lhs.address + rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
    if (opcode === 'Sub') {
      const address = lhs.address - rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
  }
  if (lhs instanceof IntegralValue && rhs instanceof PointerValue) {
    if (opcode === 'Add') {
      const address = rhs.address + lhs.number * rhs.type.pointee.size;
      return new PointerValue(rhs.type, address);
    }
  }
  if (lhs instanceof PointerValue && rhs instanceof PointerValue) {
    if (opcode === 'Sub') {
      const offset = lhs.address - rhs.address;
      return new IntegralValue(builtinTypes['int'], offset);
    }
  }
  throw new Error(`not implemented: ${lhs} ${opcode} ${rhs}`);
};

export const evalUnaryOperation = function (opcode, operand) {
  if (operand instanceof IntegralValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new IntegralValue(operand.type, -operand.number);
      case 'LNot': return new IntegralValue(builtinTypes['int'], !operand.toBool());
      case 'Not': return new IntegralValue(operand.type, ~operand.number);
    }
  }
  if (operand instanceof FloatingValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new FloatingValue(operand.type, -operand.number);
    }
  }
  throw new Error(`not implemented: ${opcode} ${operand}`);
};

export const evalCast = function (type, operand) {
  if (type.kind === 'builtin') {
    if (/^(unsigned )?char$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xff);
    }
    if (/^(unsigned )?short$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffff);
    }
    if (/^(unsigned )?(int|long)$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffffffff);
    }
    if (/^(unsigned )?long long$/.test(type.repr)) {
      // XXX this only works up to 2^53, use npm:long
      return new IntegralValue(type, operand.toInteger());
    }
    if (type.repr === 'float') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number)
      }
    }
    if (type.repr === 'double') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number);
      }
    }
  }
  if (type.kind === 'pointer') {
    if (operand instanceof PointerValue) {
      return new PointerValue(type, operand.address);
    }
    if (operand instanceof IntegralValue) {
      return new PointerValue(type, operand.toInteger())
    }
    if (operand instanceof BuiltinValue || operand instanceof FunctionValue) {
      // XXX temporary cheat for non-addressable values.
      return operand;
    }
  }
  throw new Error(`not implemented: (${type})${operand}`);
};

export const zeroAtType = function (type) {
  if (type.kind === 'pointer') {
    return new PointerValue(type, 0);
  }
  if (type.kind === 'builtin') {
    switch (type.repr) {
      case 'char':
      case 'unsigned char':
      case 'short':
      case 'unsigned short':
      case 'int':
      case 'unsigned int':
      case 'long':
      case 'unsigned long':
      case 'long long':
      case 'unsigned long long':
        return new IntegralValue(type, 0);
      case 'float':
      case 'double':
        return new FloatingValue(type, 0);
    }
  }
  throw new Error(`undefined zero at type ${type.kind}`);
};
<commit_msg>clean up<commit_after>
// TextEncoder shim for older browsers and Safari.
import {TextEncoder} from 'text-encoding-utf-8';

import {builtinTypes, arrayType, lubType, decayedType, pointerType, functionType} from './type';

export function IntegralValue (type, number) {
  this.type = type;
  if (/^unsigned/.test(this.type.repr)) {
    this.number = number >>> 0;
  } else {
    this.number = number | 0;
  }
};
IntegralValue.prototype.toString = function () {
  if (this.type.repr === 'char') {
    if (this.number >= 32 && this.number < 128) {
      switch (this.number) {
        case 39: return '\\\'';
        case 92: return '\\\\';
        default: return `'${String.fromCharCode(this.number)}'`;
      }
    } else {
      switch (this.number) {
        case 0: return '\\0';
        case 8: return '\\t';
        case 10: return '\\r';
        case 13: return '\\n';
        default: {
          const n = this.number + (this.number >= 0 ? 0 : 256);
          return `'\\x${n.toString(16)}'`;
        }
      }
    }
  } else {
    return this.number.toString();
  }
};
IntegralValue.prototype.toInteger = function () {
  return this.number;
};
IntegralValue.prototype.toBool = function () {
  return 0 !== this.number;
};
IntegralValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'char':
      view.setInt8(offset, this.number);
      break;
    case 'unsigned char':
      view.setUint8(offset, this.number);
      break;
    case 'short':
      view.setInt16(offset, this.number, littleEndian);
      break;
    case 'unsigned short':
      view.setUint16(offset, this.number, littleEndian);
      break;
    case 'int':
    case 'long':
      view.setInt32(offset, this.number, littleEndian);
      break;
    case 'unsigned int':
    case 'unsigned long':
      view.setUint32(offset, this.number, littleEndian);
      break;
    default:
      throw new Error(`cannot pack integral value ${this.type.repr}`);
  }
};

export function FloatingValue (type, number) {
  this.type = type;
  this.number = type.size === 4 ? Math.fround(number) : number;
};
FloatingValue.prototype.toString = function () {
  let str = this.number.toFixed(6);
  // Trim the trailing zeros, and the decimal point if there are no digits
  // to its right.
  str = str.replace(/(\.[0-9]*?)0+$/, function (m, n) {
    return n === '.' ? '' : n;
  });
  return str;
};
FloatingValue.prototype.toInteger = function () {
  return this.number | 0;
};
FloatingValue.prototype.toBool = function () {
  return 0 !== this.number;
};
FloatingValue.prototype.pack = function (view, offset, littleEndian) {
  switch (this.type.repr) {
    case 'float':
      view.setFloat32(offset, this.number, littleEndian);
      break;
    case 'double':
      view.setFloat64(offset, this.number, littleEndian);
      break;
    default:
      throw new Error(`cannot pack floating value ${this.type.repr}`);
  }
};

export function PointerValue (type, address) {
  this.type = type;
  this.address = address | 0;
};
PointerValue.prototype.toString = function () {
  return `0x${this.address.toString(16)}`;
};
PointerValue.prototype.toInteger = function () {
  return this.address;
};
PointerValue.prototype.toBool = function () {
  return 0 !== this.address;
};
PointerValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint32(offset, this.address, littleEndian);
};

export const makeRef = function (elemType, address) {
  let refType;
  if (elemType.kind === 'array') {
    // A array reference decays to a pointer to its first element.
    refType = decayedType(elemType);
  } else {
    refType = pointerType(elemType)
  }
  return new PointerValue(refType, address);
};

export function ArrayValue (type, elements) {
  this.type = type;
  this.elements = elements;
};
ArrayValue.prototype.toString = function () {
  return `array`;
};
ArrayValue.prototype.pack = function (view, offset, littleEndian) {
  const elemSize = this.type.elem.size;
  this.elements.forEach(function (elem, index) {
    packValue(view, offset + index * elemSize, elem, littleEndian);
  });
};

export function RecordValue (type, props) {
  this.type = type;
  this.props = props;
};
RecordValue.prototype.toString = function () {
  return `record`;
};
RecordValue.prototype.pack = function (view, offset, littleEndian) {
  const {type: {fields, fieldMap}, props} = this;
  // {offset, type, refType}
  for (let name of fields) {
    const value = props[name];
    if (value) {
      const {offset: fieldOffset} = fieldMap[name];
      console.log('pack field', offset + fieldOffset, value);
      packValue(view, offset + fieldOffset, value, littleEndian);
    }
  }
};

export function FunctionValue (type, codePtr, name, body, decl) {
  this.type = pointerType(type);
  this.codePtr = codePtr;
  this.name = name;
  this.body = body;
  this.decl = decl;
};
FunctionValue.prototype.toString = function () {
  return `&${this.name}`;
}
FunctionValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint16(offset, this.codePtr, littleEndian);
};
FunctionValue.prototype.toInteger = function () {
  return this.codePtr;
};

export function BuiltinValue (type, codePtr, name, func) {
  this.type = type;
  this.codePtr = codePtr;
  this.name = name;
  this.func = func;
};
BuiltinValue.prototype.toString = function () {
  return `&${this.name}`;
}
BuiltinValue.prototype.pack = function (view, offset, littleEndian) {
  view.setUint16(offset, this.codePtr, littleEndian);
};
BuiltinValue.prototype.toInteger = function () {
  return this.codePtr;
};

export const badFunction = new BuiltinValue (
  functionType(builtinTypes['void'], []), 0, "bad_func",
  function (state, cont, values) {
    return {error: `bad function`};
  });

export const packValue = function (view, offset, value, littleEndian) {
  value.pack(view, offset, littleEndian);
};

export const unpackValue = function (view, offset, type, littleEndian, core) {
  switch (type.kind) {
    case 'builtin':
      switch (type.repr) {
        case 'char':
          return new IntegralValue(type, view.getInt8(offset));
        case 'unsigned char':
          return new IntegralValue(type, view.getUint8(offset));
        case 'short':
          return new IntegralValue(type, view.getInt16(offset, littleEndian));
        case 'unsigned short':
          return new IntegralValue(type, view.getUint16(offset, littleEndian));
        case 'int':
        case 'long':
          return new IntegralValue(type, view.getInt32(offset, littleEndian));
        case 'unsigned int':
        case 'unsigned long':
          return new IntegralValue(type, view.getUint32(offset, littleEndian));
        case 'float':
          return new FloatingValue(type, view.getFloat32(offset, littleEndian));
        case 'double':
          return new FloatingValue(type, view.getFloat64(offset, littleEndian));
        default:
          throw new Error(`unpack builtin ${type.repr}`);
      }
    case 'array':
      {
        const elemType = type.elem;
        const elemSize = elemType.size;
        const elements = [];
        for (var index = 0; index < type.count; index++) {
          elements.push(unpackValue(view, offset + index * elemSize, elemType, littleEndian, core));
        }
        return new ArrayValue(type, elements);
      }
    case 'record':
      {
        const props = {};
        for (let name of type.fields) {
          const {offset: fieldOffset, type: fieldType} = type.fieldMap[name];
          props[name] = unpackValue(view, offset + fieldOffset, fieldType, littleEndian, core);
        }
        return new RecordValue(type, props);
      }
    case 'pointer': {
      if (type.pointee.kind === 'function') {
        const codePtr = view.getUint16(offset, littleEndian);
        return core.functions[codePtr] || badFunction;
      } else {
        const address = view.getUint32(offset, littleEndian);
        return new PointerValue(type, address);
      }
    }
    default:
      throw new Error(`not implemented: unpack ${type.kind}`);
  }
};

export const stringValue = function (string) {
  const encoder = new TextEncoder('utf-8');
  const bytesArray = encoder.encode(string);
  const charType = builtinTypes['char'];
  const charLen = bytesArray.length;
  const chars = [];
  for (let charPos = 0; charPos < charLen; charPos++) {
    chars.push(new IntegralValue(charType, bytesArray[charPos]));
  }
  chars.push(new IntegralValue(charType, 0));
  const lenValue = new IntegralValue(builtinTypes['int'], chars.length);
  return new ArrayValue(arrayType(charType, lenValue), chars);
};

const isRelational = function (op) {
  return /^(EQ|NE|LT|LE|GT|GE)$/.test(op);
};

const evalRelationalOperation = function (op, v1, v2) {
  switch (op) {
    case 'EQ':  return v1 === v2;
    case 'NE':  return v1 !== v2;
    case 'LT':  return v1 < v2;
    case 'LE':  return v1 <= v2;
    case 'GT':  return v1 > v2;
    case 'GE':  return v1 >= v2;
  }
};

const evalIntegerBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
    case 'Rem': case 'RemAssign': return v1 % v2;
    case 'And': case 'AndAssign': return v1 & v2;
    case 'Or':  case 'OrAssign':  return v1 | v2;
    case 'Xor': case 'XorAssign': return v1 ^ v2;
    case 'Shl': case 'ShlAssign': return v1 << v2;
    case 'Shr': case 'ShrAssign': return v1 >> v2;
  }
};

const evalFloatingBinaryOperation = function (op, v1, v2) {
  switch (op) {
    case 'Add': case 'AddAssign': return v1 + v2;
    case 'Sub': case 'SubAssign': return v1 - v2;
    case 'Mul': case 'MulAssign': return v1 * v2;
    case 'Div': case 'DivAssign': return v1 / v2;
  }
};

export const evalBinaryOperation = function (opcode, lhs, rhs) {
  // Relational operators
  if (isRelational(opcode)) {
    const result = evalRelationalOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(builtinTypes['int'], result ? 1 : 0);
  }
  // Integer arithmetic
  if (lhs instanceof IntegralValue && rhs instanceof IntegralValue) {
    const result = evalIntegerBinaryOperation(opcode, lhs.number, rhs.number);
    return new IntegralValue(lubType(lhs.type, rhs.type), result);
  }
  // Float arithmetic
  if (lhs instanceof FloatingValue && rhs instanceof FloatingValue) {
    const result = evalFloatingBinaryOperation(opcode, lhs.number, rhs.number)
    return new FloatingValue(lubType(lhs.type, rhs.type), result);
  }
  // Pointer arithmetic
  if (lhs instanceof PointerValue && rhs instanceof IntegralValue) {
    if (opcode === 'Add') {
      const address = lhs.address + rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
    if (opcode === 'Sub') {
      const address = lhs.address - rhs.number * lhs.type.pointee.size;
      return new PointerValue(lhs.type, address);
    }
  }
  if (lhs instanceof IntegralValue && rhs instanceof PointerValue) {
    if (opcode === 'Add') {
      const address = rhs.address + lhs.number * rhs.type.pointee.size;
      return new PointerValue(rhs.type, address);
    }
  }
  if (lhs instanceof PointerValue && rhs instanceof PointerValue) {
    if (opcode === 'Sub') {
      const offset = lhs.address - rhs.address;
      return new IntegralValue(builtinTypes['int'], offset);
    }
  }
  throw new Error(`not implemented: ${lhs} ${opcode} ${rhs}`);
};

export const evalUnaryOperation = function (opcode, operand) {
  if (operand instanceof IntegralValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new IntegralValue(operand.type, -operand.number);
      case 'LNot': return new IntegralValue(builtinTypes['int'], !operand.toBool());
      case 'Not': return new IntegralValue(operand.type, ~operand.number);
    }
  }
  if (operand instanceof FloatingValue) {
    switch (opcode) {
      case 'Plus': return operand;
      case 'Minus': return new FloatingValue(operand.type, -operand.number);
    }
  }
  throw new Error(`not implemented: ${opcode} ${operand}`);
};

export const evalCast = function (type, operand) {
  if (type.kind === 'builtin') {
    if (/^(unsigned )?char$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xff);
    }
    if (/^(unsigned )?short$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffff);
    }
    if (/^(unsigned )?(int|long)$/.test(type.repr)) {
      return new IntegralValue(type, operand.toInteger() & 0xffffffff);
    }
    if (/^(unsigned )?long long$/.test(type.repr)) {
      // XXX this only works up to 2^53, use npm:long
      return new IntegralValue(type, operand.toInteger());
    }
    if (type.repr === 'float') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number)
      }
    }
    if (type.repr === 'double') {
      if (operand instanceof FloatingValue || operand instanceof IntegralValue) {
        return new FloatingValue(type, operand.number);
      }
    }
  }
  if (type.kind === 'pointer') {
    if (operand instanceof PointerValue) {
      return new PointerValue(type, operand.address);
    }
    if (operand instanceof IntegralValue) {
      return new PointerValue(type, operand.toInteger())
    }
    if (operand instanceof BuiltinValue || operand instanceof FunctionValue) {
      // XXX temporary cheat for non-addressable values.
      return operand;
    }
  }
  throw new Error(`not implemented: (${type})${operand}`);
};

export const zeroAtType = function (type) {
  if (type.kind === 'pointer') {
    return new PointerValue(type, 0);
  }
  if (type.kind === 'builtin') {
    switch (type.repr) {
      case 'char':
      case 'unsigned char':
      case 'short':
      case 'unsigned short':
      case 'int':
      case 'unsigned int':
      case 'long':
      case 'unsigned long':
      case 'long long':
      case 'unsigned long long':
        return new IntegralValue(type, 0);
      case 'float':
      case 'double':
        return new FloatingValue(type, 0);
    }
  }
  throw new Error(`undefined zero at type ${type.kind}`);
};
<|endoftext|>


<commit_before>var isRetina = require('./retina')();

var Video = function(app, canvas, config) {
  this.app = app;

  this.config = config;

  this.canvas = canvas;

  this.canvas.style.width = '100%';
  this.canvas.style.height = '100%';

  this.width = app.width;

  this.height = app.height;

  this._parent = null;
  this._isRoot = false;
  this._children = [];
};

Video.prototype.init = function() {
  if (this.config.getCanvasContext) {
    this.ctx = this.canvas.getContext('2d');
  }

  this._applySizeToCanvas();
};

Video.prototype.include = function(methods) {
  for (var method in methods) {
    this[method] = methods[method];
  }
};

Video.prototype.beginFrame = function() {};

Video.prototype.endFrame = function() {};

Video.prototype.destroy = function() {
  if (!this._isRoot) {
    var index = this._parent._children.indexOf(this);
    if (index !== -1) {
      this._parent._children.splice(index, 1);
    }
  }

  this.canvas.parentElement.removeChild(this.canvas);
};

Video.prototype.scaleCanvas = function(scale) {
  this.canvas.width *= scale;
  this.canvas.height *= scale;

  if (this.ctx) {
    this.ctx.scale(scale, scale);
  }
};

Video.prototype._resize = function(width, height) {
  this.width = width;
  this.height = height;

  this._applySizeToCanvas();

  for (var i=0, len=this._children.length; i<len; i++) {
    var item = this._children[i];
    item._resize(width, height);
  }
};

Video.prototype._applySizeToCanvas = function() {
  this.canvas.width = this.width;
  this.canvas.height = this.height;

  if (this.config.allowHiDPI && isRetina) {
    this.scaleCanvas(2);
  }
};

Video.prototype.clear = function() {
  if (this.ctx) { this.ctx.clearRect(0, 0, this.width, this.height); }
};

Video.prototype.createLayer = function(config) {
  config = config || {};

  var container = this.app.container;
  var canvas = document.createElement('canvas');
  canvas.width = this.width;
  canvas.height = this.height;
  canvas.style.position = 'absolute';
  canvas.style.top = '0px';
  canvas.style.left = '0px';
  container.appendChild(canvas);

  var video = new Video(this.app, canvas, config);

  video._parent = this;
  video._isRoot = false;

  video.init();

  this._children.push(video);

  return video;
};

module.exports = Video;
<commit_msg>set canvas container size in _resize method<commit_after>var isRetina = require('./retina')();

var Video = function(app, canvas, config) {
  this.app = app;

  this.config = config;

  this.canvas = canvas;

  this.width = app.width;

  this.height = app.height;

  this._parent = null;
  this._isRoot = false;
  this._children = [];
};

Video.prototype.init = function() {
  if (this.config.getCanvasContext) {
    this.ctx = this.canvas.getContext('2d');
  }

  this._applySizeToCanvas();
};

Video.prototype.include = function(methods) {
  for (var method in methods) {
    this[method] = methods[method];
  }
};

Video.prototype.beginFrame = function() {};

Video.prototype.endFrame = function() {};

Video.prototype.destroy = function() {
  if (!this._isRoot) {
    var index = this._parent._children.indexOf(this);
    if (index !== -1) {
      this._parent._children.splice(index, 1);
    }
  }

  this.canvas.parentElement.removeChild(this.canvas);
};

Video.prototype.scaleCanvas = function(scale) {
  this.canvas.width *= scale;
  this.canvas.height *= scale;

  if (this.ctx) {
    this.ctx.scale(scale, scale);
  }
};

Video.prototype._resize = function(width, height) {
  this.width = width;
  this.height = height;

  this.canvas.style.width = '100%';
  this.canvas.style.height = '100%';

  this._applySizeToCanvas();

  for (var i=0, len=this._children.length; i<len; i++) {
    var item = this._children[i];
    item._resize(width, height);
  }
};

Video.prototype._applySizeToCanvas = function() {
  this.canvas.width = this.width;
  this.canvas.height = this.height;

  if (this.config.allowHiDPI && isRetina) {
    this.scaleCanvas(2);
  }
};

Video.prototype.clear = function() {
  if (this.ctx) { this.ctx.clearRect(0, 0, this.width, this.height); }
};

Video.prototype.createLayer = function(config) {
  config = config || {};

  var container = this.app.container;
  var canvas = document.createElement('canvas');
  canvas.width = this.width;
  canvas.height = this.height;
  canvas.style.position = 'absolute';
  canvas.style.top = '0px';
  canvas.style.left = '0px';
  container.appendChild(canvas);

  var video = new Video(this.app, canvas, config);

  video._parent = this;
  video._isRoot = false;

  video.init();

  this._children.push(video);

  return video;
};

module.exports = Video;
<|endoftext|>


<commit_before>var pumpkinImages = [
  "http://i.imgur.com/x6DeXSP.png",
  "http://i.imgur.com/jiLxvQC.png",
  "http://i.imgur.com/MKMIEAj.png",
  "http://i.imgur.com/ufguJsr.png"
].map(function(url) {
  var el = document.createElement('img');
  el.src = url;
  return el;
});

var pumpkinIndex = 0;

var pumpkinImageEl = document.getElementById('asteroid');

var getUrlParameter = function getUrlParameter(sParam) {
    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
        sURLVariables = sPageURL.split('&'),
        sParameterName,
        i;

    for (i = 0; i < sURLVariables.length; i++) {
        sParameterName = sURLVariables[i].split('=');

        if (sParameterName[0] === sParam) {
            return sParameterName[1] === undefined ? true : sParameterName[1];
        }
    }
};

var field = document.getElementById("field");
var f = field.getContext("2d");

var stars = {};
var starIndex = 0;
var numStars = 0;
var acceleration = 1;
var starsToDraw = (field.width * field.height) / 500;
if (getUrlParameter("stars")) {
  starsToDraw = getUrlParameter("stars");
}
if (getUrlParameter("accel")) {
  acceleration = getUrlParameter("accel");
}

function Star() {
    this.X = field.width / 2;
    this.Y = field.height / 2;

    this.SX = Math.random() * 10 - 5;
    this.SY = Math.random() * 10 - 5;

    var start = 0;

    if (field.width > field.height)
        start = field.width;
    else
        start = field.height;

    this.X += this.SX * start / 10;
    this.Y += this.SY * start / 10;

    if (Math.random() < 0.02) {
      this.image = pumpkinImages[pumpkinIndex =
        (pumpkinIndex + 1) % pumpkinImages.length];
      this.W = 20;
      this.H = 20;
    } else {
    this.W = 1;
    this.H = 1;

    }

    this.age = 0;
    this.dies = 500;

    starIndex++;
    stars[starIndex] = this;

    this.ID = starIndex;
    this.C = "#ffffff";

}

Star.prototype.Draw = function () {
    this.X += this.SX;
    this.Y += this.SY;

    this.SX += this.SX / (50 / acceleration);
  	this.SY += this.SY / (50 / acceleration);

    this.age++;

    var starGrowFactor = this.image? 50 : 1;

    this.W = this.H = Math.max(1, this.age * starGrowFactor / 75);

    if (this.X + this.W < 0 | this.X > field.width |
        this.Y + this.H < 0 | this.Y > field.height)
      {
        delete stars[this.ID];
        numStars--;
			}

    f.fillStyle = this.C;
    if (this.image) {
      f.drawImage(this.image, this.X, this.Y, this.W, this.H);
    } else {
      f.fillRect(this.X, this.Y, this.W, this.H);
    }
}

field.width = window.innerWidth;
field.height = window.innerHeight;

function draw() {
  	if (field.width != window.innerWidth)
      	field.width = window.innerWidth;
  	if (field.height != window.innerHeight)
      	field.height = window.innerHeight;

  	// Play with the "a" value to create streams...it's fun!
    f.fillStyle = "rgba(0, 0, 0, 0.8)";
    f.fillRect(0, 0, field.width, field.height);

    for (var i = numStars; i < starsToDraw; i++) {
        new Star();
        numStars++;
    }

    for (var star in stars) {
        stars[star].Draw();
    }
}

// Original timing of the screensaver
setInterval(draw, 40);
<commit_msg>Use set instead of weird leaky object<commit_after>var pumpkinImages = [
  "http://i.imgur.com/x6DeXSP.png",
  "http://i.imgur.com/jiLxvQC.png",
  "http://i.imgur.com/MKMIEAj.png",
  "http://i.imgur.com/ufguJsr.png"
].map(function(url) {
  var el = document.createElement('img');
  el.src = url;
  return el;
});

var pumpkinIndex = 0;

var pumpkinImageEl = document.getElementById('asteroid');

var getUrlParameter = function getUrlParameter(sParam) {
    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
        sURLVariables = sPageURL.split('&'),
        sParameterName,
        i;

    for (i = 0; i < sURLVariables.length; i++) {
        sParameterName = sURLVariables[i].split('=');

        if (sParameterName[0] === sParam) {
            return sParameterName[1] === undefined ? true : sParameterName[1];
        }
    }
};

var field = document.getElementById("field");
var f = field.getContext("2d");

var stars = new Set();
var starIndex = 0;
var numStars = 0;
var acceleration = 1;
var starsToDraw = (field.width * field.height) / 500;
if (getUrlParameter("stars")) {
  starsToDraw = getUrlParameter("stars");
}
if (getUrlParameter("accel")) {
  acceleration = getUrlParameter("accel");
}

function Star() {
    this.X = field.width / 2;
    this.Y = field.height / 2;

    this.SX = Math.random() * 10 - 5;
    this.SY = Math.random() * 10 - 5;

    var start = 0;

    if (field.width > field.height)
        start = field.width;
    else
        start = field.height;

    this.X += this.SX * start / 10;
    this.Y += this.SY * start / 10;

    if (Math.random() < 0.02) {
      this.image = pumpkinImages[pumpkinIndex =
        (pumpkinIndex + 1) % pumpkinImages.length];
      this.W = 20;
      this.H = 20;
    } else {
    this.W = 1;
    this.H = 1;

    }

    this.age = 0;
    this.dies = 500;

    stars.add(this);

    this.ID = starIndex;
    this.C = "#ffffff";

}

Star.prototype.Draw = function () {
    this.X += this.SX;
    this.Y += this.SY;

    this.SX += this.SX / (50 / acceleration);
  	this.SY += this.SY / (50 / acceleration);

    this.age++;

    var starGrowFactor = this.image? 50 : 1;

    this.W = this.H = Math.max(1, this.age * starGrowFactor / 75);

    if (this.X + this.W < 0 | this.X > field.width |
        this.Y + this.H < 0 | this.Y > field.height)
      {
        stars.delete(this);
			}

    f.fillStyle = this.C;
    if (this.image) {
      f.drawImage(this.image, this.X, this.Y, this.W, this.H);
    } else {
      f.fillRect(this.X, this.Y, this.W, this.H);
    }
}

field.width = window.innerWidth;
field.height = window.innerHeight;

function draw() {
  	if (field.width != window.innerWidth)
      	field.width = window.innerWidth;
  	if (field.height != window.innerHeight)
      	field.height = window.innerHeight;

  	// Play with the "a" value to create streams...it's fun!
    f.fillStyle = "rgba(0, 0, 0, 0.8)";
    f.fillRect(0, 0, field.width, field.height);

    for (var i = stars.size; i < starsToDraw; i++) {
        new Star();
    }

    for (var star of stars) {
        star.Draw();
    }
}

// Original timing of the screensaver
setInterval(draw, 40);
<|endoftext|>


<commit_before>/* eslint react/sort-comp: 0 */

import _ from 'lodash';
import React from 'react';
import Reddit from './reddit.js';
import dispatcher from '../../core/dispatcher.js';
import RedditActions from '../../reddit/actions.js';
import { appState } from '../../core/state.js';

class RedditController extends React.Component {
  constructor(props) {
    super(props);
  }

  componentWillMount() {
    this.state = appState.getState();
    this.fetchPostsIfEmpty();
  }

  componentWillUpdate() {
    this.state = appState.getState();
    this.fetchPostsIfEmpty();
  }

  getPosts() {
    const activeListingType = this.state.activeListingType;
    const activeSortRange = this.state.activeSortRange;
    const storageKey = activeListingType === 'top' ?
      `${activeListingType}:${activeSortRange}` : `${activeListingType}:`;
    return this.state.subreddits[this.state.activeSubreddit][storageKey];
  }

  onActiveSubredditChange(e) {
    let newSubreddit = e.currentTarget.getAttribute('data-payload');
    if (newSubreddit) {
      let action = RedditActions.setActiveSubreddit(newSubreddit);
      dispatcher.dispatch(action);
    }
  }

  setActiveListingType(listingType) {
    let action = RedditActions.setActiveListingType(listingType);
    dispatcher.dispatch(action);
  }

  setActiveSortRange(sortRange) {
    let action = RedditActions.setActiveSortRange(sortRange);
    dispatcher.dispatch(action);
  }

  fetchPosts() {
    const activeSubreddit = this.state.activeSubreddit;
    const activeListingType = this.state.activeListingType;
    const activeSortRange = this.state.activeSortRange;
    const posts = this.getPosts();
    const after = (_.last(posts) || {}).name;
    const limit = 5;
    let action = RedditActions.fetchPosts(
      activeSubreddit, activeListingType, activeSortRange, after, limit);
    dispatcher.dispatch(action);
  }

  fetchPostsIfEmpty() {
    if (_.isEmpty(this.getPosts())) {
      this.fetchPosts();
    }
  }

  render() {
    let subreddits = _.keys(this.state.subreddits);
    let activeSubreddit = this.state.activeSubreddit;
    let listingTypes = this.state.listingTypes;
    let sortRanges = this.state.sortRanges;
    let activeListingType = this.state.activeListingType;
    let activeSortRange = this.state.activeSortRange;
    let posts = this.getPosts();

    return (
      <Reddit
        posts={posts}
        subreddits={subreddits}
        activeSubreddit={activeSubreddit}
        listingTypes={listingTypes}
        activeListingType={activeListingType}
        sortRanges={sortRanges}
        activeSortRange={activeSortRange}
        onActiveSubredditChange={this.onActiveSubredditChange.bind(this)}
        setActiveListingType={this.setActiveListingType.bind(this)}
        setActiveSortRange={this.setActiveSortRange.bind(this)}
        fetchPosts={this.fetchPosts.bind(this)} />
    );
  }
}

export default RedditController;
<commit_msg>Fetch 15 songs by default<commit_after>/* eslint react/sort-comp: 0 */

import _ from 'lodash';
import React from 'react';
import Reddit from './reddit.js';
import dispatcher from '../../core/dispatcher.js';
import RedditActions from '../../reddit/actions.js';
import { appState } from '../../core/state.js';

class RedditController extends React.Component {
  constructor(props) {
    super(props);
  }

  componentWillMount() {
    this.state = appState.getState();
    this.fetchPostsIfEmpty();
  }

  componentWillUpdate() {
    this.state = appState.getState();
    this.fetchPostsIfEmpty();
  }

  getPosts() {
    const activeListingType = this.state.activeListingType;
    const activeSortRange = this.state.activeSortRange;
    const storageKey = activeListingType === 'top' ?
      `${activeListingType}:${activeSortRange}` : `${activeListingType}:`;
    return this.state.subreddits[this.state.activeSubreddit][storageKey];
  }

  onActiveSubredditChange(e) {
    let newSubreddit = e.currentTarget.getAttribute('data-payload');
    if (newSubreddit) {
      let action = RedditActions.setActiveSubreddit(newSubreddit);
      dispatcher.dispatch(action);
    }
  }

  setActiveListingType(listingType) {
    let action = RedditActions.setActiveListingType(listingType);
    dispatcher.dispatch(action);
  }

  setActiveSortRange(sortRange) {
    let action = RedditActions.setActiveSortRange(sortRange);
    dispatcher.dispatch(action);
  }

  fetchPosts() {
    const activeSubreddit = this.state.activeSubreddit;
    const activeListingType = this.state.activeListingType;
    const activeSortRange = this.state.activeSortRange;
    const posts = this.getPosts();
    const after = (_.last(posts) || {}).name;
    const limit = 15;
    let action = RedditActions.fetchPosts(
      activeSubreddit, activeListingType, activeSortRange, after, limit);
    dispatcher.dispatch(action);
  }

  fetchPostsIfEmpty() {
    if (_.isEmpty(this.getPosts())) {
      this.fetchPosts();
    }
  }

  render() {
    let subreddits = _.keys(this.state.subreddits);
    let activeSubreddit = this.state.activeSubreddit;
    let listingTypes = this.state.listingTypes;
    let sortRanges = this.state.sortRanges;
    let activeListingType = this.state.activeListingType;
    let activeSortRange = this.state.activeSortRange;
    let posts = this.getPosts();

    return (
      <Reddit
        posts={posts}
        subreddits={subreddits}
        activeSubreddit={activeSubreddit}
        listingTypes={listingTypes}
        activeListingType={activeListingType}
        sortRanges={sortRanges}
        activeSortRange={activeSortRange}
        onActiveSubredditChange={this.onActiveSubredditChange.bind(this)}
        setActiveListingType={this.setActiveListingType.bind(this)}
        setActiveSortRange={this.setActiveSortRange.bind(this)}
        fetchPosts={this.fetchPosts.bind(this)} />
    );
  }
}

export default RedditController;
<|endoftext|>