Curl_cookie_add(struct Curl_easy *data,
                /*
                 * The 'data' pointer here may be NULL at times, and thus
                 * must only be used very carefully for things that can deal
                 * with data being NULL. Such as infof() and similar
                 */
                struct CookieInfo *c,
                bool httpheader, /* TRUE if HTTP header-style line */
                bool noexpire, /* if TRUE, skip remove_expired() */
                char *lineptr,   /* first character of the line */
                const char *domain, /* default domain */
                const char *path,   /* full path used when this cookie is set,
                                       used to get default path for the cookie
                                       unless set */
                bool secure)  /* TRUE if connection is over secure origin */
{
  struct Cookie *clist;
  struct Cookie *co;
  struct Cookie *lastc = NULL;
  struct Cookie *replace_co = NULL;
  struct Cookie *replace_clist = NULL;
  time_t now = time(NULL);
  bool replace_old = FALSE;
  bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
  size_t myhash;

#ifdef CURL_DISABLE_VERBOSE_STRINGS
  (void)data;
#endif

  DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
  if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
    return NULL;

  /* First, alloc and init a new struct for it */
  co = calloc(1, sizeof(struct Cookie));
  if(!co)
    return NULL; /* bail out if we're this low on memory */

  if(httpheader) {
    /* This line was read off a HTTP-header */
    char name[MAX_NAME];
    char what[MAX_NAME];
    const char *ptr;
    const char *semiptr;

    size_t linelength = strlen(lineptr);
    if(linelength > MAX_COOKIE_LINE) {
      /* discard overly long lines at once */
      free(co);
      return NULL;
    }

    semiptr = strchr(lineptr, ';'); /* first, find a semicolon */

    while(*lineptr && ISBLANK(*lineptr))
      lineptr++;

    ptr = lineptr;
    do {
      /* we have a <what>=<this> pair or a stand-alone word here */
      name[0] = what[0] = 0; /* init the buffers */
      if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n=] =%"
                     MAX_NAME_TXT "[^;\r\n]",
                     name, what)) {
        /*
         * Use strstore() below to properly deal with received cookie
         * headers that have the same string property set more than once,
         * and then we use the last one.
         */
        const char *whatptr;
        bool done = FALSE;
        bool sep;
        size_t len = strlen(what);
        size_t nlen = strlen(name);
        const char *endofn = &ptr[ nlen ];

        /*
         * Check for too long individual name or contents, or too long
         * combination of name + contents. Chrome and Firefox support 4095 or
         * 4096 bytes combo
         */
        if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) ||
           ((nlen + len) > MAX_NAME)) {
          freecookie(co);
          infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
                nlen, len);
          return NULL;
        }

        /* name ends with a '=' ? */
        sep = (*endofn == '=')?TRUE:FALSE;

        if(nlen) {
          endofn--; /* move to the last character */
          if(ISBLANK(*endofn)) {
            /* skip trailing spaces in name */
            while(*endofn && ISBLANK(*endofn) && nlen) {
              endofn--;
              nlen--;
            }
            name[nlen] = 0; /* new end of name */
          }
        }

        /* Strip off trailing whitespace from the 'what' */
        while(len && ISBLANK(what[len-1])) {
          what[len-1] = 0;
          len--;
        }

        /* Skip leading whitespace from the 'what' */
        whatptr = what;
        while(*whatptr && ISBLANK(*whatptr))
          whatptr++;

        /*
         * Check if we have a reserved prefix set before anything else, as we
         * otherwise have to test for the prefix in both the cookie name and
         * "the rest". Prefixes must start with '__' and end with a '-', so
         * only test for names where that can possibly be true.
         */
        if(nlen > 3 && name[0] == '_' && name[1] == '_') {
          if(!strncmp("__Secure-", name, 9))
            co->prefix |= COOKIE_PREFIX__SECURE;
          else if(!strncmp("__Host-", name, 7))
            co->prefix |= COOKIE_PREFIX__HOST;
        }

        if(!co->name) {
          /* The very first name/value pair is the actual cookie name */
          if(!sep) {
            /* Bad name/value pair. */
            badcookie = TRUE;
            break;
          }
          co->name = strdup(name);
          co->value = strdup(whatptr);
          done = TRUE;
          if(!co->name || !co->value) {
            badcookie = TRUE;
            break;
          }
          if(invalid_octets(whatptr) || invalid_octets(name)) {
            infof(data, "invalid octets in name/value, cookie dropped");
            badcookie = TRUE;
            break;
          }
        }
        else if(!len) {
          /*
           * this was a "<name>=" with no content, and we must allow
           * 'secure' and 'httponly' specified this weirdly
           */
          done = TRUE;
          /*
           * secure cookies are only allowed to be set when the connection is
           * using a secure protocol, or when the cookie is being set by
           * reading from file
           */
          if(strcasecompare("secure", name)) {
            if(secure || !c->running) {
              co->secure = TRUE;
            }
            else {
              badcookie = TRUE;
              break;
            }
          }
          else if(strcasecompare("httponly", name))
            co->httponly = TRUE;
          else if(sep)
            /* there was a '=' so we're not done parsing this field */
            done = FALSE;
        }
        if(done)
          ;
        else if(strcasecompare("path", name)) {
          strstore(&co->path, whatptr);
          if(!co->path) {
            badcookie = TRUE; /* out of memory bad */
            break;
          }
          free(co->spath); /* if this is set again */
          co->spath = sanitize_cookie_path(co->path);
          if(!co->spath) {
            badcookie = TRUE; /* out of memory bad */
            break;
          }
        }
        else if(strcasecompare("domain", name) && whatptr[0]) {
          bool is_ip;

          /*
           * Now, we make sure that our host is within the given domain, or
           * the given domain is not valid and thus cannot be set.
           */

          if('.' == whatptr[0])
            whatptr++; /* ignore preceding dot */

#ifndef USE_LIBPSL
          /*
           * Without PSL we don't know when the incoming cookie is set on a
           * TLD or otherwise "protected" suffix. To reduce risk, we require a
           * dot OR the exact host name being "localhost".
           */
          if(bad_domain(whatptr))
            domain = ":";
#endif

          is_ip = Curl_host_is_ipnum(domain ? domain : whatptr);

          if(!domain
             || (is_ip && !strcmp(whatptr, domain))
             || (!is_ip && tailmatch(whatptr, domain))) {
            strstore(&co->domain, whatptr);
            if(!co->domain) {
              badcookie = TRUE;
              break;
            }
            if(!is_ip)
              co->tailmatch = TRUE; /* we always do that if the domain name was
                                       given */
          }
          else {
            /*
             * We did not get a tailmatch and then the attempted set domain is
             * not a domain to which the current host belongs. Mark as bad.
             */
            badcookie = TRUE;
            infof(data, "skipped cookie with bad tailmatch domain: %s",
                  whatptr);
          }
        }
        else if(strcasecompare("version", name)) {
          strstore(&co->version, whatptr);
          if(!co->version) {
            badcookie = TRUE;
            break;
          }
        }
        else if(strcasecompare("max-age", name)) {
          /*
           * Defined in RFC2109:
           *
           * Optional.  The Max-Age attribute defines the lifetime of the
           * cookie, in seconds.  The delta-seconds value is a decimal non-
           * negative integer.  After delta-seconds seconds elapse, the
           * client should discard the cookie.  A value of zero means the
           * cookie should be discarded immediately.
           */
          strstore(&co->maxage, whatptr);
          if(!co->maxage) {
            badcookie = TRUE;
            break;
          }
        }
        else if(strcasecompare("expires", name)) {
          strstore(&co->expirestr, whatptr);
          if(!co->expirestr) {
            badcookie = TRUE;
            break;
          }
        }

        /*
         * Else, this is the second (or more) name we don't know about!
         */
      }
      else {
        /* this is an "illegal" <what>=<this> pair */
      }

      if(!semiptr || !*semiptr) {
        /* we already know there are no more cookies */
        semiptr = NULL;
        continue;
      }

      ptr = semiptr + 1;
      while(*ptr && ISBLANK(*ptr))
        ptr++;
      semiptr = strchr(ptr, ';'); /* now, find the next semicolon */

      if(!semiptr && *ptr)
        /*
         * There are no more semicolons, but there's a final name=value pair
         * coming up
         */
        semiptr = strchr(ptr, '\0');
    } while(semiptr);

    if(co->maxage) {
      CURLofft offt;
      offt = curlx_strtoofft((*co->maxage == '\"')?
                             &co->maxage[1]:&co->maxage[0], NULL, 10,
                             &co->expires);
      if(offt == CURL_OFFT_FLOW)
        /* overflow, used max value */
        co->expires = CURL_OFF_T_MAX;
      else if(!offt) {
        if(!co->expires)
          /* already expired */
          co->expires = 1;
        else if(CURL_OFF_T_MAX - now < co->expires)
          /* would overflow */
          co->expires = CURL_OFF_T_MAX;
        else
          co->expires += now;
      }
    }
    else if(co->expirestr) {
      /*
       * Note that if the date couldn't get parsed for whatever reason, the
       * cookie will be treated as a session cookie
       */
      co->expires = Curl_getdate_capped(co->expirestr);

      /*
       * Session cookies have expires set to 0 so if we get that back from the
       * date parser let's add a second to make it a non-session cookie
       */
      if(co->expires == 0)
        co->expires = 1;
      else if(co->expires < 0)
        co->expires = 0;
    }

    if(!badcookie && !co->domain) {
      if(domain) {
        /* no domain was given in the header line, set the default */
        co->domain = strdup(domain);
        if(!co->domain)
          badcookie = TRUE;
      }
    }

    if(!badcookie && !co->path && path) {
      /*
       * No path was given in the header line, set the default.  Note that the
       * passed-in path to this function MAY have a '?' and following part that
       * MUST NOT be stored as part of the path.
       */
      char *queryp = strchr(path, '?');

      /*
       * queryp is where the interesting part of the path ends, so now we
       * want to the find the last
       */
      char *endslash;
      if(!queryp)
        endslash = strrchr(path, '/');
      else
        endslash = memrchr(path, '/', (queryp - path));
      if(endslash) {
        size_t pathlen = (endslash-path + 1); /* include end slash */
        co->path = malloc(pathlen + 1); /* one extra for the zero byte */
        if(co->path) {
          memcpy(co->path, path, pathlen);
          co->path[pathlen] = 0; /* null-terminate */
          co->spath = sanitize_cookie_path(co->path);
          if(!co->spath)
            badcookie = TRUE; /* out of memory bad */
        }
        else
          badcookie = TRUE;
      }
    }

    /*
     * If we didn't get a cookie name, or a bad one, the this is an illegal
     * line so bail out.
     */
    if(badcookie || !co->name) {
      freecookie(co);
      return NULL;
    }
    data->req.setcookies++;
  }
  else {
    /*
     * This line is NOT a HTTP header style line, we do offer support for
     * reading the odd netscape cookies-file format here
     */
    char *ptr;
    char *firstptr;
    char *tok_buf = NULL;
    int fields;

    /*
     * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked
     * with httpOnly after the domain name are not accessible from javascripts,
     * but since curl does not operate at javascript level, we include them
     * anyway. In Firefox's cookie files, these lines are preceded with
     * #HttpOnly_ and then everything is as usual, so we skip 10 characters of
     * the line..
     */
    if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
      lineptr += 10;
      co->httponly = TRUE;
    }

    if(lineptr[0]=='#') {
      /* don't even try the comments */
      free(co);
      return NULL;
    }
    /* strip off the possible end-of-line characters */
    ptr = strchr(lineptr, '\r');
    if(ptr)
      *ptr = 0; /* clear it */
    ptr = strchr(lineptr, '\n');
    if(ptr)
      *ptr = 0; /* clear it */

    firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */

    /*
     * Now loop through the fields and init the struct we already have
     * allocated
     */
    for(ptr = firstptr, fields = 0; ptr && !badcookie;
        ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
      switch(fields) {
      case 0:
        if(ptr[0]=='.') /* skip preceding dots */
          ptr++;
        co->domain = strdup(ptr);
        if(!co->domain)
          badcookie = TRUE;
        break;
      case 1:
        /*
         * flag: A TRUE/FALSE value indicating if all machines within a given
         * domain can access the variable. Set TRUE when the cookie says
         * .domain.com and to false when the domain is complete www.domain.com
         */
        co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
        break;
      case 2:
        /* The file format allows the path field to remain not filled in */
        if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
          /* only if the path doesn't look like a boolean option! */
          co->path = strdup(ptr);
          if(!co->path)
            badcookie = TRUE;
          else {
            co->spath = sanitize_cookie_path(co->path);
            if(!co->spath) {
              badcookie = TRUE; /* out of memory bad */
            }
          }
          break;
        }
        /* this doesn't look like a path, make one up! */
        co->path = strdup("/");
        if(!co->path)
          badcookie = TRUE;
        co->spath = strdup("/");
        if(!co->spath)
          badcookie = TRUE;
        fields++; /* add a field and fall down to secure */
        /* FALLTHROUGH */
      case 3:
        co->secure = FALSE;
        if(strcasecompare(ptr, "TRUE")) {
          if(secure || c->running)
            co->secure = TRUE;
          else
            badcookie = TRUE;
        }
        break;
      case 4:
        if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
          badcookie = TRUE;
        break;
      case 5:
        co->name = strdup(ptr);
        if(!co->name)
          badcookie = TRUE;
        else {
          /* For Netscape file format cookies we check prefix on the name */
          if(strncasecompare("__Secure-", co->name, 9))
            co->prefix |= COOKIE_PREFIX__SECURE;
          else if(strncasecompare("__Host-", co->name, 7))
            co->prefix |= COOKIE_PREFIX__HOST;
        }
        break;
      case 6:
        co->value = strdup(ptr);
        if(!co->value)
          badcookie = TRUE;
        break;
      }
    }
    if(6 == fields) {
      /* we got a cookie with blank contents, fix it */
      co->value = strdup("");
      if(!co->value)
        badcookie = TRUE;
      else
        fields++;
    }

    if(!badcookie && (7 != fields))
      /* we did not find the sufficient number of fields */
      badcookie = TRUE;

    if(badcookie) {
      freecookie(co);
      return NULL;
    }

  }

  if(co->prefix & COOKIE_PREFIX__SECURE) {
    /* The __Secure- prefix only requires that the cookie be set secure */
    if(!co->secure) {
      freecookie(co);
      return NULL;
    }
  }
  if(co->prefix & COOKIE_PREFIX__HOST) {
    /*
     * The __Host- prefix requires the cookie to be secure, have a "/" path
     * and not have a domain set.
     */
    if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
      ;
    else {
      freecookie(co);
      return NULL;
    }
  }

  if(!c->running &&    /* read from a file */
     c->newsession &&  /* clean session cookies */
     !co->expires) {   /* this is a session cookie since it doesn't expire! */
    freecookie(co);
    return NULL;
  }

  co->livecookie = c->running;
  co->creationtime = ++c->lastct;

  /*
   * Now we have parsed the incoming line, we must now check if this supersedes
   * an already existing cookie, which it may if the previous have the same
   * domain and path as this.
   */

  /* at first, remove expired cookies */
  if(!noexpire)
    remove_expired(c);

#ifdef USE_LIBPSL
  /*
   * Check if the domain is a Public Suffix and if yes, ignore the cookie. We
   * must also check that the data handle isn't NULL since the psl code will
   * dereference it.
   */
  if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
    const psl_ctx_t *psl = Curl_psl_use(data);
    int acceptable;

    if(psl) {
      acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain);
      Curl_psl_release(data);
    }
    else
      acceptable = !bad_domain(domain);

    if(!acceptable) {
      infof(data, "cookie '%s' dropped, domain '%s' must not "
                  "set cookies for '%s'", co->name, domain, co->domain);
      freecookie(co);
      return NULL;
    }
  }
#endif

  /* A non-secure cookie may not overlay an existing secure cookie. */
  myhash = cookiehash(co->domain);
  clist = c->cookies[myhash];
  while(clist) {
    if(strcasecompare(clist->name, co->name)) {
      /* the names are identical */
      bool matching_domains = FALSE;

      if(clist->domain && co->domain) {
        if(strcasecompare(clist->domain, co->domain))
          /* The domains are identical */
          matching_domains = TRUE;
      }
      else if(!clist->domain && !co->domain)
        matching_domains = TRUE;

      if(matching_domains && /* the domains were identical */
         clist->spath && co->spath && /* both have paths */
         clist->secure && !co->secure && !secure) {
        size_t cllen;
        const char *sep;

        /*
         * A non-secure cookie may not overlay an existing secure cookie.
         * For an existing cookie "a" with path "/login", refuse a new
         * cookie "a" with for example path "/login/en", while the path
         * "/loginhelper" is ok.
         */

        sep = strchr(clist->spath + 1, '/');

        if(sep)
          cllen = sep - clist->spath;
        else
          cllen = strlen(clist->spath);

        if(strncasecompare(clist->spath, co->spath, cllen)) {
          infof(data, "cookie '%s' for domain '%s' dropped, would "
                "overlay an existing cookie", co->name, co->domain);
          freecookie(co);
          return NULL;
        }
      }
    }

    if(!replace_co && strcasecompare(clist->name, co->name)) {
      /* the names are identical */

      if(clist->domain && co->domain) {
        if(strcasecompare(clist->domain, co->domain) &&
          (clist->tailmatch == co->tailmatch))
          /* The domains are identical */
          replace_old = TRUE;
      }
      else if(!clist->domain && !co->domain)
        replace_old = TRUE;

      if(replace_old) {
        /* the domains were identical */

        if(clist->spath && co->spath) {
          if(strcasecompare(clist->spath, co->spath))
            replace_old = TRUE;
          else
            replace_old = FALSE;
        }
        else if(!clist->spath && !co->spath)
          replace_old = TRUE;
        else
          replace_old = FALSE;

      }

      if(replace_old && !co->livecookie && clist->livecookie) {
        /*
         * Both cookies matched fine, except that the already present cookie is
         * "live", which means it was set from a header, while the new one was
         * read from a file and thus isn't "live". "live" cookies are preferred
         * so the new cookie is freed.
         */
        freecookie(co);
        return NULL;
      }
      if(replace_old) {
        replace_co = co;
        replace_clist = clist;
      }
    }
    lastc = clist;
    clist = clist->next;
  }
  if(replace_co) {
    co = replace_co;
    clist = replace_clist;
    co->next = clist->next; /* get the next-pointer first */

    /* when replacing, creationtime is kept from old */
    co->creationtime = clist->creationtime;

    /* then free all the old pointers */
    free(clist->name);
    free(clist->value);
    free(clist->domain);
    free(clist->path);
    free(clist->spath);
    free(clist->expirestr);
    free(clist->version);
    free(clist->maxage);

    *clist = *co;  /* then store all the new data */

    free(co);   /* free the newly allocated memory */
    co = clist;
  }

  if(c->running)
    /* Only show this when NOT reading the cookies from a file */
    infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
          "expire %" CURL_FORMAT_CURL_OFF_T,
          replace_old?"Replaced":"Added", co->name, co->value,
          co->domain, co->path, co->expires);

  if(!replace_old) {
    /* then make the last item point on this new one */
    if(lastc)
      lastc->next = co;
    else
      c->cookies[myhash] = co;
    c->numcookies++; /* one more cookie in the jar */
  }

  /*
   * Now that we've added a new cookie to the jar, update the expiration
   * tracker in case it is the next one to expire.
   */
  if(co->expires && (co->expires < c->next_expiration))
    c->next_expiration = co->expires;

  return co;
}


cookie: reject cookies with "control bytes"

Rejects 0x01 - 0x1f (except 0x09) plus 0x7f

Reported-by: Axel Chong

Bug: https://curl.se/docs/CVE-2022-35252.html

CVE-2022-35252

Closes #9381

static CURLcode cookie_output(struct Curl_easy *data,
                              struct CookieInfo *c, const char *filename)
{
  struct Cookie *co;
  FILE *out = NULL;
  bool use_stdout = FALSE;
  char *tempstore = NULL;
  CURLcode error = CURLE_OK;

  if(!c)
    /* no cookie engine alive */
    return CURLE_OK;

  /* at first, remove expired cookies */
  remove_expired(c);

  if(!strcmp("-", filename)) {
    /* use stdout */
    out = stdout;
    use_stdout = TRUE;
  }
  else {
    error = Curl_fopen(data, filename, &out, &tempstore);
    if(error)
      goto error;
  }

  fputs("# Netscape HTTP Cookie File\n"
        "# https://curl.se/docs/http-cookies.html\n"
        "# This file was generated by libcurl! Edit at your own risk.\n\n",
        out);

  if(c->numcookies) {
    unsigned int i;
    size_t nvalid = 0;
    struct Cookie **array;

    array = calloc(1, sizeof(struct Cookie *) * c->numcookies);
    if(!array) {
      error = CURLE_OUT_OF_MEMORY;
      goto error;
    }

    /* only sort the cookies with a domain property */
    for(i = 0; i < COOKIE_HASH_SIZE; i++) {
      for(co = c->cookies[i]; co; co = co->next) {
        if(!co->domain)
          continue;
        array[nvalid++] = co;
      }
    }

    qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);

    for(i = 0; i < nvalid; i++) {
      char *format_ptr = get_netscape_format(array[i]);
      if(!format_ptr) {
        free(array);
        error = CURLE_OUT_OF_MEMORY;
        goto error;
      }
      fprintf(out, "%s\n", format_ptr);
      free(format_ptr);
    }

    free(array);
  }

  if(!use_stdout) {
    fclose(out);
    out = NULL;
    if(tempstore && Curl_rename(tempstore, filename)) {
      unlink(tempstore);
      error = CURLE_WRITE_ERROR;
      goto error;
    }
  }

  /*
   * If we reach here we have successfully written a cookie file so theree is
   * no need to inspect the error, any error case should have jumped into the
   * error block below.
   */
  free(tempstore);
  return CURLE_OK;

error:
  if(out && !use_stdout)
    fclose(out);
  free(tempstore);
  return error;
}


struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
                                   struct CookieInfo *c,
                                   const char *host, const char *path,
                                   bool secure)
{
  struct Cookie *newco;
  struct Cookie *co;
  struct Cookie *mainco = NULL;
  size_t matches = 0;
  bool is_ip;
  const size_t myhash = cookiehash(host);

  if(!c || !c->cookies[myhash])
    return NULL; /* no cookie struct or no cookies in the struct */

  /* at first, remove expired cookies */
  remove_expired(c);

  /* check if host is an IP(v4|v6) address */
  is_ip = Curl_host_is_ipnum(host);

  co = c->cookies[myhash];

  while(co) {
    /* if the cookie requires we're secure we must only continue if we are! */
    if(co->secure?secure:TRUE) {

      /* now check if the domain is correct */
      if(!co->domain ||
         (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
         ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
        /*
         * the right part of the host matches the domain stuff in the
         * cookie data
         */

        /*
         * now check the left part of the path with the cookies path
         * requirement
         */
        if(!co->spath || pathmatch(co->spath, path) ) {

          /*
           * and now, we know this is a match and we should create an
           * entry for the return-linked-list
           */

          newco = dup_cookie(co);
          if(newco) {
            /* then modify our next */
            newco->next = mainco;

            /* point the main to us */
            mainco = newco;

            matches++;
            if(matches >= MAX_COOKIE_SEND_AMOUNT) {
              infof(data, "Included max number of cookies (%zu) in request!",
                    matches);
              break;
            }
          }
          else
            goto fail;
        }
      }
    }
    co = co->next;
  }

  if(matches) {
    /*
     * Now we need to make sure that if there is a name appearing more than
     * once, the longest specified path version comes first. To make this
     * the swiftest way, we just sort them all based on path length.
     */
    struct Cookie **array;
    size_t i;

    /* alloc an array and store all cookie pointers */
    array = malloc(sizeof(struct Cookie *) * matches);
    if(!array)
      goto fail;

    co = mainco;

    for(i = 0; co; co = co->next)
      array[i++] = co;

    /* now sort the cookie pointers in path length order */
    qsort(array, matches, sizeof(struct Cookie *), cookie_sort);

    /* remake the linked list order according to the new order */

    mainco = array[0]; /* start here */
    for(i = 0; i<matches-1; i++)
      array[i]->next = array[i + 1];
    array[matches-1]->next = NULL; /* terminate the list */

    free(array); /* remove the temporary data again */
  }

  return mainco; /* return the new list */

fail:
  /* failure, clear up the allocated chain and return NULL */
  Curl_cookie_freelist(mainco);
  return NULL;
}


GF_Err BM_ParseIndexInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	GF_Err e;
	u32 NodeID;
	u32 NumBits, ind, field_ind;
	u8 type;
	GF_Command *com;
	GF_CommandField *inf;
	s32 pos;
	GF_Node *def, *node;
	GF_FieldInfo field, sffield;

	NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	def = gf_sg_find_node(codec->current_graph, NodeID);
	if (!def) return GF_NON_COMPLIANT_BITSTREAM;
	/*index insertion uses IN mode for field index*/
	NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(def, GF_SG_FIELD_CODING_IN)-1);
	ind = gf_bs_read_int(bs, NumBits);

	e = gf_bifs_get_field_index(def, ind, GF_SG_FIELD_CODING_IN, &field_ind);
	if (e) return e;

	type = gf_bs_read_int(bs, 2);
	switch (type) {
	case 0:
		pos = gf_bs_read_int(bs, 16);
		break;
	case 2:
		pos = 0;
		break;
	case 3:
		pos = -1;
		break;
	default:
		return GF_NON_COMPLIANT_BITSTREAM;
	}

	e = gf_node_get_field(def, field_ind, &field);
	if (e) return e;
	if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM;

	memcpy(&sffield, &field, sizeof(GF_FieldInfo));
	sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType);

	/*rescale the MFField and parse the SFField*/
	if (field.fieldType==GF_SG_VRML_MFNODE) {
		node = gf_bifs_dec_node(codec, bs, field.NDTtype);
		if (!codec->LastError) {
			com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_INSERT);
			BM_SetCommandNode(com, def);
			inf = gf_sg_command_field_new(com);
			inf->pos = pos;
			inf->fieldIndex = field_ind;
			inf->fieldType = sffield.fieldType;
			inf->new_node = node;
			inf->field_ptr = &inf->new_node;
			gf_list_add(com_list, com);
			/*register*/
			gf_node_register(node, NULL);
		}
	} else {
		com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_INSERT);
		BM_SetCommandNode(com, def);
		inf = gf_sg_command_field_new(com);
		inf->pos = pos;
		inf->fieldIndex = field_ind;
		inf->fieldType = sffield.fieldType;
		sffield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(sffield.fieldType);
		codec->LastError = gf_bifs_dec_sf_field(codec, bs, def, &sffield, GF_TRUE);
		gf_list_add(com_list, com);
	}
	return codec->LastError;
}

Use After Free in GitHub repository gpac/gpac prior to v2.1.0-DEV.


GF_Err BM_ParseCommand(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u8 go, type;
	GF_Err e;
	go = 1;
	e = GF_OK;
	GF_SceneGraph *cur_graph = codec->current_graph;
	GF_Proto *cur_proto = codec->pCurrentProto;

	codec->LastError = GF_OK;
	while (go) {
		type = gf_bs_read_int(bs, 2);
		switch (type) {
		case 0:
			e = BM_ParseInsert(codec, bs, com_list);
			break;
		case 1:
			e = BM_ParseDelete(codec, bs, com_list);
			break;
		case 2:
			e = BM_ParseReplace(codec, bs, com_list);
			break;
		case 3:
			e = BM_SceneReplace(codec, bs, com_list);
			break;
		}
		if (e) break;
		go = gf_bs_read_int(bs, 1);
	}
	while (gf_list_count(codec->QPs)) {
		gf_bifs_dec_qp_remove(codec, GF_TRUE);
	}

	codec->current_graph = cur_graph;
	codec->pCurrentProto = cur_proto;
	return e;
}


GF_Err gf_bifs_flush_command_list(GF_BifsDecoder *codec)
{
	GF_BitStream *bs;
	GF_Err e;
	CommandBufferItem *cbi;
	GF_SceneGraph *prev_root = codec->current_graph;
	M_QuantizationParameter *prev_qp = codec->ActiveQP;
	u32 prev_qp_count = gf_list_count(codec->QPs);
	u32 NbPass = gf_list_count(codec->command_buffers);

	codec->ActiveQP = NULL;
	GF_List *nextPass = gf_list_new();
	while (NbPass) {
		while (gf_list_count(codec->command_buffers)) {
			cbi = (CommandBufferItem *)gf_list_get(codec->command_buffers, 0);
			gf_list_rem(codec->command_buffers, 0);

			codec->current_graph = gf_node_get_graph(cbi->node);
			e = GF_OK;
			if (cbi->cb->bufferSize) {
				bs = gf_bs_new((char*)cbi->cb->buffer, cbi->cb->bufferSize, GF_BITSTREAM_READ);
				gf_bs_set_eos_callback(bs, BM_EndOfStream, codec);
				e = BM_ParseCommand(codec, bs, cbi->cb->commandList);
				gf_bs_del(bs);
			}
			if (!e) {
				gf_node_unregister(cbi->node, NULL);
				gf_free(cbi);
				continue;
			}
			/*this may be an error or a dependency pb - reset coimmand list and move to next pass*/
			while (gf_list_count(cbi->cb->commandList)) {
				u32 i;
				GF_CommandField *cf;
				GF_Command *com = (GF_Command *)gf_list_get(cbi->cb->commandList, 0);
				gf_list_rem(cbi->cb->commandList, 0);
				cf = (GF_CommandField *) gf_list_get(com->command_fields, 0);
				if (cf && cf->fieldType==GF_SG_VRML_SFCOMMANDBUFFER) {
					for (i=0; i<gf_list_count(codec->command_buffers); i++) {
						CommandBufferItem *cbi2 = (CommandBufferItem *)gf_list_get(codec->command_buffers, i);
						if (cbi2->cb == cf->field_ptr) {
							gf_node_unregister(cbi2->node, NULL);
							gf_free(cbi2);
							gf_list_rem(codec->command_buffers, i);
							i--;
						}
					}
				}
				gf_sg_command_del(com);
			}
			gf_list_add(nextPass, cbi);
		}
		if (!gf_list_count(nextPass)) break;
		/*prepare next pass*/
		while (gf_list_count(nextPass)) {
			cbi = (CommandBufferItem *)gf_list_get(nextPass, 0);
			gf_list_rem(nextPass, 0);
			gf_list_add(codec->command_buffers, cbi);
		}
		NbPass --;
		if (NbPass > gf_list_count(codec->command_buffers)) NbPass = gf_list_count(codec->command_buffers);

		//restore QP state
		while (gf_list_count(codec->QPs) > prev_qp_count) {
			gf_list_rem(codec->QPs, 0); //QPs are inserted at head of list
		}
		codec->ActiveQP = NULL;
		codec->LastError = GF_OK;
	}
	gf_list_del(nextPass);
	codec->current_graph = prev_root;
	codec->ActiveQP = prev_qp;
	return GF_OK;
}


GF_Err BM_ParseFieldReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	GF_Err e;
	GF_Command *com;
	u32 NodeID, ind, field_ind, NumBits;
	GF_Node *node;
	GF_FieldInfo field;
	GF_CommandField *inf;

	NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	node = gf_sg_find_node(codec->current_graph, NodeID);
	if (!node) return GF_NON_COMPLIANT_BITSTREAM;
	NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1);
	ind = gf_bs_read_int(bs, NumBits);
	e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind);
	if (e) return e;

	e = gf_node_get_field(node, field_ind, &field);
	if (e) return e;

	com = gf_sg_command_new(codec->current_graph, GF_SG_FIELD_REPLACE);
	BM_SetCommandNode(com, node);
	inf = gf_sg_command_field_new(com);
	inf->fieldIndex = field_ind;
	inf->fieldType = field.fieldType;
	if (inf->fieldType == GF_SG_VRML_SFNODE) {
		field.far_ptr = inf->field_ptr = &inf->new_node;
	} else if (inf->fieldType == GF_SG_VRML_MFNODE) {
		field.far_ptr = inf->field_ptr = &inf->node_list;
	} else {
		field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(field.fieldType);
	}
	/*parse the field*/
	codec->LastError = gf_bifs_dec_field(codec, bs, node, &field, GF_TRUE);

	gf_list_add(com_list, com);
	return codec->LastError;
}


void BM_EndOfStream(void *co)
{
	((GF_BifsDecoder *) co)->LastError = GF_NON_COMPLIANT_BITSTREAM;
}


GF_Err gf_bifs_decode_command_list(GF_BifsDecoder *codec, u16 ESID, u8 *data, u32 data_length, GF_List *com_list)
{
	GF_BitStream *bs;
	GF_Err e;

	if (!codec || !data || !codec->dec_memory_mode || !com_list) return GF_BAD_PARAM;

	codec->info = gf_bifs_dec_get_stream(codec, ESID);
	if (!codec->info) return GF_BAD_PARAM;
	if (codec->info->config.elementaryMasks ) return GF_NOT_SUPPORTED;

	/*root parse (not conditionals)*/
	assert(codec->scenegraph);
	/*setup current scene graph*/
	codec->current_graph = codec->scenegraph;

	codec->ActiveQP = (M_QuantizationParameter*) codec->scenegraph->global_qp;

	bs = gf_bs_new(data, data_length, GF_BITSTREAM_READ);
	gf_bs_set_eos_callback(bs, BM_EndOfStream, codec);

	e = BM_ParseCommand(codec, bs, com_list);
	gf_bs_del(bs);

	/*decode conditionals / input sensors*/
	if (!e) {
		gf_bifs_flush_command_list(codec);
	}
	/*if err or not reset conditionals*/
	while (gf_list_count(codec->command_buffers)) {
		CommandBufferItem *cbi = (CommandBufferItem *)gf_list_get(codec->command_buffers, 0);
		gf_node_unregister(cbi->node, NULL);
		gf_free(cbi);
		gf_list_rem(codec->command_buffers, 0);
	}

	/*reset current config*/
	codec->info = NULL;
	codec->current_graph = NULL;

//	gf_mx_v(codec->mx);
	return e;
}


GF_Err BM_ParseInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u8 type;

	type = gf_bs_read_int(bs, 2);
	switch (type) {
	case 0:
		return BM_ParseNodeInsert(codec, bs, com_list);
	case 1:
		return BM_ParseExtendedUpdates(codec, bs, com_list);
	case 2:
		return BM_ParseIndexInsert(codec, bs, com_list);
	case 3:
		return BM_ParseRouteInsert(codec, bs, com_list);
	default:
		return GF_NON_COMPLIANT_BITSTREAM;
	}
}


static GF_Err BM_ParseExtendedUpdates(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 type = gf_bs_read_int(bs, 8);
	GF_Err e;

	switch (type) {
	case 0:
	{
		GF_Command *com = gf_sg_command_new(codec->current_graph, GF_SG_PROTO_INSERT);
		e = gf_bifs_dec_proto_list(codec, bs, com->new_proto_list);
		if (e) gf_sg_command_del(com);
		else gf_list_add(com_list, com);
	}
	return e;
	case 1:
		return BM_ParseProtoDelete(codec, bs, com_list);
	case 2:
	{
		GF_Command *com = gf_sg_command_new(codec->current_graph, GF_SG_PROTO_DELETE_ALL);
		return gf_list_add(com_list, com);
	}
	case 3:
		return BM_ParseMultipleIndexedReplace(codec, bs, com_list);
	case 4:
		return BM_ParseMultipleReplace(codec, bs, com_list);
	case 5:
		return BM_ParseGlobalQuantizer(codec, bs, com_list);
	case 6:
	{
		GF_Command *com;
		u32 ID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
		GF_Node *n = gf_sg_find_node(codec->current_graph, ID);
		if (!n) return GF_OK;
		com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_DELETE_EX);
		BM_SetCommandNode(com, n);
		gf_list_add(com_list, com);
	}
	return GF_OK;
	case 7:
		return BM_XReplace(codec, bs, com_list);

	default:
		return GF_BIFS_UNKNOWN_VERSION;
	}
}


GF_Err BM_ParseIndexDelete(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 NodeID, NumBits, ind, field_ind;
	s32 pos;
	GF_Command *com;
	u8 type;
	GF_Node *node;
	GF_Err e;
	GF_CommandField *inf;
	GF_FieldInfo field;

	NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	node = gf_sg_find_node(codec->current_graph, NodeID);
	if (!node) return GF_NON_COMPLIANT_BITSTREAM;

	NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN) - 1);
	ind = gf_bs_read_int(bs, NumBits);

	type = gf_bs_read_int(bs, 2);
	switch (type) {
	case 0:
		pos = (u32) gf_bs_read_int(bs, 16);
		break;
	case 2:
		pos = 0;
		break;
	case 3:
		pos = -1;
		break;
	default:
		return GF_NON_COMPLIANT_BITSTREAM;
	}
	e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind);
	if (e) return e;
	e = gf_node_get_field(node, field_ind, &field);
	if (e) return e;
	if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM;
	com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_DELETE);
	BM_SetCommandNode(com, node);
	inf = gf_sg_command_field_new(com);
	inf->pos = pos;
	inf->fieldIndex = field.fieldIndex;
	inf->fieldType = gf_sg_vrml_get_sf_type(field.fieldType);
	gf_list_add(com_list, com);
	return codec->LastError;
}


GF_Err BM_ParseRouteInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	GF_Err e;
	u8 flag;
	GF_Command *com;
	GF_Node *InNode, *OutNode;
	u32 RouteID, outField, inField, numBits, ind, node_id;
	char name[1000];

	RouteID = 0;

	flag = gf_bs_read_int(bs, 1);
	/*def'ed route*/
	if (flag) {
		RouteID = 1 + gf_bs_read_int(bs, codec->info->config.RouteIDBits);
		if (codec->UseName) gf_bifs_dec_name(bs, name, 1000);
	}
	/*origin*/
	node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	OutNode = gf_sg_find_node(codec->current_graph, node_id);
	if (!OutNode) return GF_SG_UNKNOWN_NODE;

	numBits = gf_node_get_num_fields_in_mode(OutNode, GF_SG_FIELD_CODING_OUT) - 1;
	numBits = gf_get_bit_size(numBits);
	ind = gf_bs_read_int(bs, numBits);
	e = gf_bifs_get_field_index(OutNode, ind, GF_SG_FIELD_CODING_OUT, &outField);
	if (e) return e;

	/*target*/
	node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	InNode = gf_sg_find_node(codec->current_graph, node_id);
	if (!InNode) return GF_SG_UNKNOWN_NODE;

	numBits = gf_node_get_num_fields_in_mode(InNode, GF_SG_FIELD_CODING_IN) - 1;
	numBits = gf_get_bit_size(numBits);
	ind = gf_bs_read_int(bs, numBits);
	e = gf_bifs_get_field_index(InNode, ind, GF_SG_FIELD_CODING_IN, &inField);
	if (e) return e;

	com = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_INSERT);
	com->RouteID = RouteID;
	if (codec->UseName) com->def_name = gf_strdup( name);
	com->fromNodeID = gf_node_get_id(OutNode);
	com->fromFieldIndex = outField;
	com->toNodeID = gf_node_get_id(InNode);
	com->toFieldIndex = inField;
	gf_list_add(com_list, com);
	return codec->LastError;
}


GF_Err BM_ParseNodeReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 NodeID;
	GF_Command *com;
	GF_Node *node;
	GF_CommandField *inf;

	NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	/*this is delete / new on a DEF node: replace ALL instances*/
	node = gf_sg_find_node(codec->current_graph, NodeID);
	if (!node) return GF_NON_COMPLIANT_BITSTREAM;

	com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_REPLACE);
	BM_SetCommandNode(com, node);
	inf = gf_sg_command_field_new(com);
	inf->new_node = gf_bifs_dec_node(codec, bs, NDT_SFWorldNode);
	inf->fieldType = GF_SG_VRML_SFNODE;
	inf->field_ptr = &inf->new_node;
	gf_list_add(com_list, com);
	gf_node_register(inf->new_node, NULL);
	return codec->LastError;
}


static GF_Err BM_ParseGlobalQuantizer(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	GF_Node *node;
	GF_Command *com;
	GF_CommandField *inf;
	node = gf_bifs_dec_node(codec, bs, NDT_SFWorldNode);
	if (!node) return GF_NON_COMPLIANT_BITSTREAM;

	/*reset global QP*/
	if (codec->scenegraph->global_qp) {
		gf_node_unregister(codec->scenegraph->global_qp, NULL);
	}
	codec->ActiveQP = NULL;
	codec->scenegraph->global_qp = NULL;

	if (gf_node_get_tag(node) != TAG_MPEG4_QuantizationParameter) {
		//if node was just created (num_instances == 0), unregister
		//otherwise (USE node) don't do anything
		if (!node->sgprivate->num_instances) {
			node->sgprivate->num_instances = 1;
			gf_node_unregister(node, NULL);
		}
		return GF_NON_COMPLIANT_BITSTREAM;
	}

	/*register global QP*/
	codec->ActiveQP = (M_QuantizationParameter *) node;
	codec->ActiveQP->isLocal = 0;
	codec->scenegraph->global_qp = node;

	/*register TWICE: once for the command, and for the scenegraph globalQP*/
	gf_node_unregister(node, NULL);
	gf_node_unregister(node, NULL);

	com = gf_sg_command_new(codec->current_graph, GF_SG_GLOBAL_QUANTIZER);
	inf = gf_sg_command_field_new(com);
	inf->new_node = node;
	inf->field_ptr = &inf->new_node;
	inf->fieldType = GF_SG_VRML_SFNODE;
	gf_list_add(com_list, com);
	return GF_OK;
}


static GF_Err BM_ParseMultipleReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 i, numFields, index, flag, nbBits, field_ref, fieldind;
	GF_Err e;
	GF_FieldInfo field;
	u32 NodeID;
	GF_Node *node;
	GF_Command *com;
	GF_CommandField *inf;

	NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	node = gf_sg_find_node(codec->current_graph, NodeID);
	if (!node) return GF_NON_COMPLIANT_BITSTREAM;

	e = GF_OK;
	com = gf_sg_command_new(codec->current_graph, GF_SG_MULTIPLE_REPLACE);
	BM_SetCommandNode(com, node);
	flag = gf_bs_read_int(bs, 1);
	if (flag) {
		numFields = gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF);
		for (i=0; i<numFields; i++) {
			flag = gf_bs_read_int(bs, 1);
			if (!flag) continue;
			gf_bifs_get_field_index(node, i, GF_SG_FIELD_CODING_DEF, &index);
			e = gf_node_get_field(node, index, &field);
			if (e) goto exit;
			inf = gf_sg_command_field_new(com);
			inf->fieldType = field.fieldType;
			inf->fieldIndex = field.fieldIndex;
			if (inf->fieldType==GF_SG_VRML_SFNODE) {
				field.far_ptr = inf->field_ptr = &inf->new_node;
			} else if (inf->fieldType==GF_SG_VRML_MFNODE) {
				field.far_ptr = inf->field_ptr = &inf->node_list;
			} else {
				field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType);
			}
			e = gf_bifs_dec_field(codec, bs, node, &field, GF_TRUE);
			if (e) goto exit;
		}
	} else {
		flag = gf_bs_read_int(bs, 1);
		nbBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_DEF)-1);
		while (!flag && (codec->LastError>=0)) {
			field_ref = gf_bs_read_int(bs, nbBits);
			e = gf_bifs_get_field_index(node, field_ref, GF_SG_FIELD_CODING_DEF, &fieldind);
			if (e) goto exit;
			e = gf_node_get_field(node, fieldind, &field);
			if (e) goto exit;
			inf = gf_sg_command_field_new(com);
			inf->fieldType = field.fieldType;
			inf->fieldIndex = field.fieldIndex;
			if (inf->fieldType==GF_SG_VRML_SFNODE) {
				field.far_ptr = inf->field_ptr = &inf->new_node;
			} else if (inf->fieldType==GF_SG_VRML_MFNODE) {
				field.far_ptr = inf->field_ptr = &inf->node_list;
			} else {
				field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType);
			}
			e = gf_bifs_dec_field(codec, bs, node, &field, GF_TRUE);
			if (e) goto exit;
			flag = gf_bs_read_int(bs, 1);
		}
	}

exit:
	if (e) gf_sg_command_del(com);
	else gf_list_add(com_list, com);
	return e;
}


GF_Err BM_SceneReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	GF_Command *com;
	GF_Node *backup_root;
	GF_List *backup_routes;
	GF_Err BD_DecSceneReplace(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List *proto_list);

	backup_routes = codec->scenegraph->Routes;
	backup_root = codec->scenegraph->RootNode;
	com = gf_sg_command_new(codec->current_graph, GF_SG_SCENE_REPLACE);
	codec->scenegraph->Routes = gf_list_new();
	codec->current_graph = codec->scenegraph;
	codec->LastError = BD_DecSceneReplace(codec, bs, com->new_proto_list);
	com->use_names = codec->UseName;

	/*restore*/
	com->node = codec->scenegraph->RootNode;
	codec->scenegraph->RootNode = backup_root;
	gf_list_add(com_list, com);
	/*insert routes*/
	while (gf_list_count(codec->scenegraph->Routes)) {
		GF_Route *r = (GF_Route*)gf_list_get(codec->scenegraph->Routes, 0);
		GF_Command *ri = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_INSERT);
		gf_list_rem(codec->scenegraph->Routes, 0);
		ri->fromFieldIndex = r->FromField.fieldIndex;
		ri->fromNodeID = gf_node_get_id(r->FromNode);
		ri->toFieldIndex = r->ToField.fieldIndex;
		ri->toNodeID = gf_node_get_id(r->ToNode);
		if (r->ID) ri->RouteID = r->ID;
		ri->def_name = r->name ? gf_strdup(r->name) : NULL;
		gf_list_add(com_list, ri);
		gf_sg_route_del(r);
	}
	gf_list_del(codec->scenegraph->Routes);
	codec->scenegraph->Routes = backup_routes;
	return codec->LastError;
}


static void BM_SetCommandNode(GF_Command *com, GF_Node *node)
{
	com->node = node;
	gf_node_register(node, NULL);
}


GF_Err BM_ParseIndexValueReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 NodeID, ind, field_ind, NumBits;
	s32 type, pos;
	GF_Command *com;
	GF_Node *node;
	GF_Err e;
	GF_FieldInfo field, sffield;
	GF_CommandField *inf;

	/*get the node*/
	NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);

	node = gf_sg_find_node(codec->current_graph, NodeID);
	if (!node) return GF_NON_COMPLIANT_BITSTREAM;
	NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1);
	ind = gf_bs_read_int(bs, NumBits);
	e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind);
	if (e) return e;

	e = gf_node_get_field(node, field_ind, &field);
	if (e) return e;
	if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM;

	type = gf_bs_read_int(bs, 2);
	switch (type) {
	case 0:
		pos = gf_bs_read_int(bs, 16);
		break;
	case 2:
		pos = 0;
		break;
	case 3:
		pos = ((GenMFField *) field.far_ptr)->count - 1;
		break;
	default:
		return GF_NON_COMPLIANT_BITSTREAM;
	}

	com = gf_sg_command_new(codec->current_graph, GF_SG_INDEXED_REPLACE);
	BM_SetCommandNode(com, node);
	inf = gf_sg_command_field_new(com);
	inf->fieldIndex = field.fieldIndex;
	inf->pos = pos;

	if (field.fieldType == GF_SG_VRML_MFNODE) {
		inf->fieldType = GF_SG_VRML_SFNODE;
		inf->new_node = gf_bifs_dec_node(codec, bs, field.NDTtype);
		inf->field_ptr = &inf->new_node;
		if (inf->new_node) gf_node_register(inf->new_node, NULL);
	} else {
		memcpy(&sffield, &field, sizeof(GF_FieldInfo));
		sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType);
		inf->fieldType = sffield.fieldType;
		sffield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(sffield.fieldType);
		codec->LastError = gf_bifs_dec_sf_field(codec, bs, node, &sffield, GF_TRUE);
	}
	gf_list_add(com_list, com);
	return codec->LastError;
}


GF_Err BM_ParseNodeInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 NodeID, NDT;
	GF_CommandField *inf;
	s32 type, pos;
	GF_Node *node, *def;

	NodeID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	def = gf_sg_find_node(codec->current_graph, NodeID);
	if (!def) return GF_NON_COMPLIANT_BITSTREAM;
	NDT = gf_bifs_get_child_table(def);
	if (!NDT) return GF_NON_COMPLIANT_BITSTREAM;

	type = gf_bs_read_int(bs, 2);
	switch (type) {
	case 0:
		pos = gf_bs_read_int(bs, 8);
		break;
	case 2:
		pos = 0;
		break;
	case 3:
		/*-1 means append*/
		pos = -1;
		break;
	default:
		return GF_NON_COMPLIANT_BITSTREAM;
	}
	node = gf_bifs_dec_node(codec, bs, NDT);
	if (!codec->LastError) {
		GF_Command *com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_INSERT);
		BM_SetCommandNode(com, def);
		inf = gf_sg_command_field_new(com);
		inf->pos = pos;
		inf->new_node = node;
		inf->field_ptr = &inf->new_node;
		inf->fieldType = GF_SG_VRML_SFNODE;
		gf_list_add(com_list, com);
		/*register*/
		gf_node_register(node, NULL);
	}
	return codec->LastError;
}


static GF_Err BM_ParseProtoDelete(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 flag, count;
	GF_Command *com = gf_sg_command_new(codec->current_graph, GF_SG_PROTO_DELETE);
	flag = gf_bs_read_int(bs, 1);
	if (flag) {
		count = 0;
		flag = gf_bs_read_int(bs, 1);
		while (flag) {
			com->del_proto_list = (u32*)gf_realloc(com->del_proto_list, sizeof(u32) * (com->del_proto_list_size+1));
			com->del_proto_list[count] = gf_bs_read_int(bs, codec->info->config.ProtoIDBits);
			com->del_proto_list_size++;
			flag = gf_bs_read_int(bs, 1);
		}
	} else {
		flag = gf_bs_read_int(bs, 5);
		com->del_proto_list_size = gf_bs_read_int(bs, flag);
		com->del_proto_list = (u32*)gf_realloc(com->del_proto_list, sizeof(u32) * (com->del_proto_list_size));
		flag = 0;
		while (flag<com->del_proto_list_size) {
			com->del_proto_list[flag] = gf_bs_read_int(bs, codec->info->config.ProtoIDBits);
			flag++;
		}
	}
	gf_list_add(com_list, com);
	return GF_OK;
}


GF_Err BM_ParseReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u8 type;
	type = gf_bs_read_int(bs, 2);
	switch (type) {
	case 0:
		return BM_ParseNodeReplace(codec, bs, com_list);
	case 1:
		return BM_ParseFieldReplace(codec, bs, com_list);
	case 2:
		return BM_ParseIndexValueReplace(codec, bs, com_list);
	case 3:
		return BM_ParseRouteReplace(codec, bs, com_list);
	}
	return GF_OK;
}


static GF_Err BM_XReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	GF_FieldInfo targetField, fromField, decfield;
	GF_Node *target, *fromNode;
	s32 pos = -2;
	u32 id, nbBits, ind, aind;
	GF_Err e;
	GF_Command *com;
	GF_CommandField *inf;

	id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	target = gf_sg_find_node(codec->current_graph, id);
	if (!target) return GF_SG_UNKNOWN_NODE;

	com = gf_sg_command_new(codec->current_graph, GF_SG_XREPLACE);
	BM_SetCommandNode(com, target);
	gf_list_add(com_list, com);

	nbBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(target, GF_SG_FIELD_CODING_IN)-1);
	ind = gf_bs_read_int(bs, nbBits);
	e = gf_bifs_get_field_index(target, ind, GF_SG_FIELD_CODING_IN, &aind);
	if (e) return e;
	e = gf_node_get_field(target, aind, &targetField);
	if (e) return e;

	inf = gf_sg_command_field_new(com);
	inf->fieldIndex = aind;

	if (!gf_sg_vrml_is_sf_field(targetField.fieldType)) {
		/*this is indexed replacement*/
		if (gf_bs_read_int(bs, 1)) {
			/*index is dynamic*/
			if (gf_bs_read_int(bs, 1)) {
				GF_Node *n;
				id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
				n = gf_sg_find_node(codec->current_graph, id);
				if (!n) return GF_SG_UNKNOWN_NODE;
				com->toNodeID = id;

				nbBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(n, GF_SG_FIELD_CODING_DEF)-1);
				ind = gf_bs_read_int(bs, nbBits);
				e = gf_bifs_get_field_index(n, ind, GF_SG_FIELD_CODING_DEF, &aind);
				if (e) return e;
				e = gf_node_get_field(n, aind, &fromField);
				if (e) return e;
				com->toFieldIndex = aind;
			} else {
				u32 type = gf_bs_read_int(bs, 2);
				switch (type) {
				case 0:
					pos = gf_bs_read_int(bs, 16);
					break;
				case 2:
					pos = 0;
					break;
				case 3:
					pos = -1;
					break;
				}
			}
		}
		if (targetField.fieldType==GF_SG_VRML_MFNODE) {
			if (gf_bs_read_int(bs, 1)) {
				target = gf_node_list_get_child(*(GF_ChildNodeItem **)targetField.far_ptr, pos);
				if (!target) return GF_SG_UNKNOWN_NODE;

				nbBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(target, GF_SG_FIELD_CODING_IN)-1);
				ind = gf_bs_read_int(bs, nbBits);
				e = gf_bifs_get_field_index(target, ind, GF_SG_FIELD_CODING_IN, &aind);
				if (e) return e;
				e = gf_node_get_field(target, aind, &targetField);
				if (e) return e;
				pos = -2;
				com->child_field = aind;
				com->ChildNodeTag = gf_node_get_tag(target);
				if (com->ChildNodeTag == TAG_ProtoNode) {
					s32 p_id = gf_sg_proto_get_id(gf_node_get_proto(target));
					com->ChildNodeTag = -p_id;
				}
			}
		}
		inf->pos = pos;
	}

	fromNode = NULL;
	if (gf_bs_read_int(bs, 1)) {
		id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
		fromNode = gf_sg_find_node(codec->current_graph, id);
		if (!fromNode) return GF_SG_UNKNOWN_NODE;
		com->fromNodeID = id;

		nbBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(fromNode, GF_SG_FIELD_CODING_DEF)-1);
		ind = gf_bs_read_int(bs, nbBits);
		e = gf_bifs_get_field_index(fromNode, ind, GF_SG_FIELD_CODING_DEF, &aind);
		if (e) return e;
		e = gf_node_get_field(fromNode, aind, &fromField);
		if (e) return e;
		com->fromFieldIndex = aind;

		return GF_OK;
	}

	if (pos>= -1) {
		inf->fieldType = gf_sg_vrml_get_sf_type(targetField.fieldType);
	} else {
		inf->fieldType = targetField.fieldType;
	}
	decfield.fieldIndex = inf->fieldIndex;
	decfield.fieldType = inf->fieldType;

	if (inf->fieldType==GF_SG_VRML_SFNODE) {
		decfield.far_ptr = inf->field_ptr = &inf->new_node;
	} else if (inf->fieldType==GF_SG_VRML_MFNODE) {
		decfield.far_ptr = inf->field_ptr = &inf->node_list;
	} else {
		decfield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType);
	}
	e = gf_bifs_dec_sf_field(codec, bs, target, &decfield, GF_TRUE);
	return e;
}


GF_Err BM_ParseDelete(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u8 type;
	u32 ID;
	GF_Command *com;
	GF_Node *n;

	type = gf_bs_read_int(bs, 2);
	switch (type) {
	case 0:
		ID = 1+gf_bs_read_int(bs, codec->info->config.NodeIDBits);
		n = gf_sg_find_node(codec->current_graph, ID);
		if (!n) return GF_OK;
		com = gf_sg_command_new(codec->current_graph, GF_SG_NODE_DELETE);
		BM_SetCommandNode(com, n);
		gf_list_add(com_list, com);
		return GF_OK;
	case 2:
		return BM_ParseIndexDelete(codec, bs, com_list);
	case 3:
		com = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_DELETE);
		com->RouteID = 1+gf_bs_read_int(bs, codec->info->config.RouteIDBits);
		gf_list_add(com_list, com);
		return GF_OK;
	default:
		return GF_NON_COMPLIANT_BITSTREAM;
	}
	return GF_OK;
}


GF_Err BM_ParseRouteReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	GF_Err e;
	GF_Command *com;
	u32 RouteID, numBits, ind, node_id, fromID, toID;
	GF_Node *OutNode, *InNode;

	RouteID = 1+gf_bs_read_int(bs, codec->info->config.RouteIDBits);

	/*origin*/
	node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	OutNode = gf_sg_find_node(codec->current_graph, node_id);
	if (!OutNode) return GF_NON_COMPLIANT_BITSTREAM;
	numBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(OutNode, GF_SG_FIELD_CODING_OUT) - 1);
	ind = gf_bs_read_int(bs, numBits);
	e = gf_bifs_get_field_index(OutNode, ind, GF_SG_FIELD_CODING_OUT, &fromID);
	if (e) return e;

	/*target*/
	node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	InNode = gf_sg_find_node(codec->current_graph, node_id);
	if (!InNode) return GF_NON_COMPLIANT_BITSTREAM;
	numBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(InNode, GF_SG_FIELD_CODING_IN) - 1);
	ind = gf_bs_read_int(bs, numBits);
	e = gf_bifs_get_field_index(InNode, ind, GF_SG_FIELD_CODING_IN, &toID);
	if (e) return e;

	com = gf_sg_command_new(codec->current_graph, GF_SG_ROUTE_REPLACE);
	com->RouteID = RouteID;
	com->fromNodeID = gf_node_get_id(OutNode);
	com->fromFieldIndex = fromID;
	com->toNodeID = gf_node_get_id(InNode);
	com->toFieldIndex = toID;
	gf_list_add(com_list, com);
	return codec->LastError;
}


static GF_Err BM_ParseMultipleIndexedReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_list)
{
	u32 ID, ind, field_ind, NumBits, lenpos, lennum, count;
	GF_Node *node;
	GF_Err e;
	GF_Command *com;
	GF_CommandField *inf;
	GF_FieldInfo field;

	ID = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits);
	node = gf_sg_find_node(codec->current_graph, ID);
	if (!node) return GF_NON_COMPLIANT_BITSTREAM;
	NumBits = gf_get_bit_size(gf_node_get_num_fields_in_mode(node, GF_SG_FIELD_CODING_IN)-1);
	ind = gf_bs_read_int(bs, NumBits);
	e = gf_bifs_get_field_index(node, ind, GF_SG_FIELD_CODING_IN, &field_ind);
	if (e) return e;
	e = gf_node_get_field(node, field_ind, &field);
	if (gf_sg_vrml_is_sf_field(field.fieldType)) return GF_NON_COMPLIANT_BITSTREAM;

	lenpos = gf_bs_read_int(bs, 5);
	lennum = gf_bs_read_int(bs, 5);
	count = gf_bs_read_int(bs, lennum);

	com = gf_sg_command_new(codec->current_graph, GF_SG_MULTIPLE_INDEXED_REPLACE);
	BM_SetCommandNode(com, node);
	field.fieldType = gf_sg_vrml_get_sf_type(field.fieldType);

	while (count) {
		inf = gf_sg_command_field_new(com);
		inf->pos = gf_bs_read_int(bs, lenpos);
		inf->fieldIndex = field.fieldIndex;
		inf->fieldType = field.fieldType;

		if (field.fieldType==GF_SG_VRML_SFNODE) {
			inf->new_node = gf_bifs_dec_node(codec, bs, field.NDTtype);
			if (codec->LastError) goto err;
			inf->field_ptr = &inf->new_node;
			gf_node_register(inf->new_node, NULL);
		} else {
			field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType);
			e = gf_bifs_dec_sf_field(codec, bs, node, &field, GF_TRUE);
			if (e) goto err;
		}
		count--;
	}
err:
	if (e) gf_sg_command_del(com);
	else gf_list_add(com_list, com);
	return e;
}


Fix class static block opening brace parsing (#4942)

The next character should not be consumed after finding the static block opening brace.
This patch fixes #4916.

JerryScript-DCO-1.0-Signed-off-by: Martin Negyokru negyokru@inf.u-szeged.hu

lexer_next_token (parser_context_t *context_p) /**< context */
{
  size_t length;

#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING
  /* Needed by arrow functions with expression body */
  context_p->function_end_p = context_p->source_p;
#endif /* JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING */

  lexer_skip_spaces (context_p);

  context_p->token.keyword_type = LEXER_EOS;
  context_p->token.line = context_p->line;
  context_p->token.column = context_p->column;

  length = (size_t) (context_p->source_end_p - context_p->source_p);
  if (length == 0)
  {
    context_p->token.type = LEXER_EOS;
    return;
  }

  if (lexer_parse_identifier (context_p, LEXER_PARSE_CHECK_KEYWORDS))
  {
    return;
  }

  if (context_p->source_p[0] >= LIT_CHAR_0 && context_p->source_p[0] <= LIT_CHAR_9)
  {
    lexer_parse_number (context_p);
    return;
  }

  switch (context_p->source_p[0])
  {
    LEXER_TYPE_A_TOKEN (LIT_CHAR_LEFT_BRACE, LEXER_LEFT_BRACE);
    LEXER_TYPE_A_TOKEN (LIT_CHAR_LEFT_PAREN, LEXER_LEFT_PAREN);
    LEXER_TYPE_A_TOKEN (LIT_CHAR_LEFT_SQUARE, LEXER_LEFT_SQUARE);
    LEXER_TYPE_A_TOKEN (LIT_CHAR_RIGHT_BRACE, LEXER_RIGHT_BRACE);
    LEXER_TYPE_A_TOKEN (LIT_CHAR_RIGHT_PAREN, LEXER_RIGHT_PAREN);
    LEXER_TYPE_A_TOKEN (LIT_CHAR_RIGHT_SQUARE, LEXER_RIGHT_SQUARE);
    LEXER_TYPE_A_TOKEN (LIT_CHAR_SEMICOLON, LEXER_SEMICOLON);
    LEXER_TYPE_A_TOKEN (LIT_CHAR_COMMA, LEXER_COMMA);
#if JERRY_ESNEXT
    LEXER_TYPE_A_TOKEN (LIT_CHAR_HASHMARK, LEXER_HASHMARK);
#endif /* JERRY_ESNEXT */

    case (uint8_t) LIT_CHAR_DOT:
    {
      if (length >= 2 && (context_p->source_p[1] >= LIT_CHAR_0 && context_p->source_p[1] <= LIT_CHAR_9))
      {
        lexer_parse_number (context_p);
        return;
      }

#if JERRY_ESNEXT
      if (length >= 3 && context_p->source_p[1] == LIT_CHAR_DOT && context_p->source_p[2] == LIT_CHAR_DOT)
      {
        context_p->token.type = LEXER_THREE_DOTS;
        length = 3;
        break;
      }
#endif /* JERRY_ESNEXT */

      context_p->token.type = LEXER_DOT;
      length = 1;
      break;
    }

    case (uint8_t) LIT_CHAR_LESS_THAN:
    {
      if (length >= 2)
      {
        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS)
        {
          context_p->token.type = LEXER_LESS_EQUAL;
          length = 2;
          break;
        }

        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_LESS_THAN)
        {
          if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS)
          {
            context_p->token.type = LEXER_ASSIGN_LEFT_SHIFT;
            length = 3;
            break;
          }

          context_p->token.type = LEXER_LEFT_SHIFT;
          length = 2;
          break;
        }
      }

      context_p->token.type = LEXER_LESS;
      length = 1;
      break;
    }

    case (uint8_t) LIT_CHAR_GREATER_THAN:
    {
      if (length >= 2)
      {
        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS)
        {
          context_p->token.type = LEXER_GREATER_EQUAL;
          length = 2;
          break;
        }

        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_GREATER_THAN)
        {
          if (length >= 3)
          {
            if (context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS)
            {
              context_p->token.type = LEXER_ASSIGN_RIGHT_SHIFT;
              length = 3;
              break;
            }

            if (context_p->source_p[2] == (uint8_t) LIT_CHAR_GREATER_THAN)
            {
              if (length >= 4 && context_p->source_p[3] == (uint8_t) LIT_CHAR_EQUALS)
              {
                context_p->token.type = LEXER_ASSIGN_UNS_RIGHT_SHIFT;
                length = 4;
                break;
              }

              context_p->token.type = LEXER_UNS_RIGHT_SHIFT;
              length = 3;
              break;
            }
          }

          context_p->token.type = LEXER_RIGHT_SHIFT;
          length = 2;
          break;
        }
      }

      context_p->token.type = LEXER_GREATER;
      length = 1;
      break;
    }

    case (uint8_t) LIT_CHAR_EQUALS:
    {
      if (length >= 2)
      {
        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS)
        {
          if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS)
          {
            context_p->token.type = LEXER_STRICT_EQUAL;
            length = 3;
            break;
          }

          context_p->token.type = LEXER_EQUAL;
          length = 2;
          break;
        }

#if JERRY_ESNEXT
        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_GREATER_THAN)
        {
          context_p->token.type = LEXER_ARROW;
          length = 2;
          break;
        }
#endif /* JERRY_ESNEXT */
      }

      context_p->token.type = LEXER_ASSIGN;
      length = 1;
      break;
    }

    case (uint8_t) LIT_CHAR_EXCLAMATION:
    {
      if (length >= 2 && context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS)
      {
        if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS)
        {
          context_p->token.type = LEXER_STRICT_NOT_EQUAL;
          length = 3;
          break;
        }

        context_p->token.type = LEXER_NOT_EQUAL;
        length = 2;
        break;
      }

      context_p->token.type = LEXER_LOGICAL_NOT;
      length = 1;
      break;
    }

      LEXER_TYPE_C_TOKEN (LIT_CHAR_PLUS, LEXER_ADD, LIT_CHAR_EQUALS, LEXER_ASSIGN_ADD, LIT_CHAR_PLUS, LEXER_INCREASE)
      LEXER_TYPE_C_TOKEN (LIT_CHAR_MINUS,
                          LEXER_SUBTRACT,
                          LIT_CHAR_EQUALS,
                          LEXER_ASSIGN_SUBTRACT,
                          LIT_CHAR_MINUS,
                          LEXER_DECREASE)

    case (uint8_t) LIT_CHAR_ASTERISK:
    {
      if (length >= 2)
      {
        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_EQUALS)
        {
          context_p->token.type = LEXER_ASSIGN_MULTIPLY;
          length = 2;
          break;
        }

#if JERRY_ESNEXT
        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_ASTERISK)
        {
          if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS)
          {
            context_p->token.type = LEXER_ASSIGN_EXPONENTIATION;
            length = 3;
            break;
          }

          context_p->token.type = LEXER_EXPONENTIATION;
          length = 2;
          break;
        }
#endif /* JERRY_ESNEXT */
      }

      context_p->token.type = LEXER_MULTIPLY;
      length = 1;
      break;
    }

      LEXER_TYPE_B_TOKEN (LIT_CHAR_SLASH, LEXER_DIVIDE, LIT_CHAR_EQUALS, LEXER_ASSIGN_DIVIDE)
      LEXER_TYPE_B_TOKEN (LIT_CHAR_PERCENT, LEXER_MODULO, LIT_CHAR_EQUALS, LEXER_ASSIGN_MODULO)

      LEXER_TYPE_D_TOKEN (LIT_CHAR_AMPERSAND,
                          LEXER_BIT_AND,
                          LIT_CHAR_EQUALS,
                          LEXER_ASSIGN_BIT_AND,
                          LIT_CHAR_AMPERSAND,
                          LEXER_LOGICAL_AND,
                          LIT_CHAR_EQUALS,
                          LEXER_ASSIGN_LOGICAL_AND)
      LEXER_TYPE_D_TOKEN (LIT_CHAR_VLINE,
                          LEXER_BIT_OR,
                          LIT_CHAR_EQUALS,
                          LEXER_ASSIGN_BIT_OR,
                          LIT_CHAR_VLINE,
                          LEXER_LOGICAL_OR,
                          LIT_CHAR_EQUALS,
                          LEXER_ASSIGN_LOGICAL_OR)

      LEXER_TYPE_B_TOKEN (LIT_CHAR_CIRCUMFLEX, LEXER_BIT_XOR, LIT_CHAR_EQUALS, LEXER_ASSIGN_BIT_XOR)

      LEXER_TYPE_A_TOKEN (LIT_CHAR_TILDE, LEXER_BIT_NOT);
    case (uint8_t) (LIT_CHAR_QUESTION):
    {
#if JERRY_ESNEXT
      if (length >= 2)
      {
        if (context_p->source_p[1] == (uint8_t) LIT_CHAR_QUESTION)
        {
          if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS)
          {
            context_p->token.type = LEXER_ASSIGN_NULLISH_COALESCING;
            length = 3;
            break;
          }
          context_p->token.type = LEXER_NULLISH_COALESCING;
          length = 2;
          break;
        }
      }
#endif /* JERRY_ESNEXT */
      context_p->token.type = LEXER_QUESTION_MARK;
      length = 1;
      break;
    }

      LEXER_TYPE_A_TOKEN (LIT_CHAR_COLON, LEXER_COLON);

    case LIT_CHAR_SINGLE_QUOTE:
    case LIT_CHAR_DOUBLE_QUOTE:
#if JERRY_ESNEXT
    case LIT_CHAR_GRAVE_ACCENT:
#endif /* JERRY_ESNEXT */
    {
      lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
      return;
    }

    default:
    {
      parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER);
    }
  }

  context_p->source_p += length;
  PARSER_PLUS_EQUAL_LC (context_p->column, length);
} /* lexer_next_token */


lexer_construct_class_static_block_function (parser_context_t *context_p) /**< context */
{
  ecma_compiled_code_t *compiled_code_p;
  lexer_literal_t *literal_p;
  uint16_t result_index;

  literal_p = lexer_construct_unused_literal (context_p);
  result_index = context_p->literal_count;
  context_p->literal_count++;

  parser_flush_cbc (context_p);
  compiled_code_p = parser_parse_class_static_block (context_p);

  literal_p->u.bytecode_p = compiled_code_p;
  literal_p->type = LEXER_FUNCTION_LITERAL;

  return result_index;
} /* lexer_construct_class_static_block_function */


lexer_expect_identifier (parser_context_t *context_p, /**< context */
                         uint8_t literal_type) /**< literal type */
{
  JERRY_ASSERT (literal_type == LEXER_STRING_LITERAL || literal_type == LEXER_IDENT_LITERAL
                || literal_type == LEXER_NEW_IDENT_LITERAL);

  lexer_skip_spaces (context_p);
  context_p->token.keyword_type = LEXER_EOS;
  context_p->token.line = context_p->line;
  context_p->token.column = context_p->column;

  if (context_p->source_p < context_p->source_end_p
      && lexer_parse_identifier (
        context_p,
        (literal_type != LEXER_STRING_LITERAL ? LEXER_PARSE_CHECK_KEYWORDS : LEXER_PARSE_NO_OPTS)))
  {
    if (context_p->token.type == LEXER_LITERAL)
    {
      JERRY_ASSERT (context_p->token.lit_location.type == LEXER_IDENT_LITERAL);

      lexer_construct_literal_object (context_p, &context_p->token.lit_location, literal_type);

      if (literal_type != LEXER_STRING_LITERAL && (context_p->status_flags & PARSER_IS_STRICT))
      {
        if (context_p->token.keyword_type == LEXER_KEYW_EVAL)
        {
          parser_raise_error (context_p, PARSER_ERR_EVAL_NOT_ALLOWED);
        }
        else if (context_p->token.keyword_type == LEXER_KEYW_ARGUMENTS)
        {
          parser_raise_error (context_p, PARSER_ERR_ARGUMENTS_NOT_ALLOWED);
        }
      }
      return;
    }
  }
#if JERRY_MODULE_SYSTEM
  else if (context_p->status_flags & PARSER_MODULE_DEFAULT_CLASS_OR_FUNC)
  {
    /* When parsing default exports for modules, it is not required by functions or classes to have identifiers.
     * In this case we use a synthetic name for them. */
    context_p->token.type = LEXER_LITERAL;
    context_p->token.lit_location = lexer_default_literal;
    lexer_construct_literal_object (context_p, &context_p->token.lit_location, literal_type);
    context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC);
    return;
  }
#endif /* JERRY_MODULE_SYSTEM */

#if JERRY_ESNEXT
  if (context_p->token.type == LEXER_KEYW_YIELD)
  {
    parser_raise_error (context_p, PARSER_ERR_YIELD_NOT_ALLOWED);
  }
  if (context_p->token.type == LEXER_KEYW_AWAIT)
  {
    parser_raise_error (context_p, PARSER_ERR_AWAIT_NOT_ALLOWED);
  }
#endif /* JERRY_ESNEXT */
  parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED);
} /* lexer_expect_identifier */


lexer_parse_identifier (parser_context_t *context_p, /**< context */
                        lexer_parse_options_t options) /**< check keywords */
{
  /* Only very few identifiers contains \u escape sequences. */
  const uint8_t *source_p = context_p->source_p;
  /* Note: newline or tab cannot be part of an identifier. */
  parser_line_counter_t column = context_p->column;
  const uint8_t *source_end_p = context_p->source_end_p;
  size_t length = 0;
  lexer_lit_location_flags_t status_flags = LEXER_LIT_LOCATION_IS_ASCII;

  do
  {
    if (*source_p == LIT_CHAR_BACKSLASH)
    {
      /* After a backslash an identifier must start. */
      lit_code_point_t code_point = UINT32_MAX;
      uint32_t escape_length = 6;

      if (options & (LEXER_PARSE_CHECK_START_AND_RETURN | LEXER_PARSE_CHECK_PART_AND_RETURN))
      {
        return true;
      }

      status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE;

#if JERRY_ESNEXT
      if (source_p + 5 <= source_end_p && source_p[1] == LIT_CHAR_LOWERCASE_U)
      {
        if (source_p[2] == LIT_CHAR_LEFT_BRACE)
        {
          code_point = lexer_hex_in_braces_to_code_point (source_p + 3, source_end_p, &escape_length);
        }
        else if (source_p + 6 <= source_end_p)
        {
          code_point = lexer_hex_to_code_point (source_p + 2, 4);
        }
      }
#else /* !JERRY_ESNEXT */
      if (source_p + 6 <= source_end_p && source_p[1] == LIT_CHAR_LOWERCASE_U)
      {
        code_point = lexer_hex_to_code_point (source_p + 2, 4);
      }
#endif /* JERRY_ESNEXT */

      if (code_point == UINT32_MAX)
      {
        context_p->source_p = source_p;
        context_p->token.column = column;
        parser_raise_error (context_p, PARSER_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE);
      }

      if (length == 0)
      {
        if (!lit_code_point_is_identifier_start (code_point))
        {
          parser_raise_error (context_p, PARSER_ERR_INVALID_IDENTIFIER_START);
        }
      }
      else
      {
        if (!lit_code_point_is_identifier_part (code_point))
        {
          parser_raise_error (context_p, PARSER_ERR_INVALID_IDENTIFIER_PART);
        }
      }

      length += lit_code_point_get_cesu8_length (code_point);
      source_p += escape_length;
      PARSER_PLUS_EQUAL_LC (column, escape_length);
      continue;
    }

    lit_code_point_t code_point = *source_p;
    lit_utf8_size_t utf8_length = 1, decoded_length = 1, char_count = 1;

    if (JERRY_UNLIKELY (code_point >= LIT_UTF8_2_BYTE_MARKER))
    {
      status_flags &= (uint32_t) ~LEXER_LIT_LOCATION_IS_ASCII;

#if JERRY_ESNEXT
      utf8_length = lit_read_code_point_from_utf8 (source_p, (lit_utf8_size_t) (source_end_p - source_p), &code_point);
      decoded_length = utf8_length;

      /* Only ES2015+ supports code points outside of the basic plane which can be part of an identifier. */
      if ((code_point >= LIT_UTF16_HIGH_SURROGATE_MIN && code_point <= LIT_UTF16_HIGH_SURROGATE_MAX)
          && source_p + 3 < source_end_p)
      {
        lit_code_point_t low_surrogate;
        lit_read_code_point_from_utf8 (source_p + 3, (lit_utf8_size_t) (source_end_p - (source_p + 3)), &low_surrogate);

        if (low_surrogate >= LIT_UTF16_LOW_SURROGATE_MIN && low_surrogate <= LIT_UTF16_LOW_SURROGATE_MAX)
        {
          code_point = lit_convert_surrogate_pair_to_code_point ((ecma_char_t) code_point, (ecma_char_t) low_surrogate);
          utf8_length = 2 * 3;
          decoded_length = 2 * 3;
          char_count = 2;
        }
      }
      else if (source_p[0] >= LIT_UTF8_4_BYTE_MARKER)
      {
        decoded_length = 2 * 3;
        status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE;
#if JERRY_FUNCTION_TO_STRING
        context_p->global_status_flags |= ECMA_PARSE_INTERNAL_HAS_4_BYTE_MARKER;
#endif /* JERRY_FUNCTION_TO_STRING */
      }
#else /* !JERRY_ESNEXT */
      if (code_point < LIT_UTF8_4_BYTE_MARKER)
      {
        utf8_length =
          lit_read_code_point_from_utf8 (source_p, (lit_utf8_size_t) (source_end_p - source_p), &code_point);
        decoded_length = utf8_length;
      }
      else
      {
        code_point = 0;
      }
#endif /* JERRY_ESNEXT */
    }

    if (length == 0)
    {
      if (JERRY_UNLIKELY (options & (LEXER_PARSE_CHECK_START_AND_RETURN | LEXER_PARSE_CHECK_PART_AND_RETURN)))
      {
        if (options & LEXER_PARSE_CHECK_START_AND_RETURN)
        {
          return lit_code_point_is_identifier_start (code_point);
        }
        else
        {
          return lit_code_point_is_identifier_part (code_point);
        }
      }

      if (!lit_code_point_is_identifier_start (code_point))
      {
        return false;
      }
    }
    else if (!lit_code_point_is_identifier_part (code_point))
    {
      break;
    }

    source_p += utf8_length;
    length += decoded_length;
    PARSER_PLUS_EQUAL_LC (column, char_count);
  } while (source_p < source_end_p);

  JERRY_ASSERT (length > 0);

  context_p->token.type = LEXER_LITERAL;
  context_p->token.lit_location.type = LEXER_IDENT_LITERAL;
  context_p->token.lit_location.status_flags = (uint8_t) status_flags;

  context_p->token.column = context_p->column;
  context_p->token.lit_location.char_p = context_p->source_p;
  context_p->token.lit_location.length = (prop_length_t) length;

  if (JERRY_UNLIKELY (length > PARSER_MAXIMUM_IDENT_LENGTH))
  {
    parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_TOO_LONG);
  }

  /* Check keywords. */
  if ((options & LEXER_PARSE_CHECK_KEYWORDS)
      && (length >= LEXER_KEYWORD_MIN_LENGTH && length <= LEXER_KEYWORD_MAX_LENGTH))
  {
    const uint8_t *ident_start_p = context_p->source_p;
    uint8_t buffer_p[LEXER_KEYWORD_MAX_LENGTH];

    if (JERRY_UNLIKELY (context_p->token.lit_location.status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))
    {
      lexer_convert_ident_to_cesu8 (buffer_p, ident_start_p, (prop_length_t) length);
      ident_start_p = buffer_p;
    }

    const keyword_string_t *keyword_list_p = keyword_strings_list[length - LEXER_KEYWORD_MIN_LENGTH];

    int start = 0;
    int end = keyword_lengths_list[length - LEXER_KEYWORD_MIN_LENGTH];
    int middle = end / 2;

    do
    {
      const keyword_string_t *keyword_p = keyword_list_p + middle;
      int compare_result = ident_start_p[0] - keyword_p->keyword_p[0];

      if (compare_result == 0)
      {
        compare_result = memcmp (ident_start_p, keyword_p->keyword_p, length);

        if (compare_result == 0)
        {
          context_p->token.keyword_type = (uint8_t) keyword_p->type;

          if (JERRY_LIKELY (keyword_p->type < LEXER_FIRST_NON_RESERVED_KEYWORD))
          {
#if JERRY_ESNEXT
            if (JERRY_UNLIKELY (keyword_p->type == LEXER_KEYW_AWAIT))
            {
              if (!(context_p->status_flags & (PARSER_IS_ASYNC_FUNCTION | PARSER_IS_CLASS_STATIC_BLOCK))
                  && !(context_p->global_status_flags & ECMA_PARSE_MODULE))
              {
                break;
              }

              if (context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD)
              {
                if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p))
                {
                  parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD);
                }
                parser_raise_error (context_p, PARSER_ERR_AWAIT_NOT_ALLOWED);
              }

              context_p->token.type = (uint8_t) LEXER_KEYW_AWAIT;
              break;
            }
#endif /* JERRY_ESNEXT */

            if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p))
            {
              /* Escape sequences are not allowed in a keyword. */
              parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD);
            }

            context_p->token.type = (uint8_t) keyword_p->type;
            break;
          }

#if JERRY_ESNEXT
          if (keyword_p->type == LEXER_KEYW_LET && (context_p->status_flags & PARSER_IS_STRICT))
          {
            if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p))
            {
              parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD);
            }

            context_p->token.type = (uint8_t) LEXER_KEYW_LET;
            break;
          }

          if (keyword_p->type == LEXER_KEYW_YIELD && (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION))
          {
            if (context_p->status_flags & PARSER_DISALLOW_AWAIT_YIELD)
            {
              if (LEXER_CHECK_INVALID_KEYWORD (ident_start_p, buffer_p))
              {
                parser_raise_error (context_p, PARSER_ERR_INVALID_KEYWORD);
              }
              parser_raise_error (context_p, PARSER_ERR_YIELD_NOT_ALLOWED);
            }

            context_p->token.type = (uint8_t) LEXER_KEYW_YIELD;
            break;
          }

          if (keyword_p->type == LEXER_KEYW_ARGUMENTS && (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD))
          {
            parser_raise_error (context_p, PARSER_ERR_ARGUMENTS_IN_CLASS_FIELD);
          }
#endif /* JERRY_ESNEXT */

          if (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD && (context_p->status_flags & PARSER_IS_STRICT)
              && !(options & LEXER_PARSE_NO_STRICT_IDENT_ERROR))
          {
            parser_raise_error (context_p, PARSER_ERR_STRICT_IDENT_NOT_ALLOWED);
          }
          break;
        }
      }

      if (compare_result > 0)
      {
        start = middle + 1;
      }
      else
      {
        JERRY_ASSERT (compare_result < 0);
        end = middle;
      }

      middle = (start + end) / 2;
    } while (start < end);
  }

  context_p->source_p = source_p;
  context_p->column = column;
  return true;
} /* lexer_parse_identifier */


lexer_token_is_identifier (parser_context_t *context_p, /**< context */
                           const char *identifier_p, /**< identifier */
                           size_t identifier_length) /**< identifier length */
{
  /* Checking has_escape is unnecessary because memcmp will fail if escape sequences are present. */
  return (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL
          && context_p->token.lit_location.length == identifier_length
          && memcmp (context_p->token.lit_location.char_p, identifier_p, identifier_length) == 0);
} /* lexer_token_is_identifier */


lexer_check_next_character (parser_context_t *context_p, /**< context */
                            lit_utf8_byte_t character) /**< specified character */
{
  if (!(context_p->token.flags & LEXER_NO_SKIP_SPACES))
  {
    lexer_skip_spaces (context_p);
    context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES);
  }

  return (context_p->source_p < context_p->source_end_p && context_p->source_p[0] == (uint8_t) character);
} /* lexer_check_next_character */


lexer_convert_literal_to_chars (parser_context_t *context_p, /**< context */
                                const lexer_lit_location_t *literal_p, /**< literal location */
                                uint8_t *local_byte_array_p, /**< local byte array to store chars */
                                lexer_string_options_t opts) /**< options */
{
  JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL);

  if (!(literal_p->status_flags & LEXER_LIT_LOCATION_HAS_ESCAPE))
  {
    return literal_p->char_p;
  }

  uint8_t *destination_start_p;
  if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE)
  {
    context_p->u.allocated_buffer_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length);
    context_p->allocated_buffer_size = literal_p->length;
    destination_start_p = context_p->u.allocated_buffer_p;
  }
  else
  {
    destination_start_p = local_byte_array_p;
  }

  if (literal_p->type == LEXER_IDENT_LITERAL)
  {
    lexer_convert_ident_to_cesu8 (destination_start_p, literal_p->char_p, literal_p->length);
    return destination_start_p;
  }

  const uint8_t *source_p = literal_p->char_p;
  uint8_t *destination_p = destination_start_p;

  uint8_t str_end_character = source_p[-1];

#if JERRY_ESNEXT
  if (str_end_character == LIT_CHAR_RIGHT_BRACE)
  {
    str_end_character = LIT_CHAR_GRAVE_ACCENT;
  }

  bool is_raw = (opts & LEXER_STRING_RAW) != 0;
#else /* !JERRY_ESNEXT */
  JERRY_UNUSED (opts);
  bool is_raw = false;
#endif /* JERRY_ESNEXT */

  while (true)
  {
    if (*source_p == str_end_character)
    {
      break;
    }

    if (*source_p == LIT_CHAR_BACKSLASH && !is_raw)
    {
      uint8_t conv_character;

      source_p++;
      JERRY_ASSERT (source_p < context_p->source_end_p);

      /* Newline is ignored. */
      if (*source_p == LIT_CHAR_CR)
      {
        source_p++;
        JERRY_ASSERT (source_p < context_p->source_end_p);

        if (*source_p == LIT_CHAR_LF)
        {
          source_p++;
        }
        continue;
      }
      else if (*source_p == LIT_CHAR_LF)
      {
        source_p++;
        continue;
      }
      else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))
      {
        source_p += 3;
        continue;
      }

      if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_3)
      {
        lit_code_point_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);

        source_p++;
        JERRY_ASSERT (source_p < context_p->source_end_p);

        if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
        {
          octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
          source_p++;
          JERRY_ASSERT (source_p < context_p->source_end_p);

          if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
          {
            octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
            source_p++;
            JERRY_ASSERT (source_p < context_p->source_end_p);
          }
        }

        destination_p += lit_code_point_to_cesu8_bytes (destination_p, octal_number);
        continue;
      }

      if (*source_p >= LIT_CHAR_4 && *source_p <= LIT_CHAR_7)
      {
        uint32_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);

        source_p++;
        JERRY_ASSERT (source_p < context_p->source_end_p);

        if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
        {
          octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
          source_p++;
          JERRY_ASSERT (source_p < context_p->source_end_p);
        }

        *destination_p++ = (uint8_t) octal_number;
        continue;
      }

      if (*source_p == LIT_CHAR_LOWERCASE_X || *source_p == LIT_CHAR_LOWERCASE_U)
      {
        source_p++;
        destination_p += lit_code_point_to_cesu8_bytes (destination_p, lexer_unchecked_hex_to_character (&source_p));
        continue;
      }

      conv_character = *source_p;
      switch (*source_p)
      {
        case LIT_CHAR_LOWERCASE_B:
        {
          conv_character = 0x08;
          break;
        }
        case LIT_CHAR_LOWERCASE_T:
        {
          conv_character = 0x09;
          break;
        }
        case LIT_CHAR_LOWERCASE_N:
        {
          conv_character = 0x0a;
          break;
        }
        case LIT_CHAR_LOWERCASE_V:
        {
          conv_character = 0x0b;
          break;
        }
        case LIT_CHAR_LOWERCASE_F:
        {
          conv_character = 0x0c;
          break;
        }
        case LIT_CHAR_LOWERCASE_R:
        {
          conv_character = 0x0d;
          break;
        }
      }

      if (conv_character != *source_p)
      {
        *destination_p++ = conv_character;
        source_p++;
        continue;
      }
    }
#if JERRY_ESNEXT
    else if (str_end_character == LIT_CHAR_GRAVE_ACCENT)
    {
      if (source_p[0] == LIT_CHAR_DOLLAR_SIGN && source_p[1] == LIT_CHAR_LEFT_BRACE)
      {
        source_p++;
        JERRY_ASSERT (source_p < context_p->source_end_p);
        break;
      }
      if (*source_p == LIT_CHAR_CR)
      {
        *destination_p++ = LIT_CHAR_LF;
        source_p++;
        if (*source_p != str_end_character && *source_p == LIT_CHAR_LF)
        {
          source_p++;
        }
        continue;
      }
      if ((*source_p == LIT_CHAR_BACKSLASH) && is_raw)
      {
        JERRY_ASSERT (source_p + 1 < context_p->source_end_p);
        if ((*(source_p + 1) == LIT_CHAR_GRAVE_ACCENT) || (*(source_p + 1) == LIT_CHAR_BACKSLASH))
        {
          *destination_p++ = *source_p++;
          *destination_p++ = *source_p++;
          continue;
        }
      }
    }
#endif /* JERRY_ESNEXT */

    if (*source_p >= LIT_UTF8_4_BYTE_MARKER)
    {
      /* Processing 4 byte unicode sequence (even if it is
       * after a backslash). Always converted to two 3 byte
       * long sequence. */
      lit_four_byte_utf8_char_to_cesu8 (destination_p, source_p);

      destination_p += 6;
      source_p += 4;
      continue;
    }

    *destination_p++ = *source_p++;

    /* There is no need to check the source_end_p
     * since the string is terminated by a quotation mark. */
    while (IS_UTF8_INTERMEDIATE_OCTET (*source_p))
    {
      *destination_p++ = *source_p++;
    }
  }

  JERRY_ASSERT (destination_p == destination_start_p + literal_p->length);

  return destination_start_p;
} /* lexer_convert_literal_to_chars */


lexer_scan_identifier (parser_context_t *context_p, /**< context */
                       lexer_parse_options_t opts) /**< identifier parse options */
{
  lexer_skip_spaces (context_p);
  context_p->token.keyword_type = LEXER_EOS;
  context_p->token.line = context_p->line;
  context_p->token.column = context_p->column;

  if (context_p->source_p < context_p->source_end_p && lexer_parse_identifier (context_p, opts))
  {
    return true;
  }

  context_p->token.flags |= LEXER_NO_SKIP_SPACES;
  lexer_next_token (context_p);
  return false;
} /* lexer_scan_identifier */


lexer_init_line_info (parser_context_t *context_p) /**< context */
{
  context_p->line = 1;
  context_p->column = 1;

  const jerry_parse_options_t *options_p = context_p->options_p;

  if (options_p != NULL && (options_p->options & JERRY_PARSE_HAS_START))
  {
    if (options_p->start_line > 0)
    {
      context_p->line = options_p->start_line;
    }

    if (options_p->start_column > 0)
    {
      context_p->column = options_p->start_column;
    }
  }
} /* lexer_init_line_info */


lexer_hex_in_braces_to_code_point (const uint8_t *source_p, /**< current source position */
                                   const uint8_t *source_end_p, /**< source end */
                                   uint32_t *length_p) /**< [out] length of the sequence */
{
  lit_code_point_t result = 0;
  /* Four is the size of \u{} sequence. */
  uint32_t length = 4;

  JERRY_ASSERT (source_p[-1] == LIT_CHAR_LEFT_BRACE);
  JERRY_ASSERT (source_p < source_end_p);

  do
  {
    uint32_t byte = *source_p++;

    result <<= 4;

    if (byte >= LIT_CHAR_0 && byte <= LIT_CHAR_9)
    {
      result += byte - LIT_CHAR_0;
    }
    else
    {
      byte = LEXER_TO_ASCII_LOWERCASE (byte);
      if (byte >= LIT_CHAR_LOWERCASE_A && byte <= LIT_CHAR_LOWERCASE_F)
      {
        result += byte - (LIT_CHAR_LOWERCASE_A - 10);
      }
      else
      {
        return UINT32_MAX;
      }
    }

    if (result >= (LIT_UNICODE_CODE_POINT_MAX + 1) || source_p >= source_end_p)
    {
      return UINT32_MAX;
    }
    length++;
  } while (*source_p != LIT_CHAR_RIGHT_BRACE);

  *length_p = length;
  return result;
} /* lexer_hex_in_braces_to_code_point */


lexer_string_is_directive (parser_context_t *context_p) /**< context */
{
  return (context_p->token.type == LEXER_SEMICOLON || context_p->token.type == LEXER_RIGHT_BRACE
          || context_p->token.type == LEXER_EOS
          || ((context_p->token.flags & LEXER_WAS_NEWLINE) && !LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)
              && context_p->token.type != LEXER_LEFT_PAREN && context_p->token.type != LEXER_LEFT_SQUARE
              && context_p->token.type != LEXER_DOT));
} /* lexer_string_is_directive */


lexer_current_is_literal (parser_context_t *context_p, /**< context */
                          const lexer_lit_location_t *right_ident_p) /**< identifier */
{
  JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL);

  lexer_lit_location_t *left_ident_p = &context_p->token.lit_location;

  JERRY_ASSERT (left_ident_p->length > 0 && right_ident_p->length > 0);

  if (left_ident_p->length != right_ident_p->length)
  {
    return false;
  }

  if (!((left_ident_p->status_flags | right_ident_p->status_flags) & LEXER_LIT_LOCATION_HAS_ESCAPE))
  {
    return memcmp (left_ident_p->char_p, right_ident_p->char_p, left_ident_p->length) == 0;
  }

  return lexer_compare_identifiers (context_p, left_ident_p, right_ident_p);
} /* lexer_current_is_literal */


lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
                                uint32_t ident_opts) /**< lexer_obj_ident_opts_t option bits */
{
  lexer_skip_spaces (context_p);

  if (context_p->source_p >= context_p->source_end_p)
  {
    parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED);
  }

  context_p->token.keyword_type = LEXER_EOS;
  context_p->token.line = context_p->line;
  context_p->token.column = context_p->column;
  bool create_literal_object = false;

  JERRY_ASSERT ((ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) || !(ident_opts & LEXER_OBJ_IDENT_CLASS_NO_STATIC));

#if JERRY_FUNCTION_TO_STRING
  if (ident_opts & LEXER_OBJ_IDENT_SET_FUNCTION_START)
  {
    context_p->function_start_p = context_p->source_p;
  }
#endif /* JERRY_FUNCTION_TO_STRING */

  if (lexer_parse_identifier (context_p, LEXER_PARSE_NO_OPTS))
  {
    if (!(ident_opts & (LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJECT_PATTERN)))
    {
      lexer_skip_spaces (context_p);
      context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES);

      if (context_p->source_p < context_p->source_end_p
#if JERRY_ESNEXT
          && context_p->source_p[0] != LIT_CHAR_COMMA && context_p->source_p[0] != LIT_CHAR_RIGHT_BRACE
          && context_p->source_p[0] != LIT_CHAR_LEFT_PAREN && context_p->source_p[0] != LIT_CHAR_SEMICOLON
          && context_p->source_p[0] != LIT_CHAR_EQUALS
#endif /* JERRY_ESNEXT */
          && context_p->source_p[0] != LIT_CHAR_COLON)
      {
        if (lexer_compare_literal_to_string (context_p, "get", 3))
        {
          context_p->token.type = LEXER_PROPERTY_GETTER;
          return;
        }

        if (lexer_compare_literal_to_string (context_p, "set", 3))
        {
          context_p->token.type = LEXER_PROPERTY_SETTER;
          return;
        }

#if JERRY_ESNEXT
        if (lexer_compare_literal_to_string (context_p, "async", 5))
        {
          context_p->token.type = LEXER_KEYW_ASYNC;
          return;
        }

        if (ident_opts & LEXER_OBJ_IDENT_CLASS_NO_STATIC)
        {
          if (lexer_compare_literal_to_string (context_p, "static", 6))
          {
            context_p->token.type = LEXER_KEYW_STATIC;
          }
          return;
        }
#endif /* JERRY_ESNEXT */
      }
    }

    create_literal_object = true;
  }
#if JERRY_ESNEXT
  else if (ident_opts & LEXER_OBJ_IDENT_CLASS_PRIVATE)
  {
    parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER);
  }
#endif /* JERRY_ESNEXT */
  else
  {
    switch (context_p->source_p[0])
    {
      case LIT_CHAR_DOUBLE_QUOTE:
      case LIT_CHAR_SINGLE_QUOTE:
      {
        lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
        create_literal_object = true;
        break;
      }
#if JERRY_ESNEXT
      case LIT_CHAR_LEFT_SQUARE:
      {
#if JERRY_FUNCTION_TO_STRING
        const uint8_t *function_start_p = context_p->function_start_p;
#endif /* JERRY_FUNCTION_TO_STRING */

        lexer_consume_next_character (context_p);

        lexer_next_token (context_p);
        parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);

        if (context_p->token.type != LEXER_RIGHT_SQUARE)
        {
          parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED);
        }

#if JERRY_FUNCTION_TO_STRING
        context_p->function_start_p = function_start_p;
#endif /* JERRY_FUNCTION_TO_STRING */
        return;
      }
      case LIT_CHAR_ASTERISK:
      {
        if (ident_opts & (LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJECT_PATTERN))
        {
          break;
        }

        context_p->token.type = LEXER_MULTIPLY;
        lexer_consume_next_character (context_p);
        return;
      }
      case LIT_CHAR_HASHMARK:
      {
        if (ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER)
        {
          context_p->token.type = LEXER_HASHMARK;
          return;
        }

        break;
      }
#endif /* JERRY_ESNEXT */
      case LIT_CHAR_LEFT_BRACE:
      {
        const uint32_t static_block_flags =
          (LEXER_OBJ_IDENT_CLASS_NO_STATIC | LEXER_OBJ_IDENT_CLASS_PRIVATE | LEXER_OBJ_IDENT_CLASS_IDENTIFIER);

        if ((ident_opts & static_block_flags) == LEXER_OBJ_IDENT_CLASS_IDENTIFIER)
        {
          context_p->token.type = LEXER_LEFT_BRACE;
          lexer_consume_next_character (context_p);
          return;
        }

        break;
      }
      case LIT_CHAR_RIGHT_BRACE:
      {
        if (ident_opts & LEXER_OBJ_IDENT_ONLY_IDENTIFIERS)
        {
          break;
        }

        context_p->token.type = LEXER_RIGHT_BRACE;
        lexer_consume_next_character (context_p);
        return;
      }
#if JERRY_ESNEXT
      case LIT_CHAR_DOT:
      {
        if (!(context_p->source_p + 1 >= context_p->source_end_p || lit_char_is_decimal_digit (context_p->source_p[1])))
        {
          if ((ident_opts & ((uint32_t) ~(LEXER_OBJ_IDENT_OBJECT_PATTERN | LEXER_OBJ_IDENT_SET_FUNCTION_START)))
              || context_p->source_p + 2 >= context_p->source_end_p || context_p->source_p[1] != LIT_CHAR_DOT
              || context_p->source_p[2] != LIT_CHAR_DOT)
          {
            break;
          }

          context_p->token.type = LEXER_THREE_DOTS;
          context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES;
          PARSER_PLUS_EQUAL_LC (context_p->column, 3);
          context_p->source_p += 3;
          return;
        }
        /* FALLTHRU */
      }
#endif /* JERRY_ESNEXT */
      default:
      {
        const uint8_t *char_p = context_p->source_p;

        if (char_p[0] == LIT_CHAR_DOT)
        {
          char_p++;
        }

        if (char_p < context_p->source_end_p && char_p[0] >= LIT_CHAR_0 && char_p[0] <= LIT_CHAR_9)
        {
          lexer_parse_number (context_p);

          if (!(ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER))
          {
            lexer_construct_number_object (context_p, false, false);
          }
          return;
        }
        break;
      }
    }
  }

  if (create_literal_object)
  {
#if JERRY_ESNEXT
    if (ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER)
    {
      return;
    }

    if (ident_opts & LEXER_OBJ_IDENT_CLASS_PRIVATE)
    {
      parser_resolve_private_identifier (context_p);
      return;
    }
#endif /* JERRY_ESNEXT */

    lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL);
    return;
  }

  parser_raise_error (context_p, PARSER_ERR_PROPERTY_IDENTIFIER_EXPECTED);
} /* lexer_expect_object_literal_id */


lexer_hex_to_code_point (const uint8_t *source_p, /**< current source position */
                         parser_line_counter_t length) /**< source length */
{
  lit_code_point_t result = 0;

  do
  {
    uint32_t byte = *source_p++;

    result <<= 4;

    if (byte >= LIT_CHAR_0 && byte <= LIT_CHAR_9)
    {
      result += byte - LIT_CHAR_0;
    }
    else
    {
      byte = LEXER_TO_ASCII_LOWERCASE (byte);
      if (byte >= LIT_CHAR_LOWERCASE_A && byte <= LIT_CHAR_LOWERCASE_F)
      {
        result += byte - (LIT_CHAR_LOWERCASE_A - 10);
      }
      else
      {
        return UINT32_MAX;
      }
    }
  } while (--length > 0);

  return result;
} /* lexer_hex_to_code_point */


lexer_check_property_modifier (parser_context_t *context_p) /**< context */
{
  JERRY_ASSERT (!(context_p->token.flags & LEXER_NO_SKIP_SPACES));
  JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL);

  lexer_skip_spaces (context_p);
  context_p->token.flags = (uint8_t) (context_p->token.flags | LEXER_NO_SKIP_SPACES);

  if (context_p->source_p >= context_p->source_end_p
#if JERRY_ESNEXT
      || context_p->source_p[0] == LIT_CHAR_COMMA || context_p->source_p[0] == LIT_CHAR_RIGHT_BRACE
      || context_p->source_p[0] == LIT_CHAR_LEFT_PAREN || context_p->source_p[0] == LIT_CHAR_EQUALS
#endif /* JERRY_ESNEXT */
      || context_p->source_p[0] == LIT_CHAR_COLON)
  {
    return;
  }

  if (lexer_compare_literal_to_string (context_p, "get", 3))
  {
    context_p->token.type = LEXER_PROPERTY_GETTER;
    return;
  }

  if (lexer_compare_literal_to_string (context_p, "set", 3))
  {
    context_p->token.type = LEXER_PROPERTY_SETTER;
    return;
  }

#if JERRY_ESNEXT
  if (lexer_compare_literal_to_string (context_p, "async", 5))
  {
    context_p->token.type = LEXER_KEYW_ASYNC;
    return;
  }
#endif /* JERRY_ESNEXT */
} /* lexer_check_property_modifier */