Merge branch 'master' of git.savannah.gnu.org:/srv/git/emacs

This commit is contained in:
Eli Zaretskii 2024-04-01 14:21:10 +03:00
commit 601e772b06
14 changed files with 329 additions and 229 deletions

View file

@ -1218,6 +1218,12 @@ instead of:
This allows the user to specify command line arguments to the non
interactive Python interpreter specified by 'python-interpreter'.
** Scheme mode
Scheme mode now handles regular expression literal #/regexp/ that is
available in some Scheme implementations.
Also, it should now handle nested sexp-comments.
** use-package
+++
@ -1715,6 +1721,11 @@ Use a float value for the first argument instead.
Instead, use 'eshell-process-wait-time', which supports floating-point
values.
---
** The JSON parser sometimes signals different types of errors.
It will now signal 'json-utf8-decode-error' for inputs that are not
correctly UTF-8 encoded.
* Lisp Changes in Emacs 30.1
@ -2315,6 +2326,10 @@ this case, would mean repeating the object in the argument list.) When
replacing an object with a different one, passing both the new and old
objects is still necessary.
---
** The JSON encoder and decoder now accept arbitarily large integers.
Previously, they were limited to the range of signed 64-bit integers.
* Changes in Emacs 30.1 on Non-Free Operating Systems

View file

@ -3402,8 +3402,8 @@ lambda-expression."
(t "."))))
(let ((mutargs (function-get (car form) 'mutates-arguments)))
(when mutargs
(dolist (idx (if (eq mutargs 'all-but-last)
(number-sequence 1 (- (length form) 2))
(dolist (idx (if (symbolp mutargs)
(funcall mutargs form)
mutargs))
(let ((arg (nth idx form)))
(when (and (or (and (eq (car-safe arg) 'quote)
@ -3472,13 +3472,15 @@ lambda-expression."
(if byte-compile--for-effect
(byte-compile-discard)))))
(defun bytecomp--sort-call-in-place-p (form)
(or (= (length form) 3) ; old-style
(plist-get (cddr form) :in-place))) ; new-style
(defun bytecomp--actually-important-return-value-p (form)
"Whether FORM is really a call with a return value that should not go unused.
This assumes the function has the `important-return-value' property."
(cond ((eq (car form) 'sort)
;; For `sort', we only care about non-destructive uses.
(and (zerop (% (length form) 2)) ; new-style call
(not (plist-get (cddr form) :in-place))))
(not (bytecomp--sort-call-in-place-p form)))
(t t)))
(let ((important-return-value-fns
@ -3504,18 +3506,27 @@ This assumes the function has the `important-return-value' property."
(dolist (fn important-return-value-fns)
(put fn 'important-return-value t)))
(defun bytecomp--mutargs-nconc (form)
;; For `nconc', all arguments but the last are mutated.
(number-sequence 1 (- (length form) 2)))
(defun bytecomp--mutargs-sort (form)
;; For `sort', the first argument is mutated if the call is in-place.
(and (bytecomp--sort-call-in-place-p form) '(1)))
(let ((mutating-fns
;; FIXME: Should there be a function declaration for this?
;;
;; (FUNC . ARGS) means that FUNC mutates arguments whose indices are
;; in the list ARGS, starting at 1, or all but the last argument if
;; ARGS is `all-but-last'.
;; in the list ARGS, starting at 1. ARGS can also be a function
;; taking the function call form as argument and returning the
;; list of indices.
'(
(setcar 1) (setcdr 1) (aset 1)
(nreverse 1)
(nconc . all-but-last)
(nconc . bytecomp--mutargs-nconc)
(nbutlast 1) (ntake 2)
(sort 1)
(sort . bytecomp--mutargs-sort)
(delq 2) (delete 2)
(delete-dups 1) (delete-consecutive-dups 1)
(plist-put 1)

View file

@ -40,31 +40,34 @@
(require 'pcase)
(defconst ert-font-lock--face-symbol-re
(rx (one-or-more (or alphanumeric "-" "_" ".")))
"A face symbol matching regex.")
(rx (+ (or alphanumeric "-" "_" "." "/")))
"A face symbol matching regex.
The regexp cannot use character classes as these can be redefined by the
major mode of the host language.")
(defconst ert-font-lock--face-symbol-list-re
(rx "("
(* whitespace)
(one-or-more
(seq (regexp ert-font-lock--face-symbol-re)
(* whitespace)))
(? (regexp ert-font-lock--face-symbol-re))
(* (+ whitespace)
(regexp ert-font-lock--face-symbol-re))
(* whitespace)
")")
"A face symbol list matching regex.")
(defconst ert-font-lock--assertion-line-re
(rx
;; leading column assertion (arrow/caret)
(group (or "^" "<-"))
(zero-or-more whitespace)
(group-n 1 (or "^" "<-"))
(* whitespace)
;; possible to have many carets on an assertion line
(group (zero-or-more (seq "^" (zero-or-more whitespace))))
(group-n 2 (* "^" (* whitespace)))
;; optional negation of the face specification
(group (optional "!"))
(zero-or-more whitespace)
(group-n 3 (optional "!"))
(* whitespace)
;; face symbol name or a list of symbols
(group (or (regexp ert-font-lock--face-symbol-re)
(regexp ert-font-lock--face-symbol-list-re))))
(group-n 4 (or (regexp ert-font-lock--face-symbol-re)
(regexp ert-font-lock--face-symbol-list-re))))
"An ert-font-lock assertion line regex.")
(defun ert-font-lock--validate-major-mode (mode)

View file

@ -366,13 +366,19 @@ FUNCTION."
;; suitable options for specifying the mount namespace and
;; suchlike.
(setq
p (make-process
:name name :buffer buffer
:command (if (tramp-get-connection-property v "remote-namespace")
(append (list "su" "-mm" "-" user "-c") command)
(append (list "su" "-" user "-c") command))
:coding coding :noquery noquery :connection-type connection-type
:sentinel sentinel :stderr stderr))
p (let ((android-use-exec-loader nil))
(make-process
:name name
:buffer buffer
:command
(if (tramp-get-connection-property v "remote-namespace")
(append (list "su" "-mm" "-" user "-c") command)
(append (list "su" "-" user "-c") command))
:coding coding
:noquery noquery
:connection-type connection-type
:sentinel sentinel
:stderr stderr)))
;; Set filter. Prior Emacs 29.1, it doesn't work reliably
;; to provide it as `make-process' argument when filter is
;; t. See Bug#51177.

View file

@ -105,7 +105,7 @@
("2.3.5.26.3" . "26.3")
("2.4.3.27.1" . "27.1") ("2.4.5.27.2" . "27.2")
("2.5.2.28.1" . "28.1") ("2.5.3.28.2" . "28.2") ("2.5.4" . "28.3")
("2.6.0.29.1" . "29.1") ("2.6.2.29.2" . "29.2")))
("2.6.0.29.1" . "29.1") ("2.6.2.29.2" . "29.2") ("2.6.3-pre" . "29.3")))
(add-hook 'tramp-unload-hook
(lambda ()

View file

@ -50,6 +50,7 @@
;;; Code:
(require 'lisp-mode)
(eval-when-compile 'subr-x) ;For `named-let'.
(defvar scheme-mode-syntax-table
(let ((st (make-syntax-table))
@ -409,26 +410,73 @@ See `run-hooks'."
(defun scheme-syntax-propertize (beg end)
(goto-char beg)
(scheme-syntax-propertize-sexp-comment (point) end)
(scheme-syntax-propertize-sexp-comment end)
(scheme-syntax-propertize-regexp end)
(funcall
(syntax-propertize-rules
("\\(#\\);" (1 (prog1 "< cn"
(scheme-syntax-propertize-sexp-comment (point) end)))))
(scheme-syntax-propertize-sexp-comment end))))
("\\(#\\)/" (1 (when (null (nth 8 (save-excursion
(syntax-ppss (match-beginning 0)))))
(put-text-property
(match-beginning 1)
(match-end 1)
'syntax-table (string-to-syntax "|"))
(scheme-syntax-propertize-regexp end)
nil))))
(point) end))
(defun scheme-syntax-propertize-sexp-comment (_ end)
(let ((state (syntax-ppss)))
(defun scheme-syntax-propertize-sexp-comment (end)
(let ((state (syntax-ppss))
(checked (point)))
(when (eq 2 (nth 7 state))
;; It's a sexp-comment. Tell parse-partial-sexp where it ends.
(condition-case nil
(progn
(goto-char (+ 2 (nth 8 state)))
;; FIXME: this doesn't handle the case where the sexp
;; itself contains a #; comment.
(forward-sexp 1)
(put-text-property (1- (point)) (point)
'syntax-table (string-to-syntax "> cn")))
(scan-error (goto-char end))))))
(named-let loop ((startpos (+ 2 (nth 8 state))))
(let ((found nil))
(while
(progn
(setq found nil)
(condition-case nil
(progn
(goto-char startpos)
(forward-sexp 1)
(setq found (point)))
(scan-error (goto-char end)))
;; If there's a nested `#;', the syntax-tables will normally
;; consider the `;' to start a normal comment, so the
;; (forward-sexp 1) above may have landed at the wrong place.
;; So look for `#;' in the text over which we jumped, and
;; mark those we found as nested sexp-comments.
(let ((limit (or found end)))
(when (< checked limit)
(goto-char checked)
(when (re-search-forward "\\(#\\);" limit 'move)
(setq checked (point))
(put-text-property (match-beginning 1) (match-end 1)
'syntax-table
(string-to-syntax "< cn"))
(loop (point)))
(< (point) limit)))))
(when found
(goto-char found)
(put-text-property (1- found) found
'syntax-table (string-to-syntax "> cn"))))))))
(defun scheme-syntax-propertize-regexp (end)
(let* ((state (syntax-ppss))
(within-str (nth 3 state))
(start-delim-pos (nth 8 state)))
(when (and within-str
(char-equal ?# (char-after start-delim-pos)))
(while (and (re-search-forward "/" end 'move)
(eq -1
(% (save-excursion
(backward-char)
(skip-chars-backward "\\\\"))
2))))
(when (< (point) end)
(put-text-property (match-beginning 0) (match-end 0)
'syntax-table (string-to-syntax "|"))))))
;;;###autoload
(define-derived-mode dsssl-mode scheme-mode "DSSSL"

View file

@ -3194,12 +3194,6 @@ shell command and conveniently use this command."
(defvar-local sh--shellcheck-process nil)
(defalias 'sh--json-read
(if (fboundp 'json-parse-buffer)
(lambda () (json-parse-buffer :object-type 'alist))
(require 'json)
'json-read))
(defun sh-shellcheck-flymake (report-fn &rest _args)
"Flymake backend using the shellcheck program.
Takes a Flymake callback REPORT-FN as argument, as expected of a
@ -3223,7 +3217,7 @@ member of `flymake-diagnostic-functions'."
(with-current-buffer (process-buffer proc)
(goto-char (point-min))
(thread-last
(sh--json-read)
(json-parse-buffer :object-type 'alist)
(alist-get 'comments)
(seq-filter
(lambda (item)

View file

@ -6730,7 +6730,8 @@ android_root_closedir (struct android_vdir *vdir)
if (dir->directory)
closedir (dir->directory);
else if (root_fd_references--)
if (root_fd_references--)
;
else
{
@ -6745,13 +6746,7 @@ android_root_closedir (struct android_vdir *vdir)
static int
android_root_dirfd (struct android_vdir *vdir)
{
struct android_unix_vdir *dir;
dir = (struct android_unix_vdir *) vdir;
if (dir->directory)
return dirfd (dir->directory);
eassert (root_fd != -1);
return root_fd;
}
@ -6778,13 +6773,13 @@ android_root_opendir (struct android_vnode *vnode)
dir->directory = directory;
dir->index = 0;
if (!directory)
{
/* Allocate a temporary file descriptor for this ersatz root. */
if (root_fd < 0)
root_fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
root_fd_references++;
}
/* Allocate a temporary file descriptor for this ersatz root. This is
required regardless of the value of DIRECTORY, as android_fstatat
and co. will not defer to the VFS layer if a directory file
descriptor is not known to be special. */
if (root_fd < 0)
root_fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
root_fd_references++;
return &dir->vdir;
}

View file

@ -1689,6 +1689,22 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
bus, build_string (interface), build_string (member));
value = Fgethash (key, Vdbus_registered_objects_table, Qnil);
/* A signal could be registered with a nil interface or member. */
if (mtype == DBUS_MESSAGE_TYPE_SIGNAL)
{
key = list4 (QCsignal, bus, Qnil, build_string (member));
value = CALLN (Fappend, value,
Fgethash (key, Vdbus_registered_objects_table, Qnil));
key = list4 (QCsignal, bus, build_string (interface), Qnil);
value = CALLN (Fappend, value,
Fgethash (key, Vdbus_registered_objects_table, Qnil));
key = list4 (QCsignal, bus, Qnil, Qnil);
value = CALLN (Fappend, value,
Fgethash (key, Vdbus_registered_objects_table, Qnil));
}
/* Loop over the registered functions. Construct an event. */
for (; !NILP (value); value = CDR_SAFE (value))
{

View file

@ -699,24 +699,6 @@ usage: (json-insert OBJECT &rest ARGS) */)
}
/* Note that all callers of make_string_from_utf8 and build_string_from_utf8
below either pass only value UTF-8 strings or use the function for
formatting error messages; in the latter case correctness isn't
critical. */
/* Return a unibyte string containing the sequence of UTF-8 encoding
units of the UTF-8 representation of STRING. If STRING does not
represent a sequence of Unicode scalar values, return a string with
unspecified contents. */
static Lisp_Object
json_encode (Lisp_Object string)
{
/* FIXME: Raise an error if STRING is not a scalar value
sequence. */
return encode_string_utf_8 (string, Qnil, false, Qt, Qt);
}
#define JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE 64
#define JSON_PARSER_INTERNAL_BYTE_WORKSPACE_SIZE 512
@ -1081,52 +1063,21 @@ json_parse_unicode (struct json_parser *parser)
return v[0] << 12 | v[1] << 8 | v[2] << 4 | v[3];
}
/* Parses an utf-8 code-point encoding (except the first byte), and
returns the numeric value of the code-point (without considering
the first byte) */
static int
json_handle_utf8_tail_bytes (struct json_parser *parser, int n)
static AVOID
utf8_error (struct json_parser *parser)
{
int v = 0;
for (int i = 0; i < n; i++)
{
int c = json_input_get (parser);
json_byte_workspace_put (parser, c);
if ((c & 0xc0) != 0x80)
json_signal_error (parser, Qjson_utf8_decode_error);
v = (v << 6) | (c & 0x3f);
}
return v;
json_signal_error (parser, Qjson_utf8_decode_error);
}
/* Reads a JSON string, and puts the result into the byte workspace */
static void
json_parse_string (struct json_parser *parser)
/* Parse a string literal. Optionally prepend a ':'.
Return the string or an interned symbol. */
static Lisp_Object
json_parse_string (struct json_parser *parser, bool intern, bool leading_colon)
{
/* a single_uninteresting byte can be simply copied from the input
to output, it doesn't need any extra care. This means all the
characters between [0x20;0x7f], except the double quote and
the backslash */
static const char is_single_uninteresting[256] = {
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 1 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 2 */ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 3 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 4 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 5 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
/* 6 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 7 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 8 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* b */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* c */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* d */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* e */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
json_byte_workspace_reset (parser);
if (leading_colon)
json_byte_workspace_put (parser, ':');
ptrdiff_t chars_delta = 0; /* nchars - nbytes */
for (;;)
{
/* This if is only here for a possible speedup. If there are 4
@ -1138,10 +1089,10 @@ json_parse_string (struct json_parser *parser)
int c1 = parser->input_current[1];
int c2 = parser->input_current[2];
int c3 = parser->input_current[3];
bool v0 = is_single_uninteresting[c0];
bool v1 = is_single_uninteresting[c1];
bool v2 = is_single_uninteresting[c2];
bool v3 = is_single_uninteresting[c3];
bool v0 = json_plain_char[c0];
bool v1 = json_plain_char[c1];
bool v2 = json_plain_char[c2];
bool v3 = json_plain_char[c3];
if (v0 && v1 && v2 && v3)
{
json_byte_workspace_put (parser, c0);
@ -1156,43 +1107,62 @@ json_parse_string (struct json_parser *parser)
int c = json_input_get (parser);
parser->current_column++;
if (is_single_uninteresting[c])
if (json_plain_char[c])
{
json_byte_workspace_put (parser, c);
continue;
}
if (c == '"')
return;
else if (c & 0x80)
{
/* Handle utf-8 encoding */
ptrdiff_t nbytes
= parser->byte_workspace_current - parser->byte_workspace;
ptrdiff_t nchars = nbytes - chars_delta;
const char *str = (const char *)parser->byte_workspace;
return intern ? intern_c_multibyte (str, nchars, nbytes)
: make_multibyte_string (str, nchars, nbytes);
}
if (c & 0x80)
{
/* Parse UTF-8, strictly. This is the correct thing to do
whether or not the input is a unibyte or multibyte string. */
json_byte_workspace_put (parser, c);
if (c < 0xc0)
json_signal_error (parser, Qjson_utf8_decode_error);
else if (c < 0xe0)
unsigned char c1 = json_input_get (parser);
if ((c1 & 0xc0) != 0x80)
utf8_error (parser);
json_byte_workspace_put (parser, c1);
if (c <= 0xc1)
utf8_error (parser);
else if (c <= 0xdf)
chars_delta += 1;
else if (c <= 0xef)
{
int n = ((c & 0x1f) << 6
| json_handle_utf8_tail_bytes (parser, 1));
if (n < 0x80)
json_signal_error (parser, Qjson_utf8_decode_error);
unsigned char c2 = json_input_get (parser);
if ((c2 & 0xc0) != 0x80)
utf8_error (parser);
int v = ((c & 0x0f) << 12) + ((c1 & 0x3f) << 6) + (c2 & 0x3f);
if (v < 0x800 || (v >= 0xd800 && v <= 0xdfff))
utf8_error (parser);
json_byte_workspace_put (parser, c2);
chars_delta += 2;
}
else if (c < 0xf0)
else if (c <= 0xf7)
{
int n = ((c & 0xf) << 12
| json_handle_utf8_tail_bytes (parser, 2));
if (n < 0x800 || (n >= 0xd800 && n < 0xe000))
json_signal_error (parser, Qjson_utf8_decode_error);
}
else if (c < 0xf8)
{
int n = ((c & 0x7) << 18
| json_handle_utf8_tail_bytes (parser, 3));
if (n < 0x10000 || n > 0x10ffff)
json_signal_error (parser, Qjson_utf8_decode_error);
unsigned char c2 = json_input_get (parser);
unsigned char c3 = json_input_get (parser);
if ((c2 & 0xc0) != 0x80 || (c3 & 0xc0) != 0x80)
utf8_error (parser);
int v = (((c & 0x07) << 18) + ((c1 & 0x3f) << 12)
+ ((c2 & 0x3f) << 6) + (c3 & 0x3f));
if (v < 0x10000 || v > 0x10ffff)
utf8_error (parser);
json_byte_workspace_put (parser, c2);
json_byte_workspace_put (parser, c3);
chars_delta += 3;
}
else
json_signal_error (parser, Qjson_utf8_decode_error);
utf8_error (parser);
}
else if (c == '\\')
{
@ -1249,6 +1219,7 @@ json_parse_string (struct json_parser *parser)
json_byte_workspace_put (parser, 0xc0 | num >> 6);
json_byte_workspace_put (parser,
0x80 | (num & 0x3f));
chars_delta += 1;
}
else if (num < 0x10000)
{
@ -1258,6 +1229,7 @@ json_parse_string (struct json_parser *parser)
| ((num >> 6) & 0x3f)));
json_byte_workspace_put (parser,
0x80 | (num & 0x3f));
chars_delta += 2;
}
else
{
@ -1270,6 +1242,7 @@ json_parse_string (struct json_parser *parser)
| ((num >> 6) & 0x3f)));
json_byte_workspace_put (parser,
0x80 | (num & 0x3f));
chars_delta += 3;
}
}
else
@ -1566,16 +1539,11 @@ json_parse_object (struct json_parser *parser)
if (c != '"')
json_signal_error (parser, Qjson_parse_error);
json_byte_workspace_reset (parser);
switch (parser->conf.object_type)
{
case json_object_hashtable:
{
json_parse_string (parser);
Lisp_Object key
= make_string_from_utf8 ((char *) parser->byte_workspace,
(parser->byte_workspace_current
- parser->byte_workspace));
Lisp_Object key = json_parse_string (parser, false, false);
Lisp_Object value = json_parse_object_member_value (parser);
json_make_object_workspace_for (parser, 2);
parser->object_workspace[parser->object_workspace_current] = key;
@ -1586,13 +1554,7 @@ json_parse_object (struct json_parser *parser)
}
case json_object_alist:
{
json_parse_string (parser);
char *workspace = (char *) parser->byte_workspace;
ptrdiff_t nbytes
= parser->byte_workspace_current - parser->byte_workspace;
Lisp_Object key = Fintern (make_string_from_utf8 (workspace,
nbytes),
Qnil);
Lisp_Object key = json_parse_string (parser, true, false);
Lisp_Object value = json_parse_object_member_value (parser);
Lisp_Object nc = Fcons (Fcons (key, value), Qnil);
*cdr = nc;
@ -1601,11 +1563,7 @@ json_parse_object (struct json_parser *parser)
}
case json_object_plist:
{
json_byte_workspace_put (parser, ':');
json_parse_string (parser);
Lisp_Object key = intern_1 ((char *) parser->byte_workspace,
(parser->byte_workspace_current
- parser->byte_workspace));
Lisp_Object key = json_parse_string (parser, true, true);
Lisp_Object value = json_parse_object_member_value (parser);
Lisp_Object nc = Fcons (key, Qnil);
*cdr = nc;
@ -1692,22 +1650,14 @@ json_parse_value (struct json_parser *parser, int c)
else if (c == '[')
return json_parse_array (parser);
else if (c == '"')
{
json_byte_workspace_reset (parser);
json_parse_string (parser);
Lisp_Object result
= make_string_from_utf8 ((const char *) parser->byte_workspace,
(parser->byte_workspace_current
- parser->byte_workspace));
return result;
}
return json_parse_string (parser, false, false);
else if ((c >= '0' && c <= '9') || (c == '-'))
return json_parse_number (parser, c);
else
{
int c2 = json_input_get (parser);
int c3 = json_input_get (parser);
int c4 = json_input_get (parser);
int c2 = json_input_get_if_possible (parser);
int c3 = json_input_get_if_possible (parser);
int c4 = json_input_get_if_possible (parser);
int c5 = json_input_get_if_possible (parser);
if (c == 't' && c2 == 'r' && c3 == 'u' && c4 == 'e'
@ -1816,14 +1766,13 @@ usage: (json-parse-string STRING &rest ARGS) */)
Lisp_Object string = args[0];
CHECK_STRING (string);
Lisp_Object encoded = json_encode (string);
struct json_configuration conf
= { json_object_hashtable, json_array_array, QCnull, QCfalse };
json_parse_args (nargs - 1, args + 1, &conf, true);
struct json_parser p;
const unsigned char *begin = (const unsigned char *) SSDATA (encoded);
json_parser_init (&p, conf, begin, begin + SBYTES (encoded), NULL, NULL);
const unsigned char *begin = SDATA (string);
json_parser_init (&p, conf, begin, begin + SBYTES (string), NULL, NULL);
record_unwind_protect_ptr (json_parser_done, &p);
return unbind_to (count,

View file

@ -4744,6 +4744,8 @@ extern ptrdiff_t evxprintf (char **, ptrdiff_t *, char *, ptrdiff_t,
extern Lisp_Object intern_1 (const char *, ptrdiff_t);
extern Lisp_Object intern_c_string_1 (const char *, ptrdiff_t);
extern Lisp_Object intern_driver (Lisp_Object, Lisp_Object, Lisp_Object);
extern Lisp_Object intern_c_multibyte (const char *str,
ptrdiff_t nchars, ptrdiff_t nbytes);
extern void init_symbol (Lisp_Object, Lisp_Object);
extern Lisp_Object oblookup (Lisp_Object, const char *, ptrdiff_t, ptrdiff_t);
INLINE void

View file

@ -4993,6 +4993,18 @@ intern_c_string_1 (const char *str, ptrdiff_t len)
return tem;
}
/* Intern STR of NBYTES bytes and NCHARS characters in the default obarray. */
Lisp_Object
intern_c_multibyte (const char *str, ptrdiff_t nchars, ptrdiff_t nbytes)
{
Lisp_Object obarray = check_obarray (Vobarray);
Lisp_Object sym = oblookup (obarray, str, nchars, nbytes);
if (BARE_SYMBOL_P (sym))
return sym;
return intern_driver (make_multibyte_string (str, nchars, nbytes),
obarray, sym);
}
static void
define_symbol (Lisp_Object sym, char const *str)
{

View file

@ -44,13 +44,56 @@
(goto-char (point-min))
,@body))
(defun ert-font-lock--wrap-begin-end (re)
(concat "^" re "$"))
;;; Regexp tests
;;;
(ert-deftest test-regexp--face-symbol-re ()
(let ((re (ert-font-lock--wrap-begin-end
ert-font-lock--face-symbol-re)))
(should (string-match-p re "font-lock-keyword-face"))
(should (string-match-p re "-face"))
(should (string-match-p re "weird-package/-face"))
(should (string-match-p re "-"))
(should (string-match-p re "font-lock.face"))
(should-not (string-match-p re "face suffix-with"))
(should-not (string-match-p re "("))))
(ert-deftest test-regexp--face-symbol-list-re ()
(let ((re (ert-font-lock--wrap-begin-end
ert-font-lock--face-symbol-list-re)))
(should (string-match-p re "(face1 face2)"))
(should (string-match-p re "(face1)"))
(should (string-match-p re "()"))
(should-not (string-match-p re ")"))
(should-not (string-match-p re "("))))
(ert-deftest test-regexp--assertion-line-re ()
(let ((re (ert-font-lock--wrap-begin-end
ert-font-lock--assertion-line-re)))
(should (string-match-p re "^ something-face"))
(should (string-match-p re "^ !something-face"))
(should (string-match-p re "^ (face1 face2)"))
(should (string-match-p re "^ !(face1 face2)"))
(should (string-match-p re "^ ()"))
(should (string-match-p re "^ !()"))
(should (string-match-p re "^ nil"))
(should (string-match-p re "^ !nil"))
(should (string-match-p re "<- something-face"))
(should (string-match-p re "<- ^ something-face"))
(should (string-match-p re "^^ ^ something-face"))
(should (string-match-p re "^ ^something-face"))
(should-not (string-match-p re "^ <- ^something-face"))))
;;; Comment parsing tests
;;
(ert-deftest test-line-comment-p--fundamental ()
(with-temp-buffer-str-mode fundamental-mode
"// comment\n"
(should-not (ert-font-lock--line-comment-p))))
"// comment\n"
(should-not (ert-font-lock--line-comment-p))))
(ert-deftest test-line-comment-p--emacs-lisp ()
(with-temp-buffer-str-mode emacs-lisp-mode

View file

@ -25,6 +25,7 @@
(require 'cl-lib)
(require 'map)
(require 'subr-x)
(declare-function json-serialize "json.c" (object &rest args))
(declare-function json-insert "json.c" (object &rest args))
@ -34,7 +35,6 @@
(define-error 'json-tests--error "JSON test error")
(ert-deftest json-serialize/roundtrip ()
(skip-unless (fboundp 'json-serialize))
;; The noncharacter U+FFFF should be passed through,
;; cf. https://www.unicode.org/faq/private_use.html#noncharacters.
(let ((lisp [:null :false t 0 123 -456 3.75 "abc\uFFFFαβγ𝔸𝐁𝖢\"\\"])
@ -53,7 +53,6 @@
(ert-deftest json-serialize/roundtrip-scalars ()
"Check that Bug#42994 is fixed."
(skip-unless (fboundp 'json-serialize))
(dolist (case '((:null "null")
(:false "false")
(t "true")
@ -80,7 +79,6 @@
(should (eobp)))))))
(ert-deftest json-serialize/object ()
(skip-unless (fboundp 'json-serialize))
(let ((table (make-hash-table :test #'equal)))
(puthash "abc" [1 2 t] table)
(puthash "def" :null table)
@ -125,8 +123,6 @@
}")))
(ert-deftest json-serialize/object-with-duplicate-keys ()
(skip-unless (fboundp 'json-serialize))
(dolist (n '(1 5 20 100))
(let ((symbols (mapcar (lambda (i) (make-symbol (format "s%d" i)))
(number-sequence 1 n)))
@ -160,7 +156,9 @@
)
(ert-deftest json-parse-string/object ()
(skip-unless (fboundp 'json-parse-string))
:expected-result :failed
;; FIXME: This currently fails. Should the parser deduplicate keys?
;; Never, always, or for alist and plist only?
(let ((input
"{ \"abc\" : [1, 2, true], \"def\" : null, \"abc\" : [9, false] }\n"))
(let ((actual (json-parse-string input)))
@ -173,8 +171,16 @@
(should (equal (json-parse-string input :object-type 'plist)
'(:abc [9 :false] :def :null)))))
(ert-deftest json-parse-string/object-unicode-keys ()
(let ((input "{\"é\":1,\"\":2,\"𐌐\":3}"))
(let ((actual (json-parse-string input)))
(should (equal (sort (hash-table-keys actual)) '("é" "" "𐌐"))))
(should (equal (json-parse-string input :object-type 'alist)
'((é . 1) ( . 2) (𐌐 . 3))))
(should (equal (json-parse-string input :object-type 'plist)
'( 1 : 2 :𐌐 3)))))
(ert-deftest json-parse-string/array ()
(skip-unless (fboundp 'json-parse-string))
(let ((input "[\"a\", 1, [\"b\", 2]]"))
(should (equal (json-parse-string input)
["a" 1 ["b" 2]]))
@ -182,7 +188,6 @@
'("a" 1 ("b" 2))))))
(ert-deftest json-parse-string/string ()
(skip-unless (fboundp 'json-parse-string))
(should-error (json-parse-string "[\"formfeed\f\"]") :type 'json-parse-error)
(should (equal (json-parse-string "[\"foo \\\"bar\\\"\"]") ["foo \"bar\""]))
(should (equal (json-parse-string "[\"abcαβγ\"]") ["abcαβγ"]))
@ -190,11 +195,10 @@
["\nasdфывfgh\t"]))
(should (equal (json-parse-string "[\"\\uD834\\uDD1E\"]") ["\U0001D11E"]))
(should-error (json-parse-string "foo") :type 'json-parse-error)
;; FIXME: Is this the right behavior?
(should (equal (json-parse-string "[\"\u00C4\xC3\x84\"]") ["\u00C4\u00C4"])))
(should-error (json-parse-string "[\"\u00C4\xC3\x84\"]")
:type 'json-utf8-decode-error))
(ert-deftest json-serialize/string ()
(skip-unless (fboundp 'json-serialize))
(should (equal (json-serialize ["foo"]) "[\"foo\"]"))
(should (equal (json-serialize ["a\n\fb"]) "[\"a\\n\\fb\"]"))
(should (equal (json-serialize ["\nasdфыв\u001f\u007ffgh\t"])
@ -204,17 +208,27 @@
(should-error (json-serialize ["\u00C4\xC3\x84"])))
(ert-deftest json-serialize/invalid-unicode ()
(skip-unless (fboundp 'json-serialize))
(should-error (json-serialize ["a\uDBBBb"]) :type 'wrong-type-argument)
(should-error (json-serialize ["u\x110000v"]) :type 'wrong-type-argument)
(should-error (json-serialize ["u\x3FFFFFv"]) :type 'wrong-type-argument)
(should-error (json-serialize ["u\xCCv"]) :type 'wrong-type-argument)
(should-error (json-serialize ["u\u00C4\xCCv"]) :type 'wrong-type-argument))
(ert-deftest json-parse-string/short ()
(should-error (json-parse-string "") :type 'json-end-of-file)
(should-error (json-parse-string " ") :type 'json-end-of-file)
(dolist (s '("a" "ab" "abc" "abcd" "\0" "\1"
"t" "tr" "tru" "truE" "truee"
"n" "nu" "nul" "nulL" "nulll"
"f" "fa" "fal" "fals" "falsE" "falsee"))
(condition-case err
(json-parse-string s)
(error
(should (eq (car err) 'json-parse-error)))
(:success (error "parsing %S should fail" s)))))
(ert-deftest json-parse-string/null ()
(skip-unless (fboundp 'json-parse-string))
(should-error (json-parse-string "\x00") :type 'wrong-type-argument)
(should (json-parse-string "[\"a\\u0000b\"]"))
(should (equal (json-parse-string "[\"a\\u0000b\"]") ["a\0b"]))
(let* ((string "{\"foo\":\"this is a string including a literal \\u0000\"}")
(data (json-parse-string string)))
(should (hash-table-p data))
@ -224,43 +238,43 @@
"Some examples from
https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt.
Test with both unibyte and multibyte strings."
(skip-unless (fboundp 'json-parse-string))
;; Invalid UTF-8 code unit sequences.
(should-error (json-parse-string "[\"\x80\"]") :type 'json-parse-error)
(should-error (json-parse-string "[\"\u00C4\x80\"]") :type 'json-parse-error)
(should-error (json-parse-string "[\"\xBF\"]") :type 'json-parse-error)
(should-error (json-parse-string "[\"\u00C4\xBF\"]") :type 'json-parse-error)
(should-error (json-parse-string "[\"\xFE\"]") :type 'json-parse-error)
(should-error (json-parse-string "[\"\u00C4\xFE\"]") :type 'json-parse-error)
(should-error (json-parse-string "[\"\xC0\xAF\"]") :type 'json-parse-error)
(should-error (json-parse-string "[\"\x80\"]") :type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\u00C4\x80\"]")
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\xBF\"]") :type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\u00C4\xBF\"]")
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\xFE\"]") :type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\u00C4\xFE\"]")
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\xC0\xAF\"]")
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\u00C4\xC0\xAF\"]")
:type 'json-parse-error)
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\u00C4\xC0\x80\"]")
:type 'json-parse-error)
:type 'json-utf8-decode-error)
;; Surrogates.
(should-error (json-parse-string "[\"\uDB7F\"]")
:type 'json-parse-error)
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\xED\xAD\xBF\"]")
:type 'json-parse-error)
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\u00C4\xED\xAD\xBF\"]")
:type 'json-parse-error)
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\uDB7F\uDFFF\"]")
:type 'json-parse-error)
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\xED\xAD\xBF\xED\xBF\xBF\"]")
:type 'json-parse-error)
:type 'json-utf8-decode-error)
(should-error (json-parse-string "[\"\u00C4\xED\xAD\xBF\xED\xBF\xBF\"]")
:type 'json-parse-error))
:type 'json-utf8-decode-error))
(ert-deftest json-parse-string/incomplete ()
(skip-unless (fboundp 'json-parse-string))
(should-error (json-parse-string "[123") :type 'json-end-of-file))
(ert-deftest json-parse-string/trailing ()
(skip-unless (fboundp 'json-parse-string))
(should-error (json-parse-string "[123] [456]") :type 'json-trailing-content))
(ert-deftest json-parse-buffer/incomplete ()
(skip-unless (fboundp 'json-parse-buffer))
(with-temp-buffer
(insert "[123")
(goto-char 1)
@ -268,7 +282,6 @@ Test with both unibyte and multibyte strings."
(should (bobp))))
(ert-deftest json-parse-buffer/trailing ()
(skip-unless (fboundp 'json-parse-buffer))
(with-temp-buffer
(insert "[123] [456]")
(goto-char 1)
@ -277,8 +290,6 @@ Test with both unibyte and multibyte strings."
(should (looking-at-p (rx " [456]" eos)))))
(ert-deftest json-parse-with-custom-null-and-false-objects ()
(skip-unless (and (fboundp 'json-serialize)
(fboundp 'json-parse-string)))
(let* ((input
"{ \"abc\" : [9, false] , \"def\" : null }")
(output
@ -316,7 +327,6 @@ Test with both unibyte and multibyte strings."
(should-error (json-serialize '() :object-type 'alist))))
(ert-deftest json-insert/signal ()
(skip-unless (fboundp 'json-insert))
(with-temp-buffer
(let ((calls 0))
(add-hook 'after-change-functions
@ -331,7 +341,6 @@ Test with both unibyte and multibyte strings."
(should (equal calls 1)))))
(ert-deftest json-insert/throw ()
(skip-unless (fboundp 'json-insert))
(with-temp-buffer
(let ((calls 0))
(add-hook 'after-change-functions
@ -347,7 +356,6 @@ Test with both unibyte and multibyte strings."
(should (equal calls 1)))))
(ert-deftest json-serialize/bignum ()
(skip-unless (fboundp 'json-serialize))
(should (equal (json-serialize (vector (1+ most-positive-fixnum)
(1- most-negative-fixnum)))
(format "[%d,%d]"
@ -356,12 +364,10 @@ Test with both unibyte and multibyte strings."
(ert-deftest json-parse-string/wrong-type ()
"Check that Bug#42113 is fixed."
(skip-unless (fboundp 'json-parse-string))
(should-error (json-parse-string 1) :type 'wrong-type-argument))
(ert-deftest json-serialize/wrong-hash-key-type ()
"Check that Bug#42113 is fixed."
(skip-unless (fboundp 'json-serialize))
(let ((table (make-hash-table :test #'eq)))
(puthash 1 2 table)
(should-error (json-serialize table) :type 'wrong-type-argument)))