JSFunctionWriter.cpp 18 KB

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