Skip to content

Tags: lispnik/objc

Tags

v0.4.1

Toggle v0.4.1's commit message
Release 0.4.1 -- examples and documentation

src/ is byte for byte what it was at v0.4.0.  Anyone depending on #:objc gets
nothing new, which is what the patch number says; this exists so a distribution
has a fixed point to reference rather than a moving master.

OBJC-BLOCK-LIVE-P gains the example it was missing, in audio.lisp -- the only
example whose block outlives the form that made it, because a render block is
held by an engine that will call it until stopped.  That is the case
WITH-OBJC-BLOCK cannot serve and the reason MAKE-OBJC-BLOCK is exported.  With
it, every exported symbol that should have an example has one; the three that
remain are type descriptors that appear in signatures the runtime hands you and
never in code you write.

doc/demo.md scripts the live-coding demo over canvas.lisp and shader.lisp, with
every form in it run before it was written down -- which changed it three times.
The pitch was "edit in Emacs and watch the window change", and that cannot work:
AppKit needs thread 1, SLY evaluates on a worker, and the entry points refuse.
It is a terminal beside a window, which is the better demo anyway.

934 checks, green on arm64 and Intel.

v0.4.0

Toggle v0.4.0's commit message
Release 0.4.0 -- six examples, and two tests that were not testing an…

…ything

src/ changes here are comments and docstrings only: no behaviour, no
exported symbol.  Anyone depending on #:objc gets nothing new.  The minor
number is for the size of everything else, not for an API change --
block creation, which is the last real addition, shipped in 0.3.0.

SIX NEW EXAMPLES, chosen by measuring which exported operators no example
used and closing the gap:

collections.lisp -- a Lisp object as a first-class citizen of Cocoa's own
data structures, asked -hash, -isEqual: and -description and answering
from CLOS slots.  The CLOS half of the library had no example at all.

browser.lisp -- a class browser over the library's own introspection,
which was exported, documented, and called by nothing but the test
suite.

undo.lisp -- NSUndoManager running Lisp methods as its undo operations,
and the discovery that a forwarding proxy is invisible here:
-prepareWithInvocationTarget: cannot be sent to, because dispatch
resolves the Method first.  True of every -forwardInvocation: proxy.

memory.lisp -- retain counts and pools, with deaths observed through
OBJC-OBJECT-DESTROYED rather than inferred.  A tagged pointer's retain
count is nonsense and inconsistent nonsense: 2^64-1 from a short
NSString, 2^63-1 from a tagged NSNumber.  Draining a pool on the wrong
thread is a memory fault, not a diagnostic.  A missing pool is silent
even under OBJC_DEBUG_MISSING_POOLS.

notifications.lisp -- NSNotificationCenter, where the handler runs on
the thread that POSTED rather than the one that registered.  A dead
observer is no longer a crash: modern macOS zeroes the reference, which
the folklore has not caught up with.

geometry, strings, task and plugin -- the four that finish the job.
COCOA is now fully exercised and so is every defining macro.  A filled
foreign structure passes anywhere its vector does; NSNotFound is
NSIntegerMax and not -1; an NSString counts UTF-16 code units so a Lisp
SUBSEQ at a Cocoa offset is in bounds and wrong; -waitUntilExit before
draining a pipe deadlocks past 64KB; and a typedef is erased at the
boundary.

TWO TESTS WERE PASSING WITHOUT TESTING ANYTHING.
ASDF:SYSTEM-RELATIVE-PATHNAME quotes a wildcard, so "src/*.lisp" named a
file that does not exist and DIRECTORY returned NIL.
ONLY-ABI-LISP-KNOWS-ABOUT-SB-ALIEN -- the test CLAUDE.md describes as
what keeps the seam a seam -- was green with sb-alien: sitting in
src/cocoa.lisp.  Both file-scanning tests now enumerate properly and
assert a floor on the count first, and each was verified by breaking
something on purpose.  An audit found no test in the suite that makes
zero assertions: 266 tests, 266 ran, 931 checks.

Green on arm64 and Intel.

v0.3.1

Toggle v0.3.1's commit message
Release 0.3.1 -- four more examples, and no library change at all

src/ is byte for byte what it was at v0.3.0.  Anyone depending on #:objc
gets nothing new, which is what the patch number is saying; everything
here is in examples/ and test/.

metal.lisp -- GPU compute.  A Metal Shading Language kernel compiled at
run time from a string and run over a Lisp vector, so the GPU program is
data another function wrote.  It exercises more of the bridge at once
than anything else: a plain C entry point, message sends, an
owned-object convention, and MTLSize passed by value.  Its timing
report corrects the claim people expect -- for element-wise work the GPU
LOSES, because copying dominates, and it wins by 200x or more only once
the arithmetic per element covers two copies.

scene-kit.lisp -- a scene graph built from Lisp forms and rendered to a
PNG with no window, because SCNRenderer draws into an image rather than
a view.  Ships no assets; the picture is described where you can read
it.

audio.lisp -- an AVAudioSourceNode whose render block is a Lisp closure,
so the block is the instrument.  Tested offline through the engine's
manual rendering mode, which is deterministic and silent and needs no
sound hardware, and demonstrated live through PLAY, which no test calls.
The header records what makes it unlike everything else here: the render
block runs on a real-time thread with a deadline, where a collection
that overruns is audible.

shader.lisp -- one MSL expression per pixel, written to a PNG or
animated in a window through the canvas's draw hook.  The canvas's live
loop with the GPU drawing.

Four traps recorded, each found by writing the example rather than by
reasoning about it:

  MTLCreateSystemDefaultDevice signals FLOATING-POINT-OVERFLOW.  The
  bridge masks Cocoa's traps around message sends, and a CFFI:DEFCFUN is
  not a message send.

  -bitmapData returns `unsigned char *', which encodes exactly as a C
  string does, so plain INVOKE hands back "" -- the buffer starts with a
  zero byte.  INVOKE-INTO with :POINTER is what the manual provides, and
  nothing else here had needed it.

  -dispatchThreads: rounds the grid up to whole threadgroups, so a
  kernel without a bounds check writes past its buffer on any size that
  is not a multiple of the group.

  An amplitude of 0.5 comes back peaking at 0.5/sqrt(2), because the
  main mixer attenuates.  Measured; the pan-law explanation is inference
  and is labelled as such.

820 checks, green on stock and safepoint builds, both architectures.

v0.3.0

Toggle v0.3.0's commit message
Release 0.3.0 -- Objective-C blocks

The headline is block creation.  This library could pass and receive a
block as an opaque pointer but not make one, which put the
completion-handler half of macOS out of reach.  MAKE-OBJC-BLOCK builds a
real Block ABI literal whose invoke function is an alien callable, so
Cocoa calls a Lisp closure; CALL-OBJC-BLOCK goes the other way.
Structures cross by value in both directions.  Foundation now sorts an
NSArray through a Lisp comparator, and dispatch_async enters Lisp on a
libdispatch worker thread.

Lifetimes are refcounted through the descriptor's copy and dispose
helpers, so freeing a block Cocoa still holds is safe: the copy took its
own reference.  WITH-OBJC-BLOCK is right even for asynchronous work.

This widens the OBJC package past the 42 symbols the LispWorks manual
documents, by 8.  LispWorks has no block interface in OBJC at all -- it
lives in the FLI there, and there is no FLI here.  The seam test asserts
the exact exported set rather than a count, so the widening had to be
written down rather than absorbed.

Two bugs fixed, both silent, both older than this release:

  Every BOOL ARGUMENT was NO.  :bool mapped to sb-alien's (boolean 8),
  which converts on SBCL's side and wants a generalized boolean, while
  every conversion function here produces the integer 1 or 0 -- and
  (boolean 8) given 1 arrives as NO.  Results were right the whole time,
  which is why the suite never noticed.  It is (unsigned 8) now.

  INVOKE returned a pointer into a freed buffer for a structure result
  with no Lisp representation.  It read as plausible numbers.  It signals
  now, and names INVOKE-INTO.

Both were found by writing examples, not by writing tests.

A concurrency limit, measured and then lifted.  Two libdispatch worker
threads inside Lisp at once kill a stock SBCL: a GC stops the world by
signalling, and Darwin will not signal a workqueue thread at all -- you
get one for free only because the collector skips the thread that
triggered it.  Building SBCL --with-sb-safepoint removes it entirely, and
CI now proves that on every push.  PARALLEL-MAP exists and refuses,
rather than crashing, where the build cannot take it.

Ten new examples: GCD, NSURLSession, NaturalLanguage, Core Image, a
dispatch-source file watcher, KVO, NSDataDetector, NSPredicate and
variadic sends, headless PDFKit, Quick Look thumbnails, and NSWorkspace.
Several exist to record a trap rather than to show an API.

798 checks, green on stock and safepoint builds, on both architectures.

v0.2.0

Toggle v0.2.0's commit message
objc v0.2.0

A live drawing canvas (examples/canvas.lisp): a real NSView subclass whose
-drawRect: calls a Lisp function you redefine at the REPL while the window is
open, drawing with NSColor and NSBezierPath. It puts struct-by-value on the hot
path -- -drawRect: takes an NSRect by value, every shape passes NSRect/NSPoint
by value -- and renders offscreen for a windowless test.

Underneath, large structure returns are routed through objc_msgSend_stret where
the ABI calls for it. CI: dropped the vanished free Intel runner, then took the
Intel label that still exists. And the Status section now says plainly there
will be no FLI compatibility layer.

v0.1.0

Toggle v0.1.0's commit message
objc 0.1.0

The LispWorks Objective-C interface, reimplemented for SBCL on macOS.

All 42 symbols of the OBJC package and all 11 of COCOA, with the LispWorks
names and lambda lists.  Dynamic dispatch through compiled per-signature
trampolines over objc_msgSend; Objective-C classes defined from Lisp with real
IMPs, taking and returning C structs by value.

643 checks, green locally and on CI.  Behaviour the manual leaves ambiguous was
settled against a running LispWorks Personal 8.1 and is asserted against.

Known limits: an Objective-C exception terminates the process, variadic methods
need :variadic-num-of-fixed, and arm64 is the only architecture this has run on.