WebAssembly.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 "RuntimeLibraryPch.h"
  6. #ifdef ENABLE_WASM
  7. #include "../WasmReader/WasmReaderPch.h"
  8. #include "Language/WebAssemblySource.h"
  9. using namespace Js;
  10. Var WebAssembly::EntryCompile(RecyclableObject* function, CallInfo callInfo, ...)
  11. {
  12. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  13. ARGUMENTS(args, callInfo);
  14. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  15. ScriptContext* scriptContext = function->GetScriptContext();
  16. Assert(!(callInfo.Flags & CallFlags_New));
  17. try
  18. {
  19. if (args.Info.Count < 2)
  20. {
  21. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedBufferSource);
  22. }
  23. WebAssemblySource src(args[1], true, scriptContext);
  24. WebAssemblyModule* wasmModule = WebAssemblyModule::CreateModule(scriptContext, &src);
  25. return JavascriptPromise::CreateResolvedPromise(wasmModule, scriptContext);
  26. }
  27. catch (JavascriptException & e)
  28. {
  29. return JavascriptPromise::CreateRejectedPromise(e.GetAndClear()->GetThrownObject(scriptContext), scriptContext);
  30. }
  31. }
  32. Var WebAssembly::EntryCompileStreaming(RecyclableObject* function, CallInfo callInfo, ...)
  33. {
  34. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  35. ARGUMENTS(args, callInfo);
  36. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  37. ScriptContext* scriptContext = function->GetScriptContext();
  38. JavascriptLibrary* library = scriptContext->GetLibrary();
  39. Assert(!(callInfo.Flags & CallFlags_New));
  40. try
  41. {
  42. if (args.Info.Count < 2)
  43. {
  44. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  45. }
  46. // Check to see if it was a response object
  47. Var responsePromise = TryResolveResponse(function, args[0], args[1]);
  48. if (responsePromise)
  49. {
  50. // Once we've resolved everything, create the module
  51. return JavascriptPromise::CreateThenPromise((JavascriptPromise*)responsePromise, library->GetWebAssemblyCompileFunction(), library->GetThrowerFunction(), scriptContext);
  52. }
  53. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  54. }
  55. catch (JavascriptException & e)
  56. {
  57. return JavascriptPromise::CreateRejectedPromise(e.GetAndClear()->GetThrownObject(scriptContext), scriptContext);
  58. }
  59. }
  60. Var WebAssembly::EntryInstantiate(RecyclableObject* function, CallInfo callInfo, ...)
  61. {
  62. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  63. ARGUMENTS(args, callInfo);
  64. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  65. ScriptContext* scriptContext = function->GetScriptContext();
  66. Assert(!(callInfo.Flags & CallFlags_New));
  67. Var resultObject = nullptr;
  68. try
  69. {
  70. if (args.Info.Count < 2)
  71. {
  72. JavascriptError::ThrowTypeError(scriptContext, WASMERR_InvalidInstantiateArgument);
  73. }
  74. Var importObject = scriptContext->GetLibrary()->GetUndefined();
  75. if (args.Info.Count >= 3)
  76. {
  77. importObject = args[2];
  78. }
  79. if (VarIs<WebAssemblyModule>(args[1]))
  80. {
  81. resultObject = WebAssemblyInstance::CreateInstance(VarTo<WebAssemblyModule>(args[1]), importObject);
  82. }
  83. else
  84. {
  85. WebAssemblySource src(args[1], true, scriptContext);
  86. WebAssemblyModule* wasmModule = WebAssemblyModule::CreateModule(scriptContext, &src);
  87. WebAssemblyInstance* instance = WebAssemblyInstance::CreateInstance(wasmModule, importObject);
  88. resultObject = JavascriptOperators::NewJavascriptObjectNoArg(scriptContext);
  89. JavascriptOperators::OP_SetProperty(resultObject, PropertyIds::module, wasmModule, scriptContext);
  90. JavascriptOperators::OP_SetProperty(resultObject, PropertyIds::instance, instance, scriptContext);
  91. }
  92. return JavascriptPromise::CreateResolvedPromise(resultObject, scriptContext);
  93. }
  94. catch (JavascriptException & e)
  95. {
  96. return JavascriptPromise::CreateRejectedPromise(e.GetAndClear()->GetThrownObject(scriptContext), scriptContext);
  97. }
  98. }
  99. Var WebAssembly::EntryInstantiateStreaming(RecyclableObject* function, CallInfo callInfo, ...)
  100. {
  101. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  102. ARGUMENTS(args, callInfo);
  103. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  104. ScriptContext* scriptContext = function->GetScriptContext();
  105. JavascriptLibrary* library = scriptContext->GetLibrary();
  106. Assert(!(callInfo.Flags & CallFlags_New));
  107. try
  108. {
  109. if (args.Info.Count < 2)
  110. {
  111. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  112. }
  113. // Check to see if it was a response object
  114. Var responsePromise = TryResolveResponse(function, args[0], args[1]);
  115. if (responsePromise)
  116. {
  117. Var importObject = scriptContext->GetLibrary()->GetUndefined();
  118. if (args.Info.Count >= 3)
  119. {
  120. importObject = args[2];
  121. }
  122. // Since instantiate takes extra arguments, we have to create a bound function to carry the importsObject until the response is resolved
  123. // Because function::bind() binds arguments from the left first, we have to calback a different function to reverse the order of the arguments
  124. Var boundArgs[] = { library->GetWebAssemblyInstantiateBoundFunction(), args[0], importObject };
  125. CallInfo boundCallInfo(CallFlags_Value, 3);
  126. ArgumentReader myargs(&boundCallInfo, boundArgs);
  127. RecyclableObject* boundFunction = BoundFunction::New(scriptContext, myargs);
  128. return JavascriptPromise::CreateThenPromise((JavascriptPromise*)responsePromise, boundFunction, library->GetThrowerFunction(), scriptContext);
  129. }
  130. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  131. }
  132. catch (JavascriptException & e)
  133. {
  134. return JavascriptPromise::CreateRejectedPromise(e.GetAndClear()->GetThrownObject(scriptContext), scriptContext);
  135. }
  136. }
  137. Var WebAssembly::EntryInstantiateBound(RecyclableObject* function, CallInfo callInfo, ...)
  138. {
  139. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  140. ARGUMENTS(args, callInfo);
  141. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  142. Assert(!(callInfo.Flags & CallFlags_New));
  143. Var thisVar = args[0];
  144. Var importObj = callInfo.Count > 1 ? args[1] : function->GetScriptContext()->GetLibrary()->GetUndefined();
  145. Var bufferSrc = callInfo.Count > 2 ? args[2] : function->GetScriptContext()->GetLibrary()->GetUndefined();
  146. return CALL_ENTRYPOINT_NOASSERT(EntryInstantiate, function, CallInfo(CallFlags_Value, 3), thisVar, bufferSrc, importObj);
  147. }
  148. Var WebAssembly::EntryValidate(RecyclableObject* function, CallInfo callInfo, ...)
  149. {
  150. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  151. ARGUMENTS(args, callInfo);
  152. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  153. ScriptContext* scriptContext = function->GetScriptContext();
  154. Assert(!(callInfo.Flags & CallFlags_New));
  155. if (args.Info.Count < 2)
  156. {
  157. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedBufferSource);
  158. }
  159. WebAssemblySource src(args[1], false, scriptContext);
  160. if (WebAssemblyModule::ValidateModule(scriptContext, &src))
  161. {
  162. return scriptContext->GetLibrary()->GetTrue();
  163. }
  164. else
  165. {
  166. return scriptContext->GetLibrary()->GetFalse();
  167. }
  168. }
  169. Var WebAssembly::EntryQueryResponse(RecyclableObject* function, CallInfo callInfo, ...)
  170. {
  171. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  172. ARGUMENTS(args, callInfo);
  173. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  174. ScriptContext* scriptContext = function->GetScriptContext();
  175. Assert(!(callInfo.Flags & CallFlags_New));
  176. // Make sure this is a Response object
  177. if (args.Info.Count < 2 || !IsResponseObject(args[1], scriptContext))
  178. {
  179. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  180. }
  181. Var responseObject = args[1];
  182. // Get the arrayBuffer method from the object
  183. PropertyString* propStr = scriptContext->GetPropertyString(PropertyIds::arrayBuffer);
  184. Var arrayBufferProp = JavascriptOperators::OP_GetElementI(responseObject, propStr, scriptContext);
  185. // Call res.arrayBuffer()
  186. if (!JavascriptConversion::IsCallable(arrayBufferProp))
  187. {
  188. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  189. }
  190. RecyclableObject* arrayBufferFunc = VarTo<RecyclableObject>(arrayBufferProp);
  191. Var arrayBufferRes = nullptr;
  192. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  193. {
  194. arrayBufferRes = CALL_FUNCTION(scriptContext->GetThreadContext(), arrayBufferFunc, Js::CallInfo(CallFlags_Value, 1), responseObject);
  195. }
  196. END_SAFE_REENTRANT_CALL
  197. // Make sure res.arrayBuffer() is a Promise
  198. if (!VarIs<JavascriptPromise>(arrayBufferRes))
  199. {
  200. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  201. }
  202. return arrayBufferRes;
  203. }
  204. bool WebAssembly::IsResponseObject(Var responseObject, ScriptContext* scriptContext)
  205. {
  206. if (!VarIs<RecyclableObject>(responseObject))
  207. {
  208. return false;
  209. }
  210. TypeId typeId = VarTo<RecyclableObject>(responseObject)->GetTypeId();
  211. if (!CONFIG_FLAG(WasmIgnoreResponse))
  212. {
  213. return scriptContext->IsWellKnownHostType<WellKnownHostType_Response>(typeId) && typeId != TypeIds_Undefined;
  214. }
  215. // Consider all object as Response objects under -wasmIgnoreResponse
  216. return typeId == TypeIds_Object;
  217. }
  218. Var WebAssembly::TryResolveResponse(RecyclableObject* function, Var thisArg, Var responseArg)
  219. {
  220. ScriptContext* scriptContext = function->GetScriptContext();
  221. JavascriptLibrary* library = scriptContext->GetLibrary();
  222. Var responsePromise = nullptr;
  223. bool isResponse = IsResponseObject(responseArg, scriptContext);
  224. if (isResponse)
  225. {
  226. CallInfo newCallInfo;
  227. newCallInfo.Count = 2;
  228. // We already have a response object, query it now
  229. responsePromise = CALL_ENTRYPOINT_NOASSERT(EntryQueryResponse, function, Js::CallInfo(CallFlags_Value, 2), thisArg, responseArg);
  230. }
  231. else if (VarIs<JavascriptPromise>(responseArg))
  232. {
  233. JavascriptPromise* promise = (JavascriptPromise*)responseArg;
  234. // Wait until this promise resolves and then try to query the response object (if it's a response object)
  235. responsePromise = JavascriptPromise::CreateThenPromise(promise, library->GetWebAssemblyQueryResponseFunction(), library->GetThrowerFunction(), scriptContext);
  236. }
  237. if (responsePromise && !VarIs<JavascriptPromise>(responsePromise))
  238. {
  239. AssertMsg(UNREACHED, "How did we end up with something other than a promise here ?");
  240. JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedResponse);
  241. }
  242. return responsePromise;
  243. }
  244. uint32
  245. WebAssembly::ToNonWrappingUint32(Var val, ScriptContext * ctx)
  246. {
  247. double i = JavascriptConversion::ToNumber(val, ctx);
  248. if (
  249. JavascriptNumber::IsNan(i) ||
  250. JavascriptNumber::IsPosInf(i) ||
  251. JavascriptNumber::IsNegInf(i) ||
  252. i < 0 ||
  253. i > (double)UINT32_MAX
  254. )
  255. {
  256. JavascriptError::ThrowTypeError(ctx, JSERR_NeedNumber);
  257. }
  258. return (uint32)JavascriptConversion::ToInteger(i);
  259. }
  260. void
  261. WebAssembly::CheckSignature(ScriptContext * scriptContext, Wasm::WasmSignature * sig1, Wasm::WasmSignature * sig2)
  262. {
  263. JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(Op_CheckWasmSignature);
  264. if (!sig1->IsEquivalent(sig2))
  265. {
  266. JavascriptError::ThrowWebAssemblyRuntimeError(scriptContext, WASMERR_SignatureMismatch);
  267. }
  268. JIT_HELPER_END(Op_CheckWasmSignature);
  269. }
  270. uint
  271. WebAssembly::GetSignatureSize()
  272. {
  273. return sizeof(Wasm::WasmSignature);
  274. }
  275. #endif // ENABLE_WASM