static void ldapsrv_process_call_trigger(struct tevent_req *req,
					 void *private_data)
{
	struct ldapsrv_process_call_state *state =
		tevent_req_data(req,
		struct ldapsrv_process_call_state);
	struct ldapsrv_connection *conn = state->call->conn;
	NTSTATUS status;

	if (conn->deferred_expire_disconnect != NULL) {
		/*
		 * Just drop this on the floor
		 */
		tevent_req_done(req);
		return;
	}

	/* make the call */
	status = ldapsrv_do_call(state->call);

	if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
		/*
		 * For testing purposes, defer the TCP disconnect
		 * after having sent the msgid 0
		 * 1.3.6.1.4.1.1466.20036 exop response. LDAP clients
		 * should not wait for the TCP connection to close but
		 * handle this packet equivalent to a TCP
		 * disconnect. This delay enables testing both cases
		 * in LDAP client libraries.
		 */

		int defer_msec = lpcfg_parm_int(
			conn->lp_ctx,
			NULL,
			"ldap_server",
			"delay_expire_disconnect",
			0);

		conn->deferred_expire_disconnect = tevent_wakeup_send(
			conn,
			conn->connection->event.ctx,
			timeval_current_ofs_msec(defer_msec));
		if (tevent_req_nomem(conn->deferred_expire_disconnect, req)) {
			return;
		}
		tevent_req_set_callback(
			conn->deferred_expire_disconnect,
			ldapsrv_disconnect_ticket_expired,
			conn);

		tevent_req_done(req);
		return;
	}

	if (!NT_STATUS_IS_OK(status)) {
		tevent_req_nterror(req, status);
		return;
	}

	tevent_req_done(req);
}


CVE-2021-3670 ldap_server: Ensure value of MaxQueryDuration is greater than zero

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14694

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
(cherry picked from commit e1ab0c43629686d1d2c0b0b2bcdc90057a792049)

NTSTATUS server_service_ldap_init(TALLOC_CTX *ctx)
{
	static const struct service_details details = {
		.inhibit_fork_on_accept = false,
		.inhibit_pre_fork = false,
		.task_init = ldapsrv_task_init,
		.post_fork = ldapsrv_post_fork,
	};
	return register_server_service(ctx, "ldap", &details);
}


static void ldapsrv_terminate_connection(struct ldapsrv_connection *conn,
					 const char *reason)
{
	struct tevent_req *subreq;

	if (conn->limits.reason) {
		return;
	}

	DLIST_REMOVE(conn->service->connections, conn);

	conn->limits.endtime = timeval_current_ofs(0, 500);

	tevent_queue_stop(conn->sockets.send_queue);
	TALLOC_FREE(conn->sockets.read_req);
	TALLOC_FREE(conn->deferred_expire_disconnect);
	if (conn->active_call) {
		tevent_req_cancel(conn->active_call);
		conn->active_call = NULL;
	}

	conn->limits.reason = talloc_strdup(conn, reason);
	if (conn->limits.reason == NULL) {
		TALLOC_FREE(conn->sockets.tls);
		TALLOC_FREE(conn->sockets.sasl);
		TALLOC_FREE(conn->sockets.raw);
		stream_terminate_connection(conn->connection, reason);
		return;
	}

	subreq = tstream_disconnect_send(conn,
					 conn->connection->event.ctx,
					 conn->sockets.active);
	if (subreq == NULL) {
		TALLOC_FREE(conn->sockets.tls);
		TALLOC_FREE(conn->sockets.sasl);
		TALLOC_FREE(conn->sockets.raw);
		stream_terminate_connection(conn->connection, reason);
		return;
	}
	tevent_req_set_endtime(subreq,
			       conn->connection->event.ctx,
			       conn->limits.endtime);
	tevent_req_set_callback(subreq, ldapsrv_terminate_connection_done, conn);
}


static NTSTATUS ldapsrv_process_call_recv(struct tevent_req *req)
{
	NTSTATUS status;

	if (tevent_req_is_nterror(req, &status)) {
		tevent_req_received(req);
		return status;
	}

	tevent_req_received(req);
	return NT_STATUS_OK;
}


static void ldapsrv_call_wait_done(struct tevent_req *subreq)
{
	struct ldapsrv_call *call =
		tevent_req_callback_data(subreq,
		struct ldapsrv_call);
	struct ldapsrv_connection *conn = call->conn;
	NTSTATUS status;

	conn->active_call = NULL;

	status = call->wait_recv(subreq);
	TALLOC_FREE(subreq);
	if (!NT_STATUS_IS_OK(status)) {
		const char *reason;

		reason = talloc_asprintf(call, "ldapsrv_call_wait_done: "
					 "call->wait_recv() - %s",
					 nt_errstr(status));
		if (reason == NULL) {
			reason = nt_errstr(status);
		}

		ldapsrv_terminate_connection(conn, reason);
		return;
	}

	ldapsrv_call_writev_start(call);
}


static void ldapsrv_call_postprocess_done(struct tevent_req *subreq)
{
	struct ldapsrv_call *call =
		tevent_req_callback_data(subreq,
		struct ldapsrv_call);
	struct ldapsrv_connection *conn = call->conn;
	NTSTATUS status;

	status = call->postprocess_recv(subreq);
	TALLOC_FREE(subreq);
	if (!NT_STATUS_IS_OK(status)) {
		const char *reason;

		reason = talloc_asprintf(call, "ldapsrv_call_postprocess_done: "
					 "call->postprocess_recv() - %s",
					 nt_errstr(status));
		if (reason == NULL) {
			reason = nt_errstr(status);
		}

		ldapsrv_terminate_connection(conn, reason);
		return;
	}

	TALLOC_FREE(call);

	ldapsrv_call_read_next(conn);
}


static struct tevent_req *ldapsrv_process_call_send(TALLOC_CTX *mem_ctx,
						    struct tevent_context *ev,
						    struct tevent_queue *call_queue,
						    struct ldapsrv_call *call)
{
	struct tevent_req *req;
	struct ldapsrv_process_call_state *state;
	bool ok;

	req = tevent_req_create(mem_ctx, &state,
				struct ldapsrv_process_call_state);
	if (req == NULL) {
		return req;
	}

	state->call = call;

	ok = tevent_queue_add(call_queue, ev, req,
			      ldapsrv_process_call_trigger, NULL);
	if (!ok) {
		tevent_req_oom(req);
		return tevent_req_post(req, ev);
	}

	return req;
}


static void ldapsrv_disconnect_ticket_expired(struct tevent_req *subreq)
{
	struct ldapsrv_connection *conn = tevent_req_callback_data(
		subreq, struct ldapsrv_connection);
	bool ok;

	ok = tevent_wakeup_recv(subreq);
	TALLOC_FREE(subreq);
	if (!ok) {
		DBG_WARNING("tevent_wakeup_recv failed\n");
	}
	conn->deferred_expire_disconnect = NULL;
	ldapsrv_terminate_connection(conn, "network session expired");
}


static NTSTATUS ldapsrv_task_init(struct task_server *task)
{
	char *ldapi_path;
#ifdef WITH_LDAPI_PRIV_SOCKET
	char *priv_dir;
#endif
	const char *dns_host_name;
	struct ldapsrv_service *ldap_service;
	NTSTATUS status;

	switch (lpcfg_server_role(task->lp_ctx)) {
	case ROLE_STANDALONE:
		task_server_terminate(task, "ldap_server: no LDAP server required in standalone configuration",
				      false);
		return NT_STATUS_INVALID_DOMAIN_ROLE;
	case ROLE_DOMAIN_MEMBER:
		task_server_terminate(task, "ldap_server: no LDAP server required in member server configuration",
				      false);
		return NT_STATUS_INVALID_DOMAIN_ROLE;
	case ROLE_ACTIVE_DIRECTORY_DC:
		/* Yes, we want an LDAP server */
		break;
	}

	task_server_set_title(task, "task[ldapsrv]");

	ldap_service = talloc_zero(task, struct ldapsrv_service);
	if (ldap_service == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto failed;
	}

	ldap_service->task = task;

	dns_host_name = talloc_asprintf(ldap_service, "%s.%s",
					lpcfg_netbios_name(task->lp_ctx),
					lpcfg_dnsdomain(task->lp_ctx));
	if (dns_host_name == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto failed;
	}

	status = tstream_tls_params_server(ldap_service,
					   dns_host_name,
					   lpcfg_tls_enabled(task->lp_ctx),
					   lpcfg_tls_keyfile(ldap_service, task->lp_ctx),
					   lpcfg_tls_certfile(ldap_service, task->lp_ctx),
					   lpcfg_tls_cafile(ldap_service, task->lp_ctx),
					   lpcfg_tls_crlfile(ldap_service, task->lp_ctx),
					   lpcfg_tls_dhpfile(ldap_service, task->lp_ctx),
					   lpcfg_tls_priority(task->lp_ctx),
					   &ldap_service->tls_params);
	if (!NT_STATUS_IS_OK(status)) {
		DBG_ERR("ldapsrv failed tstream_tls_params_server - %s\n",
			nt_errstr(status));
		goto failed;
	}

	ldap_service->call_queue = tevent_queue_create(ldap_service, "ldapsrv_call_queue");
	if (ldap_service->call_queue == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto failed;
	}

	if (lpcfg_interfaces(task->lp_ctx) && lpcfg_bind_interfaces_only(task->lp_ctx)) {
		struct interface *ifaces;
		int num_interfaces;
		int i;

		load_interface_list(task, task->lp_ctx, &ifaces);
		num_interfaces = iface_list_count(ifaces);

		/* We have been given an interfaces line, and been
		   told to only bind to those interfaces. Create a
		   socket per interface and bind to only these.
		*/
		for(i = 0; i < num_interfaces; i++) {
			const char *address = iface_list_n_ip(ifaces, i);
			status = add_socket(task, task->lp_ctx, task->model_ops,
					    address, ldap_service);
			if (!NT_STATUS_IS_OK(status)) goto failed;
		}
	} else {
		char **wcard;
		size_t i;
		size_t num_binds = 0;
		wcard = iface_list_wildcard(task);
		if (wcard == NULL) {
			DBG_ERR("No wildcard addresses available\n");
			status = NT_STATUS_UNSUCCESSFUL;
			goto failed;
		}
		for (i=0; wcard[i]; i++) {
			status = add_socket(task, task->lp_ctx, task->model_ops,
					    wcard[i], ldap_service);
			if (NT_STATUS_IS_OK(status)) {
				num_binds++;
			}
		}
		talloc_free(wcard);
		if (num_binds == 0) {
			status = NT_STATUS_UNSUCCESSFUL;
			goto failed;
		}
	}

	ldapi_path = lpcfg_private_path(ldap_service, task->lp_ctx, "ldapi");
	if (!ldapi_path) {
		status = NT_STATUS_UNSUCCESSFUL;
		goto failed;
	}

	status = stream_setup_socket(task, task->event_ctx, task->lp_ctx,
				     task->model_ops, &ldap_stream_nonpriv_ops,
				     "unix", ldapi_path, NULL,
				     lpcfg_socket_options(task->lp_ctx),
				     ldap_service, task->process_context);
	talloc_free(ldapi_path);
	if (!NT_STATUS_IS_OK(status)) {
		DBG_ERR("ldapsrv failed to bind to %s - %s\n",
			ldapi_path, nt_errstr(status));
	}

#ifdef WITH_LDAPI_PRIV_SOCKET
	priv_dir = lpcfg_private_path(ldap_service, task->lp_ctx, "ldap_priv");
	if (priv_dir == NULL) {
		status = NT_STATUS_UNSUCCESSFUL;
		goto failed;
	}
	/*
	 * Make sure the directory for the privileged ldapi socket exists, and
	 * is of the correct permissions
	 */
	if (!directory_create_or_exist(priv_dir, 0750)) {
		task_server_terminate(task, "Cannot create ldap "
				      "privileged ldapi directory", true);
		return NT_STATUS_UNSUCCESSFUL;
	}
	ldapi_path = talloc_asprintf(ldap_service, "%s/ldapi", priv_dir);
	talloc_free(priv_dir);
	if (ldapi_path == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto failed;
	}

	status = stream_setup_socket(task, task->event_ctx, task->lp_ctx,
				     task->model_ops, &ldap_stream_priv_ops,
				     "unix", ldapi_path, NULL,
				     lpcfg_socket_options(task->lp_ctx),
				     ldap_service,
				     task->process_context);
	talloc_free(ldapi_path);
	if (!NT_STATUS_IS_OK(status)) {
		DBG_ERR("ldapsrv failed to bind to %s - %s\n",
			ldapi_path, nt_errstr(status));
	}

#endif

	/* register the server */
	irpc_add_name(task->msg_ctx, "ldap_server");

	task->private_data = ldap_service;

	return NT_STATUS_OK;

failed:
	task_server_terminate(task, "Failed to startup ldap server task", true);
	return status;
}


static void ldapsrv_send(struct stream_connection *c, uint16_t flags)
{
	smb_panic(__location__);
}


static NTSTATUS ldapsrv_packet_check(
	void *private_data,
	DATA_BLOB blob,
	size_t *packet_size)
{
	NTSTATUS ret;
	struct ldapsrv_connection *conn = private_data;
	int result = LDB_SUCCESS;

	ret = ldap_full_packet(private_data, blob, packet_size);
	if (!NT_STATUS_IS_OK(ret)) {
		return ret;
	}
	result = ldapsrv_check_packet_size(conn, *packet_size);
	if (result != LDAP_SUCCESS) {
		return NT_STATUS_LDAP(result);
	}
	return NT_STATUS_OK;
}


void ldapsrv_recv(struct stream_connection *c, uint16_t flags)
{
	smb_panic(__location__);
}


static void ldapsrv_call_writev_start(struct ldapsrv_call *call)
{
	struct ldapsrv_connection *conn = call->conn;
	struct ldapsrv_reply *reply = NULL;
	struct tevent_req *subreq = NULL;
	size_t length = 0;
	size_t i;

	call->iov_count = 0;

	/* build all the replies into an IOV (no copy) */
	for (reply = call->replies;
	     reply != NULL;
	     reply = reply->next) {

		/* Cap output at 25MB per writev() */
		if (length > length + reply->blob.length
		    || length + reply->blob.length > LDAP_SERVER_MAX_CHUNK_SIZE) {
			break;
		}

		/*
		 * Overflow is harmless here, just used below to
		 * decide if to read or write, but checked above anyway
		 */
		length += reply->blob.length;

		/*
		 * At worst an overflow would mean we send less
		 * replies
		 */
		call->iov_count++;
	}

	if (length == 0) {
		if (!call->notification.busy) {
			TALLOC_FREE(call);
		}

		ldapsrv_call_read_next(conn);
		return;
	}

	/* Cap call->iov_count at IOV_MAX */
	call->iov_count = MIN(call->iov_count, IOV_MAX);

	call->out_iov = talloc_array(call,
				     struct iovec,
				     call->iov_count);
	if (!call->out_iov) {
		/* This is not ideal */
		ldapsrv_terminate_connection(conn,
					     "failed to allocate "
					     "iovec array");
		return;
	}

	/* We may have had to cap the number of replies at IOV_MAX */
	for (i = 0;
	     i < call->iov_count && call->replies != NULL;
	     i++) {
		reply = call->replies;
		call->out_iov[i].iov_base = reply->blob.data;
		call->out_iov[i].iov_len = reply->blob.length;

		/* Keep only the ASN.1 encoded data */
		talloc_steal(call->out_iov, reply->blob.data);

		DLIST_REMOVE(call->replies, reply);
		TALLOC_FREE(reply);
	}

	if (i > call->iov_count) {
		/* This is not ideal, but also (essentially) impossible */
		ldapsrv_terminate_connection(conn,
					     "call list ended"
					     "before iov_count");
		return;
	}

	subreq = tstream_writev_queue_send(call,
					   conn->connection->event.ctx,
					   conn->sockets.active,
					   conn->sockets.send_queue,
					   call->out_iov, call->iov_count);
	if (subreq == NULL) {
		ldapsrv_terminate_connection(conn, "stream_writev_queue_send failed");
		return;
	}
	tevent_req_set_callback(subreq, ldapsrv_call_writev_done, call);
}


static void ldapsrv_accept_tls_done(struct tevent_req *subreq)
{
	struct ldapsrv_connection *conn =
		tevent_req_callback_data(subreq,
		struct ldapsrv_connection);
	int ret;
	int sys_errno;

	ret = tstream_tls_accept_recv(subreq, &sys_errno,
				      conn, &conn->sockets.tls);
	TALLOC_FREE(subreq);
	if (ret == -1) {
		const char *reason;

		reason = talloc_asprintf(conn, "ldapsrv_accept_tls_loop: "
					 "tstream_tls_accept_recv() - %d:%s",
					 sys_errno, strerror(sys_errno));
		if (!reason) {
			reason = "ldapsrv_accept_tls_loop: "
				 "tstream_tls_accept_recv() - failed";
		}

		ldapsrv_terminate_connection(conn, reason);
		return;
	}

	conn->sockets.active = conn->sockets.tls;
	conn->referral_scheme = LDAP_REFERRAL_SCHEME_LDAPS;
	ldapsrv_call_read_next(conn);
}


static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn)
{
	struct tevent_req *subreq;

	if (conn->pending_calls != NULL) {
		conn->limits.endtime = timeval_zero();

		ldapsrv_notification_retry_setup(conn->service, false);
	} else if (timeval_is_zero(&conn->limits.endtime)) {
		conn->limits.endtime =
			timeval_current_ofs(conn->limits.initial_timeout, 0);
	} else {
		conn->limits.endtime =
			timeval_current_ofs(conn->limits.conn_idle_time, 0);
	}

	if (conn->sockets.read_req != NULL) {
		return true;
	}

	/*
	 * The minimum size of a LDAP pdu is 7 bytes
	 *
	 * dumpasn1 -hh ldap-unbind-min.dat
	 *
	 *     <30 05 02 01 09 42 00>
	 *    0    5: SEQUENCE {
	 *     <02 01 09>
	 *    2    1:   INTEGER 9
	 *     <42 00>
	 *    5    0:   [APPLICATION 2]
	 *          :     Error: Object has zero length.
	 *          :   }
	 *
	 * dumpasn1 -hh ldap-unbind-windows.dat
	 *
	 *     <30 84 00 00 00 05 02 01 09 42 00>
	 *    0    5: SEQUENCE {
	 *     <02 01 09>
	 *    6    1:   INTEGER 9
	 *     <42 00>
	 *    9    0:   [APPLICATION 2]
	 *          :     Error: Object has zero length.
	 *          :   }
	 *
	 * This means using an initial read size
	 * of 7 is ok.
	 */
	subreq = tstream_read_pdu_blob_send(conn,
					    conn->connection->event.ctx,
					    conn->sockets.active,
					    7, /* initial_read_size */
					    ldapsrv_packet_check,
					    conn);
	if (subreq == NULL) {
		ldapsrv_terminate_connection(conn, "ldapsrv_call_read_next: "
				"no memory for tstream_read_pdu_blob_send");
		return false;
	}
	if (!timeval_is_zero(&conn->limits.endtime)) {
		bool ok;
		ok = tevent_req_set_endtime(subreq,
					    conn->connection->event.ctx,
					    conn->limits.endtime);
		if (!ok) {
			ldapsrv_terminate_connection(
				conn,
				"ldapsrv_call_read_next: "
				"no memory for tevent_req_set_endtime");
			return false;
		}
	}
	tevent_req_set_callback(subreq, ldapsrv_call_read_done, conn);
	conn->sockets.read_req = subreq;
	return true;
}


static void ldapsrv_accept_nonpriv(struct stream_connection *c)
{
	struct ldapsrv_service *ldapsrv_service = talloc_get_type_abort(
		c->private_data, struct ldapsrv_service);
	struct auth_session_info *session_info;
	NTSTATUS status;

	status = auth_anonymous_session_info(
		c, ldapsrv_service->task->lp_ctx, &session_info);
	if (!NT_STATUS_IS_OK(status)) {
		stream_terminate_connection(c, "failed to setup anonymous "
					    "session info");
		return;
	}
	ldapsrv_accept(c, session_info, false);
}


static void ldapsrv_accept_priv(struct stream_connection *c)
{
	struct ldapsrv_service *ldapsrv_service = talloc_get_type_abort(
		c->private_data, struct ldapsrv_service);
	struct auth_session_info *session_info;

	session_info = system_session(ldapsrv_service->task->lp_ctx);
	if (!session_info) {
		stream_terminate_connection(c, "failed to setup system "
					    "session info");
		return;
	}
	ldapsrv_accept(c, session_info, true);
}


static int ldapsrv_load_limits(struct ldapsrv_connection *conn)
{
	TALLOC_CTX *tmp_ctx;
	const char *attrs[] = { "configurationNamingContext", NULL };
	const char *attrs2[] = { "lDAPAdminLimits", NULL };
	struct ldb_message_element *el;
	struct ldb_result *res = NULL;
	struct ldb_dn *basedn;
	struct ldb_dn *conf_dn;
	struct ldb_dn *policy_dn;
	unsigned int i;
	int ret;

	/* set defaults limits in case of failure */
	conn->limits.initial_timeout = 120;
	conn->limits.conn_idle_time = 900;
	conn->limits.max_page_size = 1000;
	conn->limits.max_notifications = 5;
	conn->limits.search_timeout = 120;
	conn->limits.expire_time = (struct timeval) {
		.tv_sec = get_time_t_max(),
	};

	tmp_ctx = talloc_new(conn);
	if (tmp_ctx == NULL) {
		return -1;
	}

	basedn = ldb_dn_new(tmp_ctx, conn->ldb, NULL);
	if (basedn == NULL) {
		goto failed;
	}

	ret = ldb_search(conn->ldb, tmp_ctx, &res, basedn, LDB_SCOPE_BASE, attrs, NULL);
	if (ret != LDB_SUCCESS) {
		goto failed;
	}

	if (res->count != 1) {
		goto failed;
	}

	conf_dn = ldb_msg_find_attr_as_dn(conn->ldb, tmp_ctx, res->msgs[0], "configurationNamingContext");
	if (conf_dn == NULL) {
		goto failed;
	}

	policy_dn = ldb_dn_copy(tmp_ctx, conf_dn);
	ldb_dn_add_child_fmt(policy_dn, "CN=Default Query Policy,CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services");
	if (policy_dn == NULL) {
		goto failed;
	}

	ret = ldb_search(conn->ldb, tmp_ctx, &res, policy_dn, LDB_SCOPE_BASE, attrs2, NULL);
	if (ret != LDB_SUCCESS) {
		goto failed;
	}

	if (res->count != 1) {
		goto failed;
	}

	el = ldb_msg_find_element(res->msgs[0], "lDAPAdminLimits");
	if (el == NULL) {
		goto failed;
	}

	for (i = 0; i < el->num_values; i++) {
		char policy_name[256];
		int policy_value, s;

		s = sscanf((const char *)el->values[i].data, "%255[^=]=%d", policy_name, &policy_value);
		if (s != 2 || policy_value == 0)
			continue;
		if (strcasecmp("InitRecvTimeout", policy_name) == 0) {
			conn->limits.initial_timeout = policy_value;
			continue;
		}
		if (strcasecmp("MaxConnIdleTime", policy_name) == 0) {
			conn->limits.conn_idle_time = policy_value;
			continue;
		}
		if (strcasecmp("MaxPageSize", policy_name) == 0) {
			conn->limits.max_page_size = policy_value;
			continue;
		}
		if (strcasecmp("MaxNotificationPerConn", policy_name) == 0) {
			conn->limits.max_notifications = policy_value;
			continue;
		}
		if (strcasecmp("MaxQueryDuration", policy_name) == 0) {
			if (policy_value > 0) {
				conn->limits.search_timeout = policy_value;
			}
			continue;
		}
	}

	return 0;

failed:
	DBG_ERR("Failed to load ldap server query policies\n");
	talloc_free(tmp_ctx);
	return -1;
}


static void ldapsrv_call_process_done(struct tevent_req *subreq)
{
	struct ldapsrv_call *call =
		tevent_req_callback_data(subreq,
		struct ldapsrv_call);
	struct ldapsrv_connection *conn = call->conn;
	NTSTATUS status;

	conn->active_call = NULL;

	status = ldapsrv_process_call_recv(subreq);
	TALLOC_FREE(subreq);
	if (!NT_STATUS_IS_OK(status)) {
		ldapsrv_terminate_connection(conn, nt_errstr(status));
		return;
	}

	if (call->wait_send != NULL) {
		subreq = call->wait_send(call,
					 conn->connection->event.ctx,
					 call->wait_private);
		if (subreq == NULL) {
			ldapsrv_terminate_connection(conn,
					"ldapsrv_call_process_done: "
					"call->wait_send - no memory");
			return;
		}
		tevent_req_set_callback(subreq,
					ldapsrv_call_wait_done,
					call);
		conn->active_call = subreq;
		return;
	}

	ldapsrv_call_writev_start(call);
}


static void ldapsrv_call_writev_done(struct tevent_req *subreq)
{
	struct ldapsrv_call *call =
		tevent_req_callback_data(subreq,
		struct ldapsrv_call);
	struct ldapsrv_connection *conn = call->conn;
	int sys_errno;
	int rc;

	rc = tstream_writev_queue_recv(subreq, &sys_errno);
	TALLOC_FREE(subreq);

	/* This releases the ASN.1 encoded packets from memory */
	TALLOC_FREE(call->out_iov);
	if (rc == -1) {
		const char *reason;

		reason = talloc_asprintf(call, "ldapsrv_call_writev_done: "
					 "tstream_writev_queue_recv() - %d:%s",
					 sys_errno, strerror(sys_errno));
		if (reason == NULL) {
			reason = "ldapsrv_call_writev_done: "
				 "tstream_writev_queue_recv() failed";
		}

		ldapsrv_terminate_connection(conn, reason);
		return;
	}

	if (call->postprocess_send) {
		subreq = call->postprocess_send(call,
						conn->connection->event.ctx,
						call->postprocess_private);
		if (subreq == NULL) {
			ldapsrv_terminate_connection(conn, "ldapsrv_call_writev_done: "
					"call->postprocess_send - no memory");
			return;
		}
		tevent_req_set_callback(subreq,
					ldapsrv_call_postprocess_done,
					call);
		return;
	}

	/* Perhaps still some more to send */
	if (call->replies != NULL) {
		ldapsrv_call_writev_start(call);
		return;
	}

	if (!call->notification.busy) {
		TALLOC_FREE(call);
	}

	ldapsrv_call_read_next(conn);
}


Fix null CHECK issue with `tf.raw_ops.EncodePng`.

PiperOrigin-RevId: 369717714
Change-Id: I24136cd99c20b8466671f4f93b670ef6f6dd1250

  void Compute(OpKernelContext* context) override {
    const Tensor& image = context->input(0);
    OP_REQUIRES(context, image.dims() == 3,
                errors::InvalidArgument("image must be 3-dimensional",
                                        image.shape().DebugString()));
    OP_REQUIRES(context, image.NumElements() > 0,
                errors::Internal("Invalid image provided."));
    OP_REQUIRES(
        context,
        FastBoundsCheck(image.NumElements(), std::numeric_limits<int32>::max()),
        errors::InvalidArgument("image cannot have >= int32 max elements"));
    const int32 height = static_cast<int32>(image.dim_size(0));
    const int32 width = static_cast<int32>(image.dim_size(1));
    const int32 channels = static_cast<int32>(image.dim_size(2));

    // In some cases, we pass width*channels*2 to png.
    const int32 max_row_width = std::numeric_limits<int32>::max() / 2;

    OP_REQUIRES(context, FastBoundsCheck(width * channels, max_row_width),
                errors::InvalidArgument("image too wide to encode"));

    OP_REQUIRES(context, channels >= 1 && channels <= 4,
                errors::InvalidArgument(
                    "image must have 1, 2, 3, or 4 channels, got ", channels));

    // Encode image to png string
    Tensor* output = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output(0, TensorShape({}), &output));
    if (desired_channel_bits_ == 8) {
      OP_REQUIRES(context,
                  png::WriteImageToBuffer(
                      image.flat<uint8>().data(), width, height,
                      width * channels, channels, desired_channel_bits_,
                      compression_, &output->scalar<tstring>()(), nullptr),
                  errors::Internal("PNG encoding failed"));
    } else {
      OP_REQUIRES(context,
                  png::WriteImageToBuffer(
                      image.flat<uint16>().data(), width, height,
                      width * channels * 2, channels, desired_channel_bits_,
                      compression_, &output->scalar<tstring>()(), nullptr),
                  errors::Internal("PNG encoding failed"));
    }
  }

TensorFlow is an end-to-end open source platform for machine learning. An attacker can trigger a `CHECK` fail in PNG encoding by providing an empty input tensor as the pixel data. This is because the implementation(https://github.com/tensorflow/tensorflow/blob/e312e0791ce486a80c9d23110841525c6f7c3289/tensorflow/core/kernels/image/encode_png_op.cc#L57-L60) only validates that the total number of pixels in the image does not overflow. Thus, an attacker can send an empty matrix for encoding. However, if the tensor is empty, then the associated buffer is `nullptr`. Hence, when calling `png::WriteImageToBuffer`(https://github.com/tensorflow/tensorflow/blob/e312e0791ce486a80c9d23110841525c6f7c3289/tensorflow/core/kernels/image/encode_png_op.cc#L79-L93), the first argument (i.e., `image.flat<T>().data()`) is `NULL`. This then triggers the `CHECK_NOTNULL` in the first line of `png::WriteImageToBuffer`(https://github.com/tensorflow/tensorflow/blob/e312e0791ce486a80c9d23110841525c6f7c3289/tensorflow/core/lib/png/png_io.cc#L345-L349). Since `image` is null, this results in `abort` being called after printing the stacktrace. Effectively, this allows an attacker to mount a denial of service attack. The fix will be included in TensorFlow 2.5.0. We will also cherrypick this commit on TensorFlow 2.4.2, TensorFlow 2.3.3, TensorFlow 2.2.3 and TensorFlow 2.1.4, as these are also affected and still in supported range.


  explicit EncodePngOp(OpKernelConstruction* context) : OpKernel(context) {
    OP_REQUIRES_OK(context, context->GetAttr("compression", &compression_));
    OP_REQUIRES(context, -1 <= compression_ && compression_ <= 9,
                errors::InvalidArgument("compression should be in [-1,9], got ",
                                        compression_));

    DataType dt = context->input_type(0);
    OP_REQUIRES(context, dt == DataType::DT_UINT8 || dt == DataType::DT_UINT16,
                errors::InvalidArgument(
                    "image must have type uint8 or uint16, got ", dt));

    if (dt == DataType::DT_UINT8) {
      desired_channel_bits_ = 8;
    } else {
      desired_channel_bits_ = 16;
    }
  }


Fix error handling in scanner when in case of OOM (#3793)

This patch fixes #3786 and fixes #3788.

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu

scanner_scan_post_primary_expression (parser_context_t *context_p, /**< context */
                                      scanner_context_t *scanner_context_p, /**< scanner context */
                                      lexer_token_type_t type, /**< current token type */
                                      scan_stack_modes_t stack_top) /**< current stack top */
{
  switch (type)
  {
    case LEXER_DOT:
    {
      lexer_scan_identifier (context_p);

      if (context_p->token.type != LEXER_LITERAL
          || context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
      {
        scanner_raise_error (context_p);
      }

      return true;
    }
    case LEXER_LEFT_PAREN:
    {
      parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION);
      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
      return true;
    }
#if ENABLED (JERRY_ES2015)
    case LEXER_TEMPLATE_LITERAL:
    {
      if (JERRY_UNLIKELY (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT))
      {
        scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
        parser_stack_push_uint8 (context_p, SCAN_STACK_TAGGED_TEMPLATE_LITERAL);
      }
      return true;
    }
#endif /* ENABLED (JERRY_ES2015) */
    case LEXER_LEFT_SQUARE:
    {
      parser_stack_push_uint8 (context_p, SCAN_STACK_PROPERTY_ACCESSOR);
      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
      return true;
    }
    case LEXER_INCREASE:
    case LEXER_DECREASE:
    {
      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END;

      if (context_p->token.flags & LEXER_WAS_NEWLINE)
      {
        return false;
      }

      lexer_next_token (context_p);
      type = (lexer_token_type_t) context_p->token.type;

      if (type != LEXER_QUESTION_MARK)
      {
        break;
      }
      /* FALLTHRU */
    }
    case LEXER_QUESTION_MARK:
    {
      parser_stack_push_uint8 (context_p, SCAN_STACK_COLON_EXPRESSION);
      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
      return true;
    }
    default:
    {
      break;
    }
  }

  if (LEXER_IS_BINARY_OP_TOKEN (type)
      && (type != LEXER_KEYW_IN || !SCANNER_IS_FOR_START (stack_top)))
  {
    scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
    return true;
  }

  return false;
} /* scanner_scan_post_primary_expression */

parser/js/js-scanner.c in JerryScript 2.2.0 mishandles errors during certain out-of-memory conditions, as demonstrated by a scanner_reverse_info_list NULL pointer dereference and a scanner_scan_all assertion failure.


scanner_scan_all (parser_context_t *context_p, /**< context */
                  const uint8_t *arg_list_p, /**< function argument list */
                  const uint8_t *arg_list_end_p, /**< end of argument list */
                  const uint8_t *source_p, /**< valid UTF-8 source code */
                  const uint8_t *source_end_p) /**< end of source code */
{
  scanner_context_t scanner_context;

#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
  if (context_p->is_show_opcodes)
  {
    JERRY_DEBUG_MSG ("\n--- Scanning start ---\n\n");
  }
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */

  scanner_context.context_status_flags = context_p->status_flags;
  scanner_context.status_flags = SCANNER_CONTEXT_NO_FLAGS;
#if ENABLED (JERRY_DEBUGGER)
  if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
  {
    scanner_context.status_flags |= SCANNER_CONTEXT_DEBUGGER_ENABLED;
  }
#endif /* ENABLED (JERRY_DEBUGGER) */
#if ENABLED (JERRY_ES2015)
  scanner_context.binding_type = SCANNER_BINDING_NONE;
  scanner_context.active_binding_list_p = NULL;
#endif /* ENABLED (JERRY_ES2015) */
  scanner_context.active_literal_pool_p = NULL;
  scanner_context.active_switch_statement.last_case_p = NULL;
  scanner_context.end_arguments_p = NULL;
#if ENABLED (JERRY_ES2015)
  scanner_context.async_source_p = NULL;
#endif /* ENABLED (JERRY_ES2015) */

  /* This assignment must be here because of Apple compilers. */
  context_p->u.scanner_context_p = &scanner_context;

  parser_stack_init (context_p);

  PARSER_TRY (context_p->try_buffer)
  {
    context_p->line = 1;
    context_p->column = 1;

    if (arg_list_p == NULL)
    {
      context_p->source_p = source_p;
      context_p->source_end_p = source_end_p;

      uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS | SCANNER_LITERAL_POOL_CAN_EVAL;

      if (context_p->status_flags & PARSER_IS_STRICT)
      {
        status_flags |= SCANNER_LITERAL_POOL_IS_STRICT;
      }

      scanner_literal_pool_t *literal_pool_p = scanner_push_literal_pool (context_p, &scanner_context, status_flags);
      literal_pool_p->source_p = source_p;

      parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT);

      lexer_next_token (context_p);
      scanner_check_directives (context_p, &scanner_context);
    }
    else
    {
      context_p->source_p = arg_list_p;
      context_p->source_end_p = arg_list_end_p;

      uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION;

      if (context_p->status_flags & PARSER_IS_STRICT)
      {
        status_flags |= SCANNER_LITERAL_POOL_IS_STRICT;
      }

#if ENABLED (JERRY_ES2015)
      if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)
      {
        status_flags |= SCANNER_LITERAL_POOL_GENERATOR;
      }
#endif /* ENABLED (JERRY_ES2015) */

      scanner_push_literal_pool (context_p, &scanner_context, status_flags);
      scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS;
      parser_stack_push_uint8 (context_p, SCAN_STACK_SCRIPT_FUNCTION);

      /* Faking the first token. */
      context_p->token.type = LEXER_LEFT_PAREN;
    }

    while (true)
    {
      lexer_token_type_t type = (lexer_token_type_t) context_p->token.type;
      scan_stack_modes_t stack_top = (scan_stack_modes_t) context_p->stack_top_uint8;

      switch (scanner_context.mode)
      {
        case SCAN_MODE_PRIMARY_EXPRESSION:
        {
          if (type == LEXER_ADD
              || type == LEXER_SUBTRACT
              || LEXER_IS_UNARY_OP_TOKEN (type))
          {
            break;
          }
          /* FALLTHRU */
        }
        case SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW:
        {
          if (scanner_scan_primary_expression (context_p, &scanner_context, type, stack_top) != SCAN_NEXT_TOKEN)
          {
            continue;
          }
          break;
        }
#if ENABLED (JERRY_ES2015)
        case SCAN_MODE_CLASS_DECLARATION:
        {
          if (context_p->token.type == LEXER_KEYW_EXTENDS)
          {
            parser_stack_push_uint8 (context_p, SCAN_STACK_CLASS_EXTENDS);
            scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
            break;
          }
          else if (context_p->token.type != LEXER_LEFT_BRACE)
          {
            scanner_raise_error (context_p);
          }

          scanner_context.mode = SCAN_MODE_CLASS_METHOD;
          /* FALLTHRU */
        }
        case SCAN_MODE_CLASS_METHOD:
        {
          JERRY_ASSERT (stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR
                        || stack_top == SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR);

          lexer_skip_empty_statements (context_p);

          lexer_scan_identifier (context_p);

          if (context_p->token.type == LEXER_RIGHT_BRACE)
          {
            scanner_source_start_t source_start;

            parser_stack_pop_uint8 (context_p);

            if (stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR)
            {
              parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t));
            }

            stack_top = context_p->stack_top_uint8;

            JERRY_ASSERT (stack_top == SCAN_STACK_CLASS_STATEMENT || stack_top == SCAN_STACK_CLASS_EXPRESSION);

            if (stack_top == SCAN_STACK_CLASS_STATEMENT)
            {
              /* The token is kept to disallow consuming a semicolon after it. */
              scanner_context.mode = SCAN_MODE_STATEMENT_END;
              continue;
            }

            scanner_context.mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;
            parser_stack_pop_uint8 (context_p);
            break;
          }

          if (context_p->token.type == LEXER_LITERAL
              && LEXER_IS_IDENT_OR_STRING (context_p->token.lit_location.type)
              && lexer_compare_literal_to_string (context_p, "constructor", 11))
          {
            if (stack_top == SCAN_STACK_IMPLICIT_CLASS_CONSTRUCTOR)
            {
              scanner_source_start_t source_start;
              parser_stack_pop_uint8 (context_p);
              parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t));

              scanner_info_t *info_p = scanner_insert_info (context_p, source_start.source_p, sizeof (scanner_info_t));
              info_p->type = SCANNER_TYPE_CLASS_CONSTRUCTOR;
              parser_stack_push_uint8 (context_p, SCAN_STACK_EXPLICIT_CLASS_CONSTRUCTOR);
            }
          }

          if (lexer_token_is_identifier (context_p, "static", 6))
          {
            lexer_scan_identifier (context_p);
          }

          parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY);
          scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS;

          uint16_t literal_pool_flags = SCANNER_LITERAL_POOL_FUNCTION;

          if (lexer_token_is_identifier (context_p, "get", 3)
              || lexer_token_is_identifier (context_p, "set", 3))
          {
            lexer_scan_identifier (context_p);

            if (context_p->token.type == LEXER_LEFT_PAREN)
            {
              scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION);
              continue;
            }
          }
          else if (lexer_token_is_identifier (context_p, "async", 5))
          {
            lexer_scan_identifier (context_p);

            if (context_p->token.type == LEXER_LEFT_PAREN)
            {
              scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION);
              continue;
            }

            literal_pool_flags |= SCANNER_LITERAL_POOL_ASYNC;

            if (context_p->token.type == LEXER_MULTIPLY)
            {
              lexer_scan_identifier (context_p);
              literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR;
            }
          }
          else if (context_p->token.type == LEXER_MULTIPLY)
          {
            lexer_scan_identifier (context_p);
            literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR;
          }

          if (context_p->token.type == LEXER_LEFT_SQUARE)
          {
            parser_stack_push_uint8 (context_p, SCANNER_FROM_LITERAL_POOL_TO_COMPUTED (literal_pool_flags));
            scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
            break;
          }

          if (context_p->token.type != LEXER_LITERAL)
          {
            scanner_raise_error (context_p);
          }

          if (literal_pool_flags & SCANNER_LITERAL_POOL_GENERATOR)
          {
            context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION;
          }

          scanner_push_literal_pool (context_p, &scanner_context, literal_pool_flags);
          lexer_next_token (context_p);
          continue;
        }
#endif /* ENABLED (JERRY_ES2015) */
        case SCAN_MODE_POST_PRIMARY_EXPRESSION:
        {
          if (scanner_scan_post_primary_expression (context_p, &scanner_context, type, stack_top))
          {
            break;
          }
          type = (lexer_token_type_t) context_p->token.type;
          /* FALLTHRU */
        }
        case SCAN_MODE_PRIMARY_EXPRESSION_END:
        {
          if (scanner_scan_primary_expression_end (context_p, &scanner_context, type, stack_top) != SCAN_NEXT_TOKEN)
          {
            continue;
          }
          break;
        }
        case SCAN_MODE_STATEMENT_OR_TERMINATOR:
        {
          if (type == LEXER_RIGHT_BRACE || type == LEXER_EOS)
          {
            scanner_context.mode = SCAN_MODE_STATEMENT_END;
            continue;
          }
          /* FALLTHRU */
        }
        case SCAN_MODE_STATEMENT:
        {
          if (scanner_scan_statement (context_p, &scanner_context, type, stack_top) != SCAN_NEXT_TOKEN)
          {
            continue;
          }
          break;
        }
        case SCAN_MODE_STATEMENT_END:
        {
          if (scanner_scan_statement_end (context_p, &scanner_context, type) != SCAN_NEXT_TOKEN)
          {
            continue;
          }

          if (context_p->token.type == LEXER_EOS)
          {
            goto scan_completed;
          }

          break;
        }
        case SCAN_MODE_VAR_STATEMENT:
        {
#if ENABLED (JERRY_ES2015)
          if (type == LEXER_LEFT_SQUARE || type == LEXER_LEFT_BRACE)
          {
            uint8_t binding_type = SCANNER_BINDING_VAR;

            if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_FOR_LET_START)
            {
              binding_type = SCANNER_BINDING_LET;
            }
            else if (stack_top == SCAN_STACK_CONST || stack_top == SCAN_STACK_FOR_CONST_START)
            {
              binding_type = SCANNER_BINDING_CONST;
            }

            scanner_push_destructuring_pattern (context_p, &scanner_context, binding_type, false);

            if (type == LEXER_LEFT_SQUARE)
            {
              parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL);
              scanner_context.mode = SCAN_MODE_BINDING;
              break;
            }

            parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL);
            scanner_context.mode = SCAN_MODE_PROPERTY_NAME;
            continue;
          }
#endif /* ENABLED (JERRY_ES2015) */

          if (type != LEXER_LITERAL
              || context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
          {
            scanner_raise_error (context_p);
          }

          lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context);

#if ENABLED (JERRY_ES2015)
          if (stack_top != SCAN_STACK_VAR && stack_top != SCAN_STACK_FOR_VAR_START)
          {
            scanner_detect_invalid_let (context_p, literal_p);

            if (stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_FOR_LET_START)
            {
              literal_p->type |= SCANNER_LITERAL_IS_LET;
            }
            else
            {
              JERRY_ASSERT (stack_top == SCAN_STACK_CONST || stack_top == SCAN_STACK_FOR_CONST_START);
              literal_p->type |= SCANNER_LITERAL_IS_CONST;
            }

            lexer_next_token (context_p);

            if (literal_p->type & SCANNER_LITERAL_IS_USED)
            {
              literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
            }
            else if (context_p->token.type == LEXER_ASSIGN)
            {
              scanner_binding_literal_t binding_literal;
              binding_literal.literal_p = literal_p;

              parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t));
              parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT);
            }
          }
          else
          {
            if (!(literal_p->type & SCANNER_LITERAL_IS_VAR))
            {
              scanner_detect_invalid_var (context_p, &scanner_context, literal_p);
              literal_p->type |= SCANNER_LITERAL_IS_VAR;

              if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH)
              {
                literal_p->type |= SCANNER_LITERAL_NO_REG;
              }
            }

            lexer_next_token (context_p);
          }
#else /* !ENABLED (JERRY_ES2015) */
          literal_p->type |= SCANNER_LITERAL_IS_VAR;

          if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH)
          {
            literal_p->type |= SCANNER_LITERAL_NO_REG;
          }

          lexer_next_token (context_p);
#endif /* ENABLED (JERRY_ES2015) */

#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
          if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_EXPORT)
          {
            literal_p->type |= SCANNER_LITERAL_NO_REG;
          }
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */

          switch (context_p->token.type)
          {
            case LEXER_ASSIGN:
            {
              scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
              /* FALLTHRU */
            }
            case LEXER_COMMA:
            {
              lexer_next_token (context_p);
              continue;
            }
          }

          if (SCANNER_IS_FOR_START (stack_top))
          {
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
            JERRY_ASSERT (!(scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_EXPORT));
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */

            if (context_p->token.type != LEXER_SEMICOLON
                && context_p->token.type != LEXER_KEYW_IN
                && !SCANNER_IDENTIFIER_IS_OF ())
            {
              scanner_raise_error (context_p);
            }

            scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
            continue;
          }

#if ENABLED (JERRY_ES2015)
          JERRY_ASSERT (stack_top == SCAN_STACK_VAR || stack_top == SCAN_STACK_LET || stack_top == SCAN_STACK_CONST);
#else /* !ENABLED (JERRY_ES2015) */
          JERRY_ASSERT (stack_top == SCAN_STACK_VAR);
#endif /* ENABLED (JERRY_ES2015) */

#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
          scanner_context.active_literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_EXPORT;
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */

          scanner_context.mode = SCAN_MODE_STATEMENT_END;
          parser_stack_pop_uint8 (context_p);
          continue;
        }
        case SCAN_MODE_FUNCTION_ARGUMENTS:
        {
          JERRY_ASSERT (stack_top == SCAN_STACK_SCRIPT_FUNCTION
                        || stack_top == SCAN_STACK_FUNCTION_STATEMENT
                        || stack_top == SCAN_STACK_FUNCTION_EXPRESSION
                        || stack_top == SCAN_STACK_FUNCTION_PROPERTY);

          scanner_literal_pool_t *literal_pool_p = scanner_context.active_literal_pool_p;

          JERRY_ASSERT (literal_pool_p != NULL && (literal_pool_p->status_flags & SCANNER_LITERAL_POOL_FUNCTION));

          literal_pool_p->source_p = context_p->source_p;

#if ENABLED (JERRY_ES2015)
          if (JERRY_UNLIKELY (scanner_context.async_source_p != NULL))
          {
            literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ASYNC;
            literal_pool_p->source_p = scanner_context.async_source_p;
            scanner_context.async_source_p = NULL;
          }
#endif /* ENABLED (JERRY_ES2015) */

          if (type != LEXER_LEFT_PAREN)
          {
            scanner_raise_error (context_p);
          }
          lexer_next_token (context_p);

#if ENABLED (JERRY_ES2015)
          /* FALLTHRU */
        }
        case SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS:
        {
#endif /* ENABLED (JERRY_ES2015) */
          if (context_p->token.type != LEXER_RIGHT_PAREN && context_p->token.type != LEXER_EOS)
          {
#if ENABLED (JERRY_ES2015)
            lexer_lit_location_t *argument_literal_p;
#endif /* ENABLED (JERRY_ES2015) */

            while (true)
            {
#if ENABLED (JERRY_ES2015)
              if (context_p->token.type == LEXER_THREE_DOTS)
              {
                scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARGUMENTS_UNMAPPED;

                lexer_next_token (context_p);
              }

              if (context_p->token.type == LEXER_LEFT_SQUARE || context_p->token.type == LEXER_LEFT_BRACE)
              {
                argument_literal_p = NULL;
                break;
              }
#endif /* ENABLED (JERRY_ES2015) */

              if (context_p->token.type != LEXER_LITERAL
                  || context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
              {
                scanner_raise_error (context_p);
              }

#if ENABLED (JERRY_ES2015)
              argument_literal_p = scanner_append_argument (context_p, &scanner_context);
#else /* !ENABLED (JERRY_ES2015) */
              scanner_append_argument (context_p, &scanner_context);
#endif /* ENABLED (JERRY_ES2015) */

              lexer_next_token (context_p);

              if (context_p->token.type != LEXER_COMMA)
              {
                break;
              }
              lexer_next_token (context_p);
            }

#if ENABLED (JERRY_ES2015)
            if (argument_literal_p == NULL)
            {
              scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARGUMENTS_UNMAPPED;

              parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PARAMETERS);
              scanner_append_hole (context_p, &scanner_context);
              scanner_push_destructuring_pattern (context_p, &scanner_context, SCANNER_BINDING_ARG, false);

              if (context_p->token.type == LEXER_LEFT_SQUARE)
              {
                parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL);
                scanner_context.mode = SCAN_MODE_BINDING;
                break;
              }

              parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL);
              scanner_context.mode = SCAN_MODE_PROPERTY_NAME;
              continue;
            }

            if (context_p->token.type == LEXER_ASSIGN)
            {
              scanner_context.active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_ARGUMENTS_UNMAPPED;

              parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PARAMETERS);
              scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;

              if (argument_literal_p->type & SCANNER_LITERAL_IS_USED)
              {
                JERRY_ASSERT (argument_literal_p->type & SCANNER_LITERAL_EARLY_CREATE);
                break;
              }

              scanner_binding_literal_t binding_literal;
              binding_literal.literal_p = argument_literal_p;

              parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t));
              parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT);
              break;
            }
#endif /* ENABLED (JERRY_ES2015) */
          }

          if (context_p->token.type == LEXER_EOS && stack_top == SCAN_STACK_SCRIPT_FUNCTION)
          {
            /* End of argument parsing. */
            scanner_info_t *scanner_info_p = (scanner_info_t *) scanner_malloc (context_p, sizeof (scanner_info_t));
            scanner_info_p->next_p = context_p->next_scanner_info_p;
            scanner_info_p->source_p = NULL;
            scanner_info_p->type = SCANNER_TYPE_END_ARGUMENTS;
            scanner_context.end_arguments_p = scanner_info_p;

            context_p->next_scanner_info_p = scanner_info_p;
            context_p->source_p = source_p;
            context_p->source_end_p = source_end_p;
            context_p->line = 1;
            context_p->column = 1;

            scanner_filter_arguments (context_p, &scanner_context);
            lexer_next_token (context_p);
            scanner_check_directives (context_p, &scanner_context);
            continue;
          }

          if (context_p->token.type != LEXER_RIGHT_PAREN)
          {
            scanner_raise_error (context_p);
          }

          lexer_next_token (context_p);

          if (context_p->token.type != LEXER_LEFT_BRACE)
          {
            scanner_raise_error (context_p);
          }

          scanner_filter_arguments (context_p, &scanner_context);
          lexer_next_token (context_p);
          scanner_check_directives (context_p, &scanner_context);
          continue;
        }
        case SCAN_MODE_PROPERTY_NAME:
        {
          JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL);

          if (lexer_scan_identifier (context_p))
          {
            lexer_check_property_modifier (context_p);
          }

#if ENABLED (JERRY_ES2015)
          if (context_p->token.type == LEXER_LEFT_SQUARE)
          {
            parser_stack_push_uint8 (context_p, SCAN_STACK_COMPUTED_PROPERTY);
            scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
            break;
          }
#endif /* ENABLED (JERRY_ES2015) */

          if (context_p->token.type == LEXER_RIGHT_BRACE)
          {
            scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
            continue;
          }

          if (context_p->token.type == LEXER_PROPERTY_GETTER
#if ENABLED (JERRY_ES2015)
              || context_p->token.type == LEXER_KEYW_ASYNC
              || context_p->token.type == LEXER_MULTIPLY
#endif /* ENABLED (JERRY_ES2015) */
              || context_p->token.type == LEXER_PROPERTY_SETTER)
          {
            uint16_t literal_pool_flags = SCANNER_LITERAL_POOL_FUNCTION;

#if ENABLED (JERRY_ES2015)
            if (context_p->token.type == LEXER_MULTIPLY)
            {
              literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR;
            }
            else if (context_p->token.type == LEXER_KEYW_ASYNC)
            {
              literal_pool_flags |= SCANNER_LITERAL_POOL_ASYNC;

              if (lexer_consume_generator (context_p))
              {
                literal_pool_flags |= SCANNER_LITERAL_POOL_GENERATOR;
              }
            }
#endif /* ENABLED (JERRY_ES2015) */

            parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY);
            lexer_scan_identifier (context_p);

#if ENABLED (JERRY_ES2015)
            if (context_p->token.type == LEXER_LEFT_SQUARE)
            {
              parser_stack_push_uint8 (context_p, SCANNER_FROM_LITERAL_POOL_TO_COMPUTED (literal_pool_flags));
              scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
              break;
            }
#endif /* ENABLED (JERRY_ES2015) */

            if (context_p->token.type != LEXER_LITERAL)
            {
              scanner_raise_error (context_p);
            }

            scanner_push_literal_pool (context_p, &scanner_context, literal_pool_flags);
            scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS;
            break;
          }

          if (context_p->token.type != LEXER_LITERAL)
          {
            scanner_raise_error (context_p);
          }

#if ENABLED (JERRY_ES2015)
          parser_line_counter_t start_line = context_p->token.line;
          parser_line_counter_t start_column = context_p->token.column;
          bool is_ident = (context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
#endif /* ENABLED (JERRY_ES2015) */

          lexer_next_token (context_p);

#if ENABLED (JERRY_ES2015)
          if (context_p->token.type == LEXER_LEFT_PAREN)
          {
            scanner_push_literal_pool (context_p, &scanner_context, SCANNER_LITERAL_POOL_FUNCTION);

            parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY);
            scanner_context.mode = SCAN_MODE_FUNCTION_ARGUMENTS;
            continue;
          }

          if (is_ident
              && (context_p->token.type == LEXER_COMMA
                  || context_p->token.type == LEXER_RIGHT_BRACE
                  || context_p->token.type == LEXER_ASSIGN))
          {
            context_p->source_p = context_p->token.lit_location.char_p;
            context_p->line = start_line;
            context_p->column = start_column;

            lexer_next_token (context_p);

            JERRY_ASSERT (context_p->token.type != LEXER_LITERAL
                          || context_p->token.lit_location.type == LEXER_IDENT_LITERAL);

            if (context_p->token.type != LEXER_LITERAL)
            {
              scanner_raise_error (context_p);
            }

            if (scanner_context.binding_type != SCANNER_BINDING_NONE)
            {
              scanner_context.mode = SCAN_MODE_BINDING;
              continue;
            }

            scanner_add_reference (context_p, &scanner_context);

            lexer_next_token (context_p);

            if (context_p->token.type == LEXER_ASSIGN)
            {
              scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
              break;
            }

            scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
            continue;
          }
#endif /* ENABLED (JERRY_ES2015) */

          if (context_p->token.type != LEXER_COLON)
          {
            scanner_raise_error (context_p);
          }

          scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;

#if ENABLED (JERRY_ES2015)
          if (scanner_context.binding_type != SCANNER_BINDING_NONE)
          {
            scanner_context.mode = SCAN_MODE_BINDING;
          }
#endif /* ENABLED (JERRY_ES2015) */
          break;
        }
#if ENABLED (JERRY_ES2015)
        case SCAN_MODE_BINDING:
        {
          JERRY_ASSERT (scanner_context.binding_type == SCANNER_BINDING_VAR
                        || scanner_context.binding_type == SCANNER_BINDING_LET
                        || scanner_context.binding_type == SCANNER_BINDING_CATCH
                        || scanner_context.binding_type == SCANNER_BINDING_CONST
                        || scanner_context.binding_type == SCANNER_BINDING_ARG
                        || scanner_context.binding_type == SCANNER_BINDING_ARROW_ARG);

          if (type == LEXER_THREE_DOTS)
          {
            lexer_next_token (context_p);
            type = (lexer_token_type_t) context_p->token.type;
          }

          if (type == LEXER_LEFT_SQUARE || type == LEXER_LEFT_BRACE)
          {
            scanner_push_destructuring_pattern (context_p, &scanner_context, scanner_context.binding_type, true);

            if (type == LEXER_LEFT_SQUARE)
            {
              parser_stack_push_uint8 (context_p, SCAN_STACK_ARRAY_LITERAL);
              break;
            }

            parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL);
            scanner_context.mode = SCAN_MODE_PROPERTY_NAME;
            continue;
          }

          if (type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
          {
            scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
            continue;
          }

          lexer_lit_location_t *literal_p = scanner_add_literal (context_p, &scanner_context);

          scanner_context.mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;

          if (scanner_context.binding_type == SCANNER_BINDING_VAR)
          {
            if (!(literal_p->type & SCANNER_LITERAL_IS_VAR))
            {
              scanner_detect_invalid_var (context_p, &scanner_context, literal_p);
              literal_p->type |= SCANNER_LITERAL_IS_VAR;

              if (scanner_context.active_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH)
              {
                literal_p->type |= SCANNER_LITERAL_NO_REG;
              }
            }
            break;
          }

          if (scanner_context.binding_type == SCANNER_BINDING_ARROW_ARG)
          {
            literal_p->type |= SCANNER_LITERAL_IS_ARG | SCANNER_LITERAL_IS_ARROW_DESTRUCTURED_ARG;

            if (literal_p->type & SCANNER_LITERAL_IS_USED)
            {
              literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
              break;
            }
          }
          else
          {
            scanner_detect_invalid_let (context_p, literal_p);

            if (scanner_context.binding_type <= SCANNER_BINDING_CATCH)
            {
              JERRY_ASSERT ((scanner_context.binding_type == SCANNER_BINDING_LET)
                            || (scanner_context.binding_type == SCANNER_BINDING_CATCH));

              literal_p->type |= SCANNER_LITERAL_IS_LET;
            }
            else
            {
              literal_p->type |= SCANNER_LITERAL_IS_CONST;

              if (scanner_context.binding_type == SCANNER_BINDING_ARG)
              {
                literal_p->type |= SCANNER_LITERAL_IS_ARG;

                if (literal_p->type & SCANNER_LITERAL_IS_USED)
                {
                  literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
                  break;
                }
              }
            }

            if (literal_p->type & SCANNER_LITERAL_IS_USED)
            {
              literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
              break;
            }
          }

          scanner_binding_item_t *binding_item_p;
          binding_item_p = (scanner_binding_item_t *) scanner_malloc (context_p, sizeof (scanner_binding_item_t));

          binding_item_p->next_p = scanner_context.active_binding_list_p->items_p;
          binding_item_p->literal_p = literal_p;

          scanner_context.active_binding_list_p->items_p = binding_item_p;

          lexer_next_token (context_p);
          if (context_p->token.type != LEXER_ASSIGN)
          {
            continue;
          }

          scanner_binding_literal_t binding_literal;
          binding_literal.literal_p = literal_p;

          parser_stack_push (context_p, &binding_literal, sizeof (scanner_binding_literal_t));
          parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_INIT);

          scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION;
          break;
        }
#endif /* ENABLED (JERRY_ES2015) */
      }

      lexer_next_token (context_p);
    }

scan_completed:
    if (context_p->stack_top_uint8 != SCAN_STACK_SCRIPT
        && context_p->stack_top_uint8 != SCAN_STACK_SCRIPT_FUNCTION)
    {
      scanner_raise_error (context_p);
    }

    scanner_pop_literal_pool (context_p, &scanner_context);

#if ENABLED (JERRY_ES2015)
    JERRY_ASSERT (scanner_context.active_binding_list_p == NULL);
#endif /* ENABLED (JERRY_ES2015) */
    JERRY_ASSERT (scanner_context.active_literal_pool_p == NULL);

#ifndef JERRY_NDEBUG
    scanner_context.context_status_flags |= PARSER_SCANNING_SUCCESSFUL;
#endif /* !JERRY_NDEBUG */
  }
  PARSER_CATCH
  {
#if ENABLED (JERRY_ES2015)
    while (scanner_context.active_binding_list_p != NULL)
    {
      scanner_pop_binding_list (&scanner_context);
    }
#endif /* ENABLED (JERRY_ES2015) */

    if (JERRY_UNLIKELY (context_p->error != PARSER_ERR_OUT_OF_MEMORY))
    {
      /* Ignore the errors thrown by the lexer. */
      context_p->error = PARSER_ERR_NO_ERROR;

      /* The following code may allocate memory, so it is enclosed in a try/catch. */
      PARSER_TRY (context_p->try_buffer)
      {
  #if ENABLED (JERRY_ES2015)
        if (scanner_context.status_flags & SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION)
        {
          JERRY_ASSERT (scanner_context.async_source_p != NULL);

          scanner_info_t *info_p;
          info_p = scanner_insert_info (context_p, scanner_context.async_source_p, sizeof (scanner_info_t));
          info_p->type = SCANNER_TYPE_ERR_ASYNC_FUNCTION;
        }
  #endif /* ENABLED (JERRY_ES2015) */

        while (scanner_context.active_literal_pool_p != NULL)
        {
          scanner_pop_literal_pool (context_p, &scanner_context);
        }
      }
      PARSER_CATCH
      {
        JERRY_ASSERT (context_p->error == PARSER_ERR_OUT_OF_MEMORY);
      }
      PARSER_TRY_END
    }

    JERRY_ASSERT (context_p->error == PARSER_ERR_NO_ERROR || context_p->error == PARSER_ERR_OUT_OF_MEMORY);

    if (context_p->error == PARSER_ERR_OUT_OF_MEMORY)
    {
      while (scanner_context.active_literal_pool_p != NULL)
      {
        scanner_literal_pool_t *literal_pool_p = scanner_context.active_literal_pool_p;

        scanner_context.active_literal_pool_p = literal_pool_p->prev_p;

        parser_list_free (&literal_pool_p->literal_pool);
        scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t));
      }

      parser_stack_free (context_p);
      return;
    }
  }
  PARSER_TRY_END

  context_p->status_flags = scanner_context.context_status_flags;
  scanner_reverse_info_list (context_p);

#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
  if (context_p->is_show_opcodes)
  {
    scanner_info_t *info_p = context_p->next_scanner_info_p;
    const uint8_t *source_start_p = (arg_list_p == NULL) ? source_p : arg_list_p;

    while (info_p->type != SCANNER_TYPE_END)
    {
      const char *name_p = NULL;
      bool print_location = false;

      switch (info_p->type)
      {
        case SCANNER_TYPE_END_ARGUMENTS:
        {
          JERRY_DEBUG_MSG ("  END_ARGUMENTS\n");
          source_start_p = source_p;
          break;
        }
        case SCANNER_TYPE_FUNCTION:
        case SCANNER_TYPE_BLOCK:
        {
          const uint8_t *prev_source_p = info_p->source_p - 1;
          const uint8_t *data_p;

          if (info_p->type == SCANNER_TYPE_FUNCTION)
          {
            data_p = (const uint8_t *) (info_p + 1);

            JERRY_DEBUG_MSG ("  FUNCTION: flags: 0x%x declarations: %d",
                             (int) info_p->u8_arg,
                             (int) info_p->u16_arg);
          }
          else
          {
            data_p = (const uint8_t *) (info_p + 1);

            JERRY_DEBUG_MSG ("  BLOCK:");
          }

          JERRY_DEBUG_MSG (" source:%d\n", (int) (info_p->source_p - source_start_p));

          while (data_p[0] != SCANNER_STREAM_TYPE_END)
          {
            switch (data_p[0] & SCANNER_STREAM_TYPE_MASK)
            {
              case SCANNER_STREAM_TYPE_VAR:
              {
                JERRY_DEBUG_MSG ("    VAR ");
                break;
              }
#if ENABLED (JERRY_ES2015)
              case SCANNER_STREAM_TYPE_LET:
              {
                JERRY_DEBUG_MSG ("    LET ");
                break;
              }
              case SCANNER_STREAM_TYPE_CONST:
              {
                JERRY_DEBUG_MSG ("    CONST ");
                break;
              }
              case SCANNER_STREAM_TYPE_LOCAL:
              {
                JERRY_DEBUG_MSG ("    LOCAL ");
                break;
              }
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
              case SCANNER_STREAM_TYPE_IMPORT:
              {
                JERRY_DEBUG_MSG ("    IMPORT ");
                break;
              }
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
              case SCANNER_STREAM_TYPE_ARG:
              {
                JERRY_DEBUG_MSG ("    ARG ");
                break;
              }
#if ENABLED (JERRY_ES2015)
              case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG:
              {
                JERRY_DEBUG_MSG ("    DESTRUCTURED_ARG ");
                break;
              }
#endif /* ENABLED (JERRY_ES2015) */
              case SCANNER_STREAM_TYPE_ARG_FUNC:
              {
                JERRY_DEBUG_MSG ("    ARG_FUNC ");
                break;
              }
#if ENABLED (JERRY_ES2015)
              case SCANNER_STREAM_TYPE_DESTRUCTURED_ARG_FUNC:
              {
                JERRY_DEBUG_MSG ("    DESTRUCTURED_ARG_FUNC ");
                break;
              }
#endif /* ENABLED (JERRY_ES2015) */
              case SCANNER_STREAM_TYPE_FUNC:
              {
                JERRY_DEBUG_MSG ("    FUNC ");
                break;
              }
              default:
              {
                JERRY_ASSERT ((data_p[0] & SCANNER_STREAM_TYPE_MASK) == SCANNER_STREAM_TYPE_HOLE);
                JERRY_DEBUG_MSG ("    HOLE\n");
                data_p++;
                continue;
              }
            }

            size_t length;

            if (!(data_p[0] & SCANNER_STREAM_UINT16_DIFF))
            {
              if (data_p[2] != 0)
              {
                prev_source_p += data_p[2];
                length = 2 + 1;
              }
              else
              {
                memcpy (&prev_source_p, data_p + 2 + 1, sizeof (const uint8_t *));
                length = 2 + 1 + sizeof (const uint8_t *);
              }
            }
            else
            {
              int32_t diff = ((int32_t) data_p[2]) | ((int32_t) data_p[3]) << 8;

              if (diff <= UINT8_MAX)
              {
                diff = -diff;
              }

              prev_source_p += diff;
              length = 2 + 2;
            }

#if ENABLED (JERRY_ES2015)
            if (data_p[0] & SCANNER_STREAM_EARLY_CREATE)
            {
              JERRY_ASSERT (data_p[0] & SCANNER_STREAM_NO_REG);
              JERRY_DEBUG_MSG ("*");
            }
#endif /* ENABLED (JERRY_ES2015) */

            if (data_p[0] & SCANNER_STREAM_NO_REG)
            {
              JERRY_DEBUG_MSG ("* ");
            }

            JERRY_DEBUG_MSG ("'%.*s'\n", data_p[1], (char *) prev_source_p);
            prev_source_p += data_p[1];
            data_p += length;
          }
          break;
        }
        case SCANNER_TYPE_WHILE:
        {
          name_p = "WHILE";
          print_location = true;
          break;
        }
        case SCANNER_TYPE_FOR:
        {
          scanner_for_info_t *for_info_p = (scanner_for_info_t *) info_p;
          JERRY_DEBUG_MSG ("  FOR: source:%d expression:%d[%d:%d] end:%d[%d:%d]\n",
                           (int) (for_info_p->info.source_p - source_start_p),
                           (int) (for_info_p->expression_location.source_p - source_start_p),
                           (int) for_info_p->expression_location.line,
                           (int) for_info_p->expression_location.column,
                           (int) (for_info_p->end_location.source_p - source_start_p),
                           (int) for_info_p->end_location.line,
                           (int) for_info_p->end_location.column);
          break;
        }
        case SCANNER_TYPE_FOR_IN:
        {
          name_p = "FOR-IN";
          print_location = true;
          break;
        }
#if ENABLED (JERRY_ES2015)
        case SCANNER_TYPE_FOR_OF:
        {
          name_p = "FOR-OF";
          print_location = true;
          break;
        }
#endif /* ENABLED (JERRY_ES2015) */
        case SCANNER_TYPE_SWITCH:
        {
          JERRY_DEBUG_MSG ("  SWITCH: source:%d\n",
                           (int) (info_p->source_p - source_start_p));

          scanner_case_info_t *current_case_p = ((scanner_switch_info_t *) info_p)->case_p;

          while (current_case_p != NULL)
          {
            JERRY_DEBUG_MSG ("    CASE: location:%d[%d:%d]\n",
                             (int) (current_case_p->location.source_p - source_start_p),
                             (int) current_case_p->location.line,
                             (int) current_case_p->location.column);

            current_case_p = current_case_p->next_p;
          }
          break;
        }
        case SCANNER_TYPE_CASE:
        {
          name_p = "CASE";
          print_location = true;
          break;
        }
#if ENABLED (JERRY_ES2015)
        case SCANNER_TYPE_INITIALIZER:
        {
          name_p = "INITIALIZER";
          print_location = true;
          break;
        }
        case SCANNER_TYPE_CLASS_CONSTRUCTOR:
        {
          JERRY_DEBUG_MSG ("  CLASS-CONSTRUCTOR: source:%d\n",
                           (int) (info_p->source_p - source_start_p));
          print_location = false;
          break;
        }
        case SCANNER_TYPE_LET_EXPRESSION:
        {
          JERRY_DEBUG_MSG ("  LET_EXPRESSION: source:%d\n",
                           (int) (info_p->source_p - source_start_p));
          break;
        }
        case SCANNER_TYPE_ERR_REDECLARED:
        {
          JERRY_DEBUG_MSG ("  ERR_REDECLARED: source:%d\n",
                           (int) (info_p->source_p - source_start_p));
          break;
        }
        case SCANNER_TYPE_ERR_ASYNC_FUNCTION:
        {
          JERRY_DEBUG_MSG ("  ERR_ASYNC_FUNCTION: source:%d\n",
                           (int) (info_p->source_p - source_start_p));
          break;
        }
#endif /* ENABLED (JERRY_ES2015) */
      }

      if (print_location)
      {
        scanner_location_info_t *location_info_p = (scanner_location_info_t *) info_p;
        JERRY_DEBUG_MSG ("  %s: source:%d location:%d[%d:%d]\n",
                         name_p,
                         (int) (location_info_p->info.source_p - source_start_p),
                         (int) (location_info_p->location.source_p - source_start_p),
                         (int) location_info_p->location.line,
                         (int) location_info_p->location.column);
      }

      info_p = info_p->next_p;
    }

    JERRY_DEBUG_MSG ("\n--- Scanning end ---\n\n");
  }
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */

  parser_stack_free (context_p);
} /* scanner_scan_all */


scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context */
                                     scanner_context_t *scanner_context_p, /**< scanner context */
                                     lexer_token_type_t type, /**< current token type */
                                     scan_stack_modes_t stack_top) /**< current stack top */
{
  if (type == LEXER_COMMA)
  {
    switch (stack_top)
    {
      case SCAN_STACK_VAR:
#if ENABLED (JERRY_ES2015)
      case SCAN_STACK_LET:
      case SCAN_STACK_CONST:
#endif /* ENABLED (JERRY_ES2015) */
      case SCAN_STACK_FOR_VAR_START:
#if ENABLED (JERRY_ES2015)
      case SCAN_STACK_FOR_LET_START:
      case SCAN_STACK_FOR_CONST_START:
#endif /* ENABLED (JERRY_ES2015) */
      {
        scanner_context_p->mode = SCAN_MODE_VAR_STATEMENT;
        return SCAN_NEXT_TOKEN;
      }
      case SCAN_STACK_COLON_EXPRESSION:
      {
        scanner_raise_error (context_p);
        break;
      }
#if ENABLED (JERRY_ES2015)
      case SCAN_STACK_BINDING_INIT:
      case SCAN_STACK_BINDING_LIST_INIT:
      {
        break;
      }
      case SCAN_STACK_ARROW_ARGUMENTS:
      {
        lexer_next_token (context_p);
        scanner_check_arrow_arg (context_p, scanner_context_p);
        return SCAN_KEEP_TOKEN;
      }
      case SCAN_STACK_ARROW_EXPRESSION:
      {
        break;
      }
      case SCAN_STACK_FUNCTION_PARAMETERS:
      {
        scanner_context_p->mode = SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS;
        parser_stack_pop_uint8 (context_p);
        return SCAN_NEXT_TOKEN;
      }
      case SCAN_STACK_ARRAY_LITERAL:
      {
        scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;

        if (scanner_context_p->binding_type != SCANNER_BINDING_NONE)
        {
          scanner_context_p->mode = SCAN_MODE_BINDING;
        }

        return SCAN_NEXT_TOKEN;
      }
#endif /* ENABLED (JERRY_ES2015) */
      case SCAN_STACK_OBJECT_LITERAL:
      {
        scanner_context_p->mode = SCAN_MODE_PROPERTY_NAME;
        return SCAN_KEEP_TOKEN;
      }
      default:
      {
        scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
        return SCAN_NEXT_TOKEN;
      }
    }
  }

  switch (stack_top)
  {
    case SCAN_STACK_WITH_EXPRESSION:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      parser_stack_pop_uint8 (context_p);

      uint16_t status_flags = scanner_context_p->active_literal_pool_p->status_flags;
      parser_stack_push_uint8 (context_p, (status_flags & SCANNER_LITERAL_POOL_IN_WITH) ? 1 : 0);
      parser_stack_push_uint8 (context_p, SCAN_STACK_WITH_STATEMENT);
      status_flags |= SCANNER_LITERAL_POOL_IN_WITH;
      scanner_context_p->active_literal_pool_p->status_flags = status_flags;

      scanner_context_p->mode = SCAN_MODE_STATEMENT;
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_DO_EXPRESSION:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      scanner_context_p->mode = SCAN_MODE_STATEMENT_END;
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_WHILE_EXPRESSION:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      scanner_source_start_t source_start;

      parser_stack_pop_uint8 (context_p);
      parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t));

      scanner_location_info_t *location_info_p;
      location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p,
                                                                         source_start.source_p,
                                                                         sizeof (scanner_location_info_t));
      location_info_p->info.type = SCANNER_TYPE_WHILE;

      scanner_get_location (&location_info_p->location, context_p);

      scanner_context_p->mode = SCAN_MODE_STATEMENT;
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_PAREN_EXPRESSION:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      parser_stack_pop_uint8 (context_p);

#if ENABLED (JERRY_ES2015)
      if (context_p->stack_top_uint8 == SCAN_STACK_USE_ASYNC)
      {
        scanner_add_async_literal (context_p, scanner_context_p);
      }
#endif /* ENABLED (JERRY_ES2015) */

      scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_STATEMENT_WITH_EXPR:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      parser_stack_pop_uint8 (context_p);

#if ENABLED (JERRY_ES2015)
      if (context_p->stack_top_uint8 == SCAN_STACK_IF_STATEMENT)
      {
        scanner_check_function_after_if (context_p, scanner_context_p);
        return SCAN_KEEP_TOKEN;
      }
#endif /* ENABLED (JERRY_ES2015) */

      scanner_context_p->mode = SCAN_MODE_STATEMENT;
      return SCAN_NEXT_TOKEN;
    }
#if ENABLED (JERRY_ES2015)
    case SCAN_STACK_BINDING_LIST_INIT:
    {
      parser_stack_pop_uint8 (context_p);

      JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_ARRAY_LITERAL
                    || context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL
                    || context_p->stack_top_uint8 == SCAN_STACK_LET
                    || context_p->stack_top_uint8 == SCAN_STACK_CONST
                    || context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START
                    || context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START
                    || context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_PARAMETERS
                    || context_p->stack_top_uint8 == SCAN_STACK_ARROW_ARGUMENTS);

      scanner_binding_item_t *item_p = scanner_context_p->active_binding_list_p->items_p;

      while (item_p != NULL)
      {
        if (item_p->literal_p->type & SCANNER_LITERAL_IS_USED)
        {
          item_p->literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
        }
        item_p = item_p->next_p;
      }

      scanner_pop_binding_list (scanner_context_p);
      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
      return SCAN_KEEP_TOKEN;
    }
    case SCAN_STACK_BINDING_INIT:
    {
      scanner_binding_literal_t binding_literal;

      parser_stack_pop_uint8 (context_p);
      parser_stack_pop (context_p, &binding_literal, sizeof (scanner_binding_literal_t));

      JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_ARRAY_LITERAL
                    || context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL
                    || context_p->stack_top_uint8 == SCAN_STACK_LET
                    || context_p->stack_top_uint8 == SCAN_STACK_CONST
                    || context_p->stack_top_uint8 == SCAN_STACK_FOR_LET_START
                    || context_p->stack_top_uint8 == SCAN_STACK_FOR_CONST_START
                    || context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_PARAMETERS
                    || context_p->stack_top_uint8 == SCAN_STACK_ARROW_ARGUMENTS);

      JERRY_ASSERT ((stack_top != SCAN_STACK_ARRAY_LITERAL && stack_top != SCAN_STACK_OBJECT_LITERAL)
                    || SCANNER_NEEDS_BINDING_LIST (scanner_context_p->binding_type));

      if (binding_literal.literal_p->type & SCANNER_LITERAL_IS_USED)
      {
        binding_literal.literal_p->type |= SCANNER_LITERAL_EARLY_CREATE;
      }

      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
      return SCAN_KEEP_TOKEN;
    }
#endif /* ENABLED (JERRY_ES2015) */
    case SCAN_STACK_VAR:
#if ENABLED (JERRY_ES2015)
    case SCAN_STACK_LET:
    case SCAN_STACK_CONST:
#endif /* ENABLED (JERRY_ES2015) */
    {
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
      scanner_context_p->active_literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_EXPORT;
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */

      parser_stack_pop_uint8 (context_p);
      return SCAN_KEEP_TOKEN;
    }
    case SCAN_STACK_FOR_VAR_START:
#if ENABLED (JERRY_ES2015)
    case SCAN_STACK_FOR_LET_START:
    case SCAN_STACK_FOR_CONST_START:
#endif /* ENABLED (JERRY_ES2015) */
    case SCAN_STACK_FOR_START:
    {
      if (type == LEXER_KEYW_IN || SCANNER_IDENTIFIER_IS_OF ())
      {
        scanner_for_statement_t for_statement;

        parser_stack_pop_uint8 (context_p);
        parser_stack_pop (context_p, &for_statement, sizeof (scanner_for_statement_t));

        scanner_location_info_t *location_info;
        location_info = (scanner_location_info_t *) scanner_insert_info (context_p,
                                                                         for_statement.u.source_p,
                                                                         sizeof (scanner_location_info_t));
#if ENABLED (JERRY_ES2015)
        location_info->info.type = (type == LEXER_KEYW_IN) ? SCANNER_TYPE_FOR_IN : SCANNER_TYPE_FOR_OF;

        if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START)
        {
          parser_stack_push_uint8 (context_p, SCAN_STACK_PRIVATE_BLOCK_EARLY);
        }
#else /* !ENABLED (JERRY_ES2015) */
        location_info->info.type = SCANNER_TYPE_FOR_IN;
#endif /* ENABLED (JERRY_ES2015) */

        scanner_get_location (&location_info->location, context_p);

        parser_stack_push_uint8 (context_p, SCAN_STACK_STATEMENT_WITH_EXPR);
        scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
        return SCAN_NEXT_TOKEN;
      }

      if (type != LEXER_SEMICOLON)
      {
        break;
      }

      scanner_for_statement_t for_statement;

      parser_stack_pop_uint8 (context_p);
      parser_stack_pop (context_p, NULL, sizeof (scanner_for_statement_t));

#if ENABLED (JERRY_ES2015)
      if (stack_top == SCAN_STACK_FOR_LET_START || stack_top == SCAN_STACK_FOR_CONST_START)
      {
        parser_stack_push_uint8 (context_p, SCAN_STACK_PRIVATE_BLOCK);
      }
#endif /* ENABLED (JERRY_ES2015) */

      for_statement.u.source_p = context_p->source_p;
      parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t));
      parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_CONDITION);

      lexer_next_token (context_p);

      if (context_p->token.type != LEXER_SEMICOLON)
      {
        scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
        return SCAN_KEEP_TOKEN;
      }

      type = LEXER_SEMICOLON;
      /* FALLTHRU */
    }
    case SCAN_STACK_FOR_CONDITION:
    {
      if (type != LEXER_SEMICOLON)
      {
        break;
      }

      scanner_for_statement_t for_statement;

      parser_stack_pop_uint8 (context_p);
      parser_stack_pop (context_p, &for_statement, sizeof (scanner_for_statement_t));

      scanner_for_info_t *for_info_p;
      for_info_p = (scanner_for_info_t *) scanner_insert_info (context_p,
                                                               for_statement.u.source_p,
                                                               sizeof (scanner_for_info_t));
      for_info_p->info.type = SCANNER_TYPE_FOR;

      scanner_get_location (&for_info_p->expression_location, context_p);
      for_info_p->end_location.source_p = NULL;

      for_statement.u.for_info_p = for_info_p;

      parser_stack_push (context_p, &for_statement, sizeof (scanner_for_statement_t));
      parser_stack_push_uint8 (context_p, SCAN_STACK_FOR_EXPRESSION);

      lexer_next_token (context_p);

      if (context_p->token.type != LEXER_RIGHT_PAREN)
      {
        scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
        return SCAN_KEEP_TOKEN;
      }

      type = LEXER_RIGHT_PAREN;
      /* FALLTHRU */
    }
    case SCAN_STACK_FOR_EXPRESSION:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      scanner_for_statement_t for_statement;

      parser_stack_pop_uint8 (context_p);
      parser_stack_pop (context_p, &for_statement, sizeof (scanner_for_statement_t));

      scanner_get_location (&for_statement.u.for_info_p->end_location, context_p);

      scanner_context_p->mode = SCAN_MODE_STATEMENT;
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_SWITCH_EXPRESSION:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      lexer_next_token (context_p);

      if (context_p->token.type != LEXER_LEFT_BRACE)
      {
        break;
      }

#if ENABLED (JERRY_ES2015)
      scanner_literal_pool_t *literal_pool_p;
      literal_pool_p = scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_BLOCK);
      literal_pool_p->source_p = context_p->source_p - 1;
#endif /* ENABLED (JERRY_ES2015) */

      parser_stack_pop_uint8 (context_p);

      scanner_switch_statement_t switch_statement = scanner_context_p->active_switch_statement;
      parser_stack_push (context_p, &switch_statement, sizeof (scanner_switch_statement_t));
      parser_stack_push_uint8 (context_p, SCAN_STACK_SWITCH_BLOCK);

      scanner_switch_info_t *switch_info_p;
      switch_info_p = (scanner_switch_info_t *) scanner_insert_info (context_p,
                                                                     context_p->source_p,
                                                                     sizeof (scanner_switch_info_t));
      switch_info_p->info.type = SCANNER_TYPE_SWITCH;
      switch_info_p->case_p = NULL;
      scanner_context_p->active_switch_statement.last_case_p = &switch_info_p->case_p;

      lexer_next_token (context_p);

      if (context_p->token.type != LEXER_RIGHT_BRACE
          && context_p->token.type != LEXER_KEYW_CASE
          && context_p->token.type != LEXER_KEYW_DEFAULT)
      {
        break;
      }

      scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR;
      return SCAN_KEEP_TOKEN;
    }
    case SCAN_STACK_CASE_STATEMENT:
    {
      if (type != LEXER_COLON)
      {
        break;
      }

      scanner_source_start_t source_start;

      parser_stack_pop_uint8 (context_p);
      parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t));

      scanner_location_info_t *location_info_p;
      location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p,
                                                                         source_start.source_p,
                                                                         sizeof (scanner_location_info_t));
      location_info_p->info.type = SCANNER_TYPE_CASE;

      scanner_get_location (&location_info_p->location, context_p);

      scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR;
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_COLON_EXPRESSION:
    {
      if (type != LEXER_COLON)
      {
        break;
      }

      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
      parser_stack_pop_uint8 (context_p);
      return SCAN_NEXT_TOKEN;
    }
#if ENABLED (JERRY_ES2015)
    case SCAN_STACK_ARRAY_LITERAL:
    case SCAN_STACK_OBJECT_LITERAL:
    {
      if (((stack_top == SCAN_STACK_ARRAY_LITERAL) && (type != LEXER_RIGHT_SQUARE))
          || ((stack_top == SCAN_STACK_OBJECT_LITERAL) && (type != LEXER_RIGHT_BRACE)))
      {
        break;
      }

      scanner_source_start_t source_start;
      uint8_t binding_type = scanner_context_p->binding_type;

      parser_stack_pop_uint8 (context_p);
      scanner_context_p->binding_type = context_p->stack_top_uint8;
      parser_stack_pop_uint8 (context_p);
      parser_stack_pop (context_p, &source_start, sizeof (scanner_source_start_t));

      lexer_next_token (context_p);

      if (binding_type == SCANNER_BINDING_CATCH && context_p->stack_top_uint8 == SCAN_STACK_CATCH_STATEMENT)
      {
        scanner_pop_binding_list (scanner_context_p);

        if (context_p->token.type != LEXER_RIGHT_PAREN)
        {
          scanner_raise_error (context_p);
        }

        lexer_next_token (context_p);

        if (context_p->token.type != LEXER_LEFT_BRACE)
        {
          scanner_raise_error (context_p);
        }

        scanner_context_p->mode = SCAN_MODE_STATEMENT_OR_TERMINATOR;
        return SCAN_NEXT_TOKEN;
      }

      if (context_p->token.type != LEXER_ASSIGN)
      {
        if (SCANNER_NEEDS_BINDING_LIST (binding_type))
        {
          scanner_pop_binding_list (scanner_context_p);
        }

        scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;
        return SCAN_KEEP_TOKEN;
      }

      scanner_location_info_t *location_info_p;
      location_info_p = (scanner_location_info_t *) scanner_insert_info (context_p,
                                                                         source_start.source_p,
                                                                         sizeof (scanner_location_info_t));
      location_info_p->info.type = SCANNER_TYPE_INITIALIZER;
      scanner_get_location (&location_info_p->location, context_p);
      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;

      if (SCANNER_NEEDS_BINDING_LIST (binding_type))
      {
        scanner_binding_item_t *item_p = scanner_context_p->active_binding_list_p->items_p;

        while (item_p != NULL)
        {
          item_p->literal_p->type &= (uint8_t) ~SCANNER_LITERAL_IS_USED;
          item_p = item_p->next_p;
        }

        parser_stack_push_uint8 (context_p, SCAN_STACK_BINDING_LIST_INIT);
      }
      return SCAN_NEXT_TOKEN;
    }
#else /* !ENABLED (JERRY_ES2015) */
    case SCAN_STACK_ARRAY_LITERAL:
#endif /* ENABLED (JERRY_ES2015) */
    case SCAN_STACK_PROPERTY_ACCESSOR:
    {
      if (type != LEXER_RIGHT_SQUARE)
      {
        break;
      }

      scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;
      parser_stack_pop_uint8 (context_p);
      return SCAN_NEXT_TOKEN;
    }
#if !ENABLED (JERRY_ES2015)
    case SCAN_STACK_OBJECT_LITERAL:
    {
      if (type != LEXER_RIGHT_BRACE)
      {
        break;
      }

      scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;
      parser_stack_pop_uint8 (context_p);
      return SCAN_NEXT_TOKEN;
    }
#endif /* !ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_ES2015)
    case SCAN_STACK_COMPUTED_PROPERTY:
    {
      if (type != LEXER_RIGHT_SQUARE)
      {
        break;
      }

      lexer_next_token (context_p);

      parser_stack_pop_uint8 (context_p);
      stack_top = (scan_stack_modes_t) context_p->stack_top_uint8;

      if (stack_top == SCAN_STACK_FUNCTION_PROPERTY)
      {
        scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION);

        scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS;
        return SCAN_KEEP_TOKEN;
      }

      JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL);

      if (context_p->token.type == LEXER_LEFT_PAREN)
      {
        scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION);

        parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_PROPERTY);
        scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS;
        return SCAN_KEEP_TOKEN;
      }

      if (context_p->token.type != LEXER_COLON)
      {
        scanner_raise_error (context_p);
      }

      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;

      if (scanner_context_p->binding_type != SCANNER_BINDING_NONE)
      {
        scanner_context_p->mode = SCAN_MODE_BINDING;
      }
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_COMPUTED_GENERATOR:
    case SCAN_STACK_COMPUTED_ASYNC:
    case SCAN_STACK_COMPUTED_ASYNC_GENERATOR:
    {
      if (type != LEXER_RIGHT_SQUARE)
      {
        break;
      }

      lexer_next_token (context_p);
      parser_stack_pop_uint8 (context_p);

      JERRY_ASSERT (context_p->stack_top_uint8 == SCAN_STACK_OBJECT_LITERAL
                    || context_p->stack_top_uint8 == SCAN_STACK_FUNCTION_PROPERTY);

      uint16_t status_flags = (uint16_t) (SCANNER_LITERAL_POOL_FUNCTION
                                          | SCANNER_LITERAL_POOL_GENERATOR
                                          | SCANNER_FROM_COMPUTED_TO_LITERAL_POOL (stack_top));

      scanner_push_literal_pool (context_p, scanner_context_p, status_flags);

      scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS;
      return SCAN_KEEP_TOKEN;
    }
    case SCAN_STACK_TEMPLATE_STRING:
    case SCAN_STACK_TAGGED_TEMPLATE_LITERAL:
    {
      if (type != LEXER_RIGHT_BRACE)
      {
        break;
      }

      context_p->source_p--;
      context_p->column--;
      lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);

      if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)
      {
        scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
      }
      else
      {
        parser_stack_pop_uint8 (context_p);
        scanner_context_p->mode = SCAN_MODE_POST_PRIMARY_EXPRESSION;
      }
      return SCAN_NEXT_TOKEN;
    }
    case SCAN_STACK_ARROW_ARGUMENTS:
    {
      if (type != LEXER_RIGHT_PAREN)
      {
        break;
      }

      scanner_check_arrow (context_p, scanner_context_p);
      return SCAN_KEEP_TOKEN;
    }
    case SCAN_STACK_ARROW_EXPRESSION:
    {
      scanner_pop_literal_pool (context_p, scanner_context_p);
      parser_stack_pop_uint8 (context_p);
      lexer_update_await_yield (context_p, context_p->status_flags);
      scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
      return SCAN_KEEP_TOKEN;
    }
    case SCAN_STACK_CLASS_EXTENDS:
    {
      if (type != LEXER_LEFT_BRACE)
      {
        break;
      }

      scanner_context_p->mode = SCAN_MODE_CLASS_METHOD;
      parser_stack_pop_uint8 (context_p);

      return SCAN_KEEP_TOKEN;
    }
    case SCAN_STACK_FUNCTION_PARAMETERS:
    {
      parser_stack_pop_uint8 (context_p);

      if (type != LEXER_RIGHT_PAREN
          && (type != LEXER_EOS || context_p->stack_top_uint8 != SCAN_STACK_SCRIPT_FUNCTION))
      {
        break;
      }

      scanner_context_p->mode = SCAN_MODE_CONTINUE_FUNCTION_ARGUMENTS;
      return SCAN_KEEP_TOKEN;
    }
#endif /* ENABLED (JERRY_ES2015) */
    default:
    {
      scanner_context_p->mode = SCAN_MODE_STATEMENT_END;
      return SCAN_KEEP_TOKEN;
    }
  }

  scanner_raise_error (context_p);
  return SCAN_NEXT_TOKEN;
} /* scanner_scan_primary_expression_end */