njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
    njs_value_t *key)
{
    double          num;
    uint32_t        index;
    njs_int_t       ret;
    njs_object_t    *obj;
    njs_value_t     prop;
    njs_function_t  *function;

    if (njs_slow_path(!njs_is_primitive(key))) {
        ret = njs_value_to_string(vm, &prop, key);
        if (ret != NJS_OK) {
            return ret;
        }

        key = &prop;
    }

    switch (value->type) {

    case NJS_BOOLEAN:
    case NJS_NUMBER:
    case NJS_SYMBOL:
        index = njs_primitive_prototype_index(value->type);
        obj = &vm->prototypes[index].object;
        break;

    case NJS_STRING:
        if (njs_fast_path(!njs_is_null_or_undefined_or_boolean(key))) {
            num = njs_key_to_index(key);
            if (njs_fast_path(njs_key_is_integer_index(num, key))) {
                return njs_string_property_query(vm, pq, value, num);
            }
        }

        obj = &vm->string_object;
        break;

    case NJS_OBJECT:
    case NJS_ARRAY:
    case NJS_ARRAY_BUFFER:
    case NJS_DATA_VIEW:
    case NJS_TYPED_ARRAY:
    case NJS_REGEXP:
    case NJS_DATE:
    case NJS_PROMISE:
    case NJS_OBJECT_VALUE:
        obj = njs_object(value);
        break;

    case NJS_FUNCTION:
        function = njs_function_value_copy(vm, value);
        if (njs_slow_path(function == NULL)) {
            return NJS_ERROR;
        }

        obj = &function->object;
        break;

    case NJS_UNDEFINED:
    case NJS_NULL:
    default:
        ret = njs_primitive_value_to_string(vm, &pq->key, key);

        if (njs_fast_path(ret == NJS_OK)) {
            njs_string_get(&pq->key, &pq->lhq.key);
            njs_type_error(vm, "cannot get property \"%V\" of undefined",
                           &pq->lhq.key);
            return NJS_ERROR;
        }

        njs_type_error(vm, "cannot get property \"unknown\" of undefined");

        return NJS_ERROR;
    }

    ret = njs_primitive_value_to_key(vm, &pq->key, key);

    if (njs_fast_path(ret == NJS_OK)) {

        if (njs_is_symbol(key)) {
            pq->lhq.key_hash = njs_symbol_key(key);
            pq->lhq.key.start = NULL;

        } else {
            njs_string_get(&pq->key, &pq->lhq.key);
            pq->lhq.key_hash = njs_djb_hash(pq->lhq.key.start,
                                            pq->lhq.key.length);
        }

        ret = njs_object_property_query(vm, pq, obj, key);

        if (njs_slow_path(ret == NJS_DECLINED && obj->slots != NULL)) {
            return njs_external_property_query(vm, pq, value);
        }
    }

    return ret;
}
njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
    njs_value_t *key)
{
    double          num;
    uint32_t        index;
    njs_int_t       ret;
    njs_object_t    *obj;
    njs_value_t     prop;
    njs_function_t  *function;

    if (njs_slow_path(!njs_is_primitive(key))) {
        ret = njs_value_to_string(vm, &prop, key);
        if (ret != NJS_OK) {
            return ret;
        }

        key = &prop;
    }

    switch (value->type) {

    case NJS_BOOLEAN:
    case NJS_NUMBER:
    case NJS_SYMBOL:
        index = njs_primitive_prototype_index(value->type);
        obj = &vm->prototypes[index].object;
        break;

    case NJS_STRING:
        if (njs_fast_path(!njs_is_null_or_undefined_or_boolean(key))) {
            num = njs_key_to_index(key);
            if (njs_fast_path(njs_key_is_integer_index(num, key))) {
                return njs_string_property_query(vm, pq, value, num);
            }
        }

        obj = &vm->string_object;
        break;

    case NJS_OBJECT:
    case NJS_ARRAY:
    case NJS_ARRAY_BUFFER:
    case NJS_DATA_VIEW:
    case NJS_TYPED_ARRAY:
    case NJS_REGEXP:
    case NJS_DATE:
    case NJS_PROMISE:
    case NJS_OBJECT_VALUE:
        obj = njs_object(value);
        break;

    case NJS_FUNCTION:
        function = njs_function_value_copy(vm, value);
        if (njs_slow_path(function == NULL)) {
            return NJS_ERROR;
        }

        obj = &function->object;
        break;

    case NJS_UNDEFINED:
    case NJS_NULL:
    default:
        ret = njs_primitive_value_to_string(vm, &pq->key, key);

        if (njs_fast_path(ret == NJS_OK)) {
            njs_string_get(&pq->key, &pq->lhq.key);
            njs_type_error(vm, "cannot get property \"%V\" of undefined",
                           &pq->lhq.key);
            return NJS_ERROR;
        }

        njs_type_error(vm, "cannot get property \"unknown\" of undefined");

        return NJS_ERROR;
    }

    ret = njs_primitive_value_to_key(vm, &pq->key, key);

    if (njs_fast_path(ret == NJS_OK)) {

        if (njs_is_symbol(key)) {
            pq->lhq.key_hash = njs_symbol_key(key);
            pq->lhq.key.start = NULL;

        } else {
            njs_string_get(&pq->key, &pq->lhq.key);
            pq->lhq.key_hash = njs_djb_hash(pq->lhq.key.start,
                                            pq->lhq.key.length);
        }

        ret = njs_object_property_query(vm, pq, obj, key);

        if (njs_slow_path(ret == NJS_DECLINED && obj->slots != NULL)) {
            return njs_external_property_query(vm, pq, value);
        }
    }

    return ret;
}