ch.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "stdafx.h"
  6. #include "core/AtomLockGuids.h"
  7. unsigned int MessageBase::s_messageCount = 0;
  8. LPCWSTR hostName = L"ch.exe";
  9. extern "C"
  10. HRESULT __stdcall OnChakraCoreLoadedEntry(TestHooks& testHooks)
  11. {
  12. return ChakraRTInterface::OnChakraCoreLoaded(testHooks);
  13. }
  14. JsRuntimeAttributes jsrtAttributes = JsRuntimeAttributeAllowScriptInterrupt;
  15. LPCWSTR JsErrorCodeToString(JsErrorCode jsErrorCode)
  16. {
  17. switch (jsErrorCode)
  18. {
  19. case JsNoError:
  20. return L"JsNoError";
  21. break;
  22. case JsErrorInvalidArgument:
  23. return L"JsErrorInvalidArgument";
  24. break;
  25. case JsErrorNullArgument:
  26. return L"JsErrorNullArgument";
  27. break;
  28. case JsErrorNoCurrentContext:
  29. return L"JsErrorNoCurrentContext";
  30. break;
  31. case JsErrorInExceptionState:
  32. return L"JsErrorInExceptionState";
  33. break;
  34. case JsErrorNotImplemented:
  35. return L"JsErrorNotImplemented";
  36. break;
  37. case JsErrorWrongThread:
  38. return L"JsErrorWrongThread";
  39. break;
  40. case JsErrorRuntimeInUse:
  41. return L"JsErrorRuntimeInUse";
  42. break;
  43. case JsErrorBadSerializedScript:
  44. return L"JsErrorBadSerializedScript";
  45. break;
  46. case JsErrorInDisabledState:
  47. return L"JsErrorInDisabledState";
  48. break;
  49. case JsErrorCannotDisableExecution:
  50. return L"JsErrorCannotDisableExecution";
  51. break;
  52. case JsErrorHeapEnumInProgress:
  53. return L"JsErrorHeapEnumInProgress";
  54. break;
  55. case JsErrorOutOfMemory:
  56. return L"JsErrorOutOfMemory";
  57. break;
  58. case JsErrorScriptException:
  59. return L"JsErrorScriptException";
  60. break;
  61. case JsErrorScriptCompile:
  62. return L"JsErrorScriptCompile";
  63. break;
  64. case JsErrorScriptTerminated:
  65. return L"JsErrorScriptTerminated";
  66. break;
  67. case JsErrorFatal:
  68. return L"JsErrorFatal";
  69. break;
  70. default:
  71. return L"<unknown>";
  72. break;
  73. }
  74. }
  75. #define IfJsErrorFailLog(expr) do { JsErrorCode jsErrorCode = expr; if ((jsErrorCode) != JsNoError) { fwprintf(stderr, L"ERROR: " TEXT(#expr) L" failed. JsErrorCode=0x%x (%s)\n", jsErrorCode, JsErrorCodeToString(jsErrorCode)); fflush(stderr); goto Error; } } while (0)
  76. int HostExceptionFilter(int exceptionCode, _EXCEPTION_POINTERS *ep)
  77. {
  78. ChakraRTInterface::NotifyUnhandledException(ep);
  79. bool crashOnException = false;
  80. ChakraRTInterface::GetCrashOnExceptionFlag(&crashOnException);
  81. if (exceptionCode == EXCEPTION_BREAKPOINT || (crashOnException && exceptionCode != 0xE06D7363))
  82. {
  83. return EXCEPTION_CONTINUE_SEARCH;
  84. }
  85. fwprintf(stderr, L"FATAL ERROR: %ls failed due to exception code %x\n", hostName, exceptionCode);
  86. fflush(stderr);
  87. return EXCEPTION_EXECUTE_HANDLER;
  88. }
  89. void __stdcall PrintUsageFormat()
  90. {
  91. wprintf(L"\nUsage: ch.exe [flaglist] filename\n");
  92. }
  93. void __stdcall PrintUsage()
  94. {
  95. PrintUsageFormat();
  96. wprintf(L"Try 'ch.exe -?' for help\n");
  97. }
  98. // On success the param byteCodeBuffer will be allocated in the function.
  99. // The caller of this function should de-allocate the memory.
  100. HRESULT GetSerializedBuffer(LPCOLESTR fileContents, __out BYTE **byteCodeBuffer, __out DWORD *byteCodeBufferSize)
  101. {
  102. HRESULT hr = S_OK;
  103. *byteCodeBuffer = nullptr;
  104. *byteCodeBufferSize = 0;
  105. BYTE *bcBuffer = nullptr;
  106. DWORD bcBufferSize = 0;
  107. IfJsErrorFailLog(ChakraRTInterface::JsSerializeScript(fileContents, bcBuffer, &bcBufferSize));
  108. // Above call will return the size of the buffer only, once succeed we need to allocate memory of that much and call it again.
  109. if (bcBufferSize == 0)
  110. {
  111. AssertMsg(false, "bufferSize should not be zero");
  112. IfFailGo(E_FAIL);
  113. }
  114. bcBuffer = new BYTE[bcBufferSize];
  115. DWORD newBcBufferSize = bcBufferSize;
  116. IfJsErrorFailLog(ChakraRTInterface::JsSerializeScript(fileContents, bcBuffer, &newBcBufferSize));
  117. Assert(bcBufferSize == newBcBufferSize);
  118. Error:
  119. if (hr != S_OK)
  120. {
  121. // In the failure release the buffer
  122. if (bcBuffer != nullptr)
  123. {
  124. delete[] bcBuffer;
  125. }
  126. }
  127. else
  128. {
  129. *byteCodeBuffer = bcBuffer;
  130. *byteCodeBufferSize = bcBufferSize;
  131. }
  132. return hr;
  133. }
  134. HRESULT CreateLibraryByteCodeHeader(LPCOLESTR fileContents, BYTE * contentsRaw, DWORD lengthBytes, LPCWSTR bcFullPath, LPCWSTR libraryNameWide)
  135. {
  136. HRESULT hr = S_OK;
  137. HANDLE bcFileHandle = nullptr;
  138. BYTE *bcBuffer = nullptr;
  139. DWORD bcBufferSize = 0;
  140. IfFailGo(GetSerializedBuffer(fileContents, &bcBuffer, &bcBufferSize));
  141. bcFileHandle = CreateFile(bcFullPath, GENERIC_WRITE, FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
  142. if (bcFileHandle == INVALID_HANDLE_VALUE)
  143. {
  144. IfFailGo(E_FAIL);
  145. }
  146. DWORD written;
  147. // For validating the header file against the library file
  148. auto outputStr =
  149. "//-------------------------------------------------------------------------------------------------------\r\n"
  150. "// Copyright (C) Microsoft. All rights reserved.\r\n"
  151. "// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.\r\n"
  152. "//-------------------------------------------------------------------------------------------------------\r\n"
  153. "#if 0\r\n";
  154. IfFalseGo(WriteFile(bcFileHandle, outputStr, (DWORD)strlen(outputStr), &written, nullptr));
  155. IfFalseGo(WriteFile(bcFileHandle, contentsRaw, lengthBytes, &written, nullptr));
  156. if (lengthBytes < 2 || contentsRaw[lengthBytes - 2] != '\r' || contentsRaw[lengthBytes - 1] != '\n')
  157. {
  158. outputStr = "\r\n#endif\r\n";
  159. }
  160. else
  161. {
  162. outputStr = "#endif\r\n";
  163. }
  164. IfFalseGo(WriteFile(bcFileHandle, outputStr, (DWORD)strlen(outputStr), &written, nullptr));
  165. // Write out the bytecode
  166. outputStr = "namespace Js\r\n{\r\n const char Library_Bytecode_";
  167. IfFalseGo(WriteFile(bcFileHandle, outputStr, (DWORD)strlen(outputStr), &written, nullptr));
  168. size_t convertedChars;
  169. char libraryNameNarrow[MAX_PATH + 1];
  170. IfFalseGo((wcstombs_s(&convertedChars, libraryNameNarrow, libraryNameWide, _TRUNCATE) == 0));
  171. IfFalseGo(WriteFile(bcFileHandle, libraryNameNarrow, (DWORD)strlen(libraryNameNarrow), &written, nullptr));
  172. outputStr = "[] = {\r\n/* 00000000 */";
  173. IfFalseGo(WriteFile(bcFileHandle, outputStr, (DWORD)strlen(outputStr), &written, nullptr));
  174. for (unsigned int i = 0; i < bcBufferSize; i++)
  175. {
  176. char scratch[6];
  177. auto scratchLen = sizeof(scratch);
  178. int num = _snprintf_s(scratch, scratchLen, " 0x%02X", bcBuffer[i]);
  179. Assert(num == 5);
  180. IfFalseGo(WriteFile(bcFileHandle, scratch, (DWORD)(scratchLen - 1), &written, nullptr));
  181. // Add a comma and a space if this is not the last item
  182. if (i < bcBufferSize - 1)
  183. {
  184. char commaSpace[2];
  185. _snprintf_s(commaSpace, sizeof(commaSpace), ","); // close quote, new line, offset and open quote
  186. IfFalseGo(WriteFile(bcFileHandle, commaSpace, (DWORD)strlen(commaSpace), &written, nullptr));
  187. }
  188. // Add a line break every 16 scratches, primarily so the compiler doesn't complain about the string being too long.
  189. // Also, won't add for the last scratch
  190. if (i % 16 == 15 && i < bcBufferSize - 1)
  191. {
  192. char offset[17];
  193. _snprintf_s(offset, sizeof(offset), "\r\n/* %08X */", i + 1); // close quote, new line, offset and open quote
  194. IfFalseGo(WriteFile(bcFileHandle, offset, (DWORD)strlen(offset), &written, nullptr));
  195. }
  196. }
  197. outputStr = "};\r\n\r\n";
  198. IfFalseGo(WriteFile(bcFileHandle, outputStr, (DWORD)strlen(outputStr), &written, nullptr));
  199. outputStr = "}\r\n";
  200. IfFalseGo(WriteFile(bcFileHandle, outputStr, (DWORD)strlen(outputStr), &written, nullptr));
  201. Error:
  202. if (bcFileHandle != nullptr)
  203. {
  204. CloseHandle(bcFileHandle);
  205. }
  206. if (bcBuffer != nullptr)
  207. {
  208. delete[] bcBuffer;
  209. }
  210. return hr;
  211. }
  212. static void CALLBACK PromiseContinuationCallback(JsValueRef task, void *callbackState)
  213. {
  214. Assert(task != JS_INVALID_REFERENCE);
  215. Assert(callbackState != JS_INVALID_REFERENCE);
  216. MessageQueue * messageQueue = (MessageQueue *)callbackState;
  217. WScriptJsrt::CallbackMessage *msg = new WScriptJsrt::CallbackMessage(0, task);
  218. messageQueue->Push(msg);
  219. }
  220. HRESULT RunScript(LPCWSTR fileName, LPCWSTR fileContents, BYTE *bcBuffer, wchar_t *fullPath)
  221. {
  222. HRESULT hr = S_OK;
  223. MessageQueue * messageQueue = new MessageQueue();
  224. WScriptJsrt::AddMessageQueue(messageQueue);
  225. IfJsErrorFailLog(ChakraRTInterface::JsSetPromiseContinuationCallback(PromiseContinuationCallback, (void*)messageQueue));
  226. Assert(fileContents != nullptr || bcBuffer != nullptr);
  227. JsErrorCode runScript;
  228. if (bcBuffer != nullptr)
  229. {
  230. runScript = ChakraRTInterface::JsRunSerializedScript(fileContents, bcBuffer, WScriptJsrt::GetNextSourceContext(), fullPath, nullptr /*result*/);
  231. }
  232. else
  233. {
  234. runScript = ChakraRTInterface::JsRunScript(fileContents, WScriptJsrt::GetNextSourceContext(), fullPath, nullptr /*result*/);
  235. }
  236. if (runScript != JsNoError)
  237. {
  238. WScriptJsrt::PrintException(fileName, runScript);
  239. }
  240. else
  241. {
  242. // Repeatedly flush the message queue until it's empty. It is necessary to loop on this
  243. // because setTimeout can add scripts to execute.
  244. do
  245. {
  246. IfFailGo(messageQueue->ProcessAll(fileName));
  247. } while (!messageQueue->IsEmpty());
  248. }
  249. Error:
  250. if (messageQueue != nullptr)
  251. {
  252. delete messageQueue;
  253. }
  254. return hr;
  255. }
  256. HRESULT CreateAndRunSerializedScript(LPCWSTR fileName, LPCWSTR fileContents, wchar_t *fullPath)
  257. {
  258. HRESULT hr = S_OK;
  259. JsRuntimeHandle runtime = JS_INVALID_RUNTIME_HANDLE;
  260. JsContextRef context = JS_INVALID_REFERENCE, current = JS_INVALID_REFERENCE;
  261. BYTE *bcBuffer = nullptr;
  262. DWORD bcBufferSize = 0;
  263. IfFailGo(GetSerializedBuffer(fileContents, &bcBuffer, &bcBufferSize));
  264. // Bytecode buffer is created in one runtime and will be executed on different runtime.
  265. IfJsErrorFailLog(ChakraRTInterface::JsCreateRuntime(jsrtAttributes, nullptr, &runtime));
  266. IfJsErrorFailLog(ChakraRTInterface::JsCreateContext(runtime, &context));
  267. IfJsErrorFailLog(ChakraRTInterface::JsGetCurrentContext(&current));
  268. IfJsErrorFailLog(ChakraRTInterface::JsSetCurrentContext(context));
  269. // Initialized the WScript object on the new context
  270. if (!WScriptJsrt::Initialize())
  271. {
  272. IfFailGo(E_FAIL);
  273. }
  274. IfFailGo(RunScript(fileName, fileContents, bcBuffer, fullPath));
  275. Error:
  276. if (bcBuffer != nullptr)
  277. {
  278. delete[] bcBuffer;
  279. }
  280. if (current != JS_INVALID_REFERENCE)
  281. {
  282. ChakraRTInterface::JsSetCurrentContext(current);
  283. }
  284. if (runtime != JS_INVALID_RUNTIME_HANDLE)
  285. {
  286. ChakraRTInterface::JsDisposeRuntime(runtime);
  287. }
  288. return hr;
  289. }
  290. HRESULT ExecuteTest(LPCWSTR fileName)
  291. {
  292. HRESULT hr = S_OK;
  293. LPCWSTR fileContents = nullptr;
  294. JsRuntimeHandle runtime = JS_INVALID_RUNTIME_HANDLE;
  295. bool isUtf8 = false;
  296. LPCOLESTR contentsRaw = nullptr;
  297. UINT lengthBytes = 0;
  298. hr = Helpers::LoadScriptFromFile(fileName, fileContents, &isUtf8, &contentsRaw, &lengthBytes);
  299. contentsRaw; lengthBytes; // Unused for now.
  300. IfFailGo(hr);
  301. if (HostConfigFlags::flags.GenerateLibraryByteCodeHeaderIsEnabled)
  302. {
  303. jsrtAttributes = (JsRuntimeAttributes)(jsrtAttributes | JsRuntimeAttributeSerializeLibraryByteCode);
  304. }
  305. IfJsErrorFailLog(ChakraRTInterface::JsCreateRuntime(jsrtAttributes, nullptr, &runtime));
  306. JsContextRef context = JS_INVALID_REFERENCE;
  307. IfJsErrorFailLog(ChakraRTInterface::JsCreateContext(runtime, &context));
  308. IfJsErrorFailLog(ChakraRTInterface::JsSetCurrentContext(context));
  309. if (!WScriptJsrt::Initialize())
  310. {
  311. IfFailGo(E_FAIL);
  312. }
  313. wchar_t fullPath[_MAX_PATH];
  314. if (_wfullpath(fullPath, fileName, _MAX_PATH) == nullptr)
  315. {
  316. IfFailGo(E_FAIL);
  317. }
  318. // canonicalize that path name to lower case for the profile storage
  319. size_t len = wcslen(fullPath);
  320. for (size_t i = 0; i < len; i++)
  321. {
  322. fullPath[i] = towlower(fullPath[i]);
  323. }
  324. if (HostConfigFlags::flags.GenerateLibraryByteCodeHeaderIsEnabled)
  325. {
  326. if (isUtf8)
  327. {
  328. if (HostConfigFlags::flags.GenerateLibraryByteCodeHeader != nullptr && *HostConfigFlags::flags.GenerateLibraryByteCodeHeader != L'\0')
  329. {
  330. WCHAR libraryName[_MAX_PATH];
  331. WCHAR ext[_MAX_EXT];
  332. _wsplitpath_s(fullPath, NULL, 0, NULL, 0, libraryName, _countof(libraryName), ext, _countof(ext));
  333. IfFailGo(CreateLibraryByteCodeHeader(fileContents, (BYTE*)contentsRaw, lengthBytes, HostConfigFlags::flags.GenerateLibraryByteCodeHeader, libraryName));
  334. }
  335. else
  336. {
  337. fwprintf(stderr, L"FATAL ERROR: -GenerateLibraryByteCodeHeader must provide the file name, i.e., -GenerateLibraryByteCodeHeader:<bytecode file name>, exiting\n");
  338. IfFailGo(E_FAIL);
  339. }
  340. }
  341. else
  342. {
  343. fwprintf(stderr, L"FATAL ERROR: GenerateLibraryByteCodeHeader flag can only be used on UTF8 file, exiting\n");
  344. IfFailGo(E_FAIL);
  345. }
  346. }
  347. else if (HostConfigFlags::flags.SerializedIsEnabled)
  348. {
  349. if (isUtf8)
  350. {
  351. CreateAndRunSerializedScript(fileName, fileContents, fullPath);
  352. }
  353. else
  354. {
  355. fwprintf(stderr, L"FATAL ERROR: Serialized flag can only be used on UTF8 file, exiting\n");
  356. IfFailGo(E_FAIL);
  357. }
  358. }
  359. else
  360. {
  361. IfFailGo(RunScript(fileName, fileContents, nullptr, fullPath));
  362. }
  363. Error:
  364. ChakraRTInterface::JsSetCurrentContext(nullptr);
  365. if (runtime != JS_INVALID_RUNTIME_HANDLE)
  366. {
  367. ChakraRTInterface::JsDisposeRuntime(runtime);
  368. }
  369. _flushall();
  370. return hr;
  371. }
  372. HRESULT ExecuteTestWithMemoryCheck(BSTR fileName)
  373. {
  374. HRESULT hr = E_FAIL;
  375. #ifdef CHECK_MEMORY_LEAK
  376. // Always check memory leak, unless user specified the flag already
  377. if (!ChakraRTInterface::IsEnabledCheckMemoryFlag())
  378. {
  379. ChakraRTInterface::SetCheckMemoryLeakFlag(true);
  380. }
  381. // Disable the output in case an unhandled exception happens
  382. // We will re-enable it if there is no unhandled exceptions
  383. ChakraRTInterface::SetEnableCheckMemoryLeakOutput(false);
  384. #endif
  385. __try
  386. {
  387. hr = ExecuteTest(fileName);
  388. }
  389. __except (HostExceptionFilter(GetExceptionCode(), GetExceptionInformation()))
  390. {
  391. _flushall();
  392. // Exception happened, so we probably didn't clean up properly,
  393. // Don't exit normally, just terminate
  394. TerminateProcess(::GetCurrentProcess(), GetExceptionCode());
  395. }
  396. _flushall();
  397. #ifdef CHECK_MEMORY_LEAK
  398. ChakraRTInterface::SetEnableCheckMemoryLeakOutput(true);
  399. #endif
  400. return hr;
  401. }
  402. unsigned int WINAPI StaticThreadProc(void *lpParam)
  403. {
  404. ChakraRTInterface::ArgInfo* argInfo = static_cast<ChakraRTInterface::ArgInfo* >(lpParam);
  405. _endthreadex(ExecuteTestWithMemoryCheck(*(argInfo->filename)));
  406. return 0;
  407. }
  408. int _cdecl wmain(int argc, __in_ecount(argc) LPWSTR argv[])
  409. {
  410. if (argc < 2)
  411. {
  412. PrintUsage();
  413. return EXIT_FAILURE;
  414. }
  415. HostConfigFlags::pfnPrintUsage = PrintUsageFormat;
  416. ATOM lock = ::AddAtom(szChakraCoreLock);
  417. AssertMsg(lock, "failed to lock chakracore.dll");
  418. HostConfigFlags::HandleArgsFlag(argc, argv);
  419. CComBSTR fileName;
  420. ChakraRTInterface::ArgInfo argInfo = { argc, argv, PrintUsage, &fileName.m_str };
  421. HINSTANCE chakraLibrary = ChakraRTInterface::LoadChakraDll(argInfo);
  422. if (chakraLibrary != nullptr)
  423. {
  424. HANDLE threadHandle;
  425. threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, &StaticThreadProc, &argInfo, STACK_SIZE_PARAM_IS_A_RESERVATION, 0));
  426. if (threadHandle != nullptr)
  427. {
  428. DWORD waitResult = WaitForSingleObject(threadHandle, INFINITE);
  429. Assert(waitResult == WAIT_OBJECT_0);
  430. CloseHandle(threadHandle);
  431. }
  432. else
  433. {
  434. fwprintf(stderr, L"FATAL ERROR: failed to create worker thread error code %d, exiting\n", errno);
  435. AssertMsg(false, "failed to create worker thread");
  436. }
  437. ChakraRTInterface::UnloadChakraDll(chakraLibrary);
  438. }
  439. return 0;
  440. }