JSBFunction.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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/Atomic.h>
  5. #include "JSBFunction.h"
  6. void JSBFunction::WriteParameterMarshal(String& source)
  7. {
  8. // generate args
  9. int cparam = 0;
  10. if (parameters_.Size())
  11. {
  12. for (unsigned int i = 0; i < parameters_.Size(); i++, cparam++)
  13. {
  14. JSBFunctionType * ptype = parameters_.At(i);
  15. // ignore "Context" parameters
  16. if (ptype->type_->asClassType())
  17. {
  18. JSBClassType* classType = ptype->type_->asClassType();
  19. JSBClass* klass = classType->class_;
  20. if (klass->GetClassName() == "Context")
  21. {
  22. cparam--;
  23. continue;
  24. }
  25. }
  26. String pstring = ptype->ToArgString(cparam);
  27. const String& init = ptype->initializer_;
  28. if (ptype->type_->asClassType())
  29. {
  30. JSBClassType* classType = ptype->type_->asClassType();
  31. JSBClass* klass = classType->class_;
  32. if (!klass->isNumberArray())
  33. {
  34. if (init.Length())
  35. {
  36. source.AppendWithFormat("%s = duk_get_top(ctx) >= %i ? js_to_class_instance<%s>(ctx, %i, 0) : %s;\n",
  37. pstring.CString(), cparam + 1, klass->GetClassName().CString(), cparam, init.CString());
  38. }
  39. else
  40. {
  41. source.AppendWithFormat("%s = js_to_class_instance<%s>(ctx, %i, 0);\n",
  42. pstring.CString(), klass->GetClassName().CString(), cparam);
  43. }
  44. }
  45. else
  46. {
  47. int elements = klass->GetNumberArrayElements();
  48. String elementType = klass->GetArrayElementType();
  49. source.AppendWithFormat("%s arrayData%i[%i];\n", elementType.CString(), cparam, elements);
  50. if (init.Length())
  51. {
  52. source.AppendWithFormat("const %s& defaultArg%i = %s;\n", klass->GetClassName().CString(), cparam, init.CString());
  53. source.AppendWithFormat("if (duk_get_top(ctx) >= %i) {\n", cparam + 1);
  54. }
  55. for (int j = 0; j < elements; j++)
  56. {
  57. source.AppendWithFormat("duk_get_prop_index(ctx, %i, %i);\n", cparam, j);
  58. source.AppendWithFormat("arrayData%i[%i] = (%s) duk_to_number(ctx, -1);\n", cparam, j, elementType.CString());
  59. }
  60. source.AppendWithFormat("duk_pop_n(ctx, %i);\n", elements);
  61. if (init.Length())
  62. {
  63. source.Append("}\n");
  64. source.AppendWithFormat("%s __arg%i(duk_get_top(ctx) >= %i ? arrayData%i : defaultArg%i);\n",
  65. klass->GetClassName().CString(), cparam, cparam + 1, cparam, cparam);
  66. }
  67. else
  68. {
  69. source.AppendWithFormat("%s __arg%i(arrayData%i);\n", klass->GetClassName().CString(), cparam, cparam);
  70. }
  71. }
  72. }
  73. else if (ptype->type_->asStringType() || ptype->type_->asStringHashType())
  74. {
  75. if (init.Length())
  76. {
  77. source.AppendWithFormat("%s = duk_get_top(ctx) >= %i ? duk_to_string(ctx, %i) : %s;\n", pstring.CString(), cparam + 1, cparam, init.CString());
  78. }
  79. else
  80. {
  81. source.AppendWithFormat("%s = duk_to_string(ctx, %i);\n", pstring.CString(), cparam);
  82. }
  83. }
  84. else if (ptype->type_->asHeapPtrType())
  85. {
  86. if (init.Length())
  87. {
  88. source.AppendWithFormat("%s = duk_get_top(ctx) >= %i ? duk_get_heapptr(ctx, %i) : %s;\n", pstring.CString(), cparam + 1, cparam, init.CString());
  89. }
  90. else
  91. {
  92. source.AppendWithFormat("%s = duk_get_heapptr(ctx, %i);\n", pstring.CString(), cparam);
  93. }
  94. }
  95. else if (ptype->type_->asPrimitiveType())
  96. {
  97. JSBPrimitiveType* prtype = ptype->type_->asPrimitiveType();
  98. if (prtype->kind_ == JSBPrimitiveType::Bool)
  99. {
  100. if (init.Length())
  101. {
  102. source.AppendWithFormat("bool __arg%i = duk_get_top(ctx) >= %i ? (duk_to_boolean(ctx, %i) ? true : false) : %s;\n",
  103. cparam, cparam + 1, cparam, init.CString());
  104. }
  105. else
  106. {
  107. source.AppendWithFormat("bool __arg%i = duk_to_boolean(ctx, %i) ? true : false;\n", cparam, cparam);
  108. }
  109. }
  110. else
  111. {
  112. if (init.Length())
  113. {
  114. source.AppendWithFormat("double __arg%i = duk_get_top(ctx) >= %i ? (duk_to_number(ctx, %i)) : %s;\n",
  115. cparam, cparam + 1, cparam, init.CString());
  116. }
  117. else
  118. {
  119. source.AppendWithFormat("double __arg%i = duk_to_number(ctx, %i);\n", cparam, cparam);
  120. }
  121. }
  122. }
  123. else if (ptype->type_->asEnumType())
  124. {
  125. JSBEnumType* etype = ptype->type_->asEnumType();
  126. if (init.Length())
  127. {
  128. source.AppendWithFormat("%s __arg%i = duk_get_top(ctx) >= %i ? ((%s) ((int) duk_to_number(ctx, %i))) : %s;\n", etype->enum_->name_.CString(),
  129. cparam, cparam + 1, etype->enum_->name_.CString(), cparam, init.CString());
  130. }
  131. else
  132. {
  133. source.AppendWithFormat("%s __arg%i = (%s) ((int)duk_to_number(ctx, %i));\n", etype->enum_->name_.CString(),
  134. cparam, etype->enum_->name_.CString(), cparam);
  135. }
  136. }
  137. }
  138. }
  139. }
  140. void JSBFunction::Process()
  141. {
  142. if (skip_)
  143. {
  144. return;
  145. }
  146. // if not already marked as a getter
  147. if (!isGetter_)
  148. {
  149. if (!parameters_.Size() && returnType_)
  150. {
  151. if (name_.Length() > 3 && name_.StartsWith("Get") && isupper(name_[3]))
  152. {
  153. String pname = name_.Substring(3);
  154. class_->SetSkipFunction(pname);
  155. isGetter_ = true;
  156. propertyName_ = pname;
  157. }
  158. }
  159. }
  160. if (!isSetter_)
  161. {
  162. if (parameters_.Size() == 1 && !returnType_)
  163. {
  164. if (name_.Length() > 3 && name_.StartsWith("Set") && isupper(name_[3]))
  165. {
  166. String pname = name_.Substring(3);
  167. class_->SetSkipFunction(pname);
  168. isSetter_ = true;
  169. propertyName_ = pname;
  170. }
  171. }
  172. }
  173. if (isGetter_)
  174. class_->AddPropertyFunction(this);
  175. if (isSetter_)
  176. class_->AddPropertyFunction(this);
  177. }
  178. void JSBFunction::WriteConstructor(String& source)
  179. {
  180. // TODO: refactor this
  181. if (name_ == "RefCounted")
  182. {
  183. source.Append("// finalizer may be called more than once\n" \
  184. "static int jsb_finalizer_RefCounted(duk_context *ctx)\n" \
  185. "{\n" \
  186. "JSVM* vm = JSVM::GetJSVM(ctx);\n" \
  187. \
  188. "duk_get_prop_index(ctx, 0, JS_INSTANCE_INDEX_FINALIZED);\n" \
  189. \
  190. "if (!duk_is_boolean(ctx, -1))\n" \
  191. "{\n" \
  192. "RefCounted* ref = vm->GetObjectPtr(duk_get_heapptr(ctx, 0));\n" \
  193. "vm->RemoveObject(ref);\n" \
  194. "ref->ReleaseRef();\n" \
  195. "duk_push_boolean(ctx, 1);\n" \
  196. "duk_put_prop_index(ctx, 0, JS_INSTANCE_INDEX_FINALIZED);\n" \
  197. "}\n" \
  198. \
  199. "return 0;\n" \
  200. "}\n");
  201. }
  202. JSBClass* base = class_->GetBaseClass();
  203. // Constructor
  204. source.AppendWithFormat("duk_ret_t jsb_constructor_%s(duk_context* ctx)\n{\n", class_->GetName().CString());
  205. source.Append(" if (duk_is_constructor_call(ctx))\n {\n");
  206. if (!class_->isAbstract() && !class_->isNumberArray())
  207. {
  208. String marshal;
  209. WriteParameterMarshal(marshal);
  210. String sparams;
  211. int cparam = 0;
  212. for (unsigned i = 0; i < parameters_.Size(); i++, cparam++)
  213. {
  214. JSBFunctionType * ptype = parameters_.At(i);
  215. String sarg;
  216. if (ptype->type_->asClassType())
  217. {
  218. JSBClassType* classType = ptype->type_->asClassType();
  219. JSBClass* klass = classType->class_;
  220. if (klass->GetClassName() == "Context")
  221. {
  222. sarg = "JSVM::GetJSVM(ctx)->GetContext()";
  223. cparam--;
  224. }
  225. }
  226. if (!sarg.Length())
  227. {
  228. sarg.AppendWithFormat("__arg%i", cparam);
  229. }
  230. sparams += sarg;
  231. if (i + 1 < parameters_.Size())
  232. sparams += ", ";
  233. }
  234. source.AppendWithFormat("if (!duk_get_top(ctx) || !duk_is_pointer(ctx, 0))\n"\
  235. "{\n"\
  236. "%s\n"\
  237. "%s* native = new %s(%s);\n" \
  238. "duk_push_this(ctx);\n" \
  239. "JSVM::GetJSVM(ctx)->AddObject(duk_get_heapptr(ctx, -1), native);\n"\
  240. "duk_pop(ctx);\n"\
  241. "}\n" \
  242. "else if (duk_is_pointer(ctx, 0))\n" \
  243. "{\n" \
  244. "duk_push_this(ctx);\n" \
  245. "JSVM::GetJSVM(ctx)->AddObject(duk_get_heapptr(ctx, -1), (RefCounted*) duk_get_pointer(ctx, 0));\n" \
  246. "duk_pop(ctx);\n" \
  247. "}\n", marshal.CString(), class_->GetClassName().CString(), class_->GetClassName().CString(), sparams.CString());
  248. }
  249. else
  250. {
  251. if (class_->isAbstract())
  252. source.Append("assert(0); // abstract class new'd\n");
  253. if (class_->isNumberArray())
  254. source.Append("assert(0); // number array class new'd\n");
  255. }
  256. source.Append(" }\n");
  257. if (base)
  258. {
  259. source.AppendWithFormat(" js_constructor_basecall(ctx, \"%s\");\n", base->GetName().CString());
  260. }
  261. if (name_ == "RefCounted")
  262. {
  263. source.Append("duk_push_this(ctx);\n "\
  264. "duk_push_c_function(ctx, jsb_finalizer_RefCounted, 1);\n "\
  265. "duk_set_finalizer(ctx, -2);\n "\
  266. \
  267. "RefCounted* ref = JSVM::GetJSVM(ctx)->GetObjectPtr(duk_get_heapptr(ctx, -1));\n "\
  268. "ref->AddRef();\n "\
  269. \
  270. "duk_pop(ctx);\n");
  271. }
  272. source += " return 0;";
  273. source += "\n}\n";
  274. }
  275. void JSBFunction::WriteFunction(String& source)
  276. {
  277. source.AppendWithFormat("static int jsb_class_%s_%s(duk_context* ctx)\n{\n", class_->GetName().CString(), name_.CString());
  278. WriteParameterMarshal(source);
  279. source.Append("duk_push_this(ctx);\n");
  280. source.AppendWithFormat("%s* native = js_to_class_instance<%s>(ctx, -1, 0);\n", class_->GetClassName().CString(), class_->GetClassName().CString());
  281. // declare return value;
  282. bool returnDeclared = false;
  283. if (returnType_)
  284. {
  285. if (returnType_->type_->asStringType())
  286. {
  287. returnDeclared = true;
  288. source.Append("const String& retValue = ");
  289. }
  290. else if (returnType_->type_->asPrimitiveType())
  291. {
  292. returnDeclared = true;
  293. JSBPrimitiveType* prtype = returnType_->type_->asPrimitiveType();
  294. if (prtype->kind_ == JSBPrimitiveType::Bool)
  295. {
  296. source.Append("bool retValue = ");
  297. }
  298. else
  299. {
  300. source.Append("double retValue = ");
  301. }
  302. }
  303. else if (returnType_->type_->asClassType())
  304. {
  305. JSBClassType* klassType = returnType_->type_->asClassType();
  306. if (returnType_->isTemplate_)
  307. {
  308. returnDeclared = true;
  309. source.AppendWithFormat("SharedPtr<%s> object = ", klassType->class_->GetClassName().CString());
  310. }
  311. else if (klassType->class_->isObject())
  312. {
  313. returnDeclared = true;
  314. source.Append("const Object* object = ");
  315. }
  316. else if (klassType->class_->isNumberArray())
  317. {
  318. returnDeclared = true;
  319. source.AppendWithFormat("const %s& retValue = ", klassType->class_->GetName().CString());
  320. }
  321. else
  322. {
  323. returnDeclared = true;
  324. source.Append("const RefCounted* object = ");
  325. }
  326. }
  327. else if (returnType_->type_->asEnumType())
  328. {
  329. JSBEnumType* enumType = returnType_->type_->asEnumType();
  330. returnDeclared = true;
  331. source.AppendWithFormat("%s retValue = ", enumType->enum_->name_.CString());
  332. }
  333. }
  334. source.AppendWithFormat("native->%s(", name_.CString());
  335. for (unsigned int i = 0; i < parameters_.Size(); i++)
  336. {
  337. source.AppendWithFormat("__arg%i", i);
  338. if (i != parameters_.Size() - 1)
  339. {
  340. source += ", ";
  341. }
  342. }
  343. source += ");\n";
  344. if (returnDeclared)
  345. {
  346. if (returnType_->type_->asStringType())
  347. {
  348. source.Append("duk_push_string(ctx, retValue.CString());\n");
  349. }
  350. else if (returnType_->type_->asPrimitiveType())
  351. {
  352. JSBPrimitiveType* prtype = returnType_->type_->asPrimitiveType();
  353. if (prtype->kind_ == JSBPrimitiveType::Bool)
  354. {
  355. source.Append("duk_push_boolean(ctx, retValue ? 1 : 0);\n");
  356. }
  357. else
  358. {
  359. source.Append("duk_push_number(ctx, retValue);\n");
  360. }
  361. }
  362. else if (returnType_->type_->asClassType())
  363. {
  364. JSBClassType* klassType = returnType_->type_->asClassType();
  365. if (klassType->class_->isObject())
  366. {
  367. returnDeclared = true;
  368. source.Append("js_push_class_object_instance(ctx, object);\n");
  369. }
  370. else if (klassType->class_->isNumberArray())
  371. {
  372. returnDeclared = true;
  373. String elementType = klassType->class_->GetArrayElementType();
  374. source.AppendWithFormat("const %s* arrayData = retValue.Data();\n", elementType.CString());
  375. source.Append("duk_push_array(ctx);\n");
  376. for (int i = 0; i < klassType->class_->GetNumberArrayElements(); i++)
  377. {
  378. source.AppendWithFormat("duk_push_number(ctx, arrayData[%i]);\n", i);
  379. source.AppendWithFormat("duk_put_prop_index(ctx, -2, %i);\n", i);
  380. }
  381. }
  382. else
  383. {
  384. returnDeclared = true;
  385. source.AppendWithFormat("js_push_class_object_instance(ctx, object, \"%s\");\n", klassType->class_->GetName().CString());
  386. }
  387. }
  388. else if (returnType_->type_->asEnumType())
  389. {
  390. returnDeclared = true;
  391. source.Append("duk_push_number(ctx, (double) retValue);\n");
  392. }
  393. source += "return 1;\n";
  394. }
  395. else
  396. {
  397. source += "return 0;\n";
  398. }
  399. source.Append("}\n");
  400. }
  401. void JSBFunction::Write(String& source)
  402. {
  403. if (isConstructor_)
  404. {
  405. WriteConstructor(source);
  406. }
  407. else
  408. {
  409. WriteFunction(source);
  410. }
  411. }