MysoreScript
runtime.cc
Go to the documentation of this file.
1 #include "runtime.hh"
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <iostream>
6 #include <unordered_map>
7 #include <vector>
8 #include <gc.h>
9 
10 using namespace MysoreScript;
11 
12 namespace
13 {
14 
19 std::vector<std::string> selNames;
20 
26 {
27  auto selName = selNames[sel];
28  if (!obj)
29  {
30  std::cerr << std::endl << "ERROR: method " << selName
31  << " called on null object." << std::endl;
32  return nullptr;
33  }
34  Class *cls = isInteger(obj) ? &SmallIntClass : obj->isa;
35  std::cerr << std::endl << "ERROR: " << cls->className
36  << " does not respond to selector " << selName << '.' << std::endl;
37  return nullptr;
38 }
39 
43 struct File
44 {
48  intptr_t fd;
49 };
50 
55 File *FileOpen(File *f, Selector sel, String *file)
56 {
57  if (f->fd > 0)
58  {
59  close(f->fd);
60  }
61  // The file name must be a string
62  if (file == nullptr || isInteger(reinterpret_cast<Obj>(file)) || file->isa != &StringClass)
63  {
64  return nullptr;
65  }
66  std::string filename(file->characters, getInteger(file->length));
67  f->fd = open(filename.c_str(), O_RDWR | O_CREAT, 0600);
68  return f;
69 }
75 {
76  if (f->fd > 0)
77  {
78  close(f->fd);
79  f->fd = 0;
80  }
81  return reinterpret_cast<Obj>(f);
82 }
83 
89 {
90  int fd = f->fd ? f->fd : STDIN_FILENO;
91  std::string buffer;
92  char c;
93  // This is very inefficient
94  while (1 == read(fd, &c, 1))
95  {
96  if (c == '\n')
97  {
98  break;
99  }
100  buffer.push_back(c);
101  }
102  uintptr_t len = buffer.size();
103  if (len == 0)
104  {
105  return nullptr;
106  }
107  String *newStr = gcAlloc<String>(len);
108  newStr->isa = &StringClass;
109  newStr->length = createSmallInteger(len);
110  memcpy(newStr->characters, buffer.c_str(), len);
111  return newStr;
112 }
113 
118 {
119  // The data must be a string
120  if (data == nullptr || isInteger(reinterpret_cast<Obj>(data)) || data->isa != &StringClass)
121  {
122  return nullptr;
123  }
124  int fd = f->fd ? f->fd : STDOUT_FILENO;
125  // FIXME: Handle interrupted system calls correctly!
126  write(fd, data->characters, getInteger(data->length));
127  return reinterpret_cast<Obj>(f);
128 }
129 
134 {
135  return str->length;
136 }
137 
142 {
143  // If the index isn't a small integer, then return 0.
144  if (!isInteger(idx))
145  {
146  return nullptr;
147  }
148  intptr_t i = getInteger(idx);
149  intptr_t len = getInteger(str->length);
150  // Check that the access is in bounds.
151  if (i >= len && (i > 0))
152  {
153  return nullptr;
154  }
155  // Turn the small integer object into a primitive integer and use it to
156  // dereference the character array, then turn the result into an integer
157  // value.
158  return createSmallInteger(str->characters[i]);
159 }
164 {
165  if (arr->length == 0)
166  {
167  arr->length = createSmallInteger(0);
168  }
169  return arr->length;
170 }
174 Obj ArrayAt(Array *arr, Selector sel, Obj idx)
175 {
176  if (!isInteger(idx))
177  {
178  return nullptr;
179  }
180  intptr_t i = getInteger(idx);
181  intptr_t len = arr->length ? getInteger(arr->length) : 0;
182  // Check that the access is in bounds.
183  if (i >= len && (i > 0))
184  {
185  return nullptr;
186  }
187  return arr->buffer[i];
188 }
192 Obj ArrayAtPut(Array *arr, Selector sel, Obj idx, Obj obj)
193 {
194  // If the index isn't an integer, return null
195  if (!isInteger(idx))
196  {
197  return nullptr;
198  }
199  intptr_t i = getInteger(idx);
200  intptr_t len = arr->length ? getInteger(arr->length) : 0;
201  // Check that the index is positive.
202  if (i < 0)
203  {
204  return nullptr;
205  }
206  intptr_t bufferSize = arr->bufferSize ? getInteger(arr->bufferSize) : 0;
207  if (i >= bufferSize)
208  {
209  // Note that this is really inefficient, but will stress the GC a
210  // little bit, which should make life a bit more interesting for
211  // students...
212  size_t newSize = i + 1;
213  Obj *buffer = reinterpret_cast<Obj*>(GC_MALLOC(newSize * sizeof(Obj)));
214  memcpy(buffer, arr->buffer, len * sizeof(Obj));
215  arr->buffer = buffer;
216  arr->bufferSize = createSmallInteger(newSize);
217  }
218  if (i >= len)
219  {
220  arr->length = createSmallInteger(i+1);
221  }
222  arr->buffer[i] = obj;
223  return obj;
224 }
225 
230 {
231  std::cerr << getInteger(str) << std::endl;
232  return nullptr;
233 }
238 {
239  std::cout << getInteger(str) << std::endl;
240  return nullptr;
241 }
242 
247 {
248  fwrite(str->characters, getInteger(str->length), 1, stderr);
249  return nullptr;
250 }
255 {
256  fwrite(str->characters, getInteger(str->length), 1, stdout);
257  return nullptr;
258 }
262 Obj StringAdd(String *str, Selector sel, String *other)
263 {
264  // If we are trying to concatenate something that's not a string, return
265  // null
266  if (other == nullptr || isInteger(reinterpret_cast<Obj>(other)) || other->isa != &StringClass)
267  {
268  return nullptr;
269  }
270  uintptr_t len1 = getInteger(str->length);
271  uintptr_t len2 = getInteger(other->length);
272  uintptr_t lenTotal = len1+len2;
273  String *newStr = gcAlloc<String>(lenTotal);
274  newStr->isa = &StringClass;
275  newStr->length = createSmallInteger(lenTotal);
276  memcpy(newStr->characters, str->characters, len1);
277  memcpy(newStr->characters+len1, other->characters, len2);
278  return reinterpret_cast<Obj>(newStr);
279 }
280 
284 Obj StringCmp(String *str, Selector sel, String *other)
285 {
286  // If we are trying to compare something that's not a string, return null
287  if (other == nullptr || isInteger(reinterpret_cast<Obj>(other)) || other->isa != &StringClass)
288  {
289  return nullptr;
290  }
291  uintptr_t len1 = getInteger(str->length);
292  uintptr_t len2 = getInteger(other->length);
293  uintptr_t len = std::min(len1, len2);
294  int result = memcmp(str->characters, other->characters, len);
295  if (result == 0)
296  {
297  if (len1 > len2)
298  {
299  result = str->characters[len2];
300  }
301  else if (len1 < len2)
302  {
303  result = 0 - other->characters[len1];
304  }
305  }
306  return createSmallInteger(result);
307 }
308 
313 {
314  length = 1,
320  at,
331 };
332 
336 const char *StaticSelectorNames[] =
337 {
338  "length",
339  "charAt",
340  "dump",
341  "print",
342  "invoke",
343  "atPut",
344  "at",
345  "add",
346  "sub",
347  "mul",
348  "div",
349  "compare",
350  "open",
351  "close",
352  "readline",
353  "write"
354 };
355 static_assert(sizeof(StaticSelectorNames) / sizeof(char*) ==
356  LAST_STATIC_SELECTOR-1, "Static selector names and enum out of sync");
357 
361 struct Method FileMethods[] =
362 {
363  {
364  open,
365  0,
366  reinterpret_cast<CompiledMethod>(FileOpen),
367  nullptr
368  },
369  {
370  close,
371  0,
372  reinterpret_cast<CompiledMethod>(FileClose),
373  nullptr
374  },
375  {
376  readline,
377  0,
378  reinterpret_cast<CompiledMethod>(FileReadLine),
379  nullptr
380  },
381  {
382  write,
383  0,
384  reinterpret_cast<CompiledMethod>(FileWrite),
385  nullptr
386  }
387 };
392 {
393  {
394  length,
395  0,
396  reinterpret_cast<CompiledMethod>(StringLength),
397  nullptr
398  },
399  {
400  charAt,
401  1,
402  reinterpret_cast<CompiledMethod>(StringCharAt),
403  nullptr
404  },
405  {
406  dump,
407  0,
408  reinterpret_cast<CompiledMethod>(StringDump),
409  nullptr
410  },
411  {
412  print,
413  0,
414  reinterpret_cast<CompiledMethod>(StringPrint),
415  nullptr
416  },
417  {
418  add,
419  1,
420  reinterpret_cast<CompiledMethod>(StringAdd),
421  nullptr
422  },
423  {
424  compare,
425  1,
426  reinterpret_cast<CompiledMethod>(StringCmp),
427  nullptr
428  }
429 };
434 {
435  {
436  dump,
437  0,
438  reinterpret_cast<CompiledMethod>(NumberDump),
439  nullptr
440  },
441  {
442  print,
443  0,
444  reinterpret_cast<CompiledMethod>(NumberPrint),
445  nullptr
446  }
447 };
451 struct Method ArrayMethods[] =
452 {
453  {
454  length,
455  0,
456  reinterpret_cast<CompiledMethod>(ArrayLength),
457  nullptr
458  },
459  {
460  at,
461  1,
462  reinterpret_cast<CompiledMethod>(ArrayAt),
463  nullptr
464  },
465  {
466  atPut,
467  2,
468  reinterpret_cast<CompiledMethod>(ArrayAtPut),
469  nullptr
470  }
471 };
475 const char *StringIvars[] = { "length" };
479 const char *ArrayIvars[] = { "length", "bufferSize", "buffer" };
483 const char *FileIvars[] = { "fd" };
484 
485 } // namespace
486 
487 namespace MysoreScript
488 {
493 {
494  NULL,
495  "String",
496  sizeof(StringMethods) / sizeof(Method),
497  sizeof(StringIvars) / sizeof(char*),
500 };
504 struct Class FileClass =
505 {
506  NULL,
507  "File",
508  sizeof(FileMethods) / sizeof(Method),
509  sizeof(FileIvars) / sizeof(char*),
510  FileMethods,
511  FileIvars
512 };
517 {
518  NULL,
519  "Array",
520  sizeof(ArrayMethods) / sizeof(Method),
521  sizeof(ArrayIvars) / sizeof(char*),
522  ArrayMethods,
523  ArrayIvars
524 };
532 {
533  NULL,
534  "Number",
535  sizeof(NumberMethods) / sizeof(Method),
536  0,
538  nullptr
539 };
544 {
545  NULL,
546  "Closure",
547  0,
548  0,
549  nullptr,
550  nullptr
551 };
552 
553 Selector lookupSelector(const std::string &str)
554 {
555  static std::unordered_map<std::string, Selector> selectors;
556  // If we don't have any selectors in this array yet then register all of the
557  // static ones.
558  if (selectors.empty())
559  {
560  selNames.push_back("<invalid>");
561  for (int i=1 ; i<LAST_STATIC_SELECTOR ; i++)
562  {
563  selectors[std::string(StaticSelectorNames[i-1])] = i;
564  selNames.push_back(StaticSelectorNames[i-1]);
565  }
566  }
567  // Look up the selector
568  size_t next = selectors.size() + 1;
569  Selector &sel = selectors[str];
570  // If it doesn't exist, register it
571  if (sel == 0)
572  {
573  sel = next;
574  selNames.push_back(str);
575  }
576  return sel;
577 }
578 
584 static std::unordered_map<std::string, struct Class*> classTable;
585 
586 static void registerClasses()
587 {
588  if (classTable.empty())
589  {
590  classTable["String"] = &StringClass;
591  classTable["Array"] = &ArrayClass;
592  classTable["File"] = &FileClass;
593  }
594 }
595 
596 void registerClass(const std::string &name, struct Class *cls)
597 {
598  registerClasses();
599  classTable[name] = cls;
600 }
601 struct Class* lookupClass(const std::string &name)
602 {
603  registerClasses();
604  return classTable[name];
605 }
606 Obj newObject(struct Class *cls)
607 {
608  // Allocate space for the object
609  Obj obj = gcAlloc<struct Object>(sizeof(Obj)*cls->indexedIVarCount);
610  // Set its class pointer
611  obj->isa = cls;
612  return obj;
613 }
614 
616 {
617  // Perform a very simple linear search (O(n) in the number of methods in the
618  // class hierarchy) to find the relevant method.
619  for (; cls ; cls = cls->superclass)
620  {
621  for (intptr_t i=0 ; i<cls->methodCount; i++)
622  {
623  if (cls->methodList[i].selector == sel)
624  {
625  return &cls->methodList[i];
626  }
627  }
628  }
629  return nullptr;
630 }
631 
633  int argCount)
634 {
635  switch (argCount)
636  {
637  default:
638  assert(0 && "Too many arguments!");
639  return nullptr;
640  case 0:
641  return (reinterpret_cast<Obj(*)(Obj, Selector)>(m))(receiver, sel);
642  case 1:
643  return (reinterpret_cast<Obj(*)(Obj, Selector, Obj)>(m))(receiver, sel, args[0]);
644  case 2:
645  return (reinterpret_cast<Obj(*)(Obj, Selector, Obj, Obj)>(m))(receiver, sel, args[0],
646  args[1]);
647  case 3:
648  return (reinterpret_cast<Obj(*)(Obj, Selector, Obj, Obj, Obj)>(m))(receiver, sel,
649  args[0], args[1], args[2]);
650  case 4:
651  return (reinterpret_cast<Obj(*)(Obj, Selector, Obj, Obj, Obj, Obj)>(m))(receiver,
652  sel, args[0], args[1], args[2], args[3]);
653  }
654 }
655 
657  int argCount)
658 {
659  switch (argCount)
660  {
661  default:
662  assert(0 && "Too many arguments!");
663  return nullptr;
664  case 0:
665  return (reinterpret_cast<Obj(*)(Closure*)>(m))(receiver);
666  case 1:
667  return (reinterpret_cast<Obj(*)(Closure*, Obj)>(m))(receiver, args[0]);
668  case 2:
669  return (reinterpret_cast<Obj(*)(Closure*, Obj, Obj)>(m))(receiver, args[0], args[1]);
670  case 3:
671  return (reinterpret_cast<Obj(*)(Closure*, Obj, Obj, Obj)>(m))(receiver, args[0], args[1],
672  args[2]);
673  case 4:
674  return (reinterpret_cast<Obj(*)(Closure*, Obj, Obj, Obj, Obj)>(m))(receiver, args[0],
675  args[1], args[2], args[3]);
676  }
677 }
678 
679 extern "C"
680 {
682 {
683  return compiledMethodForSelector(lhs, add)(lhs, add, rhs);
684 }
686 {
687  return compiledMethodForSelector(lhs, sub)(lhs, sub, rhs);
688 }
690 {
691  return compiledMethodForSelector(lhs, mul)(lhs, mul, rhs);
692 }
694 {
696 }
698 {
699  // If this object is null, we'll call the invalid method handler when we
700  // invoke a method on it. Note that we could easily follow the Smalltalk
701  // model of having a Null class whose methods are invoked, or the
702  // Objective-C model of always returning null here.
703  if (!obj)
704  {
705  return reinterpret_cast<CompiledMethod>(invalidMethod);
706  }
707  // If it's a small integer, then use the small integer class, otherwise
708  // follow the class pointer.
709  Class *cls = isInteger(obj) ? &SmallIntClass : obj->isa;
710  Method *mth = methodForSelector(cls, sel);
711  // If the method doesn't exist, return the invalid method function,
712  // otherwise return the function that we've just looked up.
713  if (!mth)
714  {
715  return reinterpret_cast<CompiledMethod>(invalidMethod);
716  }
717  return mth->function;
718 }
719 }
720 
721 } // namespace MysoreScript
722 
Obj StringLength(String *str, Selector sel)
The .length() method for String objects.
Definition: runtime.cc:133
struct Class * isa
The pointer to the class of this object.
Definition: runtime.hh:150
Object *(* CompiledMethod)(Object *, Selector,...)
A compiled method is a function that takes an object (the receiver) and the selector as implicit argu...
Definition: runtime.hh:75
Class * isa
The class pointer.
Definition: runtime.cc:46
Obj StringCmp(String *str, Selector sel, String *other)
Compare two strings, returning an integer representing the ordering.
Definition: runtime.cc:284
Obj FileClose(File *f, Selector sel)
The close method on File objects.
Definition: runtime.cc:74
Object * Obj
Object pointer.
Definition: runtime.hh:33
Obj mysoreScriptAdd(Obj lhs, Obj rhs)
Helper function called by compiled code for the + operator on objects that are not small (embedded in...
Definition: runtime.cc:681
Obj callCompiledClosure(ClosureInvoke m, Closure *receiver, Obj *args, int argCount)
Calls a compiled closure from the specified argument list.
Definition: runtime.cc:656
String * FileReadLine(File *f, Selector sel)
The readline method on File objects.
Definition: runtime.cc:88
Obj ArrayLength(Array *arr, Selector sel)
The .length() method for Array objects.
Definition: runtime.cc:163
Obj mysoreScriptDiv(Obj lhs, Obj rhs)
Helper function called by compiled code for the / operator on objects that are not small (embedded in...
Definition: runtime.cc:693
uint32_t Selector
Selectors are unique identifiers for methods.
Definition: runtime.hh:70
struct Method StringMethods[]
Method table for the String class.
Definition: runtime.cc:391
Obj bufferSize
The size of the buffer.
Definition: runtime.hh:169
StaticSelectors
Selectors for methods that are defined as part of the runtime.
Definition: runtime.cc:312
struct Class ArrayClass
The Array class structure.
Definition: runtime.cc:516
struct Class SmallIntClass
The SmallInt (Number) class structure.
Definition: runtime.cc:531
Class * isa
Class pointer.
Definition: runtime.hh:184
Obj NumberDump(Obj str, Selector sel)
The .dump() method for Number objects.
Definition: runtime.cc:229
struct Method * methodList
An array of methodCount elements describing the methods that this class implements.
Definition: runtime.hh:132
intptr_t fd
The file descriptor to use.
Definition: runtime.cc:48
Selector lookupSelector(const std::string &str)
Looks up the selector for a specified string value, registering a new value if this is the first time...
Definition: runtime.cc:553
Obj newObject(struct Class *cls)
Instantiate an object.
Definition: runtime.cc:606
Object *(* ClosureInvoke)(Closure *,...)
A compiled closure invoke function.
Definition: runtime.hh:80
Obj mysoreScriptMul(Obj lhs, Obj rhs)
Helper function called by compiled code for the * operator on objects that are not small (embedded in...
Definition: runtime.cc:689
void registerClass(const std::string &name, struct Class *cls)
Register a newly constructed class.
Definition: runtime.cc:596
struct Method FileMethods[]
Methods for the file class.
Definition: runtime.cc:361
struct Class * lookupClass(const std::string &name)
Look up an existing class.
Definition: runtime.cc:601
const char * StringIvars[]
The names of the instance variables in the String class.
Definition: runtime.cc:475
A generic MysoreScript object.
Definition: runtime.hh:144
Obj ArrayAtPut(Array *arr, Selector sel, Obj idx, Obj obj)
The .atPut(idx, obj) method for Array objects.
Definition: runtime.cc:192
struct Class StringClass
The String class structure.
Definition: runtime.cc:492
Obj StringCharAt(String *str, Selector sel, Obj idx)
The .charAt(idx) method for String objects.
Definition: runtime.cc:141
Obj NumberPrint(Obj str, Selector sel)
The .print() method for Number objects.
Definition: runtime.cc:237
struct Class ClosureClass
The Closure class structure.
Definition: runtime.cc:543
CompiledMethod function
The compiled method, if one exists.
Definition: runtime.hh:98
Method * methodForSelector(Class *cls, Selector sel)
Looks up the Method that should be invoked for the specified selector on this class.
Definition: runtime.cc:615
CompiledMethod compiledMethodForSelector(Obj obj, Selector sel)
Look up the compiled method to call for a specific selector.
Definition: runtime.cc:697
Obj callCompiledMethod(CompiledMethod m, Obj receiver, Selector sel, Obj *args, int argCount)
Calls a compiled method, constructing the correct argument frame based on the arguments.
Definition: runtime.cc:632
char characters[0]
An array of characters.
Definition: runtime.hh:193
Obj StringAdd(String *str, Selector sel, String *other)
The + method on a string, allocates a new string with the specified length.
Definition: runtime.cc:262
The layout of the primitive Array class in MysoreScript.
Definition: runtime.hh:156
Obj ArrayAt(Array *arr, Selector sel, Obj idx)
The .at(idx) method for Array objects.
Definition: runtime.cc:174
struct Class * superclass
The superclass of this class, if it has one, or a null pointer if it is a root class.
Definition: runtime.hh:115
const char * ArrayIvars[]
The names of the instance variables in the Array class.
Definition: runtime.cc:479
intptr_t getInteger(Obj o)
Assuming that o is a small integer (an integer embedded in a pointer), return it as a C integer...
Definition: runtime.hh:51
int32_t methodCount
The number of methods that this class implements.
Definition: runtime.hh:123
const char * FileIvars[]
The names of the instance variables in the File class.
Definition: runtime.cc:483
The primitive String class in MysoreScript.
Definition: runtime.hh:179
int32_t indexedIVarCount
The number of indexed instance variables that this class has.
Definition: runtime.hh:127
struct Method NumberMethods[]
Method table for the Number class.
Definition: runtime.cc:433
std::vector< std::string > selNames
Global vector of selector names.
Definition: runtime.cc:19
Obj StringDump(String *str, Selector sel)
The .dump() method for String objects.
Definition: runtime.cc:246
const char * className
The name of this class.
Definition: runtime.hh:119
Obj createSmallInteger(intptr_t i)
Construct a small integer object from the given integer.
Definition: runtime.hh:59
Obj invalidMethod(Obj obj, Selector sel)
Invalid method function.
Definition: runtime.cc:25
struct Class FileClass
The File class structure.
Definition: runtime.cc:504
Obj * buffer
The buffer storing the values in this array.
Definition: runtime.hh:173
Obj FileWrite(File *f, Selector sel, String *data)
The write method on File objects.
Definition: runtime.cc:117
Obj mysoreScriptSub(Obj lhs, Obj rhs)
Helper function called by compiled code for the - operator on objects that are not small (embedded in...
Definition: runtime.cc:685
Obj length
Length of the array (number of elements in it).
Definition: runtime.hh:165
File * FileOpen(File *f, Selector sel, String *file)
The open method on File objects.
Definition: runtime.cc:55
Obj length
The number of characters in the string.
Definition: runtime.hh:188
Struct holding metadata about a class.
Definition: runtime.hh:109
Obj StringPrint(String *str, Selector sel)
The .dump() method for String objects.
Definition: runtime.cc:254
Methods in a class&#39;s method list.
Definition: runtime.hh:85
The structure representing MysoreScript File objects.
Definition: runtime.cc:43
const char * StaticSelectorNames[]
The names of the selectors in the StaticSelectors enumeration.
Definition: runtime.cc:336
bool isInteger(Obj o)
Is this object a small integer (lowest bit is 1, next two bits are 0).
Definition: runtime.hh:43
struct Method ArrayMethods[]
Method table for the Array class.
Definition: runtime.cc:451
The layout of all closures in MysoreScript.
Definition: runtime.hh:199
Selector selector
The selector that this method applies to.
Definition: runtime.hh:90