JSCore.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  2. // Please see LICENSE.md in repository root for license information
  3. // https://github.com/AtomicGameEngine/AtomicGameEngine
  4. #include <Atomic/Core/ProcessUtils.h>
  5. #include "JSCore.h"
  6. #include "JSEventHelper.h"
  7. #include "JSVM.h"
  8. namespace Atomic
  9. {
  10. static int EventHandler_Property_Get(duk_context* ctx)
  11. {
  12. // targ, key, recv
  13. if (duk_is_string(ctx, 1))
  14. {
  15. const char* cstr = duk_to_string(ctx, 1);
  16. StringHash key = cstr;
  17. JSEventHelper* helper = js_to_class_instance<JSEventHelper>(ctx, 0, 0);
  18. VariantMap& data = helper->GetCurrentData();
  19. if (!data.Contains(key))
  20. {
  21. duk_push_undefined(ctx);
  22. return 1;
  23. }
  24. js_push_variant(ctx, data[key]);
  25. return 1;
  26. }
  27. duk_push_undefined(ctx);
  28. return 1;
  29. }
  30. static int Object_SubscribeToEvent(duk_context* ctx)
  31. {
  32. int top = duk_get_top(ctx);
  33. Object* sender = NULL;
  34. StringHash eventType = StringHash::ZERO;
  35. if ( top == 2 ) // General notification: subscribeToEvent("ScreenMode", function() {});
  36. {
  37. if (duk_is_string(ctx, 0) && duk_is_function(ctx, 1))
  38. {
  39. eventType = duk_to_string(ctx, 0);
  40. }
  41. }
  42. else if (top == 3) // Listen to specific object sender subscribeToEvent(graphics, "ScreenMode", function() {});
  43. {
  44. if (duk_is_object(ctx, 0) && duk_is_string(ctx, 1) && duk_is_function(ctx, 2))
  45. {
  46. sender = js_to_class_instance<Object>(ctx, 0, 0);
  47. eventType = duk_to_string(ctx, 1);
  48. }
  49. }
  50. if ( eventType == StringHash::ZERO)
  51. {
  52. duk_push_string(ctx, "Object.subscribeToEvent() - Bad Arguments");
  53. duk_throw(ctx);
  54. }
  55. duk_push_this(ctx);
  56. // event receiver
  57. Object* object = js_to_class_instance<Object>(ctx, -1, 0);
  58. duk_get_prop_string(ctx, -1, "__eventHelper");
  59. // need to setup the handler
  60. if (duk_is_null_or_undefined(ctx, -1))
  61. {
  62. duk_pop(ctx); // pop null or undefined
  63. // construct a new event helper
  64. js_push_class_object_instance(ctx, new JSEventHelper(object->GetContext()));
  65. // proxy support
  66. duk_get_global_string(ctx, "Proxy");
  67. duk_dup(ctx, -2);
  68. // setup property handler
  69. duk_push_object(ctx);
  70. duk_push_c_function(ctx, EventHandler_Property_Get, 3);
  71. duk_put_prop_string(ctx, -2, "get");
  72. duk_new(ctx, 2);
  73. duk_put_prop_string(ctx, -2, "__eventHelperProxy");
  74. duk_push_object(ctx);
  75. duk_put_prop_string(ctx, -2, "__eventHelperFunctions");
  76. // dup so when we set the helper is left on stack
  77. duk_dup_top(ctx);
  78. duk_put_prop_string(ctx, -3, "__eventHelper");
  79. }
  80. JSEventHelper* helper = js_to_class_instance<JSEventHelper>(ctx, -1, 0);
  81. duk_get_prop_string(ctx, -1, "__eventHelperFunctions");
  82. assert(duk_is_object(ctx, -1));
  83. assert(duk_is_function(ctx, sender ? 2 : 1));
  84. duk_dup(ctx, sender ? 2 : 1);
  85. duk_put_prop_string(ctx, -2, eventType.ToString().CString());
  86. duk_pop(ctx);
  87. if (sender)
  88. helper->AddEventHandler(sender, eventType);
  89. else
  90. helper->AddEventHandler(eventType);
  91. return 0;
  92. }
  93. // so we don't keep allocating these
  94. static VariantMap sendEventVMap;
  95. static int Object_SendEvent(duk_context* ctx)
  96. {
  97. int top = duk_get_top(ctx);
  98. duk_push_this(ctx);
  99. // event sender
  100. Object* sender = js_to_class_instance<Object>(ctx, -1, 0);
  101. if (top == 1)
  102. {
  103. sender->SendEvent(duk_to_string(ctx, 0));
  104. }
  105. else if (top == 2)
  106. {
  107. if (duk_is_object(ctx, 1)) {
  108. js_object_to_variantmap(ctx, 1, sendEventVMap);
  109. sender->SendEvent(duk_to_string(ctx, 0), sendEventVMap);
  110. }
  111. }
  112. return 0;
  113. }
  114. static int Atomic_GetArguments(duk_context* ctx)
  115. {
  116. duk_push_array(ctx);
  117. for (unsigned i = 0; i < GetArguments().Size(); i++)
  118. {
  119. duk_push_string(ctx, GetArguments()[i].CString());
  120. duk_put_prop_index(ctx, -2, i);
  121. }
  122. return 1;
  123. }
  124. void jsapi_init_core(JSVM* vm)
  125. {
  126. duk_context* ctx = vm->GetJSContext();
  127. duk_get_global_string(ctx, "Atomic");
  128. duk_push_c_function(ctx, Atomic_GetArguments, 0);
  129. duk_put_prop_string(ctx, -2, "getArguments");
  130. duk_pop(ctx); // pop Atomic object
  131. js_class_get_prototype(ctx, "Atomic", "AObject");
  132. duk_push_c_function(ctx, Object_SubscribeToEvent, DUK_VARARGS);
  133. duk_put_prop_string(ctx, -2, "subscribeToEvent");
  134. duk_push_c_function(ctx, Object_SendEvent, DUK_VARARGS);
  135. duk_put_prop_string(ctx, -2, "sendEvent");
  136. duk_pop(ctx); // pop AObject prototype
  137. }
  138. }