Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
LunaTheFoxgirl committed Nov 8, 2020
0 parents commit 2790898
Show file tree
Hide file tree
Showing 17 changed files with 2,331 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.dub
docs.json
__dummy.html
docs/
/dangel
libdangel.so
dangel.dylib
dangel.dll
libdangel.a
dangel.lib
dangel-test-*
*.exe
*.o
*.obj
*.lst
*.a
*.so

dub.selections.json
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# DAngel
D bindings and wrapper for AngelScript with support for D strings.

Do note this is still work in progress and there's currently issues with D calling conventions breaking with angelscript (will be working on patching angelscript to support D calling conventions)

This library also depends on a patched C binding to angelscript that you will need to compile first, you can find that [here](https://github.com/KitsunebiGames/angelscriptc)

# Why?
Because I can, especially with the string support.
I'm sorry in advance, there's some cursed GC stuff going on there and I'm not sure whether it will leak memory at some point.

# Using the binding
First compile AngelScript and the [patched c bindings](https://github.com/KitsunebiGames/angelscriptc) **AS DYNAMIC LIBRARIES** and install the libraries to your system's library path (or whatever the heck you do on Windows)

Then put the libraries somewhere that the D linker will be able to find them, in my case that's `/usr/lib` and `/usr/include`. You may want to ship the libraries with your application.

Once you have that sorted, just compile your application with dub and it _should_ link to the libraries, hopefully...

# Using D strings
To enable D string support register the D string subsystem in angelscript like this:
```d
import as.addons.str : registerDStrings;
ScriptEngine engine = ScriptEngine.create();
engine.registerDStrings();
// Register all your other stuff and execute your scripts.
```

# Registering D types
Currently DAngel supports registering a few D types automatically, one being **integer** enums. To register a D integer enum do the following:
```d
enum MyEnum {
Item1,
Item2,
Item3 = 42,
}
// After this call the enum will be available to your scripts.
engine.registerEnum!MyEnum;
```

There's experimental and buggy support for registering D structs in `as.addons.dwrap`. I don't recommend using it currently, though.

# There's parts of the API missing!
I know, there's also a lot of places where I haven't done any error handling yet that will just fail silently. It will be fixed with time.

# My functions don't run correctly!
I know, there's some incompatibility between AngelScript and the D ABI's calling conventions, changing the calling convention to the C calling convention via `extern(C)` and marking your function as `asCDECL` should work around it temporarily.

I'll be working on making a patched version of AngelScript that supports the D calling convention unless the creator of AngelScript decides to add it themself.
6 changes: 6 additions & 0 deletions dub.sdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name "dangel"
description "D AngelScript binding"
authors "Luna Nielsen"
copyright "Copyright © 2020, Luna Nielsen"
license "BSD 2-Clause"
libs "as_c" "angelscript"
114 changes: 114 additions & 0 deletions source/as/addons/dwrap.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
module as.addons.dwrap;
import as.def;
import as.engine;
import core.memory;
import core.stdc.stdlib : free;
import std.traits;
import std.format;
import std.string;
import std.stdio;

private {
extern(C) void constructClassOrStruct(T)(T* self) {
import std.conv : emplace;
emplace!T(self);
}

extern(C) void destructClassOrStruct(T)(T* self) {
destroy(self);
}

extern(C) T structAssign(T)(ref T in_, ref T self) {
writeln(self, " ", in_);
self = in_;
return self;
}

string toASType(string typeName) {
switch(typeName) {

case "string":
case "int":
case "float":
case "double": return typeName;

case "ubyte": return "byte";
case "ushort": return "uint16";
case "uint": return "uint";
case "ulong": return "uint64";
default: {
while (typeName.endsWith("*")) {
typeName.length--;
}
return typeName;
}
}
}

string buildArgList(alias T)() {
string[] o;
static foreach(param; Parameters!T) {
o ~= toASType(param.stringof);
}
return o.length == 0 ? "" : o.join(", ");
}
}

void registerDStruct(T, string namespace = "")(ScriptEngine engine) if (is(T == struct)) {
mixin("import ", moduleName!T, ";");

static if (namespace.length != 0) {
string prevNamespace = engine.getDefaultNamespace();
scope(exit) engine.setDefaultNamespace(prevNamespace);

// Set up namespace
engine.setDefaultNamespace(namespace);
}

engine.registerObjectType(T.stringof, T.sizeof, asEObjTypeFlags.asOBJ_VALUE | asEObjTypeFlags.asOBJ_APP_CLASS_CDAK);
engine.registerObjectBehaviour(
T.stringof,
asEBehaviours.asBEHAVE_CONSTRUCT,
"void f()",
cast(asFUNCTION_t)&constructClassOrStruct!T,
DCallConvClass,
null
);

engine.registerObjectBehaviour(
T.stringof,
asEBehaviours.asBEHAVE_DESTRUCT,
"void f()",
cast(asFUNCTION_t)&destructClassOrStruct!T,
DCallConvClass,
null
);
engine.registerObjectMethod(
T.stringof,
"%s &opAssign(const %s &in)".format(T.stringof, T.stringof),
&structAssign!T,
DCallConvClassR
);


static foreach(member; __traits(allMembers, T)) {
{
alias mInstance = mixin(T.stringof, ".", member);
enum visibility = __traits(getProtection, mInstance);
static if (visibility == "public" || visibility == "static") {
static if (is(typeof(mInstance) == function)) {
{
string retType = (ReturnType!mInstance).stringof;

engine.registerObjectMethod(T.stringof, "%s %s(%s)".format(toASType(retType), member, buildArgList!mInstance()), cast(asFUNCTION_t)&mInstance, DCallConvClassR, null);
}
} else static if(!is(typeof(mInstance) == struct) && !is(typeof(mInstance) == class)) {
engine.registerObjectProperty(T.stringof, "%s %s".format(toASType(typeof(mInstance).stringof), member), mInstance.offsetof);
} else static if(is(typeof(mInstance) == struct)) {
// TODO: Auto register structs if needed
}
}
}
}

}
165 changes: 165 additions & 0 deletions source/as/addons/str.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
module as.addons.str;
import as.def;
import as.engine;
import core.memory;
import std.stdio;

private extern(C) {

/**
A proto string
*/
union protostring {
struct pstr_impl {
size_t len;
const(char)* data;
}

this(size_t length, const(char)* data) {
proto = pstr_impl(length, data);
GC.addRoot(&this);
GC.setAttr(&this, GC.BlkAttr.NO_MOVE);
}

this(string str) {
this.str = str.idup;
GC.addRoot(&this);
GC.setAttr(&this, GC.BlkAttr.NO_MOVE);
}

void release() {
GC.removeRoot(&this);
}

size_t length() {
return proto.len;
}

void setText(string text) {
GC.removeRoot(cast(void*)str);
str = text.idup;
GC.addRoot(cast(void*)str);
}

pstr_impl proto;
string str;
}

/**
The cache of constant values
*/
int[protostring*] constantCache;

// FACTORY
void* getStringConstant(const(char)* data, asUINT length) {
protostring* text = new protostring(cast(string)data[0..length]);

// Handle adding references to strings
if (text !in constantCache) constantCache[text] = 1;
else constantCache[text]++;

return cast(void*)text;
}

int releaseStringConstant(const(void)* str) {
if (str is null) return asERetCodes.asERROR;

auto text = cast(protostring*)str;

// Handle releasing strings
if (text !in constantCache) return asERetCodes.asERROR;
else if (--constantCache[text] <= 0) constantCache.remove(text);

return asERetCodes.asSUCCESS;
}

int getRawStringData(const(void)* istr, char* data, asUINT* length) {
if (istr is null) return asERetCodes.asERROR;

protostring* proto = cast(protostring*)istr;

// If the length is not null set the length
if (length !is null)
*length = cast(uint)proto.length;

// If the data pointer is not null fill the data buffer
if (data !is null)
data[0..proto.length] = proto.str[0..proto.length];

// We're done
return asERetCodes.asSUCCESS;
}

void strConstructor(protostring* self) {
self = new protostring;

GC.addRoot(self);
GC.setAttr(self, GC.BlkAttr.NO_MOVE);
}

void strDestructor(protostring* self) {
GC.removeRoot(self);
GC.clrAttr(self, GC.BlkAttr.FINALIZE | GC.BlkAttr.NO_MOVE);
}

protostring* strOpAssign(ref string in_, protostring* self) {
self.setText(in_);
return self;
}

protostring* strOpAddAssign(ref string in_, protostring* self) {
self.setText(self.str~in_);
return self;
}

protostring* strOpAdd(protostring* lhs, ref string rhs) {
return new protostring(lhs.str~rhs);
}

uint strLength(protostring* self) {
return cast(uint)self.length;
}

bool strEmpty(protostring* self) {
return self.length == 0;
}

void strResize(uint size, protostring* self) {
string newString = self.str.idup;
newString.length = size;
self.setText(newString);
}

protostring* strSubstr(uint start, uint length, protostring* self) {

// TODO: Slice via code points?
return new protostring(self.str[start..start+length]);
}
}

/**
Makes a copy of a string on the heap and gets a pointer to it
*/
string* toAS(string text) {
protostring* proto = new protostring(text.idup);
return &proto.str;
}

/**
Registers D UTF-8 strings
*/
void registerDStrings(ScriptEngine engine) {
engine.registerObjectType("string", string.sizeof, asEObjTypeFlags.asOBJ_VALUE | asEObjTypeFlags.asOBJ_APP_CLASS_CDAK);
engine.registerStringFactory("string", &getStringConstant, &releaseStringConstant, &getRawStringData);
engine.registerObjectBehaviour("string", asEBehaviours.asBEHAVE_CONSTRUCT, "void f()", &strConstructor, asECallConvTypes.asCALL_CDECL_OBJLAST);
engine.registerObjectBehaviour("string", asEBehaviours.asBEHAVE_DESTRUCT, "void f()", &strDestructor, asECallConvTypes.asCALL_CDECL_OBJLAST);

engine.registerObjectMethod("string", "string &opAssign(const string &in)", &strOpAssign, asECallConvTypes.asCALL_CDECL_OBJLAST);
engine.registerObjectMethod("string", "string &opAddAssign(const string &in)", &strOpAddAssign, asECallConvTypes.asCALL_CDECL_OBJLAST);
engine.registerObjectMethod("string", "string &opAdd(const string &in)", &strOpAdd, asECallConvTypes.asCALL_CDECL_OBJFIRST);

engine.registerObjectMethod("string", "uint length() const", &strLength, asECallConvTypes.asCALL_CDECL_OBJLAST);
engine.registerObjectMethod("string", "bool isEmpty() const", &strEmpty, asECallConvTypes.asCALL_CDECL_OBJLAST);
engine.registerObjectMethod("string", "void resize(uint size) const", &strResize, asECallConvTypes.asCALL_CDECL_OBJLAST);
engine.registerObjectMethod("string", "string &substr(uint start, uint length)", &strSubstr, asECallConvTypes.asCALL_CDECL_OBJLAST);
}
Loading

0 comments on commit 2790898

Please sign in to comment.