JIT compiler

* lisp/emacs-lisp/jit-support.el: New file.
* src/alloc.c (make_byte_code): Remove.
(Fmake_byte_code): Rewrite.
* src/data.c (Fsubr_arity, notify_variable_watchers): Update.
* src/emacs.c (main): Call syms_of_jit, init_jit.
* src/eval.c (eval_sub, Fapply, FUNCTIONP, Ffuncall, funcall_subr)
(funcall_lambda): Update.
* src/jit.c: New file.
* src/lisp.h (struct subr_function): New struct, extracted from
Lisp_Subr.
(SUBR_MAX_ARGS): New define.
(struct Lisp_Subr): Use struct subr_function.
(COMPILED_JIT_CODE): New constant.
(DEFUN): Update.
(make_byte_code): Don't declare.
(funcall_subr): Add error_obj argument.
(syms_of_jit, init_jit, emacs_jit_compile): Declare.
* src/lread.c (read1): Use Fmake_byte_code.
* test/src/jit-tests.el: New file.
This commit is contained in:
Tom Tromey 2018-08-12 15:29:43 -06:00
parent 62d9c0cf9c
commit a166e8fabd
9 changed files with 2825 additions and 73 deletions

View file

@ -0,0 +1,37 @@
;;; jit-support.el --- helper functions for JIT compilation -*- lexical-binding: t -*-
;; Copyright (C) 2018 Free Software Foundation, Inc.
;; Keywords: lisp
;; Package: emacs
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;;###autoload
(defun jit-disassemble (func)
(interactive "aDisassemble function: ")
(when (symbolp func)
(setf func (symbol-function func)))
(let ((str (jit-disassemble-to-string func)))
(with-current-buffer (get-buffer-create "*JIT*")
(erase-buffer)
(save-excursion
(insert str))
(pop-to-buffer (current-buffer)))))
(provide 'jit-support)
;;; jit-support.el ends here

View file

@ -3515,23 +3515,6 @@ usage: (vector &rest OBJECTS) */)
return val;
}
void
make_byte_code (struct Lisp_Vector *v)
{
/* Don't allow the global zero_vector to become a byte code object. */
eassert (0 < v->header.size);
if (v->header.size > 1 && STRINGP (v->contents[1])
&& STRING_MULTIBYTE (v->contents[1]))
/* BYTECODE-STRING must have been produced by Emacs 20.2 or the
earlier because they produced a raw 8-bit string for byte-code
and now such a byte-code string is loaded as multibyte while
raw 8-bit characters converted to multibyte form. Thus, now we
must convert them back to the original unibyte form. */
v->contents[1] = Fstring_as_unibyte (v->contents[1]);
XSETPVECTYPE (v, PVEC_COMPILED);
}
DEFUN ("make-byte-code", Fmake_byte_code, Smake_byte_code, 4, MANY, 0,
doc: /* Create a byte-code object with specified arguments as elements.
The arguments should be the ARGLIST, bytecode-string BYTE-CODE, constant
@ -3550,8 +3533,14 @@ stack before executing the byte-code.
usage: (make-byte-code ARGLIST BYTE-CODE CONSTANTS DEPTH &optional DOCSTRING INTERACTIVE-SPEC &rest ELEMENTS) */)
(ptrdiff_t nargs, Lisp_Object *args)
{
Lisp_Object val = make_uninit_vector (nargs);
struct Lisp_Vector *p = XVECTOR (val);
Lisp_Object val;
struct Lisp_Vector *p = allocate_pseudovector (COMPILED_JIT_CODE + 1,
COMPILED_JIT_CODE,
COMPILED_JIT_CODE + 1,
PVEC_COMPILED);
/* Don't allow the global zero_vector to become a byte code object. */
eassert (0 < nargs);
/* We used to purecopy everything here, if purify-flag was set. This worked
OK for Emacs-23, but with Emacs-24's lexical binding code, it can be
@ -3562,7 +3551,21 @@ usage: (make-byte-code ARGLIST BYTE-CODE CONSTANTS DEPTH &optional DOCSTRING INT
to be setcar'd). */
memcpy (p->contents, args, nargs * sizeof *args);
make_byte_code (p);
for (int i = nargs; i < COMPILED_JIT_CODE; ++i)
p->contents[i] = Qnil;
/* Not really a Lisp_Object. */
p->contents[COMPILED_JIT_CODE] = (Lisp_Object) NULL;
if (STRINGP (p->contents[COMPILED_BYTECODE])
&& STRING_MULTIBYTE (p->contents[COMPILED_BYTECODE]))
/* BYTECODE-STRING must have been produced by Emacs 20.2 or the
earlier because they produced a raw 8-bit string for byte-code
and now such a byte-code string is loaded as multibyte while
raw 8-bit characters converted to multibyte form. Thus, now we
must convert them back to the original unibyte form. */
p->contents[COMPILED_BYTECODE] = Fstring_as_unibyte (p->contents[COMPILED_BYTECODE]);
XSETCOMPILED (val, p);
return val;
}

View file

@ -866,8 +866,8 @@ function with `&rest' args, or `unevalled' for a special form. */)
{
short minargs, maxargs;
CHECK_SUBR (subr);
minargs = XSUBR (subr)->min_args;
maxargs = XSUBR (subr)->max_args;
minargs = XSUBR (subr)->function.min_args;
maxargs = XSUBR (subr)->function.max_args;
return Fcons (make_fixnum (minargs),
maxargs == MANY ? Qmany
: maxargs == UNEVALLED ? Qunevalled
@ -1571,7 +1571,8 @@ notify_variable_watchers (Lisp_Object symbol,
if (SUBRP (watcher))
{
Lisp_Object args[] = { symbol, newval, operation, where };
funcall_subr (XSUBR (watcher), ARRAYELTS (args), args);
funcall_subr (watcher, &XSUBR (watcher)->function,
ARRAYELTS (args), args);
}
else
CALLN (Ffuncall, watcher, symbol, newval, operation, where);

View file

@ -1643,6 +1643,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_json ();
#endif
#ifdef HAVE_LIBJIT
syms_of_jit ();
#endif
keys_of_casefiddle ();
keys_of_cmds ();
keys_of_buffer ();
@ -1663,6 +1667,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#if defined WINDOWSNT || defined HAVE_NTGUI
globals_of_w32select ();
#endif
#ifdef HAVE_LIBJIT
init_jit ();
#endif
}
init_charset ();

View file

@ -30,6 +30,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "dispextern.h"
#include "buffer.h"
#ifdef HAVE_LIBJIT
#include <jit/jit.h>
#endif
/* CACHEABLE is ordinarily nothing, except it is 'volatile' if
necessary to cajole GCC into not warning incorrectly that a
variable should be volatile. */
@ -2230,14 +2234,14 @@ eval_sub (Lisp_Object form)
check_cons_list ();
if (XFIXNUM (numargs) < XSUBR (fun)->min_args
|| (XSUBR (fun)->max_args >= 0
&& XSUBR (fun)->max_args < XFIXNUM (numargs)))
if (XFIXNUM (numargs) < XSUBR (fun)->function.min_args
|| (XSUBR (fun)->function.max_args >= 0
&& XSUBR (fun)->function.max_args < XFIXNUM (numargs)))
xsignal2 (Qwrong_number_of_arguments, original_fun, numargs);
else if (XSUBR (fun)->max_args == UNEVALLED)
val = (XSUBR (fun)->function.aUNEVALLED) (args_left);
else if (XSUBR (fun)->max_args == MANY)
else if (XSUBR (fun)->function.max_args == UNEVALLED)
val = (XSUBR (fun)->function.function.aUNEVALLED) (args_left);
else if (XSUBR (fun)->function.max_args == MANY)
{
/* Pass a vector of evaluated arguments. */
Lisp_Object *vals;
@ -2255,7 +2259,7 @@ eval_sub (Lisp_Object form)
set_backtrace_args (specpdl + count, vals, argnum);
val = XSUBR (fun)->function.aMANY (argnum, vals);
val = XSUBR (fun)->function.function.aMANY (argnum, vals);
check_cons_list ();
lisp_eval_depth--;
@ -2268,7 +2272,7 @@ eval_sub (Lisp_Object form)
}
else
{
int i, maxargs = XSUBR (fun)->max_args;
int i, maxargs = XSUBR (fun)->function.max_args;
for (i = 0; i < maxargs; i++)
{
@ -2281,40 +2285,40 @@ eval_sub (Lisp_Object form)
switch (i)
{
case 0:
val = (XSUBR (fun)->function.a0 ());
val = (XSUBR (fun)->function.function.a0 ());
break;
case 1:
val = (XSUBR (fun)->function.a1 (argvals[0]));
val = (XSUBR (fun)->function.function.a1 (argvals[0]));
break;
case 2:
val = (XSUBR (fun)->function.a2 (argvals[0], argvals[1]));
val = (XSUBR (fun)->function.function.a2 (argvals[0], argvals[1]));
break;
case 3:
val = (XSUBR (fun)->function.a3
val = (XSUBR (fun)->function.function.a3
(argvals[0], argvals[1], argvals[2]));
break;
case 4:
val = (XSUBR (fun)->function.a4
val = (XSUBR (fun)->function.function.a4
(argvals[0], argvals[1], argvals[2], argvals[3]));
break;
case 5:
val = (XSUBR (fun)->function.a5
val = (XSUBR (fun)->function.function.a5
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4]));
break;
case 6:
val = (XSUBR (fun)->function.a6
val = (XSUBR (fun)->function.function.a6
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4], argvals[5]));
break;
case 7:
val = (XSUBR (fun)->function.a7
val = (XSUBR (fun)->function.function.a7
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4], argvals[5], argvals[6]));
break;
case 8:
val = (XSUBR (fun)->function.a8
val = (XSUBR (fun)->function.function.a8
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4], argvals[5], argvals[6], argvals[7]));
break;
@ -2411,16 +2415,16 @@ usage: (apply FUNCTION &rest ARGUMENTS) */)
fun = args[0];
}
if (SUBRP (fun) && XSUBR (fun)->max_args > numargs
if (SUBRP (fun) && XSUBR (fun)->function.max_args > numargs
/* Don't hide an error by adding missing arguments. */
&& numargs >= XSUBR (fun)->min_args)
&& numargs >= XSUBR (fun)->function.min_args)
{
/* Avoid making funcall cons up a yet another new vector of arguments
by explicitly supplying nil's for optional values. */
SAFE_ALLOCA_LISP (funcall_args, 1 + XSUBR (fun)->max_args);
SAFE_ALLOCA_LISP (funcall_args, 1 + XSUBR (fun)->function.max_args);
memclear (funcall_args + numargs + 1,
(XSUBR (fun)->max_args - numargs) * word_size);
funcall_nargs = 1 + XSUBR (fun)->max_args;
(XSUBR (fun)->function.max_args - numargs) * word_size);
funcall_nargs = 1 + XSUBR (fun)->function.max_args;
}
else
{ /* We add 1 to numargs because funcall_args includes the
@ -2764,7 +2768,7 @@ FUNCTIONP (Lisp_Object object)
}
if (SUBRP (object))
return XSUBR (object)->max_args != UNEVALLED;
return XSUBR (object)->function.max_args != UNEVALLED;
else if (COMPILEDP (object) || MODULE_FUNCTIONP (object))
return true;
else if (CONSP (object))
@ -2819,7 +2823,12 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */)
fun = indirect_function (fun);
if (SUBRP (fun))
val = funcall_subr (XSUBR (fun), numargs, args + 1);
val = funcall_subr (fun, &XSUBR (fun)->function, numargs, args + 1);
else if (COMPILEDP (fun)
&& XVECTOR (fun)->contents[COMPILED_JIT_CODE] != NULL)
val = funcall_subr (fun,
(struct subr_function *) XVECTOR (fun)->contents[COMPILED_JIT_CODE],
numargs, args + 1);
else if (COMPILEDP (fun) || MODULE_FUNCTIONP (fun))
val = funcall_lambda (fun, numargs, args + 1);
else
@ -2856,28 +2865,19 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */)
and return the result of evaluation. */
Lisp_Object
funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, Lisp_Object *args)
funcall_subr (Lisp_Object error_obj, struct subr_function *subr,
ptrdiff_t numargs, Lisp_Object *args)
{
if (numargs < subr->min_args
|| (subr->max_args >= 0 && subr->max_args < numargs))
{
Lisp_Object fun;
XSETSUBR (fun, subr);
xsignal2 (Qwrong_number_of_arguments, fun, make_fixnum (numargs));
}
xsignal2 (Qwrong_number_of_arguments, error_obj, make_fixnum (numargs));
else if (subr->max_args == UNEVALLED)
{
Lisp_Object fun;
XSETSUBR (fun, subr);
xsignal1 (Qinvalid_function, fun);
}
xsignal1 (Qinvalid_function, error_obj);
else if (subr->max_args == MANY)
return (subr->function.aMANY) (numargs, args);
else
{
Lisp_Object internal_argbuf[8];
Lisp_Object internal_argbuf[SUBR_MAX_ARGS];
Lisp_Object *internal_args;
if (subr->max_args > numargs)
{
@ -3020,6 +3020,22 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
and constants vector yet, fetch them from the file. */
if (CONSP (AREF (fun, COMPILED_BYTECODE)))
Ffetch_bytecode (fun);
#ifdef HAVE_LIBJIT
if (initialized)
{
struct Lisp_Vector *vec = XVECTOR (fun);
if (vec->contents[COMPILED_JIT_CODE] == NULL)
emacs_jit_compile (fun);
if (vec->contents[COMPILED_JIT_CODE] != NULL)
return funcall_subr (fun,
(struct subr_function *) vec->contents[COMPILED_JIT_CODE],
nargs, arg_vector);
}
#endif /* HAVE_LIBJIT */
return exec_byte_code (AREF (fun, COMPILED_BYTECODE),
AREF (fun, COMPILED_CONSTANTS),
AREF (fun, COMPILED_STACK_DEPTH),

2367
src/jit.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1891,13 +1891,12 @@ CHAR_TABLE_SET (Lisp_Object ct, int idx, Lisp_Object val)
char_table_set (ct, idx, val);
}
/* This structure describes a built-in function.
It is generated by the DEFUN macro only.
defsubr makes it into a Lisp object. */
/* The inner part of a Lisp_Subr, used when calling the function.
This is separate so it can be reused by the JIT compiler without
requiring an entire Lisp_Subr to be created there. */
struct Lisp_Subr
struct subr_function
{
union vectorlike_header header;
union {
Lisp_Object (*a0) (void);
Lisp_Object (*a1) (Lisp_Object);
@ -1912,6 +1911,18 @@ struct Lisp_Subr
Lisp_Object (*aMANY) (ptrdiff_t, Lisp_Object *);
} function;
short min_args, max_args;
};
#define SUBR_MAX_ARGS 9
/* This structure describes a built-in function.
It is generated by the DEFUN macro only.
defsubr makes it into a Lisp object. */
struct Lisp_Subr
{
union vectorlike_header header;
struct subr_function function;
const char *symbol_name;
const char *intspec;
EMACS_INT doc;
@ -2634,7 +2645,8 @@ enum Lisp_Compiled
COMPILED_CONSTANTS = 2,
COMPILED_STACK_DEPTH = 3,
COMPILED_DOC_STRING = 4,
COMPILED_INTERACTIVE = 5
COMPILED_INTERACTIVE = 5,
COMPILED_JIT_CODE = 6
};
/* Flag bits in a character. These also get used in termhooks.h.
@ -2916,8 +2928,8 @@ CHECK_FIXNUM_CDR (Lisp_Object x)
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc) \
static struct Lisp_Subr sname = \
{ { PVEC_SUBR << PSEUDOVECTOR_AREA_BITS }, \
{ .a ## maxargs = fnname }, \
minargs, maxargs, lname, intspec, 0}; \
{ { .a ## maxargs = fnname }, \
minargs, maxargs }, lname, intspec, 0}; \
Lisp_Object fnname
/* defsubr (Sname);
@ -3649,7 +3661,6 @@ build_string (const char *str)
}
extern Lisp_Object pure_cons (Lisp_Object, Lisp_Object);
extern void make_byte_code (struct Lisp_Vector *);
extern struct Lisp_Vector *allocate_vector (EMACS_INT);
/* Make an uninitialized vector for SIZE objects. NOTE: you must
@ -3848,7 +3859,9 @@ extern _Noreturn void xsignal3 (Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object);
extern _Noreturn void signal_error (const char *, Lisp_Object);
extern bool FUNCTIONP (Lisp_Object);
extern Lisp_Object funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, Lisp_Object *arg_vector);
extern Lisp_Object funcall_subr (Lisp_Object error_obj,
struct subr_function *subr,
ptrdiff_t numargs, Lisp_Object *arg_vector);
extern Lisp_Object eval_sub (Lisp_Object form);
extern Lisp_Object apply1 (Lisp_Object, Lisp_Object);
extern Lisp_Object call0 (Lisp_Object);
@ -4437,6 +4450,10 @@ extern bool profiler_memory_running;
extern void malloc_probe (size_t);
extern void syms_of_profiler (void);
/* Defined in jit.c. */
extern void syms_of_jit (void);
extern void init_jit (void);
extern void emacs_jit_compile (Lisp_Object);
#ifdef DOS_NT
/* Defined in msdos.c, w32.c. */

View file

@ -2948,8 +2948,7 @@ read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)
vec = XVECTOR (tmp);
if (vec->header.size == 0)
invalid_syntax ("Empty byte-code object");
make_byte_code (vec);
return tmp;
return Fmake_byte_code (vec->header.size, vec->contents);
}
if (c == '(')
{

304
test/src/jit-tests.el Normal file
View file

@ -0,0 +1,304 @@
;;; jit-tests.el --- unit tests for src/jijt.c -*- lexical-binding: t; -*-
;; Copyright (C) 2018 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Unit tests for src/jit.c.
;;; Code:
(require 'ert)
(defun jit-test-apply (func &rest args)
(unless (byte-code-function-p (symbol-function func))
(byte-compile func))
(apply func args))
;; Test Bconsp.
(defun jit-test-consp (x) (consp x))
(ert-deftest jit-consp ()
(should-not (jit-test-apply 'jit-test-consp 23))
(should-not (jit-test-apply 'jit-test-consp nil))
(should (jit-test-apply 'jit-test-consp '(1 . 2))))
;; Test Blistp.
(defun jit-test-listp (x) (listp x))
(ert-deftest jit-listp ()
(should-not (jit-test-apply 'jit-test-listp 23))
(should (jit-test-apply 'jit-test-listp nil))
(should (jit-test-apply 'jit-test-listp '(1 . 2))))
;; Test Bstringp.
(defun jit-test-stringp (x) (stringp x))
(ert-deftest jit-stringp ()
(should-not (jit-test-apply 'jit-test-stringp 23))
(should-not (jit-test-apply 'jit-test-stringp nil))
(should (jit-test-apply 'jit-test-stringp "hi")))
;; Test Bsymbolp.
(defun jit-test-symbolp (x) (symbolp x))
(ert-deftest jit-symbolp ()
(should-not (jit-test-apply 'jit-test-symbolp 23))
(should-not (jit-test-apply 'jit-test-symbolp "hi"))
(should (jit-test-apply 'jit-test-symbolp 'whatever)))
;; Test Bintegerp.
(defun jit-test-integerp (x) (integerp x))
(ert-deftest jit-integerp ()
(should (jit-test-apply 'jit-test-integerp 23))
(should-not (jit-test-apply 'jit-test-integerp 57.5))
(should-not (jit-test-apply 'jit-test-integerp "hi"))
(should-not (jit-test-apply 'jit-test-integerp 'whatever)))
;; Test Bnumberp.
(defun jit-test-numberp (x) (numberp x))
(ert-deftest jit-numberp ()
(should (jit-test-apply 'jit-test-numberp 23))
(should (jit-test-apply 'jit-test-numberp 57.5))
(should-not (jit-test-apply 'jit-test-numberp "hi"))
(should-not (jit-test-apply 'jit-test-numberp 'whatever)))
;; Test Badd1.
(defun jit-test-add1 (x) (1+ x))
(ert-deftest jit-add1 ()
(should (eq (jit-test-apply 'jit-test-add1 23) 24))
(should (eq (jit-test-apply 'jit-test-add1 -17) -16))
(should (eql (jit-test-apply 'jit-test-add1 1.0) 2.0))
(should-error (jit-test-apply 'jit-test-add1 nil)
:type 'wrong-type-argument))
;; Test Bsub1.
(defun jit-test-sub1 (x) (1- x))
(ert-deftest jit-sub1 ()
(should (eq (jit-test-apply 'jit-test-sub1 23) 22))
(should (eq (jit-test-apply 'jit-test-sub1 -17) -18))
(should (eql (jit-test-apply 'jit-test-sub1 1.0) 0.0))
(should-error (jit-test-apply 'jit-test-sub1 nil)
:type 'wrong-type-argument))
;; Test Bneg.
(defun jit-test-negate (x) (- x))
(ert-deftest jit-negate ()
(should (eq (jit-test-apply 'jit-test-negate 23) -23))
(should (eq (jit-test-apply 'jit-test-negate -17) 17))
(should (eql (jit-test-apply 'jit-test-negate 1.0) -1.0))
(should-error (jit-test-apply 'jit-test-negate nil)
:type 'wrong-type-argument))
;; Test Bnot.
(defun jit-test-not (x) (not x))
(ert-deftest jit-not ()
(should (eq (jit-test-apply 'jit-test-not 23) nil))
(should (eq (jit-test-apply 'jit-test-not nil) t))
(should (eq (jit-test-apply 'jit-test-not t) nil)))
;; Test Bbobp, Beobp, Bpoint, Bpoint_min, Bpoint_max.
(defun jit-test-bobp () (bobp))
(defun jit-test-eobp () (eobp))
(defun jit-test-point () (point))
(defun jit-test-point-min () (point-min))
(defun jit-test-point-max () (point-max))
(ert-deftest jit-bobp-and-eobp ()
(with-temp-buffer
(should (jit-test-apply 'jit-test-bobp))
(should (jit-test-apply 'jit-test-eobp))
(insert "hi")
(goto-char (point-min))
(should (eq (jit-test-apply 'jit-test-point-min) (point-min)))
(should (eq (jit-test-apply 'jit-test-point) (point-min)))
(should (jit-test-apply 'jit-test-bobp))
(should-not (jit-test-apply 'jit-test-eobp))
(goto-char (point-max))
(should (eq (jit-test-apply 'jit-test-point-max) (point-max)))
(should (eq (jit-test-apply 'jit-test-point) (point-max)))
(should-not (jit-test-apply 'jit-test-bobp))
(should (jit-test-apply 'jit-test-eobp))))
;; Test Bcar and Bcdr.
(defun jit-test-car (x) (car x))
(defun jit-test-cdr (x) (cdr x))
(ert-deftest jit-car-cdr ()
(let ((pair '(1 . b)))
(should (eq (jit-test-apply 'jit-test-car pair) 1))
(should (eq (jit-test-apply 'jit-test-car nil) nil))
(should-error (jit-test-apply 'jit-test-car 23)
:type 'wrong-type-argument)
(should (eq (jit-test-apply 'jit-test-cdr pair) 'b))
(should (eq (jit-test-apply 'jit-test-cdr nil) nil))
(should-error (jit-test-apply 'jit-test-cdr 23)
:type 'wrong-type-argument)))
;; Test Bcar_safe and Bcdr_safe.
(defun jit-test-car-safe (x) (car-safe x))
(defun jit-test-cdr-safe (x) (cdr-safe x))
(ert-deftest jit-car-cdr-safe ()
(let ((pair '(1 . b)))
(should (eq (jit-test-apply 'jit-test-car-safe pair) 1))
(should (eq (jit-test-apply 'jit-test-car-safe nil) nil))
(should (eq (jit-test-apply 'jit-test-car-safe 23) nil))
(should (eq (jit-test-apply 'jit-test-cdr-safe pair) 'b))
(should (eq (jit-test-apply 'jit-test-cdr-safe nil) nil))
(should (eq (jit-test-apply 'jit-test-cdr-safe 23) nil))))
;; Test Beq.
(defun jit-test-eq (x y) (eq x y))
(ert-deftest jit-eq ()
(should (jit-test-apply 'jit-test-eq 'a 'a))
(should (jit-test-apply 'jit-test-eq 5 5))
(should-not (jit-test-apply 'jit-test-eq 'a 'b))
(should-not (jit-test-apply 'jit-test-eq "x" "x")))
;; Test Bgotoifnil.
(defun jit-test-if (x y) (if x x y))
(ert-deftest jit-if ()
(should (eq (jit-test-apply 'jit-test-if 'a 'b) 'a))
(should (eq (jit-test-apply 'jit-test-if 0 23) 0))
(should (eq (jit-test-apply 'jit-test-if nil 'b) 'b)))
;; Test Bgotoifnilelsepop.
(defun jit-test-and (x y) (and x y))
(ert-deftest jit-and ()
(should (eq (jit-test-apply 'jit-test-and 'a 'b) 'b))
(should (eq (jit-test-apply 'jit-test-and 0 23) 23))
(should (eq (jit-test-apply 'jit-test-and nil 'b) nil)))
;; Test Bgotoifnonnilelsepop.
(defun jit-test-or (x y) (or x y))
(ert-deftest jit-or ()
(should (eq (jit-test-apply 'jit-test-or 'a 'b) 'a))
(should (eq (jit-test-apply 'jit-test-or 0 23) 0))
(should (eq (jit-test-apply 'jit-test-or nil 'b) 'b)))
;; Test Bsave_excursion.
(defun jit-test-save-excursion ()
(save-excursion
(insert "XYZ")))
;; Test Bcurrent_buffer.
(defun jit-test-current-buffer () (current-buffer))
(ert-deftest jit-save-excursion ()
(with-temp-buffer
(jit-test-apply 'jit-test-save-excursion)
(should (eq (point) (point-min)))
(should (eq (jit-test-apply 'jit-test-current-buffer) (current-buffer)))))
;; Test Bgtr.
(defun jit-test-> (a b)
(> a b))
(ert-deftest jit-> ()
(should (eq (jit-test-apply 'jit-test-> 0 23) nil))
(should (eq (jit-test-apply 'jit-test-> 23 0) t)))
;; Test Bpushcatch.
(defun jit-test-catch (&rest l)
(catch 'done
(dolist (v l)
(when (> v 23)
(throw 'done v)))))
(ert-deftest jit-catch ()
(should (eq (jit-test-apply 'jit-test-catch 0 1 2 3 4) nil))
(should (eq (jit-test-apply 'jit-test-catch 20 21 22 23 24 25 26 27 28) 24)))
;; Test Bmemq.
(defun jit-test-memq (val list)
(memq val list))
(ert-deftest jit-memq ()
(should (equal (jit-test-apply 'jit-test-memq 0 '(5 4 3 2 1 0)) '(0)))
(should (eq (jit-test-apply 'jit-test-memq 72 '(5 4 3 2 1 0)) nil)))
;; Test BlistN.
(defun jit-test-listN (x)
(list x x x x x x x x x x x x x x x x))
(ert-deftest jit-listN ()
(should (equal (jit-test-apply 'jit-test-listN 57)
'(57 57 57 57 57 57 57 57 57 57 57 57 57 57 57 57))))
;; Test BconcatN.
(defun jit-test-concatN (x)
(concat x x x x x x))
(ert-deftest jit-concatN ()
(should (equal (jit-test-apply 'jit-test-concatN "x") "xxxxxx")))
;; Test optional and rest arguments.
(defun jit-test-opt-rest (a &optional b &rest c)
(list a b c))
(ert-deftest jit-opt-rest ()
(should (equal (jit-test-apply 'jit-test-opt-rest 1) '(1 nil nil)))
(should (equal (jit-test-apply 'jit-test-opt-rest 1 2) '(1 2 nil)))
(should (equal (jit-test-apply 'jit-test-opt-rest 1 2 3) '(1 2 (3))))
(should (equal (jit-test-apply 'jit-test-opt-rest 1 2 56 57 58)
'(1 2 (56 57 58)))))
;; Test for too many arguments.
(defun jit-test-opt (a &optional b)
(cons a b))
(ert-deftest jit-opt ()
(should (equal (jit-test-apply 'jit-test-opt 23) '(23)))
(should (equal (jit-test-apply 'jit-test-opt 23 24) '(23 . 24)))
(should-error (jit-test-apply 'jit-test-opt)
:type 'wrong-number-of-arguments)
(should-error (jit-test-apply 'jit-test-opt nil 24 97)
:type 'wrong-number-of-arguments))
;; Test for unwind-protect.
(defvar jit-test-up-val nil)
(defun jit-test-unwind-protect (fun)
(setq jit-test-up-val nil)
(unwind-protect
(progn
(setq jit-test-up-val 23)
(funcall fun)
(setq jit-test-up-val 24))
(setq jit-test-up-val 999)))
(ert-deftest jit-unwind-protect ()
(jit-test-unwind-protect 'ignore)
(should (eq jit-test-up-val 999))
(condition-case nil
(jit-test-unwind-protect (lambda () (error "HI")))
(error
nil))
(should (eq jit-test-up-val 999)))
;;; jit-tests.el ends here