"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Serializer = undefined;

var _environment = require("../environment.js");

var _realm = require("../realm.js");

var _errors = require("../errors.js");

var _completions = require("../completions.js");

var _generator = require("../utils/generator.js");

var _babelGenerator = require("babel-generator");

var _babelGenerator2 = _interopRequireDefault(_babelGenerator);

var _traverseFast = require("../utils/traverse-fast.js");

var _traverseFast2 = _interopRequireDefault(_traverseFast);

var _invariant = require("../invariant.js");

var _invariant2 = _interopRequireDefault(_invariant);

var _types = require("./types.js");

var _functions = require("./functions.js");

var _logger = require("../utils/logger.js");

var _modules = require("../utils/modules.js");

var _flow = require("../utils/flow.js");

var _LoggingTracer = require("./LoggingTracer.js");

var _ResidualHeapVisitor = require("./ResidualHeapVisitor.js");

var _ResidualHeapSerializer = require("./ResidualHeapSerializer.js");

var _ResidualHeapValueIdentifiers = require("./ResidualHeapValueIdentifiers.js");

var _LazyObjectsSerializer = require("./LazyObjectsSerializer.js");

var _babelTypes = require("babel-types");

var t = _interopRequireWildcard(_babelTypes);

var _ResidualHeapRefCounter = require("./ResidualHeapRefCounter");

var _ResidualHeapGraphGenerator = require("./ResidualHeapGraphGenerator");

var _Referentializer = require("./Referentializer.js");

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Copyright (c) 2017-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

class Serializer {
  constructor(realm, serializerOptions = {}) {
    (0, _invariant2.default)(realm.useAbstractInterpretation);
    // Start tracking mutations
    realm.generator = new _generator.Generator(realm, "main");

    this.realm = realm;
    this.logger = new _logger.Logger(this.realm, !!serializerOptions.internalDebug);
    this.statistics = new _types.SerializerStatistics();
    this.modules = new _modules.Modules(this.realm, this.logger, this.statistics, !!serializerOptions.logModules, !!serializerOptions.delayUnsupportedRequires, !!serializerOptions.accelerateUnsupportedRequires);
    this.functions = new _functions.Functions(this.realm, this.modules.moduleTracer);
    if (serializerOptions.trace) this.realm.tracers.push(new _LoggingTracer.LoggingTracer(this.realm));

    this.options = serializerOptions;
    this.react = {
      usedReactElementKeys: new Set()
    };
  }

  _execute(sources, sourceMaps = false) {
    let realm = this.realm;
    let [res, code] = realm.$GlobalEnv.executeSources(sources, "script", ast => {
      let realmPreludeGenerator = realm.preludeGenerator;
      (0, _invariant2.default)(realmPreludeGenerator);
      let forbiddenNames = realmPreludeGenerator.nameGenerator.forbiddenNames;
      (0, _traverseFast2.default)(ast, node => {
        if (!t.isIdentifier(node)) return false;

        forbiddenNames.add(node.name);
        return true;
      });
    });

    if (res instanceof _completions.AbruptCompletion) {
      let context = new _realm.ExecutionContext();
      realm.pushContext(context);
      try {
        this.logger.logCompletion(res);
      } finally {
        realm.popContext(context);
      }
      let diagnostic = new _errors.CompilerDiagnostic("Global code may end abruptly", res.location, "PP0016", "FatalError");
      realm.handleError(diagnostic);
      throw new _errors.FatalError();
    }
    return code;
  }

  init(sources, sourceMaps = false) {
    // Phase 1: Let's interpret.
    let timingStats = this.options.profile ? new _types.TimingStatistics() : undefined;
    if (timingStats !== undefined) {
      timingStats.totalTime = Date.now();
      timingStats.globalCodeTime = Date.now();
    }
    if (this.realm.react.verbose) {
      this.logger.logInformation(`Evaluating initialization path...`);
    }
    let code = this._execute(sources);
    let environmentRecordIdAfterGlobalCode = _environment.EnvironmentRecord.nextId;
    if (timingStats !== undefined) timingStats.globalCodeTime = Date.now() - timingStats.globalCodeTime;
    if (this.logger.hasErrors()) return undefined;
    this.modules.resolveInitializedModules();
    this.functions.checkThatFunctionsAreIndependent(environmentRecordIdAfterGlobalCode);
    let reactStatistics;
    if (this.realm.react.enabled) {
      reactStatistics = new _types.ReactStatistics();
      this.functions.optimizeReactComponentTreeRoots(reactStatistics, this.react, environmentRecordIdAfterGlobalCode);
    }

    if (this.options.initializeMoreModules) {
      if (timingStats !== undefined) timingStats.initializeMoreModulesTime = Date.now();
      this.modules.initializeMoreModules();
      if (this.logger.hasErrors()) return undefined;
      if (timingStats !== undefined) timingStats.initializeMoreModulesTime = Date.now() - timingStats.initializeMoreModulesTime;
    }

    let additionalFunctionValuesAndEffects = this.functions.getAdditionalFunctionValuesToEffects();

    // Deep traversal of the heap to identify the necessary scope of residual functions
    if (timingStats !== undefined) timingStats.deepTraversalTime = Date.now();
    let preludeGenerator = this.realm.preludeGenerator;
    (0, _invariant2.default)(preludeGenerator !== undefined);
    let referentializer = new _Referentializer.Referentializer(this.realm, this.options, preludeGenerator.createNameGenerator("__scope_"), preludeGenerator.createNameGenerator("$"), this.statistics);
    if (this.realm.react.verbose) {
      this.logger.logInformation(`Visiting evaluated nodes...`);
    }
    let residualHeapVisitor = new _ResidualHeapVisitor.ResidualHeapVisitor(this.realm, this.logger, this.modules, additionalFunctionValuesAndEffects, referentializer);
    residualHeapVisitor.visitRoots();
    if (this.logger.hasErrors()) return undefined;
    if (timingStats !== undefined) timingStats.deepTraversalTime = Date.now() - timingStats.deepTraversalTime;

    if (this.realm.react.verbose) {
      this.logger.logInformation(`Serializing evaluated nodes...`);
    }
    const realmPreludeGenerator = this.realm.preludeGenerator;
    (0, _invariant2.default)(realmPreludeGenerator);
    const residualHeapValueIdentifiers = new _ResidualHeapValueIdentifiers.ResidualHeapValueIdentifiers(residualHeapVisitor.values.keys(), realmPreludeGenerator);

    let heapGraph;
    if (this.options.heapGraphFormat) {
      const heapRefCounter = new _ResidualHeapRefCounter.ResidualHeapRefCounter(this.realm, this.logger, this.modules, additionalFunctionValuesAndEffects, referentializer);
      heapRefCounter.visitRoots();

      const heapGraphGenerator = new _ResidualHeapGraphGenerator.ResidualHeapGraphGenerator(this.realm, this.logger, this.modules, additionalFunctionValuesAndEffects, residualHeapValueIdentifiers, heapRefCounter.getResult(), referentializer);
      heapGraphGenerator.visitRoots();
      (0, _invariant2.default)(this.options.heapGraphFormat);
      heapGraph = heapGraphGenerator.generateResult(this.options.heapGraphFormat);
    }

    // Phase 2: Let's serialize the heap and generate code.
    // Serialize for the first time in order to gather reference counts

    if (this.options.inlineExpressions) {
      if (timingStats !== undefined) timingStats.referenceCountsTime = Date.now();
      residualHeapValueIdentifiers.initPass1();
      new _ResidualHeapSerializer.ResidualHeapSerializer(this.realm, this.logger, this.modules, residualHeapValueIdentifiers, residualHeapVisitor.inspector, residualHeapVisitor.values, residualHeapVisitor.functionInstances, residualHeapVisitor.classMethodInstances, residualHeapVisitor.functionInfos, this.options, residualHeapVisitor.referencedDeclaredValues, additionalFunctionValuesAndEffects, residualHeapVisitor.additionalFunctionValueInfos, residualHeapVisitor.declarativeEnvironmentRecordsBindings, this.statistics, this.react, referentializer, residualHeapVisitor.generatorParents).serialize();
      if (this.logger.hasErrors()) return undefined;
      if (timingStats !== undefined) timingStats.referenceCountsTime = Date.now() - timingStats.referenceCountsTime;
      residualHeapValueIdentifiers.initPass2();
    }

    // Serialize for a second time, using reference counts to minimize number of generated identifiers
    if (timingStats !== undefined) timingStats.serializePassTime = Date.now();
    const TargetSerializer = this.options.lazyObjectsRuntime != null ? _LazyObjectsSerializer.LazyObjectsSerializer : _ResidualHeapSerializer.ResidualHeapSerializer;
    this.statistics.resetBeforePass();
    let residualHeapSerializer = new TargetSerializer(this.realm, this.logger, this.modules, residualHeapValueIdentifiers, residualHeapVisitor.inspector, residualHeapVisitor.values, residualHeapVisitor.functionInstances, residualHeapVisitor.classMethodInstances, residualHeapVisitor.functionInfos, this.options, residualHeapVisitor.referencedDeclaredValues, additionalFunctionValuesAndEffects, residualHeapVisitor.additionalFunctionValueInfos, residualHeapVisitor.declarativeEnvironmentRecordsBindings, this.statistics, this.react, referentializer, residualHeapVisitor.generatorParents);

    let ast = residualHeapSerializer.serialize();
    if (this.realm.stripFlow) {
      (0, _flow.stripFlowTypeAnnotations)(ast);
    }

    // the signature for generate is not complete, hence the any
    let generated = (0, _babelGenerator2.default)(ast, { sourceMaps: sourceMaps }, code);
    if (timingStats !== undefined) {
      timingStats.serializePassTime = Date.now() - timingStats.serializePassTime;
      timingStats.totalTime = Date.now() - timingStats.totalTime;
    }
    (0, _invariant2.default)(!this.logger.hasErrors());
    if (this.options.logStatistics) residualHeapSerializer.statistics.log();
    return {
      code: generated.code,
      map: generated.map,
      reactStatistics: reactStatistics,
      statistics: residualHeapSerializer.statistics,
      timingStats: timingStats,
      heapGraph
    };
  }
}
exports.Serializer = Serializer;
//# sourceMappingURL=serializer.js.map