1 module janet.wrap; 2 3 import janet.c; 4 5 import janet.d : JanetDAbstractHead; 6 7 private template isInternal(string field) // grabbed up from LuaD 8 { 9 enum isInternal = field.length >= 2 && field[0..2] == "__"; 10 } 11 12 import std.traits : isSomeString,isArray,isAssociativeArray,isPointer; 13 /// Converts a Janet string to a D string. 14 string fromJanetString(Janet janet) 15 { 16 import std.string : fromStringz; 17 const auto unwrapped = janet_unwrap_string(janet); 18 const(char)* charArray = cast(const(char)*)(unwrapped); 19 return cast(string)fromStringz(charArray); 20 } 21 /// Returns true if the Janet argument's type is compatible with the template type. Used for type checking at runtime. 22 bool janetCompatible(T)(Janet janet) 23 { 24 static if(is(T == bool)) 25 { 26 return janet.type == JanetType.JANET_BOOLEAN; 27 } 28 else static if(is(T : double) || is(T : int)) 29 { 30 return janet.type == JanetType.JANET_NUMBER; 31 } 32 else static if(is(T == JanetFiber*)) 33 { 34 return janet.type == JanetType.JANET_FIBER; 35 } 36 else static if(is(T == JanetFunction*)) 37 { 38 return janet.type == JanetType.JANET_FUNCTION; 39 } 40 else static if(isSomeString!T) 41 { 42 return janet.type == JanetType.JANET_STRING || janet.type == JanetType.JANET_SYMBOL || janet.type == JanetType.JANET_KEYWORD || janet.type == JanetType.JANET_BUFFER; 43 } 44 else static if(isArray!T) 45 { 46 return janet.type == JanetType.JANET_ARRAY || janet.type == JanetType.JANET_TUPLE; 47 } 48 else static if(isAssociativeArray!T || is(T == struct)) 49 { 50 return janet.type == JanetType.JANET_TABLE || janet.type == JanetType.JANET_STRUCT; 51 } 52 else static if(is(T==JanetCFunction)) 53 { 54 return janet.type == JanetType.JANET_CFUNCTION; 55 } 56 else static if(is(T==class)) 57 { 58 return janet.type == JanetType.JANET_ABSTRACT; 59 } 60 else static if(isPointer!T) 61 { 62 return janet.type == JanetType.JANET_POINTER; 63 } 64 else 65 { 66 return false; 67 } 68 } 69 70 bool janetCompatible(T)(T x,Janet janet) 71 { 72 return janetCompatible!(T)(janet); 73 } 74 75 /** Converts from a Janet object to an object of the given type. 76 Does absolutely no checking on its own! You must check or ensure that it will never coerce to an invalid type yourself.*/ 77 @system T getFromJanet(T)(Janet janet) 78 { 79 static if(is(T == bool)) 80 { 81 return cast(bool)janet_unwrap_boolean(janet); 82 } 83 else static if(is(T : int)) 84 { 85 return janet_unwrap_integer(janet); 86 } 87 else static if(is(T : double)) 88 { 89 return janet_unwrap_number(janet); 90 } 91 else static if(is(T == JanetFiber*)) 92 { 93 return janet_unwrap_fiber(janet); 94 } 95 else static if(is(T == JanetFunction*)) 96 { 97 return janet_unwrap_function(janet); 98 } 99 else static if(isSomeString!T) 100 { 101 return cast(T)(fromJanetString(janet)); 102 } 103 else static if(is(T == JanetArray*)) 104 { 105 return janet_unwrap_array(janet); 106 } 107 else static if(is(T == JanetTable*)) 108 { 109 return janet_unwrap_table(janet); 110 } 111 else static if(is(T == JanetBuffer*)) 112 { 113 return janet_unwrap_buffer(janet); 114 } 115 else static if(is(T == class)) 116 { 117 return cast(T)(janet_unwrap_abstract(janet)); 118 } 119 else static if(is(T == struct)) 120 { 121 JanetTable* tbl = janet_unwrap_table(janet); 122 T newStruct; 123 for(int i=0;i<tbl.count;i++) 124 { 125 JanetKV kv = tbl.data[i]; 126 static foreach(field; __traits(allMembers,T)) 127 { 128 if(getFromJanet!(typeof("T."~field))(kv.key) == field) 129 { 130 mixin("newStruct."~field) = getFromJanet!(typeof(mixin("T."~field)))(kv.value); 131 } 132 } 133 } 134 return newStruct; 135 } 136 else static if(is(T==JanetCFunction)) 137 { 138 return janet_unwrap_cfunction(janet); 139 } 140 else static if(isPointer!T) 141 { 142 return cast(T)(janet_unwrap_pointer(janet)); 143 } 144 } 145 146 /** 147 Wraps a D value to a Janet value. 148 Works for a bunch of built-in types as well as structs; for classes, see register.d. 149 */ 150 Janet janetWrap(T,string strType = "string")(T x) 151 { 152 static if(is(T==Janet)) 153 { 154 return x; 155 } 156 else static if(is(T == bool)) 157 { 158 return janet_wrap_boolean(x); 159 } 160 else static if(is(T==void*)) 161 { 162 return janet_wrap_abstract(x); 163 } 164 else static if(is(T == JanetCFunction)) 165 { 166 return janet_wrap_cfunction(x); 167 } 168 else static if(isSomeString!T) 169 { 170 import std.string : toStringz; 171 final switch(strType) 172 { 173 case "string": 174 return janet_wrap_string(janet_string(cast(ubyte*)x,cast(int)x.length)); 175 case "symbol": 176 return janet_wrap_symbol(janet_string(cast(ubyte*)x,cast(int)x.length)); 177 case "keyword": 178 return janet_wrap_keyword(janet_string(cast(ubyte*)x,cast(int)x.length)); 179 } 180 } 181 else static if(is(T==const(char)*)) 182 { 183 final switch(strType) 184 { 185 case "string": 186 return janet_wrap_string(janet_cstring(x)); 187 case "symbol": 188 return janet_wrap_symbol(janet_cstring(x)); 189 case "keyword": 190 return janet_wrap_keyword(janet_cstring(x)); 191 } 192 } 193 else static if(isPointer!T) 194 { 195 return janet_wrap_pointer(x); 196 } 197 else static if(is(T : double)) 198 { 199 return janet_wrap_number_safe(x); 200 } 201 else static if(is(T : int)) 202 { 203 return janet_wrap_integer(x); 204 } 205 else static if(isArray!T) 206 { 207 JanetArray* arr = janet_array(x.length); 208 foreach(item; x) 209 { 210 janet_array_push(arr,wrap(item)); 211 } 212 return janet_wrap_array(arr); 213 } 214 else static if(is(T == struct)) 215 { 216 JanetTable* tbl = janet_table(x.tupleof.length); 217 static foreach(field; __traits(allMembers,T)) 218 { 219 static if(!isInternal!field && 220 field != "this" && 221 field != "opAssign") // also from LuaD 222 { 223 enum isMemberFunction = mixin("is(typeof(&x." ~ field ~ ") == delegate)"); //LuaD 224 static if(!isMemberFunction) 225 { 226 janet_table_put(tbl,janetWrap(field),mixin("janetWrap(x."~field~")")); 227 } 228 } 229 } 230 return janet_wrap_table(tbl); 231 } 232 else static if(is(T == class)) 233 { 234 //we're not using the GC here so we do the offset stuff manually... which is highly unsafe. annoying. 235 //seriously i think this might cause problems in the future. it's baffling that i might be doing this. 236 //for reference, janet_abstract_head(cast(void*)x) makes a janet_abstract_head object... a few bytes BEFORE the pointer. 237 //so if there's anything in the way, it'll just... overwrite it, i guess? it's not good. 238 //Upon further testing, this will fail after exactly 500 runs, without fail. This is not good. 239 return janet_wrap_abstract(new JanetDAbstractHead!(T)(x).ptr); 240 } 241 static assert("Not a compatible type for janet wrap!"); 242 } 243 244 Janet janetWrap(alias func)() 245 { 246 import janet.func : makeJanetCFunc; 247 return wrap(makeJanetCFunc!func); 248 } 249 250 /// ditto 251 Janet janetWrap(K,V)(V[K] arr) 252 { 253 JanetTable* tbl = janet_table(arr.length); 254 foreach(K key,V value; arr) 255 { 256 janet_table_put(arr,janetWrap(key),wrjanetWrapap(value)); 257 } 258 return janet_wrap_table(arr); 259 } 260 261 unittest 262 { 263 import janet.vm : initJanet, coreEnv; 264 initJanet(); 265 scope(exit) janet_deinit(); 266 const string foo = "foo"; 267 Janet* j; 268 auto janetFoo = janetWrap(foo); 269 import std.string : toStringz; 270 janet_def(coreEnv,toStringz("foo"),janetFoo,"A simple string, which should say 'foo'."); 271 const auto janetedString = getFromJanet!string(janetWrap(foo)); 272 assert(janetedString == foo,janetedString~" is not "~foo~". This is likely caused by compiling with wrong settings (turn nanboxing off!)"); 273 }