RawObject dictMergeImpl()

in runtime/dict-builtins.cpp [607:738]


RawObject dictMergeImpl(Thread* thread, const Dict& dict, const Object& mapping,
                        Override do_override) {
  Runtime* runtime = thread->runtime();
  if (runtime->isInstanceOfDict(*mapping)) {
    return dictMergeDict(thread, dict, mapping, do_override);
  }

  HandleScope scope(thread);
  Object key(&scope, NoneType::object());
  Object hash_obj(&scope, NoneType::object());
  Object value(&scope, NoneType::object());
  Object included(&scope, NoneType::object());
  Object keys_method(&scope,
                     runtime->attributeAtById(thread, mapping, ID(keys)));
  if (keys_method.isError()) {
    return *keys_method;
  }

  // Generic mapping, use keys() and __getitem__()
  Object subscr_method(
      &scope, runtime->attributeAtById(thread, mapping, ID(__getitem__)));

  if (subscr_method.isError()) {
    return *subscr_method;
  }
  Object keys(&scope, Interpreter::callMethod1(thread, keys_method, mapping));
  if (keys.isError()) return *keys;

  if (keys.isList()) {
    List keys_list(&scope, *keys);
    Object dict_result(&scope, NoneType::object());
    for (word i = 0; i < keys_list.numItems(); ++i) {
      key = keys_list.at(i);
      hash_obj = Interpreter::hash(thread, key);
      if (hash_obj.isErrorException()) return *hash_obj;
      word hash = SmallInt::cast(*hash_obj).value();
      if (do_override == Override::kOverride) {
        value = Interpreter::callMethod2(thread, subscr_method, mapping, key);
        if (value.isError()) return *value;
        dict_result = dictAtPut(thread, dict, key, hash, value);
        if (dict_result.isErrorException()) return *dict_result;
      } else {
        included = dictIncludes(thread, dict, key, hash);
        if (included.isErrorException()) return *included;
        if (included == Bool::falseObj()) {
          value = Interpreter::callMethod2(thread, subscr_method, mapping, key);
          if (value.isError()) return *value;
          dict_result = dictAtPut(thread, dict, key, hash, value);
          if (dict_result.isErrorException()) return *dict_result;
        } else if (do_override == Override::kError) {
          return thread->raise(LayoutId::kKeyError, *key);
        }
      }
    }
    return NoneType::object();
  }

  if (keys.isTuple()) {
    Tuple keys_tuple(&scope, *keys);
    Object dict_result(&scope, NoneType::object());
    for (word i = 0; i < keys_tuple.length(); ++i) {
      key = keys_tuple.at(i);
      hash_obj = Interpreter::hash(thread, key);
      if (hash_obj.isErrorException()) return *hash_obj;
      word hash = SmallInt::cast(*hash_obj).value();
      if (do_override == Override::kOverride) {
        value = Interpreter::callMethod2(thread, subscr_method, mapping, key);
        if (value.isError()) return *value;
        dict_result = dictAtPut(thread, dict, key, hash, value);
        if (dict_result.isErrorException()) return *dict_result;
      } else {
        included = dictIncludes(thread, dict, key, hash);
        if (included.isErrorException()) return *included;
        if (included == Bool::falseObj()) {
          value = Interpreter::callMethod2(thread, subscr_method, mapping, key);
          if (value.isError()) return *value;
          dict_result = dictAtPut(thread, dict, key, hash, value);
          if (dict_result.isErrorException()) return *dict_result;
        } else if (do_override == Override::kError) {
          return thread->raise(LayoutId::kKeyError, *key);
        }
      }
    }
    return NoneType::object();
  }

  // keys is probably an iterator
  Object iter_method(&scope,
                     Interpreter::lookupMethod(thread, keys, ID(__iter__)));
  if (iter_method.isError()) {
    return thread->raiseWithFmt(LayoutId::kTypeError, "keys() is not iterable");
  }

  Object iterator(&scope, Interpreter::callMethod1(thread, iter_method, keys));
  if (iterator.isError()) {
    return thread->raiseWithFmt(LayoutId::kTypeError, "keys() is not iterable");
  }
  Object next_method(&scope,
                     Interpreter::lookupMethod(thread, iterator, ID(__next__)));
  if (next_method.isError()) {
    return thread->raiseWithFmt(LayoutId::kTypeError, "keys() is not iterable");
  }
  Object dict_result(&scope, NoneType::object());
  for (;;) {
    key = Interpreter::callMethod1(thread, next_method, iterator);
    if (key.isError()) {
      if (thread->clearPendingStopIteration()) break;
      return *key;
    }
    hash_obj = Interpreter::hash(thread, key);
    if (hash_obj.isErrorException()) return *hash_obj;
    word hash = SmallInt::cast(*hash_obj).value();
    if (do_override == Override::kOverride) {
      value = Interpreter::callMethod2(thread, subscr_method, mapping, key);
      if (value.isError()) return *value;
      dict_result = dictAtPut(thread, dict, key, hash, value);
      if (dict_result.isErrorException()) return *dict_result;
    } else {
      included = dictIncludes(thread, dict, key, hash);
      if (included.isErrorException()) return *included;
      if (included == Bool::falseObj()) {
        value = Interpreter::callMethod2(thread, subscr_method, mapping, key);
        if (value.isError()) return *value;
        dict_result = dictAtPut(thread, dict, key, hash, value);
        if (dict_result.isErrorException()) return *dict_result;
      } else if (do_override == Override::kError) {
        return thread->raise(LayoutId::kKeyError, *key);
      }
    }
  }
  return NoneType::object();
}