Skip to content

Commit

Permalink
Add deinitializeDMD function to deinitialize the front end
Browse files Browse the repository at this point in the history
This is a start of being able to restore the global state initialized
by `initDMD` to its original state. It only deinitializes state
initialized by `initDMD`, state store directly inside functions are
not restored.

This is useful to invoke the compiler multiple times within the same
application. This allows to write more fine grained tests for the
compiler, more of a unit test style compared to the current end to end
tests.
  • Loading branch information
jacob-carlborg committed Feb 10, 2019
1 parent fd2f4ed commit 46c27f1
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/dmd/builtin.d
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,17 @@ public extern (C++) void builtin_init()
add_builtin("_D4core5bitop7_popcntFNaNbNiNfmZi", &eval_popcnt);
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `builtin_init` to its original
* state.
*/
public void builtinDeinitialize()
{
builtins = builtins.init;
}

/**********************************
* Determine if function is a builtin one that we can
* evaluate at compile time.
Expand Down
11 changes: 11 additions & 0 deletions src/dmd/dmodule.d
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,17 @@ extern (C++) final class Module : Package
modules = new DsymbolTable();
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `_init` to its original
* state.
*/
static void deinitialize()
{
modules = modules.init;
}

extern (C++) __gshared AggregateDeclaration moduleinfo;

const(char)* arg; // original argument name
Expand Down
16 changes: 16 additions & 0 deletions src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,22 @@ extern (C++) abstract class Expression : RootObject
CTFEExp.showcontext = new CTFEExp(TOK.showCtfeContext);
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `_init` to its original
* state.
*/
static void deinitialize()
{
CTFEExp.cantexp = CTFEExp.cantexp.init;
CTFEExp.voidexp = CTFEExp.voidexp.init;
CTFEExp.breakexp = CTFEExp.breakexp.init;
CTFEExp.continueexp = CTFEExp.continueexp.init;
CTFEExp.gotoexp = CTFEExp.gotoexp.init;
CTFEExp.showcontext = CTFEExp.showcontext.init;
}

/*********************************
* Does *not* do a deep copy.
*/
Expand Down
29 changes: 29 additions & 0 deletions src/dmd/frontend.d
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,35 @@ void initDMD()
FileCache._init();
}

/**
Deinitializes the global variables of the DMD compiler.
This can be used to restore the state set by `initDMD` to its original state.
Useful if there's a need for multiple sessions of the DMD compiler in the same
application.
*/
void deinitializeDMD()
{
import dmd.builtin : builtinDeinitialize;
import dmd.dmodule : Module;
import dmd.expression : Expression;
import dmd.globals : global;
import dmd.id : Id;
import dmd.mtype : Type;
import dmd.objc : Objc;
import dmd.target : target;

global.deinitialize();

Type.deinitialize();
Id.deinitialize();
Module.deinitialize();
target.deinitialize();
Expression.deinitialize();
Objc.deinitialize();
builtinDeinitialize();
}

/**
Add import path to the `global.path`.
Params:
Expand Down
11 changes: 11 additions & 0 deletions src/dmd/globals.d
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,17 @@ struct Global
params.color = Console.detectTerminal();
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `_init` to its original
* state.
*/
void deinitialize()
{
this = this.init;
}

/**
Returns: the version as the number that would be returned for __VERSION__
*/
Expand Down
17 changes: 17 additions & 0 deletions src/dmd/id.d
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ struct Id
{
mixin(msgtable.generate(&initializer));
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `initialize` to its original
* state.
*/
void deinitialize()
{
mixin(msgtable.generate(&deinitializer));
}
}

private:
Expand Down Expand Up @@ -487,3 +498,9 @@ string initializer(Msgtable m)
{
return m.ident ~ ` = Identifier.idPool("` ~ m.name ~ `");`;
}

// Used to generate the code for each deinitializer.
string deinitializer(Msgtable m)
{
return m.ident ~ " = Identifier.init;";
}
11 changes: 11 additions & 0 deletions src/dmd/mtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,17 @@ extern (C++) abstract class Type : RootObject
thash_t = tsize_t;
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `_init` to its original
* state.
*/
static void deinitialize()
{
stringtable = stringtable.init;
}

final d_uns64 size()
{
return size(Loc.initial);
Expand Down
11 changes: 11 additions & 0 deletions src/dmd/objc.d
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ extern(C++) abstract class Objc
_objc = new Unsupported;
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `_init` to its original
* state.
*/
static void deinitialize()
{
_objc = _objc.init;
}

abstract void setObjc(ClassDeclaration cd);
abstract void setObjc(InterfaceDeclaration);

Expand Down
11 changes: 11 additions & 0 deletions src/dmd/target.d
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ struct Target
params.isDragonFlyBSD || params.isOSX;
}

/**
* Deinitializes the global state of the compiler.
*
* This can be used to restore the state set by `_init` to its original
* state.
*/
void deinitialize()
{
this = this.init;
}

/**
* Requested target memory alignment size of the given type.
* Params:
Expand Down
166 changes: 166 additions & 0 deletions test/unit/deinitialization.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
module deinitialization;

@("global.deinitialize")
unittest
{
import dmd.globals : global;

static void assertStructsEqual(T)(const ref T a, const ref T b) @nogc pure nothrow
if (is(T == struct))
{
foreach (i, _; typeof(a.tupleof))
{
enum name = __traits(identifier, a.tupleof[i]);

static if (is(typeof(a.tupleof[i]) == const(char*)))
assert(a.tupleof[i].fromStringz == b.tupleof[i].fromStringz, name);
else
assert(a.tupleof[i] == b.tupleof[i], name);
}
}

immutable init = global.init;
assertStructsEqual(global, init);

global._init();
global.deinitialize();

assert(global == global.init);
}

@("Type.deinitialize")
unittest
{
import dmd.mars : addDefaultVersionIdentifiers, setTarget;
import dmd.mtype : Type;
import dmd.globals : global;

assert(Type.stringtable == Type.stringtable.init);

global._init();
setTarget(global.params);

Type._init();
Type.deinitialize();

global.deinitialize();

assert(Type.stringtable == Type.stringtable.init);
}

@("Id.deinitialize")
unittest
{
import dmd.id : Id;

static void assertInitialState()
{
foreach (e ; __traits(allMembers, Id))
{
static if (!__traits(isStaticFunction, mixin("Id." ~ e)))
assert(__traits(getMember, Id, e) is null);
}
}

assertInitialState();

Id.initialize();
Id.deinitialize();

assertInitialState();
}

@("Module.deinitialize")
unittest
{
import dmd.dmodule : Module;

assert(Module.modules is Module.modules.init);

Module._init();
Module.deinitialize();

assert(Module.modules is Module.modules.init);
}

@("Target.deinitialize")
unittest
{
import dmd.globals : Param;
import dmd.mars : setTarget;
import dmd.target : target, Target;

static bool isFPTypeProperties(T)()
{
return is(T == const(typeof(Target.FloatProperties))) ||
is (T == const(typeof(Target.DoubleProperties))) ||
is (T == const(typeof(Target.RealProperties)));
}

static void assertStructsEqual(T)(const ref T a, const ref T b) @nogc pure nothrow
if (is(T == struct))
{
foreach (i, _; typeof(a.tupleof))
{
alias Type = typeof(a.tupleof[i]);
enum name = __traits(identifier, a.tupleof[i]);

static if (!isFPTypeProperties!Type)
assert(a.tupleof[i] == b.tupleof[i], name);
}
}

const init = target.init;
assertStructsEqual(target, init);

Param params;
setTarget(params);

target._init(params);
target.deinitialize();

assertStructsEqual(target, init);
}

@("Expression.deinitialize")
unittest
{
import dmd.ctfeexpr : CTFEExp;
import dmd.expression : Expression;

static void assertInitialState()
{
assert(CTFEExp.cantexp is null);
assert(CTFEExp.voidexp is null);
assert(CTFEExp.breakexp is null);
assert(CTFEExp.continueexp is null);
assert(CTFEExp.gotoexp is null);
assert(CTFEExp.showcontext is null);
}

assertInitialState();

Expression._init();
Expression.deinitialize();

assertInitialState();
}

@("Objc.deinitialize")
unittest
{
import dmd.objc : Objc, objc;

assert(objc is null);

Objc._init();
Objc.deinitialize();

assert(objc is null);
}

private inout(char)[] fromStringz(inout(char)* cString) @nogc @system pure nothrow
{
import core.stdc.string : strlen;
return cString ? cString[0 .. strlen(cString)] : null;
}
4 changes: 2 additions & 2 deletions test/unit/self_test.d
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import support : afterEach, beforeEach, defaultImportPaths;

@afterEach deinitializeFrontend()
{
// import dmd.frontend : deinitializeDMD;
// deinitializeDMD();
import dmd.frontend : deinitializeDMD;
deinitializeDMD();
}

@("self test")
Expand Down

0 comments on commit 46c27f1

Please sign in to comment.