testStencil.cpp (10771B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include <string.h> 9 10 #include "jsapi.h" 11 12 #include "frontend/CompilationStencil.h" 13 #include "js/CompilationAndEvaluation.h" 14 #include "js/EnvironmentChain.h" // JS::EnvironmentChain 15 #include "js/experimental/CompileScript.h" 16 #include "js/experimental/JSStencil.h" 17 #include "js/Modules.h" 18 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_HasOwnProperty, JS_SetProperty 19 #include "js/Transcoding.h" 20 #include "jsapi-tests/tests.h" 21 #include "vm/HelperThreads.h" // js::RunPendingSourceCompressions 22 #include "vm/Monitor.h" // js::Monitor, js::AutoLockMonitor 23 24 BEGIN_TEST(testStencil_Basic) { 25 const char* chars = 26 "function f() { return 42; }" 27 "f();"; 28 auto result = basic_test<char, mozilla::Utf8Unit>(chars); 29 CHECK(result); 30 31 const char16_t* chars16 = 32 u"function f() { return 42; }" 33 u"f();"; 34 auto result16 = basic_test<char16_t, char16_t>(chars16); 35 CHECK(result16); 36 37 return true; 38 } 39 40 template <typename CharT, typename SourceT> 41 bool basic_test(const CharT* chars) { 42 size_t length = std::char_traits<CharT>::length(chars); 43 44 JS::SourceText<SourceT> srcBuf; 45 CHECK(srcBuf.init(cx, chars, length, JS::SourceOwnership::Borrowed)); 46 47 JS::CompileOptions options(cx); 48 RefPtr<JS::Stencil> stencil = 49 JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 50 CHECK(stencil); 51 52 JS::InstantiateOptions instantiateOptions(options); 53 JS::RootedScript script( 54 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 55 CHECK(script); 56 57 JS::RootedValue rval(cx); 58 CHECK(JS_ExecuteScript(cx, script, &rval)); 59 CHECK(rval.isNumber() && rval.toNumber() == 42); 60 61 return true; 62 } 63 END_TEST(testStencil_Basic) 64 65 BEGIN_TEST(testStencil_Module) { 66 const char* chars = 67 "export function f() { return 42; }" 68 "globalThis.x = f();"; 69 auto result = basic_test<char, mozilla::Utf8Unit>(chars); 70 CHECK(result); 71 72 const char16_t* chars16 = 73 u"export function f() { return 42; }" 74 u"globalThis.x = f();"; 75 auto result16 = basic_test<char16_t, char16_t>(chars16); 76 CHECK(result16); 77 78 return true; 79 } 80 81 template <typename CharT, typename SourceT> 82 bool basic_test(const CharT* chars) { 83 size_t length = std::char_traits<CharT>::length(chars); 84 85 JS::SourceText<SourceT> srcBuf; 86 CHECK(srcBuf.init(cx, chars, length, JS::SourceOwnership::Borrowed)); 87 88 JS::CompileOptions options(cx); 89 options.setFile("testStencil_Module"); 90 91 RefPtr<JS::Stencil> stencil = 92 JS::CompileModuleScriptToStencil(cx, options, srcBuf); 93 CHECK(stencil); 94 95 JS::InstantiateOptions instantiateOptions(options); 96 JS::RootedObject moduleObject( 97 cx, JS::InstantiateModuleStencil(cx, instantiateOptions, stencil)); 98 CHECK(moduleObject); 99 100 CHECK(JS::LoadRequestedModules(cx, moduleObject, JS::UndefinedHandleValue, 101 OnResolve, OnReject)); 102 103 // Link and evaluate the module graph. The link step used to be call 104 // "instantiate" but is unrelated to the concept in Stencil with same name. 105 JS::RootedValue rval(cx); 106 CHECK(JS::ModuleLink(cx, moduleObject)); 107 CHECK(JS::ModuleEvaluate(cx, moduleObject, &rval)); 108 CHECK(!rval.isUndefined()); 109 110 js::RunJobs(cx); 111 CHECK(JS_GetProperty(cx, global, "x", &rval)); 112 CHECK(rval.isNumber() && rval.toNumber() == 42); 113 114 return true; 115 } 116 117 static bool OnResolve(JSContext* cx, JS::HandleValue hostDefined) { 118 return true; 119 } 120 static bool OnReject(JSContext* cx, JS::HandleValue hostDefined, 121 JS::HandleValue error) { 122 return true; 123 } 124 125 END_TEST(testStencil_Module) 126 127 BEGIN_TEST(testStencil_NonSyntactic) { 128 const char* chars = 129 "function f() { return x; }" 130 "f();"; 131 132 JS::SourceText<mozilla::Utf8Unit> srcBuf; 133 CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); 134 135 JS::CompileOptions options(cx); 136 options.setNonSyntacticScope(true); 137 138 RefPtr<JS::Stencil> stencil = 139 JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 140 CHECK(stencil); 141 142 JS::InstantiateOptions instantiateOptions(options); 143 JS::RootedScript script( 144 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 145 CHECK(script); 146 147 JS::RootedObject obj(cx, JS_NewPlainObject(cx)); 148 JS::RootedValue val(cx, JS::Int32Value(42)); 149 CHECK(obj); 150 CHECK(JS_SetProperty(cx, obj, "x", val)); 151 152 JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::No); 153 CHECK(envChain.append(obj)); 154 155 JS::RootedValue rval(cx); 156 CHECK(JS_ExecuteScript(cx, envChain, script, &rval)); 157 CHECK(rval.isNumber() && rval.toNumber() == 42); 158 159 return true; 160 } 161 END_TEST(testStencil_NonSyntactic) 162 163 BEGIN_TEST(testStencil_MultiGlobal) { 164 const char* chars = 165 "/**************************************/" 166 "/**************************************/" 167 "/**************************************/" 168 "/**************************************/" 169 "/**************************************/" 170 "/**************************************/" 171 "function f() { return 42; }" 172 "f();"; 173 174 JS::SourceText<mozilla::Utf8Unit> srcBuf; 175 CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); 176 177 JS::CompileOptions options(cx); 178 RefPtr<JS::Stencil> stencil = 179 JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 180 CHECK(stencil); 181 182 CHECK(RunInNewGlobal(cx, stencil)); 183 CHECK(RunInNewGlobal(cx, stencil)); 184 CHECK(RunInNewGlobal(cx, stencil)); 185 186 // Start any pending SourceCompressionTasks now to confirm nothing fell apart 187 // when using a JS::Stencil multiple times. 188 CHECK(strlen(chars) > js::ScriptSource::MinimumCompressibleLength); 189 js::RunPendingSourceCompressions(cx->runtime()); 190 191 return true; 192 } 193 bool RunInNewGlobal(JSContext* cx, RefPtr<JS::Stencil> stencil) { 194 JS::RootedObject otherGlobal(cx, createGlobal()); 195 CHECK(otherGlobal); 196 197 JSAutoRealm ar(cx, otherGlobal); 198 199 JS::InstantiateOptions instantiateOptions; 200 JS::RootedScript script( 201 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 202 CHECK(script); 203 204 JS::RootedValue rval(cx); 205 CHECK(JS_ExecuteScript(cx, script, &rval)); 206 CHECK(rval.isNumber() && rval.toNumber() == 42); 207 208 return true; 209 } 210 END_TEST(testStencil_MultiGlobal) 211 212 BEGIN_TEST(testStencil_Transcode) { 213 JS::SetProcessBuildIdOp(TestGetBuildId); 214 215 JS::TranscodeBuffer buffer; 216 217 { 218 const char* chars = 219 "function f() { return 42; }" 220 "f();"; 221 222 JS::SourceText<mozilla::Utf8Unit> srcBuf; 223 CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); 224 225 JS::CompileOptions options(cx); 226 RefPtr<JS::Stencil> stencil = 227 JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 228 CHECK(stencil); 229 230 // Encode Stencil to XDR 231 JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); 232 CHECK(res == JS::TranscodeResult::Ok); 233 CHECK(!buffer.empty()); 234 235 // Instantiate and Run 236 JS::InstantiateOptions instantiateOptions(options); 237 JS::RootedScript script( 238 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 239 JS::RootedValue rval(cx); 240 CHECK(script); 241 CHECK(JS_ExecuteScript(cx, script, &rval)); 242 CHECK(rval.isNumber() && rval.toNumber() == 42); 243 } 244 245 // Create a new global 246 CHECK(createGlobal()); 247 JSAutoRealm ar(cx, global); 248 249 // Confirm it doesn't have the old code 250 bool found = false; 251 CHECK(JS_HasOwnProperty(cx, global, "f", &found)); 252 CHECK(!found); 253 254 { 255 // Decode the stencil into new range 256 RefPtr<JS::Stencil> stencil; 257 258 { 259 JS::DecodeOptions decodeOptions; 260 JS::TranscodeRange range(buffer.begin(), buffer.length()); 261 JS::TranscodeResult res = 262 JS::DecodeStencil(cx, decodeOptions, range, getter_AddRefs(stencil)); 263 CHECK(res == JS::TranscodeResult::Ok); 264 } 265 266 { 267 JS::FrontendContext* fc = JS::NewFrontendContext(); 268 JS::DecodeOptions decodeOptions; 269 JS::TranscodeRange range(buffer.begin(), buffer.length()); 270 JS::TranscodeResult res = 271 JS::DecodeStencil(fc, decodeOptions, range, getter_AddRefs(stencil)); 272 CHECK(res == JS::TranscodeResult::Ok); 273 JS::DestroyFrontendContext(fc); 274 } 275 276 // Delete the buffer to verify that the decoded stencil has no dependency 277 // to the buffer. 278 memset(buffer.begin(), 0, buffer.length()); 279 buffer.clear(); 280 281 // Instantiate and Run 282 JS::InstantiateOptions instantiateOptions; 283 JS::RootedScript script( 284 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 285 stencil = nullptr; 286 JS::RootedValue rval(cx); 287 CHECK(script); 288 CHECK(JS_ExecuteScript(cx, script, &rval)); 289 CHECK(rval.isNumber() && rval.toNumber() == 42); 290 } 291 292 return true; 293 } 294 static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { 295 const char buildid[] = "testXDR"; 296 return buildId->append(buildid, sizeof(buildid)); 297 } 298 END_TEST(testStencil_Transcode) 299 300 BEGIN_TEST(testStencil_TranscodeBorrowing) { 301 JS::SetProcessBuildIdOp(TestGetBuildId); 302 303 JS::TranscodeBuffer buffer; 304 305 { 306 const char* chars = 307 "function f() { return 42; }" 308 "f();"; 309 310 JS::SourceText<mozilla::Utf8Unit> srcBuf; 311 CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); 312 313 JS::CompileOptions options(cx); 314 RefPtr<JS::Stencil> stencil = 315 JS::CompileGlobalScriptToStencil(cx, options, srcBuf); 316 CHECK(stencil); 317 318 // Encode Stencil to XDR 319 JS::TranscodeResult res = JS::EncodeStencil(cx, stencil, buffer); 320 CHECK(res == JS::TranscodeResult::Ok); 321 CHECK(!buffer.empty()); 322 } 323 324 JS::RootedScript script(cx); 325 { 326 JS::TranscodeRange range(buffer.begin(), buffer.length()); 327 JS::DecodeOptions decodeOptions; 328 decodeOptions.borrowBuffer = true; 329 RefPtr<JS::Stencil> stencil; 330 JS::TranscodeResult res = 331 JS::DecodeStencil(cx, decodeOptions, range, getter_AddRefs(stencil)); 332 CHECK(res == JS::TranscodeResult::Ok); 333 334 JS::InstantiateOptions instantiateOptions; 335 script = JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil); 336 CHECK(script); 337 } 338 339 // Delete the buffer to verify that the instantiated script has no dependency 340 // to the buffer. 341 memset(buffer.begin(), 0, buffer.length()); 342 buffer.clear(); 343 344 JS::RootedValue rval(cx); 345 CHECK(JS_ExecuteScript(cx, script, &rval)); 346 CHECK(rval.isNumber() && rval.toNumber() == 42); 347 348 return true; 349 } 350 static bool TestGetBuildId(JS::BuildIdCharVector* buildId) { 351 const char buildid[] = "testXDR"; 352 return buildId->append(buildid, sizeof(buildid)); 353 } 354 END_TEST(testStencil_TranscodeBorrowing)