<commit_before>// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @ts-nocheck
// TODO(crbug.com/1011811): Enable TypeScript compiler checks

import * as Common from '../common/common.js';
import * as Formatter from '../formatter/formatter.js';
import * as Platform from '../platform/platform.js';
import * as Root from '../root/root.js';
import * as SDK from '../sdk/sdk.js';
import * as TextUtils from '../text_utils/text_utils.js';
import * as UI from '../ui/ui.js';

const DEFAULT_TIMEOUT = 500;

export class JavaScriptAutocomplete {
  constructor() {
    /** @type {!Map<string, {date: number, value: !Promise<?Object>}>} */
    this._expressionCache = new Map();
    SDK.ConsoleModel.ConsoleModel.instance().addEventListener(
        SDK.ConsoleModel.Events.CommandEvaluated, this._clearCache, this);
    UI.Context.Context.instance().addFlavorChangeListener(SDK.RuntimeModel.ExecutionContext, this._clearCache, this);
    SDK.SDKModel.TargetManager.instance().addModelListener(
        SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, this._clearCache, this);
    SDK.SDKModel.TargetManager.instance().addModelListener(
        SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, this._clearCache, this);
  }

  _clearCache() {
    this._expressionCache.clear();
  }

  /**
   * @param {string} fullText
   * @param {string} query
   * @param {boolean=} force
   * @return {!Promise<!UI.SuggestBox.Suggestions>}
   */
  async completionsForTextInCurrentContext(fullText, query, force) {
    const trimmedText = fullText.trim();

    const [mapCompletions, expressionCompletions] = await Promise.all(
        [this._mapCompletions(trimmedText, query), this._completionsForExpression(trimmedText, query, force)]);
    return mapCompletions.concat(expressionCompletions);
  }

  /**
   * @param {string} fullText
   * @return {!Promise<?{args: !Array<!Array<string>>, argumentIndex: number}|undefined>}
   */
  async argumentsHint(fullText) {
    const functionCall = await Formatter.FormatterWorkerPool.formatterWorkerPool().findLastFunctionCall(fullText);
    if (!functionCall) {
      return null;
    }
    const executionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
    if (!executionContext) {
      return null;
    }
    const result = await executionContext.evaluate(
        {
          expression: functionCall.baseExpression,
          objectGroup: 'argumentsHint',
          includeCommandLineAPI: true,
          silent: true,
          returnByValue: false,
          generatePreview: false,
          throwOnSideEffect: true,
          timeout: DEFAULT_TIMEOUT
        },
        /* userGesture */ false, /* awaitPromise */ false);
    if (!result || result.exceptionDetails || !result.object || result.object.type !== 'function') {
      executionContext.runtimeModel.releaseObjectGroup('argumentsHint');
      return null;
    }

    const args = await this._argumentsForFunction(result.object, async () => {
      const result = await executionContext.evaluate(
          {
            expression: functionCall.receiver,
            objectGroup: 'argumentsHint',
            includeCommandLineAPI: true,
            silent: true,
            returnByValue: false,
            generatePreview: false,
            throwOnSideEffect: true,
            timeout: DEFAULT_TIMEOUT
          },
          /* userGesture */ false, /* awaitPromise */ false);
      return (result && !result.exceptionDetails && result.object) ? result.object : null;
    }, functionCall.functionName);
    executionContext.runtimeModel.releaseObjectGroup('argumentsHint');
    if (!args.length || (args.length === 1 && !args[0].length)) {
      return null;
    }
    return {args, argumentIndex: functionCall.argumentIndex};
  }

  /**
   * @param {!SDK.RemoteObject.RemoteObject} functionObject
   * @param {function():!Promise<?SDK.RemoteObject.RemoteObject>} receiverObjGetter
   * @param {string=} parsedFunctionName
   * @return {!Promise<!Array<!Array<string>>>}
   */
  async _argumentsForFunction(functionObject, receiverObjGetter, parsedFunctionName) {
    const description = functionObject.description;
    if (!description.endsWith('{ [native code] }')) {
      return [await Formatter.FormatterWorkerPool.formatterWorkerPool().argumentsList(description)];
    }

    // Check if this is a bound function.
    if (description === 'function () { [native code] }') {
      const properties = await functionObject.getOwnProperties(false);
      const internalProperties = properties.internalProperties || [];
      const targetProperty = internalProperties.find(property => property.name === '[[TargetFunction]]');
      const argsProperty = internalProperties.find(property => property.name === '[[BoundArgs]]');
      const thisProperty = internalProperties.find(property => property.name === '[[BoundThis]]');
      if (thisProperty && targetProperty && argsProperty) {
        const originalSignatures =
            await this._argumentsForFunction(targetProperty.value, () => Promise.resolve(thisProperty.value));
        const boundArgsLength = SDK.RemoteObject.RemoteObject.arrayLength(argsProperty.value);
        const clippedArgs = [];
        for (const signature of originalSignatures) {
          const restIndex = signature.slice(0, boundArgsLength).findIndex(arg => arg.startsWith('...'));
          if (restIndex !== -1) {
            clippedArgs.push(signature.slice(restIndex));
          } else {
            clippedArgs.push(signature.slice(boundArgsLength));
          }
        }
        return clippedArgs;
      }
    }
    const javaScriptMetadata =
        await Root.Runtime.Runtime.instance().extension(Common.JavaScriptMetaData.JavaScriptMetaData).instance();

    const name = /^function ([^(]*)\(/.exec(description)[1] || parsedFunctionName;
    if (!name) {
      return [];
    }
    const uniqueSignatures = javaScriptMetadata.signaturesForNativeFunction(name);
    if (uniqueSignatures) {
      return uniqueSignatures;
    }
    const receiverObj = await receiverObjGetter();
    const className = receiverObj.className;
    if (javaScriptMetadata.signaturesForInstanceMethod(name, className)) {
      return javaScriptMetadata.signaturesForInstanceMethod(name, className);
    }

    // Check for static methods on a constructor.
    if (receiverObj.type === 'function' && receiverObj.description.endsWith('{ [native code] }')) {
      const receiverName = /^function ([^(]*)\(/.exec(receiverObj.description)[1];
      const staticSignatures = javaScriptMetadata.signaturesForStaticMethod(name, receiverName);
      if (staticSignatures) {
        return staticSignatures;
      }
    }


    let protoNames;
    if (receiverObj.type === 'number') {
      protoNames = ['Number', 'Object'];<commit_msg>Fix two exceptions in JavascriptAutoComplete.js<commit_after>// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @ts-nocheck
// TODO(crbug.com/1011811): Enable TypeScript compiler checks

import * as Common from '../common/common.js';
import * as Formatter from '../formatter/formatter.js';
import * as Platform from '../platform/platform.js';
import * as Root from '../root/root.js';
import * as SDK from '../sdk/sdk.js';
import * as TextUtils from '../text_utils/text_utils.js';
import * as UI from '../ui/ui.js';

const DEFAULT_TIMEOUT = 500;

export class JavaScriptAutocomplete {
  constructor() {
    /** @type {!Map<string, {date: number, value: !Promise<?Object>}>} */
    this._expressionCache = new Map();
    SDK.ConsoleModel.ConsoleModel.instance().addEventListener(
        SDK.ConsoleModel.Events.CommandEvaluated, this._clearCache, this);
    UI.Context.Context.instance().addFlavorChangeListener(SDK.RuntimeModel.ExecutionContext, this._clearCache, this);
    SDK.SDKModel.TargetManager.instance().addModelListener(
        SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, this._clearCache, this);
    SDK.SDKModel.TargetManager.instance().addModelListener(
        SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, this._clearCache, this);
  }

  _clearCache() {
    this._expressionCache.clear();
  }

  /**
   * @param {string} fullText
   * @param {string} query
   * @param {boolean=} force
   * @return {!Promise<!UI.SuggestBox.Suggestions>}
   */
  async completionsForTextInCurrentContext(fullText, query, force) {
    const trimmedText = fullText.trim();

    const [mapCompletions, expressionCompletions] = await Promise.all(
        [this._mapCompletions(trimmedText, query), this._completionsForExpression(trimmedText, query, force)]);
    return mapCompletions.concat(expressionCompletions);
  }

  /**
   * @param {string} fullText
   * @return {!Promise<?{args: !Array<!Array<string>>, argumentIndex: number}|undefined>}
   */
  async argumentsHint(fullText) {
    const functionCall = await Formatter.FormatterWorkerPool.formatterWorkerPool().findLastFunctionCall(fullText);
    if (!functionCall) {
      return null;
    }
    const executionContext = UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext);
    if (!executionContext) {
      return null;
    }
    const result = await executionContext.evaluate(
        {
          expression: functionCall.baseExpression,
          objectGroup: 'argumentsHint',
          includeCommandLineAPI: true,
          silent: true,
          returnByValue: false,
          generatePreview: false,
          throwOnSideEffect: true,
          timeout: DEFAULT_TIMEOUT
        },
        /* userGesture */ false, /* awaitPromise */ false);
    if (!result || result.exceptionDetails || !result.object || result.object.type !== 'function') {
      executionContext.runtimeModel.releaseObjectGroup('argumentsHint');
      return null;
    }

    const args = await this._argumentsForFunction(result.object, async () => {
      const result = await executionContext.evaluate(
          {
            expression: functionCall.receiver,
            objectGroup: 'argumentsHint',
            includeCommandLineAPI: true,
            silent: true,
            returnByValue: false,
            generatePreview: false,
            throwOnSideEffect: true,
            timeout: DEFAULT_TIMEOUT
          },
          /* userGesture */ false, /* awaitPromise */ false);
      return (result && !result.exceptionDetails && result.object) ? result.object : null;
    }, functionCall.functionName);
    executionContext.runtimeModel.releaseObjectGroup('argumentsHint');
    if (!args.length || (args.length === 1 && (!args[0] || !args[0].length))) {
      return null;
    }
    return {args, argumentIndex: functionCall.argumentIndex};
  }

  /**
   * @param {!SDK.RemoteObject.RemoteObject} functionObject
   * @param {function():!Promise<?SDK.RemoteObject.RemoteObject>} receiverObjGetter
   * @param {string=} parsedFunctionName
   * @return {!Promise<!Array<!Array<string>>>}
   */
  async _argumentsForFunction(functionObject, receiverObjGetter, parsedFunctionName) {
    const description = functionObject.description;
    if (!description.endsWith('{ [native code] }')) {
      return [await Formatter.FormatterWorkerPool.formatterWorkerPool().argumentsList(description)];
    }

    // Check if this is a bound function.
    if (description === 'function () { [native code] }') {
      const properties = await functionObject.getOwnProperties(false);
      const internalProperties = properties.internalProperties || [];
      const targetProperty = internalProperties.find(property => property.name === '[[TargetFunction]]');
      const argsProperty = internalProperties.find(property => property.name === '[[BoundArgs]]');
      const thisProperty = internalProperties.find(property => property.name === '[[BoundThis]]');
      if (thisProperty && targetProperty && argsProperty) {
        const originalSignatures =
            await this._argumentsForFunction(targetProperty.value, () => Promise.resolve(thisProperty.value));
        const boundArgsLength = SDK.RemoteObject.RemoteObject.arrayLength(argsProperty.value);
        const clippedArgs = [];
        for (const signature of originalSignatures) {
          const restIndex = signature.slice(0, boundArgsLength).findIndex(arg => arg.startsWith('...'));
          if (restIndex !== -1) {
            clippedArgs.push(signature.slice(restIndex));
          } else {
            clippedArgs.push(signature.slice(boundArgsLength));
          }
        }
        return clippedArgs;
      }
    }
    const javaScriptMetadata =
        await Root.Runtime.Runtime.instance().extension(Common.JavaScriptMetaData.JavaScriptMetaData).instance();

    const name = /^function ([^(]*)\(/.exec(description)[1] || parsedFunctionName;
    if (!name) {
      return [];
    }
    const uniqueSignatures = javaScriptMetadata.signaturesForNativeFunction(name);
    if (uniqueSignatures) {
      return uniqueSignatures;
    }
    const receiverObj = await receiverObjGetter();
    if (!receiverObj) {
      return [];
    }
    const className = receiverObj.className;
    if (javaScriptMetadata.signaturesForInstanceMethod(name, className)) {
      return javaScriptMetadata.signaturesForInstanceMethod(name, className);
    }

    // Check for static methods on a constructor.
    if (receiverObj.type === 'function' && receiverObj.description.endsWith('{ [native code] }')) {
      const receiverName = /^function ([^(]*)\(/.exec(receiverObj.description)[1];
      const staticSignatures = javaScriptMetadata.signaturesForStaticMethod(name, receiverName);
      if (staticSignatures) {
        return staticSignatures;
      }
    }


    let protoNames;
    if (receiverObj.type === 'number') {
      protoNames = ['Number', 'Object'];
{'fraction_non_alphanumeric': 0.12575919971418364, 'fraction_numerical': 0.0029296177206145054, 'mean_word_length': 3.434727503168568, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5904142', 'n_tokens_mistral': 3789, 'n_tokens_neox': 3700, 'n_words': 799}