This PR addresses a missing check in the module system where private names that remain in the public environment map for technical reasons (e.g. inductive constructors generated by the kernel and relied on by the code generator) accidentally were accessible in the public scope.
113 lines
4.7 KiB
Text
113 lines
4.7 KiB
Text
/-
|
|
Copyright (c) 2021 Mac Malone. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Mac Malone
|
|
-/
|
|
module
|
|
|
|
prelude
|
|
public import Init.System.IO
|
|
|
|
public section
|
|
|
|
namespace Lean
|
|
|
|
private opaque DynlibImpl : NonemptyType.{0}
|
|
/-- A dynamic library handle. -/
|
|
def Dynlib := DynlibImpl.type
|
|
instance : Nonempty Dynlib := by exact DynlibImpl.property
|
|
|
|
private opaque Dynlib.SymbolImpl (dynlib : Dynlib) : NonemptyType.{0}
|
|
/-- A reference to a symbol within a dynamic library. -/
|
|
def Dynlib.Symbol (dynlib : Dynlib) := SymbolImpl dynlib |>.type
|
|
instance : Nonempty (Dynlib.Symbol dynlib) := by exact Dynlib.SymbolImpl dynlib |>.property
|
|
|
|
/--
|
|
Dynamically loads a shared library.
|
|
|
|
The path may also be used perform a system-dependent search for library.
|
|
To avoid this, use an absolute path (e.g., from `IO.FS.realPath`).
|
|
-/
|
|
@[extern "lean_dynlib_load"]
|
|
opaque Dynlib.load (path : @& System.FilePath) : IO Dynlib
|
|
|
|
/-- Returns the symbol of the dynamic library with the specified name (if any). -/
|
|
@[extern "lean_dynlib_get"]
|
|
opaque Dynlib.get? (dynlib : @& Dynlib) (sym : @& String) : Option dynlib.Symbol
|
|
|
|
/--
|
|
Runs a module initializer function.
|
|
The symbol should have the signature `(builtin : Bool) → IO Unit`
|
|
(e.g., `initialize_Foo(uint8_t builtin, obj_arg)`).
|
|
|
|
This function is unsafe because there is no guarantee the symbol has the
|
|
expected signature. An invalid symbol can thus produce undefined behavior.
|
|
Furthermore, if the initializer introduces pointers (e.g., function closures)
|
|
from the dynamic library into the global state, future garbage collection of
|
|
the library will produce undefined behavior. In such cases, garbage collection
|
|
of the dynamic library can be prevented via `Runtime.markPersistent` or
|
|
`Runtime.forget`.
|
|
-/
|
|
@[extern "lean_dynlib_symbol_run_as_init"]
|
|
unsafe opaque Dynlib.Symbol.runAsInit {dynlib : @& Dynlib} (sym : @& dynlib.Symbol) : IO Unit
|
|
|
|
/--
|
|
Dynamically loads a shared library so that its symbols can be used by
|
|
the Lean interpreter (e.g., for interpreting `@[extern]` declarations).
|
|
Equivalent to passing `--load-dynlib=path` to `lean`.
|
|
|
|
**Lean never unloads libraries.** Attempting to load a library that defines
|
|
symbols shared with a previously loaded library (including itself) will error.
|
|
If multiple libraries share common symbols, those symbols should be linked
|
|
and loaded as separate libraries.
|
|
-/
|
|
@[export lean_load_dynlib]
|
|
def loadDynlib (path : @& System.FilePath) : IO Unit := do
|
|
let dynlib ← Dynlib.load path
|
|
-- Lean never unloads libraries.
|
|
-- Safety: There are no concurrent accesses to `dynlib` at this point.
|
|
let _ ← unsafe Runtime.markPersistent dynlib
|
|
|
|
/--
|
|
Loads a Lean plugin and runs its initializers.
|
|
|
|
A Lean plugin is a shared library built from a Lean module.
|
|
This means it has an `initialize_<module-name>` symbol that runs the
|
|
module's initializers (including its imports' initializers). Initializers
|
|
are declared with the `initialize` or `builtin_initialize` commands.
|
|
|
|
This is similar to passing `--plugin=path` to `lean`.
|
|
Lean environment initializers, such as definitions calling
|
|
`registerEnvExtension`, also require `Lean.initializing` to be `true`.
|
|
To enable them, use `loadPlugin` within a `withImporting` block. This will
|
|
set `Lean.initializing` (but not `IO.initializing`).
|
|
|
|
**Lean never unloads plugins.** Attempting to load a plugin that defines
|
|
symbols shared with a previously loaded plugin (including itself) will error.
|
|
If multiple plugins share common symbols (e.g., imports), those symbols
|
|
should be linked and loaded separately.
|
|
-/
|
|
@[export lean_load_plugin]
|
|
def loadPlugin (path : System.FilePath) : IO Unit := do
|
|
-- We never want to look up plugins using the system library search
|
|
let path ← IO.FS.realPath path
|
|
let some name := path.fileStem
|
|
| throw <| IO.userError s!"error, plugin has invalid file name '{path}'"
|
|
let dynlib ← Dynlib.load path
|
|
-- Lean libraries can be prefixed with `lib` or suffixed with `_shared`
|
|
-- under some configurations. We strip these from the initializer symbol.
|
|
let name := name.stripPrefix "lib" |>.stripSuffix "_shared"
|
|
let name := s!"initialize_{name}"
|
|
let some sym := dynlib.get? name
|
|
| throw <| IO.userError s!"error loading plugin, initializer not found '{name}'"
|
|
-- Lean never unloads plugins (once initialized).
|
|
-- Safety: There are no concurrent accesses to `dynlib` at this point.
|
|
let _ ← unsafe Runtime.markPersistent dynlib
|
|
/-
|
|
Safety:
|
|
* As `dynlib` is marked persistent, there is no danger of garbage collection.
|
|
* There is still no guarantee that `sym` has the proper signature, but this
|
|
is about as safe as it can be. The initializer naming convention helps
|
|
avoid accidentally mistaking non-plugins as plugins.
|
|
-/
|
|
unsafe sym.runAsInit
|