Petr Mrázek 2012-06-23 00:25:50 +02:00
commit 59fb4daa9a
65 changed files with 3327 additions and 809 deletions

@ -152,10 +152,13 @@ Valid and useful build types include 'Release', 'Debug' and
================================ ================================
Using the library as a developer Using the library as a developer
================================ ================================
Currently, the only way to use the library is to write a plugin that can be loaded by it.
Currently, the most direct way to use the library is to write a plugin that can be loaded by it.
All the plugins can be found in the 'plugins' folder. There's no in-depth documentation All the plugins can be found in the 'plugins' folder. There's no in-depth documentation
on how to write one yet, but it should be easy enough to copy one and just follow the pattern. on how to write one yet, but it should be easy enough to copy one and just follow the pattern.
Other than through plugins, it is possible to use DFHack via remote access interface, or by writing Lua scripts.
The most important parts of DFHack are the Core, Console, Modules and Plugins. The most important parts of DFHack are the Core, Console, Modules and Plugins.
* Core acts as the centerpiece of DFHack - it acts as a filter between DF and SDL and synchronizes the various plugins with DF. * Core acts as the centerpiece of DFHack - it acts as a filter between DF and SDL and synchronizes the various plugins with DF.
@ -171,6 +174,24 @@ The main license is zlib/libpng, some bits are MIT licensed, and some are BSD li
Feel free to add your own extensions and plugins. Contributing back to Feel free to add your own extensions and plugins. Contributing back to
the dfhack repository is welcome and the right thing to do :) the dfhack repository is welcome and the right thing to do :)
DF data structure definitions
=============================
DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.
Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.
Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.
Remote access interface
=======================
DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The ``dfhack-run`` command uses this interface to invoke ordinary console commands.
Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.
Protocol client implementations exist for Java and C#.
Contributing to DFHack Contributing to DFHack
====================== ======================

@ -334,10 +334,12 @@ ul.auto-toc {
</li> </li>
<li><a class="reference internal" href="#build-types" id="id12">Build types</a></li> <li><a class="reference internal" href="#build-types" id="id12">Build types</a></li>
<li><a class="reference internal" href="#using-the-library-as-a-developer" id="id13">Using the library as a developer</a><ul> <li><a class="reference internal" href="#using-the-library-as-a-developer" id="id13">Using the library as a developer</a><ul>
<li><a class="reference internal" href="#contributing-to-dfhack" id="id14">Contributing to DFHack</a><ul> <li><a class="reference internal" href="#df-data-structure-definitions" id="id14">DF data structure definitions</a></li>
<li><a class="reference internal" href="#coding-style" id="id15">Coding style</a></li> <li><a class="reference internal" href="#remote-access-interface" id="id15">Remote access interface</a></li>
<li><a class="reference internal" href="#how-to-get-new-code-into-dfhack" id="id16">How to get new code into DFHack</a></li> <li><a class="reference internal" href="#contributing-to-dfhack" id="id16">Contributing to DFHack</a><ul>
<li><a class="reference internal" href="#memory-research" id="id17">Memory research</a></li> <li><a class="reference internal" href="#coding-style" id="id17">Coding style</a></li>
<li><a class="reference internal" href="#how-to-get-new-code-into-dfhack" id="id18">How to get new code into DFHack</a></li>
<li><a class="reference internal" href="#memory-research" id="id19">Memory research</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -470,9 +472,10 @@ cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
</div> </div>
<div class="section" id="using-the-library-as-a-developer"> <div class="section" id="using-the-library-as-a-developer">
<h1><a class="toc-backref" href="#id13">Using the library as a developer</a></h1> <h1><a class="toc-backref" href="#id13">Using the library as a developer</a></h1>
<p>Currently, the only way to use the library is to write a plugin that can be loaded by it. <p>Currently, the most direct way to use the library is to write a plugin that can be loaded by it.
All the plugins can be found in the 'plugins' folder. There's no in-depth documentation All the plugins can be found in the 'plugins' folder. There's no in-depth documentation
on how to write one yet, but it should be easy enough to copy one and just follow the pattern.</p> on how to write one yet, but it should be easy enough to copy one and just follow the pattern.</p>
<p>Other than through plugins, it is possible to use DFHack via remote access interface, or by writing Lua scripts.</p>
<p>The most important parts of DFHack are the Core, Console, Modules and Plugins.</p> <p>The most important parts of DFHack are the Core, Console, Modules and Plugins.</p>
<ul class="simple"> <ul class="simple">
<li>Core acts as the centerpiece of DFHack - it acts as a filter between DF and SDL and synchronizes the various plugins with DF.</li> <li>Core acts as the centerpiece of DFHack - it acts as a filter between DF and SDL and synchronizes the various plugins with DF.</li>
@ -485,18 +488,30 @@ on how to write one yet, but it should be easy enough to copy one and just follo
The main license is zlib/libpng, some bits are MIT licensed, and some are BSD licensed.</p> The main license is zlib/libpng, some bits are MIT licensed, and some are BSD licensed.</p>
<p>Feel free to add your own extensions and plugins. Contributing back to <p>Feel free to add your own extensions and plugins. Contributing back to
the dfhack repository is welcome and the right thing to do :)</p> the dfhack repository is welcome and the right thing to do :)</p>
<div class="section" id="df-data-structure-definitions">
<h2><a class="toc-backref" href="#id14">DF data structure definitions</a></h2>
<p>DFHack uses information about the game data structures, represented via xml files in the library/xml/ submodule.</p>
<p>Data structure layouts are described in files following the df.*.xml name pattern. This information is transformed by a perl script into C++ headers describing the structures, and associated metadata for the Lua wrapper. These headers and data are then compiled into the DFHack libraries, thus necessitating a compatibility break every time layouts change; in return it significantly boosts the efficiency and capabilities of DFHack code.</p>
<p>Global object addresses are stored in symbols.xml, which is copied to the dfhack release package and loaded as data at runtime.</p>
</div>
<div class="section" id="remote-access-interface">
<h2><a class="toc-backref" href="#id15">Remote access interface</a></h2>
<p>DFHack supports remote access by exchanging Google protobuf messages via a TCP socket. Both the core and plugins can define remotely accessible methods. The <tt class="docutils literal"><span class="pre">dfhack-run</span></tt> command uses this interface to invoke ordinary console commands.</p>
<p>Currently the supported set of requests is limited, because the developers don't know what exactly is most useful.</p>
<p>Protocol client implementations exist for Java and C#.</p>
</div>
<div class="section" id="contributing-to-dfhack"> <div class="section" id="contributing-to-dfhack">
<h2><a class="toc-backref" href="#id14">Contributing to DFHack</a></h2> <h2><a class="toc-backref" href="#id16">Contributing to DFHack</a></h2>
<p>Several things should be kept in mind when contributing to DFHack.</p> <p>Several things should be kept in mind when contributing to DFHack.</p>
<div class="section" id="coding-style"> <div class="section" id="coding-style">
<h3><a class="toc-backref" href="#id15">Coding style</a></h3> <h3><a class="toc-backref" href="#id17">Coding style</a></h3>
<p>DFhack uses ANSI formatting and four spaces as indentation. Line <p>DFhack uses ANSI formatting and four spaces as indentation. Line
endings are UNIX. The files use UTF-8 encoding. Code not following this endings are UNIX. The files use UTF-8 encoding. Code not following this
won't make me happy, because I'll have to fix it. There's a good chance won't make me happy, because I'll have to fix it. There's a good chance
I'll make <em>you</em> fix it ;)</p> I'll make <em>you</em> fix it ;)</p>
</div> </div>
<div class="section" id="how-to-get-new-code-into-dfhack"> <div class="section" id="how-to-get-new-code-into-dfhack">
<h3><a class="toc-backref" href="#id16">How to get new code into DFHack</a></h3> <h3><a class="toc-backref" href="#id18">How to get new code into DFHack</a></h3>
<p>You can send patches or make a clone of the github repo and ask me on <p>You can send patches or make a clone of the github repo and ask me on
the IRC channel to pull your code in. I'll review it and see if there the IRC channel to pull your code in. I'll review it and see if there
are any problems. I'll fix them if they are minor.</p> are any problems. I'll fix them if they are minor.</p>
@ -506,7 +521,7 @@ this is also a good place to dump new ideas and/or bugs that need
fixing.</p> fixing.</p>
</div> </div>
<div class="section" id="memory-research"> <div class="section" id="memory-research">
<h3><a class="toc-backref" href="#id17">Memory research</a></h3> <h3><a class="toc-backref" href="#id19">Memory research</a></h3>
<p>If you want to do memory research, you'll need some tools and some knowledge. <p>If you want to do memory research, you'll need some tools and some knowledge.
In general, you'll need a good memory viewer and optionally something In general, you'll need a good memory viewer and optionally something
to look at machine code without getting crazy :)</p> to look at machine code without getting crazy :)</p>

@ -4,9 +4,26 @@ DFHack Lua API
.. contents:: .. contents::
==================== The current version of DFHack has extensive support for
DF structure wrapper the Lua scripting language, providing access to:
====================
1. Raw data structures used by the game.
2. Many C++ functions for high-level access to these
structures, and interaction with dfhack itself.
3. Some functions exported by C++ plugins.
Lua code can be used both for writing scripts, which
are treated by DFHack command line prompt almost as
native C++ commands, and invoked by plugins written in c++.
This document describes native API available to Lua in detail.
For the most part it does not describe utility functions
implemented by Lua files located in hack/lua/...
=========================
DF data structure wrapper
=========================
DF structures described by the xml files in library/xml are exported DF structures described by the xml files in library/xml are exported
to lua code as a tree of objects and functions under the ``df`` global, to lua code as a tree of objects and functions under the ``df`` global,
@ -426,13 +443,17 @@ not destroy any objects allocated in this way, so the user
should be prepared to catch the error and do the necessary should be prepared to catch the error and do the necessary
cleanup. cleanup.
================ ==========
DFHack utilities DFHack API
================ ==========
DFHack utility functions are placed in the ``dfhack`` global tree. DFHack utility functions are placed in the ``dfhack`` global tree.
Currently it defines the following features: Native utilities
================
Input & Output
--------------
* ``dfhack.print(args...)`` * ``dfhack.print(args...)``
@ -451,6 +472,7 @@ Currently it defines the following features:
* ``dfhack.color([color])`` * ``dfhack.color([color])``
Sets the current output color. If color is *nil* or *-1*, resets to default. Sets the current output color. If color is *nil* or *-1*, resets to default.
Returns the previous color value.
* ``dfhack.is_interactive()`` * ``dfhack.is_interactive()``
@ -473,15 +495,27 @@ Currently it defines the following features:
If the interactive console is not accessible, returns *nil, error*. If the interactive console is not accessible, returns *nil, error*.
Exception handling
------------------
* ``dfhack.error(msg[,level[,verbose]])``
Throws a dfhack exception object with location and stack trace.
The verbose parameter controls whether the trace is printed by default.
* ``qerror(msg[,level])``
Calls ``dfhack.error()`` with ``verbose`` being *false*. Intended to
be used for user-caused errors in scripts, where stack traces are not
desirable.
* ``dfhack.pcall(f[,args...])`` * ``dfhack.pcall(f[,args...])``
Invokes f via xpcall, using an error function that attaches Invokes f via xpcall, using an error function that attaches
a stack trace to the error. The same function is used by SafeCall a stack trace to the error. The same function is used by SafeCall
in C++, and dfhack.safecall. in C++, and dfhack.safecall.
The returned error is a table with separate ``message`` and
``stacktrace`` string fields; it implements ``__tostring``.
* ``safecall(f[,args...])``, ``dfhack.safecall(f[,args...])`` * ``safecall(f[,args...])``, ``dfhack.safecall(f[,args...])``
Just like pcall, but also prints the error using printerr before Just like pcall, but also prints the error using printerr before
@ -491,6 +525,34 @@ Currently it defines the following features:
Compares to coroutine.resume like dfhack.safecall vs pcall. Compares to coroutine.resume like dfhack.safecall vs pcall.
* ``dfhack.exception``
Metatable of error objects used by dfhack. The objects have the
following properties:
``err.where``
The location prefix string, or *nil*.
``err.message``
The base message string.
``err.stacktrace``
The stack trace string, or *nil*.
``err.cause``
A different exception object, or *nil*.
``err.thread``
The coroutine that has thrown the exception.
``err.verbose``
Boolean, or *nil*; specifies if where and stacktrace should be printed.
``tostring(err)``, or ``err:tostring([verbose])``
Converts the exception to string.
* ``dfhack.exception.verbose``
The default value of the ``verbose`` argument of ``err:tostring()``.
Locking and finalization
------------------------
* ``dfhack.with_suspend(f[,args...])`` * ``dfhack.with_suspend(f[,args...])``
Calls ``f`` with arguments after grabbing the DF core suspend lock. Calls ``f`` with arguments after grabbing the DF core suspend lock.
@ -529,7 +591,7 @@ Currently it defines the following features:
Persistent configuration storage Persistent configuration storage
================================ --------------------------------
This api is intended for storing configuration options in the world itself. This api is intended for storing configuration options in the world itself.
It probably should be restricted to data that is world-dependent. It probably should be restricted to data that is world-dependent.
@ -571,7 +633,7 @@ functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead. However, currently every entry has a 180+-byte dead-weight overhead.
Material info lookup Material info lookup
==================== --------------------
A material info record has fields: A material info record has fields:
@ -1148,6 +1210,11 @@ Internal API
These functions are intended for the use by dfhack developers, These functions are intended for the use by dfhack developers,
and are only documented here for completeness: and are only documented here for completeness:
* ``dfhack.internal.scripts``
The table used by ``dfhack.run_script()`` to give every script its own
global environment, persistent between calls to the script.
* ``dfhack.internal.getAddress(name)`` * ``dfhack.internal.getAddress(name)``
Returns the global address ``name``, or *nil*. Returns the global address ``name``, or *nil*.
@ -1156,14 +1223,38 @@ and are only documented here for completeness:
Sets the global address ``name``. Returns the value of ``getAddress`` before the change. Sets the global address ``name``. Returns the value of ``getAddress`` before the change.
* ``dfhack.internal.getBase()`` * ``dfhack.internal.getVTable(name)``
Returns the pre-extracted vtable address ``name``, or *nil*.
Returns the base address of the process. * ``dfhack.internal.getRebaseDelta()``
Returns the ASLR rebase offset of the DF executable.
* ``dfhack.internal.getMemRanges()`` * ``dfhack.internal.getMemRanges()``
Returns a sequence of tables describing virtual memory ranges of the process. Returns a sequence of tables describing virtual memory ranges of the process.
* ``dfhack.internal.memmove(dest,src,count)``
Wraps the standard memmove function. Accepts both numbers and refs as pointers.
* ``dfhack.internal.memcmp(ptr1,ptr2,count)``
Wraps the standard memcmp function.
* ``dfhack.internal.memscan(haystack,count,step,needle,nsize)``
Searches for ``needle`` of ``nsize`` bytes in ``haystack``,
using ``count`` steps of ``step`` bytes.
Returns: *step_idx, sum_idx, found_ptr*, or *nil* if not found.
* ``dfhack.internal.diffscan(old_data, new_data, start_idx, end_idx, eltsize[, oldval, newval, delta])``
Searches for differences between buffers at ptr1 and ptr2, as integers of size eltsize.
The oldval, newval or delta arguments may be used to specify additional constraints.
Returns: *found_index*, or *nil* if end reached.
Core interpreter context Core interpreter context
======================== ========================
@ -1231,6 +1322,42 @@ Features:
Invokes all listeners contained in the event in an arbitrary Invokes all listeners contained in the event in an arbitrary
order using ``dfhack.safecall``. order using ``dfhack.safecall``.
=======
Modules
=======
DFHack sets up the lua interpreter so that the built-in ``require``
function can be used to load shared lua code from hack/lua/.
The ``dfhack`` namespace reference itself may be obtained via
``require('dfhack')``, although it is initially created as a
global by C++ bootstrap code.
The following functions are provided:
* ``mkmodule(name)``
Creates an environment table for the module. Intended to be used as::
local _ENV = mkmodule('foo')
...
return _ENV
If called the second time, returns the same table; thus providing reload support.
* ``reload(name)``
Reloads a previously ``require``-d module *"name"* from the file.
Intended as a help for module development.
* ``dfhack.BASE_G``
This variable contains the root global environment table, which is
used as a base for all module and script environments. Its contents
should be kept limited to the standard Lua library and API described
in this document.
======= =======
Plugins Plugins
======= =======
@ -1292,3 +1419,40 @@ sort
Does not export any native functions as of now. Instead, it Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items. calls lua code to perform the actual ordering of list items.
=======
Scripts
=======
Any files with the .lua extension placed into hack/scripts/*
are automatically used by the DFHack core as commands. The
matching command name consists of the name of the file sans
the extension.
**NOTE:** Scripts placed in subdirectories still can be accessed, but
do not clutter the ``ls`` command list; thus it is preferred
for obscure developer-oriented scripts and scripts used by tools.
When calling such scripts, always use '/' as the separator for
directories, e.g. ``devel/lua-example``.
Scripts are re-read from disk every time they are used
(this may be changed later to check the file change time); however
the global variable values persist in memory between calls.
Every script gets its own separate environment for global
variables.
Arguments are passed in to the scripts via the **...** built-in
quasi-variable; when the script is called by the DFHack core,
they are all guaranteed to be non-nil strings.
DFHack core invokes the scripts in the *core context* (see above);
however it is possible to call them from any lua code (including
from other scripts) in any context, via the same function the core uses:
* ``dfhack.run_script(name[,args...])``
Run a lua script in hack/scripts/, as if it was started from dfhack command-line.
The ``name`` argument should be the name stem, as would be used on the command line.
Note that this function lets errors propagate to the caller.

@ -320,7 +320,7 @@ ul.auto-toc {
<div class="contents topic" id="contents"> <div class="contents topic" id="contents">
<p class="topic-title first">Contents</p> <p class="topic-title first">Contents</p>
<ul class="simple"> <ul class="simple">
<li><a class="reference internal" href="#df-structure-wrapper" id="id1">DF structure wrapper</a><ul> <li><a class="reference internal" href="#df-data-structure-wrapper" id="id1">DF data structure wrapper</a><ul>
<li><a class="reference internal" href="#typed-object-references" id="id2">Typed object references</a><ul> <li><a class="reference internal" href="#typed-object-references" id="id2">Typed object references</a><ul>
<li><a class="reference internal" href="#primitive-references" id="id3">Primitive references</a></li> <li><a class="reference internal" href="#primitive-references" id="id3">Primitive references</a></li>
<li><a class="reference internal" href="#struct-references" id="id4">Struct references</a></li> <li><a class="reference internal" href="#struct-references" id="id4">Struct references</a></li>
@ -333,36 +333,58 @@ ul.auto-toc {
<li><a class="reference internal" href="#recursive-table-assignment" id="id9">Recursive table assignment</a></li> <li><a class="reference internal" href="#recursive-table-assignment" id="id9">Recursive table assignment</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#dfhack-utilities" id="id10">DFHack utilities</a><ul> <li><a class="reference internal" href="#dfhack-api" id="id10">DFHack API</a><ul>
<li><a class="reference internal" href="#persistent-configuration-storage" id="id11">Persistent configuration storage</a></li> <li><a class="reference internal" href="#native-utilities" id="id11">Native utilities</a><ul>
<li><a class="reference internal" href="#material-info-lookup" id="id12">Material info lookup</a></li> <li><a class="reference internal" href="#input-output" id="id12">Input &amp; Output</a></li>
<li><a class="reference internal" href="#c-function-wrappers" id="id13">C++ function wrappers</a><ul> <li><a class="reference internal" href="#exception-handling" id="id13">Exception handling</a></li>
<li><a class="reference internal" href="#gui-module" id="id14">Gui module</a></li> <li><a class="reference internal" href="#locking-and-finalization" id="id14">Locking and finalization</a></li>
<li><a class="reference internal" href="#job-module" id="id15">Job module</a></li> <li><a class="reference internal" href="#persistent-configuration-storage" id="id15">Persistent configuration storage</a></li>
<li><a class="reference internal" href="#units-module" id="id16">Units module</a></li> <li><a class="reference internal" href="#material-info-lookup" id="id16">Material info lookup</a></li>
<li><a class="reference internal" href="#items-module" id="id17">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id18">Maps module</a></li>
<li><a class="reference internal" href="#burrows-module" id="id19">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id20">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id21">Constructions module</a></li>
<li><a class="reference internal" href="#internal-api" id="id22">Internal API</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#core-interpreter-context" id="id23">Core interpreter context</a><ul> <li><a class="reference internal" href="#c-function-wrappers" id="id17">C++ function wrappers</a><ul>
<li><a class="reference internal" href="#event-type" id="id24">Event type</a></li> <li><a class="reference internal" href="#gui-module" id="id18">Gui module</a></li>
<li><a class="reference internal" href="#job-module" id="id19">Job module</a></li>
<li><a class="reference internal" href="#units-module" id="id20">Units module</a></li>
<li><a class="reference internal" href="#items-module" id="id21">Items module</a></li>
<li><a class="reference internal" href="#maps-module" id="id22">Maps module</a></li>
<li><a class="reference internal" href="#burrows-module" id="id23">Burrows module</a></li>
<li><a class="reference internal" href="#buildings-module" id="id24">Buildings module</a></li>
<li><a class="reference internal" href="#constructions-module" id="id25">Constructions module</a></li>
<li><a class="reference internal" href="#internal-api" id="id26">Internal API</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#core-interpreter-context" id="id27">Core interpreter context</a><ul>
<li><a class="reference internal" href="#event-type" id="id28">Event type</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#plugins" id="id25">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id26">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id27">sort</a></li>
</ul> </ul>
</li> </li>
<li><a class="reference internal" href="#modules" id="id29">Modules</a></li>
<li><a class="reference internal" href="#plugins" id="id30">Plugins</a><ul>
<li><a class="reference internal" href="#burrows" id="id31">burrows</a></li>
<li><a class="reference internal" href="#sort" id="id32">sort</a></li>
</ul>
</li>
<li><a class="reference internal" href="#scripts" id="id33">Scripts</a></li>
</ul> </ul>
</div> </div>
<div class="section" id="df-structure-wrapper"> <p>The current version of DFHack has extensive support for
<h1><a class="toc-backref" href="#id1">DF structure wrapper</a></h1> the Lua scripting language, providing access to:</p>
<ol class="arabic simple">
<li>Raw data structures used by the game.</li>
<li>Many C++ functions for high-level access to these
structures, and interaction with dfhack itself.</li>
<li>Some functions exported by C++ plugins.</li>
</ol>
<p>Lua code can be used both for writing scripts, which
are treated by DFHack command line prompt almost as
native C++ commands, and invoked by plugins written in c++.</p>
<p>This document describes native API available to Lua in detail.
For the most part it does not describe utility functions
implemented by Lua files located in hack/lua/...</p>
<div class="section" id="df-data-structure-wrapper">
<h1><a class="toc-backref" href="#id1">DF data structure wrapper</a></h1>
<p>DF structures described by the xml files in library/xml are exported <p>DF structures described by the xml files in library/xml are exported
to lua code as a tree of objects and functions under the <tt class="docutils literal">df</tt> global, to lua code as a tree of objects and functions under the <tt class="docutils literal">df</tt> global,
which broadly maps to the <tt class="docutils literal">df</tt> namespace in C++.</p> which broadly maps to the <tt class="docutils literal">df</tt> namespace in C++.</p>
@ -717,10 +739,13 @@ should be prepared to catch the error and do the necessary
cleanup.</p> cleanup.</p>
</div> </div>
</div> </div>
<div class="section" id="dfhack-utilities"> <div class="section" id="dfhack-api">
<h1><a class="toc-backref" href="#id10">DFHack utilities</a></h1> <h1><a class="toc-backref" href="#id10">DFHack API</a></h1>
<p>DFHack utility functions are placed in the <tt class="docutils literal">dfhack</tt> global tree.</p> <p>DFHack utility functions are placed in the <tt class="docutils literal">dfhack</tt> global tree.</p>
<p>Currently it defines the following features:</p> <div class="section" id="native-utilities">
<h2><a class="toc-backref" href="#id11">Native utilities</a></h2>
<div class="section" id="input-output">
<h3><a class="toc-backref" href="#id12">Input &amp; Output</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.print(args...)</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.print(args...)</span></tt></p>
<p>Output tab-separated args as standard lua print would do, <p>Output tab-separated args as standard lua print would do,
@ -734,7 +759,8 @@ works with DFHack output infrastructure.</p>
<p>Same as println; intended for errors. Uses red color and logs to stderr.log.</p> <p>Same as println; intended for errors. Uses red color and logs to stderr.log.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.color([color])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.color([color])</span></tt></p>
<p>Sets the current output color. If color is <em>nil</em> or <em>-1</em>, resets to default.</p> <p>Sets the current output color. If color is <em>nil</em> or <em>-1</em>, resets to default.
Returns the previous color value.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.is_interactive()</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.is_interactive()</tt></p>
<p>Checks if the thread can access the interactive console and returns <em>true</em> or <em>false</em>.</p> <p>Checks if the thread can access the interactive console and returns <em>true</em> or <em>false</em>.</p>
@ -752,12 +778,24 @@ this, forcing the function to block on input with lock held.</p>
string, global environment and command-line history file.</p> string, global environment and command-line history file.</p>
<p>If the interactive console is not accessible, returns <em>nil, error</em>.</p> <p>If the interactive console is not accessible, returns <em>nil, error</em>.</p>
</li> </li>
</ul>
</div>
<div class="section" id="exception-handling">
<h3><a class="toc-backref" href="#id13">Exception handling</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.error(msg[,level[,verbose]])</span></tt></p>
<p>Throws a dfhack exception object with location and stack trace.
The verbose parameter controls whether the trace is printed by default.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">qerror(msg[,level])</span></tt></p>
<p>Calls <tt class="docutils literal">dfhack.error()</tt> with <tt class="docutils literal">verbose</tt> being <em>false</em>. Intended to
be used for user-caused errors in scripts, where stack traces are not
desirable.</p>
</li>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.pcall(f[,args...])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.pcall(f[,args...])</span></tt></p>
<p>Invokes f via xpcall, using an error function that attaches <p>Invokes f via xpcall, using an error function that attaches
a stack trace to the error. The same function is used by SafeCall a stack trace to the error. The same function is used by SafeCall
in C++, and dfhack.safecall.</p> in C++, and dfhack.safecall.</p>
<p>The returned error is a table with separate <tt class="docutils literal">message</tt> and
<tt class="docutils literal">stacktrace</tt> string fields; it implements <tt class="docutils literal">__tostring</tt>.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal"><span class="pre">safecall(f[,args...])</span></tt>, <tt class="docutils literal"><span class="pre">dfhack.safecall(f[,args...])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">safecall(f[,args...])</span></tt>, <tt class="docutils literal"><span class="pre">dfhack.safecall(f[,args...])</span></tt></p>
<p>Just like pcall, but also prints the error using printerr before <p>Just like pcall, but also prints the error using printerr before
@ -766,6 +804,41 @@ returning. Intended as a convenience function.</p>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.saferesume(coroutine[,args...])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.saferesume(coroutine[,args...])</span></tt></p>
<p>Compares to coroutine.resume like dfhack.safecall vs pcall.</p> <p>Compares to coroutine.resume like dfhack.safecall vs pcall.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.exception</tt></p>
<p>Metatable of error objects used by dfhack. The objects have the
following properties:</p>
<dl class="docutils">
<dt><tt class="docutils literal">err.where</tt></dt>
<dd><p class="first last">The location prefix string, or <em>nil</em>.</p>
</dd>
<dt><tt class="docutils literal">err.message</tt></dt>
<dd><p class="first last">The base message string.</p>
</dd>
<dt><tt class="docutils literal">err.stacktrace</tt></dt>
<dd><p class="first last">The stack trace string, or <em>nil</em>.</p>
</dd>
<dt><tt class="docutils literal">err.cause</tt></dt>
<dd><p class="first last">A different exception object, or <em>nil</em>.</p>
</dd>
<dt><tt class="docutils literal">err.thread</tt></dt>
<dd><p class="first last">The coroutine that has thrown the exception.</p>
</dd>
<dt><tt class="docutils literal">err.verbose</tt></dt>
<dd><p class="first last">Boolean, or <em>nil</em>; specifies if where and stacktrace should be printed.</p>
</dd>
<dt><tt class="docutils literal">tostring(err)</tt>, or <tt class="docutils literal"><span class="pre">err:tostring([verbose])</span></tt></dt>
<dd><p class="first last">Converts the exception to string.</p>
</dd>
</dl>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.exception.verbose</tt></p>
<p>The default value of the <tt class="docutils literal">verbose</tt> argument of <tt class="docutils literal">err:tostring()</tt>.</p>
</li>
</ul>
</div>
<div class="section" id="locking-and-finalization">
<h3><a class="toc-backref" href="#id14">Locking and finalization</a></h3>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_suspend(f[,args...])</span></tt></p> <li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.with_suspend(f[,args...])</span></tt></p>
<p>Calls <tt class="docutils literal">f</tt> with arguments after grabbing the DF core suspend lock. <p>Calls <tt class="docutils literal">f</tt> with arguments after grabbing the DF core suspend lock.
Suspending is necessary for accessing a consistent state of DF memory.</p> Suspending is necessary for accessing a consistent state of DF memory.</p>
@ -795,8 +868,9 @@ Implemented using <tt class="docutils literal"><span class="pre">call_with_final
<p>Calls <tt class="docutils literal"><span class="pre">fn(obj,args...)</span></tt>, then finalizes with <tt class="docutils literal">obj:delete()</tt>.</p> <p>Calls <tt class="docutils literal"><span class="pre">fn(obj,args...)</span></tt>, then finalizes with <tt class="docutils literal">obj:delete()</tt>.</p>
</li> </li>
</ul> </ul>
</div>
<div class="section" id="persistent-configuration-storage"> <div class="section" id="persistent-configuration-storage">
<h2><a class="toc-backref" href="#id11">Persistent configuration storage</a></h2> <h3><a class="toc-backref" href="#id15">Persistent configuration storage</a></h3>
<p>This api is intended for storing configuration options in the world itself. <p>This api is intended for storing configuration options in the world itself.
It probably should be restricted to data that is world-dependent.</p> It probably should be restricted to data that is world-dependent.</p>
<p>Entries are identified by a string <tt class="docutils literal">key</tt>, but it is also possible to manage <p>Entries are identified by a string <tt class="docutils literal">key</tt>, but it is also possible to manage
@ -831,7 +905,7 @@ functions can just copy values in memory without doing any actual I/O.
However, currently every entry has a 180+-byte dead-weight overhead.</p> However, currently every entry has a 180+-byte dead-weight overhead.</p>
</div> </div>
<div class="section" id="material-info-lookup"> <div class="section" id="material-info-lookup">
<h2><a class="toc-backref" href="#id12">Material info lookup</a></h2> <h3><a class="toc-backref" href="#id16">Material info lookup</a></h3>
<p>A material info record has fields:</p> <p>A material info record has fields:</p>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">type</tt>, <tt class="docutils literal">index</tt>, <tt class="docutils literal">material</tt></p> <li><p class="first"><tt class="docutils literal">type</tt>, <tt class="docutils literal">index</tt>, <tt class="docutils literal">material</tt></p>
@ -874,8 +948,9 @@ Accept dfhack_material_category auto-assign table.</p>
</li> </li>
</ul> </ul>
</div> </div>
</div>
<div class="section" id="c-function-wrappers"> <div class="section" id="c-function-wrappers">
<h2><a class="toc-backref" href="#id13">C++ function wrappers</a></h2> <h2><a class="toc-backref" href="#id17">C++ function wrappers</a></h2>
<p>Thin wrappers around C++ functions, similar to the ones for virtual methods. <p>Thin wrappers around C++ functions, similar to the ones for virtual methods.
One notable difference is that these explicit wrappers allow argument count One notable difference is that these explicit wrappers allow argument count
adjustment according to the usual lua rules, so trailing false/nil arguments adjustment according to the usual lua rules, so trailing false/nil arguments
@ -904,7 +979,7 @@ can be omitted.</p>
</li> </li>
</ul> </ul>
<div class="section" id="gui-module"> <div class="section" id="gui-module">
<h3><a class="toc-backref" href="#id14">Gui module</a></h3> <h3><a class="toc-backref" href="#id18">Gui module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.gui.getCurViewscreen()</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.gui.getCurViewscreen()</tt></p>
<p>Returns the viewscreen that is current in the core.</p> <p>Returns the viewscreen that is current in the core.</p>
@ -940,7 +1015,7 @@ The is_bright boolean actually seems to invert the brightness.</p>
</ul> </ul>
</div> </div>
<div class="section" id="job-module"> <div class="section" id="job-module">
<h3><a class="toc-backref" href="#id15">Job module</a></h3> <h3><a class="toc-backref" href="#id19">Job module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.job.cloneJobStruct(job)</tt></p>
<p>Creates a deep copy of the given job.</p> <p>Creates a deep copy of the given job.</p>
@ -977,7 +1052,7 @@ a lua list containing them.</p>
</ul> </ul>
</div> </div>
<div class="section" id="units-module"> <div class="section" id="units-module">
<h3><a class="toc-backref" href="#id16">Units module</a></h3> <h3><a class="toc-backref" href="#id20">Units module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.units.getPosition(unit)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.units.getPosition(unit)</tt></p>
<p>Returns true <em>x,y,z</em> of the unit, or <em>nil</em> if invalid; may be not equal to unit.pos if caged.</p> <p>Returns true <em>x,y,z</em> of the unit, or <em>nil</em> if invalid; may be not equal to unit.pos if caged.</p>
@ -1031,7 +1106,7 @@ or raws. The <tt class="docutils literal">ignore_noble</tt> boolean disables the
</ul> </ul>
</div> </div>
<div class="section" id="items-module"> <div class="section" id="items-module">
<h3><a class="toc-backref" href="#id17">Items module</a></h3> <h3><a class="toc-backref" href="#id21">Items module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.items.getPosition(item)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.items.getPosition(item)</tt></p>
<p>Returns true <em>x,y,z</em> of the item, or <em>nil</em> if invalid; may be not equal to item.pos if in inventory.</p> <p>Returns true <em>x,y,z</em> of the item, or <em>nil</em> if invalid; may be not equal to item.pos if in inventory.</p>
@ -1074,7 +1149,7 @@ Returns <em>false</em> in case of error.</p>
</ul> </ul>
</div> </div>
<div class="section" id="maps-module"> <div class="section" id="maps-module">
<h3><a class="toc-backref" href="#id18">Maps module</a></h3> <h3><a class="toc-backref" href="#id22">Maps module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.maps.getSize()</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.maps.getSize()</tt></p>
<p>Returns map size in blocks: <em>x, y, z</em></p> <p>Returns map size in blocks: <em>x, y, z</em></p>
@ -1115,7 +1190,7 @@ burrows, or the presence of invaders.</p>
</ul> </ul>
</div> </div>
<div class="section" id="burrows-module"> <div class="section" id="burrows-module">
<h3><a class="toc-backref" href="#id19">Burrows module</a></h3> <h3><a class="toc-backref" href="#id23">Burrows module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.burrows.findByName(name)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.burrows.findByName(name)</tt></p>
<p>Returns the burrow pointer or <em>nil</em>.</p> <p>Returns the burrow pointer or <em>nil</em>.</p>
@ -1150,7 +1225,7 @@ burrows, or the presence of invaders.</p>
</ul> </ul>
</div> </div>
<div class="section" id="buildings-module"> <div class="section" id="buildings-module">
<h3><a class="toc-backref" href="#id20">Buildings module</a></h3> <h3><a class="toc-backref" href="#id24">Buildings module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.buildings.getSize(building)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.buildings.getSize(building)</tt></p>
<p>Returns <em>width, height, centerx, centery</em>.</p> <p>Returns <em>width, height, centerx, centery</em>.</p>
@ -1290,7 +1365,7 @@ can be determined this way, <tt class="docutils literal">constructBuilding</tt>
</ul> </ul>
</div> </div>
<div class="section" id="constructions-module"> <div class="section" id="constructions-module">
<h3><a class="toc-backref" href="#id21">Constructions module</a></h3> <h3><a class="toc-backref" href="#id25">Constructions module</a></h3>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.constructions.designateNew(pos,type,item_type,mat_index)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.constructions.designateNew(pos,type,item_type,mat_index)</tt></p>
<p>Designates a new construction at given position. If there already is <p>Designates a new construction at given position. If there already is
@ -1306,27 +1381,50 @@ Returns <em>true, was_only_planned</em> if removed; or <em>false</em> if none fo
</ul> </ul>
</div> </div>
<div class="section" id="internal-api"> <div class="section" id="internal-api">
<h3><a class="toc-backref" href="#id22">Internal API</a></h3> <h3><a class="toc-backref" href="#id26">Internal API</a></h3>
<p>These functions are intended for the use by dfhack developers, <p>These functions are intended for the use by dfhack developers,
and are only documented here for completeness:</p> and are only documented here for completeness:</p>
<ul> <ul>
<li><p class="first"><tt class="docutils literal">dfhack.internal.scripts</tt></p>
<p>The table used by <tt class="docutils literal">dfhack.run_script()</tt> to give every script its own
global environment, persistent between calls to the script.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.getAddress(name)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.internal.getAddress(name)</tt></p>
<p>Returns the global address <tt class="docutils literal">name</tt>, or <em>nil</em>.</p> <p>Returns the global address <tt class="docutils literal">name</tt>, or <em>nil</em>.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.setAddress(name, value)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.internal.setAddress(name, value)</tt></p>
<p>Sets the global address <tt class="docutils literal">name</tt>. Returns the value of <tt class="docutils literal">getAddress</tt> before the change.</p> <p>Sets the global address <tt class="docutils literal">name</tt>. Returns the value of <tt class="docutils literal">getAddress</tt> before the change.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.getBase()</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.internal.getVTable(name)</tt></p>
<p>Returns the base address of the process.</p> <p>Returns the pre-extracted vtable address <tt class="docutils literal">name</tt>, or <em>nil</em>.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.getRebaseDelta()</tt></p>
<p>Returns the ASLR rebase offset of the DF executable.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.getMemRanges()</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.internal.getMemRanges()</tt></p>
<p>Returns a sequence of tables describing virtual memory ranges of the process.</p> <p>Returns a sequence of tables describing virtual memory ranges of the process.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.memmove(dest,src,count)</tt></p>
<p>Wraps the standard memmove function. Accepts both numbers and refs as pointers.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.memcmp(ptr1,ptr2,count)</tt></p>
<p>Wraps the standard memcmp function.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.memscan(haystack,count,step,needle,nsize)</tt></p>
<p>Searches for <tt class="docutils literal">needle</tt> of <tt class="docutils literal">nsize</tt> bytes in <tt class="docutils literal">haystack</tt>,
using <tt class="docutils literal">count</tt> steps of <tt class="docutils literal">step</tt> bytes.
Returns: <em>step_idx, sum_idx, found_ptr</em>, or <em>nil</em> if not found.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.diffscan(old_data, new_data, start_idx, end_idx, eltsize[, oldval, newval, delta])</tt></p>
<p>Searches for differences between buffers at ptr1 and ptr2, as integers of size eltsize.
The oldval, newval or delta arguments may be used to specify additional constraints.
Returns: <em>found_index</em>, or <em>nil</em> if end reached.</p>
</li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="section" id="core-interpreter-context"> <div class="section" id="core-interpreter-context">
<h2><a class="toc-backref" href="#id23">Core interpreter context</a></h2> <h2><a class="toc-backref" href="#id27">Core interpreter context</a></h2>
<p>While plugins can create any number of interpreter instances, <p>While plugins can create any number of interpreter instances,
there is one special context managed by dfhack core. It is the there is one special context managed by dfhack core. It is the
only context that can receive events from DF and plugins.</p> only context that can receive events from DF and plugins.</p>
@ -1357,7 +1455,7 @@ Using <tt class="docutils literal">timeout_active(id,nil)</tt> cancels the timer
</li> </li>
</ul> </ul>
<div class="section" id="event-type"> <div class="section" id="event-type">
<h3><a class="toc-backref" href="#id24">Event type</a></h3> <h3><a class="toc-backref" href="#id28">Event type</a></h3>
<p>An event is just a lua table with a predefined metatable that <p>An event is just a lua table with a predefined metatable that
contains a __call metamethod. When it is invoked, it loops contains a __call metamethod. When it is invoked, it loops
through the table with next and calls all contained values. through the table with next and calls all contained values.
@ -1382,15 +1480,45 @@ order using <tt class="docutils literal">dfhack.safecall</tt>.</p>
</div> </div>
</div> </div>
</div> </div>
<div class="section" id="modules">
<h1><a class="toc-backref" href="#id29">Modules</a></h1>
<p>DFHack sets up the lua interpreter so that the built-in <tt class="docutils literal">require</tt>
function can be used to load shared lua code from hack/lua/.
The <tt class="docutils literal">dfhack</tt> namespace reference itself may be obtained via
<tt class="docutils literal"><span class="pre">require('dfhack')</span></tt>, although it is initially created as a
global by C++ bootstrap code.</p>
<p>The following functions are provided:</p>
<ul>
<li><p class="first"><tt class="docutils literal">mkmodule(name)</tt></p>
<p>Creates an environment table for the module. Intended to be used as:</p>
<pre class="literal-block">
local _ENV = mkmodule('foo')
...
return _ENV
</pre>
<p>If called the second time, returns the same table; thus providing reload support.</p>
</li>
<li><p class="first"><tt class="docutils literal">reload(name)</tt></p>
<p>Reloads a previously <tt class="docutils literal">require</tt>-d module <em>&quot;name&quot;</em> from the file.
Intended as a help for module development.</p>
</li>
<li><p class="first"><tt class="docutils literal">dfhack.BASE_G</tt></p>
<p>This variable contains the root global environment table, which is
used as a base for all module and script environments. Its contents
should be kept limited to the standard Lua library and API described
in this document.</p>
</li>
</ul>
</div>
<div class="section" id="plugins"> <div class="section" id="plugins">
<h1><a class="toc-backref" href="#id25">Plugins</a></h1> <h1><a class="toc-backref" href="#id30">Plugins</a></h1>
<p>DFHack plugins may export native functions and events <p>DFHack plugins may export native functions and events
to lua contexts. They are automatically imported by to lua contexts. They are automatically imported by
<tt class="docutils literal"><span class="pre">mkmodule('plugins.&lt;name&gt;')</span></tt>; this means that a lua <tt class="docutils literal"><span class="pre">mkmodule('plugins.&lt;name&gt;')</span></tt>; this means that a lua
module file is still necessary for <tt class="docutils literal">require</tt> to read.</p> module file is still necessary for <tt class="docutils literal">require</tt> to read.</p>
<p>The following plugins have lua support.</p> <p>The following plugins have lua support.</p>
<div class="section" id="burrows"> <div class="section" id="burrows">
<h2><a class="toc-backref" href="#id26">burrows</a></h2> <h2><a class="toc-backref" href="#id31">burrows</a></h2>
<p>Implements extended burrow manipulations.</p> <p>Implements extended burrow manipulations.</p>
<p>Events:</p> <p>Events:</p>
<ul> <ul>
@ -1428,11 +1556,41 @@ set is the same as used by the command line.</p>
<p>The lua module file also re-exports functions from <tt class="docutils literal">dfhack.burrows</tt>.</p> <p>The lua module file also re-exports functions from <tt class="docutils literal">dfhack.burrows</tt>.</p>
</div> </div>
<div class="section" id="sort"> <div class="section" id="sort">
<h2><a class="toc-backref" href="#id27">sort</a></h2> <h2><a class="toc-backref" href="#id32">sort</a></h2>
<p>Does not export any native functions as of now. Instead, it <p>Does not export any native functions as of now. Instead, it
calls lua code to perform the actual ordering of list items.</p> calls lua code to perform the actual ordering of list items.</p>
</div> </div>
</div> </div>
<div class="section" id="scripts">
<h1><a class="toc-backref" href="#id33">Scripts</a></h1>
<p>Any files with the .lua extension placed into hack/scripts/*
are automatically used by the DFHack core as commands. The
matching command name consists of the name of the file sans
the extension.</p>
<p><strong>NOTE:</strong> Scripts placed in subdirectories still can be accessed, but
do not clutter the <tt class="docutils literal">ls</tt> command list; thus it is preferred
for obscure developer-oriented scripts and scripts used by tools.
When calling such scripts, always use '/' as the separator for
directories, e.g. <tt class="docutils literal"><span class="pre">devel/lua-example</span></tt>.</p>
<p>Scripts are re-read from disk every time they are used
(this may be changed later to check the file change time); however
the global variable values persist in memory between calls.
Every script gets its own separate environment for global
variables.</p>
<p>Arguments are passed in to the scripts via the <strong>...</strong> built-in
quasi-variable; when the script is called by the DFHack core,
they are all guaranteed to be non-nil strings.</p>
<p>DFHack core invokes the scripts in the <em>core context</em> (see above);
however it is possible to call them from any lua code (including
from other scripts) in any context, via the same function the core uses:</p>
<ul>
<li><p class="first"><tt class="docutils literal"><span class="pre">dfhack.run_script(name[,args...])</span></tt></p>
<p>Run a lua script in hack/scripts/, as if it was started from dfhack command-line.
The <tt class="docutils literal">name</tt> argument should be the name stem, as would be used on the command line.</p>
</li>
</ul>
<p>Note that this function lets errors propagate to the caller.</p>
</div>
</div> </div>
</body> </body>
</html> </html>

@ -1350,7 +1350,12 @@ produce undesirable results. There are a few good ones though.</p>
<p class="last">You are in fort game mode, managing your fortress and paused. <p class="last">You are in fort game mode, managing your fortress and paused.
You switch to the arena game mode, <em>assume control of a creature</em> and then You switch to the arena game mode, <em>assume control of a creature</em> and then
switch to adventure game mode(1). switch to adventure game mode(1).
You just lost a fortress and gained an adventurer.</p> You just lost a fortress and gained an adventurer.
You could also do this.
You are in fort game mode, managing your fortress and paused at the esc menu.
You switch to the adventure game mode, then use Dfusion to <em>assume control of a creature</em> and then
save or retire.
You just created a returnable mountain home and gained an adventurer.</p>
</div> </div>
<p>I take no responsibility of anything that happens as a result of using this tool</p> <p>I take no responsibility of anything that happens as a result of using this tool</p>
</div> </div>

@ -1,5 +1,5 @@
/* /*
** $Id: lfunc.h,v 2.6 2010/06/04 13:06:15 roberto Exp $ ** $Id: lfunc.h,v 2.8 2012/05/08 13:53:33 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures ** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -20,12 +20,11 @@
LUAI_FUNC Proto *luaF_newproto (lua_State *L); LUAI_FUNC Proto *luaF_newproto (lua_State *L);
LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems); LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems);
LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, Proto *p); LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems);
LUAI_FUNC UpVal *luaF_newupval (lua_State *L); LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level); LUAI_FUNC void luaF_close (lua_State *L, StkId level);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
int pc); int pc);

@ -1,5 +1,5 @@
/* /*
** $Id: lgc.h,v 2.52 2011/10/03 17:54:25 roberto Exp $ ** $Id: lgc.h,v 2.56 2012/05/23 15:43:14 roberto Exp $
** Garbage Collector ** Garbage Collector
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -25,6 +25,14 @@
*/ */
/* how much to allocate before next GC step */
#if !defined(GCSTEPSIZE)
/* ~100 small strings */
#define GCSTEPSIZE (cast_int(100 * sizeof(TString)))
#endif
/* /*
** Possible states of the Garbage Collector ** Possible states of the Garbage Collector
*/ */

@ -1,5 +1,5 @@
/* /*
** $Id: llimits.h,v 1.95 2011/12/06 16:58:36 roberto Exp $ ** $Id: llimits.h,v 1.99 2012/05/28 20:32:28 roberto Exp $
** Limits, basic types, and some other `installation-dependent' definitions ** Limits, basic types, and some other `installation-dependent' definitions
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -31,6 +31,8 @@ typedef unsigned char lu_byte;
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) #define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
#define MAX_LMEM ((l_mem) ((MAX_LUMEM >> 1) - 2))
#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ #define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
@ -209,31 +211,36 @@ typedef lu_int32 Instruction;
#elif defined(LUA_IEEE754TRICK) /* }{ */ #elif defined(LUA_IEEE754TRICK) /* }{ */
/* the next trick should work on any machine using IEEE754 with /* the next trick should work on any machine using IEEE754 with
a 32-bit integer type */ a 32-bit int type */
union luai_Cast { double l_d; LUA_INT32 l_p[2]; }; union luai_Cast { double l_d; LUA_INT32 l_p[2]; };
#if !defined(LUA_IEEEENDIAN) /* { */ #if !defined(LUA_IEEEENDIAN) /* { */
#define LUAI_EXTRAIEEE \ #define LUAI_EXTRAIEEE \
static const union luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)}; static const union luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)};
#define LUA_IEEEENDIAN (ieeeendian.l_p[1] == 33) #define LUA_IEEEENDIANLOC (ieeeendian.l_p[1] == 33)
#else #else
#define LUA_IEEEENDIANLOC LUA_IEEEENDIAN
#define LUAI_EXTRAIEEE /* empty */ #define LUAI_EXTRAIEEE /* empty */
#endif /* } */ #endif /* } */
#define lua_number2int32(i,n,t) \ #define lua_number2int32(i,n,t) \
{ LUAI_EXTRAIEEE \ { LUAI_EXTRAIEEE \
volatile union luai_Cast u; u.l_d = (n) + 6755399441055744.0; \ volatile union luai_Cast u; u.l_d = (n) + 6755399441055744.0; \
(i) = (t)u.l_p[LUA_IEEEENDIAN]; } (i) = (t)u.l_p[LUA_IEEEENDIANLOC]; }
#define luai_hashnum(i,n) \ #define luai_hashnum(i,n) \
{ volatile union luai_Cast u; u.l_d = (n) + 1.0; /* avoid -0 */ \ { volatile union luai_Cast u; u.l_d = (n) + 1.0; /* avoid -0 */ \
(i) = u.l_p[0]; (i) += u.l_p[1]; } /* add double bits for his hash */ (i) = u.l_p[0]; (i) += u.l_p[1]; } /* add double bits for his hash */
#define lua_number2int(i,n) lua_number2int32(i, n, int) #define lua_number2int(i,n) lua_number2int32(i, n, int)
#define lua_number2integer(i,n) lua_number2int32(i, n, lua_Integer)
#define lua_number2unsigned(i,n) lua_number2int32(i, n, lua_Unsigned) #define lua_number2unsigned(i,n) lua_number2int32(i, n, lua_Unsigned)
/* the trick can be expanded to lua_Integer when it is a 32-bit value */
#if defined(LUA_IEEELL)
#define lua_number2integer(i,n) lua_number2int32(i, n, lua_Integer)
#endif
#endif /* } */ #endif /* } */

@ -1,5 +1,5 @@
/* /*
** $Id: lobject.h,v 2.64 2011/10/31 17:48:22 roberto Exp $ ** $Id: lobject.h,v 2.70 2012/05/11 14:10:50 roberto Exp $
** Type definitions for Lua objects ** Type definitions for Lua objects
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -36,6 +36,9 @@
** bit 6: whether value is collectable ** bit 6: whether value is collectable
*/ */
#define VARBITS (3 << 4)
/* /*
** LUA_TFUNCTION variants: ** LUA_TFUNCTION variants:
** 0 - Lua function ** 0 - Lua function
@ -49,6 +52,12 @@
#define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */ #define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */
/*
** LUA_TSTRING variants */
#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4)) /* short strings */
#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) /* long strings */
/* Bit mark for collectable types */ /* Bit mark for collectable types */
#define BIT_ISCOLLECTABLE (1 << 6) #define BIT_ISCOLLECTABLE (1 << 6)
@ -109,23 +118,28 @@ typedef struct lua_TValue TValue;
/* raw type tag of a TValue */ /* raw type tag of a TValue */
#define rttype(o) ((o)->tt_) #define rttype(o) ((o)->tt_)
/* tag with no variants (bits 0-3) */
#define novariant(x) ((x) & 0x0F)
/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ /* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
#define ttype(o) (rttype(o) & 0x3F) #define ttype(o) (rttype(o) & 0x3F)
/* type tag of a TValue with no variants (bits 0-3) */ /* type tag of a TValue with no variants (bits 0-3) */
#define ttypenv(o) (rttype(o) & 0x0F) #define ttypenv(o) (novariant(rttype(o)))
/* Macros to test type */ /* Macros to test type */
#define checktag(o,t) (rttype(o) == (t)) #define checktag(o,t) (rttype(o) == (t))
#define checktype(o,t) (ttypenv(o) == (t))
#define ttisnumber(o) checktag((o), LUA_TNUMBER) #define ttisnumber(o) checktag((o), LUA_TNUMBER)
#define ttisnil(o) checktag((o), LUA_TNIL) #define ttisnil(o) checktag((o), LUA_TNIL)
#define ttisboolean(o) checktag((o), LUA_TBOOLEAN) #define ttisboolean(o) checktag((o), LUA_TBOOLEAN)
#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA) #define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)
#define ttisstring(o) checktag((o), ctb(LUA_TSTRING)) #define ttisstring(o) checktype((o), LUA_TSTRING)
#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))
#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))
#define ttistable(o) checktag((o), ctb(LUA_TTABLE)) #define ttistable(o) checktag((o), ctb(LUA_TTABLE))
#define ttisfunction(o) (ttypenv(o) == LUA_TFUNCTION) #define ttisfunction(o) checktype(o, LUA_TFUNCTION)
#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION) #define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION)
#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL)) #define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))
#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL)) #define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))
@ -161,7 +175,7 @@ typedef struct lua_TValue TValue;
/* Macros for internal tests */ /* Macros for internal tests */
#define righttt(obj) (ttypenv(obj) == gcvalue(obj)->gch.tt) #define righttt(obj) (ttype(obj) == gcvalue(obj)->gch.tt)
#define checkliveness(g,obj) \ #define checkliveness(g,obj) \
lua_longassert(!iscollectable(obj) || \ lua_longassert(!iscollectable(obj) || \
@ -193,7 +207,8 @@ typedef struct lua_TValue TValue;
#define setsvalue(L,obj,x) \ #define setsvalue(L,obj,x) \
{ TValue *io=(obj); \ { TValue *io=(obj); \
val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TSTRING)); \ TString *x_ = (x); \
val_(io).gc=cast(GCObject *, x_); settt_(io, ctb(x_->tsv.tt)); \
checkliveness(G(L),io); } checkliveness(G(L),io); }
#define setuvalue(L,obj,x) \ #define setuvalue(L,obj,x) \
@ -221,11 +236,6 @@ typedef struct lua_TValue TValue;
val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTABLE)); \ val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TTABLE)); \
checkliveness(G(L),io); } checkliveness(G(L),io); }
#define setptvalue(L,obj,x) \
{ TValue *io=(obj); \
val_(io).gc=cast(GCObject *, (x)); settt_(io, ctb(LUA_TPROTO)); \
checkliveness(G(L),io); }
#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY) #define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY)
@ -256,6 +266,8 @@ typedef struct lua_TValue TValue;
#define setsvalue2n setsvalue #define setsvalue2n setsvalue
/* check whether a number is valid (useful only for NaN trick) */
#define luai_checknum(L,o,c) { /* empty */ }
/* /*
@ -263,10 +275,7 @@ typedef struct lua_TValue TValue;
** NaN Trick ** NaN Trick
** ======================================================= ** =======================================================
*/ */
#if defined(LUA_NANTRICK)
#if defined(LUA_NANTRICK) \
|| defined(LUA_NANTRICK_LE) \
|| defined(LUA_NANTRICK_BE)
/* /*
** numbers are represented in the 'd_' field. All other values have the ** numbers are represented in the 'd_' field. All other values have the
@ -274,15 +283,23 @@ typedef struct lua_TValue TValue;
** a "signaled NaN", which is never generated by regular operations by ** a "signaled NaN", which is never generated by regular operations by
** the CPU (nor by 'strtod') ** the CPU (nor by 'strtod')
*/ */
#if !defined(NNMARK)
/* allows for external implementation for part of the trick */
#if !defined(NNMARK) /* { */
#if !defined(LUA_IEEEENDIAN)
#error option 'LUA_NANTRICK' needs 'LUA_IEEEENDIAN'
#endif
#define NNMARK 0x7FF7A500 #define NNMARK 0x7FF7A500
#define NNMASK 0x7FFFFF00 #define NNMASK 0x7FFFFF00
#endif
#undef TValuefields #undef TValuefields
#undef NILCONSTANT #undef NILCONSTANT
#if defined(LUA_NANTRICK_LE) #if (LUA_IEEEENDIAN == 0) /* { */
/* little endian */ /* little endian */
#define TValuefields \ #define TValuefields \
@ -293,7 +310,7 @@ typedef struct lua_TValue TValue;
#define d_(o) ((o)->u.d__) #define d_(o) ((o)->u.d__)
#define tt_(o) ((o)->u.i.tt__) #define tt_(o) ((o)->u.i.tt__)
#elif defined(LUA_NANTRICK_BE) #else /* }{ */
/* big endian */ /* big endian */
#define TValuefields \ #define TValuefields \
@ -304,10 +321,9 @@ typedef struct lua_TValue TValue;
#define d_(o) ((o)->u.d__) #define d_(o) ((o)->u.d__)
#define tt_(o) ((o)->u.i.tt__) #define tt_(o) ((o)->u.i.tt__)
#elif !defined(TValuefields) #endif /* } */
#error option 'LUA_NANTRICK' needs declaration for 'TValuefields'
#endif #endif /* } */
/* correspondence with standard representation */ /* correspondence with standard representation */
@ -348,21 +364,18 @@ typedef struct lua_TValue TValue;
*/ */
#undef checktag #undef checktag
#undef checktype
#define checktag(o,t) (tt_(o) == tag2tt(t)) #define checktag(o,t) (tt_(o) == tag2tt(t))
#define checktype(o,t) (ctb(tt_(o) | VARBITS) == ctb(tag2tt(t) | VARBITS))
#undef ttisequal #undef ttisequal
#define ttisequal(o1,o2) \ #define ttisequal(o1,o2) \
(ttisnumber(o1) ? ttisnumber(o2) : (tt_(o1) == tt_(o2))) (ttisnumber(o1) ? ttisnumber(o2) : (tt_(o1) == tt_(o2)))
#undef luai_checknum
#define luai_checknum(L,o,c) { if (!ttisnumber(o)) c; } #define luai_checknum(L,o,c) { if (!ttisnumber(o)) c; }
#else
#define luai_checknum(L,o,c) { /* empty */ }
#endif #endif
/* }====================================================== */ /* }====================================================== */
@ -401,7 +414,7 @@ typedef union TString {
L_Umaxalign dummy; /* ensures maximum alignment for strings */ L_Umaxalign dummy; /* ensures maximum alignment for strings */
struct { struct {
CommonHeader; CommonHeader;
lu_byte reserved; lu_byte extra; /* reserved words for short strings; "has hash" for longs */
unsigned int hash; unsigned int hash;
size_t len; /* number of characters in string */ size_t len; /* number of characters in string */
} tsv; } tsv;
@ -501,7 +514,7 @@ typedef struct UpVal {
*/ */
#define ClosureHeader \ #define ClosureHeader \
CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist CommonHeader; lu_byte nupvalues; GCObject *gclist
typedef struct CClosure { typedef struct CClosure {
ClosureHeader; ClosureHeader;

@ -1,5 +1,5 @@
/* /*
** $Id: lparser.h,v 1.69 2011/07/27 18:09:01 roberto Exp $ ** $Id: lparser.h,v 1.70 2012/05/08 13:53:33 roberto Exp $
** Lua Parser ** Lua Parser
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -112,7 +112,7 @@ typedef struct FuncState {
} FuncState; } FuncState;
LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, LUAI_FUNC Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar); Dyndata *dyd, const char *name, int firstchar);

@ -1,5 +1,5 @@
/* /*
** $Id: lstate.h,v 2.74 2011/09/30 12:45:07 roberto Exp $ ** $Id: lstate.h,v 2.81 2012/06/08 15:14:04 roberto Exp $
** Global State ** Global State
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -72,6 +72,7 @@ typedef struct CallInfo {
struct CallInfo *previous, *next; /* dynamic call link */ struct CallInfo *previous, *next; /* dynamic call link */
short nresults; /* expected number of results from this function */ short nresults; /* expected number of results from this function */
lu_byte callstatus; lu_byte callstatus;
ptrdiff_t extra;
union { union {
struct { /* only for Lua functions */ struct { /* only for Lua functions */
StkId base; /* base for this function */ StkId base; /* base for this function */
@ -81,7 +82,6 @@ typedef struct CallInfo {
int ctx; /* context info. in case of yields */ int ctx; /* context info. in case of yields */
lua_CFunction k; /* continuation in case of yields */ lua_CFunction k; /* continuation in case of yields */
ptrdiff_t old_errfunc; ptrdiff_t old_errfunc;
ptrdiff_t extra;
lu_byte old_allowhook; lu_byte old_allowhook;
lu_byte status; lu_byte status;
} c; } c;
@ -100,6 +100,7 @@ typedef struct CallInfo {
#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
#define CIST_STAT (1<<5) /* call has an error status (pcall) */ #define CIST_STAT (1<<5) /* call has an error status (pcall) */
#define CIST_TAIL (1<<6) /* call was tail called */ #define CIST_TAIL (1<<6) /* call was tail called */
#define CIST_HOOKYIELD (1<<7) /* last hook called yielded */
#define isLua(ci) ((ci)->callstatus & CIST_LUA) #define isLua(ci) ((ci)->callstatus & CIST_LUA)
@ -113,9 +114,11 @@ typedef struct global_State {
void *ud; /* auxiliary data to `frealloc' */ void *ud; /* auxiliary data to `frealloc' */
lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */ lu_mem totalbytes; /* number of bytes currently allocated - GCdebt */
l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
lu_mem lastmajormem; /* memory in use after last major collection */ lu_mem GCmemtrav; /* memory traversed by the GC */
lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
stringtable strt; /* hash table for strings */ stringtable strt; /* hash table for strings */
TValue l_registry; TValue l_registry;
unsigned int seed; /* randomized seed for hashes */
lu_byte currentwhite; lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */ lu_byte gcstate; /* state of garbage collector */
lu_byte gckind; /* kind of GC running */ lu_byte gckind; /* kind of GC running */
@ -123,7 +126,8 @@ typedef struct global_State {
int sweepstrgc; /* position of sweep in `strt' */ int sweepstrgc; /* position of sweep in `strt' */
GCObject *allgc; /* list of all collectable objects */ GCObject *allgc; /* list of all collectable objects */
GCObject *finobj; /* list of collectable objects with finalizers */ GCObject *finobj; /* list of collectable objects with finalizers */
GCObject **sweepgc; /* current position of sweep */ GCObject **sweepgc; /* current position of sweep in list 'allgc' */
GCObject **sweepfin; /* current position of sweep in list 'finobj' */
GCObject *gray; /* list of gray objects */ GCObject *gray; /* list of gray objects */
GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *grayagain; /* list of objects to be traversed atomically */
GCObject *weak; /* list of tables with weak values */ GCObject *weak; /* list of tables with weak values */
@ -193,11 +197,15 @@ union GCObject {
#define gch(o) (&(o)->gch) #define gch(o) (&(o)->gch)
/* macros to convert a GCObject into a specific value */ /* macros to convert a GCObject into a specific value */
#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) #define rawgco2ts(o) \
check_exp(novariant((o)->gch.tt) == LUA_TSTRING, &((o)->ts))
#define gco2ts(o) (&rawgco2ts(o)->tsv) #define gco2ts(o) (&rawgco2ts(o)->tsv)
#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) #define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
#define gco2u(o) (&rawgco2u(o)->uv) #define gco2u(o) (&rawgco2u(o)->uv)
#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) #define gco2lcl(o) check_exp((o)->gch.tt == LUA_TLCL, &((o)->cl.l))
#define gco2ccl(o) check_exp((o)->gch.tt == LUA_TCCL, &((o)->cl.c))
#define gco2cl(o) \
check_exp(novariant((o)->gch.tt) == LUA_TFUNCTION, &((o)->cl))
#define gco2t(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) #define gco2t(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) #define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) #define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))

@ -1,5 +1,5 @@
/* /*
** $Id: lstring.h,v 1.46 2010/04/05 16:26:37 roberto Exp $ ** $Id: lstring.h,v 1.49 2012/02/01 21:57:15 roberto Exp $
** String table (keep all strings handled by Lua) ** String table (keep all strings handled by Lua)
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -23,11 +23,20 @@
/* /*
** as all string are internalized, string equality becomes ** test whether a string is a reserved word
** pointer equality
*/ */
#define eqstr(a,b) ((a) == (b)) #define isreserved(s) ((s)->tsv.tt == LUA_TSHRSTR && (s)->tsv.extra > 0)
/*
** equality for short strings, which are always internalized
*/
#define eqshrstr(a,b) check_exp((a)->tsv.tt == LUA_TSHRSTR, (a) == (b))
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
LUAI_FUNC int luaS_eqstr (TString *a, TString *b);
LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);

@ -1,5 +1,5 @@
/* /*
** $Id: lua.h,v 1.282 2011/11/29 15:55:08 roberto Exp $ ** $Id: lua.h,v 1.283 2012/04/20 13:18:26 roberto Exp $
** Lua - A Scripting Language ** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file ** See Copyright Notice at the end of this file
@ -19,11 +19,11 @@
#define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "2" #define LUA_VERSION_MINOR "2"
#define LUA_VERSION_NUM 502 #define LUA_VERSION_NUM 502
#define LUA_VERSION_RELEASE "0" #define LUA_VERSION_RELEASE "1"
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2011 Lua.org, PUC-Rio" #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2012 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@ -413,7 +413,7 @@ struct lua_Debug {
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2011 Lua.org, PUC-Rio. All rights reserved. * Copyright (C) 1994-2012 Lua.org, PUC-Rio.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the

@ -1,5 +1,5 @@
/* /*
** $Id: luaconf.h,v 1.170 2011/12/06 16:58:36 roberto Exp $ ** $Id: luaconf.h,v 1.172 2012/05/11 14:14:42 roberto Exp $
** Configuration file for Lua ** Configuration file for Lua
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -223,6 +223,13 @@
(fprintf(stderr, (s), (p)), fflush(stderr)) (fprintf(stderr, (s), (p)), fflush(stderr))
/*
@@ LUAI_MAXSHORTLEN is the maximum length for short strings, that is,
** strings that are internalized. (Cannot be smaller than reserved words
** or tags for metamethods, as these strings must be internalized;
** #("function") = 8, #("__newindex") = 10.)
*/
#define LUAI_MAXSHORTLEN 40
@ -453,66 +460,76 @@
#define LUA_UNSIGNED unsigned LUA_INT32 #define LUA_UNSIGNED unsigned LUA_INT32
#if defined(LUA_CORE) /* { */
#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */
/* On a Microsoft compiler on a Pentium, use assembler to avoid clashes /*
with a DirectX idiosyncrasy */ ** Some tricks with doubles
#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86) /* { */ */
#define MS_ASMTRICK
#else /* }{ */
/* the next definition uses a trick that should work on any machine
using IEEE754 with a 32-bit integer type */
#define LUA_IEEE754TRICK
#if defined(LUA_CORE) && \
defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */
/* /*
** The next definitions activate some tricks to speed up the
** conversion from doubles to integer types, mainly to LUA_UNSIGNED.
**
@@ MS_ASMTRICK uses Microsoft assembler to avoid clashes with a
** DirectX idiosyncrasy.
**
@@ LUA_IEEE754TRICK uses a trick that should work on any machine
** using IEEE754 with a 32-bit integer type.
**
@@ LUA_IEEELL extends the trick to LUA_INTEGER; should only be
** defined when LUA_INTEGER is a 32-bit integer.
**
@@ LUA_IEEEENDIAN is the endianness of doubles in your machine @@ LUA_IEEEENDIAN is the endianness of doubles in your machine
** (0 for little endian, 1 for big endian); if not defined, Lua will ** (0 for little endian, 1 for big endian); if not defined, Lua will
** check it dynamically. ** check it dynamically for LUA_IEEE754TRICK (but not for LUA_NANTRICK).
**
@@ LUA_NANTRICK controls the use of a trick to pack all types into
** a single double value, using NaN values to represent non-number
** values. The trick only works on 32-bit machines (ints and pointers
** are 32-bit values) with numbers represented as IEEE 754-2008 doubles
** with conventional endianess (12345678 or 87654321), in CPUs that do
** not produce signaling NaN values (all NaNs are quiet).
*/ */
/* check for known architectures */
#if defined(__i386__) || defined(__i386) || defined(__X86__) || \ /* Microsoft compiler on a Pentium (32 bit) ? */
defined (__x86_64) #if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86) /* { */
#define MS_ASMTRICK
#define LUA_IEEEENDIAN 0 #define LUA_IEEEENDIAN 0
#elif defined(__POWERPC__) || defined(__ppc__) #define LUA_NANTRICK
#define LUA_IEEEENDIAN 1
#endif
#endif /* } */
#endif /* } */ /* pentium 32 bits? */
#elif defined(__i386__) || defined(__i386) || defined(__X86__) /* }{ */
#endif /* } */ #define LUA_IEEE754TRICK
#define LUA_IEEELL
#define LUA_IEEEENDIAN 0
#define LUA_NANTRICK
/* }================================================================== */ /* pentium 64 bits? */
#elif defined(__x86_64) /* }{ */
#define LUA_IEEE754TRICK
#define LUA_IEEEENDIAN 0
/* #elif defined(__POWERPC__) || defined(__ppc__) /* }{ */
@@ LUA_NANTRICK_LE/LUA_NANTRICK_BE controls the use of a trick to
** pack all types into a single double value, using NaN values to
** represent non-number values. The trick only works on 32-bit machines
** (ints and pointers are 32-bit values) with numbers represented as
** IEEE 754-2008 doubles with conventional endianess (12345678 or
** 87654321), in CPUs that do not produce signaling NaN values (all NaNs
** are quiet).
*/
#if defined(LUA_CORE) && \
defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */
/* little-endian architectures that satisfy those conditions */ #define LUA_IEEE754TRICK
#if defined(__i386__) || defined(__i386) || defined(__X86__) || \ #define LUA_IEEEENDIAN 1
defined(_M_IX86)
#define LUA_NANTRICK_LE #else /* }{ */
#endif /* assume IEEE754 and a 32-bit integer type */
#define LUA_IEEE754TRICK
#endif /* } */
#endif /* } */ #endif /* } */
/* }================================================================== */

@ -1,5 +1,5 @@
/* /*
** $Id: lundump.h,v 1.44 2011/05/06 13:35:17 lhf Exp $ ** $Id: lundump.h,v 1.39 2012/05/08 13:53:33 roberto Exp $
** load precompiled Lua chunks ** load precompiled Lua chunks
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -11,7 +11,7 @@
#include "lzio.h" #include "lzio.h"
/* load one chunk; from lundump.c */ /* load one chunk; from lundump.c */
LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); LUAI_FUNC Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);
/* make header; from lundump.c */ /* make header; from lundump.c */
LUAI_FUNC void luaU_header (lu_byte* h); LUAI_FUNC void luaU_header (lu_byte* h);

@ -1,5 +1,5 @@
/* /*
** $Id: lapi.c,v 2.159 2011/11/30 12:32:05 roberto Exp $ ** $Id: lapi.c,v 2.164 2012/06/08 15:14:04 roberto Exp $
** Lua API ** Lua API
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -950,7 +950,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
ci->u.c.k = k; /* save continuation */ ci->u.c.k = k; /* save continuation */
ci->u.c.ctx = ctx; /* save context */ ci->u.c.ctx = ctx; /* save context */
/* save information for error recovery */ /* save information for error recovery */
ci->u.c.extra = savestack(L, c.func); ci->extra = savestack(L, c.func);
ci->u.c.old_allowhook = L->allowhook; ci->u.c.old_allowhook = L->allowhook;
ci->u.c.old_errfunc = L->errfunc; ci->u.c.old_errfunc = L->errfunc;
L->errfunc = func; L->errfunc = func;
@ -1045,17 +1045,17 @@ LUA_API int lua_gc (lua_State *L, int what, int data) {
} }
case LUA_GCSTEP: { case LUA_GCSTEP: {
if (g->gckind == KGC_GEN) { /* generational mode? */ if (g->gckind == KGC_GEN) { /* generational mode? */
res = (g->lastmajormem == 0); /* 1 if will do major collection */ res = (g->GCestimate == 0); /* true if it will do major collection */
luaC_forcestep(L); /* do a single step */ luaC_forcestep(L); /* do a single step */
} }
else { else {
while (data-- >= 0) { lu_mem debt = cast(lu_mem, data) * 1024 - GCSTEPSIZE;
if (g->gcrunning)
debt += g->GCdebt; /* include current debt */
luaE_setdebt(g, debt);
luaC_forcestep(L); luaC_forcestep(L);
if (g->gcstate == GCSpause) { /* end of cycle? */ if (g->gcstate == GCSpause) /* end of cycle? */
res = 1; /* signal it */ res = 1; /* signal it */
break;
}
}
} }
break; break;
} }

@ -1,5 +1,5 @@
/* /*
** $Id: lauxlib.c,v 1.240 2011/12/06 16:33:55 roberto Exp $ ** $Id: lauxlib.c,v 1.244 2012/05/31 20:28:45 roberto Exp $
** Auxiliary functions for building Lua libraries ** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -520,11 +520,11 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
LUALIB_API int luaL_ref (lua_State *L, int t) { LUALIB_API int luaL_ref (lua_State *L, int t) {
int ref; int ref;
t = lua_absindex(L, t);
if (lua_isnil(L, -1)) { if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* remove from stack */ lua_pop(L, 1); /* remove from stack */
return LUA_REFNIL; /* `nil' has a unique fixed reference */ return LUA_REFNIL; /* `nil' has a unique fixed reference */
} }
t = lua_absindex(L, t);
lua_rawgeti(L, t, freelist); /* get first free element */ lua_rawgeti(L, t, freelist); /* get first free element */
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
lua_pop(L, 1); /* remove it from stack */ lua_pop(L, 1); /* remove it from stack */
@ -616,8 +616,10 @@ static int skipBOM (LoadF *lf) {
static int skipcomment (LoadF *lf, int *cp) { static int skipcomment (LoadF *lf, int *cp) {
int c = *cp = skipBOM(lf); int c = *cp = skipBOM(lf);
if (c == '#') { /* first line is a comment (Unix exec. file)? */ if (c == '#') { /* first line is a comment (Unix exec. file)? */
while ((c = getc(lf->f)) != EOF && c != '\n') ; /* skip first line */ do { /* skip first line */
*cp = getc(lf->f); /* skip end-of-line */ c = getc(lf->f);
} while (c != EOF && c != '\n') ;
*cp = getc(lf->f); /* skip end-of-line, if present */
return 1; /* there was a comment */ return 1; /* there was a comment */
} }
else return 0; /* no comment */ else return 0; /* no comment */
@ -843,6 +845,7 @@ LUALIB_API void luaL_openlib (lua_State *L, const char *libname,
** Returns with only the table at the stack. ** Returns with only the table at the stack.
*/ */
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkversion(L);
luaL_checkstack(L, nup, "too many upvalues"); luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */ for (; l->name != NULL; l++) { /* fill the table with given functions */
int i; int i;
@ -889,10 +892,8 @@ LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
lua_setfield(L, -2, modname); /* _LOADED[modname] = module */ lua_setfield(L, -2, modname); /* _LOADED[modname] = module */
lua_pop(L, 1); /* remove _LOADED table */ lua_pop(L, 1); /* remove _LOADED table */
if (glb) { if (glb) {
lua_pushglobaltable(L); lua_pushvalue(L, -1); /* copy of 'mod' */
lua_pushvalue(L, -2); /* copy of 'mod' */ lua_setglobal(L, modname); /* _G[modname] = module */
lua_setfield(L, -2, modname); /* _G[modname] = module */
lua_pop(L, 1); /* remove _G table */
} }
} }

@ -1,5 +1,5 @@
/* /*
** $Id: lbaselib.c,v 1.273 2011/11/30 13:03:24 roberto Exp $ ** $Id: lbaselib.c,v 1.274 2012/04/27 14:13:19 roberto Exp $
** Basic library ** Basic library
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -293,6 +293,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
lua_pushvalue(L, 1); /* get function */ lua_pushvalue(L, 1); /* get function */
lua_call(L, 0, 1); /* call it */ lua_call(L, 0, 1); /* call it */
if (lua_isnil(L, -1)) { if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* pop result */
*size = 0; *size = 0;
return NULL; return NULL;
} }

@ -1,5 +1,5 @@
/* /*
** $Id: lcorolib.c,v 1.3 2011/08/23 17:24:34 roberto Exp $ ** $Id: lcorolib.c,v 1.4 2012/04/27 18:59:04 roberto Exp $
** Coroutine Library ** Coroutine Library
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -80,8 +80,9 @@ static int luaB_auxwrap (lua_State *L) {
static int luaB_cocreate (lua_State *L) { static int luaB_cocreate (lua_State *L) {
lua_State *NL = lua_newthread(L); lua_State *NL;
luaL_checktype(L, 1, LUA_TFUNCTION); luaL_checktype(L, 1, LUA_TFUNCTION);
NL = lua_newthread(L);
lua_pushvalue(L, 1); /* move function to top */ lua_pushvalue(L, 1); /* move function to top */
lua_xmove(L, NL, 1); /* move function from L to NL */ lua_xmove(L, NL, 1); /* move function from L to NL */
return 1; return 1;

@ -1,5 +1,5 @@
/* /*
** $Id: ldblib.c,v 1.131 2011/10/24 14:54:05 roberto Exp $ ** $Id: ldblib.c,v 1.132 2012/01/19 20:14:44 roberto Exp $
** Interface from Lua to its debug API ** Interface from Lua to its debug API
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -253,14 +253,15 @@ static int db_upvaluejoin (lua_State *L) {
} }
#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY); #define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)
static void hookf (lua_State *L, lua_Debug *ar) { static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] = static const char *const hooknames[] =
{"call", "return", "line", "count", "tail call"}; {"call", "return", "line", "count", "tail call"};
gethooktable(L); gethooktable(L);
lua_rawgetp(L, -1, L); lua_pushthread(L);
lua_rawget(L, -2);
if (lua_isfunction(L, -1)) { if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]); lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0) if (ar->currentline >= 0)
@ -306,10 +307,15 @@ static int db_sethook (lua_State *L) {
count = luaL_optint(L, arg+3, 0); count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count); func = hookf; mask = makemask(smask, count);
} }
gethooktable(L); if (gethooktable(L) == 0) { /* creating hook table? */
lua_pushstring(L, "k");
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
lua_pushvalue(L, -1);
lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
}
lua_pushthread(L1); lua_xmove(L1, L, 1);
lua_pushvalue(L, arg+1); lua_pushvalue(L, arg+1);
lua_rawsetp(L, -2, L1); /* set new hook */ lua_rawset(L, -3); /* set new hook */
lua_pop(L, 1); /* remove hook table */
lua_sethook(L1, func, mask, count); /* set hooks */ lua_sethook(L1, func, mask, count); /* set hooks */
return 0; return 0;
} }
@ -325,7 +331,8 @@ static int db_gethook (lua_State *L) {
lua_pushliteral(L, "external hook"); lua_pushliteral(L, "external hook");
else { else {
gethooktable(L); gethooktable(L);
lua_rawgetp(L, -1, L1); /* get hook */ lua_pushthread(L1); lua_xmove(L1, L, 1);
lua_rawget(L, -2); /* get hook */
lua_remove(L, -2); /* remove hook table */ lua_remove(L, -2); /* remove hook table */
} }
lua_pushstring(L, unmakemask(mask, buff)); lua_pushstring(L, unmakemask(mask, buff));

@ -1,5 +1,5 @@
/* /*
** $Id: ldebug.c,v 2.88 2011/11/30 12:43:51 roberto Exp $ ** $Id: ldebug.c,v 2.89 2012/01/20 22:05:50 roberto Exp $
** Debug Interface ** Debug Interface
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -30,6 +30,9 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL)
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);
@ -173,7 +176,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
static void funcinfo (lua_Debug *ar, Closure *cl) { static void funcinfo (lua_Debug *ar, Closure *cl) {
if (cl == NULL || cl->c.isC) { if (noLuaClosure(cl)) {
ar->source = "=[C]"; ar->source = "=[C]";
ar->linedefined = -1; ar->linedefined = -1;
ar->lastlinedefined = -1; ar->lastlinedefined = -1;
@ -191,7 +194,7 @@ static void funcinfo (lua_Debug *ar, Closure *cl) {
static void collectvalidlines (lua_State *L, Closure *f) { static void collectvalidlines (lua_State *L, Closure *f) {
if (f == NULL || f->c.isC) { if (noLuaClosure(f)) {
setnilvalue(L->top); setnilvalue(L->top);
incr_top(L); incr_top(L);
} }
@ -224,7 +227,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
} }
case 'u': { case 'u': {
ar->nups = (f == NULL) ? 0 : f->c.nupvalues; ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
if (f == NULL || f->c.isC) { if (noLuaClosure(f)) {
ar->isvararg = 1; ar->isvararg = 1;
ar->nparams = 0; ar->nparams = 0;
} }

@ -1,5 +1,5 @@
/* /*
** $Id: ldo.c,v 2.102 2011/11/29 15:55:08 roberto Exp $ ** $Id: ldo.c,v 2.105 2012/06/08 15:14:04 roberto Exp $
** Stack and Call structure of Lua ** Stack and Call structure of Lua
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -402,8 +402,9 @@ static void finishCcall (lua_State *L) {
int n; int n;
lua_assert(ci->u.c.k != NULL); /* must have a continuation */ lua_assert(ci->u.c.k != NULL); /* must have a continuation */
lua_assert(L->nny == 0); lua_assert(L->nny == 0);
/* finish 'luaD_call' */ /* finish 'lua_pcallk' */
//L->nCcalls--; if (ci->callstatus & CIST_YPCALL)
L->errfunc = ci->u.c.old_errfunc;
/* finish 'lua_callk' */ /* finish 'lua_callk' */
adjustresults(L, ci->nresults); adjustresults(L, ci->nresults);
/* call continuation function */ /* call continuation function */
@ -453,7 +454,7 @@ static int recover (lua_State *L, int status) {
CallInfo *ci = findpcall(L); CallInfo *ci = findpcall(L);
if (ci == NULL) return 0; /* no recovery point */ if (ci == NULL) return 0; /* no recovery point */
/* "finish" luaD_pcall */ /* "finish" luaD_pcall */
oldtop = restorestack(L, ci->u.c.extra); oldtop = restorestack(L, ci->extra);
luaF_close(L, oldtop); luaF_close(L, oldtop);
seterrorobj(L, status, oldtop); seterrorobj(L, status, oldtop);
L->ci = ci; L->ci = ci;
@ -484,9 +485,10 @@ static l_noret resume_error (lua_State *L, const char *msg, StkId firstArg) {
** do the work for 'lua_resume' in protected mode ** do the work for 'lua_resume' in protected mode
*/ */
static void resume (lua_State *L, void *ud) { static void resume (lua_State *L, void *ud) {
int nCcalls = L->nCcalls;
StkId firstArg = cast(StkId, ud); StkId firstArg = cast(StkId, ud);
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
if (L->nCcalls >= LUAI_MAXCCALLS) if (nCcalls >= LUAI_MAXCCALLS)
resume_error(L, "C stack overflow", firstArg); resume_error(L, "C stack overflow", firstArg);
if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->status == LUA_OK) { /* may be starting a coroutine */
if (ci != &L->base_ci) /* not in base level? */ if (ci != &L->base_ci) /* not in base level? */
@ -499,10 +501,10 @@ static void resume (lua_State *L, void *ud) {
resume_error(L, "cannot resume dead coroutine", firstArg); resume_error(L, "cannot resume dead coroutine", firstArg);
else { /* resuming from previous yield */ else { /* resuming from previous yield */
L->status = LUA_OK; L->status = LUA_OK;
ci->func = restorestack(L, ci->extra);
if (isLua(ci)) /* yielded inside a hook? */ if (isLua(ci)) /* yielded inside a hook? */
luaV_execute(L); /* just continue running Lua code */ luaV_execute(L); /* just continue running Lua code */
else { /* 'common' yield */ else { /* 'common' yield */
ci->func = restorestack(L, ci->u.c.extra);
if (ci->u.c.k != NULL) { /* does it have a continuation? */ if (ci->u.c.k != NULL) { /* does it have a continuation? */
int n; int n;
ci->u.c.status = LUA_YIELD; /* 'default' status */ ci->u.c.status = LUA_YIELD; /* 'default' status */
@ -513,11 +515,11 @@ static void resume (lua_State *L, void *ud) {
api_checknelems(L, n); api_checknelems(L, n);
firstArg = L->top - n; /* yield results come from continuation */ firstArg = L->top - n; /* yield results come from continuation */
} }
//L->nCcalls--; /* finish 'luaD_call' */
luaD_poscall(L, firstArg); /* finish 'luaD_precall' */ luaD_poscall(L, firstArg); /* finish 'luaD_precall' */
} }
unroll(L, NULL); unroll(L, NULL);
} }
lua_assert(nCcalls == L->nCcalls);
} }
@ -564,13 +566,13 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) {
luaG_runerror(L, "attempt to yield from outside a coroutine"); luaG_runerror(L, "attempt to yield from outside a coroutine");
} }
L->status = LUA_YIELD; L->status = LUA_YIELD;
ci->extra = savestack(L, ci->func); /* save current 'func' */
if (isLua(ci)) { /* inside a hook? */ if (isLua(ci)) { /* inside a hook? */
api_check(L, k == NULL, "hooks cannot continue after yielding"); api_check(L, k == NULL, "hooks cannot continue after yielding");
} }
else { else {
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
ci->u.c.ctx = ctx; /* save context */ ci->u.c.ctx = ctx; /* save context */
ci->u.c.extra = savestack(L, ci->func); /* save current 'func' */
ci->func = L->top - nresults - 1; /* protect stack below results */ ci->func = L->top - nresults - 1; /* protect stack below results */
luaD_throw(L, LUA_YIELD); luaD_throw(L, LUA_YIELD);
} }
@ -627,24 +629,23 @@ static void checkmode (lua_State *L, const char *mode, const char *x) {
static void f_parser (lua_State *L, void *ud) { static void f_parser (lua_State *L, void *ud) {
int i; int i;
Proto *tf;
Closure *cl; Closure *cl;
struct SParser *p = cast(struct SParser *, ud); struct SParser *p = cast(struct SParser *, ud);
int c = zgetc(p->z); /* read first character */ int c = zgetc(p->z); /* read first character */
if (c == LUA_SIGNATURE[0]) { if (c == LUA_SIGNATURE[0]) {
checkmode(L, p->mode, "binary"); checkmode(L, p->mode, "binary");
tf = luaU_undump(L, p->z, &p->buff, p->name); cl = luaU_undump(L, p->z, &p->buff, p->name);
} }
else { else {
checkmode(L, p->mode, "text"); checkmode(L, p->mode, "text");
tf = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
}
lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues);
for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */
UpVal *up = luaF_newupval(L);
cl->l.upvals[i] = up;
luaC_objbarrier(L, cl, up);
} }
setptvalue2s(L, L->top, tf);
incr_top(L);
cl = luaF_newLclosure(L, tf);
setclLvalue(L, L->top - 1, cl);
for (i = 0; i < tf->sizeupvalues; i++) /* initialize upvalues */
cl->l.upvals[i] = luaF_newupval(L);
} }

@ -1,5 +1,5 @@
/* /*
** $Id: ldump.c,v 1.19 2011/11/23 17:48:18 lhf Exp $ ** $Id: ldump.c,v 2.17 2012/01/23 23:02:10 roberto Exp $
** save precompiled Lua chunks ** save precompiled Lua chunks
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -84,8 +84,8 @@ static void DumpConstants(const Proto* f, DumpState* D)
for (i=0; i<n; i++) for (i=0; i<n; i++)
{ {
const TValue* o=&f->k[i]; const TValue* o=&f->k[i];
DumpChar(ttype(o),D); DumpChar(ttypenv(o),D);
switch (ttype(o)) switch (ttypenv(o))
{ {
case LUA_TNIL: case LUA_TNIL:
break; break;
@ -98,6 +98,7 @@ static void DumpConstants(const Proto* f, DumpState* D)
case LUA_TSTRING: case LUA_TSTRING:
DumpString(rawtsvalue(o),D); DumpString(rawtsvalue(o),D);
break; break;
default: lua_assert(0);
} }
} }
n=f->sizep; n=f->sizep;

@ -1,5 +1,5 @@
/* /*
** $Id: lfunc.c,v 2.27 2010/06/30 14:11:17 roberto Exp $ ** $Id: lfunc.c,v 2.29 2012/05/08 13:53:33 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures ** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -21,18 +21,15 @@
Closure *luaF_newCclosure (lua_State *L, int n) { Closure *luaF_newCclosure (lua_State *L, int n) {
Closure *c = &luaC_newobj(L, LUA_TFUNCTION, sizeCclosure(n), NULL, 0)->cl; Closure *c = &luaC_newobj(L, LUA_TCCL, sizeCclosure(n), NULL, 0)->cl;
c->c.isC = 1;
c->c.nupvalues = cast_byte(n); c->c.nupvalues = cast_byte(n);
return c; return c;
} }
Closure *luaF_newLclosure (lua_State *L, Proto *p) { Closure *luaF_newLclosure (lua_State *L, int n) {
int n = p->sizeupvalues; Closure *c = &luaC_newobj(L, LUA_TLCL, sizeLclosure(n), NULL, 0)->cl;
Closure *c = &luaC_newobj(L, LUA_TFUNCTION, sizeLclosure(n), NULL, 0)->cl; c->l.p = NULL;
c->l.isC = 0;
c->l.p = p;
c->l.nupvalues = cast_byte(n); c->l.nupvalues = cast_byte(n);
while (n--) c->l.upvals[n] = NULL; while (n--) c->l.upvals[n] = NULL;
return c; return c;
@ -146,13 +143,6 @@ void luaF_freeproto (lua_State *L, Proto *f) {
} }
void luaF_freeclosure (lua_State *L, Closure *c) {
int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :
sizeLclosure(c->l.nupvalues);
luaM_freemem(L, c, size);
}
/* /*
** Look for n-th local variable at line `line' in function `func'. ** Look for n-th local variable at line `line' in function `func'.
** Returns NULL if not found. ** Returns NULL if not found.

@ -1,5 +1,5 @@
/* /*
** $Id: lgc.c,v 2.116 2011/12/02 13:18:41 roberto Exp $ ** $Id: lgc.c,v 2.133 2012/05/31 21:28:59 roberto Exp $
** Garbage Collector ** Garbage Collector
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -24,34 +24,40 @@
/* how much to allocate before next GC step */ /*
#define GCSTEPSIZE 1024 ** cost of sweeping one element (the size of a small object divided
** by some adjust for the sweep speed)
*/
#define GCSWEEPCOST ((sizeof(TString) + 4) / 4)
/* maximum number of elements to sweep in each single step */ /* maximum number of elements to sweep in each single step */
#define GCSWEEPMAX 40 #define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4))
/* cost of sweeping one element */
#define GCSWEEPCOST 1
/* maximum number of finalizers to call in each GC step */ /* maximum number of finalizers to call in each GC step */
#define GCFINALIZENUM 4 #define GCFINALIZENUM 4
/* cost of marking the root set */
#define GCROOTCOST 10
/* cost of atomic step */ /*
#define GCATOMICCOST 1000 ** macro to adjust 'stepmul': 'stepmul' is actually used like
** 'stepmul / STEPMULADJ' (value chosen by tests)
*/
#define STEPMULADJ 200
/*
** macro to adjust 'pause': 'pause' is actually used like
** 'pause / PAUSEADJ' (value chosen by tests)
*/
#define PAUSEADJ 200
/* basic cost to traverse one object (to be added to the links the
object may have) */
#define TRAVCOST 5
/* /*
** standard negative debt for GC; a reasonable "time" to wait before ** standard negative debt for GC; a reasonable "time" to wait before
** starting a new cycle ** starting a new cycle
*/ */
#define stddebt(g) (-cast(l_mem, gettotalbytes(g)/100) * g->gcpause) #define stddebtest(g,e) (-cast(l_mem, (e)/PAUSEADJ) * g->gcpause)
#define stddebt(g) stddebtest(g, gettotalbytes(g))
/* /*
@ -65,8 +71,6 @@
#define white2gray(x) resetbits(gch(x)->marked, WHITEBITS) #define white2gray(x) resetbits(gch(x)->marked, WHITEBITS)
#define black2gray(x) resetbit(gch(x)->marked, BLACKBIT) #define black2gray(x) resetbit(gch(x)->marked, BLACKBIT)
#define stringmark(s) ((void)((s) && resetbits((s)->tsv.marked, WHITEBITS)))
#define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT) #define isfinalized(x) testbit(gch(x)->marked, FINALIZEDBIT)
@ -123,10 +127,10 @@ static void removeentry (Node *n) {
** other objects: if really collected, cannot keep them; for objects ** other objects: if really collected, cannot keep them; for objects
** being finalized, keep them in keys, but not in values ** being finalized, keep them in keys, but not in values
*/ */
static int iscleared (const TValue *o) { static int iscleared (global_State *g, const TValue *o) {
if (!iscollectable(o)) return 0; if (!iscollectable(o)) return 0;
else if (ttisstring(o)) { else if (ttisstring(o)) {
stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ markobject(g, rawtsvalue(o)); /* strings are `values', so are never weak */
return 0; return 0;
} }
else return iswhite(gcvalue(o)); else return iswhite(gcvalue(o));
@ -217,7 +221,8 @@ void luaC_checkupvalcolor (global_State *g, UpVal *uv) {
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list, GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list,
int offset) { int offset) {
global_State *g = G(L); global_State *g = G(L);
GCObject *o = obj2gco(cast(char *, luaM_newobject(L, tt, sz)) + offset); char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz));
GCObject *o = obj2gco(raw + offset);
if (list == NULL) if (list == NULL)
list = &g->allgc; /* standard list for collectable objects */ list = &g->allgc; /* standard list for collectable objects */
gch(o)->marked = luaC_white(g); gch(o)->marked = luaC_white(g);
@ -239,54 +244,63 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list,
/* /*
** mark an object. Userdata and closed upvalues are visited and turned ** mark an object. Userdata, strings, and closed upvalues are visited
** black here. Strings remain gray (it is the same as making them ** and turned black here. Other objects are marked gray and added
** black). Other objects are marked gray and added to appropriate list ** to appropriate list to be visited (and turned black) later. (Open
** to be visited (and turned black) later. (Open upvalues are already ** upvalues are already linked in 'headuv' list.)
** linked in 'headuv' list.)
*/ */
static void reallymarkobject (global_State *g, GCObject *o) { static void reallymarkobject (global_State *g, GCObject *o) {
lua_assert(iswhite(o) && !isdead(g, o)); lu_mem size;
white2gray(o); white2gray(o);
switch (gch(o)->tt) { switch (gch(o)->tt) {
case LUA_TSTRING: { case LUA_TSHRSTR:
return; /* for strings, gray is as good as black */ case LUA_TLNGSTR: {
size = sizestring(gco2ts(o));
break; /* nothing else to mark; make it black */
} }
case LUA_TUSERDATA: { case LUA_TUSERDATA: {
Table *mt = gco2u(o)->metatable; Table *mt = gco2u(o)->metatable;
markobject(g, mt); markobject(g, mt);
markobject(g, gco2u(o)->env); markobject(g, gco2u(o)->env);
gray2black(o); /* all pointers marked */ size = sizeudata(gco2u(o));
return; break;
} }
case LUA_TUPVAL: { case LUA_TUPVAL: {
UpVal *uv = gco2uv(o); UpVal *uv = gco2uv(o);
markvalue(g, uv->v); markvalue(g, uv->v);
if (uv->v == &uv->u.value) /* closed? (open upvalues remain gray) */ if (uv->v != &uv->u.value) /* open? */
gray2black(o); /* make it black */ return; /* open upvalues remain gray */
size = sizeof(UpVal);
break;
}
case LUA_TLCL: {
gco2lcl(o)->gclist = g->gray;
g->gray = o;
return; return;
} }
case LUA_TFUNCTION: { case LUA_TCCL: {
gco2cl(o)->c.gclist = g->gray; gco2ccl(o)->gclist = g->gray;
g->gray = o; g->gray = o;
break; return;
} }
case LUA_TTABLE: { case LUA_TTABLE: {
linktable(gco2t(o), &g->gray); linktable(gco2t(o), &g->gray);
break; return;
} }
case LUA_TTHREAD: { case LUA_TTHREAD: {
gco2th(o)->gclist = g->gray; gco2th(o)->gclist = g->gray;
g->gray = o; g->gray = o;
break; return;
} }
case LUA_TPROTO: { case LUA_TPROTO: {
gco2p(o)->gclist = g->gray; gco2p(o)->gclist = g->gray;
g->gray = o; g->gray = o;
break; return;
} }
default: lua_assert(0); default: lua_assert(0); return;
} }
gray2black(o);
g->GCmemtrav += size;
} }
@ -359,7 +373,7 @@ static void traverseweakvalue (global_State *g, Table *h) {
else { else {
lua_assert(!ttisnil(gkey(n))); lua_assert(!ttisnil(gkey(n)));
markvalue(g, gkey(n)); /* mark key */ markvalue(g, gkey(n)); /* mark key */
if (!hasclears && iscleared(gval(n))) /* is there a white value? */ if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */
hasclears = 1; /* table will have to be cleared */ hasclears = 1; /* table will have to be cleared */
} }
} }
@ -388,7 +402,7 @@ static int traverseephemeron (global_State *g, Table *h) {
checkdeadkey(n); checkdeadkey(n);
if (ttisnil(gval(n))) /* entry is empty? */ if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */ removeentry(n); /* remove it */
else if (iscleared(gkey(n))) { /* key is not marked (yet)? */ else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */
hasclears = 1; /* table must be cleared */ hasclears = 1; /* table must be cleared */
if (valiswhite(gval(n))) /* value not marked yet? */ if (valiswhite(gval(n))) /* value not marked yet? */
prop = 1; /* must propagate again */ prop = 1; /* must propagate again */
@ -426,30 +440,26 @@ static void traversestrongtable (global_State *g, Table *h) {
} }
static int traversetable (global_State *g, Table *h) { static lu_mem traversetable (global_State *g, Table *h) {
const char *weakkey, *weakvalue;
const TValue *mode = gfasttm(g, h->metatable, TM_MODE); const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
markobject(g, h->metatable); markobject(g, h->metatable);
if (mode && ttisstring(mode)) { /* is there a weak mode? */ if (mode && ttisstring(mode) && /* is there a weak mode? */
int weakkey = (strchr(svalue(mode), 'k') != NULL); ((weakkey = strchr(svalue(mode), 'k')),
int weakvalue = (strchr(svalue(mode), 'v') != NULL); (weakvalue = strchr(svalue(mode), 'v')),
if (weakkey || weakvalue) { /* is really weak? */ (weakkey || weakvalue))) { /* is really weak? */
black2gray(obj2gco(h)); /* keep table gray */ black2gray(obj2gco(h)); /* keep table gray */
if (!weakkey) { /* strong keys? */ if (!weakkey) /* strong keys? */
traverseweakvalue(g, h); traverseweakvalue(g, h);
return TRAVCOST + sizenode(h); else if (!weakvalue) /* strong values? */
}
else if (!weakvalue) { /* strong values? */
traverseephemeron(g, h); traverseephemeron(g, h);
return TRAVCOST + h->sizearray + sizenode(h); else /* all weak */
}
else {
linktable(h, &g->allweak); /* nothing to traverse now */ linktable(h, &g->allweak); /* nothing to traverse now */
return TRAVCOST;
}
} /* else go through */
} }
else /* not weak */
traversestrongtable(g, h); traversestrongtable(g, h);
return TRAVCOST + h->sizearray + (2 * sizenode(h)); return sizeof(Table) + sizeof(TValue) * h->sizearray +
sizeof(Node) * sizenode(h);
} }
@ -457,86 +467,101 @@ static int traverseproto (global_State *g, Proto *f) {
int i; int i;
if (f->cache && iswhite(obj2gco(f->cache))) if (f->cache && iswhite(obj2gco(f->cache)))
f->cache = NULL; /* allow cache to be collected */ f->cache = NULL; /* allow cache to be collected */
stringmark(f->source); markobject(g, f->source);
for (i = 0; i < f->sizek; i++) /* mark literals */ for (i = 0; i < f->sizek; i++) /* mark literals */
markvalue(g, &f->k[i]); markvalue(g, &f->k[i]);
for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */
stringmark(f->upvalues[i].name); markobject(g, f->upvalues[i].name);
for (i = 0; i < f->sizep; i++) /* mark nested protos */ for (i = 0; i < f->sizep; i++) /* mark nested protos */
markobject(g, f->p[i]); markobject(g, f->p[i]);
for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */
stringmark(f->locvars[i].varname); markobject(g, f->locvars[i].varname);
return TRAVCOST + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; return sizeof(Proto) + sizeof(Instruction) * f->sizecode +
sizeof(Proto *) * f->sizep +
sizeof(TValue) * f->sizek +
sizeof(int) * f->sizelineinfo +
sizeof(LocVar) * f->sizelocvars +
sizeof(Upvaldesc) * f->sizeupvalues;
} }
static int traverseclosure (global_State *g, Closure *cl) { static lu_mem traverseCclosure (global_State *g, CClosure *cl) {
if (cl->c.isC) {
int i; int i;
for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
markvalue(g, &cl->c.upvalue[i]); markvalue(g, &cl->upvalue[i]);
} return sizeCclosure(cl->nupvalues);
else { }
static lu_mem traverseLclosure (global_State *g, LClosure *cl) {
int i; int i;
lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues); markobject(g, cl->p); /* mark its prototype */
markobject(g, cl->l.p); /* mark its prototype */ for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */ markobject(g, cl->upvals[i]);
markobject(g, cl->l.upvals[i]); return sizeLclosure(cl->nupvalues);
}
return TRAVCOST + cl->c.nupvalues;
} }
static int traversestack (global_State *g, lua_State *L) { static lu_mem traversestack (global_State *g, lua_State *th) {
StkId o = L->stack; StkId o = th->stack;
if (o == NULL) if (o == NULL)
return 1; /* stack not completely built yet */ return 1; /* stack not completely built yet */
for (; o < L->top; o++) for (; o < th->top; o++)
markvalue(g, o); markvalue(g, o);
if (g->gcstate == GCSatomic) { /* final traversal? */ if (g->gcstate == GCSatomic) { /* final traversal? */
StkId lim = L->stack + L->stacksize; /* real end of stack */ StkId lim = th->stack + th->stacksize; /* real end of stack */
for (; o < lim; o++) /* clear not-marked stack slice */ for (; o < lim; o++) /* clear not-marked stack slice */
setnilvalue(o); setnilvalue(o);
} }
return TRAVCOST + cast_int(o - L->stack); return sizeof(lua_State) + sizeof(TValue) * th->stacksize;
} }
/* /*
** traverse one gray object, turning it to black (except for threads, ** traverse one gray object, turning it to black (except for threads,
** which are always gray). ** which are always gray).
** Returns number of values traversed.
*/ */
static int propagatemark (global_State *g) { static void propagatemark (global_State *g) {
lu_mem size;
GCObject *o = g->gray; GCObject *o = g->gray;
lua_assert(isgray(o)); lua_assert(isgray(o));
gray2black(o); gray2black(o);
switch (gch(o)->tt) { switch (gch(o)->tt) {
case LUA_TTABLE: { case LUA_TTABLE: {
Table *h = gco2t(o); Table *h = gco2t(o);
g->gray = h->gclist; g->gray = h->gclist; /* remove from 'gray' list */
return traversetable(g, h); size = traversetable(g, h);
break;
}
case LUA_TLCL: {
LClosure *cl = gco2lcl(o);
g->gray = cl->gclist; /* remove from 'gray' list */
size = traverseLclosure(g, cl);
break;
} }
case LUA_TFUNCTION: { case LUA_TCCL: {
Closure *cl = gco2cl(o); CClosure *cl = gco2ccl(o);
g->gray = cl->c.gclist; g->gray = cl->gclist; /* remove from 'gray' list */
return traverseclosure(g, cl); size = traverseCclosure(g, cl);
break;
} }
case LUA_TTHREAD: { case LUA_TTHREAD: {
lua_State *th = gco2th(o); lua_State *th = gco2th(o);
g->gray = th->gclist; g->gray = th->gclist; /* remove from 'gray' list */
th->gclist = g->grayagain; th->gclist = g->grayagain;
g->grayagain = o; g->grayagain = o; /* insert into 'grayagain' list */
black2gray(o); black2gray(o);
return traversestack(g, th); size = traversestack(g, th);
break;
} }
case LUA_TPROTO: { case LUA_TPROTO: {
Proto *p = gco2p(o); Proto *p = gco2p(o);
g->gray = p->gclist; g->gray = p->gclist; /* remove from 'gray' list */
return traverseproto(g, p); size = traverseproto(g, p);
break;
} }
default: lua_assert(0); return 0; default: lua_assert(0); return;
} }
g->GCmemtrav += size;
} }
@ -599,12 +624,12 @@ static void convergeephemerons (global_State *g) {
** clear entries with unmarked keys from all weaktables in list 'l' up ** clear entries with unmarked keys from all weaktables in list 'l' up
** to element 'f' ** to element 'f'
*/ */
static void clearkeys (GCObject *l, GCObject *f) { static void clearkeys (global_State *g, GCObject *l, GCObject *f) {
for (; l != f; l = gco2t(l)->gclist) { for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *n, *limit = gnodelast(h); Node *n, *limit = gnodelast(h);
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (!ttisnil(gval(n)) && (iscleared(gkey(n)))) { if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) {
setnilvalue(gval(n)); /* remove value ... */ setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* and remove entry from table */ removeentry(n); /* and remove entry from table */
} }
@ -617,18 +642,18 @@ static void clearkeys (GCObject *l, GCObject *f) {
** clear entries with unmarked values from all weaktables in list 'l' up ** clear entries with unmarked values from all weaktables in list 'l' up
** to element 'f' ** to element 'f'
*/ */
static void clearvalues (GCObject *l, GCObject *f) { static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
for (; l != f; l = gco2t(l)->gclist) { for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l); Table *h = gco2t(l);
Node *n, *limit = gnodelast(h); Node *n, *limit = gnodelast(h);
int i; int i;
for (i = 0; i < h->sizearray; i++) { for (i = 0; i < h->sizearray; i++) {
TValue *o = &h->array[i]; TValue *o = &h->array[i];
if (iscleared(o)) /* value was collected? */ if (iscleared(g, o)) /* value was collected? */
setnilvalue(o); /* remove value */ setnilvalue(o); /* remove value */
} }
for (n = gnode(h, 0); n < limit; n++) { for (n = gnode(h, 0); n < limit; n++) {
if (!ttisnil(gval(n)) && iscleared(gval(n))) { if (!ttisnil(gval(n)) && iscleared(g, gval(n))) {
setnilvalue(gval(n)); /* remove value ... */ setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* and remove entry from table */ removeentry(n); /* and remove entry from table */
} }
@ -640,13 +665,22 @@ static void clearvalues (GCObject *l, GCObject *f) {
static void freeobj (lua_State *L, GCObject *o) { static void freeobj (lua_State *L, GCObject *o) {
switch (gch(o)->tt) { switch (gch(o)->tt) {
case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; case LUA_TLCL: {
luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues));
break;
}
case LUA_TCCL: {
luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
break;
}
case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
case LUA_TTABLE: luaH_free(L, gco2t(o)); break; case LUA_TTABLE: luaH_free(L, gco2t(o)); break;
case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break; case LUA_TTHREAD: luaE_freethread(L, gco2th(o)); break;
case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break; case LUA_TUSERDATA: luaM_freemem(L, o, sizeudata(gco2u(o))); break;
case LUA_TSTRING: { case LUA_TSHRSTR:
G(L)->strt.nuse--; G(L)->strt.nuse--;
/* go through */
case LUA_TLNGSTR: {
luaM_freemem(L, o, sizestring(gco2ts(o))); luaM_freemem(L, o, sizestring(gco2ts(o)));
break; break;
} }
@ -689,7 +723,6 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
int ow = otherwhite(g); int ow = otherwhite(g);
int toclear, toset; /* bits to clear and to set in all live objects */ int toclear, toset; /* bits to clear and to set in all live objects */
int tostop; /* stop sweep when this is true */ int tostop; /* stop sweep when this is true */
l_mem debt = g->GCdebt; /* current debt */
if (isgenerational(g)) { /* generational mode? */ if (isgenerational(g)) { /* generational mode? */
toclear = ~0; /* clear nothing */ toclear = ~0; /* clear nothing */
toset = bitmask(OLDBIT); /* set the old bit of all surviving objects */ toset = bitmask(OLDBIT); /* set the old bit of all surviving objects */
@ -708,19 +741,30 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { else {
if (testbits(marked, tostop))
return NULL; /* stop sweeping this list */
if (gch(curr)->tt == LUA_TTHREAD) if (gch(curr)->tt == LUA_TTHREAD)
sweepthread(L, gco2th(curr)); /* sweep thread's upvalues */ sweepthread(L, gco2th(curr)); /* sweep thread's upvalues */
if (testbits(marked, tostop)) {
static GCObject *nullp = NULL;
p = &nullp; /* stop sweeping this list */
break;
}
/* update marks */ /* update marks */
gch(curr)->marked = cast_byte((marked & toclear) | toset); gch(curr)->marked = cast_byte((marked & toclear) | toset);
p = &gch(curr)->next; /* go to next element */ p = &gch(curr)->next; /* go to next element */
} }
} }
luaE_setdebt(g, debt); /* sweeping should not change debt */ return (*p == NULL) ? NULL : p;
}
/*
** sweep a list until a live object (or end of list)
*/
static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) {
GCObject ** old = p;
int i = 0;
do {
i++;
p = sweeplist(L, p, 1);
} while (p == old);
if (n) *n += i;
return p; return p;
} }
@ -783,12 +827,14 @@ static void GCTM (lua_State *L, int propagateerrors) {
L->allowhook = oldah; /* restore hooks */ L->allowhook = oldah; /* restore hooks */
g->gcrunning = running; /* restore state */ g->gcrunning = running; /* restore state */
if (status != LUA_OK && propagateerrors) { /* error while running __gc? */ if (status != LUA_OK && propagateerrors) { /* error while running __gc? */
if (status == LUA_ERRRUN) { /* is there an error msg.? */ if (status == LUA_ERRRUN) { /* is there an error object? */
luaO_pushfstring(L, "error in __gc metamethod (%s)", const char *msg = (ttisstring(L->top - 1))
lua_tostring(L, -1)); ? svalue(L->top - 1)
: "no message";
luaO_pushfstring(L, "error in __gc metamethod (%s)", msg);
status = LUA_ERRGCMM; /* error in __gc metamethod */ status = LUA_ERRGCMM; /* error in __gc metamethod */
} }
luaD_throw(L, status); /* re-send error */ luaD_throw(L, status); /* re-throw error */
} }
} }
} }
@ -834,11 +880,20 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
return; /* nothing to be done */ return; /* nothing to be done */
else { /* move 'o' to 'finobj' list */ else { /* move 'o' to 'finobj' list */
GCObject **p; GCObject **p;
for (p = &g->allgc; *p != o; p = &gch(*p)->next) ; GCheader *ho = gch(o);
*p = gch(o)->next; /* remove 'o' from root list */ if (g->sweepgc == &ho->next) { /* avoid removing current sweep object */
gch(o)->next = g->finobj; /* link it in list 'finobj' */ lua_assert(issweepphase(g));
g->sweepgc = sweeptolive(L, g->sweepgc, NULL);
}
/* search for pointer pointing to 'o' */
for (p = &g->allgc; *p != o; p = &gch(*p)->next) { /* empty */ }
*p = ho->next; /* remove 'o' from root list */
ho->next = g->finobj; /* link it in list 'finobj' */
g->finobj = o; g->finobj = o;
l_setbit(gch(o)->marked, SEPARATED); /* mark it as such */ l_setbit(ho->marked, SEPARATED); /* mark it as such */
if (!keepinvariant(g)) /* not keeping invariant? */
makewhite(g, o); /* "sweep" object */
else
resetoldbit(o); /* see MOVE OLD rule */ resetoldbit(o); /* see MOVE OLD rule */
} }
} }
@ -856,6 +911,28 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
#define sweepphases \ #define sweepphases \
(bitmask(GCSsweepstring) | bitmask(GCSsweepudata) | bitmask(GCSsweep)) (bitmask(GCSsweepstring) | bitmask(GCSsweepudata) | bitmask(GCSsweep))
/*
** enter first sweep phase (strings) and prepare pointers for other
** sweep phases. The calls to 'sweeptolive' make pointers point to an
** object inside the list (instead of to the header), so that the real
** sweep do not need to skip objects created between "now" and the start
** of the real sweep.
** Returns how many objects it sweeped.
*/
static int entersweep (lua_State *L) {
global_State *g = G(L);
int n = 0;
g->gcstate = GCSsweepstring;
lua_assert(g->sweepgc == NULL && g->sweepfin == NULL);
/* prepare to sweep strings, finalizable objects, and regular objects */
g->sweepstrgc = 0;
g->sweepfin = sweeptolive(L, &g->finobj, &n);
g->sweepgc = sweeptolive(L, &g->allgc, &n);
return n;
}
/* /*
** change GC mode ** change GC mode
*/ */
@ -865,15 +942,14 @@ void luaC_changemode (lua_State *L, int mode) {
if (mode == KGC_GEN) { /* change to generational mode */ if (mode == KGC_GEN) { /* change to generational mode */
/* make sure gray lists are consistent */ /* make sure gray lists are consistent */
luaC_runtilstate(L, bitmask(GCSpropagate)); luaC_runtilstate(L, bitmask(GCSpropagate));
g->lastmajormem = gettotalbytes(g); g->GCestimate = gettotalbytes(g);
g->gckind = KGC_GEN; g->gckind = KGC_GEN;
} }
else { /* change to incremental mode */ else { /* change to incremental mode */
/* sweep all objects to turn them back to white /* sweep all objects to turn them back to white
(as white has not changed, nothing extra will be collected) */ (as white has not changed, nothing extra will be collected) */
g->sweepstrgc = 0;
g->gcstate = GCSsweepstring;
g->gckind = KGC_NORMAL; g->gckind = KGC_NORMAL;
entersweep(L);
luaC_runtilstate(L, ~sweepphases); luaC_runtilstate(L, ~sweepphases);
} }
} }
@ -907,8 +983,9 @@ void luaC_freeallobjects (lua_State *L) {
} }
static void atomic (lua_State *L) { static l_mem atomic (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
l_mem work = -g->GCmemtrav; /* start counting work */
GCObject *origweak, *origall; GCObject *origweak, *origall;
lua_assert(!iswhite(obj2gco(g->mainthread))); lua_assert(!iswhite(obj2gco(g->mainthread)));
markobject(g, L); /* mark running thread */ markobject(g, L); /* mark running thread */
@ -917,77 +994,87 @@ static void atomic (lua_State *L) {
markmt(g); /* mark basic metatables */ markmt(g); /* mark basic metatables */
/* remark occasional upvalues of (maybe) dead threads */ /* remark occasional upvalues of (maybe) dead threads */
remarkupvals(g); remarkupvals(g);
propagateall(g); /* propagate changes */
work += g->GCmemtrav; /* stop counting (do not (re)count grays) */
/* traverse objects caught by write barrier and by 'remarkupvals' */ /* traverse objects caught by write barrier and by 'remarkupvals' */
retraversegrays(g); retraversegrays(g);
work -= g->GCmemtrav; /* restart counting */
convergeephemerons(g); convergeephemerons(g);
/* at this point, all strongly accessible objects are marked. */ /* at this point, all strongly accessible objects are marked. */
/* clear values from weak tables, before checking finalizers */ /* clear values from weak tables, before checking finalizers */
clearvalues(g->weak, NULL); clearvalues(g, g->weak, NULL);
clearvalues(g->allweak, NULL); clearvalues(g, g->allweak, NULL);
origweak = g->weak; origall = g->allweak; origweak = g->weak; origall = g->allweak;
work += g->GCmemtrav; /* stop counting (objects being finalized) */
separatetobefnz(L, 0); /* separate objects to be finalized */ separatetobefnz(L, 0); /* separate objects to be finalized */
markbeingfnz(g); /* mark userdata that will be finalized */ markbeingfnz(g); /* mark objects that will be finalized */
propagateall(g); /* remark, to propagate `preserveness' */ propagateall(g); /* remark, to propagate `preserveness' */
work -= g->GCmemtrav; /* restart counting */
convergeephemerons(g); convergeephemerons(g);
/* at this point, all resurrected objects are marked. */ /* at this point, all resurrected objects are marked. */
/* remove dead objects from weak tables */ /* remove dead objects from weak tables */
clearkeys(g->ephemeron, NULL); /* clear keys from all ephemeron tables */ clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */
clearkeys(g->allweak, NULL); /* clear keys from all allweak tables */ clearkeys(g, g->allweak, NULL); /* clear keys from all allweak tables */
/* clear values from resurrected weak tables */ /* clear values from resurrected weak tables */
clearvalues(g->weak, origweak); clearvalues(g, g->weak, origweak);
clearvalues(g->allweak, origall); clearvalues(g, g->allweak, origall);
g->sweepstrgc = 0; /* prepare to sweep strings */
g->gcstate = GCSsweepstring;
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
/*lua_checkmemory(L);*/ work += g->GCmemtrav; /* complete counting */
return work; /* estimate of memory marked by 'atomic' */
} }
static l_mem singlestep (lua_State *L) { static lu_mem singlestep (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
switch (g->gcstate) { switch (g->gcstate) {
case GCSpause: { case GCSpause: {
g->GCmemtrav = 0; /* start to count memory traversed */
if (!isgenerational(g)) if (!isgenerational(g))
markroot(g); /* start a new collection */ markroot(g); /* start a new collection */
/* in any case, root must be marked */ /* in any case, root must be marked at this point */
lua_assert(!iswhite(obj2gco(g->mainthread)) lua_assert(!iswhite(obj2gco(g->mainthread))
&& !iswhite(gcvalue(&g->l_registry))); && !iswhite(gcvalue(&g->l_registry)));
g->gcstate = GCSpropagate; g->gcstate = GCSpropagate;
return GCROOTCOST; return g->GCmemtrav;
} }
case GCSpropagate: { case GCSpropagate: {
if (g->gray) if (g->gray) {
return propagatemark(g); lu_mem oldtrav = g->GCmemtrav;
propagatemark(g);
return g->GCmemtrav - oldtrav; /* memory traversed in this step */
}
else { /* no more `gray' objects */ else { /* no more `gray' objects */
lu_mem work;
int sw;
g->gcstate = GCSatomic; /* finish mark phase */ g->gcstate = GCSatomic; /* finish mark phase */
atomic(L); g->GCestimate = g->GCmemtrav; /* save what was counted */;
return GCATOMICCOST; work = atomic(L); /* add what was traversed by 'atomic' */
g->GCestimate += work; /* estimate of total memory traversed */
sw = entersweep(L);
return work + sw * GCSWEEPCOST;
} }
} }
case GCSsweepstring: { case GCSsweepstring: {
if (g->sweepstrgc < g->strt.size) { int i;
sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); for (i = 0; i < GCSWEEPMAX && g->sweepstrgc + i < g->strt.size; i++)
return GCSWEEPCOST; sweepwholelist(L, &g->strt.hash[g->sweepstrgc + i]);
} g->sweepstrgc += i;
else { /* no more strings to sweep */ if (g->sweepstrgc >= g->strt.size) /* no more strings to sweep? */
g->sweepgc = &g->finobj; /* prepare to sweep finalizable objects */
g->gcstate = GCSsweepudata; g->gcstate = GCSsweepudata;
return 0; return i * GCSWEEPCOST;
}
} }
case GCSsweepudata: { case GCSsweepudata: {
if (*g->sweepgc) { if (g->sweepfin) {
g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); g->sweepfin = sweeplist(L, g->sweepfin, GCSWEEPMAX);
return GCSWEEPMAX*GCSWEEPCOST; return GCSWEEPMAX*GCSWEEPCOST;
} }
else { else {
g->sweepgc = &g->allgc; /* go to next phase */
g->gcstate = GCSsweep; g->gcstate = GCSsweep;
return GCSWEEPCOST; return 0;
} }
} }
case GCSsweep: { case GCSsweep: {
if (*g->sweepgc) { if (g->sweepgc) {
g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
return GCSWEEPMAX*GCSWEEPCOST; return GCSWEEPMAX*GCSWEEPCOST;
} }
@ -1018,43 +1105,52 @@ void luaC_runtilstate (lua_State *L, int statesmask) {
static void generationalcollection (lua_State *L) { static void generationalcollection (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
if (g->lastmajormem == 0) { /* signal for another major collection? */ if (g->GCestimate == 0) { /* signal for another major collection? */
luaC_fullgc(L, 0); /* perform a full regular collection */ luaC_fullgc(L, 0); /* perform a full regular collection */
g->lastmajormem = gettotalbytes(g); /* update control */ g->GCestimate = gettotalbytes(g); /* update control */
} }
else { else {
lu_mem estimate = g->GCestimate;
luaC_runtilstate(L, ~bitmask(GCSpause)); /* run complete cycle */ luaC_runtilstate(L, ~bitmask(GCSpause)); /* run complete cycle */
luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCSpause));
if (gettotalbytes(g) > g->lastmajormem/100 * g->gcmajorinc) if (gettotalbytes(g) > (estimate / 100) * g->gcmajorinc)
g->lastmajormem = 0; /* signal for a major collection */ g->GCestimate = 0; /* signal for a major collection */
} }
luaE_setdebt(g, stddebt(g)); luaE_setdebt(g, stddebt(g));
} }
static void step (lua_State *L) { static void incstep (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
l_mem lim = g->gcstepmul; /* how much to work */ l_mem debt = g->GCdebt;
int stepmul = g->gcstepmul;
if (stepmul < 40) stepmul = 40; /* avoid ridiculous low values */
/* convert debt from Kb to 'work units' (avoid zero debt and overflows) */
debt = (debt / STEPMULADJ) + 1;
debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM;
do { /* always perform at least one single step */ do { /* always perform at least one single step */
lim -= singlestep(L); lu_mem work = singlestep(L); /* do some work */
} while (lim > 0 && g->gcstate != GCSpause); debt -= work;
if (g->gcstate != GCSpause) } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause);
luaE_setdebt(g, g->GCdebt - GCSTEPSIZE); if (g->gcstate == GCSpause)
debt = stddebtest(g, g->GCestimate); /* pause until next cycle */
else else
luaE_setdebt(g, stddebt(g)); debt = (debt / stepmul) * STEPMULADJ; /* convert 'work units' to Kb */
luaE_setdebt(g, debt);
} }
/* /*
** performs a basic GC step even if the collector is stopped ** performs a basic GC step
*/ */
void luaC_forcestep (lua_State *L) { void luaC_forcestep (lua_State *L) {
global_State *g = G(L); global_State *g = G(L);
int i; int i;
if (isgenerational(g)) generationalcollection(L); if (isgenerational(g)) generationalcollection(L);
else step(L); else incstep(L);
for (i = 0; i < GCFINALIZENUM && g->tobefnz; i++) /* run a few finalizers (or all of them at the end of a collect cycle) */
GCTM(L, 1); /* Call a few pending finalizers */ for (i = 0; g->tobefnz && (i < GCFINALIZENUM || g->gcstate == GCSpause); i++)
GCTM(L, 1); /* call one finalizer */
} }
@ -1062,10 +1158,13 @@ void luaC_forcestep (lua_State *L) {
** performs a basic GC step only if collector is running ** performs a basic GC step only if collector is running
*/ */
void luaC_step (lua_State *L) { void luaC_step (lua_State *L) {
if (G(L)->gcrunning) luaC_forcestep(L); global_State *g = G(L);
if (g->gcrunning) luaC_forcestep(L);
else luaE_setdebt(g, -GCSTEPSIZE); /* avoid being called too often */
} }
/* /*
** performs a full GC cycle; if "isemergency", does not call ** performs a full GC cycle; if "isemergency", does not call
** finalizers (which could change stack positions) ** finalizers (which could change stack positions)
@ -1073,16 +1172,19 @@ void luaC_step (lua_State *L) {
void luaC_fullgc (lua_State *L, int isemergency) { void luaC_fullgc (lua_State *L, int isemergency) {
global_State *g = G(L); global_State *g = G(L);
int origkind = g->gckind; int origkind = g->gckind;
int someblack = keepinvariant(g);
lua_assert(origkind != KGC_EMERGENCY); lua_assert(origkind != KGC_EMERGENCY);
if (!isemergency) /* do not run finalizers during emergency GC */ if (isemergency) /* do not run finalizers during emergency GC */
g->gckind = KGC_EMERGENCY;
else {
g->gckind = KGC_NORMAL;
callallpendingfinalizers(L, 1); callallpendingfinalizers(L, 1);
if (keepinvariant(g)) { /* marking phase? */ }
if (someblack) { /* may there be some black objects? */
/* must sweep all objects to turn them back to white /* must sweep all objects to turn them back to white
(as white has not changed, nothing will be collected) */ (as white has not changed, nothing will be collected) */
g->sweepstrgc = 0; entersweep(L);
g->gcstate = GCSsweepstring;
} }
g->gckind = isemergency ? KGC_EMERGENCY : KGC_NORMAL;
/* finish any pending sweep phase to start a new cycle */ /* finish any pending sweep phase to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCSpause));
/* run entire collector */ /* run entire collector */

@ -1,5 +1,5 @@
/* /*
** $Id: llex.c,v 2.59 2011/11/30 12:43:51 roberto Exp $ ** $Id: llex.c,v 2.61 2012/01/23 23:05:51 roberto Exp $
** Lexical Analyzer ** Lexical Analyzer
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -67,7 +67,7 @@ void luaX_init (lua_State *L) {
for (i=0; i<NUM_RESERVED; i++) { for (i=0; i<NUM_RESERVED; i++) {
TString *ts = luaS_new(L, luaX_tokens[i]); TString *ts = luaS_new(L, luaX_tokens[i]);
luaS_fix(ts); /* reserved words are never collected */ luaS_fix(ts); /* reserved words are never collected */
ts->tsv.reserved = cast_byte(i+1); /* reserved word */ ts->tsv.extra = cast_byte(i+1); /* reserved word */
} }
} }
@ -222,13 +222,24 @@ static void trydecpoint (LexState *ls, SemInfo *seminfo) {
/* LUA_NUMBER */ /* LUA_NUMBER */
/*
** this function is quite liberal in what it accepts, as 'luaO_str2d'
** will reject ill-formed numerals.
*/
static void read_numeral (LexState *ls, SemInfo *seminfo) { static void read_numeral (LexState *ls, SemInfo *seminfo) {
const char *expo = "Ee";
int first = ls->current;
lua_assert(lisdigit(ls->current)); lua_assert(lisdigit(ls->current));
do {
save_and_next(ls); save_and_next(ls);
if (check_next(ls, "EePp")) /* exponent part? */ if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */
expo = "Pp";
for (;;) {
if (check_next(ls, expo)) /* exponent part? */
check_next(ls, "+-"); /* optional exponent sign */ check_next(ls, "+-"); /* optional exponent sign */
} while (lislalnum(ls->current) || ls->current == '.'); if (lisxdigit(ls->current) || ls->current == '.')
save_and_next(ls);
else break;
}
save(ls, '\0'); save(ls, '\0');
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
if (!buff2d(ls->buff, &seminfo->r)) /* format error? */ if (!buff2d(ls->buff, &seminfo->r)) /* format error? */
@ -480,8 +491,8 @@ static int llex (LexState *ls, SemInfo *seminfo) {
ts = luaX_newstring(ls, luaZ_buffer(ls->buff), ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff)); luaZ_bufflen(ls->buff));
seminfo->ts = ts; seminfo->ts = ts;
if (ts->tsv.reserved > 0) /* reserved word? */ if (isreserved(ts)) /* reserved word? */
return ts->tsv.reserved - 1 + FIRST_RESERVED; return ts->tsv.extra - 1 + FIRST_RESERVED;
else { else {
return TK_NAME; return TK_NAME;
} }

@ -1,5 +1,5 @@
/* /*
** $Id: lmathlib.c,v 1.80 2011/07/05 12:49:35 roberto Exp $ ** $Id: lmathlib.c,v 1.81 2012/05/18 17:47:53 roberto Exp $
** Standard mathematical library ** Standard mathematical library
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -17,17 +17,17 @@
#include "lualib.h" #include "lualib.h"
#undef PI
#define PI (3.14159265358979323846)
#define RADIANS_PER_DEGREE (PI/180.0)
/* macro 'l_tg' allows the addition of an 'l' or 'f' to all math operations */ /* macro 'l_tg' allows the addition of an 'l' or 'f' to all math operations */
#if !defined(l_tg) #if !defined(l_tg)
#define l_tg(x) (x) #define l_tg(x) (x)
#endif #endif
#undef PI
#define PI (l_tg(3.1415926535897932384626433832795))
#define RADIANS_PER_DEGREE (PI/180.0)
static int math_abs (lua_State *L) { static int math_abs (lua_State *L) {
lua_pushnumber(L, l_tg(fabs)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_tg(fabs)(luaL_checknumber(L, 1)));

@ -1,5 +1,5 @@
/* /*
** $Id: lmem.c,v 1.83 2011/11/30 12:42:49 roberto Exp $ ** $Id: lmem.c,v 1.84 2012/05/23 15:41:53 roberto Exp $
** Interface to Memory Manager ** Interface to Memory Manager
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -94,22 +94,6 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
} }
lua_assert((nsize == 0) == (newblock == NULL)); lua_assert((nsize == 0) == (newblock == NULL));
g->GCdebt = (g->GCdebt + nsize) - realosize; g->GCdebt = (g->GCdebt + nsize) - realosize;
#if defined(TRACEMEM)
{ /* auxiliary patch to monitor garbage collection.
** To plot, gnuplot with following command:
** plot TRACEMEM using 1:2 with lines, TRACEMEM using 1:3 with lines
*/
static unsigned long total = 0; /* our "time" */
static FILE *f = NULL; /* output file */
total++; /* "time" always grows */
if ((total % 200) == 0) {
if (f == NULL) f = fopen(TRACEMEM, "w");
fprintf(f, "%lu %u %d %d\n", total,
gettotalbytes(g), g->GCdebt, g->gcstate * 10000);
}
}
#endif
return newblock; return newblock;
} }

@ -1,5 +1,5 @@
/* /*
** $Id: loadlib.c,v 1.108 2011/12/12 16:34:03 roberto Exp $ ** $Id: loadlib.c,v 1.111 2012/05/30 12:33:44 roberto Exp $
** Dynamic library loader for Lua ** Dynamic library loader for Lua
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
** **
@ -92,9 +92,9 @@
#define LUA_OFSEP "_" #define LUA_OFSEP "_"
#define LIBPREFIX "LOADLIB: " /* table (in the registry) that keeps handles for all loaded C libraries */
#define CLIBS "_CLIBS"
#define POF LUA_POF
#define LIB_FAIL "open" #define LIB_FAIL "open"
@ -248,48 +248,54 @@ static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
#endif #endif
static void *ll_checkclib (lua_State *L, const char *path) {
static void **ll_register (lua_State *L, const char *path) { void *plib;
void **plib; lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
lua_pushfstring(L, "%s%s", LIBPREFIX, path); lua_getfield(L, -1, path);
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */
if (!lua_isnil(L, -1)) /* is there an entry? */ lua_pop(L, 2); /* pop CLIBS table and 'plib' */
plib = (void **)lua_touserdata(L, -1);
else { /* no entry yet; create one */
lua_pop(L, 1); /* remove result from gettable */
plib = (void **)lua_newuserdata(L, sizeof(const void *));
*plib = NULL;
luaL_setmetatable(L, "_LOADLIB");
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
}
return plib; return plib;
} }
static void ll_addtoclib (lua_State *L, const char *path, void *plib) {
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
lua_pushlightuserdata(L, plib);
lua_pushvalue(L, -1);
lua_setfield(L, -3, path); /* CLIBS[path] = plib */
lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
lua_pop(L, 1); /* pop CLIBS table */
}
/* /*
** __gc tag method: calls library's `ll_unloadlib' function with the lib ** __gc tag method for CLIBS table: calls 'll_unloadlib' for all lib
** handle ** handles in list CLIBS
*/ */
static int gctm (lua_State *L) { static int gctm (lua_State *L) {
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); int n = luaL_len(L, 1);
if (*lib) ll_unloadlib(*lib); for (; n >= 1; n--) { /* for each handle, in reverse order */
*lib = NULL; /* mark library as closed */ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */
ll_unloadlib(lua_touserdata(L, -1));
lua_pop(L, 1); /* pop handle */
}
return 0; return 0;
} }
static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
void **reg = ll_register(L, path); void *reg = ll_checkclib(L, path); /* check loaded C libraries */
if (*reg == NULL) *reg = ll_load(L, path, *sym == '*'); if (reg == NULL) { /* must load library? */
if (*reg == NULL) return ERRLIB; /* unable to load library */ reg = ll_load(L, path, *sym == '*');
if (reg == NULL) return ERRLIB; /* unable to load library */
ll_addtoclib(L, path, reg);
}
if (*sym == '*') { /* loading only library (no function)? */ if (*sym == '*') { /* loading only library (no function)? */
lua_pushboolean(L, 1); /* return 'true' */ lua_pushboolean(L, 1); /* return 'true' */
return 0; /* no errors */ return 0; /* no errors */
} }
else { else {
lua_CFunction f = ll_sym(L, *reg, sym); lua_CFunction f = ll_sym(L, reg, sym);
if (f == NULL) if (f == NULL)
return ERRFUNC; /* unable to find function */ return ERRFUNC; /* unable to find function */
lua_pushcfunction(L, f); /* else create new function */ lua_pushcfunction(L, f); /* else create new function */
@ -418,12 +424,12 @@ static int loadfunc (lua_State *L, const char *filename, const char *modname) {
if (mark) { if (mark) {
int stat; int stat;
funcname = lua_pushlstring(L, modname, mark - modname); funcname = lua_pushlstring(L, modname, mark - modname);
funcname = lua_pushfstring(L, POF"%s", funcname); funcname = lua_pushfstring(L, LUA_POF"%s", funcname);
stat = ll_loadfunc(L, filename, funcname); stat = ll_loadfunc(L, filename, funcname);
if (stat != ERRFUNC) return stat; if (stat != ERRFUNC) return stat;
modname = mark + 1; /* else go ahead and try old-style name */ modname = mark + 1; /* else go ahead and try old-style name */
} }
funcname = lua_pushfstring(L, POF"%s", modname); funcname = lua_pushfstring(L, LUA_POF"%s", modname);
return ll_loadfunc(L, filename, funcname); return ll_loadfunc(L, filename, funcname);
} }
@ -476,9 +482,9 @@ static void findloader (lua_State *L, const char *name) {
lua_getfield(L, lua_upvalueindex(1), "searchers"); /* will be at index 3 */ lua_getfield(L, lua_upvalueindex(1), "searchers"); /* will be at index 3 */
if (!lua_istable(L, 3)) if (!lua_istable(L, 3))
luaL_error(L, LUA_QL("package.searchers") " must be a table"); luaL_error(L, LUA_QL("package.searchers") " must be a table");
/* iterate over available seachers to find a loader */ /* iterate over available searchers to find a loader */
for (i = 1; ; i++) { for (i = 1; ; i++) {
lua_rawgeti(L, 3, i); /* get a seacher */ lua_rawgeti(L, 3, i); /* get a searcher */
if (lua_isnil(L, -1)) { /* no more searchers? */ if (lua_isnil(L, -1)) { /* no more searchers? */
lua_pop(L, 1); /* remove nil */ lua_pop(L, 1); /* remove nil */
luaL_pushresult(&msg); /* create error message */ luaL_pushresult(&msg); /* create error message */
@ -666,18 +672,10 @@ static const luaL_Reg ll_funcs[] = {
}; };
static const lua_CFunction searchers[] = static void createsearcherstable (lua_State *L) {
static const lua_CFunction searchers[] =
{searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
LUAMOD_API int luaopen_package (lua_State *L) {
int i; int i;
/* create new type _LOADLIB */
luaL_newmetatable(L, "_LOADLIB");
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc");
/* create `package' table */
luaL_newlib(L, pk_funcs);
/* create 'searchers' table */ /* create 'searchers' table */
lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
/* fill it with pre-defined searchers */ /* fill it with pre-defined searchers */
@ -686,6 +684,19 @@ LUAMOD_API int luaopen_package (lua_State *L) {
lua_pushcclosure(L, searchers[i], 1); lua_pushcclosure(L, searchers[i], 1);
lua_rawseti(L, -2, i+1); lua_rawseti(L, -2, i+1);
} }
}
LUAMOD_API int luaopen_package (lua_State *L) {
/* create table CLIBS to keep track of loaded C libraries */
luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS);
lua_createtable(L, 0, 1); /* metatable for CLIBS */
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */
lua_setmetatable(L, -2);
/* create `package' table */
luaL_newlib(L, pk_funcs);
createsearcherstable(L);
#if defined(LUA_COMPAT_LOADERS) #if defined(LUA_COMPAT_LOADERS)
lua_pushvalue(L, -1); /* make a copy of 'searchers' table */ lua_pushvalue(L, -1); /* make a copy of 'searchers' table */
lua_setfield(L, -3, "loaders"); /* put it in field `loaders' */ lua_setfield(L, -3, "loaders"); /* put it in field `loaders' */

@ -1,5 +1,6 @@
/* /*
** $Id: lopcodes.c,v 1.48 2011/04/19 16:22:13 roberto Exp $ ** $Id: lopcodes.c,v 1.49 2012/05/14 13:34:18 roberto Exp $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */

@ -1,5 +1,5 @@
/* /*
** $Id: loslib.c,v 1.38 2011/11/30 12:35:05 roberto Exp $ ** $Id: loslib.c,v 1.39 2012/05/23 15:37:09 roberto Exp $
** Standard Operating System library ** Standard Operating System library
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -28,8 +28,9 @@
#if !defined(LUA_USE_POSIX) #if !defined(LUA_USE_POSIX)
#define LUA_STRFTIMEOPTIONS { "aAbBcdHIjmMpSUwWxXyYz%", "" } #define LUA_STRFTIMEOPTIONS { "aAbBcdHIjmMpSUwWxXyYz%", "" }
#else #else
#define LUA_STRFTIMEOPTIONS { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "", \ #define LUA_STRFTIMEOPTIONS \
"E", "cCxXyY", \ { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "" \
"", "E", "cCxXyY", \
"O", "deHImMSuUVwWy" } "O", "deHImMSuUVwWy" }
#endif #endif

@ -1,5 +1,5 @@
/* /*
** $Id: lparser.c,v 2.124 2011/12/02 13:23:56 roberto Exp $ ** $Id: lparser.c,v 2.128 2012/05/20 14:51:23 roberto Exp $
** Lua Parser ** Lua Parser
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -222,7 +222,7 @@ static int searchupvalue (FuncState *fs, TString *name) {
int i; int i;
Upvaldesc *up = fs->f->upvalues; Upvaldesc *up = fs->f->upvalues;
for (i = 0; i < fs->nups; i++) { for (i = 0; i < fs->nups; i++) {
if (eqstr(up[i].name, name)) return i; if (luaS_eqstr(up[i].name, name)) return i;
} }
return -1; /* not found */ return -1; /* not found */
} }
@ -246,7 +246,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
static int searchvar (FuncState *fs, TString *n) { static int searchvar (FuncState *fs, TString *n) {
int i; int i;
for (i=fs->nactvar-1; i >= 0; i--) { for (i=fs->nactvar-1; i >= 0; i--) {
if (eqstr(n, getlocvar(fs, i)->varname)) if (luaS_eqstr(n, getlocvar(fs, i)->varname))
return i; return i;
} }
return -1; /* not found */ return -1; /* not found */
@ -342,7 +342,7 @@ static void closegoto (LexState *ls, int g, Labeldesc *label) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Labellist *gl = &ls->dyd->gt; Labellist *gl = &ls->dyd->gt;
Labeldesc *gt = &gl->arr[g]; Labeldesc *gt = &gl->arr[g];
lua_assert(eqstr(gt->name, label->name)); lua_assert(luaS_eqstr(gt->name, label->name));
if (gt->nactvar < label->nactvar) { if (gt->nactvar < label->nactvar) {
TString *vname = getlocvar(fs, gt->nactvar)->varname; TString *vname = getlocvar(fs, gt->nactvar)->varname;
const char *msg = luaO_pushfstring(ls->L, const char *msg = luaO_pushfstring(ls->L,
@ -369,7 +369,7 @@ static int findlabel (LexState *ls, int g) {
/* check labels in current block for a match */ /* check labels in current block for a match */
for (i = bl->firstlabel; i < dyd->label.n; i++) { for (i = bl->firstlabel; i < dyd->label.n; i++) {
Labeldesc *lb = &dyd->label.arr[i]; Labeldesc *lb = &dyd->label.arr[i];
if (eqstr(lb->name, gt->name)) { /* correct label? */ if (luaS_eqstr(lb->name, gt->name)) { /* correct label? */
if (gt->nactvar > lb->nactvar && if (gt->nactvar > lb->nactvar &&
(bl->upval || dyd->label.n > bl->firstlabel)) (bl->upval || dyd->label.n > bl->firstlabel))
luaK_patchclose(ls->fs, gt->pc, lb->nactvar); luaK_patchclose(ls->fs, gt->pc, lb->nactvar);
@ -403,7 +403,7 @@ static void findgotos (LexState *ls, Labeldesc *lb) {
Labellist *gl = &ls->dyd->gt; Labellist *gl = &ls->dyd->gt;
int i = ls->fs->bl->firstgoto; int i = ls->fs->bl->firstgoto;
while (i < gl->n) { while (i < gl->n) {
if (eqstr(gl->arr[i].name, lb->name)) if (luaS_eqstr(gl->arr[i].name, lb->name))
closegoto(ls, i, lb); closegoto(ls, i, lb);
else else
i++; i++;
@ -461,7 +461,7 @@ static void breaklabel (LexState *ls) {
** message when label name is a reserved word (which can only be 'break') ** message when label name is a reserved word (which can only be 'break')
*/ */
static l_noret undefgoto (LexState *ls, Labeldesc *gt) { static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
const char *msg = (gt->name->tsv.reserved > 0) const char *msg = isreserved(gt->name)
? "<%s> at line %d not inside a loop" ? "<%s> at line %d not inside a loop"
: "no visible label " LUA_QS " for <goto> at line %d"; : "no visible label " LUA_QS " for <goto> at line %d";
msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
@ -493,21 +493,30 @@ static void leaveblock (FuncState *fs) {
/* /*
** adds prototype being created into its parent list of prototypes ** adds a new prototype into list of prototypes
** and codes instruction to create new closure
*/ */
static void codeclosure (LexState *ls, Proto *clp, expdesc *v) { static Proto *addprototype (LexState *ls) {
FuncState *fs = ls->fs->prev; Proto *clp;
Proto *f = fs->f; /* prototype of function creating new closure */ lua_State *L = ls->L;
FuncState *fs = ls->fs;
Proto *f = fs->f; /* prototype of current function */
if (fs->np >= f->sizep) { if (fs->np >= f->sizep) {
int oldsize = f->sizep; int oldsize = f->sizep;
luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions");
MAXARG_Bx, "functions");
while (oldsize < f->sizep) f->p[oldsize++] = NULL; while (oldsize < f->sizep) f->p[oldsize++] = NULL;
} }
f->p[fs->np++] = clp; f->p[fs->np++] = clp = luaF_newproto(L);
luaC_objbarrier(ls->L, f, clp); luaC_objbarrier(L, f, clp);
init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); return clp;
}
/*
** codes instruction to create new closure in parent function
*/
static void codeclosure (LexState *ls, expdesc *v) {
FuncState *fs = ls->fs->prev;
init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1));
luaK_exp2nextreg(fs, v); /* fix it at stack top (for GC) */ luaK_exp2nextreg(fs, v); /* fix it at stack top (for GC) */
} }
@ -529,13 +538,9 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
fs->nactvar = 0; fs->nactvar = 0;
fs->firstlocal = ls->dyd->actvar.n; fs->firstlocal = ls->dyd->actvar.n;
fs->bl = NULL; fs->bl = NULL;
f = luaF_newproto(L); f = fs->f;
fs->f = f;
f->source = ls->source; f->source = ls->source;
f->maxstacksize = 2; /* registers 0/1 are always valid */ f->maxstacksize = 2; /* registers 0/1 are always valid */
/* anchor prototype (to avoid being collected) */
setptvalue2s(L, L->top, f);
incr_top(L);
fs->h = luaH_new(L); fs->h = luaH_new(L);
/* anchor table of constants (to avoid being collected) */ /* anchor table of constants (to avoid being collected) */
sethvalue2s(L, L->top, fs->h); sethvalue2s(L, L->top, fs->h);
@ -568,20 +573,6 @@ static void close_func (LexState *ls) {
anchor_token(ls); anchor_token(ls);
L->top--; /* pop table of constants */ L->top--; /* pop table of constants */
luaC_checkGC(L); luaC_checkGC(L);
L->top--; /* pop prototype (after possible collection) */
}
/*
** opens the main function, which is a regular vararg function with an
** upvalue named LUA_ENV
*/
static void open_mainfunc (LexState *ls, FuncState *fs, BlockCnt *bl) {
expdesc v;
open_func(ls, fs, bl);
fs->f->is_vararg = 1; /* main function is always vararg */
init_exp(&v, VLOCAL, 0);
newupvalue(fs, ls->envn, &v); /* create environment upvalue */
} }
@ -795,8 +786,9 @@ static void body (LexState *ls, expdesc *e, int ismethod, int line) {
/* body -> `(' parlist `)' block END */ /* body -> `(' parlist `)' block END */
FuncState new_fs; FuncState new_fs;
BlockCnt bl; BlockCnt bl;
open_func(ls, &new_fs, &bl); new_fs.f = addprototype(ls);
new_fs.f->linedefined = line; new_fs.f->linedefined = line;
open_func(ls, &new_fs, &bl);
checknext(ls, '('); checknext(ls, '(');
if (ismethod) { if (ismethod) {
new_localvarliteral(ls, "self"); /* create 'self' parameter */ new_localvarliteral(ls, "self"); /* create 'self' parameter */
@ -807,7 +799,7 @@ static void body (LexState *ls, expdesc *e, int ismethod, int line) {
statlist(ls); statlist(ls);
new_fs.f->lastlinedefined = ls->linenumber; new_fs.f->lastlinedefined = ls->linenumber;
check_match(ls, TK_END, TK_FUNCTION, line); check_match(ls, TK_END, TK_FUNCTION, line);
codeclosure(ls, new_fs.f, e); codeclosure(ls, e);
close_func(ls); close_func(ls);
} }
@ -879,8 +871,8 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
*/ */
static void prefixexp (LexState *ls, expdesc *v) { static void primaryexp (LexState *ls, expdesc *v) {
/* prefixexp -> NAME | '(' expr ')' */ /* primaryexp -> NAME | '(' expr ')' */
switch (ls->t.token) { switch (ls->t.token) {
case '(': { case '(': {
int line = ls->linenumber; int line = ls->linenumber;
@ -901,12 +893,12 @@ static void prefixexp (LexState *ls, expdesc *v) {
} }
static void primaryexp (LexState *ls, expdesc *v) { static void suffixedexp (LexState *ls, expdesc *v) {
/* primaryexp -> /* suffixedexp ->
prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int line = ls->linenumber; int line = ls->linenumber;
prefixexp(ls, v); primaryexp(ls, v);
for (;;) { for (;;) {
switch (ls->t.token) { switch (ls->t.token) {
case '.': { /* fieldsel */ case '.': { /* fieldsel */
@ -941,7 +933,7 @@ static void primaryexp (LexState *ls, expdesc *v) {
static void simpleexp (LexState *ls, expdesc *v) { static void simpleexp (LexState *ls, expdesc *v) {
/* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... | /* simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... |
constructor | FUNCTION body | primaryexp */ constructor | FUNCTION body | suffixedexp */
switch (ls->t.token) { switch (ls->t.token) {
case TK_NUMBER: { case TK_NUMBER: {
init_exp(v, VKNUM, 0); init_exp(v, VKNUM, 0);
@ -981,7 +973,7 @@ static void simpleexp (LexState *ls, expdesc *v) {
return; return;
} }
default: { default: {
primaryexp(ls, v); suffixedexp(ls, v);
return; return;
} }
} }
@ -1141,10 +1133,10 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
expdesc e; expdesc e;
check_condition(ls, vkisvar(lh->v.k), "syntax error"); check_condition(ls, vkisvar(lh->v.k), "syntax error");
if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ if (testnext(ls, ',')) { /* assignment -> ',' suffixedexp assignment */
struct LHS_assign nv; struct LHS_assign nv;
nv.prev = lh; nv.prev = lh;
primaryexp(ls, &nv.v); suffixedexp(ls, &nv.v);
if (nv.v.k != VINDEXED) if (nv.v.k != VINDEXED)
check_conflict(ls, lh, &nv.v); check_conflict(ls, lh, &nv.v);
checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS, checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS,
@ -1200,7 +1192,7 @@ static void gotostat (LexState *ls, int pc) {
static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) { static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) {
int i; int i;
for (i = fs->bl->firstlabel; i < ll->n; i++) { for (i = fs->bl->firstlabel; i < ll->n; i++) {
if (eqstr(label, ll->arr[i].name)) { if (luaS_eqstr(label, ll->arr[i].name)) {
const char *msg = luaO_pushfstring(fs->ls->L, const char *msg = luaO_pushfstring(fs->ls->L,
"label " LUA_QS " already defined on line %d", "label " LUA_QS " already defined on line %d",
getstr(label), ll->arr[i].line); getstr(label), ll->arr[i].line);
@ -1210,6 +1202,13 @@ static void checkrepeated (FuncState *fs, Labellist *ll, TString *label) {
} }
/* skip no-op statements */
static void skipnoopstat (LexState *ls) {
while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
statement(ls);
}
static void labelstat (LexState *ls, TString *label, int line) { static void labelstat (LexState *ls, TString *label, int line) {
/* label -> '::' NAME '::' */ /* label -> '::' NAME '::' */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
@ -1219,9 +1218,7 @@ static void labelstat (LexState *ls, TString *label, int line) {
checknext(ls, TK_DBCOLON); /* skip double colon */ checknext(ls, TK_DBCOLON); /* skip double colon */
/* create new entry for this label */ /* create new entry for this label */
l = newlabelentry(ls, ll, label, line, fs->pc); l = newlabelentry(ls, ll, label, line, fs->pc);
/* skip other no-op statements */ skipnoopstat(ls); /* skip other no-op statements */
while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
statement(ls);
if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
/* assume that locals are already out of scope */ /* assume that locals are already out of scope */
ll->arr[l].nactvar = fs->bl->nactvar; ll->arr[l].nactvar = fs->bl->nactvar;
@ -1384,6 +1381,7 @@ static void test_then_block (LexState *ls, int *escapelist) {
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
enterblock(fs, &bl, 0); /* must enter block before 'goto' */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */
gotostat(ls, v.t); /* handle goto/break */ gotostat(ls, v.t); /* handle goto/break */
skipnoopstat(ls); /* skip other no-op statements */
if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ if (block_follow(ls, 0)) { /* 'goto' is the entire block? */
leaveblock(fs); leaveblock(fs);
return; /* and that is it */ return; /* and that is it */
@ -1480,13 +1478,15 @@ static void exprstat (LexState *ls) {
/* stat -> func | assignment */ /* stat -> func | assignment */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
struct LHS_assign v; struct LHS_assign v;
primaryexp(ls, &v.v); suffixedexp(ls, &v.v);
if (v.v.k == VCALL) /* stat -> func */ if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
else { /* stat -> assignment */
v.prev = NULL; v.prev = NULL;
assignment(ls, &v, 1); assignment(ls, &v, 1);
} }
else { /* stat -> func */
check_condition(ls, v.v.k == VCALL, "syntax error");
SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
}
} }
@ -1594,27 +1594,42 @@ static void statement (LexState *ls) {
/* }====================================================================== */ /* }====================================================================== */
Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, /*
** compiles the main function, which is a regular vararg function with an
** upvalue named LUA_ENV
*/
static void mainfunc (LexState *ls, FuncState *fs) {
BlockCnt bl;
expdesc v;
open_func(ls, fs, &bl);
fs->f->is_vararg = 1; /* main function is always vararg */
init_exp(&v, VLOCAL, 0); /* create and... */
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
luaX_next(ls); /* read first token */
statlist(ls); /* parse main body */
check(ls, TK_EOS);
close_func(ls);
}
Closure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar) { Dyndata *dyd, const char *name, int firstchar) {
LexState lexstate; LexState lexstate;
FuncState funcstate; FuncState funcstate;
BlockCnt bl; Closure *cl = luaF_newLclosure(L, 1); /* create main closure */
TString *tname = luaS_new(L, name); /* anchor closure (to avoid being collected) */
setsvalue2s(L, L->top, tname); /* push name to protect it */ setclLvalue(L, L->top, cl);
incr_top(L); incr_top(L);
funcstate.f = cl->l.p = luaF_newproto(L);
funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
lexstate.buff = buff; lexstate.buff = buff;
lexstate.dyd = dyd; lexstate.dyd = dyd;
dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
luaX_setinput(L, &lexstate, z, tname, firstchar); luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
open_mainfunc(&lexstate, &funcstate, &bl); mainfunc(&lexstate, &funcstate);
luaX_next(&lexstate); /* read first token */
statlist(&lexstate); /* main body */
check(&lexstate, TK_EOS);
close_func(&lexstate);
L->top--; /* pop name */
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
/* all scopes should be correctly finished */ /* all scopes should be correctly finished */
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
return funcstate.f; return cl; /* it's on the stack too */
} }

@ -1,11 +1,12 @@
/* /*
** $Id: lstate.c,v 2.92 2011/10/03 17:54:25 roberto Exp $ ** $Id: lstate.c,v 2.98 2012/05/30 12:33:44 roberto Exp $
** Global State ** Global State
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
#include <stddef.h> #include <stddef.h>
#include <string.h>
#define lstate_c #define lstate_c
#define LUA_CORE #define LUA_CORE
@ -41,6 +42,17 @@
#define MEMERRMSG "not enough memory" #define MEMERRMSG "not enough memory"
/*
** a macro to help the creation of a unique random seed when a state is
** created; the seed is used to randomize hashes.
*/
#if !defined(luai_makeseed)
#include <time.h>
#define luai_makeseed() cast(size_t, time(NULL))
#endif
/* /*
** thread state + extra space ** thread state + extra space
*/ */
@ -65,6 +77,28 @@ typedef struct LG {
#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) #define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
/*
** Compute an initial seed as random as possible. In ANSI, rely on
** Address Space Layout Randomization (if present) to increase
** randomness..
*/
#define addbuff(b,p,e) \
{ size_t t = cast(size_t, e); \
memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); }
static unsigned int makeseed (lua_State *L) {
char buff[4 * sizeof(size_t)];
unsigned int h = luai_makeseed();
int p = 0;
addbuff(buff, p, L); /* heap variable */
addbuff(buff, p, &h); /* local variable */
addbuff(buff, p, luaO_nilobject); /* global variable */
addbuff(buff, p, &lua_newstate); /* public function */
lua_assert(p == sizeof(buff));
return luaS_hash(buff, p, h);
}
/* /*
** set GCdebt to a new value keeping the value (totalbytes + GCdebt) ** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
** invariant ** invariant
@ -242,10 +276,11 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->frealloc = f; g->frealloc = f;
g->ud = ud; g->ud = ud;
g->mainthread = L; g->mainthread = L;
g->seed = makeseed(L);
g->uvhead.u.l.prev = &g->uvhead; g->uvhead.u.l.prev = &g->uvhead;
g->uvhead.u.l.next = &g->uvhead; g->uvhead.u.l.next = &g->uvhead;
g->gcrunning = 0; /* no GC while building state */ g->gcrunning = 0; /* no GC while building state */
g->lastmajormem = 0; g->GCestimate = 0;
g->strt.size = 0; g->strt.size = 0;
g->strt.nuse = 0; g->strt.nuse = 0;
g->strt.hash = NULL; g->strt.hash = NULL;
@ -257,6 +292,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->allgc = NULL; g->allgc = NULL;
g->finobj = NULL; g->finobj = NULL;
g->tobefnz = NULL; g->tobefnz = NULL;
g->sweepgc = g->sweepfin = NULL;
g->gray = g->grayagain = NULL; g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL; g->weak = g->ephemeron = g->allweak = NULL;
g->totalbytes = sizeof(LG); g->totalbytes = sizeof(LG);

@ -1,5 +1,5 @@
/* /*
** $Id: lstring.c,v 2.19 2011/05/03 16:01:57 roberto Exp $ ** $Id: lstring.c,v 2.24 2012/05/11 14:14:42 roberto Exp $
** String table (keeps all strings handled by Lua) ** String table (keeps all strings handled by Lua)
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -18,7 +18,49 @@
#include "lstring.h" #include "lstring.h"
/*
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to
** compute its hash
*/
#if !defined(LUAI_HASHLIMIT)
#define LUAI_HASHLIMIT 5
#endif
/*
** equality for long strings
*/
int luaS_eqlngstr (TString *a, TString *b) {
size_t len = a->tsv.len;
lua_assert(a->tsv.tt == LUA_TLNGSTR && b->tsv.tt == LUA_TLNGSTR);
return (a == b) || /* same instance or... */
((len == b->tsv.len) && /* equal length and ... */
(memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
}
/*
** equality for strings
*/
int luaS_eqstr (TString *a, TString *b) {
return (a->tsv.tt == b->tsv.tt) &&
(a->tsv.tt == LUA_TSHRSTR ? eqshrstr(a, b) : luaS_eqlngstr(a, b));
}
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
unsigned int h = seed ^ l;
size_t l1;
size_t step = (l >> LUAI_HASHLIMIT) + 1;
for (l1 = l; l1 >= step; l1 -= step)
h = h ^ ((h<<5) + (h>>2) + cast_byte(str[l1 - 1]));
return h;
}
/*
** resizes the string table
*/
void luaS_resize (lua_State *L, int newsize) { void luaS_resize (lua_State *L, int newsize) {
int i; int i;
stringtable *tb = &G(L)->strt; stringtable *tb = &G(L)->strt;
@ -50,37 +92,49 @@ void luaS_resize (lua_State *L, int newsize) {
} }
static TString *newlstr (lua_State *L, const char *str, size_t l, /*
unsigned int h) { ** creates a new string object
size_t totalsize; /* total size of TString object */ */
GCObject **list; /* (pointer to) list where it will be inserted */ static TString *createstrobj (lua_State *L, const char *str, size_t l,
int tag, unsigned int h, GCObject **list) {
TString *ts; TString *ts;
stringtable *tb = &G(L)->strt; size_t totalsize; /* total size of TString object */
if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
luaM_toobig(L);
if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
luaS_resize(L, tb->size*2); /* too crowded */
totalsize = sizeof(TString) + ((l + 1) * sizeof(char)); totalsize = sizeof(TString) + ((l + 1) * sizeof(char));
list = &tb->hash[lmod(h, tb->size)]; ts = &luaC_newobj(L, tag, totalsize, list, 0)->ts;
ts = &luaC_newobj(L, LUA_TSTRING, totalsize, list, 0)->ts;
ts->tsv.len = l; ts->tsv.len = l;
ts->tsv.hash = h; ts->tsv.hash = h;
ts->tsv.reserved = 0; ts->tsv.extra = 0;
memcpy(ts+1, str, l*sizeof(char)); memcpy(ts+1, str, l*sizeof(char));
((char *)(ts+1))[l] = '\0'; /* ending 0 */ ((char *)(ts+1))[l] = '\0'; /* ending 0 */
tb->nuse++;
return ts; return ts;
} }
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { /*
** creates a new short string, inserting it into string table
*/
static TString *newshrstr (lua_State *L, const char *str, size_t l,
unsigned int h) {
GCObject **list; /* (pointer to) list where it will be inserted */
stringtable *tb = &G(L)->strt;
TString *s;
if (tb->nuse >= cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
luaS_resize(L, tb->size*2); /* too crowded */
list = &tb->hash[lmod(h, tb->size)];
s = createstrobj(L, str, l, LUA_TSHRSTR, h, list);
tb->nuse++;
return s;
}
/*
** checks whether short string exists and reuses it or creates a new one
*/
static TString *internshrstr (lua_State *L, const char *str, size_t l) {
GCObject *o; GCObject *o;
unsigned int h = cast(unsigned int, l); /* seed */ global_State *g = G(L);
size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ unsigned int h = luaS_hash(str, l, g->seed);
size_t l1; for (o = g->strt.hash[lmod(h, g->strt.size)];
for (l1=l; l1>=step; l1-=step) /* compute hash */
h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
o != NULL; o != NULL;
o = gch(o)->next) { o = gch(o)->next) {
TString *ts = rawgco2ts(o); TString *ts = rawgco2ts(o);
@ -92,10 +146,27 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
return ts; return ts;
} }
} }
return newlstr(L, str, l, h); /* not found; create a new string */ return newshrstr(L, str, l, h); /* not found; create a new string */
}
/*
** new string (with explicit length)
*/
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
if (l <= LUAI_MAXSHORTLEN) /* short string? */
return internshrstr(L, str, l);
else {
if (l + 1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
luaM_toobig(L);
return createstrobj(L, str, l, LUA_TLNGSTR, G(L)->seed, NULL);
}
} }
/*
** new zero-terminated string
*/
TString *luaS_new (lua_State *L, const char *str) { TString *luaS_new (lua_State *L, const char *str) {
return luaS_newlstr(L, str, strlen(str)); return luaS_newlstr(L, str, strlen(str));
} }

@ -1,5 +1,5 @@
/* /*
** $Id: lstrlib.c,v 1.173 2011/11/30 18:24:56 roberto Exp $ ** $Id: lstrlib.c,v 1.176 2012/05/23 15:37:09 roberto Exp $
** Standard library for string operations and pattern-matching ** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -119,8 +119,10 @@ static int str_rep (lua_State *L) {
char *p = luaL_buffinitsize(L, &b, totallen); char *p = luaL_buffinitsize(L, &b, totallen);
while (n-- > 1) { /* first n-1 copies (followed by separator) */ while (n-- > 1) { /* first n-1 copies (followed by separator) */
memcpy(p, s, l * sizeof(char)); p += l; memcpy(p, s, l * sizeof(char)); p += l;
if (lsep > 0) { /* avoid empty 'memcpy' (may be expensive) */
memcpy(p, sep, lsep * sizeof(char)); p += lsep; memcpy(p, sep, lsep * sizeof(char)); p += lsep;
} }
}
memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */
luaL_pushresultsize(&b, totallen); luaL_pushresultsize(&b, totallen);
} }
@ -756,9 +758,6 @@ static int str_gsub (lua_State *L) {
#endif #endif
#endif /* } */ #endif /* } */
#define MAX_UINTFRM ((lua_Number)(~(unsigned LUA_INTFRM_T)0))
#define MAX_INTFRM ((lua_Number)((~(unsigned LUA_INTFRM_T)0)/2))
#define MIN_INTFRM (-(lua_Number)((~(unsigned LUA_INTFRM_T)0)/2) - 1)
/* /*
** LUA_FLTFRMLEN is the length modifier for float conversions in ** LUA_FLTFRMLEN is the length modifier for float conversions in
@ -870,18 +869,22 @@ static int str_format (lua_State *L) {
} }
case 'd': case 'i': { case 'd': case 'i': {
lua_Number n = luaL_checknumber(L, arg); lua_Number n = luaL_checknumber(L, arg);
luaL_argcheck(L, (MIN_INTFRM - 1) < n && n < (MAX_INTFRM + 1), arg, LUA_INTFRM_T ni = (LUA_INTFRM_T)n;
lua_Number diff = n - (lua_Number)ni;
luaL_argcheck(L, -1 < diff && diff < 1, arg,
"not a number in proper range"); "not a number in proper range");
addlenmod(form, LUA_INTFRMLEN); addlenmod(form, LUA_INTFRMLEN);
nb = sprintf(buff, form, (LUA_INTFRM_T)n); nb = sprintf(buff, form, ni);
break; break;
} }
case 'o': case 'u': case 'x': case 'X': { case 'o': case 'u': case 'x': case 'X': {
lua_Number n = luaL_checknumber(L, arg); lua_Number n = luaL_checknumber(L, arg);
luaL_argcheck(L, 0 <= n && n < (MAX_UINTFRM + 1), arg, unsigned LUA_INTFRM_T ni = (unsigned LUA_INTFRM_T)n;
lua_Number diff = n - (lua_Number)ni;
luaL_argcheck(L, -1 < diff && diff < 1, arg,
"not a non-negative number in proper range"); "not a non-negative number in proper range");
addlenmod(form, LUA_INTFRMLEN); addlenmod(form, LUA_INTFRMLEN);
nb = sprintf(buff, form, (unsigned LUA_INTFRM_T)n); nb = sprintf(buff, form, ni);
break; break;
} }
case 'e': case 'E': case 'f': case 'e': case 'E': case 'f':

@ -1,5 +1,5 @@
/* /*
** $Id: ltable.c,v 2.67 2011/11/30 12:41:45 roberto Exp $ ** $Id: ltable.c,v 2.71 2012/05/23 15:37:09 roberto Exp $
** Lua tables (hash) ** Lua tables (hash)
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -98,7 +98,15 @@ static Node *mainposition (const Table *t, const TValue *key) {
switch (ttype(key)) { switch (ttype(key)) {
case LUA_TNUMBER: case LUA_TNUMBER:
return hashnum(t, nvalue(key)); return hashnum(t, nvalue(key));
case LUA_TSTRING: case LUA_TLNGSTR: {
TString *s = rawtsvalue(key);
if (s->tsv.extra == 0) { /* no hash? */
s->tsv.hash = luaS_hash(getstr(s), s->tsv.len, s->tsv.hash);
s->tsv.extra = 1; /* now it has its hash */
}
return hashstr(t, rawtsvalue(key));
}
case LUA_TSHRSTR:
return hashstr(t, rawtsvalue(key)); return hashstr(t, rawtsvalue(key));
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
return hashboolean(t, bvalue(key)); return hashboolean(t, bvalue(key));
@ -453,12 +461,13 @@ const TValue *luaH_getint (Table *t, int key) {
/* /*
** search function for strings ** search function for short strings
*/ */
const TValue *luaH_getstr (Table *t, TString *key) { const TValue *luaH_getstr (Table *t, TString *key) {
Node *n = hashstr(t, key); Node *n = hashstr(t, key);
lua_assert(key->tsv.tt == LUA_TSHRSTR);
do { /* check whether `key' is somewhere in the chain */ do { /* check whether `key' is somewhere in the chain */
if (ttisstring(gkey(n)) && eqstr(rawtsvalue(gkey(n)), key)) if (ttisshrstring(gkey(n)) && eqshrstr(rawtsvalue(gkey(n)), key))
return gval(n); /* that's it */ return gval(n); /* that's it */
else n = gnext(n); else n = gnext(n);
} while (n); } while (n);
@ -470,9 +479,9 @@ const TValue *luaH_getstr (Table *t, TString *key) {
** main search function ** main search function
*/ */
const TValue *luaH_get (Table *t, const TValue *key) { const TValue *luaH_get (Table *t, const TValue *key) {
switch (ttypenv(key)) { switch (ttype(key)) {
case LUA_TNIL: return luaO_nilobject; case LUA_TNIL: return luaO_nilobject;
case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); case LUA_TSHRSTR: return luaH_getstr(t, rawtsvalue(key));
case LUA_TNUMBER: { case LUA_TNUMBER: {
int k; int k;
lua_Number n = nvalue(key); lua_Number n = nvalue(key);

@ -1,5 +1,5 @@
/* /*
** $Id: lua.c,v 1.203 2011/12/12 16:34:03 roberto Exp $ ** $Id: lua.c,v 1.205 2012/05/23 15:37:09 roberto Exp $
** Lua stand-alone interpreter ** Lua stand-alone interpreter
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -223,16 +223,11 @@ static int dostring (lua_State *L, const char *s, const char *name) {
static int dolibrary (lua_State *L, const char *name) { static int dolibrary (lua_State *L, const char *name) {
int status; int status;
lua_pushglobaltable(L); lua_getglobal(L, "require");
lua_getfield(L, -1, "require");
lua_pushstring(L, name); lua_pushstring(L, name);
status = docall(L, 1, 1); status = docall(L, 1, 1); /* call 'require(name)' */
if (status == LUA_OK) { if (status == LUA_OK)
lua_setfield(L, -2, name); /* global[name] = require return */ lua_setglobal(L, name); /* global[name] = require return */
lua_pop(L, 1); /* remove global table */
}
else
lua_remove(L, -2); /* remove global table (below error msg.) */
return report(L, status); return report(L, status);
} }

@ -1,5 +1,5 @@
/* /*
** $Id: lundump.c,v 1.71 2011/12/07 10:39:12 lhf Exp $ ** $Id: lundump.c,v 2.22 2012/05/08 13:53:33 roberto Exp $
** load precompiled Lua chunks ** load precompiled Lua chunks
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -27,7 +27,7 @@ typedef struct {
const char* name; const char* name;
} LoadState; } LoadState;
static void error(LoadState* S, const char* why) static l_noret error(LoadState* S, const char* why)
{ {
luaO_pushfstring(S->L,"%s: %s precompiled chunk",S->name,why); luaO_pushfstring(S->L,"%s: %s precompiled chunk",S->name,why);
luaD_throw(S->L,LUA_ERRSYNTAX); luaD_throw(S->L,LUA_ERRSYNTAX);
@ -39,7 +39,7 @@ static void error(LoadState* S, const char* why)
#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) #define LoadVector(S,b,n,size) LoadMem(S,b,n,size)
#if !defined(luai_verifycode) #if !defined(luai_verifycode)
#define luai_verifycode(L,b,f) (f) #define luai_verifycode(L,b,f) /* empty */
#endif #endif
static void LoadBlock(LoadState* S, void* b, size_t size) static void LoadBlock(LoadState* S, void* b, size_t size)
@ -91,7 +91,7 @@ static void LoadCode(LoadState* S, Proto* f)
LoadVector(S,f->code,n,sizeof(Instruction)); LoadVector(S,f->code,n,sizeof(Instruction));
} }
static Proto* LoadFunction(LoadState* S); static void LoadFunction(LoadState* S, Proto* f);
static void LoadConstants(LoadState* S, Proto* f) static void LoadConstants(LoadState* S, Proto* f)
{ {
@ -118,13 +118,18 @@ static void LoadConstants(LoadState* S, Proto* f)
case LUA_TSTRING: case LUA_TSTRING:
setsvalue2n(S->L,o,LoadString(S)); setsvalue2n(S->L,o,LoadString(S));
break; break;
default: lua_assert(0);
} }
} }
n=LoadInt(S); n=LoadInt(S);
f->p=luaM_newvector(S->L,n,Proto*); f->p=luaM_newvector(S->L,n,Proto*);
f->sizep=n; f->sizep=n;
for (i=0; i<n; i++) f->p[i]=NULL; for (i=0; i<n; i++) f->p[i]=NULL;
for (i=0; i<n; i++) f->p[i]=LoadFunction(S); for (i=0; i<n; i++)
{
f->p[i]=luaF_newproto(S->L);
LoadFunction(S,f->p[i]);
}
} }
static void LoadUpvalues(LoadState* S, Proto* f) static void LoadUpvalues(LoadState* S, Proto* f)
@ -163,10 +168,8 @@ static void LoadDebug(LoadState* S, Proto* f)
for (i=0; i<n; i++) f->upvalues[i].name=LoadString(S); for (i=0; i<n; i++) f->upvalues[i].name=LoadString(S);
} }
static Proto* LoadFunction(LoadState* S) static void LoadFunction(LoadState* S, Proto* f)
{ {
Proto* f=luaF_newproto(S->L);
setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
f->linedefined=LoadInt(S); f->linedefined=LoadInt(S);
f->lastlinedefined=LoadInt(S); f->lastlinedefined=LoadInt(S);
f->numparams=LoadByte(S); f->numparams=LoadByte(S);
@ -176,8 +179,6 @@ static Proto* LoadFunction(LoadState* S)
LoadConstants(S,f); LoadConstants(S,f);
LoadUpvalues(S,f); LoadUpvalues(S,f);
LoadDebug(S,f); LoadDebug(S,f);
S->L->top--;
return f;
} }
/* the code below must be consistent with the code in luaU_header */ /* the code below must be consistent with the code in luaU_header */
@ -202,9 +203,10 @@ static void LoadHeader(LoadState* S)
/* /*
** load precompiled chunk ** load precompiled chunk
*/ */
Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) Closure* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
{ {
LoadState S; LoadState S;
Closure* cl;
if (*name=='@' || *name=='=') if (*name=='@' || *name=='=')
S.name=name+1; S.name=name+1;
else if (*name==LUA_SIGNATURE[0]) else if (*name==LUA_SIGNATURE[0])
@ -215,7 +217,19 @@ Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)
S.Z=Z; S.Z=Z;
S.b=buff; S.b=buff;
LoadHeader(&S); LoadHeader(&S);
return luai_verifycode(L,buff,LoadFunction(&S)); cl=luaF_newLclosure(L,1);
setclLvalue(L,L->top,cl); incr_top(L);
cl->l.p=luaF_newproto(L);
LoadFunction(&S,cl->l.p);
if (cl->l.p->sizeupvalues != 1)
{
Proto* p=cl->l.p;
cl=luaF_newLclosure(L,cl->l.p->sizeupvalues);
cl->l.p=p;
setclLvalue(L,L->top-1,cl);
}
luai_verifycode(L,buff,cl->l.p);
return cl;
} }
#define MYINT(s) (s[0]-'0') #define MYINT(s) (s[0]-'0')

@ -1,5 +1,5 @@
/* /*
** $Id: lvm.c,v 2.147 2011/12/07 14:43:55 roberto Exp $ ** $Id: lvm.c,v 2.152 2012/06/08 15:14:04 roberto Exp $
** Lua virtual machine ** Lua virtual machine
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */
@ -60,10 +60,15 @@ int luaV_tostring (lua_State *L, StkId obj) {
static void traceexec (lua_State *L) { static void traceexec (lua_State *L) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
lu_byte mask = L->hookmask; lu_byte mask = L->hookmask;
if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0);
resethookcount(L); if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1); resethookcount(L); /* reset count */
} if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
return; /* do not call hook again (VM yielded, so it did not move) */
}
if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */
if (mask & LUA_MASKLINE) { if (mask & LUA_MASKLINE) {
Proto *p = ci_func(ci)->p; Proto *p = ci_func(ci)->p;
int npc = pcRel(ci->u.l.savedpc, p); int npc = pcRel(ci->u.l.savedpc, p);
@ -71,11 +76,15 @@ static void traceexec (lua_State *L) {
if (npc == 0 || /* call linehook when enter a new function, */ if (npc == 0 || /* call linehook when enter a new function, */
ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */
newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */
luaD_hook(L, LUA_HOOKLINE, newline); luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */
} }
L->oldpc = ci->u.l.savedpc; L->oldpc = ci->u.l.savedpc;
if (L->status == LUA_YIELD) { /* did hook yield? */ if (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook)
L->hookcount = 1; /* undo decrement to zero */
ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yieled */
ci->func = L->top - 1; /* protect stack below results */
luaD_throw(L, LUA_YIELD); luaD_throw(L, LUA_YIELD);
} }
} }
@ -258,7 +267,8 @@ int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2) {
case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_TLCF: return fvalue(t1) == fvalue(t2); case LUA_TLCF: return fvalue(t1) == fvalue(t2);
case LUA_TSTRING: return eqstr(rawtsvalue(t1), rawtsvalue(t2)); case LUA_TSHRSTR: return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
case LUA_TLNGSTR: return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));
case LUA_TUSERDATA: { case LUA_TUSERDATA: {
if (uvalue(t1) == uvalue(t2)) return 1; if (uvalue(t1) == uvalue(t2)) return 1;
else if (L == NULL) return 0; else if (L == NULL) return 0;
@ -293,7 +303,7 @@ void luaV_concat (lua_State *L, int total) {
else if (tsvalue(top-1)->len == 0) /* second operand is empty? */ else if (tsvalue(top-1)->len == 0) /* second operand is empty? */
(void)tostring(L, top - 2); /* result is first operand */ (void)tostring(L, top - 2); /* result is first operand */
else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) { else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) {
setsvalue2s(L, top-2, rawtsvalue(top-1)); /* result is second op. */ setobjs2s(L, top - 2, top - 1); /* result is second op. */
} }
else { else {
/* at least two non-empty string values; get as many as possible */ /* at least two non-empty string values; get as many as possible */
@ -394,7 +404,8 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
int nup = p->sizeupvalues; int nup = p->sizeupvalues;
Upvaldesc *uv = p->upvalues; Upvaldesc *uv = p->upvalues;
int i; int i;
Closure *ncl = luaF_newLclosure(L, p); Closure *ncl = luaF_newLclosure(L, nup);
ncl->l.p = p;
setclLvalue(L, ra, ncl); /* anchor new closure in stack */ setclLvalue(L, ra, ncl); /* anchor new closure in stack */
for (i = 0; i < nup; i++) { /* fill in its upvalues */ for (i = 0; i < nup; i++) { /* fill in its upvalues */
if (uv[i].instack) /* upvalue refers to local variable? */ if (uv[i].instack) /* upvalue refers to local variable? */
@ -500,7 +511,11 @@ void luaV_finishOp (lua_State *L) {
#define Protect(x) { {x;}; base = ci->u.l.base; } #define Protect(x) { {x;}; base = ci->u.l.base; }
#define checkGC(L,c) Protect(luaC_condGC(L, c); luai_threadyield(L);) #define checkGC(L,c) \
Protect( luaC_condGC(L,{L->top = (c); /* limit of live values */ \
luaC_step(L); \
L->top = ci->top;}) /* restore top */ \
luai_threadyield(L); )
#define arith_op(op,tm) { \ #define arith_op(op,tm) { \
@ -593,11 +608,7 @@ void luaV_execute (lua_State *L) {
sethvalue(L, ra, t); sethvalue(L, ra, t);
if (b != 0 || c != 0) if (b != 0 || c != 0)
luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c));
checkGC(L, checkGC(L, ra + 1);
L->top = ra + 1; /* limit of live values */
luaC_step(L);
L->top = ci->top; /* restore top */
)
) )
vmcase(OP_SELF, vmcase(OP_SELF,
StkId rb = RB(i); StkId rb = RB(i);
@ -649,10 +660,7 @@ void luaV_execute (lua_State *L) {
ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */ ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */
rb = b + base; rb = b + base;
setobjs2s(L, ra, rb); setobjs2s(L, ra, rb);
checkGC(L, checkGC(L, (ra >= rb ? ra + 1 : rb));
L->top = (ra >= rb ? ra + 1 : rb); /* limit of live values */
luaC_step(L);
)
L->top = ci->top; /* restore top */ L->top = ci->top; /* restore top */
) )
vmcase(OP_JMP, vmcase(OP_JMP,
@ -830,11 +838,7 @@ void luaV_execute (lua_State *L) {
pushclosure(L, p, cl->upvals, base, ra); /* create a new one */ pushclosure(L, p, cl->upvals, base, ra); /* create a new one */
else else
setclLvalue(L, ra, ncl); /* push cashed closure */ setclLvalue(L, ra, ncl); /* push cashed closure */
checkGC(L, checkGC(L, ra + 1);
L->top = ra + 1; /* limit of live values */
luaC_step(L);
L->top = ci->top; /* restore top */
)
) )
vmcase(OP_VARARG, vmcase(OP_VARARG,
int b = GETARG_B(i) - 1; int b = GETARG_B(i) - 1;

@ -1,6 +1,6 @@
/* /*
** $Id: lzio.c,v 1.34 2011/07/15 12:35:32 roberto Exp $ ** $Id: lzio.c,v 1.35 2012/05/14 13:34:18 roberto Exp $
** a generic input stream interface ** Buffered streams
** See Copyright Notice in lua.h ** See Copyright Notice in lua.h
*/ */

@ -716,6 +716,8 @@ void Console::add_text(color_value color, const std::string &text)
lock_guard <recursive_mutex> g(*wlock); lock_guard <recursive_mutex> g(*wlock);
if (inited) if (inited)
d->print_text(color, text); d->print_text(color, text);
else
fwrite(text.data(), 1, text.size(), stderr);
} }
int Console::get_columns(void) int Console::get_columns(void)

@ -264,16 +264,12 @@ static bool init_run_script(color_ostream &out, lua_State *state, void *info)
return true; return true;
} }
static command_result runLuaScript(color_ostream &out, std::string filename, vector<string> &args) static command_result runLuaScript(color_ostream &out, std::string name, vector<string> &args)
{ {
ScriptArgs data; ScriptArgs data;
data.pcmd = &filename; data.pcmd = &name;
data.pargs = &args; data.pargs = &args;
#ifndef LINUX_BUILD
filename = toLower(filename);
#endif
bool ok = Lua::RunCoreQueryLoop(out, Lua::Core::State, init_run_script, &data); bool ok = Lua::RunCoreQueryLoop(out, Lua::Core::State, init_run_script, &data);
return ok ? CR_OK : CR_FAILURE; return ok ? CR_OK : CR_FAILURE;
@ -610,7 +606,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
{ {
auto filename = getHackPath() + "scripts/" + first + ".lua"; auto filename = getHackPath() + "scripts/" + first + ".lua";
if (fileExists(filename)) if (fileExists(filename))
res = runLuaScript(con, filename, parts); res = runLuaScript(con, first, parts);
else else
con.printerr("%s is not a recognized command.\n", first.c_str()); con.printerr("%s is not a recognized command.\n", first.c_str());
} }
@ -806,14 +802,19 @@ bool Core::Init()
} }
cerr << "Version: " << vinfo->getVersion() << endl; cerr << "Version: " << vinfo->getVersion() << endl;
// Init global object pointers
df::global::InitGlobals();
cerr << "Initializing Console.\n"; cerr << "Initializing Console.\n";
// init the console. // init the console.
bool is_text_mode = false; bool is_text_mode = false;
if(init && init->display.flag.is_set(init_display_flags::TEXT)) if(init && init->display.flag.is_set(init_display_flags::TEXT))
{ {
is_text_mode = true; is_text_mode = true;
con.init(true);
cerr << "Console is not available. Use dfhack-run to send commands.\n";
} }
if(con.init(is_text_mode)) else if(con.init(false))
cerr << "Console is running.\n"; cerr << "Console is running.\n";
else else
fatal ("Console has failed to initialize!\n", false); fatal ("Console has failed to initialize!\n", false);
@ -828,7 +829,6 @@ bool Core::Init()
*/ */
// initialize data defs // initialize data defs
virtual_identity::Init(this); virtual_identity::Init(this);
df::global::InitGlobals();
// initialize common lua context // initialize common lua context
Lua::Core::Init(con); Lua::Core::Init(con);
@ -838,12 +838,15 @@ bool Core::Init()
cerr << "Initializing Plugins.\n"; cerr << "Initializing Plugins.\n";
// create plugin manager // create plugin manager
plug_mgr = new PluginManager(this); plug_mgr = new PluginManager(this);
cerr << "Starting IO thread.\n";
// create IO thread
IODATA *temp = new IODATA; IODATA *temp = new IODATA;
temp->core = this; temp->core = this;
temp->plug_mgr = plug_mgr; temp->plug_mgr = plug_mgr;
if (!is_text_mode)
{
cerr << "Starting IO thread.\n";
// create IO thread
thread * IO = new thread(fIOthread, (void *) temp); thread * IO = new thread(fIOthread, (void *) temp);
}
cerr << "Starting DF input capture thread.\n"; cerr << "Starting DF input capture thread.\n";
// set up hotkey capture // set up hotkey capture
HotkeyMutex = new mutex(); HotkeyMutex = new mutex();

@ -1021,10 +1021,25 @@ static const luaL_Reg dfhack_constructions_funcs[] = {
/***** Internal module *****/ /***** Internal module *****/
static uint32_t getBase() { return Core::getInstance().p->getBase(); } static void *checkaddr(lua_State *L, int idx, bool allow_null = false)
{
luaL_checkany(L, idx);
void *rv;
if (lua_isnil(L, idx))
rv = NULL;
else if (lua_type(L, idx) == LUA_TNUMBER)
rv = (void*)lua_tounsigned(L, idx);
else
rv = Lua::CheckDFObject(L, NULL, idx);
if (!rv && !allow_null)
luaL_argerror(L, idx, "null pointer");
return rv;
}
static int getRebaseDelta() { return Core::getInstance().vinfo->getRebaseDelta(); }
static const LuaWrapper::FunctionReg dfhack_internal_module[] = { static const LuaWrapper::FunctionReg dfhack_internal_module[] = {
WRAP(getBase), WRAP(getRebaseDelta),
{ NULL, NULL } { NULL, NULL }
}; };
@ -1042,7 +1057,7 @@ static int internal_getAddress(lua_State *L)
static int internal_setAddress(lua_State *L) static int internal_setAddress(lua_State *L)
{ {
std::string name = luaL_checkstring(L, 1); std::string name = luaL_checkstring(L, 1);
uint32_t addr = luaL_checkint(L, 2); uint32_t addr = (uint32_t)checkaddr(L, 2, true);
internal_getAddress(L); internal_getAddress(L);
// Set the address // Set the address
@ -1059,9 +1074,21 @@ static int internal_setAddress(lua_State *L)
} }
// Print via printerr, so that it is definitely logged to stderr.log. // Print via printerr, so that it is definitely logged to stderr.log.
std::string msg = stl_sprintf("<global-address name='%s' value='0x%x'/>", name.c_str(), addr); uint32_t iaddr = addr - Core::getInstance().vinfo->getRebaseDelta();
dfhack_printerr(L, msg); fprintf(stderr, "Setting global '%s' to %x (%x)\n", name.c_str(), addr, iaddr);
fflush(stderr);
return 1;
}
static int internal_getVTable(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
uint32_t addr = (uint32_t)Core::getInstance().vinfo->getVTable(name);
if (addr)
lua_pushnumber(L, addr);
else
lua_pushnil(L);
return 1; return 1;
} }
@ -1076,9 +1103,9 @@ static int internal_getMemRanges(lua_State *L)
{ {
lua_newtable(L); lua_newtable(L);
lua_pushnumber(L, (uint32_t)ranges[i].start); lua_pushnumber(L, (uint32_t)ranges[i].start);
lua_setfield(L, -2, "start"); lua_setfield(L, -2, "start_addr");
lua_pushnumber(L, (uint32_t)ranges[i].end); lua_pushnumber(L, (uint32_t)ranges[i].end);
lua_setfield(L, -2, "end"); lua_setfield(L, -2, "end_addr");
lua_pushstring(L, ranges[i].name); lua_pushstring(L, ranges[i].name);
lua_setfield(L, -2, "name"); lua_setfield(L, -2, "name");
lua_pushboolean(L, ranges[i].read); lua_pushboolean(L, ranges[i].read);
@ -1097,10 +1124,100 @@ static int internal_getMemRanges(lua_State *L)
return 1; return 1;
} }
static int internal_memmove(lua_State *L)
{
void *dest = checkaddr(L, 1);
void *src = checkaddr(L, 2);
int size = luaL_checkint(L, 3);
if (size < 0) luaL_argerror(L, 1, "negative size");
memmove(dest, src, size);
return 0;
}
static int internal_memcmp(lua_State *L)
{
void *dest = checkaddr(L, 1);
void *src = checkaddr(L, 2);
int size = luaL_checkint(L, 3);
if (size < 0) luaL_argerror(L, 1, "negative size");
lua_pushinteger(L, memcmp(dest, src, size));
return 1;
}
static int internal_memscan(lua_State *L)
{
uint8_t *haystack = (uint8_t*)checkaddr(L, 1);
int hcount = luaL_checkint(L, 2);
int hstep = luaL_checkint(L, 3);
if (hstep == 0) luaL_argerror(L, 3, "zero step");
void *needle = checkaddr(L, 4);
int nsize = luaL_checkint(L, 5);
if (nsize < 0) luaL_argerror(L, 5, "negative size");
for (int i = 0; i <= hcount; i++)
{
uint8_t *p = haystack + i*hstep;
if (memcmp(p, needle, nsize) == 0) {
lua_pushinteger(L, i);
lua_pushinteger(L, (lua_Integer)p);
return 2;
}
}
lua_pushnil(L);
return 1;
}
static int internal_diffscan(lua_State *L)
{
lua_settop(L, 8);
void *old_data = checkaddr(L, 1);
void *new_data = checkaddr(L, 2);
int start_idx = luaL_checkint(L, 3);
int end_idx = luaL_checkint(L, 4);
int eltsize = luaL_checkint(L, 5);
bool has_oldv = !lua_isnil(L, 6);
bool has_newv = !lua_isnil(L, 7);
bool has_diffv = !lua_isnil(L, 8);
#define LOOP(esz, etype) \
case esz: { \
etype *pold = (etype*)old_data; \
etype *pnew = (etype*)new_data; \
etype oldv = (etype)luaL_optint(L, 6, 0); \
etype newv = (etype)luaL_optint(L, 7, 0); \
etype diffv = (etype)luaL_optint(L, 8, 0); \
for (int i = start_idx; i < end_idx; i++) { \
if (pold[i] == pnew[i]) continue; \
if (has_oldv && pold[i] != oldv) continue; \
if (has_newv && pnew[i] != newv) continue; \
if (has_diffv && etype(pnew[i]-pold[i]) != diffv) continue; \
lua_pushinteger(L, i); return 1; \
} \
break; \
}
switch (eltsize) {
LOOP(1, uint8_t);
LOOP(2, uint16_t);
LOOP(4, uint32_t);
default:
luaL_argerror(L, 5, "invalid element size");
}
#undef LOOP
lua_pushnil(L);
return 1;
}
static const luaL_Reg dfhack_internal_funcs[] = { static const luaL_Reg dfhack_internal_funcs[] = {
{ "getAddress", internal_getAddress }, { "getAddress", internal_getAddress },
{ "setAddress", internal_setAddress }, { "setAddress", internal_setAddress },
{ "getVTable", internal_getVTable },
{ "getMemRanges", internal_getMemRanges }, { "getMemRanges", internal_getMemRanges },
{ "memmove", internal_memmove },
{ "memcmp", internal_memcmp },
{ "memscan", internal_memscan },
{ "diffscan", internal_diffscan },
{ NULL, NULL } { NULL, NULL }
}; };

@ -256,8 +256,11 @@ static int lua_dfhack_color(lua_State *S)
luaL_argerror(S, 1, "invalid color value"); luaL_argerror(S, 1, "invalid color value");
color_ostream *out = Lua::GetOutput(S); color_ostream *out = Lua::GetOutput(S);
if (out) if (out) {
lua_pushinteger(S, (int)out->color());
out->color(color_ostream::color_value(cv)); out->color(color_ostream::color_value(cv));
return 1;
}
return 0; return 0;
} }
@ -423,10 +426,12 @@ static bool convert_to_exception(lua_State *L, int slevel, lua_State *thread = N
// Create a new exception for this thread // Create a new exception for this thread
lua_newtable(L); lua_newtable(L);
luaL_where(L, 1); luaL_where(L, slevel);
lua_setfield(L, -2, "where");
lua_pushstring(L, "coroutine resume failed"); lua_pushstring(L, "coroutine resume failed");
lua_concat(L, 2);
lua_setfield(L, -2, "message"); lua_setfield(L, -2, "message");
lua_getfield(L, -2, "verbose");
lua_setfield(L, -2, "verbose");
lua_swap(L); lua_swap(L);
lua_setfield(L, -2, "cause"); lua_setfield(L, -2, "cause");
} }
@ -480,12 +485,57 @@ static int dfhack_onerror(lua_State *L)
return 1; return 1;
} }
static int dfhack_error(lua_State *L)
{
luaL_checkany(L, 1);
lua_settop(L, 3);
int level = std::max(1, luaL_optint(L, 2, 1));
lua_pushvalue(L, 1);
if (convert_to_exception(L, level))
{
luaL_where(L, level);
lua_setfield(L, -2, "where");
if (!lua_isnil(L, 3))
{
lua_pushvalue(L, 3);
lua_setfield(L, -2, "verbose");
}
}
return lua_error(L);
}
static int dfhack_exception_tostring(lua_State *L) static int dfhack_exception_tostring(lua_State *L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2);
if (lua_isnil(L, 2))
{
lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
lua_getfield(L, -1, "verbose");
lua_insert(L, 2);
lua_settop(L, 2);
}
lua_getfield(L, 1, "verbose");
bool verbose =
lua_toboolean(L, 2) || lua_toboolean(L, 3) ||
(lua_isnil(L, 2) && lua_isnil(L, 3));
int base = lua_gettop(L); int base = lua_gettop(L);
if (verbose || lua_isnil(L, 3))
{
lua_getfield(L, 1, "where");
if (!lua_isstring(L, -1))
lua_pop(L, 1);
}
lua_getfield(L, 1, "message"); lua_getfield(L, 1, "message");
if (!lua_isstring(L, -1)) if (!lua_isstring(L, -1))
{ {
@ -493,15 +543,26 @@ static int dfhack_exception_tostring(lua_State *L)
lua_pushstring(L, "(error message is not a string)"); lua_pushstring(L, "(error message is not a string)");
} }
if (verbose)
{
lua_pushstring(L, "\n"); lua_pushstring(L, "\n");
lua_getfield(L, 1, "stacktrace"); lua_getfield(L, 1, "stacktrace");
if (!lua_isstring(L, -1)) if (!lua_isstring(L, -1))
lua_pop(L, 2); lua_pop(L, 2);
}
lua_pushstring(L, "\ncaused by:\n"); lua_pushstring(L, "\ncaused by:\n");
lua_getfield(L, 1, "cause"); lua_getfield(L, 1, "cause");
if (lua_isnil(L, -1)) if (lua_isnil(L, -1))
lua_pop(L, 2); lua_pop(L, 2);
else if (lua_istable(L, -1))
{
lua_pushcfunction(L, dfhack_exception_tostring);
lua_swap(L);
lua_pushvalue(L, 2);
if (lua_pcall(L, 2, 1, 0) != LUA_OK)
error_tostring(L);
}
else else
error_tostring(L); error_tostring(L);
@ -652,7 +713,12 @@ static int dfhack_coauxwrap (lua_State *L) {
if (Lua::IsSuccess(r)) if (Lua::IsSuccess(r))
return lua_gettop(L); return lua_gettop(L);
else else
{
if (lua_checkstack(L, LUA_MINSTACK))
convert_to_exception(L, 1);
return lua_error(L); return lua_error(L);
}
} }
static int dfhack_cowrap (lua_State *L) { static int dfhack_cowrap (lua_State *L) {
@ -1159,6 +1225,7 @@ static const luaL_Reg dfhack_funcs[] = {
{ "safecall", dfhack_safecall }, { "safecall", dfhack_safecall },
{ "saferesume", dfhack_saferesume }, { "saferesume", dfhack_saferesume },
{ "onerror", dfhack_onerror }, { "onerror", dfhack_onerror },
{ "error", dfhack_error },
{ "call_with_finalizer", dfhack_call_with_finalizer }, { "call_with_finalizer", dfhack_call_with_finalizer },
{ "with_suspend", lua_dfhack_with_suspend }, { "with_suspend", lua_dfhack_with_suspend },
{ "open_plugin", dfhack_open_plugin }, { "open_plugin", dfhack_open_plugin },
@ -1359,6 +1426,8 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
lua_newtable(state); lua_newtable(state);
lua_pushcfunction(state, dfhack_exception_tostring); lua_pushcfunction(state, dfhack_exception_tostring);
lua_setfield(state, -2, "__tostring"); lua_setfield(state, -2, "__tostring");
lua_pushcfunction(state, dfhack_exception_tostring);
lua_setfield(state, -2, "tostring");
lua_dup(state); lua_dup(state);
lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN); lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_EXCEPTION_META_TOKEN);
lua_setfield(state, -2, "exception"); lua_setfield(state, -2, "exception");

@ -715,7 +715,7 @@ static int meta_reinterpret_cast(lua_State *state)
if (lua_isnil(state, 2)) if (lua_isnil(state, 2))
ptr = NULL; ptr = NULL;
else if (lua_isnumber(state, 2)) else if (lua_isnumber(state, 2))
ptr = (void*)lua_tointeger(state, 2); ptr = (void*)lua_tounsigned(state, 2);
else else
{ {
ptr = get_object_internal(state, NULL, 2, false, true); ptr = get_object_internal(state, NULL, 2, false, true);

@ -233,19 +233,55 @@ struct HeapBlock
ULONG reserved; ULONG reserved;
}; };
*/ */
// FIXME: NEEDS TESTING!
// FIXME: <warmist> i noticed that if you enumerate it twice, second time it returns wrong .text region size static void GetDosNames(std::map<string, string> &table)
{
// Partially based on example from msdn:
// Translate path with device name to drive letters.
TCHAR szTemp[512];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(sizeof(szTemp)-1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = " :";
BOOL bFound = FALSE;
TCHAR* p = szTemp;
do
{
// Copy the drive letter to the template string
*szDrive = *p;
// Look up each device name
if (QueryDosDevice(szDrive, szName, MAX_PATH))
table[szName] = szDrive;
// Go to the next NULL character.
while (*p++);
} while (*p); // end of string
}
}
void Process::getMemRanges( vector<t_memrange> & ranges ) void Process::getMemRanges( vector<t_memrange> & ranges )
{ {
MEMORY_BASIC_INFORMATION MBI; MEMORY_BASIC_INFORMATION MBI;
//map<char *, unsigned int> heaps; //map<char *, unsigned int> heaps;
uint64_t movingStart = 0; uint64_t movingStart = 0;
PVOID LastAllocationBase = 0;
map <char *, string> nameMap; map <char *, string> nameMap;
map <string,string> dosDrives;
// get page size // get page size
SYSTEM_INFO si; SYSTEM_INFO si;
GetSystemInfo(&si); GetSystemInfo(&si);
uint64_t PageSize = si.dwPageSize; uint64_t PageSize = si.dwPageSize;
// get dos drive names
GetDosNames(dosDrives);
ranges.clear();
// enumerate heaps // enumerate heaps
// HeapNodes(d->my_pid, heaps); // HeapNodes(d->my_pid, heaps);
// go through all the VM regions, convert them to our internal format // go through all the VM regions, convert them to our internal format
@ -254,52 +290,106 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
movingStart = ((uint64_t)MBI.BaseAddress + MBI.RegionSize); movingStart = ((uint64_t)MBI.BaseAddress + MBI.RegionSize);
if(movingStart % PageSize != 0) if(movingStart % PageSize != 0)
movingStart = (movingStart / PageSize + 1) * PageSize; movingStart = (movingStart / PageSize + 1) * PageSize;
// skip empty regions and regions we share with other processes (DLLs)
if( !(MBI.State & MEM_COMMIT) /*|| !(MBI.Type & MEM_PRIVATE)*/ ) // Skip unallocated address space
if (MBI.State & MEM_FREE)
continue; continue;
// Find range and permissions
t_memrange temp; t_memrange temp;
memset(&temp, 0, sizeof(temp));
temp.start = (char *) MBI.BaseAddress; temp.start = (char *) MBI.BaseAddress;
temp.end = ((char *)MBI.BaseAddress + (uint64_t)MBI.RegionSize); temp.end = ((char *)MBI.BaseAddress + (uint64_t)MBI.RegionSize);
temp.read = MBI.Protect & PAGE_EXECUTE_READ || MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_READONLY || MBI.Protect & PAGE_READWRITE;
temp.write = MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_READWRITE;
temp.execute = MBI.Protect & PAGE_EXECUTE_READ || MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_EXECUTE;
temp.valid = true; temp.valid = true;
if(!GetModuleBaseName(d->my_handle, (HMODULE) temp.start, temp.name, 1024))
if (!(MBI.State & MEM_COMMIT))
temp.valid = false; // reserved address space
else if (MBI.Protect & PAGE_EXECUTE)
temp.execute = true;
else if (MBI.Protect & PAGE_EXECUTE_READ)
temp.execute = temp.read = true;
else if (MBI.Protect & PAGE_EXECUTE_READWRITE)
temp.execute = temp.read = temp.write = true;
else if (MBI.Protect & PAGE_EXECUTE_WRITECOPY)
temp.execute = temp.read = temp.write = true;
else if (MBI.Protect & PAGE_READONLY)
temp.read = true;
else if (MBI.Protect & PAGE_READWRITE)
temp.read = temp.write = true;
else if (MBI.Protect & PAGE_WRITECOPY)
temp.read = temp.write = true;
// Merge areas with the same properties
if (!ranges.empty() && LastAllocationBase == MBI.AllocationBase)
{ {
if(nameMap.count((char *)temp.start)) auto &last = ranges.back();
if (last.end == temp.start &&
last.valid == temp.valid && last.execute == temp.execute &&
last.read == temp.read && last.write == temp.write)
{ {
// potential buffer overflow... last.end = temp.end;
strcpy(temp.name, nameMap[(char *)temp.start].c_str()); continue;
} }
else }
#if 1
// Find the mapped file name
if (GetMappedFileName(d->my_handle, temp.start, temp.name, 1024))
{
int vsize = strlen(temp.name);
// Translate NT name to DOS name
for (auto it = dosDrives.begin(); it != dosDrives.end(); ++it)
{ {
// filter away shared segments without a name. int ksize = it->first.size();
if( !(MBI.Type & MEM_PRIVATE) ) if (strncmp(temp.name, it->first.data(), ksize) != 0)
continue; continue;
else
temp.name[0]=0; memcpy(temp.name, it->second.data(), it->second.size());
memmove(temp.name + it->second.size(), temp.name + ksize, vsize + 1 - ksize);
break;
} }
} }
else else
temp.name[0] = 0;
#else
// Find the executable name
char *base = (char*)MBI.AllocationBase;
if(nameMap.count(base))
{
strncpy(temp.name, nameMap[base].c_str(), 1023);
}
else if(GetModuleBaseName(d->my_handle, (HMODULE)base, temp.name, 1024))
{ {
std::string nm(temp.name);
nameMap[base] = nm;
// this is our executable! (could be generalized to pull segments from libs, but whatever) // this is our executable! (could be generalized to pull segments from libs, but whatever)
if(d->base == temp.start) if(d->base == base)
{ {
for(int i = 0; i < d->pe_header.FileHeader.NumberOfSections; i++) for(int i = 0; i < d->pe_header.FileHeader.NumberOfSections; i++)
{ {
char sectionName[9]; /*char sectionName[9];
memcpy(sectionName,d->sections[i].Name,8); memcpy(sectionName,d->sections[i].Name,8);
sectionName[8] = 0; sectionName[8] = 0;
string nm; string nm;
nm.append(temp.name); nm.append(temp.name);
nm.append(" : "); nm.append(" : ");
nm.append(sectionName); nm.append(sectionName);*/
nameMap[(char *)temp.start + d->sections[i].VirtualAddress] = nm; nameMap[base + d->sections[i].VirtualAddress] = nm;
} }
} }
else
continue;
} }
else
temp.name[0] = 0;
#endif
// Push the entry
LastAllocationBase = MBI.AllocationBase;
ranges.push_back(temp); ranges.push_back(temp);
} }
} }

@ -220,7 +220,7 @@ void ServerConnection::threadFn()
} }
if (memcmp(header.magic, RPCHandshakeHeader::REQUEST_MAGIC, sizeof(header.magic)) || if (memcmp(header.magic, RPCHandshakeHeader::REQUEST_MAGIC, sizeof(header.magic)) ||
header.version != 1) header.version < 1 || header.version > 255)
{ {
out << "In RPC server: invalid handshake header." << endl; out << "In RPC server: invalid handshake header." << endl;
return; return;

@ -379,19 +379,19 @@ static command_result GetWorldInfo(color_ostream &stream,
if (!ui || !world || !Core::getInstance().isWorldLoaded()) if (!ui || !world || !Core::getInstance().isWorldLoaded())
return CR_NOT_FOUND; return CR_NOT_FOUND;
t_gamemodes mode; df::game_type gt = game_type::DWARF_MAIN;
if (!Core::getInstance().getWorld()->ReadGameMode(mode)) if (df::global::gametype)
mode.g_type = GAMETYPE_DWARF_MAIN; gt = *df::global::gametype;
out->set_save_dir(world->cur_savegame.save_dir); out->set_save_dir(world->cur_savegame.save_dir);
if (world->world_data->name.has_name) if (world->world_data->name.has_name)
describeName(out->mutable_world_name(), &world->world_data->name); describeName(out->mutable_world_name(), &world->world_data->name);
switch (mode.g_type) switch (gt)
{ {
case GAMETYPE_DWARF_MAIN: case game_type::DWARF_MAIN:
case GAMETYPE_DWARF_RECLAIM: case game_type::DWARF_RECLAIM:
out->set_mode(GetWorldInfoOut::MODE_DWARF); out->set_mode(GetWorldInfoOut::MODE_DWARF);
out->set_civ_id(ui->civ_id); out->set_civ_id(ui->civ_id);
out->set_site_id(ui->site_id); out->set_site_id(ui->site_id);
@ -399,7 +399,7 @@ static command_result GetWorldInfo(color_ostream &stream,
out->set_race_id(ui->race_id); out->set_race_id(ui->race_id);
break; break;
case GAMETYPE_ADVENTURE_MAIN: case game_type::ADVENTURE_MAIN:
out->set_mode(GetWorldInfoOut::MODE_ADVENTURE); out->set_mode(GetWorldInfoOut::MODE_ADVENTURE);
if (auto unit = vector_get(world->units.active, 0)) if (auto unit = vector_get(world->units.active, 0))
@ -423,7 +423,7 @@ static command_result GetWorldInfo(color_ostream &stream,
} }
break; break;
case GAMETYPE_VIEW_LEGENDS: case game_type::VIEW_LEGENDS:
out->set_mode(GetWorldInfoOut::MODE_LEGENDS); out->set_mode(GetWorldInfoOut::MODE_LEGENDS);
break; break;

@ -111,6 +111,8 @@ namespace DFHack
void printerr(const char *format, ...); void printerr(const char *format, ...);
void vprinterr(const char *format, va_list args); void vprinterr(const char *format, va_list args);
/// Get color
color_value color() { return cur_color; }
/// Set color (ANSI color number) /// Set color (ANSI color number)
void color(color_value c); void color(color_value c);
/// Reset color to default /// Reset color to default

@ -35,32 +35,15 @@ distribution.
#include "Module.h" #include "Module.h"
#include <ostream> #include <ostream>
#include "DataDefs.h"
namespace DFHack namespace DFHack
{ {
/** typedef df::game_mode GameMode;
* \ingroup grp_world typedef df::game_type GameType;
*/
enum GameMode #define GAMEMODE_ADVENTURE df::enums::game_mode::ADVENTURE
{
GAMEMODE_DWARF,
GAMEMODE_ADVENTURE,
GAMEMODENUM,
GAMEMODE_NONE
};
/**
* \ingroup grp_world
*/
enum GameType
{
GAMETYPE_DWARF_MAIN,
GAMETYPE_ADVENTURE_MAIN,
GAMETYPE_VIEW_LEGENDS,
GAMETYPE_DWARF_RECLAIM,
GAMETYPE_DWARF_ARENA,
GAMETYPE_ADVENTURE_ARENA,
GAMETYPENUM,
GAMETYPE_NONE
};
/** /**
* \ingroup grp_world * \ingroup grp_world
*/ */

@ -49,6 +49,10 @@ function dfhack.pcall(f, ...)
return xpcall(f, dfhack.onerror, ...) return xpcall(f, dfhack.onerror, ...)
end end
function qerror(msg, level)
dfhack.error(msg, (level or 1) + 1, false)
end
function dfhack.with_finalize(...) function dfhack.with_finalize(...)
return dfhack.call_with_finalizer(0,true,...) return dfhack.call_with_finalizer(0,true,...)
end end
@ -64,6 +68,8 @@ function dfhack.with_temp_object(obj,fn,...)
return dfhack.call_with_finalizer(1,true,call_delete,obj,fn,obj,...) return dfhack.call_with_finalizer(1,true,call_delete,obj,fn,obj,...)
end end
dfhack.exception.__index = dfhack.exception
-- Module loading -- Module loading
function mkmodule(module,env) function mkmodule(module,env)
@ -273,19 +279,24 @@ end
-- Command scripts -- Command scripts
dfhack.scripts = dfhack.scripts or {} dfhack.internal.scripts = dfhack.internal.scripts or {}
local scripts = dfhack.internal.scripts
local hack_path = dfhack.getHackPath()
function dfhack.run_script(file,...) function dfhack.run_script(name,...)
local env = dfhack.scripts[file] local key = string.lower(name)
local file = hack_path..'scripts/'..name..'.lua'
local env = scripts[key]
if env == nil then if env == nil then
env = {} env = {}
setmetatable(env, { __index = base_env }) setmetatable(env, { __index = base_env })
dfhack.scripts[file] = env
end end
local f,perr = loadfile(file, 't', env) local f,perr = loadfile(file, 't', env)
if f == nil then if f == nil then
error(perr) error(perr)
end end
scripts[key] = env
return f(...) return f(...)
end end

@ -0,0 +1,493 @@
-- Utilities for offset scan scripts.
local _ENV = mkmodule('memscan')
local utils = require('utils')
-- A length-checked view on a memory buffer
CheckedArray = CheckedArray or {}
function CheckedArray.new(type,saddr,eaddr)
local data = df.reinterpret_cast(type,saddr)
local esize = data:sizeof()
local count = math.floor((eaddr-saddr)/esize)
local obj = {
type = type, start = saddr, size = count*esize,
esize = esize, data = data, count = count
}
setmetatable(obj, CheckedArray)
return obj
end
function CheckedArray:__len()
return self.count
end
function CheckedArray:__index(idx)
if type(idx) == number then
if idx >= self.count then
error('Index out of bounds: '..tostring(idx))
end
return self.data[idx]
else
return CheckedArray[idx]
end
end
function CheckedArray:__newindex(idx, val)
if idx >= self.count then
error('Index out of bounds: '..tostring(idx))
end
self.data[idx] = val
end
function CheckedArray:addr2idx(addr, round)
local off = addr - self.start
if off >= 0 and off < self.size and (round or (off % self.esize) == 0) then
return math.floor(off / self.esize), off
end
end
function CheckedArray:idx2addr(idx)
if idx >= 0 and idx < self.count then
return self.start + idx*self.esize
end
end
-- Search methods
function CheckedArray:find(data,sidx,eidx,reverse)
local dcnt = #data
sidx = math.max(0, sidx or 0)
eidx = math.min(self.count, eidx or self.count)
if (eidx - sidx) >= dcnt and dcnt > 0 then
return dfhack.with_temp_object(
df.new(self.type, dcnt),
function(buffer)
for i = 1,dcnt do
buffer[i-1] = data[i]
end
local cnt = eidx - sidx - dcnt
local step = self.esize
local sptr = self.start + sidx*step
local ksize = dcnt*step
if reverse then
local idx, addr = dfhack.internal.memscan(sptr + cnt*step, cnt, -step, buffer, ksize)
if idx then
return sidx + cnt - idx, addr
end
else
local idx, addr = dfhack.internal.memscan(sptr, cnt, step, buffer, ksize)
if idx then
return sidx + idx, addr
end
end
end
)
end
end
function CheckedArray:find_one(data,sidx,eidx,reverse)
local idx, addr = self:find(data,sidx,eidx,reverse)
if idx then
-- Verify this is the only match
if reverse then
eidx = idx+#data-1
else
sidx = idx+1
end
if self:find(data,sidx,eidx,reverse) then
return nil
end
end
return idx, addr
end
function CheckedArray:list_changes(old_arr,old_val,new_val,delta)
if old_arr.type ~= self.type or old_arr.count ~= self.count then
error('Incompatible arrays')
end
local eidx = self.count
local optr = old_arr.start
local nptr = self.start
local esize = self.esize
local rv
local sidx = 0
while true do
local idx = dfhack.internal.diffscan(optr, nptr, sidx, eidx, esize, old_val, new_val, delta)
if not idx then
break
end
rv = rv or {}
rv[#rv+1] = idx
sidx = idx+1
end
return rv
end
function CheckedArray:filter_changes(prev_list,old_arr,old_val,new_val,delta)
if old_arr.type ~= self.type or old_arr.count ~= self.count then
error('Incompatible arrays')
end
local eidx = self.count
local optr = old_arr.start
local nptr = self.start
local esize = self.esize
local rv
for i=1,#prev_list do
local idx = prev_list[i]
if idx < 0 or idx >= eidx then
error('Index out of bounds: '..idx)
end
if dfhack.internal.diffscan(optr, nptr, idx, idx+1, esize, old_val, new_val, delta) then
rv = rv or {}
rv[#rv+1] = idx
end
end
return rv
end
-- A raw memory area class
MemoryArea = MemoryArea or {}
MemoryArea.__index = MemoryArea
function MemoryArea.new(astart, aend)
local obj = {
start_addr = astart, end_addr = aend, size = aend - astart,
int8_t = CheckedArray.new('int8_t',astart,aend),
uint8_t = CheckedArray.new('uint8_t',astart,aend),
int16_t = CheckedArray.new('int16_t',astart,aend),
uint16_t = CheckedArray.new('uint16_t',astart,aend),
int32_t = CheckedArray.new('int32_t',astart,aend),
uint32_t = CheckedArray.new('uint32_t',astart,aend),
float = CheckedArray.new('float',astart,aend)
}
setmetatable(obj, MemoryArea)
return obj
end
function MemoryArea:__gc()
df.delete(self.buffer)
end
function MemoryArea:__tostring()
return string.format('<MemoryArea: %x..%x>', self.start_addr, self.end_addr)
end
function MemoryArea:contains_range(start,size)
return size >= 0 and start >= self.start_addr and (start+size) <= self.end_addr
end
function MemoryArea:contains_obj(obj,count)
local size, base = df.sizeof(obj)
return size and base and self:contains_range(base, size*(count or 1))
end
function MemoryArea:clone()
local buffer = df.new('int8_t', self.size)
local _, base = buffer:sizeof()
local rv = MemoryArea.new(base, base+self.size)
rv.buffer = buffer
return rv
end
function MemoryArea:copy_from(area2)
if area2.size ~= self.size then
error('Size mismatch')
end
dfhack.internal.memmove(self.start_addr, area2.start_addr, self.size)
end
function MemoryArea:delete()
setmetatable(self, nil)
df.delete(self.buffer)
for k,v in pairs(self) do self[k] = nil end
end
-- Static data segment search
local function find_data_segment()
local data_start, data_end
for i,mem in ipairs(dfhack.internal.getMemRanges()) do
if data_end then
if mem.start_addr == data_end and mem.read and mem.write then
data_end = mem.end_addr
else
break
end
elseif mem.read and mem.write
and (string.match(mem.name,'/dwarfort%.exe$')
or string.match(mem.name,'/Dwarf_Fortress$')
or string.match(mem.name,'Dwarf Fortress%.exe'))
then
data_start = mem.start_addr
data_end = mem.end_addr
end
end
return data_start, data_end
end
function get_data_segment()
local s, e = find_data_segment()
if s and e then
return MemoryArea.new(s, e)
end
end
-- Register a found offset, or report an error.
function found_offset(name,val)
local cval = dfhack.internal.getAddress(name)
if not val then
print('Could not find offset '..name)
if not cval and not utils.prompt_yes_no('Continue with the script?') then
qerror('User quit')
end
return
end
if df.isvalid(val) then
_,val = val:sizeof()
end
print(string.format('Found offset %s: %x', name, val))
if cval then
if cval ~= val then
error(string.format('Mismatch with the current value: %x',val))
end
else
dfhack.internal.setAddress(name, val)
local ival = val - dfhack.internal.getRebaseDelta()
local entry = string.format("<global-address name='%s' value='0x%x'/>\n", name, ival)
local ccolor = dfhack.color(COLOR_LIGHTGREEN)
dfhack.print(entry)
dfhack.color(ccolor)
io.stdout:write(entry)
io.stdout:flush()
end
end
-- Offset of a field within struct
function field_ref(handle,...)
local items = table.pack(...)
for i=1,items.n-1 do
handle = handle[items[i]]
end
return handle:_field(items[items.n])
end
function field_offset(type,...)
local handle = df.reinterpret_cast(type,1)
local _,addr = df.sizeof(field_ref(handle,...))
return addr-1
end
function MemoryArea:object_by_field(addr,type,...)
if not addr then
return nil
end
local base = addr - field_offset(type,...)
local obj = df.reinterpret_cast(type, base)
if not self:contains_obj(obj) then
obj = nil
end
return obj, base
end
-- Validation
function is_valid_vector(ref,size)
local ints = df.reinterpret_cast('uint32_t', ref)
return ints[0] <= ints[1] and ints[1] <= ints[2]
and (size == nil or (ints[1] - ints[0]) % size == 0)
end
-- Difference search helper
DiffSearcher = DiffSearcher or {}
DiffSearcher.__index = DiffSearcher
function DiffSearcher.new(area)
local obj = { area = area }
setmetatable(obj, DiffSearcher)
return obj
end
function DiffSearcher:begin_search(type)
self.type = type
self.old_value = nil
self.search_sets = nil
if not self.save_area then
self.save_area = self.area:clone()
end
end
function DiffSearcher:advance_search(new_value,delta)
if self.search_sets then
local nsets = #self.search_sets
local ovec = self.save_area[self.type]
local nvec = self.area[self.type]
local new_set
if nsets > 0 then
local last_set = self.search_sets[nsets]
new_set = nvec:filter_changes(last_set,ovec,self.old_value,new_value,delta)
else
new_set = nvec:list_changes(ovec,self.old_value,new_value,delta)
end
if new_set then
self.search_sets[nsets+1] = new_set
self.old_value = new_value
self.save_area:copy_from(self.area)
return #new_set, new_set
end
else
self.old_value = new_value
self.search_sets = {}
self.save_area:copy_from(self.area)
return #self.save_area[self.type], nil
end
end
function DiffSearcher:reset()
self.search_sets = nil
if self.save_area then
self.save_area:delete()
self.save_area = nil
end
end
function DiffSearcher:idx2addr(idx)
return self.area[self.type]:idx2addr(idx)
end
-- Interactive search utility
function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
enum = enum or {}
-- Loop for restarting search from scratch
while true do
print('\n'..prompt)
self:begin_search(data_type)
local found = false
local ccursor = 0
-- Loop through choices
while true do
print('')
local ok, value, delta = condition_cb(ccursor)
ccursor = ccursor + 1
if not ok then
break
end
-- Search for it in the memory
local cnt, set = self:advance_search(value, delta)
if not cnt then
dfhack.printerr(' Converged to zero candidates; probably a mistake somewhere.')
break
elseif set and cnt == 1 then
-- To confirm, wait for two 1-candidate results in a row
if found then
local addr = self:idx2addr(set[1])
print(string.format(' Confirmed address: %x\n', addr))
return addr, set[1]
else
found = true
end
end
print(' '..cnt..' candidates remaining.')
end
if not utils.prompt_yes_no('\nRetry search from the start?') then
return nil
end
end
end
function DiffSearcher:find_menu_cursor(prompt,data_type,choices,enum)
enum = enum or {}
return self:find_interactive(
prompt, data_type,
function(ccursor)
local choice
-- Select the next value to search for
if type(choices) == 'function' then
choice = choices(ccursor)
if not choice then
return false
end
else
choice = choices[(ccursor % #choices) + 1]
end
-- Ask the user to select it
if enum ~= 'noprompt' then
local cname = enum[choice] or choice
if type(choice) == 'string' and type(cname) == 'number' then
choice, cname = cname, choice
end
if cname ~= choice then
cname = cname..' ('..choice..')'
end
print(' Please select: '..cname)
if not utils.prompt_yes_no(' Continue?', true) then
return false
end
end
return true, choice
end
)
end
function DiffSearcher:find_counter(prompt,data_type,delta,action_prompt)
delta = delta or 1
return self:find_interactive(
prompt, data_type,
function(ccursor)
if ccursor > 0 then
print(" "..(action_prompt or 'Please do the action.'))
end
if not utils.prompt_yes_no(' Continue?', true) then
return false
end
return true, nil, delta
end
)
end
-- Screen size
function get_screen_size()
-- Use already known globals
if dfhack.internal.getAddress('init') then
local d = df.global.init.display
return d.grid_x, d.grid_y
end
if dfhack.internal.getAddress('gps') then
local g = df.global.gps
return g.dimx, g.dimy
end
-- Parse stdout.log for resize notifications
io.stdout:flush()
local w,h = 80,25
for line in io.lines('stdout.log') do
local cw, ch = string.match(line, '^Resizing grid to (%d+)x(%d+)$')
if cw and ch then
w, h = tonumber(cw), tonumber(ch)
end
end
return w,h
end
return _ENV

@ -361,4 +361,50 @@ function insert_or_update(vector,item,field,cmp)
return added,cur,pos return added,cur,pos
end end
-- Ask a yes-no question
function prompt_yes_no(msg,default)
local prompt = msg
if default == nil then
prompt = prompt..' (y/n): '
elseif default then
prompt = prompt..' (y/n)[y]: '
else
prompt = prompt..' (y/n)[n]: '
end
while true do
local rv = dfhack.lineedit(prompt)
if rv then
if string.match(rv,'^[Yy]') then
return true
elseif string.match(rv,'^[Nn]') then
return false
elseif rv == 'abort' then
qerror('User abort in utils.prompt_yes_no()')
elseif rv == '' and default ~= nil then
return default
end
end
end
end
-- Ask for input with check function
function prompt_input(prompt,check,quit_str)
quit_str = quit_str or '~~~'
while true do
local rv = dfhack.lineedit(prompt)
if rv == quit_str then
return nil
end
local rtbl = table.pack(check(rv))
if rtbl[1] then
return table.unpack(rtbl,2,rtbl.n)
end
end
end
function check_number(text)
local nv = tonumber(text)
return nv ~= nil, nv
end
return _ENV return _ENV

@ -85,7 +85,7 @@ World::World()
if(df::global::current_weather) if(df::global::current_weather)
d->StartedWeather = true; d->StartedWeather = true;
if (df::global::game_mode && df::global::control_mode) if (df::global::gamemode && df::global::gametype)
d->StartedMode = true; d->StartedMode = true;
d->Inited = true; d->Inited = true;
@ -132,8 +132,8 @@ bool World::ReadGameMode(t_gamemodes& rd)
{ {
if(d->Inited && d->StartedMode) if(d->Inited && d->StartedMode)
{ {
rd.g_mode = (DFHack::GameMode)*df::global::control_mode; rd.g_mode = (DFHack::GameMode)*df::global::gamemode;
rd.g_type = (DFHack::GameType)*df::global::game_mode; rd.g_type = (DFHack::GameType)*df::global::gametype;
return true; return true;
} }
return false; return false;
@ -142,8 +142,8 @@ bool World::WriteGameMode(const t_gamemodes & wr)
{ {
if(d->Inited && d->StartedMode) if(d->Inited && d->StartedMode)
{ {
*df::global::control_mode = wr.g_mode; *df::global::gamemode = wr.g_mode;
*df::global::game_mode = wr.g_type; *df::global::gametype = wr.g_type;
return true; return true;
} }
return false; return false;

@ -101,8 +101,8 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan
switch (event) { switch (event) {
case SC_MAP_LOADED: case SC_MAP_LOADED:
deinit_map(out); deinit_map(out);
if (df::global::game_mode && if (df::global::gamemode &&
*df::global::game_mode == GAMEMODE_DWARF) *df::global::gamemode == game_mode::DWARF)
init_map(out); init_map(out);
break; break;
case SC_MAP_UNLOADED: case SC_MAP_UNLOADED:

@ -10,7 +10,7 @@ using namespace std;
#include "modules/World.h" #include "modules/World.h"
#include <stdlib.h> #include <stdlib.h>
using namespace DFHack; using namespace DFHack;
using namespace df::enums;
command_result mode (color_ostream &out, vector <string> & parameters); command_result mode (color_ostream &out, vector <string> & parameters);
@ -44,28 +44,28 @@ void printCurrentModes(t_gamemodes gm, Console & con)
con << "Current game type:\t" << gm.g_type << " ("; con << "Current game type:\t" << gm.g_type << " (";
switch(gm.g_type) switch(gm.g_type)
{ {
case GAMETYPE_DWARF_MAIN: case game_type::DWARF_MAIN:
con << "Fortress)" << endl; con << "Fortress)" << endl;
break; break;
case GAMETYPE_ADVENTURE_MAIN: case game_type::ADVENTURE_MAIN:
con << "Adventurer)" << endl; con << "Adventurer)" << endl;
break; break;
case GAMETYPE_VIEW_LEGENDS: case game_type::VIEW_LEGENDS:
con << "Legends)" << endl; con << "Legends)" << endl;
break; break;
case GAMETYPE_DWARF_RECLAIM: case game_type::DWARF_RECLAIM:
con << "Reclaim)" << endl; con << "Reclaim)" << endl;
break; break;
case GAMETYPE_DWARF_ARENA: case game_type::DWARF_ARENA:
con << "Arena)" << endl; con << "Arena)" << endl;
break; break;
case GAMETYPE_ADVENTURE_ARENA: case game_type::ADVENTURE_ARENA:
con << "Arena - control creature)" << endl; con << "Arena - control creature)" << endl;
break; break;
case GAMETYPENUM: case game_type::num:
con << "INVALID)" << endl; con << "INVALID)" << endl;
break; break;
case GAMETYPE_NONE: case game_type::NONE:
con << "NONE)" << endl; con << "NONE)" << endl;
break; break;
default: default:
@ -75,16 +75,16 @@ void printCurrentModes(t_gamemodes gm, Console & con)
con << "Current game mode:\t" << gm.g_mode << " ("; con << "Current game mode:\t" << gm.g_mode << " (";
switch (gm.g_mode) switch (gm.g_mode)
{ {
case GAMEMODE_DWARF: case game_mode::DWARF:
con << "Dwarf)" << endl; con << "Dwarf)" << endl;
break; break;
case GAMEMODE_ADVENTURE: case game_mode::ADVENTURE:
con << "Adventure)" << endl; con << "Adventure)" << endl;
break; break;
case GAMEMODENUM: case game_mode::num:
con << "INVALID)" << endl; con << "INVALID)" << endl;
break; break;
case GAMEMODE_NONE: case game_mode::NONE:
con << "NONE)" << endl; con << "NONE)" << endl;
break; break;
default: default:
@ -132,7 +132,7 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
{ {
if(!abuse) if(!abuse)
{ {
if( gm.g_mode == GAMEMODE_NONE || gm.g_type == GAMETYPE_VIEW_LEGENDS) if( gm.g_mode == game_mode::NONE || gm.g_type == game_type::VIEW_LEGENDS)
{ {
out.printerr("It is not safe to set modes in menus.\n"); out.printerr("It is not safe to set modes in menus.\n");
return CR_FAILURE; return CR_FAILURE;
@ -163,24 +163,24 @@ command_result mode (color_ostream &out_, vector <string> & parameters)
switch(select) switch(select)
{ {
case 0: case 0:
gm.g_mode = GAMEMODE_DWARF; gm.g_mode = game_mode::DWARF;
gm.g_type = GAMETYPE_DWARF_MAIN; gm.g_type = game_type::DWARF_MAIN;
break; break;
case 1: case 1:
gm.g_mode = GAMEMODE_ADVENTURE; gm.g_mode = game_mode::ADVENTURE;
gm.g_type = GAMETYPE_ADVENTURE_MAIN; gm.g_type = game_type::ADVENTURE_MAIN;
break; break;
case 2: case 2:
gm.g_mode = GAMEMODE_DWARF; gm.g_mode = game_mode::DWARF;
gm.g_type = GAMETYPE_DWARF_ARENA; gm.g_type = game_type::DWARF_ARENA;
break; break;
case 3: case 3:
gm.g_mode = GAMEMODE_ADVENTURE; gm.g_mode = game_mode::ADVENTURE;
gm.g_type = GAMETYPE_ADVENTURE_ARENA; gm.g_type = game_type::ADVENTURE_ARENA;
break; break;
case 4: case 4:
gm.g_mode = GAMEMODE_DWARF; gm.g_mode = game_mode::DWARF;
gm.g_type = GAMETYPE_DWARF_RECLAIM; gm.g_type = game_type::DWARF_RECLAIM;
break; break;
} }
} }

@ -90,7 +90,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
World *World = Core::getInstance().getWorld(); World *World = Core::getInstance().getWorld();
t_gamemodes gm; t_gamemodes gm;
World->ReadGameMode(gm); World->ReadGameMode(gm);
if(gm.g_mode == GAMEMODE_DWARF) if(gm.g_mode == game_mode::DWARF)
{ {
// if the map is revealed and we're in fortress mode, force the game to pause. // if the map is revealed and we're in fortress mode, force the game to pause.
if(revealed == REVEALED) if(revealed == REVEALED)
@ -193,12 +193,12 @@ command_result reveal(color_ostream &out, vector<string> & params)
} }
t_gamemodes gm; t_gamemodes gm;
World->ReadGameMode(gm); World->ReadGameMode(gm);
if(gm.g_mode == GAMEMODE_ADVENTURE) if(gm.g_mode == game_mode::ADVENTURE)
{ {
revealAdventure(out); revealAdventure(out);
return CR_OK; return CR_OK;
} }
if(gm.g_mode != GAMEMODE_DWARF) if(gm.g_mode != game_mode::DWARF)
{ {
con.printerr("Only in fortress mode.\n"); con.printerr("Only in fortress mode.\n");
return CR_FAILURE; return CR_FAILURE;
@ -272,7 +272,7 @@ command_result unreveal(color_ostream &out, vector<string> & params)
} }
t_gamemodes gm; t_gamemodes gm;
World->ReadGameMode(gm); World->ReadGameMode(gm);
if(gm.g_mode != GAMEMODE_DWARF) if(gm.g_mode != game_mode::DWARF)
{ {
con.printerr("Only in fortress mode.\n"); con.printerr("Only in fortress mode.\n");
return CR_FAILURE; return CR_FAILURE;
@ -350,7 +350,7 @@ command_result revflood(color_ostream &out, vector<string> & params)
} }
t_gamemodes gm; t_gamemodes gm;
World->ReadGameMode(gm); World->ReadGameMode(gm);
if(gm.g_type != GAMETYPE_DWARF_MAIN && gm.g_mode != GAMEMODE_DWARF ) if(gm.g_type != game_type::DWARF_MAIN && gm.g_mode != game_mode::DWARF )
{ {
out.printerr("Only in proper dwarf mode.\n"); out.printerr("Only in proper dwarf mode.\n");
return CR_FAILURE; return CR_FAILURE;

@ -687,6 +687,7 @@ sub render_item_number {
my $initvalue = $item->getAttribute('init-value'); my $initvalue = $item->getAttribute('init-value');
my $typename = $item->getAttribute('type-name'); my $typename = $item->getAttribute('type-name');
undef $typename if ($meta and $meta eq 'bitfield-type'); undef $typename if ($meta and $meta eq 'bitfield-type');
my $g = $global_types{$typename} if ($typename);
$typename = rb_ucase($typename) if $typename; $typename = rb_ucase($typename) if $typename;
$typename = $classname if (!$typename and $subtype and $subtype eq 'enum'); # compound enum $typename = $classname if (!$typename and $subtype and $subtype eq 'enum'); # compound enum
@ -695,6 +696,7 @@ sub render_item_number {
$initvalue ||= 'nil' if $typename; $initvalue ||= 'nil' if $typename;
$subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'bitfield' or $subtype eq 'enum'); $subtype = $item->getAttribute('base-type') if (!$subtype or $subtype eq 'bitfield' or $subtype eq 'enum');
$subtype = $g->getAttribute('base-type') if ($g);
$subtype = 'int32_t' if (!$subtype); $subtype = 'int32_t' if (!$subtype);
if ($subtype eq 'int64_t') { if ($subtype eq 'int64_t') {

@ -483,6 +483,7 @@ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr)
static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr)
{ {
rb_raise(*rb_eRuntimeError, "not implemented"); rb_raise(*rb_eRuntimeError, "not implemented");
return Qnil;
} }
static VALUE rb_dfget_global_address(VALUE self, VALUE name) static VALUE rb_dfget_global_address(VALUE self, VALUE name)
@ -763,13 +764,18 @@ static VALUE rb_dfmemory_bitarray_set(VALUE self, VALUE addr, VALUE idx, VALUE v
static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE a1, VALUE a2, VALUE a3) static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE a1, VALUE a2, VALUE a3)
{ {
#ifdef WIN32 #ifdef WIN32
__thiscall int (__fastcall *fptr)(char **me, int dummy_edx, int, int, int, int);
#endif #else
int (*fptr)(char **me, int, int, int, int); int (*fptr)(char **me, int, int, int, int);
#endif
char **that = (char**)rb_num2ulong(cppobj); char **that = (char**)rb_num2ulong(cppobj);
int ret; int ret;
fptr = (decltype(fptr))*(void**)(*that + rb_num2ulong(cppvoff)); fptr = (decltype(fptr))*(void**)(*that + rb_num2ulong(cppvoff));
ret = fptr(that, rb_num2ulong(a0), rb_num2ulong(a1), rb_num2ulong(a2), rb_num2ulong(a3)); ret = fptr(that,
#ifdef WIN32
0,
#endif
rb_num2ulong(a0), rb_num2ulong(a1), rb_num2ulong(a2), rb_num2ulong(a3));
return rb_int2inum(ret); return rb_int2inum(ret);
} }
@ -836,7 +842,8 @@ static void ruby_bind_dfhack(void) {
// load the default ruby-level definitions // load the default ruby-level definitions
int state=0; int state=0;
rb_load_protect(rb_str_new2("./hack/ruby.rb"), Qfalse, &state); // windows cmake installs files in df/, linux installs files in df/hack/
rb_eval_string_protect("File.exist?('hack/ruby.rb') ? load('hack/ruby.rb') : load('ruby.rb')", &state);
if (state) if (state)
dump_rb_error(); dump_rb_error();
} }

@ -1048,7 +1048,7 @@ end
end end
# load autogen'd file # load autogen'd file
require 'hack/ruby-autogen' File.exist?('hack/ruby-autogen.rb') ? require('hack/ruby-autogen') : require('ruby-autogen')
# load optional user-specified startup file # load optional user-specified startup file
load 'ruby_custom.rb' if File.exist?('ruby_custom.rb') load 'ruby_custom.rb' if File.exist?('ruby_custom.rb')

@ -111,7 +111,7 @@ command_result df_seedwatch(color_ostream &out, vector<string>& parameters)
w->ReadGameMode(gm);// FIXME: check return value w->ReadGameMode(gm);// FIXME: check return value
// if game mode isn't fortress mode // if game mode isn't fortress mode
if(gm.g_mode != GAMEMODE_DWARF || gm.g_type != GAMETYPE_DWARF_MAIN) if(gm.g_mode != game_mode::DWARF || gm.g_type != game_type::DWARF_MAIN)
{ {
// just print the help // just print the help
printHelp(out); printHelp(out);
@ -299,7 +299,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
t_gamemodes gm; t_gamemodes gm;
w->ReadGameMode(gm);// FIXME: check return value w->ReadGameMode(gm);// FIXME: check return value
// if game mode isn't fortress mode // if game mode isn't fortress mode
if(gm.g_mode != GAMEMODE_DWARF || gm.g_type != GAMETYPE_DWARF_MAIN) if(gm.g_mode != game_mode::DWARF || gm.g_type != game_type::DWARF_MAIN)
{ {
// stop running. // stop running.
running = false; running = false;

@ -0,0 +1,938 @@
-- Find some offsets for linux.
local utils = require 'utils'
local ms = require 'memscan'
local is_known = dfhack.internal.getAddress
local os_type = dfhack.getOSType()
local force_scan = {}
for _,v in ipairs({...}) do
force_scan[v] = true
end
collectgarbage()
print[[
WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS.
Running this script on a new DF version will NOT
MAKE IT RUN CORRECTLY if any data structures
changed, thus possibly leading to CRASHES AND/OR
PERMANENT SAVE CORRUPTION.
Finding the first few globals requires this script to be
started immediately after loading the game, WITHOUT
first loading a world. The rest expect a loaded save,
not a fresh embark. Finding current_weather requires
a special save previously processed with devel/prepare-save
on a DF version with working dfhack.
The script expects vanilla game configuration, without
any custom tilesets or init file changes. Never unpause
the game unless instructed. When done, quit the game
without saving using 'die'.
]]
if not utils.prompt_yes_no('Proceed?') then
return
end
-- Data segment location
local data = ms.get_data_segment()
if not data then
qerror('Could not find data segment')
end
print('\nData section: '..tostring(data))
if data.size < 5000000 then
qerror('Data segment too short.')
end
local searcher = ms.DiffSearcher.new(data)
local function validate_offset(name,validator,addr,tname,...)
local obj = data:object_by_field(addr,tname,...)
if obj and not validator(obj) then
obj = nil
end
ms.found_offset(name,obj)
end
local function zoomed_searcher(startn, end_or_sz)
if force_scan.nozoom then
return nil
end
local sv = is_known(startn)
if not sv then
return nil
end
local ev
if type(end_or_sz) == 'number' then
ev = sv + end_or_sz
if end_or_sz < 0 then
sv, ev = ev, sv
end
else
ev = is_known(end_or_sz)
if not ev then
return nil
end
end
sv = sv - (sv % 4)
ev = ev + 3
ev = ev - (ev % 4)
if data:contains_range(sv, ev-sv) then
return ms.DiffSearcher.new(ms.MemoryArea.new(sv,ev))
end
end
local function exec_finder(finder, names)
if type(names) ~= 'table' then
names = { names }
end
local search = force_scan['all']
for _,v in ipairs(names) do
if force_scan[v] or not is_known(v) then
search = true
end
end
if search then
if not dfhack.safecall(finder) then
if not utils.prompt_yes_no('Proceed with the rest of the script?') then
searcher:reset()
qerror('Quit')
end
end
else
print('Already known: '..table.concat(names,', '))
end
end
local ordinal_names = {
[0] = '1st entry',
[1] = '2nd entry',
[2] = '3rd entry'
}
setmetatable(ordinal_names, {
__index = function(self,idx) return (idx+1)..'th entry' end
})
local function list_index_choices(length_func)
return function(id)
if id > 0 then
local ok, len = pcall(length_func)
if not ok then
len = 5
elseif len > 10 then
len = 10
end
return id % len
else
return 0
end
end
end
--
-- Cursor group
--
local function find_cursor()
print('\nPlease navigate to the title screen to find cursor.')
if not utils.prompt_yes_no('Proceed?', true) then
return false
end
-- Unpadded version
local idx, addr = data.int32_t:find_one{
-30000, -30000, -30000,
-30000, -30000, -30000, -30000, -30000, -30000,
df.game_mode.NONE, df.game_type.NONE
}
if idx then
ms.found_offset('cursor', addr)
ms.found_offset('selection_rect', addr + 12)
ms.found_offset('gamemode', addr + 12 + 24)
ms.found_offset('gametype', addr + 12 + 24 + 4)
return true
end
-- Padded version
idx, addr = data.int32_t:find_one{
-30000, -30000, -30000, 0,
-30000, -30000, -30000, -30000, -30000, -30000, 0, 0,
df.game_mode.NONE, 0, 0, 0, df.game_type.NONE
}
if idx then
ms.found_offset('cursor', addr)
ms.found_offset('selection_rect', addr + 0x10)
ms.found_offset('gamemode', addr + 0x30)
ms.found_offset('gametype', addr + 0x40)
return true
end
dfhack.printerr('Could not find cursor.')
return false
end
--
-- Announcements
--
local function find_announcements()
local idx, addr = data.int32_t:find_one{
25, 25, 31, 31, 24, 24, 40, 40, 40, 40, 40, 40, 40
}
if idx then
ms.found_offset('announcements', addr)
return
end
dfhack.printerr('Could not find announcements.')
end
--
-- d_init
--
local function is_valid_d_init(di)
if di.sky_tile ~= 178 then
print('Sky tile expected 178, found: '..di.sky_tile)
if not utils.prompt_yes_no('Ignore?') then
return false
end
end
local ann = is_known 'announcements'
local size,ptr = di:sizeof()
if ann and ptr+size ~= ann then
print('Announcements not immediately after d_init.')
if not utils.prompt_yes_no('Ignore?') then
return false
end
end
return true
end
local function find_d_init()
local idx, addr = data.int16_t:find_one{
1,0, 2,0, 5,0, 25,0, -- path_cost
4,4, -- embark_rect
20,1000,1000,1000,1000 -- store_dist
}
if idx then
validate_offset('d_init', is_valid_d_init, addr, df.d_init, 'path_cost')
return
end
dfhack.printerr('Could not find d_init')
end
--
-- gview
--
local function find_gview()
local vs_vtable = dfhack.internal.getVTable('viewscreenst')
if not vs_vtable then
dfhack.printerr('Cannot search for gview - no viewscreenst vtable.')
return
end
local idx, addr = data.uint32_t:find_one{0, vs_vtable}
if idx then
ms.found_offset('gview', addr)
return
end
dfhack.printerr('Could not find gview')
end
--
-- enabler
--
local function is_valid_enabler(e)
if not ms.is_valid_vector(e.textures.raws, 4)
or not ms.is_valid_vector(e.text_system, 4)
then
dfhack.printerr('Vector layout check failed.')
return false
end
return true
end
local function find_enabler()
-- Data from data/init/colors.txt
local colors = {
0, 0, 0, 0, 0, 128, 0, 128, 0,
0, 128, 128, 128, 0, 0, 128, 0, 128,
128, 128, 0, 192, 192, 192, 128, 128, 128,
0, 0, 255, 0, 255, 0, 0, 255, 255,
255, 0, 0, 255, 0, 255, 255, 255, 0,
255, 255, 255
}
for i = 1,#colors do colors[i] = colors[i]/255 end
local idx, addr = data.float:find_one(colors)
if idx then
validate_offset('enabler', is_valid_enabler, addr, df.enabler, 'ccolor')
return
end
dfhack.printerr('Could not find enabler')
end
--
-- gps
--
local function is_valid_gps(g)
if g.clipx[0] < 0 or g.clipx[0] > g.clipx[1] or g.clipx[1] >= g.dimx then
dfhack.printerr('Invalid clipx: ', g.clipx[0], g.clipx[1], g.dimx)
end
if g.clipy[0] < 0 or g.clipy[0] > g.clipy[1] or g.clipy[1] >= g.dimy then
dfhack.printerr('Invalid clipy: ', g.clipy[0], g.clipy[1], g.dimy)
end
return true
end
local function find_gps()
print('\nPlease ensure the mouse cursor is not over the game window.')
if not utils.prompt_yes_no('Proceed?', true) then
return
end
local zone
if os_type == 'windows' or os_type == 'linux' then
zone = zoomed_searcher('cursor', 0x1000)
elseif os_type == 'darwin' then
zone = zoomed_searcher('enabler', 0x1000)
end
zone = zone or searcher
local w,h = ms.get_screen_size()
local idx, addr = zone.area.int32_t:find_one{w, h, -1, -1}
if idx then
validate_offset('gps', is_valid_gps, addr, df.graphic, 'dimx')
return
end
dfhack.printerr('Could not find gps')
end
--
-- World
--
local function is_valid_world(world)
if not ms.is_valid_vector(world.units.all, 4)
or not ms.is_valid_vector(world.units.bad, 4)
or not ms.is_valid_vector(world.history.figures, 4)
or not ms.is_valid_vector(world.cur_savegame.map_features, 4)
then
dfhack.printerr('Vector layout check failed.')
return false
end
if #world.units.all == 0 or #world.units.all ~= #world.units.bad then
print('Different or zero size of units.all and units.bad:'..#world.units.all..' vs '..#world.units.bad)
if not utils.prompt_yes_no('Ignore?') then
return false
end
end
return true
end
local function find_world()
local addr = searcher:find_menu_cursor([[
Searching for world. Please open the stockpile creation
menu, and select different types as instructed below:]],
'int32_t',
{ 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' },
df.stockpile_category
)
validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type')
end
--
-- UI
--
local function is_valid_ui(ui)
if not ms.is_valid_vector(ui.economic_stone, 1)
or not ms.is_valid_vector(ui.dipscripts, 4)
then
dfhack.printerr('Vector layout check failed.')
return false
end
if ui.follow_item ~= -1 or ui.follow_unit ~= -1 then
print('Invalid follow state: '..ui.follow_item..', '..ui.follow_unit)
return false
end
return true
end
local function find_ui()
local addr = searcher:find_menu_cursor([[
Searching for ui. Please open the designation
menu, and switch modes as instructed below:]],
'int16_t',
{ 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', 'DesignateUpStair',
'DesignateDownStair', 'DesignateUpDownStair', 'DesignateUpRamp', 'DesignateChopTrees' },
df.ui_sidebar_mode
)
validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode')
end
--
-- ui_sidebar_menus
--
local function is_valid_ui_sidebar_menus(usm)
if not ms.is_valid_vector(usm.workshop_job.choices_all, 4)
or not ms.is_valid_vector(usm.workshop_job.choices_visible, 4)
then
dfhack.printerr('Vector layout check failed.')
return false
end
if #usm.workshop_job.choices_all == 0
or #usm.workshop_job.choices_all ~= #usm.workshop_job.choices_visible then
print('Different or zero size of visible and all choices:'..
#usm.workshop_job.choices_all..' vs '..#usm.workshop_job.choices_visible)
if not utils.prompt_yes_no('Ignore?') then
return false
end
end
return true
end
local function find_ui_sidebar_menus()
local addr = searcher:find_menu_cursor([[
Searching for ui_sidebar_menus. Please switch to 'q' mode,
select a Mason, Craftsdwarfs, or Carpenters workshop, open
the Add Job menu, and move the cursor within:]],
'int32_t',
{ 0, 1, 2, 3, 4, 5, 6 },
ordinal_names
)
validate_offset('ui_sidebar_menus', is_valid_ui_sidebar_menus,
addr, df.ui_sidebar_menus, 'workshop_job', 'cursor')
end
--
-- ui_build_selector
--
local function is_valid_ui_build_selector(ubs)
if not ms.is_valid_vector(ubs.requirements, 4)
or not ms.is_valid_vector(ubs.choices, 4)
then
dfhack.printerr('Vector layout check failed.')
return false
end
if ubs.building_type ~= df.building_type.Trap
or ubs.building_subtype ~= df.trap_type.PressurePlate then
print('Invalid building type and subtype:'..ubs.building_type..','..ubs.building_subtype)
return false
end
return true
end
local function find_ui_build_selector()
local addr = searcher:find_menu_cursor([[
Searching for ui_build_selector. Please start constructing
a pressure plate, and enable creatures. Then change the min
weight as requested, remembering that the ui truncates the
number, so when it shows "Min (5000df", it means 50000:]],
'int32_t',
{ 50000, 49000, 48000, 47000, 46000, 45000, 44000 }
)
validate_offset('ui_build_selector', is_valid_ui_build_selector,
addr, df.ui_build_selector, 'plate_info', 'unit_min')
end
--
-- init
--
local function is_valid_init(i)
-- derived from curses_*.png image sizes presumably
if i.font.small_font_dispx ~= 8 or i.font.small_font_dispy ~= 12 or
i.font.large_font_dispx ~= 10 or i.font.large_font_dispy ~= 12 then
print('Unexpected font sizes: ',
i.font.small_font_dispx, i.font.small_font_dispy,
i.font.large_font_dispx, i.font.large_font_dispy)
if not utils.prompt_yes_no('Ignore?') then
return false
end
end
return true
end
local function find_init()
local zone
if os_type == 'windows' then
zone = zoomed_searcher('ui_build_selector', 0x3000)
elseif os_type == 'linux' or os_type == 'darwin' then
zone = zoomed_searcher('d_init', -0x2000)
end
zone = zone or searcher
local idx, addr = zone.area.int32_t:find_one{250, 150, 15, 0}
if idx then
validate_offset('init', is_valid_init, addr, df.init, 'input', 'hold_time')
return
end
local w,h = ms.get_screen_size()
local idx, addr = zone.area.int32_t:find_one{w, h}
if idx then
validate_offset('init', is_valid_init, addr, df.init, 'display', 'grid_x')
return
end
dfhack.printerr('Could not find init')
end
--
-- current_weather
--
local function find_current_weather()
print('\nPlease load the save previously processed with prepare-save.')
if not utils.prompt_yes_no('Proceed?', true) then
return
end
local zone
if os_type == 'windows' then
zone = zoomed_searcher('crime_next_id', 512)
elseif os_type == 'darwin' then
zone = zoomed_searcher('cursor', -64)
elseif os_type == 'linux' then
zone = zoomed_searcher('ui_building_assign_type', -512)
end
zone = zone or searcher
local wbytes = {
2, 1, 0, 2, 0,
1, 2, 1, 0, 0,
2, 0, 2, 1, 2,
1, 2, 0, 1, 1,
2, 0, 1, 0, 2
}
local idx, addr = zone.area.int8_t:find_one(wbytes)
if idx then
ms.found_offset('current_weather', addr)
return
end
dfhack.printerr('Could not find current_weather - must be a wrong save.')
end
--
-- ui_menu_width
--
local function find_ui_menu_width()
local addr = searcher:find_menu_cursor([[
Searching for ui_menu_width. Please exit to the main
dwarfmode menu, then use Tab to do as instructed below:]],
'int8_t',
{ 2, 3, 1 },
{ [2] = 'switch to the most usual [mapmap][menu] layout',
[3] = 'hide the menu completely',
[1] = 'switch to the default [map][menu][map] layout' }
)
ms.found_offset('ui_menu_width', addr)
-- NOTE: Assume that the vars are adjacent, as always
ms.found_offset('ui_area_map_width', addr+1)
end
--
-- ui_selected_unit
--
local function find_ui_selected_unit()
if not is_known 'world' then
dfhack.printerr('Cannot search for ui_selected_unit: no world')
return
end
for i,unit in ipairs(df.global.world.units.active) do
dfhack.units.setNickname(unit, i)
end
local addr = searcher:find_menu_cursor([[
Searching for ui_selected_unit. Please activate the 'v'
mode, point it at units, and enter their numeric nickname
into the prompts below:]],
'int32_t',
function()
return utils.prompt_input(' Enter index: ', utils.check_number)
end,
'noprompt'
)
ms.found_offset('ui_selected_unit', addr)
end
--
-- ui_unit_view_mode
--
local function find_ui_unit_view_mode()
local addr = searcher:find_menu_cursor([[
Searching for ui_unit_view_mode. Having selected a unit
with 'v', switch the pages as requested:]],
'int32_t',
{ 'General', 'Inventory', 'Preferences', 'Wounds' },
df.ui_unit_view_mode.T_value
)
ms.found_offset('ui_unit_view_mode', addr)
end
--
-- ui_look_cursor
--
local function look_item_list_count()
return #df.global.ui_look_list.items
end
local function find_ui_look_cursor()
local addr = searcher:find_menu_cursor([[
Searching for ui_look_cursor. Please activate the 'k'
mode, find a tile with many items or units on the ground,
and select list entries as instructed:]],
'int32_t',
list_index_choices(look_item_list_count),
ordinal_names
)
ms.found_offset('ui_look_cursor', addr)
end
--
-- ui_building_item_cursor
--
local function building_item_list_count()
return #df.global.world.selected_building.contained_items
end
local function find_ui_building_item_cursor()
local addr = searcher:find_menu_cursor([[
Searching for ui_building_item_cursor. Please activate the 't'
mode, find a cluttered workshop, trade depot, or other building
with many contained items, and select as instructed:]],
'int32_t',
list_index_choices(building_item_list_count),
ordinal_names
)
ms.found_offset('ui_building_item_cursor', addr)
end
--
-- ui_workshop_in_add
--
local function find_ui_workshop_in_add()
local addr = searcher:find_menu_cursor([[
Searching for ui_workshop_in_add. Please activate the 'q'
mode, find a workshop without jobs (or delete jobs),
and do as instructed below.
NOTE: If not done after first 3-4 steps, resize the game window.]],
'int8_t',
{ 1, 0 },
{ [1] = 'enter the add job menu',
[0] = 'add job, thus exiting the menu' }
)
ms.found_offset('ui_workshop_in_add', addr)
end
--
-- ui_workshop_job_cursor
--
local function workshop_job_list_count()
return #df.global.world.selected_building.jobs
end
local function find_ui_workshop_job_cursor()
local addr = searcher:find_menu_cursor([[
Searching for ui_workshop_job_cursor. Please activate the 'q'
mode, find a workshop with many jobs, and select as instructed:]],
'int32_t',
list_index_choices(workshop_job_list_count),
ordinal_names
)
ms.found_offset('ui_workshop_job_cursor', addr)
end
--
-- ui_building_in_assign
--
local function find_ui_building_in_assign()
local addr = searcher:find_menu_cursor([[
Searching for ui_building_in_assign. Please activate
the 'q' mode, select a room building (e.g. a bedroom)
and do as instructed below.
NOTE: If not done after first 3-4 steps, resize the game window.]],
'int8_t',
{ 1, 0 },
{ [1] = 'enter the Assign owner menu',
[0] = 'press Esc to exit assign' }
)
ms.found_offset('ui_building_in_assign', addr)
end
--
-- ui_building_in_resize
--
local function find_ui_building_in_resize()
local addr = searcher:find_menu_cursor([[
Searching for ui_building_in_resize. Please activate
the 'q' mode, select a room building (e.g. a bedroom)
and do as instructed below.
NOTE: If not done after first 3-4 steps, resize the game window.]],
'int8_t',
{ 1, 0 },
{ [1] = 'enter the Resize room mode',
[0] = 'press Esc to exit resize' }
)
ms.found_offset('ui_building_in_resize', addr)
end
--
-- window_x
--
local function find_window_x()
local addr = searcher:find_counter([[
Searching for window_x. Please exit to main dwarfmode menu,
scroll to the LEFT edge, then do as instructed:]],
'int32_t', 10,
'Please press Right to scroll right one step.'
)
ms.found_offset('window_x', addr)
end
--
-- window_y
--
local function find_window_y()
local addr = searcher:find_counter([[
Searching for window_y. Please exit to main dwarfmode menu,
scroll to the TOP edge, then do as instructed:]],
'int32_t', 10,
'Please press Down to scroll down one step.'
)
ms.found_offset('window_y', addr)
end
--
-- window_z
--
local function find_window_z()
local addr = searcher:find_counter([[
Searching for window_z. Please exit to main dwarfmode menu,
scroll to a Z level near surface, then do as instructed below.
NOTE: If not done after first 3-4 steps, resize the game window.]],
'int32_t', -1,
"Please press '>' to scroll one Z level down."
)
ms.found_offset('window_z', addr)
end
--
-- cur_year
--
local function find_cur_year()
local zone
if os_type == 'windows' then
zone = zoomed_searcher('formation_next_id', 32)
elseif os_type == 'darwin' then
zone = zoomed_searcher('cursor', -32)
elseif os_type == 'linux' then
zone = zoomed_searcher('ui_building_assign_type', -512)
end
if not zone then
dfhack.printerr('Cannot search for cur_year - prerequisites missing.')
return
end
local yvalue = utils.prompt_input('Please enter current in-game year: ', utils.check_number)
local idx, addr = zone.area.int32_t:find_one{yvalue}
if idx then
ms.found_offset('cur_year', addr)
return
end
dfhack.printerr('Could not find cur_year')
end
--
-- cur_year_tick
--
local function find_cur_year_tick()
local zone
if os_type == 'windows' then
zone = zoomed_searcher('artifact_next_id', -32)
else
zone = zoomed_searcher('cur_year', 128)
end
if not zone then
dfhack.printerr('Cannot search for cur_year_tick - prerequisites missing.')
return
end
local addr = zone:find_counter([[
Searching for cur_year_tick. Please exit to main dwarfmode
menu, then do as instructed below:]],
'int32_t', 1,
"Please press '.' to step the game one frame."
)
ms.found_offset('cur_year_tick', addr)
end
--
-- process_jobs
--
local function get_process_zone()
if os_type == 'windows' then
return zoomed_searcher('ui_workshop_job_cursor', 'ui_building_in_resize')
elseif os_type == 'linux' or os_type == 'darwin' then
return zoomed_searcher('cur_year', 'cur_year_tick')
end
end
local function find_process_jobs()
local zone = get_process_zone() or searcher
local addr = zone:find_menu_cursor([[
Searching for process_jobs. Please do as instructed below:]],
'int8_t',
{ 1, 0 },
{ [1] = 'designate a building to be constructed, e.g a bed',
[0] = 'step or unpause the game to reset the flag' }
)
ms.found_offset('process_jobs', addr)
end
--
-- process_dig
--
local function find_process_dig()
local zone = get_process_zone() or searcher
local addr = zone:find_menu_cursor([[
Searching for process_dig. Please do as instructed below:]],
'int8_t',
{ 1, 0 },
{ [1] = 'designate a tile to be mined out',
[0] = 'step or unpause the game to reset the flag' }
)
ms.found_offset('process_dig', addr)
end
--
-- pause_state
--
local function find_pause_state()
local zone
if os_type == 'linux' or os_type == 'darwin' then
zone = zoomed_searcher('ui_look_cursor', 32)
elseif os_type == 'windows' then
zone = zoomed_searcher('ui_workshop_job_cursor', 80)
end
zone = zone or searcher
local addr = zone:find_menu_cursor([[
Searching for pause_state. Please do as instructed below:]],
'int8_t',
{ 1, 0 },
{ [1] = 'PAUSE the game',
[0] = 'UNPAUSE the game' }
)
ms.found_offset('pause_state', addr)
end
--
-- MAIN FLOW
--
print('\nInitial globals (need title screen):\n')
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
exec_finder(find_announcements, 'announcements')
exec_finder(find_d_init, 'd_init')
exec_finder(find_gview, 'gview')
exec_finder(find_enabler, 'enabler')
exec_finder(find_gps, 'gps')
print('\nCompound globals (need loaded world):\n')
exec_finder(find_world, 'world')
exec_finder(find_ui, 'ui')
exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
exec_finder(find_ui_build_selector, 'ui_build_selector')
exec_finder(find_init, 'init')
print('\nPrimitive globals:\n')
exec_finder(find_current_weather, 'current_weather')
exec_finder(find_ui_menu_width, { 'ui_menu_width', 'ui_area_map_width' })
exec_finder(find_ui_selected_unit, 'ui_selected_unit')
exec_finder(find_ui_unit_view_mode, 'ui_unit_view_mode')
exec_finder(find_ui_look_cursor, 'ui_look_cursor')
exec_finder(find_ui_building_item_cursor, 'ui_building_item_cursor')
exec_finder(find_ui_workshop_in_add, 'ui_workshop_in_add')
exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor')
exec_finder(find_ui_building_in_assign, 'ui_building_in_assign')
exec_finder(find_ui_building_in_resize, 'ui_building_in_resize')
exec_finder(find_window_x, 'window_x')
exec_finder(find_window_y, 'window_y')
exec_finder(find_window_z, 'window_z')
print('\nUnpausing globals:\n')
exec_finder(find_cur_year, 'cur_year')
exec_finder(find_cur_year_tick, 'cur_year_tick')
exec_finder(find_process_jobs, 'process_jobs')
exec_finder(find_process_dig, 'process_dig')
exec_finder(find_pause_state, 'pause_state')
print('\nDone. Now add newly-found globals to symbols.xml.')
searcher:reset()

@ -0,0 +1,71 @@
-- Prepare the current save for use with devel/find-offsets.
df.global.pause_state = true
--[[print('Placing anchor...')
do
local wp = df.global.ui.waypoints
for _,pt in ipairs(wp.points) do
if pt.name == 'dfhack_anchor' then
print('Already placed.')
goto found
end
end
local x,y,z = pos2xyz(df.global.cursor)
if not x then
error("Place cursor at your preferred anchor point.")
end
local id = wp.next_point_id
wp.next_point_id = id + 1
wp.points:insert('#',{
new = true, id = id, name = 'dfhack_anchor',
comment=(x..','..y..','..z),
tile = string.byte('!'), fg_color = COLOR_LIGHTRED, bg_color = COLOR_BLUE,
pos = xyz2pos(x,y,z)
})
::found::
end]]
print('Nicknaming units...')
for i,unit in ipairs(df.global.world.units.active) do
dfhack.units.setNickname(unit, i..':'..unit.id)
end
print('Setting weather...')
local wbytes = {
2, 1, 0, 2, 0,
1, 2, 1, 0, 0,
2, 0, 2, 1, 2,
1, 2, 0, 1, 1,
2, 0, 1, 0, 2
}
for i=0,4 do
for j = 0,4 do
df.global.current_weather[i][j] = (wbytes[i*5+j+1] or 2)
end
end
local yearstr = df.global.cur_year..','..df.global.cur_year_tick
print('Cur year and tick: '..yearstr)
dfhack.persistent.save{
key='prepare-save/cur_year',
value=yearstr,
ints={df.global.cur_year, df.global.cur_year_tick}
}
-- Save
dfhack.run_script('quicksave')

@ -116,8 +116,7 @@ if opt then
if opt == '--fix' then if opt == '--fix' then
fix = true fix = true
else else
dfhack.printerr('Invalid option: '..opt) qerror('Invalid option: '..opt)
return
end end
end end

@ -1,8 +1,7 @@
-- Makes the game immediately save the state. -- Makes the game immediately save the state.
if not dfhack.isMapLoaded() then if not dfhack.isMapLoaded() then
dfhack.printerr("World and map aren't loaded.") qerror("World and map aren't loaded.")
return
end end
local ui_main = df.global.ui.main local ui_main = df.global.ui.main