/**
   * @param lexicalScope The surrounding LexicalScope (as in Constant), or null if it is ignored (as
   *     in Mod::Constant or ::Constant)
   * @param module The receiver of the constant lookup. Must be identical to
   *     lexicalScope.getLiveModule() if lexicalScope != null.
   */
  @TruffleBoundary
  public static RubyConstant lookupConstant(
      RubyContext context, LexicalScope lexicalScope, RubyModule module, String name) {
    CompilerAsserts.neverPartOfCompilation();
    assert lexicalScope == null || lexicalScope.getLiveModule() == module;

    RubyConstant constant;

    // Look in the current module
    constant = module.getConstants().get(name);

    if (constant != null) {
      return constant;
    }

    // Look in lexical scope
    if (lexicalScope != null) {
      if (lexicalScope != context.getRootLexicalScope()) {
        // Already looked in the top lexical scope, which is module.
        lexicalScope = lexicalScope.getParent();
      }

      while (lexicalScope != context.getRootLexicalScope()) {
        constant = lexicalScope.getLiveModule().getConstants().get(name);

        if (constant != null) {
          return constant;
        }

        lexicalScope = lexicalScope.getParent();
      }
    }

    // Look in ancestors
    for (RubyModule ancestor : module.parentAncestors()) {
      constant = ancestor.getConstants().get(name);

      if (constant != null) {
        return constant;
      }
    }

    // Look in Object and its included modules
    if (module.isOnlyAModule()) {
      final RubyClass objectClass = context.getCoreLibrary().getObjectClass();

      constant = objectClass.getConstants().get(name);
      if (constant != null) {
        return constant;
      }

      for (RubyModule ancestor : objectClass.includedModules()) {
        constant = ancestor.getConstants().get(name);

        if (constant != null) {
          return constant;
        }
      }
    }

    // Nothing found
    return null;
  }