Re: [PATCH] Core: fix build without libcrypt

2024-03-18 Thread Roman Arutyunyan
Hi Piotr,

On Wed, Feb 28, 2024 at 01:23:08AM +, Piotr Sikora via nginx-devel wrote:
> # HG changeset patch
> # User Piotr Sikora 
> # Date 1708977637 0
> #  Mon Feb 26 20:00:37 2024 +
> # Branch patch013
> # Node ID cdc173477ea99fd6c952a85e5cd11db66452076a
> # Parent  04e3155b3b9651fee708898aaf82ac35532806ee
> Core: fix build without libcrypt.
> 
> libcrypt is no longer part of glibc, so it might not be available.

Can you provide an example of a system where this fix is needed?

> Signed-off-by: Piotr Sikora 
> 
> diff -r 04e3155b3b96 -r cdc173477ea9 auto/unix
> --- a/auto/unix   Mon Feb 26 20:00:35 2024 +
> +++ b/auto/unix   Mon Feb 26 20:00:37 2024 +
> @@ -150,7 +150,7 @@
>  
>  
>  ngx_feature="crypt()"
> -ngx_feature_name=
> +ngx_feature_name="NGX_HAVE_CRYPT"
>  ngx_feature_run=no
>  ngx_feature_incs=
>  ngx_feature_path=
> @@ -162,7 +162,7 @@
>  if [ $ngx_found = no ]; then
>  
>  ngx_feature="crypt() in libcrypt"
> -ngx_feature_name=
> +ngx_feature_name="NGX_HAVE_CRYPT"
>  ngx_feature_run=no
>  ngx_feature_incs=
>  ngx_feature_path=
> diff -r 04e3155b3b96 -r cdc173477ea9 src/os/unix/ngx_linux_config.h
> --- a/src/os/unix/ngx_linux_config.h  Mon Feb 26 20:00:35 2024 +
> +++ b/src/os/unix/ngx_linux_config.h  Mon Feb 26 20:00:37 2024 +
> @@ -52,7 +52,6 @@
>  #include  /* memalign() */
>  #include  /* IOV_MAX */
>  #include 
> -#include 
>  #include /* uname() */
>  
>  #include 
> @@ -61,6 +60,11 @@
>  #include 
>  
>  
> +#if (NGX_HAVE_CRYPT_H)
> +#include 
> +#endif
> +
> +
>  #if (NGX_HAVE_POSIX_SEM)
>  #include 
>  #endif
> diff -r 04e3155b3b96 -r cdc173477ea9 src/os/unix/ngx_user.c
> --- a/src/os/unix/ngx_user.c  Mon Feb 26 20:00:35 2024 +
> +++ b/src/os/unix/ngx_user.c  Mon Feb 26 20:00:37 2024 +
> @@ -44,7 +44,7 @@
>  return NGX_ERROR;
>  }
>  
> -#else
> +#elif (NGX_HAVE_CRYPT)
>  
>  ngx_int_t
>  ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char 
> **encrypted)
> @@ -76,6 +76,14 @@
>  return NGX_ERROR;
>  }
>  
> +#else
> +
> +ngx_int_t
> +ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char 
> **encrypted)
> +{
> +return NGX_ERROR;
> +}
> +
>  #endif
>  
>  #endif /* NGX_CRYPT */
> ___
> nginx-devel mailing list
> nginx-devel@nginx.org
> https://mailman.nginx.org/mailman/listinfo/nginx-devel

--
Roman Arutyunyan
___
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel


Re: [PATCH] Core: fix conversion of IPv4-mapped IPv6 addresses

2024-03-18 Thread Sergey Kandaurov
On Fri, Mar 15, 2024 at 08:12:50PM +0400, Sergey Kandaurov wrote:
> 
> > On 28 Feb 2024, at 05:21, Piotr Sikora via nginx-devel 
> >  wrote:
> > 
> > # HG changeset patch
> > # User Piotr Sikora 
> > # Date 1708977626 0
> > #  Mon Feb 26 20:00:26 2024 +
> > # Branch patch007
> > # Node ID 5584232259d28489efba149f2f5ae730691ff0d4
> > # Parent  03e5549976765912818120e11f6b08410a2af6a9
> > Core: fix conversion of IPv4-mapped IPv6 addresses.
> > 
> > Found with UndefinedBehaviorSanitizer (shift).
> 
> Hello, thanks for the patch.
> 
> The "shift" remark doesn't describe a problem in details.
> 
> What actually happens is:
> - loading from "u_char", with integer promotion
> - left shift by 24 to most significant bit 31
> 
> The latter cannot be represented in (signed) integer in popular
> data type models: bit 31 gets to the integer type sign bit,
> and is caught by UndefinedBehaviorSanitizer, e.g.:
> | runtime error: left shift of 255 by 24 places cannot be represented in type 
> 'int'
> 
> The lvalue typically has an unsigned integer type of the same
> size (POSIX states in_addr_t is equivalent to uint32_t, which
> is of the same type as "unsigned int" in ILP32 and LP64).
> Known compilers, such as GCC and Clang, represent intermediate
> result used as a source for left shift in a wide register such that
> it doesn't get truncated when storing it in the left value.
> This makes objects don't change after adding an explicit cast,
> comparing md5sum over them matches.
> 
> Notably, this differs from left-shifting signed integer
> into the sign bit fixed in ad736705a744, where, as described
> in the change, the left value is of the larger type width.
> 
> Still, I won't object to apply something similar to ad736705a744
> in order to suppress such UndefinedBehaviorSanitizer reports.
> They look valid, although seemingly innocent.
> 
> > 
> > Signed-off-by: Piotr Sikora 
> > 
> > diff -r 03e554997676 -r 5584232259d2 src/core/ngx_inet.c
> > --- a/src/core/ngx_inet.c Mon Feb 26 20:00:23 2024 +
> > +++ b/src/core/ngx_inet.c Mon Feb 26 20:00:26 2024 +
> > @@ -507,10 +507,10 @@
> > 
> >p = inaddr6->s6_addr;
> > 
> > -inaddr = p[12] << 24;
> > -inaddr += p[13] << 16;
> > -inaddr += p[14] << 8;
> > -inaddr += p[15];
> > +inaddr = (in_addr_t) p[12] << 24;
> > +inaddr += (in_addr_t) p[13] << 16;
> > +inaddr += (in_addr_t) p[14] << 8;
> > +inaddr += (in_addr_t) p[15];
> > 
> > [...]

The patch.

# HG changeset patch
# User Sergey Kandaurov 
# Date 1710767670 -14400
#  Mon Mar 18 17:14:30 2024 +0400
# Node ID 543ca08e56fbd28f82552564d70edb74d0e628a7
# Parent  89bff782528a91ad123b63b624f798e6fd9c8e68
Core: fixed undefined behaviour with IPv4-mapped IPv6 addresses.

When converting to IPv4 addresses, with implicit integer promotion to hold
the intermediate value, the most significant bit appeared on the sign bit
after left shifting by 24 places, which is undefined behaviour.

In practice, a subsequent store into the left value resulted in the same
behaviour as if there would be a proper cast, at least on known compilers,
such as GCC and Clang, because in_addr_t has the same type width as the
intermediate after integer promotion (POSIX states in_addr_t is equivalent
to uint32_t, which is the same type as "unsigned int" in ILP32 and LP64 data
type models).  The reason is that they keep intermediate result in a wide
enough register such that its value is not truncated before storing it in
the left value.  This makes to produce object file that don't change after
adding an explicit cast; comparing md5sum over them matches.  Still, it is
useful to avoid undefined behaviour and to suppress such reports.

Found with UndefinedBehaviorSanitizer.

Spotted by: Piotr Sikora

diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -507,7 +507,7 @@ ngx_cidr_match(struct sockaddr *sa, ngx_
 
 p = inaddr6->s6_addr;
 
-inaddr = p[12] << 24;
+inaddr = (in_addr_t) p[12] << 24;
 inaddr += p[13] << 16;
 inaddr += p[14] << 8;
 inaddr += p[15];
diff --git a/src/http/modules/ngx_http_access_module.c 
b/src/http/modules/ngx_http_access_module.c
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -148,7 +148,7 @@ ngx_http_access_handler(ngx_http_request
 p = sin6->sin6_addr.s6_addr;
 
 if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
-addr = p[12] << 24;
+addr = (in_addr_t) p[12] << 24;
 addr += p[13] << 16;
 addr += p[14] << 8;
 addr += p[15];
diff --git a/src/http/modules/ngx_http_geo_module.c 
b/src/http/modules/ngx_http_geo_module.c
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -199,7 +199,7 @@ ngx_http_geo_cidr_variable

Re: [PATCH 1 of 2] SSL: add support for AWS-LC

2024-03-18 Thread Roman Arutyunyan
Hi Piotr,

On Wed, Feb 28, 2024 at 01:22:14AM +, Piotr Sikora via nginx-devel wrote:
> # HG changeset patch
> # User Piotr Sikora 
> # Date 1708977630 0
> #  Mon Feb 26 20:00:30 2024 +
> # Branch patch009
> # Node ID 5e923992006199748e79b08b1e65c4ef41f07495
> # Parent  3cde11b747c08c69889edc014a700317fe4d1d88
> SSL: add support for AWS-LC.
> 
> AWS-LC is a fork of BoringSSL with some performance improvements,
> useful features (OCSP and multiple certificates), and support for
> more platforms.
> 
> Signed-off-by: Piotr Sikora 
> 
> diff -r 3cde11b747c0 -r 5e9239920061 src/event/ngx_event_openssl.h
> --- a/src/event/ngx_event_openssl.h   Mon Feb 26 20:00:28 2024 +
> +++ b/src/event/ngx_event_openssl.h   Mon Feb 26 20:00:30 2024 +
> @@ -25,7 +25,7 @@
>  #endif
>  #include 
>  #if (NGX_QUIC)
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  #include 
>  #include 
>  #else
> diff -r 3cde11b747c0 -r 5e9239920061 src/event/quic/ngx_event_quic.c
> --- a/src/event/quic/ngx_event_quic.c Mon Feb 26 20:00:28 2024 +
> +++ b/src/event/quic/ngx_event_quic.c Mon Feb 26 20:00:30 2024 +
> @@ -962,7 +962,7 @@
>  return NGX_DECLINED;
>  }
>  
> -#if !defined (OPENSSL_IS_BORINGSSL)
> +#if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
>  /* OpenSSL provides read keys for an application level before it's ready 
> */
>  
>  if (pkt->level == ssl_encryption_application && !c->ssl->handshaked) {
> diff -r 3cde11b747c0 -r 5e9239920061 
> src/event/quic/ngx_event_quic_protection.c
> --- a/src/event/quic/ngx_event_quic_protection.c  Mon Feb 26 20:00:28 
> 2024 +
> +++ b/src/event/quic/ngx_event_quic_protection.c  Mon Feb 26 20:00:30 
> 2024 +
> @@ -30,7 +30,7 @@
>  
>  static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out,
>  u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
> -#ifndef OPENSSL_IS_BORINGSSL
> +#if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
>  static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
>  u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
>  #endif
> @@ -55,7 +55,7 @@
>  switch (id) {
>  
>  case TLS1_3_CK_AES_128_GCM_SHA256:
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  ciphers->c = EVP_aead_aes_128_gcm();
>  #else
>  ciphers->c = EVP_aes_128_gcm();
> @@ -66,7 +66,7 @@
>  break;
>  
>  case TLS1_3_CK_AES_256_GCM_SHA384:
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  ciphers->c = EVP_aead_aes_256_gcm();
>  #else
>  ciphers->c = EVP_aes_256_gcm();
> @@ -77,12 +77,12 @@
>  break;
>  
>  case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  ciphers->c = EVP_aead_chacha20_poly1305();
>  #else
>  ciphers->c = EVP_chacha20_poly1305();
>  #endif
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305();
>  #else
>  ciphers->hp = EVP_chacha20();
> @@ -91,7 +91,7 @@
>  len = 32;
>  break;
>  
> -#ifndef OPENSSL_IS_BORINGSSL
> +#if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
>  case TLS1_3_CK_AES_128_CCM_SHA256:
>  ciphers->c = EVP_aes_128_ccm();
>  ciphers->hp = EVP_aes_128_ctr();
> @@ -259,7 +259,7 @@
>  ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest,
>  const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len)
>  {
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  
>  if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)
>  == 0)
> @@ -321,7 +321,7 @@
>  const u_char *secret, size_t secret_len, const u_char *salt,
>  size_t salt_len)
>  {
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  
>  if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt,
>   salt_len)
> @@ -384,7 +384,7 @@
>  ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log)
>  {
>  
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  EVP_AEAD_CTX  *ctx;
>  
>  ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len,
> @@ -444,7 +444,7 @@
>  ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce,
>  ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
>  {
> -#ifdef OPENSSL_IS_BORINGSSL
> +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
>  if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce,
>s->iv.len, in->data, in->len, ad->data, ad->len)
>  !

Re: [PATCH] Core: fix build without libcrypt

2024-03-18 Thread Sergey Kandaurov

> On 28 Feb 2024, at 05:23, Piotr Sikora via nginx-devel 
>  wrote:
> 
> # HG changeset patch
> # User Piotr Sikora 
> # Date 1708977637 0
> #  Mon Feb 26 20:00:37 2024 +
> # Branch patch013
> # Node ID cdc173477ea99fd6c952a85e5cd11db66452076a
> # Parent  04e3155b3b9651fee708898aaf82ac35532806ee
> Core: fix build without libcrypt.
> 
> libcrypt is no longer part of glibc, so it might not be available.
> 
> Signed-off-by: Piotr Sikora 

In popular RPM- and Debian-based Linux distributions with glibc 2.38,
libcrypt.so is now indeed installed in a separate package.
However, it appears that this package is critical and provides
dependency either for systemd or libc, which cannot be removed.
So, I don't see so far, how nginx is affected in practice.

> 
> diff -r 04e3155b3b96 -r cdc173477ea9 auto/unix
> --- a/auto/unix Mon Feb 26 20:00:35 2024 +
> +++ b/auto/unix Mon Feb 26 20:00:37 2024 +
> @@ -150,7 +150,7 @@
> 
> 
> ngx_feature="crypt()"
> -ngx_feature_name=
> +ngx_feature_name="NGX_HAVE_CRYPT"
> ngx_feature_run=no
> ngx_feature_incs=
> ngx_feature_path=
> @@ -162,7 +162,7 @@
> if [ $ngx_found = no ]; then
> 
> ngx_feature="crypt() in libcrypt"
> -ngx_feature_name=
> +ngx_feature_name="NGX_HAVE_CRYPT"
> ngx_feature_run=no
> ngx_feature_incs=
> ngx_feature_path=
> diff -r 04e3155b3b96 -r cdc173477ea9 src/os/unix/ngx_linux_config.h
> --- a/src/os/unix/ngx_linux_config.h Mon Feb 26 20:00:35 2024 +
> +++ b/src/os/unix/ngx_linux_config.h Mon Feb 26 20:00:37 2024 +
> @@ -52,7 +52,6 @@
> #include  /* memalign() */
> #include  /* IOV_MAX */
> #include 
> -#include 
> #include /* uname() */
> 
> #include 
> @@ -61,6 +60,11 @@
> #include 
> 
> 
> +#if (NGX_HAVE_CRYPT_H)
> +#include 
> +#endif
> +
> +
> #if (NGX_HAVE_POSIX_SEM)
> #include 
> #endif
> diff -r 04e3155b3b96 -r cdc173477ea9 src/os/unix/ngx_user.c
> --- a/src/os/unix/ngx_user.c Mon Feb 26 20:00:35 2024 +
> +++ b/src/os/unix/ngx_user.c Mon Feb 26 20:00:37 2024 +
> @@ -44,7 +44,7 @@
> return NGX_ERROR;
> }
> 
> -#else
> +#elif (NGX_HAVE_CRYPT)
> 
> ngx_int_t
> ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char 
> **encrypted)
> @@ -76,6 +76,14 @@
> return NGX_ERROR;
> }
> 
> +#else
> +
> +ngx_int_t
> +ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char 
> **encrypted)
> +{
> +return NGX_ERROR;
> +}
> +
> #endif
> 
> #endif /* NGX_CRYPT */
> ___
> nginx-devel mailing list
> nginx-devel@nginx.org
> https://mailman.nginx.org/mailman/listinfo/nginx-devel

-- 
Sergey Kandaurov
___
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel


Re: [PATCH] Core: fix build without libcrypt

2024-03-18 Thread Илья Шипицин
пн, 18 мар. 2024 г. в 15:56, Sergey Kandaurov :

>
> > On 28 Feb 2024, at 05:23, Piotr Sikora via nginx-devel <
> nginx-devel@nginx.org> wrote:
> >
> > # HG changeset patch
> > # User Piotr Sikora 
> > # Date 1708977637 0
> > #  Mon Feb 26 20:00:37 2024 +
> > # Branch patch013
> > # Node ID cdc173477ea99fd6c952a85e5cd11db66452076a
> > # Parent  04e3155b3b9651fee708898aaf82ac35532806ee
> > Core: fix build without libcrypt.
> >
> > libcrypt is no longer part of glibc, so it might not be available.
> >
> > Signed-off-by: Piotr Sikora 
>
> In popular RPM- and Debian-based Linux distributions with glibc 2.38,
> libcrypt.so is now indeed installed in a separate package.
> However, it appears that this package is critical and provides
> dependency either for systemd or libc, which cannot be removed.
> So, I don't see so far, how nginx is affected in practice.
>

I guess docker images are meant.
usually you do not have systemd inside docker


>
> >
> > diff -r 04e3155b3b96 -r cdc173477ea9 auto/unix
> > --- a/auto/unix Mon Feb 26 20:00:35 2024 +
> > +++ b/auto/unix Mon Feb 26 20:00:37 2024 +
> > @@ -150,7 +150,7 @@
> >
> >
> > ngx_feature="crypt()"
> > -ngx_feature_name=
> > +ngx_feature_name="NGX_HAVE_CRYPT"
> > ngx_feature_run=no
> > ngx_feature_incs=
> > ngx_feature_path=
> > @@ -162,7 +162,7 @@
> > if [ $ngx_found = no ]; then
> >
> > ngx_feature="crypt() in libcrypt"
> > -ngx_feature_name=
> > +ngx_feature_name="NGX_HAVE_CRYPT"
> > ngx_feature_run=no
> > ngx_feature_incs=
> > ngx_feature_path=
> > diff -r 04e3155b3b96 -r cdc173477ea9 src/os/unix/ngx_linux_config.h
> > --- a/src/os/unix/ngx_linux_config.h Mon Feb 26 20:00:35 2024 +
> > +++ b/src/os/unix/ngx_linux_config.h Mon Feb 26 20:00:37 2024 +
> > @@ -52,7 +52,6 @@
> > #include  /* memalign() */
> > #include  /* IOV_MAX */
> > #include 
> > -#include 
> > #include /* uname() */
> >
> > #include 
> > @@ -61,6 +60,11 @@
> > #include 
> >
> >
> > +#if (NGX_HAVE_CRYPT_H)
> > +#include 
> > +#endif
> > +
> > +
> > #if (NGX_HAVE_POSIX_SEM)
> > #include 
> > #endif
> > diff -r 04e3155b3b96 -r cdc173477ea9 src/os/unix/ngx_user.c
> > --- a/src/os/unix/ngx_user.c Mon Feb 26 20:00:35 2024 +
> > +++ b/src/os/unix/ngx_user.c Mon Feb 26 20:00:37 2024 +
> > @@ -44,7 +44,7 @@
> > return NGX_ERROR;
> > }
> >
> > -#else
> > +#elif (NGX_HAVE_CRYPT)
> >
> > ngx_int_t
> > ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char
> **encrypted)
> > @@ -76,6 +76,14 @@
> > return NGX_ERROR;
> > }
> >
> > +#else
> > +
> > +ngx_int_t
> > +ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char
> **encrypted)
> > +{
> > +return NGX_ERROR;
> > +}
> > +
> > #endif
> >
> > #endif /* NGX_CRYPT */
> > ___
> > nginx-devel mailing list
> > nginx-devel@nginx.org
> > https://mailman.nginx.org/mailman/listinfo/nginx-devel
>
> --
> Sergey Kandaurov
> ___
> nginx-devel mailing list
> nginx-devel@nginx.org
> https://mailman.nginx.org/mailman/listinfo/nginx-devel
>
___
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel


[njs] Introduced njs_vm_global() to get global object.

2024-03-18 Thread Dmitry Volyntsev
details:   https://hg.nginx.org/njs/rev/00b89201fb71
branches:  
changeset: 2297:00b89201fb71
user:  Dmitry Volyntsev 
date:  Thu Mar 14 23:28:03 2024 -0700
description:
Introduced njs_vm_global() to get global object.

diffstat:

 src/njs.h|  1 +
 src/njs_vm.c |  8 
 2 files changed, 9 insertions(+), 0 deletions(-)

diffs (29 lines):

diff -r 8309b884e265 -r 00b89201fb71 src/njs.h
--- a/src/njs.h Tue Mar 05 11:43:49 2024 -0800
+++ b/src/njs.h Thu Mar 14 23:28:03 2024 -0700
@@ -368,6 +368,7 @@ NJS_EXPORT njs_int_t njs_vm_bind(njs_vm_
 njs_int_t njs_vm_bind_handler(njs_vm_t *vm, const njs_str_t *var_name,
 njs_prop_handler_t handler, uint16_t magic16, uint32_t magic32,
 njs_bool_t shared);
+NJS_EXPORT njs_int_t njs_vm_global(njs_vm_t *vm, njs_value_t *retval);
 NJS_EXPORT njs_int_t njs_vm_value(njs_vm_t *vm, const njs_str_t *path,
 njs_value_t *retval);
 NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t 
*name);
diff -r 8309b884e265 -r 00b89201fb71 src/njs_vm.c
--- a/src/njs_vm.c  Tue Mar 05 11:43:49 2024 -0800
+++ b/src/njs_vm.c  Thu Mar 14 23:28:03 2024 -0700
@@ -814,6 +814,14 @@ njs_vm_error3(njs_vm_t *vm, unsigned typ
 
 
 njs_int_t
+njs_vm_global(njs_vm_t *vm, njs_value_t *retval)
+{
+njs_value_assign(retval, &vm->global_value);
+return NJS_OK;
+}
+
+
+njs_int_t
 njs_vm_value(njs_vm_t *vm, const njs_str_t *path, njs_value_t *retval)
 {
 u_char   *start, *p, *end;
___
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel


[njs] Introduced njs_vm_prototype() to get a value's prototype.

2024-03-18 Thread Dmitry Volyntsev
details:   https://hg.nginx.org/njs/rev/6808a27d254a
branches:  
changeset: 2298:6808a27d254a
user:  Dmitry Volyntsev 
date:  Thu Mar 14 23:28:03 2024 -0700
description:
Introduced njs_vm_prototype() to get a value's prototype.

diffstat:

 src/njs.h|   2 ++
 src/njs_object.c |   2 +-
 src/njs_object.h |   2 ++
 src/njs_vm.c |  13 +
 4 files changed, 18 insertions(+), 1 deletions(-)

diffs (59 lines):

diff -r 00b89201fb71 -r 6808a27d254a src/njs.h
--- a/src/njs.h Thu Mar 14 23:28:03 2024 -0700
+++ b/src/njs.h Thu Mar 14 23:28:03 2024 -0700
@@ -373,6 +373,8 @@ NJS_EXPORT njs_int_t njs_vm_value(njs_vm
 njs_value_t *retval);
 NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t 
*name);
 NJS_EXPORT njs_bool_t njs_vm_constructor(njs_vm_t *vm);
+NJS_EXPORT njs_int_t njs_vm_prototype(njs_vm_t *vm, njs_value_t *value,
+njs_value_t *retval);
 
 NJS_EXPORT void njs_vm_throw(njs_vm_t *vm, const njs_value_t *value);
 NJS_EXPORT void njs_vm_error2(njs_vm_t *vm, unsigned error_type,
diff -r 00b89201fb71 -r 6808a27d254a src/njs_object.c
--- a/src/njs_object.c  Thu Mar 14 23:28:03 2024 -0700
+++ b/src/njs_object.c  Thu Mar 14 23:28:03 2024 -0700
@@ -1598,7 +1598,7 @@ njs_object_get_own_property(njs_vm_t *vm
 }
 
 
-static njs_int_t
+njs_int_t
 njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 njs_index_t unused, njs_value_t *retval)
 {
diff -r 00b89201fb71 -r 6808a27d254a src/njs_object.h
--- a/src/njs_object.h  Thu Mar 14 23:28:03 2024 -0700
+++ b/src/njs_object.h  Thu Mar 14 23:28:03 2024 -0700
@@ -106,6 +106,8 @@ njs_int_t njs_object_prop_define(njs_vm_
 njs_value_t *name, njs_value_t *value, unsigned flags, uint32_t hash);
 njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
 njs_value_t *value, njs_value_t *setval);
+njs_int_t njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args,
+njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
 const char *njs_prop_type_string(njs_object_prop_type_t type);
 njs_int_t njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init,
 const njs_object_prop_t *base, njs_value_t *value, njs_value_t *retval);
diff -r 00b89201fb71 -r 6808a27d254a src/njs_vm.c
--- a/src/njs_vm.c  Thu Mar 14 23:28:03 2024 -0700
+++ b/src/njs_vm.c  Thu Mar 14 23:28:03 2024 -0700
@@ -1201,6 +1201,19 @@ njs_vm_object_keys(njs_vm_t *vm, njs_val
 
 
 njs_int_t
+njs_vm_prototype(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval)
+{
+njs_value_t arguments[2];
+
+njs_set_undefined(&arguments[0]);
+njs_value_assign(&arguments[1], value);
+
+return njs_object_get_prototype_of(vm, njs_value_arg(&arguments), 2, 0,
+   retval);
+}
+
+
+njs_int_t
 njs_vm_array_alloc(njs_vm_t *vm, njs_value_t *retval, uint32_t spare)
 {
 njs_array_t  *array;
___
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel


[njs] Refactoring njs_value_own_enumerate() and friends.

2024-03-18 Thread Dmitry Volyntsev
details:   https://hg.nginx.org/njs/rev/b0e42db5ca84
branches:  
changeset: 2299:b0e42db5ca84
user:  Dmitry Volyntsev 
date:  Fri Mar 15 22:47:50 2024 -0700
description:
Refactoring njs_value_own_enumerate() and friends.

All three flag-like arguments are merged into a single flag argument.

diffstat:

 src/njs_array.c   |4 +-
 src/njs_builtin.c |2 +-
 src/njs_json.c|8 +-
 src/njs_object.c  |  154 +
 src/njs_object.h  |4 +-
 src/njs_value.c   |   20 ++
 src/njs_value.h   |   21 +++---
 src/njs_vm.c  |4 +-
 src/njs_vmcode.c  |6 +-
 9 files changed, 106 insertions(+), 117 deletions(-)

diffs (727 lines):

diff -r 6808a27d254a -r b0e42db5ca84 src/njs_array.c
--- a/src/njs_array.c   Thu Mar 14 23:28:03 2024 -0700
+++ b/src/njs_array.c   Fri Mar 15 22:47:50 2024 -0700
@@ -1868,8 +1868,8 @@ njs_array_keys(njs_vm_t *vm, njs_value_t
 {
 njs_array_t  *keys;
 
-keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS, NJS_ENUM_STRING,
-   all);
+keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS | NJS_ENUM_STRING
+   | (!all ? NJS_ENUM_ENUMERABLE_ONLY : 0));
 if (njs_slow_path(keys == NULL)) {
 return NULL;
 }
diff -r 6808a27d254a -r b0e42db5ca84 src/njs_builtin.c
--- a/src/njs_builtin.c Thu Mar 14 23:28:03 2024 -0700
+++ b/src/njs_builtin.c Fri Mar 15 22:47:50 2024 -0700
@@ -648,7 +648,7 @@ njs_object_completions(njs_vm_t *vm, njs
 object->type = NJS_OBJECT;
 }
 
-keys = njs_value_enumerate(vm, object, NJS_ENUM_KEYS, NJS_ENUM_STRING, 1);
+keys = njs_value_enumerate(vm, object, NJS_ENUM_KEYS | NJS_ENUM_STRING);
 if (njs_slow_path(keys == NULL)) {
 goto done;
 }
diff -r 6808a27d254a -r b0e42db5ca84 src/njs_json.c
--- a/src/njs_json.cThu Mar 14 23:28:03 2024 -0700
+++ b/src/njs_json.cFri Mar 15 22:47:50 2024 -0700
@@ -44,7 +44,7 @@ typedef struct {
 njs_value_treplacer;
 njs_str_t  space;
 u_char space_buf[16];
-njs_object_enum_type_t keys_type;
+uint32_t   keys_type;
 } njs_json_stringify_t;
 
 
@@ -998,8 +998,10 @@ njs_json_push_stringify_state(njs_json_s
 }
 
 } else {
-state->keys = njs_value_own_enumerate(stringify->vm, value, 
NJS_ENUM_KEYS,
-  stringify->keys_type, 0);
+state->keys = njs_value_own_enumerate(stringify->vm, value,
+  NJS_ENUM_KEYS
+  | stringify->keys_type
+  | NJS_ENUM_ENUMERABLE_ONLY);
 
 if (njs_slow_path(state->keys == NULL)) {
 return NULL;
diff -r 6808a27d254a -r b0e42db5ca84 src/njs_object.c
--- a/src/njs_object.c  Thu Mar 14 23:28:03 2024 -0700
+++ b/src/njs_object.c  Fri Mar 15 22:47:50 2024 -0700
@@ -18,17 +18,16 @@ static njs_int_t njs_object_hash_test(nj
 static njs_object_prop_t *njs_object_exist_in_proto(const njs_object_t *begin,
 const njs_object_t *end, njs_lvlhsh_query_t *lhq);
 static njs_int_t njs_object_enumerate_array(njs_vm_t *vm,
-const njs_array_t *array, njs_array_t *items, njs_object_enum_t kind);
+const njs_array_t *array, njs_array_t *items, uint32_t flags);
 static njs_int_t njs_object_enumerate_typed_array(njs_vm_t *vm,
-const njs_typed_array_t *array, njs_array_t *items, njs_object_enum_t 
kind);
+const njs_typed_array_t *array, njs_array_t *items, uint32_t flags);
 static njs_int_t njs_object_enumerate_string(njs_vm_t *vm,
-const njs_value_t *value, njs_array_t *items, njs_object_enum_t kind);
+const njs_value_t *value, njs_array_t *items, uint32_t flags);
 static njs_int_t njs_object_enumerate_object(njs_vm_t *vm,
-const njs_object_t *object, njs_array_t *items, njs_object_enum_t kind,
-njs_object_enum_type_t type, njs_bool_t all);
+const njs_object_t *object, njs_array_t *items, uint32_t flags);
 static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm,
 const njs_object_t *object, const njs_object_t *parent, njs_array_t *items,
-njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
+uint32_t flags);
 static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args,
 njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
 static njs_int_t njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object,
@@ -324,8 +323,8 @@ njs_object_keys(njs_vm_t *vm, njs_value_
 return NJS_ERROR;
 }
 
-keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
-   NJS_ENUM_STRING, 0);
+keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | NJS_ENUM_STRING
+   | NJS_ENUM_ENUMERABLE_ONLY);
 if (keys == NULL) {
 return NJS_ERROR;
 }
@@ -352,8 +351,8

[njs] Introduced njs_vm_value_enumerate() and njs_vm_value_own_enumerate().

2024-03-18 Thread Dmitry Volyntsev
details:   https://hg.nginx.org/njs/rev/1aa2bb15c966
branches:  
changeset: 2300:1aa2bb15c966
user:  Dmitry Volyntsev 
date:  Fri Mar 15 23:14:39 2024 -0700
description:
Introduced njs_vm_value_enumerate() and njs_vm_value_own_enumerate().

diffstat:

 src/njs.h   |  16 ++
 src/njs_value.h |  12 ---
 src/njs_vm.c|  88 +
 3 files changed, 104 insertions(+), 12 deletions(-)

diffs (153 lines):

diff -r b0e42db5ca84 -r 1aa2bb15c966 src/njs.h
--- a/src/njs.h Fri Mar 15 22:47:50 2024 -0700
+++ b/src/njs.h Fri Mar 15 23:14:39 2024 -0700
@@ -129,6 +129,18 @@ typedef enum {
 
 
 typedef enum {
+#define njs_object_enum_kind(flags) (flags & 7)
+NJS_ENUM_KEYS = 1,
+NJS_ENUM_VALUES = 2,
+NJS_ENUM_BOTH = 4,
+#define njs_object_enum(flags) (flags & (NJS_ENUM_STRING | NJS_ENUM_SYMBOL))
+NJS_ENUM_STRING = 8,
+NJS_ENUM_SYMBOL = 16,
+NJS_ENUM_ENUMERABLE_ONLY = 32,
+} njs_object_enum_t;
+
+
+typedef enum {
 /*
  * Extern property type.
  */
@@ -497,6 +509,10 @@ NJS_EXPORT njs_int_t njs_vm_object_alloc
 ...);
 NJS_EXPORT njs_value_t *njs_vm_object_keys(njs_vm_t *vm, njs_value_t *value,
 njs_value_t *retval);
+NJS_EXPORT njs_value_t *njs_vm_value_enumerate(njs_vm_t *vm, njs_value_t 
*value,
+uint32_t flags, njs_value_t *retval);
+NJS_EXPORT njs_value_t *njs_vm_value_own_enumerate(njs_vm_t *vm,
+njs_value_t *value, uint32_t flags, njs_value_t *retval);
 NJS_EXPORT njs_value_t *njs_vm_object_prop(njs_vm_t *vm,
 njs_value_t *value, const njs_str_t *key, njs_opaque_value_t *retval);
 NJS_EXPORT njs_int_t njs_vm_object_prop_set(njs_vm_t *vm, njs_value_t *value,
diff -r b0e42db5ca84 -r 1aa2bb15c966 src/njs_value.h
--- a/src/njs_value.h   Fri Mar 15 22:47:50 2024 -0700
+++ b/src/njs_value.h   Fri Mar 15 23:14:39 2024 -0700
@@ -313,18 +313,6 @@ struct njs_object_type_init_s {
 
 
 typedef enum {
-#define njs_object_enum_kind(flags) (flags & 7)
-NJS_ENUM_KEYS = 1,
-NJS_ENUM_VALUES = 2,
-NJS_ENUM_BOTH = 4,
-#define njs_object_enum(flags) (flags & (NJS_ENUM_STRING | NJS_ENUM_SYMBOL))
-NJS_ENUM_STRING = 8,
-NJS_ENUM_SYMBOL = 16,
-NJS_ENUM_ENUMERABLE_ONLY = 32,
-} njs_object_enum_t;
-
-
-typedef enum {
 NJS_PROPERTY = 0,
 NJS_ACCESSOR,
 NJS_PROPERTY_REF,
diff -r b0e42db5ca84 -r 1aa2bb15c966 src/njs_vm.c
--- a/src/njs_vm.c  Fri Mar 15 22:47:50 2024 -0700
+++ b/src/njs_vm.c  Fri Mar 15 23:14:39 2024 -0700
@@ -1114,6 +1114,94 @@ njs_vm_exception_string(njs_vm_t *vm, nj
 }
 
 
+njs_value_t *
+njs_vm_value_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags,
+njs_value_t *retval)
+{
+ssize_t  length;
+njs_int_tret;
+njs_value_t  *val;
+njs_array_t  *keys;
+njs_rbtree_t *variables;
+njs_rbtree_node_t*rb_node;
+njs_variable_node_t  *node;
+const njs_lexer_entry_t  *lex_entry;
+
+static const njs_str_t  njs_this_str = njs_str("this");
+
+keys = njs_value_enumerate(vm, value, flags);
+if (njs_slow_path(keys == NULL)) {
+return NULL;
+}
+
+if (!njs_values_same(value, &vm->global_value)
+|| vm->global_scope == NULL)
+{
+goto done;
+}
+
+/* TODO: workaround for values in global object. */
+
+variables = &vm->global_scope->variables;
+rb_node = njs_rbtree_min(variables);
+
+while (njs_rbtree_is_there_successor(variables, rb_node)) {
+node = (njs_variable_node_t *) rb_node;
+
+lex_entry = njs_lexer_entry(node->variable->unique_id);
+if (njs_slow_path(lex_entry == NULL)) {
+return NULL;
+}
+
+if (njs_strstr_eq(&lex_entry->name, &njs_this_str)) {
+rb_node = njs_rbtree_node_successor(variables, rb_node);
+continue;
+}
+
+length = njs_utf8_length(lex_entry->name.start, 
lex_entry->name.length);
+if (njs_slow_path(length < 0)) {
+return NULL;
+}
+
+val = njs_array_push(vm, keys);
+if (njs_slow_path(value == NULL)) {
+return NULL;
+}
+
+ret = njs_string_new(vm, val, lex_entry->name.start,
+ lex_entry->name.length, length);
+if (njs_slow_path(ret != NJS_OK)) {
+return NULL;
+}
+
+rb_node = njs_rbtree_node_successor(variables, rb_node);
+}
+
+done:
+
+ njs_set_array(retval, keys);
+
+ return retval;
+}
+
+
+njs_value_t *
+njs_vm_value_own_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags,
+njs_value_t *retval)
+{
+njs_array_t  *keys;
+
+keys = njs_value_own_enumerate(vm, value, flags);
+if (njs_slow_path(keys == NULL)) {
+return NULL;
+}
+
+njs_set_array(retval, keys);
+
+return retval;
+}
+
+
 njs_int_t
 njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...)
 {
___

[njs] Shell: completions are refactored and moved out of the core.

2024-03-18 Thread Dmitry Volyntsev
details:   https://hg.nginx.org/njs/rev/fe94552843d7
branches:  
changeset: 2301:fe94552843d7
user:  Dmitry Volyntsev 
date:  Mon Mar 18 22:15:48 2024 -0700
description:
Shell: completions are refactored and moved out of the core.

diffstat:

 external/njs_shell.c|  275 +++-
 src/njs.h   |2 -
 src/njs_builtin.c   |  294 +---
 test/shell_test_njs.exp |   26 +--
 4 files changed, 201 insertions(+), 396 deletions(-)

diffs (737 lines):

diff -r 1aa2bb15c966 -r fe94552843d7 external/njs_shell.c
--- a/external/njs_shell.c  Fri Mar 15 23:14:39 2024 -0700
+++ b/external/njs_shell.c  Mon Mar 18 22:15:48 2024 -0700
@@ -83,15 +83,7 @@ typedef enum {
 
 typedef struct {
 size_t  index;
-size_t  length;
-njs_arr_t   *completions;
 njs_arr_t   *suffix_completions;
-njs_rbtree_node_t   *node;
-
-enum {
-   NJS_COMPLETION_SUFFIX = 0,
-   NJS_COMPLETION_GLOBAL
-}   phase;
 } njs_completion_t;
 
 
@@ -157,7 +149,6 @@ struct njs_engine_s {
 njs_vm_t*vm;
 
 njs_opaque_value_t  value;
-njs_completion_tcompletion;
 }   njs;
 #if (NJS_HAVE_QUICKJS)
 struct {
@@ -174,9 +165,11 @@ struct njs_engine_s {
 njs_int_t (*process_events)(njs_engine_t *engine);
 njs_int_t (*destroy)(njs_engine_t *engine);
 njs_int_t (*output)(njs_engine_t *engine, njs_int_t ret);
+njs_arr_t*(*complete)(njs_engine_t *engine, njs_str_t *ex);
 
 unsignedtype;
 njs_mp_t*pool;
+njs_completion_tcompletion;
 };
 
 typedef struct {
@@ -1361,11 +1354,6 @@ njs_engine_njs_init(njs_engine_t *engine
 return NJS_ERROR;
 }
 
-engine->u.njs.completion.completions = njs_vm_completions(vm, NULL);
-if (engine->u.njs.completion.completions == NULL) {
-return NJS_ERROR;
-}
-
 if (opts->unhandled_rejection) {
 njs_vm_set_rejection_tracker(vm, njs_rejection_tracker,
  njs_vm_external_ptr(vm));
@@ -1455,6 +1443,179 @@ njs_engine_njs_output(njs_engine_t *engi
 }
 
 
+static njs_arr_t *
+njs_object_completions(njs_vm_t *vm, njs_value_t *object, njs_str_t 
*expression)
+{
+u_char  *prefix;
+size_t  len, prefix_len;
+int64_t k, n, length;
+njs_int_t   ret;
+njs_arr_t   *array;
+njs_str_t   *completion, key;
+njs_value_t *keys;
+njs_opaque_value_t  *start, retval, prototype;
+
+prefix = expression->start + expression->length;
+
+while (prefix > expression->start && *prefix != '.') {
+prefix--;
+}
+
+if (prefix != expression->start) {
+prefix++;
+}
+
+prefix_len = prefix - expression->start;
+len = expression->length - prefix_len;
+
+array = njs_arr_create(njs_vm_memory_pool(vm), 8, sizeof(njs_str_t));
+if (njs_slow_path(array == NULL)) {
+goto fail;
+}
+
+while (!njs_value_is_null(object)) {
+keys = njs_vm_value_enumerate(vm, object, NJS_ENUM_KEYS
+  | NJS_ENUM_STRING,
+  njs_value_arg(&retval));
+if (njs_slow_path(keys == NULL)) {
+goto fail;
+}
+
+(void) njs_vm_array_length(vm, keys, &length);
+
+start = (njs_opaque_value_t *) njs_vm_array_start(vm, keys);
+if (start == NULL) {
+goto fail;
+}
+
+
+for (n = 0; n < length; n++) {
+ret = njs_vm_value_to_string(vm, &key, njs_value_arg(start));
+if (njs_slow_path(ret != NJS_OK)) {
+goto fail;
+}
+
+start++;
+
+if (len > key.length || njs_strncmp(key.start, prefix, len) != 0) {
+continue;
+}
+
+for (k = 0; k < array->items; k++) {
+completion = njs_arr_item(array, k);
+
+if ((completion->length - prefix_len - 1) == key.length
+&& njs_strncmp(&completion->start[prefix_len],
+   key.start, key.length)
+   == 0)
+{
+break;
+}
+}
+
+if (k != array->items) {
+continue;
+}
+
+completion = njs_arr_add(array);
+if (njs_slow_path(completion == NULL)) {
+goto fail;
+}
+
+completion->length = prefix_len + key.length + 1;
+completion->start = njs_mp_alloc(njs_vm_memory_pool(vm),
+ completion->length);
+if (njs_slow_path(completion->start 

[njs] Shell: added completions for QuickJS engine.

2024-03-18 Thread Dmitry Volyntsev
details:   https://hg.nginx.org/njs/rev/85313a068147
branches:  
changeset: 2302:85313a068147
user:  Dmitry Volyntsev 
date:  Mon Mar 18 22:24:57 2024 -0700
description:
Shell: added completions for QuickJS engine.

diffstat:

 external/njs_shell.c|  202 +++-
 test/shell_test.exp |  147 ++
 test/shell_test_njs.exp |  148 ---
 3 files changed, 345 insertions(+), 152 deletions(-)

diffs (541 lines):

diff -r fe94552843d7 -r 85313a068147 external/njs_shell.c
--- a/external/njs_shell.c  Mon Mar 18 22:15:48 2024 -0700
+++ b/external/njs_shell.c  Mon Mar 18 22:24:57 2024 -0700
@@ -3106,6 +3106,203 @@ njs_engine_qjs_output(njs_engine_t *engi
 return NJS_OK;
 }
 
+
+static njs_arr_t *
+njs_qjs_object_completions(njs_engine_t *engine, JSContext *ctx,
+JSValueConst object, njs_str_t *expression)
+{
+u_char  *prefix;
+size_t  len, prefix_len;
+JSValue prototype;
+uint32_tk, n, length;
+njs_int_t   ret;
+njs_arr_t   *array;
+njs_str_t   *completion, key;
+JSPropertyEnum  *ptab;
+
+prefix = expression->start + expression->length;
+
+while (prefix > expression->start && *prefix != '.') {
+prefix--;
+}
+
+if (prefix != expression->start) {
+prefix++;
+}
+
+ptab = NULL;
+key.start = NULL;
+prefix_len = prefix - expression->start;
+len = expression->length - prefix_len;
+
+array = njs_arr_create(engine->pool, 8, sizeof(njs_str_t));
+if (njs_slow_path(array == NULL)) {
+goto fail;
+}
+
+while (!JS_IsNull(object)) {
+ret = JS_GetOwnPropertyNames(ctx, &ptab, &length, object,
+ JS_GPN_STRING_MASK);
+if (ret < 0) {
+goto fail;
+}
+
+for (n = 0; n < length; n++) {
+key.start = (u_char *) JS_AtomToCString(ctx, ptab[n].atom);
+JS_FreeAtom(ctx, ptab[n].atom);
+if (njs_slow_path(key.start == NULL)) {
+goto fail;
+}
+
+key.length = njs_strlen(key.start);
+
+if (len > key.length || njs_strncmp(key.start, prefix, len) != 0) {
+goto next;
+}
+
+for (k = 0; k < array->items; k++) {
+completion = njs_arr_item(array, k);
+
+if ((completion->length - prefix_len - 1) == key.length
+&& njs_strncmp(&completion->start[prefix_len],
+   key.start, key.length)
+   == 0)
+{
+goto next;
+}
+}
+
+completion = njs_arr_add(array);
+if (njs_slow_path(completion == NULL)) {
+goto fail;
+}
+
+completion->length = prefix_len + key.length + 1;
+completion->start = njs_mp_alloc(engine->pool, completion->length);
+if (njs_slow_path(completion->start == NULL)) {
+goto fail;
+}
+
+njs_sprintf(completion->start,
+completion->start + completion->length,
+"%*s%V%Z", prefix_len, expression->start, &key);
+
+next:
+
+JS_FreeCString(ctx, (const char *) key.start);
+}
+
+js_free_rt(JS_GetRuntime(ctx), ptab);
+
+prototype = JS_GetPrototype(ctx, object);
+if (JS_IsException(prototype)) {
+goto fail;
+}
+
+JS_FreeValue(ctx, object);
+object = prototype;
+}
+
+return array;
+
+fail:
+
+if (array != NULL) {
+njs_arr_destroy(array);
+}
+
+if (key.start != NULL) {
+JS_FreeCString(ctx, (const char *) key.start);
+}
+
+if (ptab != NULL) {
+js_free_rt(JS_GetRuntime(ctx), ptab);
+}
+
+JS_FreeValue(ctx, object);
+
+return NULL;
+}
+
+
+static njs_arr_t *
+njs_engine_qjs_complete(njs_engine_t *engine, njs_str_t *expression)
+{
+u_char  *p, *start, *end;
+JSAtom  key;
+JSValue value, retval;
+njs_arr_t   *arr;
+JSContext   *ctx;
+njs_bool_t  global;
+
+ctx = engine->u.qjs.ctx;
+
+p = expression->start;
+end = p + expression->length;
+
+global = 1;
+value = JS_GetGlobalObject(ctx);
+
+while (p < end && *p != '.') { p++; }
+
+if (p == end) {
+goto done;
+}
+
+p = expression->start;
+
+for ( ;; ) {
+
+start = (*p == '.' && p < end) ? ++p: p;
+
+if (p == end) {
+break;
+}
+
+while (p < end && *p != '.') { p++; }
+
+key = JS_NewAtomLen(ctx, (char *) start, p - start);
+if (key == JS_ATOM_NULL) {
+goto fail;
+}
+
+retval = JS_GetProperty(ctx, value, key);
+
+JS_FreeAtom(ctx, key);
+
+if (JS_IsUndefined(retval)) {
+if (glob