Merge branch 'master' of github.com:adamdruppe/arsd

This commit is contained in:
Adam D. Ruppe 2019-12-19 23:37:08 -05:00
commit 4ded948efc
3 changed files with 1437 additions and 10 deletions

1189
com.d Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,27 @@
+/
module arsd.comhelpers;
/+
see: program\comtest.d on the laptop.
as administrator: from program\cs
c:\Windows\Microsoft.NEt\Framework64\v4.0.30319\regasm.exe /regfile /codebase test.dll
sn -k key.snk
program\cs\makefile
test.js in there shows it form wsh too
i can make it work through IDispatch easily enough, though
ideally you'd have a real interface, that requires cooperation
that the idispatch doesn't thanks to .net doing it for us.
passing other objects should work too btw thanks to idispatch
in the variants... not sure about arrays tho
and then fully dynamic can be done with opDispatch for teh lulz.
+/
import core.sys.windows.windows;
import core.sys.windows.com;
import core.sys.windows.oaidl;
@ -94,7 +115,12 @@ struct AutoComPtr(T) {
}
*/
// note that HKEY_CLASSES_ROOT\pretty name\CLSID has the guid
/// Create a COM object. the string params are GUID literals that i mixin (this sux i know)
/// or if the interface has no IID it will try to IDispatch it
/// or you can request a fully dynamic version via opDispatch.
/// note i can try `import core.sys.windows.uuid; IID_IDispatch` for example to generically look up ones from the system if they are not attached and come from the windows namespace
AutoComPtr!T createObject(T, string iidStr = null)(GUID classId) {
initializeCom();
@ -105,9 +131,10 @@ AutoComPtr!T createObject(T, string iidStr = null)(GUID classId) {
T obj;
auto hr = CoCreateInstance(&classId, null, CLSCTX_ALL, &iid, cast(void**) &obj);
auto hr = CoCreateInstance(&classId, null, CLSCTX_INPROC_SERVER, &iid, cast(void**) &obj);
import std.format;
if(FAILED(hr))
throw new Exception("Failed to create object");
throw new Exception("Failed to create object " ~ format("%08x", hr));
return AutoComPtr!T(obj);
}
@ -820,3 +847,102 @@ struct TmpStr {
length += s.length;
}
}
/++
module com;
import com2;
interface Refcounting {
void AddRef();
void Release();
}
interface Test : Refcounting {
void test();
}
interface Test2 : Refcounting {
void test2();
}
class Foo : Implements!Test, Implements!Test2 {
override void test() {
import std.stdio;
writeln("amazing");
}
void test2() {}
mixin Refcounts;
}
mixin RegisterComImplementation!(Foo, "some-guid");
void main() {
auto foo = new Foo();
auto c = foo.getComProxy();
c.test();
}
+/
/++
module com2;
/+
The COM interface's implementation is done by a
generated class, forwarding it to the other D
implementation
if it implements IDispatch then it can do the dynamic
thing too automatically!
+/
template Implements(Interface) {
private static class Helper : Interface {
Implements i;
this(Implements i) {
this.i = i;
}
static foreach(memberName; __traits(allMembers, Interface))
mixin(q{ void } ~ memberName ~ q{ () {
import std.stdio; writeln("wrapper begin");
__traits(getMember, i, memberName)();
writeln("wrapper end");
}});
}
interface Implements {
final Helper getComProxy() {
return new Helper(this);
}
static foreach(memberName; __traits(allMembers, Interface))
mixin(q{ void } ~ memberName ~ q{ (); });
mixin template Refcounts() {
int refcount;
void AddRef() { refcount ++; }
void Release() { refcount--; }
}
}
}
// the guid may also be a UDA on Class, but you do need to register your implementations
mixin template RegisterComImplementation(Class, string guid = null) {
}
// wraps the interface with D-friendly type and provides RAII for the object
struct ComClient(I) {}
// eg: alias XmlDocument = ComClient!IXmlDocument;
// then you get it through a com factory
ComClient!I getCom(T)(string guid) { return ComClient!I(); }
+/

128
jni.d
View File

@ -191,6 +191,114 @@ private string getJavaName(alias a)() {
return name;
}
/+
/+ Java class file definitions { +/
// see: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6
struct cp_info {
enum CONSTANT_Class = 7; // sizeof = 2
enum CONSTANT_Fieldref = 9; // sizeof = 4
enum CONSTANT_Methodref = 10; // sizeof = 4
enum CONSTANT_InterfaceMethodref = 11; // sizeof = 4
enum CONSTANT_String = 8; // sizeof = 2
enum CONSTANT_Integer = 3; // sizeof = 4
enum CONSTANT_Float = 4; // sizeof = 4
enum CONSTANT_Long = 5; // sizeof = 8, but eats two slots
enum CONSTANT_Double = 6; // sizeof = 8, but eats two slots
enum CONSTANT_NameAndType = 12; // sizeof = 2
enum CONSTANT_Utf8 = 1; // sizeof = 2 + length
enum CONSTANT_MethodHandle = 15; // sizeof = 3
enum CONSTANT_MethodType = 16; // sizeof = 2; descriptor index
enum CONSTANT_InvokeDynamic = 18; // sizeof = 4
ubyte tag;
union {
}
ubyte[] info;
}
struct field_info {
enum ACC_PUBLIC = 0x0001;
enum ACC_PRIVATE = 0x0002;
enum ACC_PROTECTED = 0x0004;
enum ACC_STATIC = 0x0008;
enum ACC_FINAL = 0x0010;
enum ACC_VOLATILE = 0x0040;
enum ACC_TRANSIENT = 0x0080;
enum ACC_SYNTHETIC = 0x1000;
enum ACC_ENUM = 0x4000;
ushort access_flags;
ushort name_index;
ushort descriptor_index;
ushort attributes_count;
attribute_info attributes[attributes_count];
}
struct method_info {
ushort access_flags;
ushort name_index;
ushort descriptor_index;
ushort attributes_count;
attribute_info attributes[attributes_count];
enum ACC_PUBLIC = 0x0001;
enum ACC_PRIVATE = 0x0002;
enum ACC_PROTECTED = 0x0004;
enum ACC_STATIC = 0x0008;
enum ACC_FINAL = 0x0010;
enum ACC_SYNCHRONIZED = 0x0020;
enum ACC_BRIDGE = 0x0040;
enum ACC_VARARGS = 0x0080;
enum ACC_NATIVE = 0x0100;
enum ACC_ABSTRACT = 0x0400;
enum ACC_STRICT = 0x0800;
enum ACC_SYNTHETIC = 0x1000;
}
struct attribute_info {
ushort attribute_name_index;
uint attribute_length;
ubyte[attribute_length] info;
}
struct ClassFile {
enum ACC_PUBLIC = 0x0001;
enum ACC_FINAL = 0x0010;
enum ACC_SUPER = 0x0020;
enum ACC_INTERFACE = 0x0200;
enum ACC_ABSTRACT = 0x0400;
enum ACC_SYNTHETIC = 0x1000;
enum ACC_ANNOTATION = 0x2000;
enum ACC_ENUM = 0x4000;
uint magic;
ushort minor_version;
ushort major_version;
ushort constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
ushort access_flags;
ushort this_class;
ushort super_class;
ushort interfaces_count;
ushort interfaces[interfaces_count];
ushort fields_count;
field_info fields[fields_count];
ushort methods_count;
method_info methods[methods_count];
ushort attributes_count;
attribute_info attributes[attributes_count];
}
/+ } end java class file definitions +/
+/
// semi-FIXME: java.lang.CharSequence is the interface for String. We should support that just as well.
// possibly other boxed types too, like Integer.
// FIXME: in general, handle substituting subclasses for interfaces nicely
@ -201,11 +309,14 @@ private string getJavaName(alias a)() {
// FIXME: what about the parent class of the java object? Best we can probably do is an interface but perhaps it could be auto-generated by the JavaClass magic. It could take the list and just copy the @Import items.
// FIXME: interfaces? I think a Java interface should just generally be turned into a D interface, but also including the IJavaObject. Basically just write D. No @Import or @Export on this level.
// Just needs a package name somewhere....
//
// Then the D compiler forces you to declare an implementation of it, and that can be @Import.
/+
lol if i wanted to try defining a new class in D, i'd prolly have to ctfe generate bytecode trampoline
to make a Java method that forwards to the native call. the actual method names would then need a magical
prefix or something to avoid namespace conflicts between the Java and the D.
I don't think I'll actually do it. But I legit think that is possible.
FIXME lol if i wanted to try defining a new class in D..... you don't even need a trampoline method. Java and native methods can override each other!!!
Perhaps could be like final class AllNew : JavaClass("package", AllNew, true) {
@Virtual void foo() {} // defines it here, but Java can override
@ -214,7 +325,8 @@ private string getJavaName(alias a)() {
and then @Import and @Export continues to work the same way.
+/
// speaking of hacking bytecode we could prolly read signatures out of a .class file too.
// FIXME: speaking of hacking bytecode we could prolly read signatures out of a .class file too.
// and generate D classes :P
// see: https://developer.android.com/training/articles/perf-jni.html
@ -600,6 +712,8 @@ private mixin template JavaImportImpl(T, alias method, size_t overloadIndex) {
auto jobj = this_.getJavaHandle();
if(!_jmethodID) {
auto jc = (*env).GetObjectClass(env, jobj);
// just a note: jc is an instance of java.lang.Class
// and we could call getName on it to fetch a String to ID it
_jmethodID = (*env).GetMethodID(env, jc,
getJavaName!method.ptr,
// java method string is (args)ret
@ -895,9 +1009,7 @@ private mixin template JavaExportImpl(T, alias method, size_t overloadIndex) {
+/
extern(System)
//pragma(mangle, JniMangle())
// I need it in the DLL, but want it to be not accessible from outside... alas.
export /*private*/ static DTypesToJni!(ReturnType!method) privateJniImplementation(JNIEnv* env, jobject obj, DTypesToJni!(Parameters!method) args) {
private static DTypesToJni!(ReturnType!method) privateJniImplementation(JNIEnv* env, jobject obj, DTypesToJni!(Parameters!method) args) {
// set it up in the thread for future calls
ActivateJniEnv thing = ActivateJniEnv(env);