VirtualMachine.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2019 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #include "VirtualMachine.h"
  24. #include "../api/timeAPI.h"
  25. using namespace dsr;
  26. VirtualMachine::VirtualMachine(const ReadableString& code, const Handle<PlanarMemory>& memory,
  27. const InsSig* machineInstructions, int32_t machineInstructionCount,
  28. const VMTypeDef* machineTypes, int32_t machineTypeCount)
  29. : memory(memory), machineInstructions(machineInstructions), machineInstructionCount(machineInstructionCount),
  30. machineTypes(machineTypes), machineTypeCount(machineTypeCount) {
  31. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  32. printText("Starting media machine.\n");
  33. #endif
  34. this->methods.pushConstruct(U"<init>", 0, this->machineTypeCount);
  35. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  36. printText("Reading assembly.\n");
  37. #endif
  38. string_split_callback([this](ReadableString currentLine) {
  39. // If the line has a comment, then skip everything from #
  40. int commentIndex = string_findFirst(currentLine, U'#');
  41. if (commentIndex > -1) {
  42. currentLine = string_before(currentLine, commentIndex);
  43. }
  44. currentLine = string_removeOuterWhiteSpace(currentLine);
  45. int colonIndex = string_findFirst(currentLine, U':');
  46. if (colonIndex > -1) {
  47. ReadableString command = string_removeOuterWhiteSpace(string_before(currentLine, colonIndex));
  48. ReadableString argumentLine = string_after(currentLine, colonIndex);
  49. List<String> arguments = string_split(argumentLine, U',', true);
  50. this->interpretMachineWord(command, arguments);
  51. } else if (string_length(currentLine) > 0) {
  52. throwError("Unexpected line \"", currentLine, "\".\n");
  53. }
  54. }, code, U'\n');
  55. // Calling "<init>" to execute global commands
  56. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  57. printText("Initializing global machine state.\n");
  58. #endif
  59. this->executeMethod(0);
  60. }
  61. int VirtualMachine::findMethod(const ReadableString& name) {
  62. for (int i = 0; i < this->methods.length(); i++) {
  63. if (string_caseInsensitiveMatch(this->methods[i].name, name)) {
  64. return i;
  65. }
  66. }
  67. return -1;
  68. }
  69. Variable* VirtualMachine::getResource(const ReadableString& name, int methodIndex) {
  70. Variable* result = this->methods[methodIndex].getLocal(name);
  71. if (result) {
  72. // If found, take the local variable
  73. return result;
  74. } else if (methodIndex > 0) {
  75. // If not found but having another scope, look for global variables in the global initiation method
  76. return getResource(name, 0);
  77. } else {
  78. return nullptr;
  79. }
  80. }
  81. void VirtualMachine::addMachineWord(MachineOperation operation, const List<VMA>& args) {
  82. this->machineWords.pushConstruct(operation, args);
  83. this->methods[this->methods.length() - 1].instructionCount++;
  84. }
  85. void VirtualMachine::addMachineWord(MachineOperation operation) {
  86. this->machineWords.pushConstruct(operation);
  87. this->methods[this->methods.length() - 1].instructionCount++;
  88. }
  89. void VirtualMachine::interpretCommand(const ReadableString& operation, const List<VMA>& resolvedArguments) {
  90. // Compare the input with overloads
  91. for (int s = 0; s < machineInstructionCount; s++) {
  92. if (machineInstructions[s].matches(operation, resolvedArguments)) {
  93. this->addMachineWord(machineInstructions[s].operation, resolvedArguments);
  94. return;
  95. }
  96. }
  97. // TODO: Allow asking the specific machine type what the given types are called.
  98. String message = string_combine(U"\nError! ", operation, U" does not match any overload for the given arguments:\n");
  99. for (int s = 0; s < machineInstructionCount; s++) {
  100. const InsSig* signature = &machineInstructions[s];
  101. if (string_caseInsensitiveMatch(signature->name, operation)) {
  102. string_append(message, " * ", signature->name, "(");
  103. for (int a = 0; a < signature->arguments.length(); a++) {
  104. if (a > 0) {
  105. string_append(message, ", ");
  106. }
  107. const ArgSig* argument = &signature->arguments[a];
  108. string_append(message, argument->name);
  109. }
  110. string_append(message, ")\n");
  111. }
  112. }
  113. throwError(message);
  114. }
  115. // TODO: Inline into declareVariable
  116. Variable* VirtualMachine::declareVariable_aux(const VMTypeDef& typeDef, int methodIndex, AccessType access, const ReadableString& name, bool initialize, const ReadableString& defaultValueText) {
  117. // Make commonly used data more readable
  118. bool global = methodIndex == 0;
  119. Method* currentMethod = &this->methods[methodIndex];
  120. // Assert correctness
  121. if (global && (access == AccessType::Input || access == AccessType::Output)) {
  122. throwError("Cannot declare inputs or outputs globally!\n");
  123. }
  124. // Count how many variables the method has of each type
  125. currentMethod->count[typeDef.dataType]++;
  126. this->methods[methodIndex].unifiedLocalIndices[typeDef.dataType].push(this->methods[methodIndex].locals.length());
  127. // Count inputs for calling the method
  128. if (access == AccessType::Input) {
  129. if (this->methods[methodIndex].declaredNonInput) {
  130. throwError("Cannot declare input \"", name, "\" after a non-input has been declared. Declare inputs, outputs and locals in order.\n");
  131. }
  132. this->methods[methodIndex].inputCount++;
  133. } else if (access == AccessType::Output) {
  134. if (this->methods[methodIndex].declaredLocals) {
  135. throwError("Cannot declare output \"", name, "\" after a local has been declared. Declare inputs, outputs and locals in order.\n");
  136. }
  137. this->methods[methodIndex].outputCount++;
  138. this->methods[methodIndex].declaredNonInput = true;
  139. } else if (access == AccessType::Hidden) {
  140. this->methods[methodIndex].declaredLocals = true;
  141. this->methods[methodIndex].declaredNonInput = true;
  142. }
  143. // Declare the variable so that code may find the type and index by name
  144. int typeLocalIndex = currentMethod->count[typeDef.dataType] - 1;
  145. int globalIndex = typeLocalToGlobalIndex(global, typeLocalIndex);
  146. this->methods[methodIndex].locals.pushConstruct(name, access, &typeDef, typeLocalIndex, global);
  147. if (initialize && access != AccessType::Input) {
  148. // Generate instructions for assigning the variable's initial value
  149. typeDef.initializer(*this, globalIndex, defaultValueText);
  150. }
  151. return &this->methods[methodIndex].locals.last();
  152. }
  153. Variable* VirtualMachine::declareVariable(int methodIndex, AccessType access, const ReadableString& typeName, const ReadableString& name, bool initialize, const ReadableString& defaultValueText) {
  154. if (this->getResource(name, methodIndex)) {
  155. throwError("A resource named \"", name, "\" already exists! Be aware that resource names are case insensitive.\n");
  156. return nullptr;
  157. } else {
  158. // Loop over type definitions to find a match
  159. const VMTypeDef* typeDef = getMachineType(typeName);
  160. if (typeDef) {
  161. if (string_length(defaultValueText) > 0 && !typeDef->allowDefaultValue) {
  162. throwError("The variable \"", name, "\" doesn't have an immediate constructor for \"", typeName, "\".\n");
  163. }
  164. return this->declareVariable_aux(*typeDef, methodIndex, access, name, initialize, defaultValueText);
  165. } else {
  166. throwError("Cannot declare variable of unknown type \"", typeName, "\"!\n");
  167. return nullptr;
  168. }
  169. }
  170. }
  171. VMA VirtualMachine::VMAfromText(int methodIndex, const ReadableString& content) {
  172. DsrChar first = content[0];
  173. DsrChar second = content[1];
  174. if (first == U'-' && second >= U'0' && second <= U'9') {
  175. return VMA(FixedPoint::fromText(content));
  176. } else if (first >= U'0' && first <= U'9') {
  177. return VMA(FixedPoint::fromText(content));
  178. } else {
  179. int leftIndex = string_findFirst(content, U'<');
  180. int rightIndex = string_findLast(content, U'>');
  181. if (leftIndex > -1 && rightIndex > -1) {
  182. ReadableString name = string_removeOuterWhiteSpace(string_before(content, leftIndex));
  183. ReadableString typeName = string_removeOuterWhiteSpace(string_inclusiveRange(content, leftIndex + 1, rightIndex - 1));
  184. ReadableString remainder = string_removeOuterWhiteSpace(string_after(content, rightIndex));
  185. if (string_length(remainder) > 0) {
  186. throwError("No code allowed after > for in-place temp declarations!\n");
  187. }
  188. Variable* resource = this->declareVariable(methodIndex, AccessType::Hidden, typeName, name, false, U"");
  189. if (resource) {
  190. return VMA(resource->typeDescription->dataType, resource->getGlobalIndex());
  191. } else {
  192. throwError("The resource \"", name, "\" could not be declared as \"", typeName, "\"!\n");
  193. return VMA(FixedPoint());
  194. }
  195. } else if (leftIndex > -1) {
  196. throwError("Using < without > for in-place temp allocation.\n");
  197. return VMA(FixedPoint());
  198. } else if (rightIndex > -1) {
  199. throwError("Using > without < for in-place temp allocation.\n");
  200. return VMA(FixedPoint());
  201. } else {
  202. Variable* resource = getResource(content, methodIndex);
  203. if (resource) {
  204. return VMA(resource->typeDescription->dataType, resource->getGlobalIndex());
  205. } else {
  206. throwError("The resource \"", content, "\" could not be found! Make sure that it's declared before being used.\n");
  207. return VMA(FixedPoint());
  208. }
  209. }
  210. }
  211. }
  212. static ReadableString getArg(const List<String>& arguments, int32_t index) {
  213. if (index < 0 || index >= arguments.length()) {
  214. return U"";
  215. } else {
  216. return string_removeOuterWhiteSpace(arguments[index]);
  217. }
  218. }
  219. void VirtualMachine::addReturnInstruction() {
  220. addMachineWord([](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
  221. if (memory.callStack.length() > 0) {
  222. // Return to caller
  223. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  224. printText("Returning from \"", machine.methods[memory.current.methodIndex].name, "\" to caller \"", machine.methods[memory.callStack.last().methodIndex].name, "\"\n");
  225. machine.debugPrintMemory();
  226. #endif
  227. memory.current = memory.callStack.last();
  228. memory.callStack.pop();
  229. memory.current.programCounter++;
  230. } else {
  231. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  232. printText("Returning from \"", machine.methods[memory.current.methodIndex].name, "\"\n");
  233. #endif
  234. // Leave the virtual machine
  235. memory.current.programCounter = -1;
  236. }
  237. });
  238. }
  239. void VirtualMachine::addCallInstructions(const List<String>& arguments) {
  240. if (arguments.length() < 1) {
  241. throwError(U"Cannot make a call without the name of a method!\n");
  242. }
  243. // TODO: Allow calling methods that aren't defined yet.
  244. int currentMethodIndex = this->methods.length() - 1;
  245. ReadableString methodName = string_removeOuterWhiteSpace(arguments[0]);
  246. int calledMethodIndex = findMethod(methodName);
  247. if (calledMethodIndex == -1) {
  248. throwError(U"Tried to make an internal call to the method \"", methodName, U"\", which was not previously defined in the virtual machine! Make sure that the name is spelled correctly and the method is defined above the caller.\n");
  249. }
  250. // Check the total number of arguments
  251. Method* calledMethod = &this->methods[calledMethodIndex];
  252. if (arguments.length() - 1 != calledMethod->outputCount + calledMethod->inputCount) {
  253. throwError(U"Wrong argument count to \"", calledMethod->name, U"\"! Call arguments should start with the method to call, continue with output references and end with inputs.\n");
  254. }
  255. // Split assembler arguments into separate input and output arguments for machine instructions
  256. List<VMA> inputArguments;
  257. List<VMA> outputArguments;
  258. inputArguments.push(VMA(FixedPoint::fromMantissa(calledMethodIndex)));
  259. outputArguments.push(VMA(FixedPoint::fromMantissa(calledMethodIndex)));
  260. int outputCount = 0;
  261. for (int a = 1; a < arguments.length(); a++) {
  262. ReadableString content = string_removeOuterWhiteSpace(arguments[a]);
  263. if (string_length(content) > 0) {
  264. if (outputCount < calledMethod->outputCount) {
  265. outputArguments.push(this->VMAfromText(currentMethodIndex, getArg(arguments, a)));
  266. outputCount++;
  267. } else {
  268. inputArguments.push(this->VMAfromText(currentMethodIndex, getArg(arguments, a)));
  269. }
  270. }
  271. }
  272. // Check types
  273. for (int a = 1; a < outputArguments.length(); a++) {
  274. // Output
  275. Variable* variable = &calledMethod->locals[a - 1 + calledMethod->inputCount];
  276. if (outputArguments[a].argType != ArgumentType::Reference) {
  277. throwError(U"Output argument for \"", variable->name, U"\" in \"", calledMethod->name, U"\" must be a reference to allow writing its result!\n");
  278. } else if (outputArguments[a].dataType != variable->typeDescription->dataType) {
  279. throwError(U"Output argument for \"", variable->name, U"\" in \"", calledMethod->name, U"\" must have the type \"", variable->typeDescription->name, U"\"!\n");
  280. }
  281. }
  282. for (int a = 1; a < inputArguments.length(); a++) {
  283. // Input
  284. Variable* variable = &calledMethod->locals[a - 1];
  285. if (inputArguments[a].dataType != variable->typeDescription->dataType) {
  286. throwError(U"Input argument for \"", variable->name, U"\" in \"", calledMethod->name, U"\" must have the type \"", variable->typeDescription->name, U"\"!\n");
  287. }
  288. }
  289. addMachineWord([](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
  290. // Get the method to call
  291. int calledMethodIndex = args[0].value.getMantissa();
  292. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  293. int oldMethodIndex = memory.current.methodIndex;
  294. #endif
  295. Method* calledMethod = &machine.methods[calledMethodIndex];
  296. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  297. printText(U"Calling \"", calledMethod->name, U"\".\n");
  298. #endif
  299. // Calculate new frame pointers
  300. int32_t newFramePointer[MAX_TYPE_COUNT] = {};
  301. int32_t newStackPointer[MAX_TYPE_COUNT] = {};
  302. for (int t = 0; t < MAX_TYPE_COUNT; t++) {
  303. newFramePointer[t] = memory.current.stackPointer[t];
  304. newStackPointer[t] = memory.current.stackPointer[t] + machine.methods[calledMethodIndex].count[t];
  305. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  306. printText(U"Allocating stack memory for type ", t, U".\n");
  307. printText(U" old frame pointer = ", memory.current.framePointer[t], U"\n");
  308. printText(U" old stack pointer = ", memory.current.stackPointer[t], U"\n");
  309. printText(U" needed elements = ", machine.methods[oldMethodIndex].count[t], U"\n");
  310. printText(U" new frame pointer = ", newFramePointer[t], U"\n");
  311. printText(U" new stack pointer = ", newStackPointer[t], U"\n");
  312. #endif
  313. }
  314. // Assign inputs
  315. for (int a = 1; a < args.length(); a++) {
  316. Variable* target = &calledMethod->locals[a - 1];
  317. DataType typeIndex = target->typeDescription->dataType;
  318. int targetStackIndex = target->getStackIndex(newFramePointer[typeIndex]);
  319. memory.store(targetStackIndex, args[a], memory.current.framePointer[typeIndex], typeIndex);
  320. }
  321. // Jump into the method
  322. memory.callStack.push(memory.current);
  323. memory.current.methodIndex = calledMethodIndex;
  324. memory.current.programCounter = machine.methods[calledMethodIndex].startAddress;
  325. for (int t = 0; t < MAX_TYPE_COUNT; t++) {
  326. memory.current.framePointer[t] = newFramePointer[t];
  327. memory.current.stackPointer[t] = newStackPointer[t];
  328. }
  329. }, inputArguments);
  330. // Get results from the method
  331. addMachineWord([](VirtualMachine& machine, PlanarMemory& memory, const List<VMA>& args) {
  332. int calledMethodIndex = args[0].value.getMantissa();
  333. Method* calledMethod = &machine.methods[calledMethodIndex];
  334. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  335. printText(U"Writing results after call to \"", calledMethod->name, U"\":\n");
  336. #endif
  337. // Assign outputs
  338. for (int a = 1; a < args.length(); a++) {
  339. Variable* source = &calledMethod->locals[a - 1 + calledMethod->inputCount];
  340. DataType typeIndex = source->typeDescription->dataType;
  341. int sourceStackIndex = source->getStackIndex(memory.current.stackPointer[typeIndex]);
  342. memory.load(sourceStackIndex, args[a], memory.current.framePointer[typeIndex], typeIndex);
  343. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  344. printText(U" ");
  345. machine.debugArgument(VMA(typeIndex, source->getGlobalIndex()), calledMethodIndex, memory.current.stackPointer, false);
  346. printText(U" -> ");
  347. machine.debugArgument(args[a], memory.current.methodIndex, memory.current.framePointer, false);
  348. printText(U"\n");
  349. #endif
  350. }
  351. // TODO: Decrease reference counts for images by zeroing memory above the new stack-pointer
  352. // Avoiding temporary memory leaks and making sure that no cloning is needed for operations that clone if needed
  353. // Planar memory will receive a new memset operation for a range of stack indices for a given type
  354. memory.current.programCounter++;
  355. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  356. machine.debugPrintMemory();
  357. #endif
  358. }, outputArguments);
  359. }
  360. void VirtualMachine::interpretMachineWord(const ReadableString& command, const List<String>& arguments) {
  361. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  362. printText(U"interpretMachineWord @", this->machineWords.length(), U" ", command, U"(");
  363. for (int a = 0; a < arguments.length(); a++) {
  364. if (a > 0) { printText(U", "); }
  365. printText(getArg(arguments, a));
  366. }
  367. printText(U")\n");
  368. #endif
  369. if (string_caseInsensitiveMatch(command, U"Begin")) {
  370. if (this->methods.length() == 1) {
  371. // When more than one function exists, the init method must end with a return instruction
  372. // Otherwise it would start executing instructions in another method and crash
  373. this->addReturnInstruction();
  374. }
  375. this->methods.pushConstruct(getArg(arguments, 0), this->machineWords.length(), this->machineTypeCount);
  376. } else if (string_caseInsensitiveMatch(command, U"Temp")) {
  377. for (int a = 1; a < arguments.length(); a++) {
  378. this->declareVariable(methods.length() - 1, AccessType::Hidden, getArg(arguments, 0), getArg(arguments, a), false, U"");
  379. }
  380. } else if (string_caseInsensitiveMatch(command, U"Hidden")) {
  381. this->declareVariable(methods.length() - 1, AccessType::Hidden, getArg(arguments, 0), getArg(arguments, 1), true, getArg(arguments, 2));
  382. } else if (string_caseInsensitiveMatch(command, U"Input")) {
  383. this->declareVariable(methods.length() - 1, AccessType::Input, getArg(arguments, 0), getArg(arguments, 1), true, getArg(arguments, 2));
  384. } else if (string_caseInsensitiveMatch(command, U"Output")) {
  385. this->declareVariable(methods.length() - 1, AccessType::Output, getArg(arguments, 0), getArg(arguments, 1), true, getArg(arguments, 2));
  386. } else if (string_caseInsensitiveMatch(command, U"End")) {
  387. this->addReturnInstruction();
  388. } else if (string_caseInsensitiveMatch(command, U"Call")) {
  389. this->addCallInstructions(arguments);
  390. } else {
  391. int methodIndex = this->methods.length() - 1;
  392. List<VMA> resolvedArguments;
  393. for (int a = 0; a < arguments.length(); a++) {
  394. ReadableString content = string_removeOuterWhiteSpace(arguments[a]);
  395. if (string_length(content) > 0) {
  396. resolvedArguments.push(this->VMAfromText(methodIndex, getArg(arguments, a)));
  397. }
  398. }
  399. this->interpretCommand(command, resolvedArguments);
  400. }
  401. }
  402. void VirtualMachine::executeMethod(int methodIndex) {
  403. Method* rootMethod = &this->methods[methodIndex];
  404. #ifdef VIRTUAL_MACHINE_PROFILE
  405. if (rootMethod->instructionCount < 1) {
  406. // TODO: Assert that each method ends with a return or jump instruction after compiling
  407. printText(U"Cannot call \"", rootMethod->name, U"\", because it doesn't have any instructions.\n");
  408. return;
  409. }
  410. #endif
  411. // Create a new current state
  412. this->memory->current.methodIndex = methodIndex;
  413. this->memory->current.programCounter = rootMethod->startAddress;
  414. for (int t = 0; t < this->machineTypeCount; t++) {
  415. int framePointer = this->methods[0].count[t];
  416. this->memory->current.framePointer[t] = framePointer;
  417. this->memory->current.stackPointer[t] = framePointer + this->methods[methodIndex].count[t];
  418. }
  419. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  420. this->debugPrintMemory();
  421. #endif
  422. #ifdef VIRTUAL_MACHINE_PROFILE
  423. printText(U"Calling \"", rootMethod->name, U"\":\n");
  424. double startTime = time_getSeconds();
  425. #endif
  426. // Execute until the program counter is out of bound (-1)
  427. while (true) {
  428. int32_t pc = this->memory->current.programCounter;
  429. if (pc < 0 || pc >= this->machineWords.length()) {
  430. // Return statements will set the program counter to -1 if there are no more callers saved in the stack
  431. if (pc != -1) {
  432. throwError(U"Unexpected program counter! @", pc, U" outside of 0..", (this->machineWords.length() - 1), U"\n");
  433. }
  434. break;
  435. }
  436. MachineWord* word = &this->machineWords[pc];
  437. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  438. const InsSig* signature = getMachineInstructionFromFunction(word->operation);
  439. if (signature) {
  440. printText(U"Executing @", pc, U" ", signature->name, U"(");
  441. for (int a = signature->targetCount; a < word->args.length(); a++) {
  442. if (a > signature->targetCount) {
  443. printText(U", ");
  444. }
  445. debugArgument(word->args[a], this->memory->current.methodIndex, this->memory->current.framePointer, false);
  446. }
  447. printText(U")");
  448. }
  449. word->operation(*this, this->memory.getReference(), word->args);
  450. if (signature) {
  451. if (signature->targetCount > 0) {
  452. printText(U" -> ");
  453. for (int a = 0; a < signature->targetCount; a++) {
  454. if (a > 0) {
  455. printText(U", ");
  456. }
  457. debugArgument(word->args[a], this->memory->current.methodIndex, this->memory->current.framePointer, true);
  458. }
  459. }
  460. }
  461. printText(U"\n");
  462. #else
  463. word->operation(*this, this->memory.getReference(), word->args);
  464. #endif
  465. }
  466. #ifdef VIRTUAL_MACHINE_PROFILE
  467. double endTime = time_getSeconds();
  468. printText(U"Done calling \"", rootMethod->name, U"\" after ", (endTime - startTime) * 1000000.0, U" microseconds.\n");
  469. #ifdef VIRTUAL_MACHINE_DEBUG_PRINT
  470. printText(U" (debug prints are active)\n");
  471. #endif
  472. #endif
  473. }