/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Created by m.sridharan on 11/6/14.
 */
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    ___LoggingAnalysis___.isBrowser = J$.Constants.isBrowser;
    // IID special values: -1 is unknown, -2 corresponds to the initial
    // DOM traversal to attach mutation observers
    (function (LogEntryType) {
        LogEntryType[LogEntryType["DECLARE"] = 0] = "DECLARE";
        LogEntryType[LogEntryType["CREATE_OBJ"] = 1] = "CREATE_OBJ";
        LogEntryType[LogEntryType["CREATE_FUN"] = 2] = "CREATE_FUN";
        LogEntryType[LogEntryType["PUTFIELD"] = 3] = "PUTFIELD";
        LogEntryType[LogEntryType["WRITE"] = 4] = "WRITE";
        LogEntryType[LogEntryType["LAST_USE"] = 5] = "LAST_USE";
        LogEntryType[LogEntryType["FUNCTION_ENTER"] = 6] = "FUNCTION_ENTER";
        LogEntryType[LogEntryType["FUNCTION_EXIT"] = 7] = "FUNCTION_EXIT";
        LogEntryType[LogEntryType["TOP_LEVEL_FLUSH"] = 8] = "TOP_LEVEL_FLUSH";
        LogEntryType[LogEntryType["UPDATE_IID"] = 9] = "UPDATE_IID";
        LogEntryType[LogEntryType["DEBUG"] = 10] = "DEBUG";
        LogEntryType[LogEntryType["RETURN"] = 11] = "RETURN";
        LogEntryType[LogEntryType["CREATE_DOM_NODE"] = 12] = "CREATE_DOM_NODE";
        LogEntryType[LogEntryType["ADD_DOM_CHILD"] = 13] = "ADD_DOM_CHILD";
        LogEntryType[LogEntryType["REMOVE_DOM_CHILD"] = 14] = "REMOVE_DOM_CHILD";
        LogEntryType[LogEntryType["ADD_TO_CHILD_SET"] = 15] = "ADD_TO_CHILD_SET";
        LogEntryType[LogEntryType["REMOVE_FROM_CHILD_SET"] = 16] = "REMOVE_FROM_CHILD_SET";
        LogEntryType[LogEntryType["DOM_ROOT"] = 17] = "DOM_ROOT";
        LogEntryType[LogEntryType["CALL"] = 18] = "CALL";
        LogEntryType[LogEntryType["SCRIPT_ENTER"] = 19] = "SCRIPT_ENTER";
        LogEntryType[LogEntryType["SCRIPT_EXIT"] = 20] = "SCRIPT_EXIT";
        LogEntryType[LogEntryType["FREE_VARS"] = 21] = "FREE_VARS";
        LogEntryType[LogEntryType["SOURCE_MAPPING"] = 22] = "SOURCE_MAPPING"; // fields: iid, filename, startLine, startColumn
    })(___LoggingAnalysis___.LogEntryType || (___LoggingAnalysis___.LogEntryType = {}));
    var LogEntryType = ___LoggingAnalysis___.LogEntryType;
    /**
     * making this enum lets the TypeScript compiler inline the constants
     */
    (function (Constants) {
        Constants[Constants["MUT_OBSERVER_IID"] = -1] = "MUT_OBSERVER_IID";
        Constants[Constants["INIT_DOM_TRAVERSAL_IID"] = -2] = "INIT_DOM_TRAVERSAL_IID";
        Constants[Constants["MAX_BUF_SIZE"] = 64000] = "MAX_BUF_SIZE";
    })(___LoggingAnalysis___.Constants || (___LoggingAnalysis___.Constants = {}));
    var Constants = ___LoggingAnalysis___.Constants;
    (function (FlushIIDSpecial) {
        FlushIIDSpecial[FlushIIDSpecial["ALREADY_FLUSHED"] = -2] = "ALREADY_FLUSHED";
        FlushIIDSpecial[FlushIIDSpecial["UNKNOWN"] = -1] = "UNKNOWN";
    })(___LoggingAnalysis___.FlushIIDSpecial || (___LoggingAnalysis___.FlushIIDSpecial = {}));
    var FlushIIDSpecial = ___LoggingAnalysis___.FlushIIDSpecial;
    ___LoggingAnalysis___.GLOBAL_OBJ = (function () {
        return this;
    })();
    function isObject(o) {
        return o && (typeof o === 'object' || typeof o === 'function');
    }
    ___LoggingAnalysis___.isObject = isObject;
    // some defense against monkey-patching
    var objGetOwnPropDesc = Object.getOwnPropertyDescriptor;
    var objGetPrototypeOf = Object.getPrototypeOf;
    var objProtoHasOwnProperty = Object.prototype.hasOwnProperty;
    var objDefProperty = Object.defineProperty;
    function getPropertyDescriptor(o, prop) {
        var t = o;
        while (t != null) {
            var desc = objGetOwnPropDesc(t, prop);
            if (desc) {
                return desc;
            }
            t = objGetPrototypeOf(t);
        }
        return null;
    }
    ___LoggingAnalysis___.getPropertyDescriptor = getPropertyDescriptor;
    function isGetterSetter(o, prop) {
        var desc = getPropertyDescriptor(o, prop);
        return desc && (desc.set !== undefined || desc.get !== undefined);
    }
    ___LoggingAnalysis___.isGetterSetter = isGetterSetter;
    function HOP(o, prop) {
        return objProtoHasOwnProperty.call(o, prop);
    }
    ___LoggingAnalysis___.HOP = HOP;
    function objDefineProperty(o, p, attributes) {
        return objDefProperty(o, p, attributes);
    }
    ___LoggingAnalysis___.objDefineProperty = objDefineProperty;
    var funEnterRegExp = /J\$\.Fe\(([0-9]+)/;
    /**
     * cache for optimization
     * @type {WeakMap<K, V>}
     */
    var instFunction2EnterIID = typeof WeakMap === 'undefined' ? undefined : new WeakMap();
    var funEnterIIDHiddenProp = "*HP$*";
    ;
    function getFunEnterIID(f) {
        var parsed = funEnterRegExp.exec(f.toString());
        var result;
        if (parsed) {
            result = parseInt(parsed[1]);
            setCachedFunEnterIID(f, result);
        }
        else {
            result = -1 /* UNKNOWN */;
        }
        return result;
    }
    ___LoggingAnalysis___.getFunEnterIID = getFunEnterIID;
    function setCachedFunEnterIID(f, enterIID) {
        if (instFunction2EnterIID) {
            instFunction2EnterIID.set(f, enterIID);
        }
        else {
            // use a hidden property
            objDefineProperty(f, funEnterIIDHiddenProp, {
                enumerable: false,
                writable: true
            });
            f[funEnterIIDHiddenProp] = enterIID;
        }
    }
    function lookupCachedFunEnterIID(f) {
        if (instFunction2EnterIID) {
            return instFunction2EnterIID.get(f);
        }
        else {
            return f[funEnterIIDHiddenProp];
        }
    }
    ___LoggingAnalysis___.lookupCachedFunEnterIID = lookupCachedFunEnterIID;
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
///<reference path='../ts-declarations/node.d.ts' />
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
///<reference path='./InstUtils.ts' />
/**
 * Created by m.sridharan on 11/6/14.
 */
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    /**
     * useful for profiling other aspects of the system
     */
    var NullLogger = (function () {
        function NullLogger() {
        }
        NullLogger.prototype.logDeclare = function (iid, name, objId) {
        };
        NullLogger.prototype.logCreateObj = function (iid, objId) {
        };
        NullLogger.prototype.logCreateFun = function (iid, funEnterIID, objId) {
        };
        NullLogger.prototype.logPutfield = function (iid, baseObjId, propName, valObjId) {
        };
        NullLogger.prototype.logWrite = function (iid, name, objId) {
        };
        NullLogger.prototype.logLastUse = function (objId, timestamp, iid) {
        };
        NullLogger.prototype.logFunctionEnter = function (iid, funObjId) {
        };
        NullLogger.prototype.logFunctionExit = function (iid) {
        };
        NullLogger.prototype.logUpdateIID = function (objId, newIID) {
        };
        NullLogger.prototype.logDebug = function (callIID, objId) {
        };
        NullLogger.prototype.logReturn = function (objId) {
        };
        NullLogger.prototype.logCreateDOMNode = function (iid, objId) {
        };
        NullLogger.prototype.logAddDOMChild = function (parentObjId, childObjId) {
        };
        NullLogger.prototype.logRemoveDOMChild = function (parentObjId, childObjId) {
        };
        NullLogger.prototype.logAddToChildSet = function (iid, parentObjId, name, childObjId) {
        };
        NullLogger.prototype.logRemoveFromChildSet = function (iid, parentObjId, name, childObjId) {
        };
        NullLogger.prototype.logDOMRoot = function (objId) {
        };
        NullLogger.prototype.logCall = function (iid, funObjId, funEnterIID) {
        };
        NullLogger.prototype.logScriptEnter = function (iid, filename) {
        };
        NullLogger.prototype.logScriptExit = function (iid) {
        };
        NullLogger.prototype.logFreeVars = function (iid, names) {
        };
        NullLogger.prototype.logSourceMapping = function (iid, filename, startLine, startColumn) {
        };
        NullLogger.prototype.getTime = function () {
            return undefined;
        };
        NullLogger.prototype.getFlushIID = function () {
            return undefined;
        };
        NullLogger.prototype.setFlushIID = function (iid) {
        };
        NullLogger.prototype.stopTracing = function () {
        };
        NullLogger.prototype.end = function (cb) {
        };
        return NullLogger;
    })();
    /**
     * these are some handy utilities for any implementation of Logger to have.
     * this class doesn't implement the Logger interface since we can't actually
     * make it an abstract class.
     */
    var AbstractLogger = (function () {
        function AbstractLogger() {
            /**
             * time stamp of *previous* log entry
             */
            this.time = -1;
            /**
             * either the IID of the most recent top-level expression, or -1 if
             * we've already emitted a TOP_LEVEL_FLUSH for that most-recent expression
             * @type {number}
             */
            this.flushIID = -2 /* ALREADY_FLUSHED */;
            this.tracingStopped = false;
        }
        AbstractLogger.prototype.getTime = function () {
            return this.time;
        };
        AbstractLogger.prototype.setFlushIID = function (iid) {
            if (this.flushIID !== -2 /* ALREADY_FLUSHED */) {
                throw new Error("invalid flush IID value " + this.flushIID);
            }
            this.flushIID = iid;
        };
        AbstractLogger.prototype.getFlushIID = function () {
            return this.flushIID;
        };
        AbstractLogger.prototype.stopTracing = function () {
            this.tracingStopped = true;
        };
        /**
         * actions before logging an entry
         * @return true if logging should continue, false otherwise
         */
        AbstractLogger.prototype.beforeLog = function () {
            if (this.tracingStopped) {
                return false;
            }
            var time = this.time;
            // check if we should flush
            if (this.flushIID !== -2 /* ALREADY_FLUSHED */) {
                this.logTopLevelFlush(this.flushIID);
                time += 2;
                this.flushIID = -2 /* ALREADY_FLUSHED */;
            }
            else {
                time += 1;
            }
            this.time = time;
            return true;
        };
        AbstractLogger.prototype.logTopLevelFlush = function (iid) {
            throw new Error("should be overridden by subclass!");
        };
        return AbstractLogger;
    })();
    /**
     * logger that writes data using a fluent interface.
     * the fluent interface is implemented in subclasses,
     * defining the data format
     */
    var AbstractFluentLogger = (function (_super) {
        __extends(AbstractFluentLogger, _super);
        function AbstractFluentLogger() {
            _super.apply(this, arguments);
        }
        ///////////////
        // fluent interface for writing out data
        ///////////////
        AbstractFluentLogger.prototype.flushIfNeeded = function (nextRecordLength) {
            throw new Error("override in subclass!");
        };
        AbstractFluentLogger.prototype.writeByte = function (val) {
            throw new Error("override in subclass!");
        };
        AbstractFluentLogger.prototype.writeInt = function (val) {
            throw new Error("override in subclass!");
        };
        AbstractFluentLogger.prototype.strLength = function (val) {
            throw new Error("override in subclass!");
        };
        AbstractFluentLogger.prototype.writeString = function (val) {
            throw new Error("override in subclass!");
        };
        AbstractFluentLogger.prototype.writeTypeAndIID = function (type, iid) {
            return this.writeByte(type).writeInt(iid);
        };
        AbstractFluentLogger.prototype.logDeclare = function (iid, name, objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 3 * 4 + this.strLength(name)).writeTypeAndIID(0 /* DECLARE */, iid).writeString(name).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logCreateObj = function (iid, objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 2 * 4).writeTypeAndIID(1 /* CREATE_OBJ */, iid).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logCreateFun = function (iid, funEnterIID, objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 3 * 4).writeTypeAndIID(2 /* CREATE_FUN */, iid).writeInt(funEnterIID).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logPutfield = function (iid, baseObjId, propName, valObjId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 4 * 4 + this.strLength(propName)).writeTypeAndIID(3 /* PUTFIELD */, iid).writeInt(baseObjId).writeString(propName).writeInt(valObjId);
        };
        AbstractFluentLogger.prototype.logWrite = function (iid, name, objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 3 * 4 + this.strLength(name)).writeTypeAndIID(4 /* WRITE */, iid).writeString(name).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logLastUse = function (objId, timestamp, iid) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 3 * 4).writeByte(5 /* LAST_USE */).writeInt(objId).writeInt(timestamp).writeInt(iid);
        };
        AbstractFluentLogger.prototype.logFunctionEnter = function (iid, funObjId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 2 * 4).writeTypeAndIID(6 /* FUNCTION_ENTER */, iid).writeInt(funObjId);
        };
        AbstractFluentLogger.prototype.logFunctionExit = function (iid) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(5).writeTypeAndIID(7 /* FUNCTION_EXIT */, iid);
        };
        AbstractFluentLogger.prototype.logUpdateIID = function (objId, newIID) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 2 * 4).writeByte(9 /* UPDATE_IID */).writeInt(objId).writeInt(newIID);
        };
        AbstractFluentLogger.prototype.logDebug = function (callIID, objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 2 * 4).writeTypeAndIID(10 /* DEBUG */, callIID).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logReturn = function (objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(5).writeByte(11 /* RETURN */).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logCreateDOMNode = function (iid, objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(9).writeTypeAndIID(12 /* CREATE_DOM_NODE */, iid).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logAddDOMChild = function (parentObjId, childObjId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(9).writeByte(13 /* ADD_DOM_CHILD */).writeInt(parentObjId).writeInt(childObjId);
        };
        AbstractFluentLogger.prototype.logRemoveDOMChild = function (parentObjId, childObjId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(9).writeByte(14 /* REMOVE_DOM_CHILD */).writeInt(parentObjId).writeInt(childObjId);
        };
        AbstractFluentLogger.prototype.logAddToChildSet = function (iid, parentObjId, name, childObjId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 4 * 4 + this.strLength(name)).writeTypeAndIID(15 /* ADD_TO_CHILD_SET */, iid).writeInt(parentObjId).writeString(name).writeInt(childObjId);
        };
        AbstractFluentLogger.prototype.logRemoveFromChildSet = function (iid, parentObjId, name, childObjId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 4 * 4 + this.strLength(name)).writeTypeAndIID(16 /* REMOVE_FROM_CHILD_SET */, iid).writeInt(parentObjId).writeString(name).writeInt(childObjId);
        };
        AbstractFluentLogger.prototype.logDOMRoot = function (objId) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(5).writeByte(17 /* DOM_ROOT */).writeInt(objId);
        };
        AbstractFluentLogger.prototype.logCall = function (iid, funObjId, funEnterIID) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(13).writeTypeAndIID(18 /* CALL */, iid).writeInt(funObjId).writeInt(funEnterIID);
        };
        AbstractFluentLogger.prototype.logScriptEnter = function (iid, filename) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 2 * 4 + this.strLength(filename)).writeTypeAndIID(19 /* SCRIPT_ENTER */, iid).writeString(filename);
        };
        AbstractFluentLogger.prototype.logScriptExit = function (iid) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(5).writeTypeAndIID(20 /* SCRIPT_EXIT */, iid);
        };
        AbstractFluentLogger.prototype.logFreeVars = function (iid, names) {
            if (!this.beforeLog())
                return;
            if (typeof names === 'string') {
                // we write -1 before the names to distinguish the case of the string ANY
                // from the array case
                this.flushIfNeeded(1 + 2 * 4 + this.strLength(names)).writeTypeAndIID(21 /* FREE_VARS */, iid).writeInt(-1).writeString(names);
            }
            else {
                var arrayByteLength = 4; // for writing array length
                for (var i = 0; i < names.length; i++) {
                    arrayByteLength += 4 + this.strLength(names[i]);
                }
                this.flushIfNeeded(1 + 4 + arrayByteLength).writeTypeAndIID(21 /* FREE_VARS */, iid).writeInt(names.length);
                for (var i = 0; i < names.length; i++) {
                    this.writeString(names[i]);
                }
            }
            // this shouldn't have incremented the time since it is metadata
            // so, subtract 1
            this.time--;
        };
        AbstractFluentLogger.prototype.logSourceMapping = function (iid, filename, startLine, startColumn) {
            if (!this.beforeLog())
                return;
            this.flushIfNeeded(1 + 4 * 4 + this.strLength(filename)).writeTypeAndIID(22 /* SOURCE_MAPPING */, iid).writeString(filename).writeInt(startLine).writeInt(startColumn);
            // this shouldn't have incremented the time since it is metadata
            // so, subtract 1
            this.time--;
        };
        AbstractFluentLogger.prototype.logTopLevelFlush = function (iid) {
            this.flushIfNeeded(5).writeTypeAndIID(8 /* TOP_LEVEL_FLUSH */, iid);
        };
        AbstractFluentLogger.prototype.end = function (cb) {
            throw new Error("should be overridden by subclass!");
        };
        return AbstractFluentLogger;
    })(AbstractLogger);
    var NODE_BUF_LENGTH = 65536;
    var AbstractNodeBufferLogger = (function (_super) {
        __extends(AbstractNodeBufferLogger, _super);
        function AbstractNodeBufferLogger() {
            _super.call(this);
            this.bufMod = require('../lib/analysis/bufferUtil.js');
            this.bufManager = new this.bufMod.BufferManager(NODE_BUF_LENGTH);
        }
        AbstractNodeBufferLogger.prototype.flushIfNeeded = function (nextRecordLength) {
            if (this.bufManager.offset + nextRecordLength > NODE_BUF_LENGTH) {
                this.flush();
            }
            return this;
        };
        AbstractNodeBufferLogger.prototype.writeByte = function (val) {
            this.bufManager.writeByte(val);
            return this;
        };
        AbstractNodeBufferLogger.prototype.writeInt = function (val) {
            this.bufManager.writeInt(val);
            return this;
        };
        AbstractNodeBufferLogger.prototype.strLength = function (val) {
            return this.bufManager.strLength(val);
        };
        AbstractNodeBufferLogger.prototype.writeString = function (val) {
            this.bufManager.writeString(val);
            return this;
        };
        AbstractNodeBufferLogger.prototype.flush = function () {
            throw new Error("override in subclass!");
        };
        return AbstractNodeBufferLogger;
    })(AbstractFluentLogger);
    var BinaryFSLogger = (function (_super) {
        __extends(BinaryFSLogger, _super);
        function BinaryFSLogger(traceLoc) {
            _super.call(this);
            this.fs = require('fs');
            this.traceFh = this.fs.openSync(traceLoc ? traceLoc : "mem-trace", 'w');
        }
        BinaryFSLogger.prototype.flush = function () {
            //            var time = process.hrtime();
            var bufMan = this.bufManager;
            this.fs.writeSync(this.traceFh, bufMan.buffer, 0, bufMan.offset);
            //            var diff = process.hrtime(time);
            //            totalFSTime += (diff[0] * 1e9 + diff[1]) / 1000000.0;
            bufMan.offset = 0;
        };
        BinaryFSLogger.prototype.end = function (cb) {
            if (this.bufManager.offset > 0) {
                this.flush();
            }
            this.fs.closeSync(this.traceFh);
            console.log("done writing log");
            cb();
        };
        return BinaryFSLogger;
    })(AbstractNodeBufferLogger);
    ___LoggingAnalysis___.BinaryFSLogger = BinaryFSLogger;
    var AsciiFSLogger = (function (_super) {
        __extends(AsciiFSLogger, _super);
        function AsciiFSLogger(traceLoc) {
            _super.call(this);
            this.fs = require('fs');
            this.traceFh = this.fs.openSync(traceLoc ? traceLoc : "ascii-mem-trace", 'w');
            this.buffer = "";
        }
        AsciiFSLogger.prototype.flushIfNeeded = function (nextRecordLength) {
            if (this.buffer.length > NODE_BUF_LENGTH) {
                this.flush();
            }
            return this;
        };
        AsciiFSLogger.prototype.writeByte = function (val) {
            this.buffer += val + ',';
            return this;
        };
        AsciiFSLogger.prototype.writeInt = function (val) {
            this.buffer += val + ',';
            return this;
        };
        AsciiFSLogger.prototype.strLength = function (val) {
            return val.length;
        };
        AsciiFSLogger.prototype.writeString = function (val) {
            this.buffer += val + ',';
            return this;
        };
        AsciiFSLogger.prototype.flush = function () {
            //            var time = process.hrtime();
            this.fs.writeSync(this.traceFh, this.buffer);
            //            var diff = process.hrtime(time);
            //            totalFSTime += (diff[0] * 1e9 + diff[1]) / 1000000.0;
            this.buffer = "";
        };
        AsciiFSLogger.prototype.end = function (cb) {
            if (this.buffer !== "") {
                this.flush();
            }
            this.fs.closeSync(this.traceFh);
            console.log("done writing log");
            cb();
        };
        return AsciiFSLogger;
    })(AbstractFluentLogger);
    ___LoggingAnalysis___.AsciiFSLogger = AsciiFSLogger;
    // to deal with monkey-patching; see NodeWebsocketLogger
    var ORIG_MATH_RANDOM = Math.random;
    var NodeWebSocketLogger = (function (_super) {
        __extends(NodeWebSocketLogger, _super);
        function NodeWebSocketLogger(appDir, serverIP, serverPort) {
            var _this = this;
            _super.call(this);
            if (!appDir) {
                throw new Error("appDir is undefined");
            }
            var WebSocketClient = require('websocket').client;
            var client = new WebSocketClient({
                fragmentOutgoingMessages: false
            });
            client.on('connect', function (connection) {
                _this.connection = connection;
                _this.connection.sendUTF('startup');
                if (_this.connectCB) {
                    _this.connectCB();
                }
            });
            var cp = require('child_process');
            var args = [
                require('path').resolve(__dirname, '../lib/server/server.js'),
                '--noHTTPServer',
                appDir
            ];
            var res = cp.spawn("node", args);
            res.on('error', function (err) {
                console.log("ERROR");
                console.log(err);
            });
            res.stdout.on('data', function (chunk) {
                // TODO fix this hack
                if (chunk.toString().indexOf(serverPort) !== -1) {
                    var url = 'ws://' + serverIP + ':' + serverPort;
                    client.connect(url, 'mem-trace-protocol');
                }
            });
            this.serverProc = res;
        }
        NodeWebSocketLogger.prototype.setConnectCB = function (connectCB) {
            if (this.connection) {
                connectCB();
            }
            else {
                this.connectCB = connectCB;
            }
        };
        NodeWebSocketLogger.prototype.flush = function () {
            var bufMan = this.bufManager;
            // YUCK: some benchmarks monkey-patch Math.random, which is used
            // by our WebSocket library.  So, temporarily un-monkey-patch it
            // here
            var backup_Math_random = Math.random;
            Math.random = ORIG_MATH_RANDOM;
            this.connection.sendBytes(bufMan.buffer.slice(0, bufMan.offset));
            Math.random = backup_Math_random;
            bufMan.offset = 0;
        };
        NodeWebSocketLogger.prototype.end = function (cb) {
            if (this.bufManager.offset > 0) {
                this.flush();
            }
            this.connection.close();
            this.serverProc.on('exit', cb);
        };
        return NodeWebSocketLogger;
    })(AbstractNodeBufferLogger);
    ___LoggingAnalysis___.NodeWebSocketLogger = NodeWebSocketLogger;
    var BinaryWebSocketLogger = (function (_super) {
        __extends(BinaryWebSocketLogger, _super);
        /**
         *
         * @param serverIP IP address of websocket server
         * @param serverPort port of websocket server
         * @param endTracingCB callback to invoke when a message to end tracing
         * is received from the websocket server
         */
        function BinaryWebSocketLogger(serverIP, serverPort, endTracingCB) {
            var _this = this;
            _super.call(this);
            this.buffer = new ArrayBuffer(64000 /* MAX_BUF_SIZE */);
            this.byteView = new Uint8Array(this.buffer);
            this.offset = 0;
            /**
             * is the socket open yet?
             * @type {boolean}
             */
            this.isOpen = false;
            /**
             * buffer of messages that remain to be flushed over socket
             * @type {Array}
             */
            this.remoteBuffer = [];
            /**
             * if true, sent a message and waiting for an ack
             * @type {boolean}
             */
            this.trying = false;
            var url = 'ws://' + serverIP + ':' + serverPort;
			
			
            this.socket = new WebSocket(url, 'mem-trace-protocol');
/**
			var agentStr = navigator.userAgent.toLowerCase() ;
			
			if(agentStr.indexOf("tizen")<0){				
				this.socket = new WebSocket('ws://127.0.0.1:8080', 'mem-trace-protocol');
			}else{				
				this.socket = new WebSocket('ws://127.0.0.2:8082', 'mem-trace-protocol');
			}
		**/	
            this.socket.onopen = function () {
                _this.isOpen = true;
                _this.socket.send('startup');
                _this.flushRemoteBuffer();
            };
            this.socket.onmessage = function (evt) {
                var data = evt.data;
                if (data === "endTracing") {
                    endTracingCB();
                }
                else {
                    _this.handleAck();
                }
            };
        }
        /**
         * flushes one message from the remote buffer
         */
        BinaryWebSocketLogger.prototype.flushRemoteBuffer = function () {
            if (this.isOpen && !this.trying) {
                var remoteBuf = this.remoteBuffer;
                if (remoteBuf.length > 0) {
                    var contents = remoteBuf.shift();
                    this.socket.send(contents);
                    this.trying = true;
                }
            }
        };
        /**
         * handle ack message from server for previously-flushed message
         */
        BinaryWebSocketLogger.prototype.handleAck = function () {
            this.trying = false;
            if (this.remoteBuffer.length === 0) {
                if (this.cb) {
                    // first, close the socket since we are done
                    this.socket.close();
                    this.cb();
                }
            }
            else {
                this.flushRemoteBuffer();
            }
        };
        /**
         * flush current buffer to the socket via remoteBuffer
         */
        BinaryWebSocketLogger.prototype.flush = function () {
            // need the <any> cast since lib.d.ts doesn't have
            // ArrayBuffer.prototype.slice
            this.remoteBuffer.push(this.buffer.slice(0, this.offset));
            this.flushRemoteBuffer();
            // the above call to slice() creates a copy, so we
            // don't need to allocate a fresh ArrayBuffer here
            this.offset = 0;
        };
        BinaryWebSocketLogger.prototype.flushIfNeeded = function (nextRecordLength) {
            if (this.offset + nextRecordLength > 64000 /* MAX_BUF_SIZE */) {
                this.flush();
            }
            return this;
        };
        BinaryWebSocketLogger.prototype.writeByte = function (val) {
            var offset = this.offset;
            this.byteView[offset] = val;
            this.offset = offset + 1;
            return this;
        };
        BinaryWebSocketLogger.prototype.writeInt = function (val) {
            var offset = this.offset;
            var bv = this.byteView;
            bv[offset] = (val >>> 24);
            bv[offset + 1] = (val >>> 16);
            bv[offset + 2] = (val >>> 8);
            bv[offset + 3] = val;
            this.offset = offset + 4;
            return this;
        };
        BinaryWebSocketLogger.prototype.strLength = function (val) {
            return val.length * 2;
        };
        BinaryWebSocketLogger.prototype.writeString = function (val) {
            var offset = this.offset;
            var bv = this.byteView;
            var strLen = this.strLength(val);
            bv[offset] = (strLen >>> 24);
            bv[offset + 1] = (strLen >>> 16);
            bv[offset + 2] = (strLen >>> 8);
            bv[offset + 3] = strLen;
            offset += 4;
            for (var i = 0; i < val.length; i++) {
                // NOTE: this doesn't handle crazy 4-byte characters
                var charCode = val.charCodeAt(i);
                bv[offset++] = charCode;
                bv[offset++] = charCode >>> 8;
            }
            this.offset = offset;
            return this;
        };
        BinaryWebSocketLogger.prototype.end = function (cb) {
            if (this.offset > 0) {
                this.flush();
            }
            if (!this.trying) {
                cb();
            }
            else {
                this.cb = cb;
            }
        };
        return BinaryWebSocketLogger;
    })(AbstractFluentLogger);
    ___LoggingAnalysis___.BinaryWebSocketLogger = BinaryWebSocketLogger;
    /**
     * useful for benchmarking other parts of system
     */
    var NoFlushWebSocketLogger = (function (_super) {
        __extends(NoFlushWebSocketLogger, _super);
        function NoFlushWebSocketLogger() {
            _super.apply(this, arguments);
        }
        NoFlushWebSocketLogger.prototype.flush = function () {
            // don't really flush; just reset the offset
            this.offset = 0;
        };
        return NoFlushWebSocketLogger;
    })(BinaryWebSocketLogger);
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
///<reference path='./Loggers.ts' />
/**
 * Created by m.sridharan on 11/7/14.
 */
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    var LastUseManager = (function () {
        function LastUseManager(logger, eagerFlush) {
            if (eagerFlush === void 0) { eagerFlush = false; }
            /**
             * maps objects to their last use time.  if we have lastUseTime[i] == t, then
             * the last use of object i logically occurred immediately *after* the event at
             * time t, where time starts at 0 and is incremented by 1 for each line in the trace
             * @type {Array}
             */
            this.lastUseTime = [];
            /**
             * maps objects to their last use iid
             * @type {Array}
             */
            this.lastUseIID = [];
            this.logger = logger;
            this.eagerFlush = eagerFlush;
        }
        LastUseManager.prototype.flushLastUse = function (cb) {
            var _this = this;
            var logger = this.logger;
            this.lastUseTime.forEach(function (val, idx) {
                if (val !== -1) {
                    logger.logLastUse(idx, val, _this.lastUseIID[idx]);
                }
            });
            logger.end(cb);
        };
        LastUseManager.prototype.updateLastUse = function (objId, iid, time) {
            if (this.eagerFlush) {
                this.logger.logLastUse(objId, time, iid);
            }
            else {
                this.lastUseTime[objId] = time;
                this.lastUseIID[objId] = iid;
            }
        };
        return LastUseManager;
    })();
    ___LoggingAnalysis___.LastUseManager = LastUseManager;
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
///<reference path='./InstUtils.ts' />
///<reference path='./Loggers.ts' />
///<reference path='./LastUseManager.ts' />
/**
 * Created by m.sridharan on 11/6/14.
 */
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    var WeakMapMetadataManager = (function () {
        function WeakMapMetadataManager() {
            /**
             * WeakMap to hold the IID at which we first encountered a native object.
             *
             * This is used to associate a correct IID with the native object if we
             * decide to create metadata for it.
             * @type {WeakMap<K, V>}
             */
            this.nativeObj2IID = new WeakMap();
            /**
             * WeakMap to hold object metadata
             * metadata is 32 bits.  31 bits for object id, highest-order bit to mark unannotated 'this'
             * objects from constructors
             * @type {WeakMap<K, V>}
             */
            this.obj2Metadata = new WeakMap();
        }
        WeakMapMetadataManager.prototype.hasMetadata = function (obj) {
            return this.obj2Metadata.has(obj);
        };
        WeakMapMetadataManager.prototype.getMetadata = function (obj) {
            return this.obj2Metadata.get(obj);
        };
        WeakMapMetadataManager.prototype.setMetadata = function (obj, metadata) {
            this.obj2Metadata.set(obj, metadata);
        };
        WeakMapMetadataManager.prototype.setIIDForNativeObj = function (obj, iid) {
            this.nativeObj2IID.set(obj, iid);
        };
        WeakMapMetadataManager.prototype.hasIIDForNativeObj = function (obj) {
            return this.nativeObj2IID.has(obj);
        };
        WeakMapMetadataManager.prototype.getIIDForNativeObj = function (obj) {
            return this.nativeObj2IID.get(obj);
        };
        WeakMapMetadataManager.prototype.flushNativeObj2IIDInfo = function () {
            this.nativeObj2IID = new WeakMap();
        };
        return WeakMapMetadataManager;
    })();
    var HiddenPropMetadataManager = (function () {
        function HiddenPropMetadataManager() {
            if (typeof Object.defineProperty !== 'function') {
                throw new Error("we need Object.defineProperty");
            }
        }
        HiddenPropMetadataManager.prototype.hasMetadata = function (obj) {
            return ___LoggingAnalysis___.HOP(obj, HiddenPropMetadataManager.METADATA_PROP);
        };
        HiddenPropMetadataManager.prototype.getMetadata = function (obj) {
            return obj[HiddenPropMetadataManager.METADATA_PROP];
        };
        HiddenPropMetadataManager.prototype.setMetadata = function (obj, metadata) {
            if (!this.hasMetadata(obj)) {
                try {
                    ___LoggingAnalysis___.objDefineProperty(obj, HiddenPropMetadataManager.METADATA_PROP, {
                        enumerable: false,
                        writable: true
                    });
                }
                catch (e) {
                }
            }
            obj[HiddenPropMetadataManager.METADATA_PROP] = metadata;
        };
        HiddenPropMetadataManager.prototype.setIIDForNativeObj = function (obj, iid) {
            ___LoggingAnalysis___.objDefineProperty(obj, HiddenPropMetadataManager.NATIVE_IID_PROP, {
                enumerable: false,
                writable: true
            });
            obj[HiddenPropMetadataManager.NATIVE_IID_PROP] = iid;
        };
        HiddenPropMetadataManager.prototype.hasIIDForNativeObj = function (obj) {
            return ___LoggingAnalysis___.HOP(obj, HiddenPropMetadataManager.NATIVE_IID_PROP);
        };
        HiddenPropMetadataManager.prototype.getIIDForNativeObj = function (obj) {
            return obj[HiddenPropMetadataManager.NATIVE_IID_PROP];
        };
        HiddenPropMetadataManager.prototype.flushNativeObj2IIDInfo = function () {
            // do nothing
        };
        HiddenPropMetadataManager.METADATA_PROP = "*M$*";
        HiddenPropMetadataManager.NATIVE_IID_PROP = "*NI$*";
        return HiddenPropMetadataManager;
    })();
    var ObjIdManagerImpl = (function () {
        function ObjIdManagerImpl(logger, lastUse, metaManager) {
            /**
             * counter for object ids
             * @type {number}
             */
            this.idCounter = 0;
            this.logger = logger;
            this.lastUse = lastUse;
            this.metaManager = metaManager;
        }
        /**
         * get a unique id for the object, creating it if necessary.
         * If created, log a CREATE event
         * @param obj the object, precondition isObject(obj) === true
         * @returns {*}
         */
        ObjIdManagerImpl.prototype.findOrCreateUniqueId = function (obj, iid, isLiteral) {
            var meta = this.metaManager;
            if (meta.hasMetadata(obj)) {
                return this.extractObjId(meta.getMetadata(obj));
            }
            else {
                return this.createObjId(obj, iid, isLiteral);
            }
        };
        ObjIdManagerImpl.prototype.extractObjId = function (metadata) {
            return metadata & 0x7FFFFFFF;
        };
        ObjIdManagerImpl.prototype.isUnannotatedThis = function (metadata) {
            return metadata < 0;
        };
        ObjIdManagerImpl.prototype.setUnannotatedThis = function (metadata) {
            // set sign bit
            return metadata | 0x80000000;
        };
        /**
         * gets unique id for object.  assumes that an ID has already been created.
         * @param obj
         * @returns {number}
         */
        ObjIdManagerImpl.prototype.findExtantObjId = function (obj) {
            return this.extractObjId(this.metaManager.getMetadata(obj));
        };
        ObjIdManagerImpl.prototype.findObjId = function (obj) {
            if (___LoggingAnalysis___.isObject(obj)) {
                var val = this.metaManager.getMetadata(obj);
                if (val !== undefined) {
                    return this.extractObjId(val);
                }
            }
            return -1;
        };
        /**
         * is obj unannotated and in need of an id?  This is to handle
         * cases where we discover objects from native / uninstrumented code
         * @param obj
         */
        ObjIdManagerImpl.prototype.needsAnId = function (obj) {
            return ___LoggingAnalysis___.isObject(obj) && !this.metaManager.hasMetadata(obj);
        };
        ObjIdManagerImpl.prototype.createObjId = function (obj, iid, isLiteral) {
            var _this = this;
            var meta = this.metaManager;
            if (meta.hasIIDForNativeObj(obj)) {
                // use the better IID that we stashed away
                iid = meta.getIIDForNativeObj(obj);
            }
            var helper = function (o) {
                var objId = _this.idCounter + 1;
                meta.setMetadata(o, objId);
                _this.lastUse.updateLastUse(objId, iid, -1);
                _this.idCounter = objId;
                return objId;
            };
            var objId = helper(obj);
            // only emit the CREATE_FUN entry for function literals
            if (isLiteral && typeof obj === 'function') {
                // create ID for prototype as well
                var proto = obj.prototype;
                var protoId = helper(proto);
                var funEnterIID = ___LoggingAnalysis___.getFunEnterIID(obj);
                this.logger.logCreateFun(iid, funEnterIID, objId);
            }
            else if (___LoggingAnalysis___.isBrowser && obj instanceof Node) {
                this.logger.logCreateDOMNode(iid, objId);
            }
            else {
                this.logger.logCreateObj(iid, objId);
            }
            return objId;
        };
        /**
         * do we have metadata for the object already?
         * @param obj
         * @returns {boolean}
         */
        ObjIdManagerImpl.prototype.hasMetadata = function (obj) {
            return this.metaManager.hasMetadata(obj);
        };
        ObjIdManagerImpl.prototype.getMetadata = function (obj) {
            return this.metaManager.getMetadata(obj);
        };
        ObjIdManagerImpl.prototype.setMetadata = function (obj, id) {
            return this.metaManager.setMetadata(obj, id);
        };
        ObjIdManagerImpl.prototype.flushNativeObj2IIDInfo = function () {
            this.metaManager.flushNativeObj2IIDInfo();
        };
        ObjIdManagerImpl.prototype.setIIDForNativeObj = function (obj, iid) {
            this.metaManager.setIIDForNativeObj(obj, iid);
        };
        return ObjIdManagerImpl;
    })();
    function createObjIdManager(logger, lastUse, useHiddenProp) {
        if (useHiddenProp === void 0) { useHiddenProp = false; }
        var result = typeof WeakMap === 'undefined' || useHiddenProp ? new ObjIdManagerImpl(logger, lastUse, new HiddenPropMetadataManager()) : new ObjIdManagerImpl(logger, lastUse, new WeakMapMetadataManager());
        // reserve object ID 1 for the global object
        result.createObjId(___LoggingAnalysis___.GLOBAL_OBJ, -1, false);
        return result;
    }
    ___LoggingAnalysis___.createObjIdManager = createObjIdManager;
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
/*!
 * https://github.com/paulmillr/es6-shim
 * @license es6-shim Copyright 2013-2014 by Paul Miller (http://paulmillr.com)
 * and contributors, MIT License
 * es6-shim: v0.20.2
 * see https://github.com/paulmillr/es6-shim/blob/master/LICENSE
 * Details and documentation:
 * https://github.com/paulmillr/es6-shim/
 */
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    var _toString = Object.prototype.toString;
    var _defineProperty = Object.defineProperty;
    var TypeIsObject = function (x) {
        /* jshint eqnull:true */
        // this is expensive when it returns false; use this function
        // when you expect it to return true in the common case.
        return x != null && Object(x) === x;
    };
    var IsCallable = function (x) {
        return typeof x === 'function' && _toString.call(x) === '[object Function]';
    };
    var isNaN = function (value) {
        // NaN !== NaN, but they are identical.
        // NaNs are the only non-reflexive value, i.e., if x !== x,
        // then x is NaN.
        // isNaN is broken: it converts its argument to number, so
        // isNaN('foo') => true
        return value !== value;
    };
    var SameValueZero = function (a, b) {
        // same as SameValue except for SameValueZero(+0, -0) == true
        return (a === b) || (isNaN(a) && isNaN(b));
    };
    var SameValue = function (a, b) {
        if (a === b) {
            // 0 === -0, but they are not identical.
            if (a === 0) {
                return 1 / a === 1 / b;
            }
            return true;
        }
        return isNaN(a) && isNaN(b);
    };
    var defineProperty = function (object, name, value, force) {
        if (!force && name in object) {
            return;
        }
        _defineProperty(object, name, {
            configurable: true,
            enumerable: false,
            writable: true,
            value: value
        });
    };
    // Define configurable, writable and non-enumerable props
    // if they don’t exist.
    var defineProperties = function (object, map) {
        Object.keys(map).forEach(function (name) {
            var method = map[name];
            defineProperty(object, name, method, false);
        });
    };
    var emulateES6construct = function (o) {
        if (!TypeIsObject(o)) {
            throw new TypeError('bad object');
        }
        // es5 approximation to es6 subclass semantics: in es6, 'new Foo'
        // would invoke Foo.@@create to allocation/initialize the new object.
        // In es5 we just get the plain object.  So if we detect an
        // uninitialized object, invoke o.constructor.@@create
        if (!o._es6construct) {
            if (o.constructor && IsCallable(o.constructor['@@create'])) {
                o = o.constructor['@@create'](o);
            }
            defineProperties(o, { _es6construct: true });
        }
        return o;
    };
    var emptyObject = function emptyObject() {
        // accomodate some older not-quite-ES5 browsers
        return Object.create ? Object.create(null) : {};
    };
    // Simple shim for Object.create on ES3 browsers
    // (unlike real shim, no attempt to support `prototype === null`)
    var create = Object.create || function (prototype, properties) {
        function Type() {
        }
        Type.prototype = prototype;
        var object = new Type();
        if (typeof properties !== 'undefined') {
            defineProperties(object, properties);
        }
        return object;
    };
    // Map and Set require a true ES5 environment
    // Their fast path also requires that the environment preserve
    // property insertion order, which is not guaranteed by the spec.
    var testOrder = function (a) {
        var b = Object.keys(a.reduce(function (o, k) {
            o[k] = true;
            return o;
        }, {}));
        return a.join(':') === b.join(':');
    };
    var preservesInsertionOrder = testOrder(['z', 'a', 'bb']);
    // some engines (eg, Chrome) only preserve insertion order for string keys
    var preservesNumericInsertionOrder = testOrder(['z', 1, 'a', '3', 2]);
    var fastkey = function fastkey(key) {
        if (!preservesInsertionOrder) {
            return null;
        }
        var type = typeof key;
        if (type === 'string') {
            return '$' + key;
        }
        else if (type === 'number') {
            // note that -0 will get coerced to "0" when used as a property key
            if (!preservesNumericInsertionOrder) {
                return 'n' + key;
            }
            return key;
        }
        return null;
    };
    var emptyObject = function emptyObject() {
        // accomodate some older not-quite-ES5 browsers
        return Object.create ? Object.create(null) : {};
    };
    var MyMap = (function () {
        var empty = {};
        function MapEntry(key, value) {
            this.key = key;
            this.value = value;
            this.next = null;
            this.prev = null;
        }
        MapEntry.prototype.isRemoved = function () {
            return this.key === empty;
        };
        function MapIterator(map, kind) {
            this.head = map._head;
            this.i = this.head;
            this.kind = kind;
        }
        MapIterator.prototype = {
            next: function () {
                var i = this.i, kind = this.kind, head = this.head, result;
                if (typeof this.i === 'undefined') {
                    return { value: void 0, done: true };
                }
                while (i.isRemoved() && i !== head) {
                    // back up off of removed entries
                    i = i.prev;
                }
                while (i.next !== head) {
                    i = i.next;
                    if (!i.isRemoved()) {
                        if (kind === 'key') {
                            result = i.key;
                        }
                        else if (kind === 'value') {
                            result = i.value;
                        }
                        else {
                            result = [i.key, i.value];
                        }
                        this.i = i;
                        return { value: result, done: false };
                    }
                }
                // once the iterator is done, it is done forever.
                this.i = void 0;
                return { value: void 0, done: true };
            }
        };
        //        addIterator(MapIterator.prototype);
        function Map() {
            var map = this;
            map = emulateES6construct(map);
            if (!map._es6map) {
                throw new TypeError('bad map');
            }
            var head = new MapEntry(null, null);
            // circular doubly-linked list.
            head.next = head.prev = head;
            defineProperties(map, {
                _head: head,
                _storage: emptyObject(),
                _size: 0
            });
            return map;
        }
        var Map$prototype = Map.prototype;
        defineProperties(Map, {
            '@@create': function (obj) {
                var constructor = this;
                var prototype = constructor.prototype || Map$prototype;
                obj = obj || create(prototype);
                defineProperties(obj, { _es6map: true });
                return obj;
            }
        });
        _defineProperty(Map.prototype, 'size', {
            configurable: true,
            enumerable: false,
            get: function () {
                if (typeof this._size === 'undefined') {
                    throw new TypeError('size method called on incompatible Map');
                }
                return this._size;
            }
        });
        defineProperties(Map.prototype, {
            get: function (key) {
                var fkey = fastkey(key);
                if (fkey !== null) {
                    // fast O(1) path
                    var entry = this._storage[fkey];
                    if (entry) {
                        return entry.value;
                    }
                    else {
                        return;
                    }
                }
                var head = this._head, i = head;
                while ((i = i.next) !== head) {
                    if (SameValueZero(i.key, key)) {
                        return i.value;
                    }
                }
                return;
            },
            has: function (key) {
                var fkey = fastkey(key);
                if (fkey !== null) {
                    // fast O(1) path
                    return typeof this._storage[fkey] !== 'undefined';
                }
                var head = this._head, i = head;
                while ((i = i.next) !== head) {
                    if (SameValueZero(i.key, key)) {
                        return true;
                    }
                }
                return false;
            },
            set: function (key, value) {
                var head = this._head, i = head, entry;
                var fkey = fastkey(key);
                if (fkey !== null) {
                    // fast O(1) path
                    if (typeof this._storage[fkey] !== 'undefined') {
                        this._storage[fkey].value = value;
                        return this;
                    }
                    else {
                        entry = this._storage[fkey] = new MapEntry(key, value);
                        i = head.prev;
                    }
                }
                while ((i = i.next) !== head) {
                    if (SameValueZero(i.key, key)) {
                        i.value = value;
                        return this;
                    }
                }
                entry = entry || new MapEntry(key, value);
                if (SameValue(-0, key)) {
                    entry.key = +0; // coerce -0 to +0 in entry
                }
                entry.next = this._head;
                entry.prev = this._head.prev;
                entry.prev.next = entry;
                entry.next.prev = entry;
                this._size += 1;
                return this;
            },
            'delete': function (key) {
                var head = this._head, i = head;
                var fkey = fastkey(key);
                if (fkey !== null) {
                    // fast O(1) path
                    if (typeof this._storage[fkey] === 'undefined') {
                        return false;
                    }
                    i = this._storage[fkey].prev;
                    delete this._storage[fkey];
                }
                while ((i = i.next) !== head) {
                    if (SameValueZero(i.key, key)) {
                        i.key = i.value = empty;
                        i.prev.next = i.next;
                        i.next.prev = i.prev;
                        this._size -= 1;
                        return true;
                    }
                }
                return false;
            },
            clear: function () {
                this._size = 0;
                this._storage = emptyObject();
                var head = this._head, i = head, p = i.next;
                while ((i = p) !== head) {
                    i.key = i.value = empty;
                    p = i.next;
                    i.next = i.prev = head;
                }
                head.next = head.prev = head;
            },
            keys: function () {
                return new MapIterator(this, 'key');
            },
            values: function () {
                return new MapIterator(this, 'value');
            },
            entries: function () {
                return new MapIterator(this, 'key+value');
            },
            forEach: function (callback) {
                var context = arguments.length > 1 ? arguments[1] : null;
                var it = this.entries();
                for (var entry = it.next(); !entry.done; entry = it.next()) {
                    callback.call(context, entry.value[1], entry.value[0], this);
                }
            }
        });
        return Map;
    })();
    function allocMap() {
        if (typeof Map !== 'undefined') {
            return new Map();
        }
        else {
            return new MyMap();
        }
    }
    ___LoggingAnalysis___.allocMap = allocMap;
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
/*
 * Copyright 2012 The Polymer Authors. All rights reserved.
 * Use of this source code is goverened by a BSD-style
 * license that can be found in the LICENSE file.
 */
/**
 * modified by Manu Sridharan to include only necessary
 * functionality and fit with our TypeScript modules
 */
///<reference path='./MapShim.ts' />
///<reference path='./InstUtils.ts' />
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    var registrationsTable = ___LoggingAnalysis___.allocMap();
    var setImmediate;
    // As much as we would like to use the native implementation, IE
    // (all versions) suffers a rather annoying bug where it will drop or defer
    // callbacks when heavy DOM operations are being performed concurrently.
    //
    // For a thorough discussion on this, see:
    // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/
    if (___LoggingAnalysis___.isBrowser) {
        if (/Trident/.test(navigator.userAgent)) {
            // Sadly, this bug also affects postMessage and MessageQueues.
            //
            // We would like to use the onreadystatechange hack for IE <= 10, but it is
            // dangerous in the polyfilled environment due to requiring that the
            // observed script element be in the document.
            setImmediate = setTimeout;
        }
        else if (window.setImmediate) {
            setImmediate = window.setImmediate;
        }
        else {
            var setImmediateQueue = [];
            var sentinel = String(Math.random());
            window.addEventListener('message', function (e) {
                if (e.data === sentinel) {
                    var queue = setImmediateQueue;
                    setImmediateQueue = [];
                    queue.forEach(function (func) {
                        func();
                    });
                }
            });
            setImmediate = function (func) {
                setImmediateQueue.push(func);
                window.postMessage(sentinel, '*');
            };
        }
    }
    // This is used to ensure that we never schedule 2 callas to setImmediate
    var isScheduled = false;
    // Keep track of observers that needs to be notified next time.
    var scheduledObservers = [];
    /**
     * Schedules |dispatchCallback| to be called in the future.
     * @param {MutationObserver} observer
     */
    function scheduleCallback(observer) {
        scheduledObservers.push(observer);
        if (!isScheduled) {
            isScheduled = true;
            setImmediate(dispatchCallbacks);
        }
    }
    function wrapIfNeeded(node) {
        // TODO not sure if this change is correct... --MS
        //return window.ShadowDOMPolyfill &&
        //    window.ShadowDOMPolyfill.wrapIfNeeded(node) ||
        //    node;
        return node;
    }
    function dispatchCallbacks() {
        // http://dom.spec.whatwg.org/#mutation-observers
        isScheduled = false; // Used to allow a new setImmediate call above.
        var observers = scheduledObservers;
        scheduledObservers = [];
        // Sort observers based on their creation UID (incremental).
        observers.sort(function (o1, o2) {
            return o1.uid_ - o2.uid_;
        });
        var anyNonEmpty = false;
        observers.forEach(function (observer) {
            // 2.1, 2.2
            var queue = observer.takeRecords();
            // 2.3. Remove all transient registered observers whose observer is mo.
            removeTransientObserversFor(observer);
            // 2.4
            if (queue.length) {
                observer.callback_(queue, observer);
                anyNonEmpty = true;
            }
        });
        // 3.
        if (anyNonEmpty)
            dispatchCallbacks();
    }
    function removeTransientObserversFor(observer) {
        observer.nodes_.forEach(function (node) {
            var registrations = registrationsTable.get(node);
            if (!registrations)
                return;
            registrations.forEach(function (registration) {
                if (registration.observer === observer)
                    registration.removeTransientObservers();
            });
        });
    }
    /**
     * This function is used for the "For each registered observer observer (with
     * observer's options as options) in target's list of registered observers,
     * run these substeps:" and the "For each ancestor ancestor of target, and for
     * each registered observer observer (with options options) in ancestor's list
     * of registered observers, run these substeps:" part of the algorithms. The
     * |options.subtree| is checked to ensure that the callback is called
     * correctly.
     *
     * @param {Node} target
     * @param {function(MutationObserverInit):MutationRecord} callback
     */
    function forEachAncestorAndObserverEnqueueRecord(target, callback) {
        for (var node = target; node; node = node.parentNode) {
            var registrations = registrationsTable.get(node);
            if (registrations) {
                for (var j = 0; j < registrations.length; j++) {
                    var registration = registrations[j];
                    var options = registration.options;
                    // Only target ignores subtree.
                    if (node !== target && !options.subtree)
                        continue;
                    var record = callback(options);
                    if (record)
                        registration.enqueue(record);
                }
            }
        }
    }
    var uidCounter = 0;
    /**
     * The class that maps to the DOM MutationObserver interface.
     * @param {Function} callback.
     * @constructor
     */
    function JsMutationObserver(callback) {
        this.callback_ = callback;
        this.nodes_ = [];
        this.records_ = [];
        this.uid_ = ++uidCounter;
    }
    JsMutationObserver.prototype = {
        observe: function (target, options) {
            target = wrapIfNeeded(target);
            // 1.1
            if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
                throw new SyntaxError();
            }
            var registrations = registrationsTable.get(target);
            if (!registrations)
                registrationsTable.set(target, registrations = []);
            // 2
            // If target's list of registered observers already includes a registered
            // observer associated with the context object, replace that registered
            // observer's options with options.
            var registration;
            for (var i = 0; i < registrations.length; i++) {
                if (registrations[i].observer === this) {
                    registration = registrations[i];
                    registration.removeListeners();
                    registration.options = options;
                    break;
                }
            }
            // 3.
            // Otherwise, add a new registered observer to target's list of registered
            // observers with the context object as the observer and options as the
            // options, and add target to context object's list of nodes on which it
            // is registered.
            if (!registration) {
                registration = new Registration(this, target, options);
                registrations.push(registration);
                this.nodes_.push(target);
            }
            registration.addListeners();
        },
        disconnect: function () {
            this.nodes_.forEach(function (node) {
                var registrations = registrationsTable.get(node);
                for (var i = 0; i < registrations.length; i++) {
                    var registration = registrations[i];
                    if (registration.observer === this) {
                        registration.removeListeners();
                        registrations.splice(i, 1);
                        break;
                    }
                }
            }, this);
            this.records_ = [];
        },
        takeRecords: function () {
            var copyOfRecords = this.records_;
            this.records_ = [];
            return copyOfRecords;
        }
    };
    /**
     * @param {string} type
     * @param {Node} target
     * @constructor
     */
    function MutationRecord(type, target) {
        this.type = type;
        this.target = target;
        this.addedNodes = [];
        this.removedNodes = [];
        this.previousSibling = null;
        this.nextSibling = null;
        this.attributeName = null;
        this.attributeNamespace = null;
        this.oldValue = null;
    }
    function copyMutationRecord(original) {
        var record = new MutationRecord(original.type, original.target);
        record.addedNodes = original.addedNodes.slice();
        record.removedNodes = original.removedNodes.slice();
        record.previousSibling = original.previousSibling;
        record.nextSibling = original.nextSibling;
        record.attributeName = original.attributeName;
        record.attributeNamespace = original.attributeNamespace;
        record.oldValue = original.oldValue;
        return record;
    }
    ;
    // We keep track of the two (possibly one) records used in a single mutation.
    var currentRecord, recordWithOldValue;
    /**
     * Creates a record without |oldValue| and caches it as |currentRecord| for
     * later use.
     * @param {string} oldValue
     * @return {MutationRecord}
     */
    function getRecord(type, target) {
        return currentRecord = new MutationRecord(type, target);
    }
    /**
     * Gets or creates a record with |oldValue| based in the |currentRecord|
     * @param {string} oldValue
     * @return {MutationRecord}
     */
    function getRecordWithOldValue(oldValue) {
        if (recordWithOldValue)
            return recordWithOldValue;
        recordWithOldValue = copyMutationRecord(currentRecord);
        recordWithOldValue.oldValue = oldValue;
        return recordWithOldValue;
    }
    function clearRecords() {
        currentRecord = recordWithOldValue = undefined;
    }
    /**
     * @param {MutationRecord} record
     * @return {boolean} Whether the record represents a record from the current
     * mutation event.
     */
    function recordRepresentsCurrentMutation(record) {
        return record === recordWithOldValue || record === currentRecord;
    }
    /**
     * Selects which record, if any, to replace the last record in the queue.
     * This returns |null| if no record should be replaced.
     *
     * @param {MutationRecord} lastRecord
     * @param {MutationRecord} newRecord
     * @param {MutationRecord}
     */
    function selectRecord(lastRecord, newRecord) {
        if (lastRecord === newRecord)
            return lastRecord;
        // Check if the the record we are adding represents the same record. If
        // so, we keep the one with the oldValue in it.
        if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord))
            return recordWithOldValue;
        return null;
    }
    /**
     * Class used to represent a registered observer.
     * @param {MutationObserver} observer
     * @param {Node} target
     * @param {MutationObserverInit} options
     * @constructor
     */
    function Registration(observer, target, options) {
        this.observer = observer;
        this.target = target;
        this.options = options;
        this.transientObservedNodes = [];
    }
    Registration.prototype = {
        enqueue: function (record) {
            var records = this.observer.records_;
            var length = records.length;
            // There are cases where we replace the last record with the new record.
            // For example if the record represents the same mutation we need to use
            // the one with the oldValue. If we get same record (this can happen as we
            // walk up the tree) we ignore the new record.
            if (records.length > 0) {
                var lastRecord = records[length - 1];
                var recordToReplaceLast = selectRecord(lastRecord, record);
                if (recordToReplaceLast) {
                    records[length - 1] = recordToReplaceLast;
                    return;
                }
            }
            else {
                scheduleCallback(this.observer);
            }
            records[length] = record;
        },
        addListeners: function () {
            this.addListeners_(this.target);
        },
        addListeners_: function (node) {
            var options = this.options;
            if (options.attributes)
                node.addEventListener('DOMAttrModified', this, true);
            if (options.characterData)
                node.addEventListener('DOMCharacterDataModified', this, true);
            if (options.childList)
                node.addEventListener('DOMNodeInserted', this, true);
            if (options.childList || options.subtree)
                node.addEventListener('DOMNodeRemoved', this, true);
        },
        removeListeners: function () {
            this.removeListeners_(this.target);
        },
        removeListeners_: function (node) {
            var options = this.options;
            if (options.attributes)
                node.removeEventListener('DOMAttrModified', this, true);
            if (options.characterData)
                node.removeEventListener('DOMCharacterDataModified', this, true);
            if (options.childList)
                node.removeEventListener('DOMNodeInserted', this, true);
            if (options.childList || options.subtree)
                node.removeEventListener('DOMNodeRemoved', this, true);
        },
        /**
         * Adds a transient observer on node. The transient observer gets removed
         * next time we deliver the change records.
         * @param {Node} node
         */
        addTransientObserver: function (node) {
            // Don't add transient observers on the target itself. We already have all
            // the required listeners set up on the target.
            if (node === this.target)
                return;
            this.addListeners_(node);
            this.transientObservedNodes.push(node);
            var registrations = registrationsTable.get(node);
            if (!registrations)
                registrationsTable.set(node, registrations = []);
            // We know that registrations does not contain this because we already
            // checked if node === this.target.
            registrations.push(this);
        },
        removeTransientObservers: function () {
            var transientObservedNodes = this.transientObservedNodes;
            this.transientObservedNodes = [];
            transientObservedNodes.forEach(function (node) {
                // Transient observers are never added to the target.
                this.removeListeners_(node);
                var registrations = registrationsTable.get(node);
                for (var i = 0; i < registrations.length; i++) {
                    if (registrations[i] === this) {
                        registrations.splice(i, 1);
                        break;
                    }
                }
            }, this);
        },
        handleEvent: function (e) {
            // Stop propagation since we are managing the propagation manually.
            // This means that other mutation events on the page will not work
            // correctly but that is by design.
            e.stopImmediatePropagation();
            switch (e.type) {
                case 'DOMAttrModified':
                    // http://dom.spec.whatwg.org/#concept-mo-queue-attributes
                    var name = e.attrName;
                    var namespace = e.relatedNode.namespaceURI;
                    var target = e.target;
                    // 1.
                    var record = new getRecord('attributes', target);
                    record.attributeName = name;
                    record.attributeNamespace = namespace;
                    // 2.
                    var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
                    forEachAncestorAndObserverEnqueueRecord(target, function (options) {
                        // 3.1, 4.2
                        if (!options.attributes)
                            return;
                        // 3.2, 4.3
                        if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
                            return;
                        }
                        // 3.3, 4.4
                        if (options.attributeOldValue)
                            return getRecordWithOldValue(oldValue);
                        // 3.4, 4.5
                        return record;
                    });
                    break;
                case 'DOMCharacterDataModified':
                    // http://dom.spec.whatwg.org/#concept-mo-queue-characterdata
                    var target = e.target;
                    // 1.
                    var record = getRecord('characterData', target);
                    // 2.
                    var oldValue = e.prevValue;
                    forEachAncestorAndObserverEnqueueRecord(target, function (options) {
                        // 3.1, 4.2
                        if (!options.characterData)
                            return;
                        // 3.2, 4.3
                        if (options.characterDataOldValue)
                            return getRecordWithOldValue(oldValue);
                        // 3.3, 4.4
                        return record;
                    });
                    break;
                case 'DOMNodeRemoved':
                    this.addTransientObserver(e.target);
                case 'DOMNodeInserted':
                    // http://dom.spec.whatwg.org/#concept-mo-queue-childlist
                    var target = e.relatedNode;
                    var changedNode = e.target;
                    var addedNodes, removedNodes;
                    if (e.type === 'DOMNodeInserted') {
                        addedNodes = [changedNode];
                        removedNodes = [];
                    }
                    else {
                        addedNodes = [];
                        removedNodes = [changedNode];
                    }
                    var previousSibling = changedNode.previousSibling;
                    var nextSibling = changedNode.nextSibling;
                    // 1.
                    var record = getRecord('childList', target);
                    record.addedNodes = addedNodes;
                    record.removedNodes = removedNodes;
                    record.previousSibling = previousSibling;
                    record.nextSibling = nextSibling;
                    forEachAncestorAndObserverEnqueueRecord(target, function (options) {
                        // 2.1, 3.2
                        if (!options.childList)
                            return;
                        // 2.2, 3.3
                        return record;
                    });
            }
            clearRecords();
        }
    };
    function allocMutationObserver(callback) {
        if (typeof MutationObserver !== 'undefined') {
            return new MutationObserver(callback);
        }
        else {
            return new JsMutationObserver(callback);
        }
    }
    ___LoggingAnalysis___.allocMutationObserver = allocMutationObserver;
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
///<reference path='./ObjIdManager.ts' />
///<reference path='./MapShim.ts' />
///<reference path='./MutationObserverShim.ts' />
/**
 * Created by m.sridharan on 11/6/14.
 */
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    var LoggingMutationObserver = (function () {
        function LoggingMutationObserver(idManager, logger) {
            var _this = this;
            this.idManager = idManager;
            this.logger = logger;
            this.mutObserverConfig = { attributes: true, childList: true, characterData: false, subtree: false };
            // for counting how many times we used modeled object ids
            this.numDOMCreationIIDUsed = 0;
            this.observer = ___LoggingAnalysis___.allocMutationObserver(function (mutations) {
                var didSomething = false;
                mutations.forEach(function (mut) {
                    var target = mut.target;
                    var targetId = idManager.findExtantObjId(target);
                    var added = mut.addedNodes;
                    for (var i = 0; i < added.length; i++) {
                        var child = added[i];
                        var childId = _this.findOrCreateIdForDOMNode(child, false);
                        _this.logger.logAddDOMChild(targetId, childId);
                        if (!_this.isObserved(child)) {
                            _this.domWalk(child);
                        }
                    }
                    var removed = mut.removedNodes;
                    for (i = 0; i < removed.length; i++) {
                        var removedNode = removed[i];
                        var removedId = _this.findOrCreateIdForDOMNode(removedNode, false);
                        _this.logger.logRemoveDOMChild(targetId, removedId);
                    }
                    didSomething = didSomething || added.length > 0 || removed.length > 0;
                });
                if (didSomething) {
                    _this.logger.setFlushIID(-1 /* UNKNOWN */);
                }
            });
            document.addEventListener('DOMContentLoaded', function () {
                // create object id for document and document.documentElement
                var documentObjId = idManager.findOrCreateUniqueId(document, -2 /* INIT_DOM_TRAVERSAL_IID */, false);
                _this.logger.logWrite(-2 /* INIT_DOM_TRAVERSAL_IID */, 'document', documentObjId);
                var docElementObjId = idManager.findOrCreateUniqueId(document.documentElement, -2 /* INIT_DOM_TRAVERSAL_IID */, false);
                _this.logger.logPutfield(-2 /* INIT_DOM_TRAVERSAL_IID */, documentObjId, "documentElement", docElementObjId);
                // mark document.documentElement as the DOM root
                _this.logger.logDOMRoot(docElementObjId);
                _this.domWalk(document.documentElement, true);
                _this.logger.setFlushIID(-1 /* UNKNOWN */);
            });
        }
        /**
         * log a creation record for all descendants of node
         * @param node
         * @param iid
         */
        LoggingMutationObserver.prototype.createDOMNodeDescendants = function (node, iid) {
            var children = node.childNodes;
            var idManager = this.idManager;
            var logger = this.logger;
            for (var i = 0; i < children.length; i++) {
                var child = children[i];
                idManager.findOrCreateUniqueId(child, iid, false);
                this.numDOMCreationIIDUsed++;
                // NOTE: by not logging an appropriate addDOMChild record here,
                // we will get bogus unreachability records for this node.  But,
                // weird interactions with the mutation observer, detached DOMs,
                // etc. make logging the add child here worse I think --MS
                this.createDOMNodeDescendants(child, iid);
            }
        };
        LoggingMutationObserver.prototype.getDOMCreationIID = function (node, initial) {
            return (initial ? -2 /* INIT_DOM_TRAVERSAL_IID */ : -1 /* MUT_OBSERVER_IID */);
        };
        LoggingMutationObserver.prototype.observe = function (node) {
            this.observer.observe(node, this.mutObserverConfig);
            node.isObserved = true;
        };
        LoggingMutationObserver.prototype.isObserved = function (node) {
            return node.isObserved === true;
        };
        LoggingMutationObserver.prototype.findOrCreateIdForDOMNode = function (obj, initial) {
            var fallbackIID = this.getDOMCreationIID(obj, initial);
            var result = this.idManager.findOrCreateUniqueId(obj, fallbackIID, false);
            return result;
        };
        LoggingMutationObserver.prototype.domWalk = function (node, initial) {
            if (this.isObserved(node)) {
                throw new Error("shouldn't happen");
            }
            else {
                this.observe(node);
            }
            var nodeId = this.findOrCreateIdForDOMNode(node, initial);
            var children = node.childNodes;
            for (var i = 0; i < children.length; i++) {
                var child = children[i];
                var childId = this.findOrCreateIdForDOMNode(child, initial);
                this.logger.logAddDOMChild(nodeId, childId);
                if (!this.isObserved(child)) {
                    this.domWalk(child, initial);
                }
            }
        };
        LoggingMutationObserver.prototype.getNumNodesModeled = function () {
            return this.numDOMCreationIIDUsed;
        };
        return LoggingMutationObserver;
    })();
    /**
     * includes our models of native functions and our mutation observer
     * code for handling the DOM
     */
    var NativeModels = (function () {
        function NativeModels(idManager, logger) {
            var _this = this;
            this.idManager = idManager;
            this.logger = logger;
            /**
             * native functions that we model before they have been called, i.e., in invokeFunPre()
             */
            this.invokeFunPreNatives = ___LoggingAnalysis___.allocMap();
            /**
             * native functions that we model after they have been called, i.e., in invokeFun()
             */
            this.invokeFunPostNatives = ___LoggingAnalysis___.allocMap();
            this.callbackIdToGlobal = ___LoggingAnalysis___.allocMap();
            this.callbackCounter = 0;
            this.nativeFunctionModels = {
                'Array.prototype.push': function (iid, f, base, args, val, isConstructor, name) {
                    var len = base.length;
                    var baseId = _this.idManager.findObjId(base);
                    if (typeof len === 'number' && baseId !== -1) {
                        var ind = len - 1;
                        for (var argInd = args.length - 1; argInd >= 0; argInd--) {
                            var argId = _this.idManager.findObjId(args[argInd]);
                            if (argId !== -1) {
                                _this.logger.logPutfield(iid, baseId, String(ind), argId);
                            }
                            ind--;
                        }
                    }
                },
                'Array.prototype.pop': function (iid, f, base, args, val, isConstructor, name) {
                    var len = base.length;
                    var baseId = _this.idManager.findObjId(base);
                    if (typeof len === 'number' && baseId !== -1) {
                        // NOTE this will emit a putfield at '0' even if the
                        // array was empty before; shouldn't be a big deal
                        _this.logger.logPutfield(iid, baseId, String(len), 0);
                    }
                },
                'Array.prototype.unshift': function (iid, f, base, args, val, isConstructor, name) {
                    // we need to do a full pass to update all indices
                    var len = base.length;
                    var baseId = _this.idManager.findObjId(base);
                    if (typeof len === 'number' && baseId !== -1) {
                        for (var i = 0; i < len; i++) {
                            // TODO base[i] could be a getter...sigh.  ignore for now
                            var elemId = _this.idManager.findObjId(base[i]);
                            if (elemId === -1) {
                                // to be safe, still log a putfield with null id
                                elemId = 0;
                            }
                            _this.logger.logPutfield(iid, baseId, String(i), elemId);
                        }
                    }
                },
                'Array.prototype.shift': function (iid, f, base, args, val, isConstructor, name) {
                    // we need to do a full pass to update all indices
                    var len = base.length;
                    var baseId = _this.idManager.findObjId(base);
                    if (typeof len === 'number' && baseId !== -1) {
                        for (var i = 0; i < len; i++) {
                            // TODO base[i] could be a getter...sigh.  ignore for now
                            var elemId = _this.idManager.findObjId(base[i]);
                            if (elemId === -1) {
                                // to be safe, still log a putfield with null id
                                elemId = 0;
                            }
                            _this.logger.logPutfield(iid, baseId, String(i), elemId);
                        }
                        // also, putfield of null at length to reflect shifted value
                        // NOTE this will emit a putfield at '0' even if the
                        // array was empty before; shouldn't be a big deal
                        _this.logger.logPutfield(iid, baseId, String(len), 0);
                    }
                },
                'Array.prototype.concat': function (iid, f, base, args, val, isConstructor, name) {
                    // full pass on result
                    if (val) {
                        // need to wrap eagerly
                        var valId = _this.idManager.findOrCreateUniqueId(val, iid, false);
                        var len = val.length;
                        if (typeof len === 'number') {
                            for (var i = 0; i < len; i++) {
                                var elemId = _this.idManager.findObjId(val[i]);
                                if (elemId !== -1) {
                                    _this.logger.logPutfield(iid, valId, String(i), elemId);
                                }
                            }
                        }
                    }
                },
                'Array.prototype.splice': function (iid, f, base, args, val, isConstructor, name) {
                    // full pass on array
                    var len = base.length;
                    var baseId = _this.idManager.findObjId(base);
                    if (typeof len === 'number' && baseId !== -1) {
                        for (var i = 0; i < len; i++) {
                            // TODO base[i] could be a getter...sigh.  ignore for now
                            var elemId = _this.idManager.findObjId(base[i]);
                            if (elemId === -1) {
                                // to be safe, still log a putfield with null id
                                elemId = 0;
                            }
                            _this.logger.logPutfield(iid, baseId, String(i), elemId);
                        }
                        // if old length was bigger than current length, need to emit putfields
                        // of null to extra elements
                        var oldLen = _this.spliceOldLen;
                        if (typeof oldLen === 'number' && oldLen > len) {
                            for (i = len; i < oldLen; i++) {
                                _this.logger.logPutfield(iid, baseId, String(i), 0);
                            }
                        }
                        _this.spliceOldLen = undefined;
                    }
                    // full pass on result
                    if (val) {
                        var len = val.length;
                        if (len > 0) {
                            // need to wrap eagerly
                            var valId = _this.idManager.findOrCreateUniqueId(val, iid, false);
                            for (var i = 0; i < len; i++) {
                                var elemId = _this.idManager.findObjId(val[i]);
                                if (elemId !== -1) {
                                    _this.logger.logPutfield(iid, valId, String(i), elemId);
                                }
                            }
                        }
                    }
                },
                'setTimeout': function (iid, f, base, args, val, isConstructor, name) {
                    var wrapperFun = args[0];
                    var globalName = wrapperFun.globalName;
                    var timeoutId = val;
                    _this.callbackIdToGlobal.set(timeoutId, globalName);
                    wrapperFun.timeoutId = timeoutId;
                },
                'clearTimeout': function (iid, f, base, args, val, isConstructor, name) {
                    var timeoutId = args[0];
                    var global = _this.callbackIdToGlobal.get(timeoutId);
                    if (global) {
                        _this.logger.logWrite(iid, global, 0);
                        _this.logger.setFlushIID(iid);
                        _this.callbackIdToGlobal.delete(timeoutId);
                    }
                },
                'setInterval': function (iid, f, base, args, val, isConstructor, name) {
                    var intervalFun = args[0];
                    var globalName = "~timer~global~" + (++_this.callbackCounter);
                    var timeoutId = val;
                    _this.callbackIdToGlobal.set(timeoutId, globalName);
                    _this.logger.logWrite(iid, globalName, _this.idManager.findOrCreateUniqueId(intervalFun, iid, false));
                },
                'clearInterval': function (iid, f, base, args, val, isConstructor, name) {
                    var timeoutId = args[0];
                    var global = _this.callbackIdToGlobal.get(timeoutId);
                    if (global) {
                        _this.logger.logWrite(iid, global, 0);
                        _this.logger.setFlushIID(iid);
                        _this.callbackIdToGlobal.delete(timeoutId);
                    }
                },
                'addEventListener': function (iid, f, base, args, val, isConstructor, name) {
                    // add to child set for event name
                    var eventType = args[0];
                    var listenerFun = args[1];
                    if (base && ___LoggingAnalysis___.isObject(base) && eventType && typeof eventType === 'string' && listenerFun) {
                        var parentId = _this.idManager.findOrCreateUniqueId(base, iid, false);
                        var childId = _this.idManager.findOrCreateUniqueId(listenerFun, iid, false);
                        var name = "~event~" + eventType;
                        _this.logger.logAddToChildSet(iid, parentId, name, childId);
                    }
                },
                'removeEventListener': function (iid, f, base, args, val, isConstructor, name) {
                    // add to child set for event name
                    var eventType = args[0];
                    var listenerFun = args[1];
                    if (base && ___LoggingAnalysis___.isObject(base) && eventType && typeof eventType === 'string' && listenerFun) {
                        var parentId = _this.idManager.findOrCreateUniqueId(base, iid, false);
                        var childId = _this.idManager.findOrCreateUniqueId(listenerFun, iid, false);
                        var name = "~event~" + eventType;
                        _this.logger.logRemoveFromChildSet(iid, parentId, name, childId);
                    }
                },
                'Object.defineProperty': function (iid, f, base, args, val, isConstructor, name) {
                    var targetObj = args[0];
                    var property = String(args[1]);
                    var descriptor = args[2];
                    if (targetObj && ___LoggingAnalysis___.isObject(targetObj) && property && descriptor) {
                        var targetId = _this.idManager.findOrCreateUniqueId(targetObj, iid, false);
                        if (___LoggingAnalysis___.HOP(descriptor, 'value')) {
                            var val = descriptor.value;
                            if (___LoggingAnalysis___.isObject(val)) {
                                var valId = _this.idManager.findOrCreateUniqueId(val, iid, false);
                                _this.logger.logPutfield(iid, targetId, property, valId);
                            }
                            else {
                                // still log a write in case it's an overwrite
                                _this.logger.logPutfield(iid, targetId, property, 0);
                            }
                        }
                        if (___LoggingAnalysis___.HOP(descriptor, 'get')) {
                            var getter = descriptor.get;
                            var getterId = _this.idManager.findOrCreateUniqueId(getter, iid, false);
                            _this.logger.logPutfield(iid, targetId, "~get~" + property, getterId);
                        }
                        if (___LoggingAnalysis___.HOP(descriptor, 'set')) {
                            var setter = descriptor.set;
                            var setterId = _this.idManager.findOrCreateUniqueId(setter, iid, false);
                            _this.logger.logPutfield(iid, targetId, "~set~" + property, setterId);
                        }
                    }
                },
                'HTMLElement.prototype.insertAdjacentHTML': function (iid, f, base, args, val, isConstructor, name) {
                    /**
                     *
                     If position is an ASCII case-insensitive match for the string "beforebegin"
    
                     Insert fragment into the context object's parent before the context object.
                     If position is an ASCII case-insensitive match for the string "afterbegin"
    
                     Insert fragment into the context object before its first child.
                     If position is an ASCII case-insensitive match for the string "beforeend"
    
                     Append fragment to the context object.
                     If position is an ASCII case-insensitive match for the string "afterend"
    
                     Insert fragment into the context object's parent before the context object's next sibling.
                     */
                    var position = args[0];
                    if (position && typeof position === 'string') {
                        var posLowerCase = position.toLowerCase();
                        if (posLowerCase === 'beforebegin' || posLowerCase === 'afterend') {
                            var parent = base.parentNode;
                            _this.mutObs.createDOMNodeDescendants(parent, iid);
                        }
                        else if (posLowerCase === 'afterbegin' || posLowerCase === 'beforeend') {
                            _this.mutObs.createDOMNodeDescendants(base, iid);
                        }
                    }
                }
            };
            if (___LoggingAnalysis___.isBrowser) {
                this.mutObs = new LoggingMutationObserver(idManager, logger);
            }
            this.initInterestingNatives();
        }
        NativeModels.prototype.getNumDOMNodesModeled = function () {
            return this.mutObs.getNumNodesModeled();
        };
        NativeModels.prototype.modelPutField = function (iid, base, offset, val) {
            if (___LoggingAnalysis___.isBrowser && base instanceof Element && offset === 'innerHTML') {
                this.mutObs.createDOMNodeDescendants(base, iid);
            }
        };
        NativeModels.prototype.modelPutFieldPre = function (iid, base, offset, val) {
            // native modeling: 'onreadystatechange' for XMLHttpRequest
            if (___LoggingAnalysis___.isBrowser && base instanceof XMLHttpRequest && offset === 'onreadystatechange') {
                // write a wrapped function
                var fun = val;
                if (typeof fun === 'function') {
                    var funIID = ___LoggingAnalysis___.getFunEnterIID(fun);
                    var self = this;
                    var freshGlobal = "~readystatechange~global~" + (++this.callbackCounter);
                    self.logger.logWrite(iid, freshGlobal, this.idManager.findOrCreateUniqueId(fun, iid, false));
                    var wrapper = function () {
                        try {
                            fun.apply(this, arguments);
                        }
                        finally {
                            if (this.readyState === this.DONE) {
                                // null out the global
                                self.logger.logWrite(funIID, freshGlobal, 0);
                                self.logger.setFlushIID(funIID);
                            }
                        }
                    };
                    return { base: base, offset: offset, val: wrapper, skip: false };
                }
            }
            return undefined;
        };
        NativeModels.prototype.initInterestingNatives = function () {
            var preMap = this.invokeFunPreNatives;
            preMap.set(Array.prototype.splice, "Array.prototype.splice");
            preMap.set(setTimeout, "setTimeout");
            var postMap = this.invokeFunPostNatives;
            postMap.set(Array.prototype.pop, "Array.prototype.pop");
            postMap.set(Array.prototype.push, "Array.prototype.push");
            postMap.set(Array.prototype.shift, "Array.prototype.shift");
            postMap.set(Array.prototype.unshift, "Array.prototype.unshift");
            postMap.set(Array.prototype.concat, "Array.prototype.concat");
            postMap.set(Array.prototype.splice, "Array.prototype.splice");
            postMap.set(setTimeout, "setTimeout");
            postMap.set(clearTimeout, "clearTimeout");
            postMap.set(setInterval, "setInterval");
            postMap.set(clearInterval, "clearInterval");
            postMap.set(Object.defineProperty, "Object.defineProperty");
            if (___LoggingAnalysis___.isBrowser) {
                postMap.set(addEventListener, "addEventListener");
                postMap.set(removeEventListener, "removeEventListener");
                // in browsers other than Chrome, window.add(remove)EventListener
                // is a different function than document.add(remove)EventListener,
                // so put both in map
                postMap.set(document.addEventListener, "addEventListener");
                postMap.set(document.removeEventListener, "removeEventListener");
                postMap.set(HTMLElement.prototype.insertAdjacentHTML, "HTMLElement.prototype.insertAdjacentHTML");
            }
        };
        NativeModels.prototype.modelInvokeFunPre = function (iid, f, base, args, isConstructor, isMethod) {
            if (this.invokeFunPreNatives.has(f)) {
                if (f === Array.prototype.splice) {
                    // to model splice, we need to stash away the old length of the array
                    this.spliceOldLen = base.length;
                }
                else if (f === setTimeout) {
                    // keep 'this' pointer ourselves since we need to refer to the real 'this'
                    // inside the function
                    var fun = args[0];
                    if (typeof fun === 'function') {
                        var funIID = ___LoggingAnalysis___.getFunEnterIID(fun);
                        var self = this;
                        var freshGlobal = "~timer~global~" + (++this.callbackCounter);
                        var wrapper = function () {
                            try {
                                fun.apply(this, arguments);
                            }
                            finally {
                                self.logger.logWrite(funIID, freshGlobal, 0);
                                self.logger.setFlushIID(funIID);
                                self.callbackIdToGlobal.delete(wrapper.timeoutId);
                            }
                        };
                        wrapper.globalName = freshGlobal;
                        this.logger.logWrite(iid, freshGlobal, this.idManager.findOrCreateUniqueId(fun, iid, false));
                        args[0] = wrapper;
                    }
                }
                return true;
            }
            return false;
        };
        NativeModels.prototype.modelInvokeFun = function (iid, f, base, args, val, isConstructor, isMethod) {
            if (this.invokeFunPostNatives.has(f)) {
                this.modelNativeFunction(iid, f, base, args, val, isConstructor, this.invokeFunPostNatives.get(f));
                return true;
            }
            return false;
        };
        NativeModels.prototype.modelNativeFunction = function (iid, f, base, args, val, isConstructor, name) {
            var fun = this.nativeFunctionModels[name];
            if (fun) {
                fun(iid, f, base, args, val, isConstructor, name);
            }
        };
        return NativeModels;
    })();
    ___LoggingAnalysis___.NativeModels = NativeModels;
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
///<reference path='../ts-declarations/node.d.ts' />
///<reference path='./Loggers.ts' />
///<reference path='./InstUtils.ts' />
///<reference path='./ObjIdManager.ts' />
///<reference path='./LastUseManager.ts' />
///<reference path='./NativeModels.ts' />
/**
 * Created by m.sridharan on 5/29/14.
 */
var ___LoggingAnalysis___;
(function (___LoggingAnalysis___) {
    var myAstUtil = ___LoggingAnalysis___.isBrowser ? astUtil : require('jalangi/src/js/utils/astUtil');
    if (!___LoggingAnalysis___.isBrowser) {
        require('../lib/analysis/memAnalysisUtils');
    }
    var LoggingAnalysis = (function () {
        /***********************************/
        /* CONSTRUCTOR AND JALANGI METHODS */
        /***********************************/
        function LoggingAnalysis() {
            /**
             * indicates top-level expressions, which necessitate a flush
             */
            this.topLevelExprs = {};
            this.emittedCall = false;
            /**
             * whether logging can be skipped for a putfield.
             * We need a stack to handle case where putfield
             * invokes a setter that itself contains a putfield
             * @type {Array}
             */
            this.skipLoggingStack = [];
            /**
             * if true, log all putfields, even if value before
             * and after is a primitive
             * @type {boolean}
             */
            this.logAllPutfields = false;
            /**
             * for each call frame, either the metadata for the unannotated this parameter,
             * or 0 if this was annotated
             * @type {Array}
             */
            this.unannotThisMetadata = [];
            /**
             * public flag indicating when logging is complete
             * @type {boolean}
             */
            this.doneLogging = false;
        }
        /***************************************/
        /* ANALYSIS STATE AND INTERNAL METHODS */
        /***************************************/
        LoggingAnalysis.prototype.updateLastUse = function (objId, iid) {
            this.lastUse.updateLastUse(objId, iid, this.logger.getTime());
        };
        LoggingAnalysis.prototype.handleTopLevel = function (iid) {
            if (this.logger.getFlushIID() === -2 /* ALREADY_FLUSHED */ && this.topLevelExprs[iid]) {
                this.logger.setFlushIID(iid);
                // at this point, we can empty the map from native objects to iids,
                // since after a flush we won't be storing them anywhere
                this.idManager.flushNativeObj2IIDInfo();
            }
        };
        LoggingAnalysis.prototype.initJalangiConfig = function () {
            var conf = J$.Config;
            var instHandler = J$.memAnalysisUtils.instHandler;
            conf.INSTR_READ = instHandler.instrRead;
            conf.INSTR_WRITE = instHandler.instrWrite;
            conf.INSTR_GETFIELD = instHandler.instrGetfield;
            conf.INSTR_PUTFIELD = instHandler.instrPutfield;
            conf.INSTR_BINARY = instHandler.instrBinary;
            conf.INSTR_PROPERTY_BINARY_ASSIGNMENT = instHandler.instrPropBinaryAssignment;
            conf.INSTR_UNARY = instHandler.instrUnary;
            conf.INSTR_LITERAL = instHandler.instrLiteral;
            conf.INSTR_CONDITIONAL = instHandler.instrConditional;
        };
        LoggingAnalysis.prototype.init = function (initParam) {
		    var websocketUrl = "ws://127.0.0.1:8085/";
			var webSocket = new WebSocket(websocketUrl);	
			webSocket.onopen = function(evt){
				console.log('websocket connection open.');
			}
			webSocket.onerror = function(evt){
				console.log('websocket error.readyState:'+evt.target.readyState);
			}
            var _this = this;
            var endTracingFun = function () {
                _this.lastUse.flushLastUse(function () {
                    alert("trace generation complete\n");
                });
                _this.logger.stopTracing();
            };
            this.initLogger(initParam, endTracingFun);
            this.lastUse = new ___LoggingAnalysis___.LastUseManager(this.logger, initParam["allUses"] !== undefined);
            var idManager = ___LoggingAnalysis___.createObjIdManager(this.logger, this.lastUse, initParam["useHiddenProp"] !== undefined);
            this.idManager = idManager;
            this.nativeModels = new ___LoggingAnalysis___.NativeModels(idManager, this.logger);
            this.logAllPutfields = initParam["allPutfields"] !== undefined;
            this.initJalangiConfig();
            var debugFun = initParam["debugFun"];
            if (debugFun) {
                // we monkey-patch here to avoid checking the debug flag on every invocation
                // of invokeFunPre
                var origInvokeFunPre = this.invokeFunPre;
                this.invokeFunPre = function (iid, f, base, args, isConstructor, isMethod) {
                    if (f && f.name === debugFun) {
                        var obj = args[0];
                        // we should already have metadata for the object
                        if (!idManager.hasMetadata(obj)) {
                            throw new Error("missing metadata for argument to debug function");
                        }
                        var objId = idManager.findExtantObjId(obj);
                        _this.logger.logDebug(iid, objId);
                    }
                    origInvokeFunPre.call(_this, iid, f, base, args, isConstructor, isMethod);
                };
            }
			if (___LoggingAnalysis___.isBrowser) {
				webSocket.onmessage = function(evt){
					console.log('server message:'+evt.data);
					if(evt.data === 'endTracing'){
						 _this.lastUse.flushLastUse(function () {
                            alert("all flushed\n" + _this.nativeModels.getNumDOMNodesModeled() + " DOM node locations from models");
                        });
                       endTracingFun();
					}
				}
                window.addEventListener('keydown', function (e) {
                    // keyboard shortcut is Alt-Shift-T for now
                    if (e.altKey && e.shiftKey && e.keyCode === 84) {
                        _this.lastUse.flushLastUse(function () {
                            alert("all flushed\n" + _this.nativeModels.getNumDOMNodesModeled() + " DOM node locations from models");
                        });
                       endTracingFun();
                    }
                });
            }
			webSocket.onclose = function(evt){
				console.log("websocket close.");
			}
            //if (isBrowser) {
            //    window.addEventListener('keydown', (e) => {
            //        // keyboard shortcut is Alt-Shift-T for now
            //        if (e.altKey && e.shiftKey && e.keyCode === 84) {
            //            endTracingFun();
            //        }
            //    });
            //    // for Tizen apps
            //    document.addEventListener('tizenhwkey', (e) => {
            //        if ((<any>e).keyName === 'menu') {
            //            endTracingFun();
            //        }
            //    });
            //}
        };
        LoggingAnalysis.prototype.initLogger = function (initParam, endTracingFun) {
            var logger;
            if (___LoggingAnalysis___.isBrowser) {
                if (initParam["syncAjax"]) {
                    throw new Error("TODO revive support for synchronous AJAX logging");
                }
                else {
                    var serverIP = initParam["serverIP"], serverPort = initParam["serverPort"];
                    if (!serverIP || !serverPort) {
                        throw new Error("server IP and/or port not specified!");
                    }
                    logger = new ___LoggingAnalysis___.BinaryWebSocketLogger(serverIP, serverPort, endTracingFun);
                }
            }
            else {
                if (initParam["syncFS"]) {
                    if (initParam["asciiFS"]) {
                        logger = new ___LoggingAnalysis___.AsciiFSLogger();
                    }
                    else {
                        logger = new ___LoggingAnalysis___.BinaryFSLogger();
                    }
                }
                else {
                    var serverIP = initParam["serverIP"], serverPort = initParam["serverPort"];
                    if (!serverIP || !serverPort) {
                        throw new Error("server IP and/or port not specified!");
                    }
                    logger = new ___LoggingAnalysis___.NodeWebSocketLogger(initParam["appDir"], serverIP, serverPort);
                }
            }
            this.logger = logger;
        };
        LoggingAnalysis.prototype.onReady = function (readyCB) {
            if (this.logger instanceof ___LoggingAnalysis___.NodeWebSocketLogger) {
                this.logger.setConnectCB(readyCB);
            }
            else {
                readyCB();
            }
        };
        LoggingAnalysis.prototype.declare = function (iid, name, val, isArgument) {
            // TODO handle case where code overwrites arguments?
            if (name !== 'arguments') {
                var id = 0;
                if (___LoggingAnalysis___.isObject(val)) {
                    id = this.idManager.findOrCreateUniqueId(val, iid, false);
                }
                this.logger.logDeclare(iid, name, id);
            }
        };
        LoggingAnalysis.prototype.literal = function (iid, val, hasGetterSetter) {
            if (___LoggingAnalysis___.isObject(val)) {
                var valId = this.idManager.findOrCreateUniqueId(val, iid, true);
                if (!(typeof val === 'function')) {
                    this.handleLiteralProperties(iid, val, valId, hasGetterSetter);
                }
            }
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.handleLiteralProperties = function (iid, lit, litId, hasGetterSetter) {
            var _this = this;
            var props = Object.keys(lit);
            var simple = function (offset) {
                var child = lit[offset];
                if (___LoggingAnalysis___.isObject(child)) {
                    var childId = _this.idManager.findOrCreateUniqueId(child, iid, false);
                    _this.logger.logPutfield(iid, litId, offset, childId);
                }
            };
            if (!hasGetterSetter) {
                props.forEach(simple);
            }
            else {
                props.forEach(function (offset) {
                    var descriptor = Object.getOwnPropertyDescriptor(lit, offset);
                    if (descriptor.get !== undefined || descriptor.set !== undefined) {
                        var annotateGetterSetter = function (fun, getter) {
                            if (fun) {
                                // fun may already be annotated in the case where we
                                // are annotating properties of an object returned from a constructor
                                // call. but, we can't detect this case.
                                var id = _this.idManager.findOrCreateUniqueId(fun, iid, true);
                                var synthProp = getter ? "~get~" + fun.name : "~set~" + fun.name;
                                _this.logger.logPutfield(iid, litId, synthProp, id);
                            }
                        };
                        annotateGetterSetter(descriptor.get, true);
                        annotateGetterSetter(descriptor.set, false);
                    }
                    else {
                        simple(offset);
                    }
                });
            }
        };
        LoggingAnalysis.prototype.invokeFunPre = function (iid, f, base, args, isConstructor, isMethod) {
            if (!this.nativeModels.modelInvokeFunPre(iid, f, base, args, isConstructor, isMethod)) {
                if (f) {
                    var funEnterIID = ___LoggingAnalysis___.lookupCachedFunEnterIID(f);
                    if (funEnterIID !== undefined) {
                        var funObjId = this.idManager.findObjId(f);
                        this.logger.logCall(iid, funObjId, funEnterIID);
                        this.emittedCall = true;
                    }
                }
            }
        };
        /**
         * if evalIID === -1, indirect eval
         * @param evalIID
         * @param iidMetadata
         */
        LoggingAnalysis.prototype.instrumentCode = function (evalIID, newAST) {
            var _this = this;
            var newTopLevel = myAstUtil.computeTopLevelExpressions(newAST);
            newTopLevel.forEach(function (iid) {
                _this.topLevelExprs[iid] = true;
            });
            var na = J$.memAnalysisUtils;
            var curVarNames = null;
            var freeVarsHandler = function (node, context) {
                var fv = na.freeVars(node);
                curVarNames = fv === na.ANY ? "ANY" : Object.keys(fv);
            };
            var visitorPost = {
                'CallExpression': function (node) {
                    if (node.callee.object && node.callee.object.name === 'J$' && (node.callee.property.name === 'Fe')) {
                        var iid = node.arguments[0].value;
                        _this.logger.logFreeVars(iid, curVarNames);
                    }
                    return node;
                }
            };
            var visitorPre = {
                'FunctionExpression': freeVarsHandler,
                'FunctionDeclaration': freeVarsHandler
            };
            myAstUtil.transformAst(newAST, visitorPost, visitorPre);
        };
        LoggingAnalysis.prototype.invokeFun = function (iid, f, base, args, val, isConstructor, isMethod) {
            var idManager = this.idManager;
            if (___LoggingAnalysis___.isObject(val)) {
                if (idManager.hasMetadata(val)) {
                    var metadata = idManager.getMetadata(val);
                    if (idManager.isUnannotatedThis(metadata)) {
                        var objId = idManager.extractObjId(metadata);
                        if (isConstructor) {
                            // update the IID
                            this.logger.logUpdateIID(objId, iid);
                            // log a putfield to expose pointer to the prototype object
                            var funProto = f.prototype;
                            if (___LoggingAnalysis___.isObject(funProto)) {
                                var funProtoId = idManager.findOrCreateUniqueId(funProto, iid, false);
                                this.logger.logPutfield(iid, objId, "__proto__", funProtoId);
                            }
                        }
                        // unset the bit
                        idManager.setMetadata(val, objId);
                    }
                }
                else {
                    // native object.  stash away the iid of the call
                    // in case we decide to create an id for the object later
                    idManager.setIIDForNativeObj(val, iid);
                }
            }
            this.nativeModels.modelInvokeFun(iid, f, base, args, val, isConstructor, isMethod);
            var funId = idManager.findObjId(f);
            if (funId !== -1) {
                this.updateLastUse(funId, iid);
            }
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.putFieldPre = function (iid, base, offset, val) {
            var skipLogging = false;
            if (___LoggingAnalysis___.isObject(base) && !this.logAllPutfields) {
                // can only skip if new value is a primitive
                if (!___LoggingAnalysis___.isObject(val)) {
                    // property must be a non-getter-setter defined on the object itself
                    var desc = Object.getOwnPropertyDescriptor(base, offset);
                    if (desc && !desc.set && !desc.get) {
                        // old value must be a primitive
                        var oldVal = base[offset];
                        if (!___LoggingAnalysis___.isObject(oldVal)) {
                            // we can skip logging!
                            skipLogging = true;
                        }
                    }
                }
                else {
                    var nativeResult = this.nativeModels.modelPutFieldPre(iid, base, offset, val);
                    if (nativeResult) {
                        return nativeResult;
                    }
                }
            }
            this.skipLoggingStack.push(skipLogging);
        };
        LoggingAnalysis.prototype.putField = function (iid, base, offset, val) {
            var skipLogging = this.skipLoggingStack.pop();
            if (___LoggingAnalysis___.isObject(base)) {
                var baseId = this.idManager.findObjId(base);
                if (baseId !== -1) {
                    if (!skipLogging) {
                        if (!___LoggingAnalysis___.isGetterSetter(base, offset)) {
                            var valId = ___LoggingAnalysis___.isObject(val) ? this.idManager.findOrCreateUniqueId(val, iid, false) : 0;
                            this.logger.logPutfield(iid, baseId, String(offset), valId);
                        }
                    }
                    this.updateLastUse(baseId, iid);
                }
                this.nativeModels.modelPutField(iid, base, offset, val);
            }
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.logWrite = function (iid, name, valId) {
            if (!name) {
                throw new Error("got an invalid name for iid " + iid);
            }
            this.logger.logWrite(iid, name, valId);
        };
        LoggingAnalysis.prototype.write = function (iid, name, val, oldValue) {
            if (___LoggingAnalysis___.isObject(val)) {
                var id = this.idManager.findOrCreateUniqueId(val, iid, false);
                this.logWrite(iid, name, id);
            }
            else if (___LoggingAnalysis___.isObject(oldValue)) {
                // need the write so oldValue's ref-count gets updated
                this.logWrite(iid, name, 0);
            }
            else {
            }
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.functionEnter = function (iid, fun, dis /* this */, args) {
            if (this.emittedCall) {
                // we emitted a call entry, so we don't need a functionEnter also
                this.emittedCall = false;
            }
            else {
                var funId = this.idManager.findOrCreateUniqueId(fun, iid, false);
                this.logger.logFunctionEnter(iid, funId);
                // in this case, we won't see the invokeFun callback at the
                // caller to update the last use of fun.  so, update it here
                this.updateLastUse(funId, iid);
            }
            var metadata = 0;
            // check for unannotated this and flag as such
            if (dis !== ___LoggingAnalysis___.GLOBAL_OBJ) {
                var idManager = this.idManager;
                if (!idManager.hasMetadata(dis)) {
                    // TODO could optimize to only add value to obj2Metadata once
                    var id = idManager.findOrCreateUniqueId(dis, iid, false);
                    metadata = idManager.setUnannotatedThis(id);
                    idManager.setMetadata(dis, metadata);
                    this.unannotThisMetadata.push(metadata);
                }
                else {
                    metadata = idManager.getMetadata(dis);
                    this.unannotThisMetadata.push(0);
                }
            }
            else {
                this.unannotThisMetadata.push(0);
            }
            if (metadata !== 0) {
                // declare the value of 'this' in the trace
                this.logger.logDeclare(iid, "this", this.idManager.extractObjId(metadata));
            }
            // functionEnter cannot be top-level
            //            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.getField = function (iid, base, offset, val) {
            // base may not be an object, e.g., if it's a string
            if (___LoggingAnalysis___.isObject(base)) {
                // TODO fix handling of prototype chain
                var id = this.idManager.findObjId(base);
                if (id !== -1) {
                    this.updateLastUse(id, iid);
                }
            }
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.functionExit = function (iid, returnVal, exceptionVal) {
            var loggedReturn = false;
            if (___LoggingAnalysis___.isObject(returnVal)) {
                var idManager = this.idManager;
                if (idManager.hasMetadata(returnVal)) {
                    this.logger.logReturn(idManager.findExtantObjId(returnVal));
                    loggedReturn = true;
                }
            }
            // NOTE: analysis should treat function exit as a top-level flush as well
            var unannotatedThis = this.unannotThisMetadata.pop();
            if (unannotatedThis !== 0 && !loggedReturn) {
                // we had an unannotated this and no explicit return.
                // we are very likely exiting from a constructor call.
                // so, add a RETURN log entry for this, so that it doesn't
                // become unreachable.
                // this could be the wrong thing to do, e.g., if this function
                // is actually being invoked from uninstrumented code.
                // don't worry about that corner case for now.
                this.logger.logReturn(this.idManager.extractObjId(unannotatedThis));
            }
            this.logger.logFunctionExit(iid);
        };
        LoggingAnalysis.prototype.read = function (iid, name, val, isGlobal) {
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.binary = function (iid, op, left, right, result_c) {
            if (op === 'delete') {
                // left is object, right is property
                var base = left;
                var offset = right;
                if (___LoggingAnalysis___.isObject(base)) {
                    var baseId = this.idManager.findObjId(base);
                    if (baseId !== -1 && offset !== null && offset !== undefined) {
                        this.logger.logPutfield(iid, baseId, String(offset), 0);
                        this.updateLastUse(baseId, iid);
                    }
                }
            }
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.unary = function (iid, op, left, result_c) {
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.conditional = function (iid, left, result_c) {
            this.handleTopLevel(iid);
        };
        LoggingAnalysis.prototype.scriptEnter = function (iid, fileName) {
            this.logger.logScriptEnter(iid, fileName);
            // get the latest top level expressions
            var topLevel = this.topLevelExprs;
            J$.topLevelExprs.forEach(function (iid) {
                topLevel[iid] = true;
            });
        };
        LoggingAnalysis.prototype.scriptExit = function (iid) {
            this.logger.logScriptExit(iid);
        };
        LoggingAnalysis.prototype.endExecution = function () {
            var _this = this;
            this.lastUse.flushLastUse(function () {
                _this.doneLogging = true;
            });
            return {};
        };
        return LoggingAnalysis;
    })();
    var loggingAnalysis = new LoggingAnalysis();
    loggingAnalysis.init(J$.initParams);
    J$.analysis = loggingAnalysis;
})(___LoggingAnalysis___ || (___LoggingAnalysis___ = {}));
function flush(){
    var evtObj = document.createEvent("Event");
    evtObj.initEvent("keydown", true, false);
    evtObj.altKey = true;
    evtObj.shiftKey = true;
    evtObj.keyCode = 84; 
    document.dispatchEvent(evtObj);
}

document.addEventListener('tizenhwkey', function(e){

	if(e.keyName === 'menu'){
		console.log("[SJ] flush");
		flush();
	}	
});
//# sourceMappingURL=LoggingAnalysis.js.map
