"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.isReactElement = isReactElement;
exports.getReactSymbol = getReactSymbol;
exports.isTagName = isTagName;
exports.isReactComponent = isReactComponent;
exports.valueIsClassComponent = valueIsClassComponent;
exports.valueIsKnownReactAbstraction = valueIsKnownReactAbstraction;
exports.valueIsReactLibraryObject = valueIsReactLibraryObject;
exports.valueIsLegacyCreateClassComponent = valueIsLegacyCreateClassComponent;
exports.valueIsFactoryClassComponent = valueIsFactoryClassComponent;
exports.addKeyToReactElement = addKeyToReactElement;
exports.getUniqueReactElementKey = getUniqueReactElementKey;
exports.forEachArrayValue = forEachArrayValue;
exports.convertSimpleClassComponentToFunctionalComponent = convertSimpleClassComponentToFunctionalComponent;
exports.convertFunctionalComponentToComplexClassComponent = convertFunctionalComponentToComplexClassComponent;
exports.normalizeFunctionalComponentParamaters = normalizeFunctionalComponentParamaters;
exports.createReactHintObject = createReactHintObject;
exports.getComponentTypeFromRootValue = getComponentTypeFromRootValue;
exports.deleteRefAndKeyFromProps = deleteRefAndKeyFromProps;
exports.objectHasNoPartialKeyAndRef = objectHasNoPartialKeyAndRef;
exports.flattenChildren = flattenChildren;
exports.evaluateWithNestedEffects = evaluateWithNestedEffects;
exports.deleteProperty = deleteProperty;
exports.setProperty = setProperty;
exports.getProperty = getProperty;
exports.createReactEvaluatedNode = createReactEvaluatedNode;
exports.getComponentName = getComponentName;
exports.convertConfigObjectToReactComponentTreeConfig = convertConfigObjectToReactComponentTreeConfig;
exports.getValueFromFunctionCall = getValueFromFunctionCall;
exports.sanitizeReactElementForFirstRenderOnly = sanitizeReactElementForFirstRenderOnly;

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

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

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

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

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

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

var _BinaryExpression = require("../evaluators/BinaryExpression.js");

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

var _invariant2 = _interopRequireDefault(_invariant);

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

var _babelTraverse = require("babel-traverse");

var _babelTraverse2 = _interopRequireDefault(_babelTraverse);

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

var t = _interopRequireWildcard(_babelTypes);

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

var _AbstractValue = require("../values/AbstractValue");

var _AbstractValue2 = _interopRequireDefault(_AbstractValue);

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.
 */

function isReactElement(val) {
  if (!(val instanceof _index.ObjectValue)) {
    return false;
  }
  let realm = val.$Realm;
  if (!realm.react.enabled) {
    return false;
  }
  if (realm.react.reactElements.has(val)) {
    return true;
  }
  if (val.properties.has("$$typeof")) {
    let $$typeof = (0, _index2.Get)(realm, val, "$$typeof");
    let globalObject = realm.$GlobalObject;
    let globalSymbolValue = (0, _index2.Get)(realm, globalObject, "Symbol");

    if (globalSymbolValue === realm.intrinsics.undefined) {
      if ($$typeof instanceof _index.NumberValue) {
        return $$typeof.value === 0xeac7;
      }
    } else if ($$typeof instanceof _index.SymbolValue) {
      let symbolFromRegistry = realm.globalSymbolRegistry.find(e => e.$Symbol === $$typeof);
      let _isReactElement = symbolFromRegistry !== undefined && symbolFromRegistry.$Key === "react.element";
      if (_isReactElement) {
        // add to Set to speed up future lookups
        realm.react.reactElements.add(val);
        return true;
      }
    }
  }
  return false;
}

function getReactSymbol(symbolKey, realm) {
  let reactSymbol = realm.react.symbols.get(symbolKey);
  if (reactSymbol !== undefined) {
    return reactSymbol;
  }
  let SymbolFor = realm.intrinsics.Symbol.properties.get("for");
  if (SymbolFor !== undefined) {
    let SymbolForDescriptor = SymbolFor.descriptor;

    if (SymbolForDescriptor !== undefined) {
      let SymbolForValue = SymbolForDescriptor.value;
      if (SymbolForValue instanceof _index.ObjectValue && typeof SymbolForValue.$Call === "function") {
        reactSymbol = SymbolForValue.$Call(realm.intrinsics.Symbol, [new _index.StringValue(realm, symbolKey)]);
        (0, _invariant2.default)(reactSymbol instanceof _index.SymbolValue);
        realm.react.symbols.set(symbolKey, reactSymbol);
      }
    }
  }
  (0, _invariant2.default)(reactSymbol instanceof _index.SymbolValue, `Symbol("${symbolKey}") could not be found in realm`);
  return reactSymbol;
}

function isTagName(ast) {
  return ast.type === "JSXIdentifier" && /^[a-z]|\-/.test(ast.name);
}

function isReactComponent(name) {
  return name.length > 0 && name[0] === name[0].toUpperCase();
}

function valueIsClassComponent(realm, value) {
  if (!(value instanceof _index.FunctionValue)) {
    return false;
  }
  let prototype = (0, _index2.Get)(realm, value, "prototype");

  if (prototype instanceof _index.ObjectValue) {
    return _singletons.To.ToBooleanPartial(realm, (0, _index2.Get)(realm, prototype, "isReactComponent"));
  }
  return false;
}

function valueIsKnownReactAbstraction(realm, value) {
  return value instanceof _index.AbstractObjectValue && realm.react.abstractHints.has(value);
}

// logger isn't typed otherwise it will increase flow cycle length :()
function valueIsReactLibraryObject(realm, value, logger) {
  if (realm.fbLibraries.react === value) {
    return true;
  }
  // we check that the object is the React or React-like library by checking for
  // core properties that should exist on it
  let reactVersion = logger.tryQuery(() => (0, _index2.Get)(realm, value, "version"), undefined);
  if (!(reactVersion instanceof _index.StringValue)) {
    return false;
  }
  let reactCreateElement = logger.tryQuery(() => (0, _index2.Get)(realm, value, "createElement"), undefined);
  if (!(reactCreateElement instanceof _index.FunctionValue)) {
    return false;
  }
  let reactCloneElement = logger.tryQuery(() => (0, _index2.Get)(realm, value, "cloneElement"), undefined);
  if (!(reactCloneElement instanceof _index.FunctionValue)) {
    return false;
  }
  let reactIsValidElement = logger.tryQuery(() => (0, _index2.Get)(realm, value, "isValidElement"), undefined);
  if (!(reactIsValidElement instanceof _index.FunctionValue)) {
    return false;
  }
  let reactComponent = logger.tryQuery(() => (0, _index2.Get)(realm, value, "Component"), undefined);
  if (!(reactComponent instanceof _index.FunctionValue)) {
    return false;
  }
  let reactChildren = logger.tryQuery(() => (0, _index2.Get)(realm, value, "Children"), undefined);
  if (!(reactChildren instanceof _index.ObjectValue)) {
    return false;
  }
  return false;
}

function valueIsLegacyCreateClassComponent(realm, value) {
  if (!(value instanceof _index.FunctionValue)) {
    return false;
  }
  let prototype = (0, _index2.Get)(realm, value, "prototype");

  if (prototype instanceof _index.ObjectValue) {
    return prototype.properties.has("__reactAutoBindPairs");
  }
  return false;
}

function valueIsFactoryClassComponent(realm, value) {
  if (value instanceof _index.ObjectValue) {
    return _singletons.To.ToBooleanPartial(realm, (0, _index2.Get)(realm, value, "render"));
  }
  return false;
}

function addKeyToReactElement(realm, reactSerializerState, reactElement) {
  // we need to apply a key when we're branched
  let currentKeyValue = (0, _index2.Get)(realm, reactElement, "key") || realm.intrinsics.null;
  let uniqueKey = getUniqueReactElementKey("", reactSerializerState.usedReactElementKeys);
  let newKeyValue = new _index.StringValue(realm, uniqueKey);
  if (currentKeyValue !== realm.intrinsics.null) {
    newKeyValue = (0, _BinaryExpression.computeBinary)(realm, "+", currentKeyValue, newKeyValue);
  }
  setProperty(reactElement, "key", newKeyValue);
}
// we create a unique key for each JSXElement to prevent collisions
// otherwise React will detect a missing/conflicting key at runtime and
// this can break the reconcilation of JSXElements in arrays
function getUniqueReactElementKey(index, usedReactElementKeys) {
  let key;
  do {
    key = Math.random().toString(36).replace(/[^a-z]+/g, "").substring(0, 2);
  } while (usedReactElementKeys.has(key));
  usedReactElementKeys.add(key);
  if (index !== undefined) {
    return `${key}${index}`;
  }
  return key;
}

// a helper function to loop over ArrayValues
function forEachArrayValue(realm, array, mapFunc) {
  let lengthValue = (0, _index2.Get)(realm, array, "length");
  (0, _invariant2.default)(lengthValue instanceof _index.NumberValue, "Invalid length on ArrayValue during reconcilation");
  let length = lengthValue.value;
  for (let i = 0; i < length; i++) {
    let elementProperty = array.properties.get("" + i);
    let elementPropertyDescriptor = elementProperty && elementProperty.descriptor;
    (0, _invariant2.default)(elementPropertyDescriptor, `Invalid ArrayValue[${i}] descriptor`);
    let elementValue = elementPropertyDescriptor.value;
    if (elementValue instanceof _index.Value) {
      mapFunc(elementValue, elementPropertyDescriptor);
    }
  }
}

function GetDescriptorForProperty(value, propertyName) {
  let object = value.properties.get(propertyName);
  (0, _invariant2.default)(object);
  return object.descriptor;
}

function convertSimpleClassComponentToFunctionalComponent(realm, complexComponentType, additionalFunctionEffects) {
  let prototype = complexComponentType.properties.get("prototype");
  (0, _invariant2.default)(prototype);
  (0, _invariant2.default)(prototype.descriptor);
  prototype.descriptor.configurable = true;
  _singletons.Properties.DeletePropertyOrThrow(realm, complexComponentType, "prototype");

  // change the function kind
  complexComponentType.$FunctionKind = "normal";
  // set the prototype back to an object
  complexComponentType.$Prototype = realm.intrinsics.FunctionPrototype;
  // give the function the functional components params
  complexComponentType.$FormalParameters = [t.identifier("props"), t.identifier("context")];
  // add a transform to occur after the additional function has serialized the body of the class
  additionalFunctionEffects.transforms.push(body => {
    // as this was a class before and is now a functional component, we need to replace
    // this.props and this.context to props and context, via the function arugments
    let funcNode = t.functionExpression(null, [], t.blockStatement(body));

    (0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(funcNode)])), {
      "Identifier|ThisExpression"(path) {
        let node = path.node;
        if (t.isIdentifier(node) && node.name === "this" || t.isThisExpression(node)) {
          let parentPath = path.parentPath;
          let parentNode = parentPath.node;

          if (t.isMemberExpression(parentNode)) {
            // remove the "this" from the member
            parentPath.replaceWith(parentNode.property);
          } else {
            throw new _errors.FatalError(`conversion of a simple class component to functional component failed due to "this" not being replaced`);
          }
        }
      }
    }, undefined, undefined, undefined);
  });
}

function createBinding(descriptor, key, object) {
  return {
    descriptor,
    key,
    object
  };
}

function cloneProperties(realm, properties, object) {
  let newProperties = new Map();
  for (let [propertyName, { descriptor }] of properties) {
    newProperties.set(propertyName, createBinding((0, _index2.cloneDescriptor)(descriptor), propertyName, object));
  }
  return newProperties;
}

function cloneSymbols(realm, symbols, object) {
  let newSymbols = new Map();
  for (let [symbol, { descriptor }] of symbols) {
    newSymbols.set(symbol, createBinding((0, _index2.cloneDescriptor)(descriptor), symbol, object));
  }
  return newSymbols;
}

function cloneValue(realm, originalValue, _prototype, copyToObject) {
  if (originalValue instanceof _index.FunctionValue) {
    return cloneFunction(realm, originalValue, _prototype, copyToObject);
  }
  (0, _invariant2.default)(false, "TODO: add support to cloneValue() for more value types");
}

function cloneFunction(realm, originalValue, _prototype, copyToObject) {
  let newValue;
  if (originalValue instanceof _index.ECMAScriptSourceFunctionValue) {
    newValue = copyToObject || new _index.ECMAScriptSourceFunctionValue(realm, originalValue.intrinsicName);
    (0, _invariant2.default)(newValue instanceof _index.ECMAScriptSourceFunctionValue);
    // $FlowFixMe: complains about Object.assign
    Object.assign(newValue, originalValue);
    let properties = cloneProperties(realm, originalValue.properties, newValue);
    newValue.properties = properties;
    let symbols = cloneSymbols(realm, originalValue.symbols, newValue);
    newValue.symbols = symbols;

    // handle home object + prototype
    let originalPrototype = originalValue.$HomeObject;
    (0, _invariant2.default)(originalPrototype instanceof _index.ObjectValue);
    let prototype = _prototype || clonePrototype(realm, originalPrototype);
    newValue.$HomeObject = prototype;
    if (originalPrototype.properties.has("constructor")) {
      _singletons.Properties.Set(realm, prototype, "constructor", newValue, false);
    }
    if (originalValue.properties.has("prototype")) {
      _singletons.Properties.Set(realm, newValue, "prototype", prototype, false);
    }
  }
  (0, _invariant2.default)(newValue instanceof _index.FunctionValue, "TODO: add support to cloneValue() for more function types");
  return newValue;
}

function clonePrototype(realm, prototype) {
  (0, _invariant2.default)(prototype instanceof _index.ObjectValue);
  let newPrototype = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, prototype.intrinsicName);

  Object.assign(newPrototype, prototype);
  for (let [propertyName] of prototype.properties) {
    if (propertyName !== "constructor") {
      let originalValue = (0, _index2.Get)(realm, prototype, propertyName);
      let newValue = cloneValue(realm, originalValue, prototype);
      _singletons.Properties.Set(realm, newPrototype, propertyName, newValue, false);
    }
  }
  for (let [symbol] of prototype.symbols) {
    let originalValue = (0, _index2.Get)(realm, prototype, symbol);
    let newValue = cloneValue(realm, originalValue, prototype);
    _singletons.Properties.Set(realm, newPrototype, symbol, newValue, false);
  }
  return newPrototype;
}

const skipFunctionProperties = new Set(["length", "prototype", "arguments", "name", "caller"]);

function convertFunctionalComponentToComplexClassComponent(realm, functionalComponentType, complexComponentType, additionalFunctionEffects) {
  (0, _invariant2.default)(complexComponentType instanceof _index.ECMAScriptSourceFunctionValue);
  // get all properties on the functional component that were added in user-code
  // we add defaultProps as undefined, as merging a class component's defaultProps on to
  // a differnet component isn't right, we can discard defaultProps instead via folding
  // we also don't want propTypes from the class component, so we remove that too
  let userCodePropertiesToAdd = new Map([["defaultProps", createBinding(undefined, "defaultProps", functionalComponentType)], ["propTypes", createBinding(undefined, "propTypes", functionalComponentType)]]);
  let userCodeSymbolsToAdd = new Map();

  for (let [propertyName, binding] of functionalComponentType.properties) {
    if (!skipFunctionProperties.has(propertyName)) {
      userCodePropertiesToAdd.set(propertyName, binding);
    }
  }
  for (let [symbol, binding] of functionalComponentType.symbols) {
    userCodeSymbolsToAdd.set(symbol, binding);
  }

  cloneValue(realm, complexComponentType, null, functionalComponentType);
  // then copy back and properties that were on the original functional component
  // ensuring we overwrite any existing ones
  for (let [propertyName, binding] of userCodePropertiesToAdd) {
    functionalComponentType.properties.set(propertyName, binding);
  }
  for (let [symbol, binding] of userCodeSymbolsToAdd) {
    functionalComponentType.symbols.set(symbol, binding);
  }
  // add a transform to occur after the additional function has serialized the body of the class
  additionalFunctionEffects.transforms.push(body => {
    // as we've converted a functional component to a complex one, we are going to have issues with
    // "props" and "context" references, as they're now going to be "this.props" and "this.context".
    // we simply need a to add to vars to beginning of the body to get around this
    // if they're not used, any DCE tool post-Prepack (GCC or Uglify) will remove them
    body.unshift(t.variableDeclaration("var", [t.variableDeclarator(t.identifier("props"), t.memberExpression(t.thisExpression(), t.identifier("props"))), t.variableDeclarator(t.identifier("context"), t.memberExpression(t.thisExpression(), t.identifier("context")))]));
  });
}

function normalizeFunctionalComponentParamaters(func) {
  // fix the length as we may change the arguments
  let lengthProperty = GetDescriptorForProperty(func, "length");
  (0, _invariant2.default)(lengthProperty);
  lengthProperty.writable = false;
  lengthProperty.enumerable = false;
  lengthProperty.configurable = true;
  // ensure the length value is set to the new value
  let lengthValue = lengthProperty.value;
  (0, _invariant2.default)(lengthValue instanceof _index.NumberValue);
  lengthValue.value = 2;

  func.$FormalParameters = func.$FormalParameters.map((param, i) => {
    if (i === 0) {
      return t.isIdentifier(param) ? param : t.identifier("props");
    } else {
      return t.isIdentifier(param) ? param : t.identifier("context");
    }
  });
  if (func.$FormalParameters.length === 1) {
    func.$FormalParameters.push(t.identifier("context"));
  }
}

function createReactHintObject(object, propertyName, args) {
  return {
    object,
    propertyName,
    args
  };
}

function getComponentTypeFromRootValue(realm, value) {
  let _valueIsKnownReactAbstraction = valueIsKnownReactAbstraction(realm, value);
  if (!(value instanceof _index.ECMAScriptSourceFunctionValue || _valueIsKnownReactAbstraction)) {
    return null;
  }
  if (_valueIsKnownReactAbstraction) {
    (0, _invariant2.default)(value instanceof _AbstractValue2.default);
    let reactHint = realm.react.abstractHints.get(value);

    (0, _invariant2.default)(reactHint);
    if (typeof reactHint !== "string" && reactHint.object === realm.fbLibraries.reactRelay) {
      switch (reactHint.propertyName) {
        case "createFragmentContainer":
        case "createPaginationContainer":
        case "createRefetchContainer":
          (0, _invariant2.default)(Array.isArray(reactHint.args));
          // componentType is the 1st argument of a ReactRelay container
          let componentType = reactHint.args[0];
          (0, _invariant2.default)(componentType instanceof _index.ECMAScriptSourceFunctionValue);
          return componentType;
        default:
          (0, _invariant2.default)(false, `unsupported known React abstraction - ReactRelay property "${reactHint.propertyName}" not supported`);
      }
    }
    (0, _invariant2.default)(false, "unsupported known React abstraction");
  } else {
    (0, _invariant2.default)(value instanceof _index.ECMAScriptSourceFunctionValue);
    return value;
  }
}

// props should never have "ref" or "key" properties, as they're part of ReactElement
// object instead. to ensure that we can give this hint, we create them and then
// delete them, so their descriptor is left undefined. we use this knowledge later
// to ensure that when dealing with creating ReactElements with partial config,
// we don't have to bail out becuase "config" may or may not have "key" or/and "ref"
function deleteRefAndKeyFromProps(realm, props) {
  setProperty(props, "ref", realm.intrinsics.undefined);
  deleteProperty(props, "ref");
  setProperty(props, "key", realm.intrinsics.undefined);
  deleteProperty(props, "key");
}

function objectHasNoPartialKeyAndRef(realm, object) {
  if (object instanceof _AbstractValue2.default) {
    return true;
  }
  return !((0, _index2.Get)(realm, object, "key") instanceof _AbstractValue2.default || (0, _index2.Get)(realm, object, "ref") instanceof _AbstractValue2.default);
}

function recursivelyFlattenArray(realm, array, targetArray) {
  forEachArrayValue(realm, array, item => {
    if (item instanceof _index.ArrayValue) {
      recursivelyFlattenArray(realm, item, targetArray);
    } else {
      let lengthValue = (0, _index2.Get)(realm, targetArray, "length");
      (0, _invariant2.default)(lengthValue instanceof _index.NumberValue);
      _singletons.Properties.Set(realm, targetArray, "" + lengthValue.value, item, true);
    }
  });
}

function flattenChildren(realm, array) {
  let flattenedChildren = _singletons.Create.ArrayCreate(realm, 0);
  recursivelyFlattenArray(realm, array, flattenedChildren);
  return flattenedChildren;
}

function evaluateWithNestedEffects(realm, nestedEffects, f) {
  let nextEffects = nestedEffects.slice();
  let effects = nextEffects.shift();
  let [value, generator, modifiedBindings, modifiedProperties, createdObjects] = effects;
  realm.applyEffects([value, new _generator.Generator(realm, "evaluateWithNestedEffects"), modifiedBindings, modifiedProperties, createdObjects]);
  try {
    if (nextEffects.length === 0) {
      return f(generator, value);
    } else {
      return evaluateWithNestedEffects(realm, nextEffects, f);
    }
  } finally {
    realm.restoreBindings(modifiedBindings);
    realm.restoreProperties(modifiedProperties);
  }
}

// This function is mainly use to delete internal properties
// on objects that we know are safe to access internally
// such as ReactElements. Deleting here does not
// emit change to modified bindings and is intended
// for only internal usage – not for user-land code
function deleteProperty(object, property) {
  if (object instanceof _index.AbstractObjectValue) {
    let elements = object.values.getElements();
    if (elements && elements.size > 0) {
      object = Array.from(elements)[0];
    }
    (0, _invariant2.default)(object instanceof _index.ObjectValue);
  }
  let binding;
  if (typeof property === "string") {
    binding = object.properties.get(property);
  } else {
    binding = object.symbols.get(property);
  }
  if (!binding) {
    return;
  }
  binding.descriptor = undefined;
}

// This function is mainly use to set internal properties
// on objects that we know are safe to access internally
// such as ReactElements. Setting properties here does not
// emit change to modified bindings and is intended
// for only internal usage – not for user-land code
function setProperty(object, property, value) {
  if (object instanceof _index.AbstractObjectValue) {
    let elements = object.values.getElements();
    if (elements && elements.size > 0) {
      object = Array.from(elements)[0];
    }
    (0, _invariant2.default)(object instanceof _index.ObjectValue);
  }
  let defaultBinding = {
    descriptor: {
      configurable: true,
      enumerable: true,
      writable: true,
      value
    },
    key: property,
    object
  };
  let binding;
  if (typeof property === "string") {
    binding = object.properties.get(property);
    if (!binding) {
      binding = defaultBinding;
      object.properties.set(property, binding);
    }
  } else if (property instanceof _index.SymbolValue) {
    binding = object.symbols.get(property);
    if (!binding) {
      binding = defaultBinding;
      object.symbols.set(property, binding);
    }
  }
  (0, _invariant2.default)(binding);
  let descriptor = binding.descriptor;

  if (!descriptor) {
    return;
  }
  descriptor.value = value;
}

// This function is mainly use to get internal properties
// on objects that we know are safe to access internally
// such as ReactElements. Getting properties here does
// not emit change to modified bindings and is intended
// for only internal usage – not for user-land code
function getProperty(realm, object, property) {
  if (object instanceof _index.AbstractObjectValue) {
    let elements = object.values.getElements();
    if (elements && elements.size > 0) {
      object = Array.from(elements)[0];
    }
    (0, _invariant2.default)(object instanceof _index.ObjectValue);
  }
  let binding;
  if (typeof property === "string") {
    binding = object.properties.get(property);
  } else {
    binding = object.symbols.get(property);
  }
  if (!binding) {
    (0, _invariant2.default)(!object.isPartialObject(), "getProperty used on a partial object with no binding");
    return realm.intrinsics.undefined;
  }
  let descriptor = binding.descriptor;

  if (!descriptor) {
    return realm.intrinsics.undefined;
  }
  let value;
  if (descriptor.value) {
    value = descriptor.value;
  } else if (descriptor.get || descriptor.set) {
    _AbstractValue2.default.reportIntrospectionError(object, `react/utils/getProperty unsupported getter/setter property`);
    throw new _errors.FatalError();
  }
  (0, _invariant2.default)(value instanceof _index.Value, `react/utils/getProperty should not be called on internal properties`);
  return value;
}

function createReactEvaluatedNode(status, name) {
  return {
    children: [],
    message: "",
    name,
    status
  };
}

function getComponentName(realm, componentType) {
  (0, _invariant2.default)(componentType instanceof _index.ECMAScriptSourceFunctionValue || componentType instanceof _index.BoundFunctionValue || componentType instanceof _index.AbstractObjectValue || componentType instanceof _AbstractValue2.default);
  let boundText = componentType instanceof _index.BoundFunctionValue ? "bound " : "";

  if (componentType.__originalName) {
    return boundText + componentType.__originalName;
  }
  if (realm.fbLibraries.reactRelay !== undefined) {
    if (componentType === (0, _index2.Get)(realm, realm.fbLibraries.reactRelay, "QueryRenderer")) {
      return boundText + "QueryRenderer";
    }
  }
  if (componentType instanceof _index.ECMAScriptSourceFunctionValue && componentType.$Prototype !== undefined) {
    let name = (0, _index2.Get)(realm, componentType, "name");

    if (name instanceof _index.StringValue) {
      return boundText + name.value;
    }
  }
  return boundText + "anonymous";
}

function convertConfigObjectToReactComponentTreeConfig(realm, config) {
  // defaults
  let firstRenderOnly = false;

  if (!(config instanceof _index.UndefinedValue)) {
    for (let [key] of config.properties) {
      let propValue = getProperty(realm, config, key);
      if (propValue instanceof _index.StringValue || propValue instanceof _index.NumberValue || propValue instanceof _index.BooleanValue) {
        let value = propValue.value;

        // boolean options
        if (typeof value === "boolean") {
          if (key === "firstRenderOnly") {
            firstRenderOnly = value;
          }
        }
      } else {
        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();
      }
    }
  }
  return {
    firstRenderOnly
  };
}

function getValueFromFunctionCall(realm, func, funcThis, args, isConstructor = false) {
  (0, _invariant2.default)(func.$Call, "Expected function to be a FunctionValue with $Call method");
  let funcCall = func.$Call;
  let newCall = func.$Construct;
  let effects;
  try {
    effects = realm.evaluateForEffects(() => {
      (0, _invariant2.default)(func);
      if (isConstructor) {
        (0, _invariant2.default)(newCall);
        return newCall(args, func);
      } else {
        return funcCall(funcThis, args);
      }
    }, null, "getValueFromFunctionCall");
  } catch (error) {
    throw error;
  }

  let completion = effects[0];
  if (completion instanceof _completions.PossiblyNormalCompletion) {
    // in this case one of the branches may complete abruptly, which means that
    // not all control flow branches join into one flow at this point.
    // Consequently we have to continue tracking changes until the point where
    // all the branches come together into one.
    completion = realm.composeWithSavedCompletion(completion);
  }
  // Note that the effects of (non joining) abrupt branches are not included
  // in joinedEffects, but are tracked separately inside completion.
  realm.applyEffects(effects);
  // return or throw completion
  if (completion instanceof _completions.AbruptCompletion) throw completion;
  (0, _invariant2.default)(completion instanceof _index.Value);
  return completion;
}

function isEventProp(name) {
  return name.length > 2 && name[0].toLowerCase() === "o" && name[1].toLowerCase() === "n";
}

function sanitizeReactElementForFirstRenderOnly(realm, reactElement) {
  let typeValue = (0, _index2.Get)(realm, reactElement, "type");

  // ensure ref is null, as we don't use that on first render
  setProperty(reactElement, "ref", realm.intrinsics.null);
  // when dealing with host nodes, we want to sanitize them futher
  if (typeValue instanceof _index.StringValue) {
    let propsValue = (0, _index2.Get)(realm, reactElement, "props");
    if (propsValue instanceof _index.ObjectValue) {
      // remove all values apart from string/number/boolean
      for (let [propName] of propsValue.properties) {
        // check for onSomething prop event handlers, i.e. onClick
        if (isEventProp(propName)) {
          deleteProperty(reactElement, "ref");
        }
      }
    }
  }
  return reactElement;
}
//# sourceMappingURL=utils.js.map