module_socket.c
Go to the documentation of this file.
1 
7 #include <string.h>
8 #include <sys/types.h>
9 #ifdef _WIN32
10 #include <winsock2.h>
11 #include <ws2tcpip.h>
12 #else
13 #include <sys/socket.h>
14 #include <arpa/inet.h>
15 #include <netdb.h>
16 #endif
17 #ifdef AF_UNIX
18 #include <sys/un.h>
19 #endif
20 #include <errno.h>
21 
22 #include <kuroko/vm.h>
23 #include <kuroko/util.h>
24 
25 static KrkClass * SocketError = NULL;
26 static KrkClass * SocketClass = NULL;
27 
28 struct socket {
29  KrkInstance inst;
30 
31  int sockfd;
32  int family;
33  int type;
34  int proto;
35 };
36 
37 #define IS_socket(o) (krk_isInstanceOf(o,SocketClass))
38 #define AS_socket(o) ((struct socket*)AS_OBJECT(o))
39 #define CURRENT_CTYPE struct socket *
40 #define CURRENT_NAME self
41 
42 KRK_Method(socket,__init__) {
43  METHOD_TAKES_AT_MOST(3);
44 
45  int family = AF_INET;
46  int type = SOCK_STREAM;
47  int proto = 0;
48 
49  if (!krk_parseArgs(".|iii:socket",
50  (const char *[]){"family","type","proto"},
51  &family, &type, &proto)) {
52  return NONE_VAL();
53  }
54 
55  int result = socket(family,type,proto);
56 
57  if (result < 0) {
58  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
59  }
60 
61  self->sockfd = result;
62  self->family = family;
63  self->type = type;
64  self->proto = proto;
65 
66  return NONE_VAL();
67 }
68 
69 static char * _af_name(int afval) {
70  static char tmp[30];
71  switch (afval) {
72  case AF_INET: return "AF_INET";
73 #ifdef AF_INET6
74  case AF_INET6: return "AF_INET6";
75 #endif
76 #ifdef AF_UNIX
77  case AF_UNIX: return "AF_UNIX";
78 #endif
79  default:
80  snprintf(tmp,30,"%d",afval);
81  return tmp;
82  }
83 }
84 
85 static char * _sock_type(int type) {
86  static char tmp[30];
87  switch (type) {
88  case SOCK_STREAM: return "SOCK_STREAM";
89  case SOCK_DGRAM: return "SOCK_DGRAM";
90 #ifdef SOCK_RAW
91  case SOCK_RAW: return "SOCK_RAW";
92 #endif
93  default:
94  snprintf(tmp,30,"%d",type);
95  return tmp;
96  }
97 }
98 
99 KRK_Method(socket,__repr__) {
100  char tmp[4096];
101  size_t len = snprintf(tmp, 4096, "<socket.socket fd=%d, family=%s, type=%s, proto=%d>",
102  self->sockfd, _af_name(self->family), _sock_type(self->type), self->proto);
103  return OBJECT_VAL(krk_copyString(tmp,len));
104 }
105 
106 static int socket_parse_address(struct socket * self, KrkValue address, struct sockaddr_storage *sock_addr, socklen_t *sock_size) {
107  if (self->family == AF_INET) {
108  /* Should be 2-tuple */
109  if (!IS_tuple(address)) {
110  krk_runtimeError(vm.exceptions->typeError, "Expected 2-tuple, not '%T'", address);
111  return 1;
112  }
113  KrkTuple * addr = AS_TUPLE(address);
114  if (addr->values.count != 2) {
115  krk_runtimeError(vm.exceptions->typeError, "Expected 2-tuple, not '%T'", address);
116  return 1;
117  }
118  if (!IS_str(addr->values.values[0])) {
119  krk_runtimeError(vm.exceptions->typeError, "Address should be str, not '%T'", addr->values.values[0]);
120  return 1;
121  }
122  if (!IS_int(addr->values.values[1])) {
123  krk_runtimeError(vm.exceptions->typeError, "Port should be int, not '%T'", addr->values.values[1]);
124  return 1;
125  }
126 
127  if (!AS_STRING(addr->values.values[0])->length) {
128  struct sockaddr_in * sin = (struct sockaddr_in*)sock_addr;
129  *sock_size = sizeof(struct sockaddr_in);
130  sin->sin_family = AF_INET;
131  sin->sin_port = htons(AS_int(addr->values.values[1]));
132  sin->sin_addr.s_addr = INADDR_ANY;
133  return 0;
134  } else {
135  struct addrinfo *result;
136  struct addrinfo *res;
137  int error = getaddrinfo(AS_CSTRING(addr->values.values[0]), NULL, NULL, &result);
138  if (error != 0) {
139  krk_runtimeError(SocketError, "getaddrinfo() returned error: %d", error);
140  return 1;
141  }
142 
143  int found = 0;
144  res = result;
145  while (res) {
146  if (res->ai_family == AF_INET) {
147  found = 1;
148  *sock_size = res->ai_addrlen;
149  memcpy(sock_addr, res->ai_addr, *sock_size);
150  break;
151  }
152  res = res->ai_next;
153  }
154 
155  freeaddrinfo(result);
156 
157  if (!found) {
158  krk_runtimeError(SocketError, "no suitable address");
159  return 1;
160  }
161 
162  struct sockaddr_in * sin = (struct sockaddr_in*)sock_addr;
163  sin->sin_family = AF_INET;
164  sin->sin_port = htons(AS_int(addr->values.values[1]));
165 
166  return 0;
167  }
168 #ifdef AF_INET6
169  } else if (self->family == AF_INET6) {
170  /* Should be 2-tuple */
171  if (!IS_tuple(address)) {
172  krk_runtimeError(vm.exceptions->typeError, "Expected 2-tuple, not '%T'", address);
173  return 1;
174  }
175  KrkTuple * addr = AS_TUPLE(address);
176  if (addr->values.count != 2) {
177  krk_runtimeError(vm.exceptions->typeError, "Expected 2-tuple, not '%T'", address);
178  return 1;
179  }
180  if (!IS_str(addr->values.values[0])) {
181  krk_runtimeError(vm.exceptions->typeError, "Address should be str, not '%T'", addr->values.values[0]);
182  return 1;
183  }
184  if (!IS_int(addr->values.values[1])) {
185  krk_runtimeError(vm.exceptions->typeError, "Port should be int, not '%T'", addr->values.values[1]);
186  return 1;
187  }
188 
189  if (!AS_STRING(addr->values.values[0])->length) {
190  struct sockaddr_in6 * sin = (struct sockaddr_in6*)sock_addr;
191  *sock_size = sizeof(struct sockaddr_in6);
192  sin->sin6_family = AF_INET6;
193  sin->sin6_port = htons(AS_int(addr->values.values[1]));
194  sin->sin6_addr = in6addr_any;
195  return 0;
196  } else {
197  struct addrinfo *result;
198  struct addrinfo *res;
199  int error = getaddrinfo(AS_CSTRING(addr->values.values[0]), NULL, NULL, &result);
200  if (error != 0) {
201  krk_runtimeError(SocketError, "getaddrinfo() returned error: %d", error);
202  return 1;
203  }
204 
205  int found = 0;
206  res = result;
207  while (res) {
208  if (res->ai_family == AF_INET6) {
209  found = 1;
210  *sock_size = res->ai_addrlen;
211  memcpy(sock_addr, res->ai_addr, *sock_size);
212  break;
213  }
214  res = res->ai_next;
215  }
216 
217  freeaddrinfo(result);
218 
219  if (!found) {
220  krk_runtimeError(SocketError, "no suitable address");
221  return 1;
222  }
223 
224  struct sockaddr_in6 * sin = (struct sockaddr_in6*)sock_addr;
225  sin->sin6_family = AF_INET6;
226  sin->sin6_port = htons(AS_int(addr->values.values[1]));
227 
228  return 0;
229  }
230 #endif
231 #ifdef AF_UNIX
232  } else if (self->family == AF_UNIX) {
233  if (!IS_str(address)) {
234  krk_runtimeError(vm.exceptions->typeError, "Address should be str, not '%T'", address);
235  return 1;
236  }
237 
238  if (AS_STRING(address)->length > 107) {
239  krk_runtimeError(vm.exceptions->valueError, "Address is too long");
240  return 1;
241  }
242 
243  struct sockaddr_un * sun = (struct sockaddr_un*)sock_addr;
244  *sock_size = sizeof(struct sockaddr_un);
245  sun->sun_family = AF_UNIX;
246  memcpy(sun->sun_path, AS_CSTRING(address), AS_STRING(address)->length + 1);
247  return 0;
248 #endif
249  } else {
250  krk_runtimeError(vm.exceptions->notImplementedError, "Not implemented.");
251  return 1;
252  }
253 
254  return 1;
255 }
256 
257 KRK_Method(socket,connect) {
258  METHOD_TAKES_EXACTLY(1);
259 
260  struct sockaddr_storage sock_addr;
261  socklen_t sock_size = 0;
262 
263  /* What do we take? I guess a tuple for AF_INET */
264  int parseResult = socket_parse_address(self, argv[1], &sock_addr, &sock_size);
265  if (parseResult) {
266  if (!(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION))
267  return krk_runtimeError(SocketError, "Unspecified error.");
268  return NONE_VAL();
269  }
270 
271  int result = connect(self->sockfd, (struct sockaddr*)&sock_addr, sock_size);
272 
273  if (result < 0) {
274  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
275  }
276 
277  return NONE_VAL();
278 }
279 
280 KRK_Method(socket,bind) {
281  METHOD_TAKES_EXACTLY(1);
282 
283  struct sockaddr_storage sock_addr;
284  socklen_t sock_size = 0;
285 
286  /* What do we take? I guess a tuple for AF_INET */
287  int parseResult = socket_parse_address(self, argv[1], &sock_addr, &sock_size);
288  if (parseResult) {
289  if (!(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION))
290  return krk_runtimeError(SocketError, "Unspecified error.");
291  return NONE_VAL();
292  }
293 
294  int result = bind(self->sockfd, (struct sockaddr*)&sock_addr, sock_size);
295 
296  if (result < 0) {
297  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
298  }
299 
300  return NONE_VAL();
301 }
302 
303 KRK_Method(socket,listen) {
304  METHOD_TAKES_AT_MOST(1);
305  int backlog = 0;
306  if (argc > 1) {
307  CHECK_ARG(1,int,krk_integer_type,val);
308  backlog = val >= 0 ? val : 0;
309  }
310 
311  int result = listen(self->sockfd, backlog);
312  if (result < 0) {
313  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
314  }
315 
316  return NONE_VAL();
317 }
318 
319 KRK_Method(socket,accept) {
320  struct sockaddr_storage addr;
321  socklen_t addrlen;
322 
323  int result = accept(self->sockfd, (struct sockaddr*)&addr, &addrlen);
324 
325  if (result < 0) {
326  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
327  }
328 
329  KrkTuple * outTuple = krk_newTuple(2);
330  krk_push(OBJECT_VAL(outTuple));
331 
332  struct socket * out = (struct socket*)krk_newInstance(SocketClass);
333  krk_push(OBJECT_VAL(out));
334 
335  out->sockfd = result;
336  out->family = self->family;
337  out->type = self->type;
338  out->proto = self->proto;
339 
340  outTuple->values.values[0] = krk_peek(0);
341  outTuple->values.count = 1;
342  krk_pop();
343 
344 
345  if (self->family == AF_INET) {
346  KrkTuple * addrTuple = krk_newTuple(2); /* TODO: Other formats */
347  krk_push(OBJECT_VAL(addrTuple));
348 
349  char hostname[NI_MAXHOST] = "";
350  getnameinfo((struct sockaddr*)&addr, addrlen, hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
351 
352  addrTuple->values.values[0] = OBJECT_VAL(krk_copyString(hostname,strlen(hostname)));
353  addrTuple->values.count = 1;
354  addrTuple->values.values[1] = INTEGER_VAL(htons(((struct sockaddr_in*)&addr)->sin_port));
355  addrTuple->values.count = 2;
356 #ifdef AF_INET6
357  } else if (self->family == AF_INET6) {
358  KrkTuple * addrTuple = krk_newTuple(2); /* TODO: Other formats */
359  krk_push(OBJECT_VAL(addrTuple));
360 
361  char hostname[NI_MAXHOST] = "";
362  getnameinfo((struct sockaddr*)&addr, addrlen, hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
363 
364  addrTuple->values.values[0] = OBJECT_VAL(krk_copyString(hostname,strlen(hostname)));
365  addrTuple->values.count = 1;
366  addrTuple->values.values[1] = INTEGER_VAL(htons(((struct sockaddr_in6*)&addr)->sin6_port));
367  addrTuple->values.count = 2;
368 #endif
369 #ifdef AF_UNIX
370  } else if (self->family == AF_UNIX) {
371  /* ignore remote path because it's meaningless? */
372  krk_push(OBJECT_VAL(S("")));
373 #endif
374  } else {
375  krk_push(NONE_VAL());
376  }
377 
378  outTuple->values.values[1] = krk_peek(0);
379  outTuple->values.count = 2;
380  krk_pop();
381 
382  return krk_pop();
383 }
384 
385 KRK_Method(socket,shutdown) {
386  METHOD_TAKES_EXACTLY(1);
387  CHECK_ARG(1,int,krk_integer_type,how);
388 
389  int result = shutdown(self->sockfd, how);
390 
391  if (result < 0) {
392  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
393  }
394 
395  return NONE_VAL();
396 }
397 
398 KRK_Method(socket,recv) {
399  METHOD_TAKES_AT_LEAST(1);
400  METHOD_TAKES_AT_MOST(2);
401  CHECK_ARG(1,int,krk_integer_type,bufsize);
402  int flags = 0;
403  if (argc > 2) {
404  CHECK_ARG(2,int,krk_integer_type,_flags);
405  flags = _flags;
406  }
407 
408  void * buf = malloc(bufsize);
409  ssize_t result = recv(self->sockfd, buf, bufsize, flags);
410  if (result < 0) {
411  free(buf);
412  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
413  }
414 
415  KrkBytes * out = krk_newBytes(result,buf);
416  free(buf);
417  return OBJECT_VAL(out);
418 }
419 
420 KRK_Method(socket,send) {
421  METHOD_TAKES_AT_LEAST(1);
422  METHOD_TAKES_AT_MOST(2);
423  CHECK_ARG(1,bytes,KrkBytes*,buf);
424  int flags = 0;
425  if (argc > 2) {
426  CHECK_ARG(2,int,krk_integer_type,_flags);
427  flags = _flags;
428  }
429 
430  ssize_t result = send(self->sockfd, (void*)buf->bytes, buf->length, flags);
431  if (result < 0) {
432  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
433  }
434 
435  return INTEGER_VAL(result);
436 }
437 
438 KRK_Method(socket,sendto) {
439  METHOD_TAKES_AT_LEAST(1);
440  METHOD_TAKES_AT_MOST(3);
441  CHECK_ARG(1,bytes,KrkBytes*,buf);
442  int flags = 0;
443  if (argc > 3) {
444  CHECK_ARG(2,int,krk_integer_type,_flags);
445  flags = _flags;
446  }
447 
448  struct sockaddr_storage sock_addr;
449  socklen_t sock_size = 0;
450 
451  /* What do we take? I guess a tuple for AF_INET */
452  int parseResult = socket_parse_address(self, argv[argc-1], &sock_addr, &sock_size);
453  if (parseResult) {
454  if (!(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION))
455  return krk_runtimeError(SocketError, "Unspecified error.");
456  return NONE_VAL();
457  }
458 
459  ssize_t result = sendto(self->sockfd, (void*)buf->bytes, buf->length, flags, (struct sockaddr*)&sock_addr, sock_size);
460  if (result < 0) {
461  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
462  }
463 
464  return INTEGER_VAL(result);
465 }
466 
467 
468 KRK_Method(socket,fileno) {
469  return INTEGER_VAL(self->sockfd);
470 }
471 
472 KRK_Method(socket,setsockopt) {
473  METHOD_TAKES_EXACTLY(3);
474  CHECK_ARG(1,int,krk_integer_type,level);
475  CHECK_ARG(2,int,krk_integer_type,optname);
476 
477  int result;
478 
479  if (IS_INTEGER(argv[3])) {
480  int val = AS_INTEGER(argv[3]);
481  result = setsockopt(self->sockfd, level, optname, (void*)&val, sizeof(int));
482  } else if (IS_BYTES(argv[3])) {
483  result = setsockopt(self->sockfd, level, optname, (void*)AS_BYTES(argv[3])->bytes, AS_BYTES(argv[3])->length);
484  } else {
485  return TYPE_ERROR(int or bytes,argv[3]);
486  }
487 
488  if (result < 0) {
489  return krk_runtimeError(SocketError, "Socket error: %s", strerror(errno));
490  }
491 
492  return NONE_VAL();
493 }
494 
495 KRK_Function(htons) {
496  FUNCTION_TAKES_EXACTLY(1);
497  CHECK_ARG(0,int,krk_integer_type,value);
498  return INTEGER_VAL(htons(value));
499 }
500 
501 KRK_Method(socket,family) {
502  if (argc > 1) return krk_runtimeError(vm.exceptions->attributeError, "readonly attribute");
503  return INTEGER_VAL(self->family);
504 }
505 
506 KRK_Method(socket,type) {
507  if (argc > 1) return krk_runtimeError(vm.exceptions->attributeError, "readonly attribute");
508  return INTEGER_VAL(self->type);
509 }
510 
511 KRK_Method(socket,proto) {
512  if (argc > 1) return krk_runtimeError(vm.exceptions->attributeError, "readonly attribute");
513  return INTEGER_VAL(self->proto);
514 }
515 
517  KRK_DOC(module, "Lightweight wrapper around the standard Berkeley sockets interface.");
518 
519  KrkClass * socket = krk_makeClass(module, &SocketClass, "socket", vm.baseClasses->objectClass);
520  SocketClass->allocSize = sizeof(struct socket);
521  KRK_DOC(BIND_METHOD(socket,__init__),
522  "@brief Create a socket object.\n"
523  "@arguments family=AF_INET,type=SOCK_STREAM,proto=0\n\n"
524  "Creates a new socket object for the given address family and type.");
525  BIND_METHOD(socket,__repr__);
526  KRK_DOC(BIND_METHOD(socket,bind),
527  "@brief Bind a socket to an address.\n"
528  "@arguments address\n\n"
529  "The format of @p address varies by address family. For @c AF_INET, @p address should be a "
530  "two-tuple of a string domain name and integer port number.");
531  KRK_DOC(BIND_METHOD(socket,listen),
532  "@brief Set a bound socket to listen.\n"
533  "@arguments backlog=0\n\n"
534  "Begin listening on a bound socket, keeping @p backlog connections in a queue.");
535  KRK_DOC(BIND_METHOD(socket,accept),
536  "@brief Accept a connection on a listening socket.\n\n"
537  "Accepts one connection and returns a two-tuple with a new socket object and "
538  "the address of the remote host.");
539  KRK_DOC(BIND_METHOD(socket,connect),
540  "@brief Connect a socket to a remote endpoint.\n"
541  "@arguments address\n\n"
542  "As with @ref socket_bind, the format of @p address varies.");
543  KRK_DOC(BIND_METHOD(socket,shutdown),
544  "@brief Shut down an active socket.\n"
545  "@arguments how\n\n"
546  "Gracefully closes an open socket.");
547  KRK_DOC(BIND_METHOD(socket,recv),
548  "@brief Receive data from a connected socket.\n"
549  "@arguments bufsize,[flags]\n\n"
550  "Receive up to @p bufsize bytes of data, which is returned as a @ref bytes object.");
551  KRK_DOC(BIND_METHOD(socket,send),
552  "@brief Send data to a connected socket.\n"
553  "@arguments buf,[flags]\n\n"
554  "Send the data in the @ref bytes object @p buf to the socket. Returns the number "
555  "of bytes written to the socket.");
556  KRK_DOC(BIND_METHOD(socket,sendto),
557  "@brief Send data to an socket with a particular destination.\n"
558  "@arguments buf,[flags],addr\n\n"
559  "Send the data in the @ref bytes object @p buf to the socket. Returns the number "
560  "of bytes written to the socket.");
561  KRK_DOC(BIND_METHOD(socket,fileno),
562  "@brief Get the file descriptor number for the underlying socket.");
563  KRK_DOC(BIND_METHOD(socket,setsockopt),
564  "@brief Set socket options.\n"
565  "@arguments level,optname,value\n\n"
566  "@p level and @p optname should be integer values defined by @c SOL and @c SO options. "
567  "@p value must be either an @ref int or a @ref bytes object.");
568 
569  BIND_PROP(socket,family);
570  BIND_PROP(socket,type);
571  BIND_PROP(socket,proto);
572 
573  krk_finalizeClass(SocketClass);
574 
575  BIND_FUNC(module, htons);
576 
577  /* Constants */
578 #define SOCK_CONST(o) krk_attachNamedValue(&module->fields, #o, INTEGER_VAL(o));
579 
586  SOCK_CONST(AF_INET);
587 #ifdef AF_INET6
588  SOCK_CONST(AF_INET6);
589 #endif
590 #ifdef AF_UNIX
591  SOCK_CONST(AF_UNIX);
592 #endif
593 
594  /* SOCK_ constants, similarly */
595  SOCK_CONST(SOCK_STREAM);
596  SOCK_CONST(SOCK_DGRAM);
597 #ifdef SOCK_RAW
598  SOCK_CONST(SOCK_RAW);
599 #endif
600 
601  /* These are OR'd together with the above on Linux */
602 #ifdef SOCK_NONBLOCK
603  SOCK_CONST(SOCK_NONBLOCK);
604 #endif
605 #ifdef SOCK_CLOEXEC
606  SOCK_CONST(SOCK_CLOEXEC);
607 #endif
608 
609 #ifdef SHUT_RD
610  SOCK_CONST(SHUT_RD);
611  SOCK_CONST(SHUT_WR);
612  SOCK_CONST(SHUT_RDWR);
613 #endif
614 
615  SOCK_CONST(SOL_SOCKET);
616 
617  SOCK_CONST(SO_REUSEADDR);
618 
619  krk_makeClass(module, &SocketError, "SocketError", vm.exceptions->baseException);
620  KRK_DOC(SocketError, "Raised on faults from socket functions.");
621  krk_finalizeClass(SocketError);
622 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
KRK_Module(socket)
Immutable sequence of bytes.
Definition: object.h:105
KrkBytes * krk_newBytes(size_t length, uint8_t *source)
Create a new byte array.
Definition: object.c:364
Type object.
Definition: object.h:205
size_t allocSize
Size to allocate when creating instances of this class.
Definition: object.h:212
KrkClass * krk_makeClass(KrkInstance *module, KrkClass **_class, const char *name, KrkClass *base)
Convenience function for creating new types.
Definition: vm.c:202
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
Definition: vm.c:227
An object of a class.
Definition: object.h:271
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:340
KrkString * krk_copyString(const char *chars, size_t length)
Obtain a string object representation of the given C string.
Definition: object.c:222
int flags
Definition: vm.h:169
Immutable sequence of arbitrary values.
Definition: object.h:313
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Definition: object.h:315
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
Definition: object.c:354
KrkValue * values
Definition: value.h:78
size_t count
Definition: value.h:77
Stack reference or primative value.
Utilities for creating native bindings.
#define krk_parseArgs(f, n,...)
Parse arguments to a function while accepting keyword arguments.
Definition: util.h:360
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:304
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
#define vm
Convenience macro for namespacing.
Definition: vm.h:262
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:169
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:156
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:177