"use strict";

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

exports.default = function (realm) {
  let global = realm.$GlobalObject;

  global.$DefineOwnProperty("dump", {
    value: new _index.NativeFunctionValue(realm, "global.dump", "dump", 0, (context, args) => {
      console.log("dump", args.map(arg => arg.serialize()));
      return context;
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // Helper function to model values that are obtained from the environment,
  // and whose concrete values are not known at Prepack-time.
  // __abstract(typeNameOrTemplate, name, options) creates a new abstract value
  // where typeNameOrTemplate can be...
  // - 'string', 'boolean', 'number', 'object', 'function' or
  // - ':string', ':boolean', ':number', ':object', ':function' to indicate that
  //   the abstract value represents a function that only returns values of the specified type, or
  // - an actual object defining known properties.
  // If the abstract value gets somehow embedded in the final heap,
  // it will be referred to by the supplied name in the generated code.
  global.$DefineOwnProperty("__abstract", {
    value: createAbstractFunction(realm),
    writable: true,
    enumerable: false,
    configurable: true
  });

  global.$DefineOwnProperty("__abstractOrNull", {
    value: createAbstractFunction(realm, realm.intrinsics.null),
    writable: true,
    enumerable: false,
    configurable: true
  });

  global.$DefineOwnProperty("__abstractOrNullOrUndefined", {
    value: createAbstractFunction(realm, realm.intrinsics.null, realm.intrinsics.undefined),
    writable: true,
    enumerable: false,
    configurable: true
  });

  global.$DefineOwnProperty("__abstractOrUndefined", {
    value: createAbstractFunction(realm, realm.intrinsics.undefined),
    writable: true,
    enumerable: false,
    configurable: true
  });

  global.$DefineOwnProperty("__optimizedFunctions", {
    value: new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, "__optimizedFunctions", true),
    writable: true,
    enumerable: false,
    configurable: true
  });

  let additonalFunctionUid = 0;
  // Allows dynamically registering optimized functions.
  // WARNING: these functions will get exposed at global scope and called there.
  // NB: If we interpret one of these calls in an evaluateForEffects context
  //     that is not subsequently applied, the function will not be registered
  //     (because prepack won't have a correct value for the FunctionValue itself)
  global.$DefineOwnProperty("__optimize", {
    value: new _index.NativeFunctionValue(realm, "global.__optimize", "__optimize", 0, (context, [value, config]) => {
      // only optimize functions for now
      if (value instanceof _index.ECMAScriptSourceFunctionValue) {
        realm.assignToGlobal(t.memberExpression(t.memberExpression(t.identifier("global"), t.identifier("__optimizedFunctions")), t.identifier("" + additonalFunctionUid++)), value);
      }
      return value;
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });

  if (realm.react.enabled) {
    global.$DefineOwnProperty("__reactComponentTrees", {
      value: new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, "__reactComponentTrees", true),
      writable: true,
      enumerable: false,
      configurable: true
    });
    let reactComponentRootUid = 0;
    global.$DefineOwnProperty("__optimizeReactComponentTree", {
      value: new _index.NativeFunctionValue(realm, "global.__optimizeReactComponentTree", "__optimizeReactComponentTree", 0, (context, [component, config]) => {
        let hasValidComponent = component instanceof _index.ECMAScriptSourceFunctionValue || (0, _utils2.valueIsKnownReactAbstraction)(realm, component);
        let hasValidConfig = config instanceof _index.ObjectValue || config === realm.intrinsics.undefined || config === undefined;

        if (!hasValidComponent || !hasValidConfig) {
          let diagnostic = new _errors.CompilerDiagnostic("__optimizeReactComponentTree(rootComponent, config) has been called with invalid arguments", realm.currentLocation, "PP0024", "FatalError");
          realm.handleError(diagnostic);
          if (realm.handleError(diagnostic) === "Fail") throw new _errors.FatalError();
        }
        let reactComponentTree = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype);
        reactComponentTree.$Set("rootComponent", component, reactComponentTree);
        reactComponentTree.$Set("config", config || realm.intrinsics.undefined, reactComponentTree);

        realm.assignToGlobal(t.memberExpression(t.memberExpression(t.identifier("global"), t.identifier("__reactComponentTrees")), t.identifier("" + reactComponentRootUid++)), reactComponentTree);
        return component;
      }),
      writable: true,
      enumerable: false,
      configurable: true
    });
  }

  global.$DefineOwnProperty("__evaluatePureFunction", {
    value: new _index.NativeFunctionValue(realm, "global.__evaluatePureFunction", "__evaluatePureFunction", 0, (context, [functionValue]) => {
      (0, _invariant2.default)(functionValue instanceof _index.ECMAScriptSourceFunctionValue);
      (0, _invariant2.default)(typeof functionValue.$Call === "function");
      let functionCall = functionValue.$Call;
      return realm.evaluatePure(() => functionCall(realm.intrinsics.undefined, []));
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // Maps from initialized moduleId to exports object
  // NB: Changes to this shouldn't ever be serialized
  global.$DefineOwnProperty("__initializedModules", {
    value: new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, "__initializedModules", true),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // Helper function used to instatiate a residual function
  function deriveNativeFunctionValue(unsafe) {
    return new _index.NativeFunctionValue(realm, "global.__residual", "__residual", 2, (context, [typeNameOrTemplate, f, ...args]) => {
      if (!realm.useAbstractInterpretation) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
      }

      let { type, template } = (0, _utils.parseTypeNameOrTemplate)(realm, typeNameOrTemplate);

      if (!_index.Value.isTypeCompatibleWith(f.constructor, _index.FunctionValue)) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "cannot determine residual function");
      }
      (0, _invariant2.default)(f instanceof _index.FunctionValue);
      f.isResidual = true;
      if (unsafe) f.isUnsafeResidual = true;
      let result = _index.AbstractValue.createTemporalFromBuildFunction(realm, type, [f].concat(args), nodes => t.callExpression(nodes[0], nodes.slice(1)));
      if (template) {
        (0, _invariant2.default)(result instanceof _index.AbstractValue, "the nested properties should only be rebuilt for an abstract value");
        template.makePartial();
        result.values = new _index2.ValuesDomain(new Set([template]));
        (0, _invariant2.default)(realm.generator);
        realm.rebuildNestedProperties(result, result.getIdentifier().name);
      }
      return result;
    });
  }

  // Helper function that identifies a computation that must remain part of the residual program and cannot be partially evaluated,
  // e.g. because it contains a loop over abstract values.
  // __residual(typeNameOrTemplate, function, arg0, arg1, ...) creates a new abstract value
  // that is computed by invoking function(arg0, arg1, ...) in the residual program and
  // where typeNameOrTemplate either either 'string', 'boolean', 'number', 'object', or an actual object defining known properties.
  // The function must not have side effects, and it must not access any state (besides the supplied arguments).
  global.$DefineOwnProperty("__residual", {
    value: deriveNativeFunctionValue(false),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // Helper function that identifies a variant of the residual function that has implicit dependencies. This version of residual will infer the dependencies
  // and rewrite the function body to do the same thing as the original residual function.
  global.$DefineOwnProperty("__residual_unsafe", {
    value: deriveNativeFunctionValue(true),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // TODO #1023: Remove this property. It's just here as some existing internal test cases assume that the __annotate property is exists and is readable.
  global.$DefineOwnProperty("__annotate", {
    value: realm.intrinsics.undefined,
    writable: true,
    enumerable: false,
    configurable: true
  });

  // Internal helper function for tests.
  // __isAbstract(value) checks if a given value is abstract.
  global.$DefineOwnProperty("__isAbstract", {
    value: new _index.NativeFunctionValue(realm, "global.__isAbstract", "__isAbstract", 1, (context, [value]) => {
      return new _index.BooleanValue(realm, value instanceof _index.AbstractValue);
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // __makePartial(object) marks an (abstract) object as partial.
  global.$DefineOwnProperty("__makePartial", {
    value: new _index.NativeFunctionValue(realm, "global.__makePartial", "__makePartial", 1, (context, [object]) => {
      if (object instanceof _index.AbstractObjectValue || object instanceof _index.ObjectValue) {
        object.makePartial();
        return object;
      }
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // __makeSimple(object) marks an (abstract) object as one that has no getters or setters.
  global.$DefineOwnProperty("__makeSimple", {
    value: new _index.NativeFunctionValue(realm, "global.__makeSimple", "__makeSimple", 1, (context, [object, option]) => {
      if (object instanceof _index.AbstractObjectValue || object instanceof _index.ObjectValue) {
        object.makeSimple(option);
        return object;
      }
      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });

  // Helper function that emits a check whether a given object property has a particular value.
  global.$DefineOwnProperty("__assumeDataProperty", {
    value: new _index.NativeFunctionValue(realm, "global.__assumeDataProperty", "__assumeDataProperty", 3, (context, [object, propertyName, value, invariantOptions]) => {
      if (!realm.useAbstractInterpretation) {
        throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
      }

      if (object instanceof _index.AbstractObjectValue || object instanceof _index.ObjectValue) {
        let generator = realm.generator;
        (0, _invariant2.default)(generator);

        let key = _singletons.To.ToStringPartial(realm, propertyName);
        let propertyIdentifier = generator.getAsPropertyNameExpression(key);
        let computed = !t.isIdentifier(propertyIdentifier);

        if (realm.emitConcreteModel) {
          generator.emitConcreteModel(key, value);
        } else {
          let accessedPropertyOf = objectNode => t.memberExpression(objectNode, propertyIdentifier, computed);
          let inExpressionOf = objectNode => t.unaryExpression("!", t.binaryExpression("in", t.stringLiteral(key), objectNode), true);

          let invariantOptionString = invariantOptions ? _singletons.To.ToStringPartial(realm, invariantOptions) : "FULL_INVARIANT";
          switch (invariantOptionString) {
            // checks (!property in object || object.property === undefined)
            case "VALUE_DEFINED_INVARIANT":
              let condition = ([objectNode, valueNode]) => t.logicalExpression("||", inExpressionOf(objectNode), t.binaryExpression("===", accessedPropertyOf(objectNode), t.valueToNode(undefined)));
              generator.emitInvariant([object, value, object], condition, objnode => accessedPropertyOf(objnode));
              break;
            case "SKIP_INVARIANT":
              break;
            case "FULL_INVARIANT":
              generator.emitFullInvariant(object, key, value);
              break;
            default:
              (0, _invariant2.default)(false, "Invalid invariantOption " + invariantOptionString);
          }
        }
        realm.generator = undefined; // don't emit code during the following $Set call
        // casting to due to Flow workaround above
        object.$Set(key, value, object);
        realm.generator = generator;
        if (object.intrinsicName) realm.rebuildObjectProperty(object, key, value, object.intrinsicName);
        return context.$Realm.intrinsics.undefined;
      }

      throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });

  global.$DefineOwnProperty("__IntrospectionError", {
    value: realm.intrinsics.__IntrospectionError,
    writable: true,
    enumerable: false,
    configurable: true
  });

  global.$DefineOwnProperty("__isIntegral", {
    value: new _index.NativeFunctionValue(realm, "global.__isIntegral", "__isIntegral", 1, (context, [value]) => {
      return new _index.BooleanValue(realm, value instanceof _index.IntegralValue);
    }),
    writable: true,
    enumerable: false,
    configurable: true
  });
};

var _index = require("../../values/index.js");

var _singletons = require("../../singletons.js");

var _index2 = require("../../domains/index.js");

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

var t = _interopRequireWildcard(_babelTypes);

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

var _invariant2 = _interopRequireDefault(_invariant);

var _utils = require("./utils.js");

var _utils2 = require("../../react/utils.js");

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

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

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 createAbstractFunction(realm, ...additionalValues) {
  return new _index.NativeFunctionValue(realm, "global.__abstract", "__abstract", 0, (context, [typeNameOrTemplate, name]) => {
    if (name instanceof _index.StringValue) name = name.value;
    if (name !== undefined && typeof name !== "string") {
      throw new TypeError("intrinsic name argument is not a string");
    }
    return (0, _utils.createAbstract)(realm, typeNameOrTemplate, name, ...additionalValues);
  });
} /**
   * 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.
   */
//# sourceMappingURL=global.js.map