testCompileScript.cpp (8082B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "mozilla/RefPtr.h" // RefPtr 6 #include "mozilla/ScopeExit.h" // MakeScopeExit 7 #include "mozilla/Utf8.h" // mozilla::Utf8Unit 8 9 #include "frontend/CompilationStencil.h" // JS::Stencil, frontend::CompilationStencil 10 #include "js/CompileOptions.h" // JS::CompileOptions, JS::InstantiateOptions 11 #include "js/experimental/CompileScript.h" // JS::NewFrontendContext 12 #include "js/SourceText.h" // JS::Source{Ownership,Text} 13 #include "jsapi-tests/tests.h" 14 #include "vm/ErrorReporting.h" 15 #include "vm/JSONPrinter.h" // js::JSONPrinter 16 17 using namespace JS; 18 19 template <typename T> 20 static void dump(const T& data) { 21 js::Fprinter printer(stderr); 22 js::JSONPrinter json(printer, true); 23 24 data->dump(json); 25 printer.put("\n"); 26 } 27 28 BEGIN_TEST(testCompileScript) { 29 CHECK(testCompile()); 30 CHECK(testNonsyntacticCompile()); 31 CHECK(testCompileModule()); 32 CHECK(testPrepareForInstantiate()); 33 34 return true; 35 } 36 37 bool testCompile() { 38 static constexpr std::string_view src = "42\n"; 39 static constexpr std::u16string_view src_16 = u"42\n"; 40 41 static_assert(src.length() == src_16.length(), 42 "Source buffers must be same length"); 43 44 JS::CompileOptions options(cx); 45 46 JS::FrontendContext* fc = JS::NewFrontendContext(); 47 CHECK(fc); 48 auto destroyFc = 49 mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); }); 50 51 { // 16-bit characters 52 JS::SourceText<char16_t> buf16; 53 CHECK(buf16.init(cx, src_16.data(), src_16.length(), 54 JS::SourceOwnership::Borrowed)); 55 56 RefPtr<JS::Stencil> stencil = 57 CompileGlobalScriptToStencil(fc, options, buf16); 58 CHECK(stencil); 59 RefPtr<const js::frontend::CompilationStencil> initial = 60 stencil->getInitial(); 61 CHECK(initial->scriptExtra.size() == 1); 62 CHECK(initial->scriptExtra[0].extent.sourceStart == 0); 63 CHECK(initial->scriptExtra[0].extent.sourceEnd == 3); 64 CHECK(initial->scriptData.size() == 1); 65 CHECK(initial->scriptData[0].hasSharedData()); // has generated bytecode 66 CHECK(initial->scriptData[0].gcThingsLength == 1); 67 } 68 69 { // 8-bit characters 70 JS::SourceText<mozilla::Utf8Unit> buf8; 71 CHECK( 72 buf8.init(cx, src.data(), src.length(), JS::SourceOwnership::Borrowed)); 73 74 RefPtr<JS::Stencil> stencil = 75 CompileGlobalScriptToStencil(fc, options, buf8); 76 CHECK(stencil); 77 RefPtr<const js::frontend::CompilationStencil> initial = 78 stencil->getInitial(); 79 CHECK(initial->scriptExtra.size() == 1); 80 CHECK(initial->scriptExtra[0].extent.sourceStart == 0); 81 CHECK(initial->scriptExtra[0].extent.sourceEnd == 3); 82 CHECK(initial->scriptData.size() == 1); 83 CHECK(initial->scriptData[0].hasSharedData()); // has generated bytecode 84 CHECK(initial->scriptData[0].gcThingsLength == 1); 85 } 86 87 { // propagates failures 88 static constexpr std::string_view badSrc = "{ a: 1, b: 3\n"; 89 JS::SourceText<mozilla::Utf8Unit> srcBuf; 90 CHECK(srcBuf.init(cx, badSrc.data(), badSrc.length(), 91 JS::SourceOwnership::Borrowed)); 92 93 RefPtr<JS::Stencil> stencil = 94 CompileGlobalScriptToStencil(fc, options, srcBuf); 95 CHECK(!stencil); 96 CHECK(fc->maybeError().isSome()); 97 const js::CompileError& error = fc->maybeError().ref(); 98 CHECK(JSEXN_SYNTAXERR == error.exnType); 99 CHECK(error.lineno == 1); 100 CHECK(error.column.oneOriginValue() == 10); 101 } 102 103 return true; 104 } 105 106 bool testNonsyntacticCompile() { 107 const char* chars = 108 "function f() { return x; }" 109 "f();"; 110 111 JS::SourceText<mozilla::Utf8Unit> srcBuf; 112 CHECK(srcBuf.init(cx, chars, strlen(chars), JS::SourceOwnership::Borrowed)); 113 114 JS::CompileOptions options(cx); 115 options.setNonSyntacticScope(true); 116 117 JS::FrontendContext* fc = JS::NewFrontendContext(); 118 CHECK(fc); 119 auto destroyFc = 120 mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); }); 121 122 RefPtr<JS::Stencil> stencil = 123 CompileGlobalScriptToStencil(fc, options, srcBuf); 124 CHECK(stencil); 125 126 JS::InstantiateOptions instantiateOptions(options); 127 JS::RootedScript script( 128 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil)); 129 CHECK(script); 130 CHECK(script->hasNonSyntacticScope()); 131 132 return true; 133 } 134 135 bool testCompileModule() { 136 static constexpr std::string_view src = "import 'a'; 42\n"; 137 static constexpr std::u16string_view src_16 = u"import 'a'; 42\n"; 138 139 static_assert(src.length() == src_16.length(), 140 "Source buffers must be same length"); 141 142 JS::CompileOptions options(cx); 143 144 JS::FrontendContext* fc = JS::NewFrontendContext(); 145 CHECK(fc); 146 auto destroyFc = 147 mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); }); 148 149 { // 16-bit characters 150 JS::SourceText<char16_t> buf16; 151 CHECK(buf16.init(cx, src_16.data(), src_16.length(), 152 JS::SourceOwnership::Borrowed)); 153 154 RefPtr<JS::Stencil> stencil = 155 CompileModuleScriptToStencil(fc, options, buf16); 156 CHECK(stencil); 157 RefPtr<const js::frontend::CompilationStencil> initial = 158 stencil->getInitial(); 159 CHECK(initial->isModule()); 160 CHECK(initial->scriptExtra.size() == 1); 161 CHECK(initial->scriptExtra[0].extent.sourceStart == 0); 162 CHECK(initial->scriptExtra[0].extent.sourceEnd == 15); 163 CHECK(initial->scriptData.size() == 1); 164 CHECK(initial->scriptData[0].hasSharedData()); // has generated bytecode 165 CHECK(initial->scriptData[0].gcThingsLength == 1); 166 } 167 168 { // 8-bit characters 169 JS::SourceText<mozilla::Utf8Unit> buf8; 170 CHECK( 171 buf8.init(cx, src.data(), src.length(), JS::SourceOwnership::Borrowed)); 172 173 RefPtr<JS::Stencil> stencil = 174 CompileModuleScriptToStencil(fc, options, buf8); 175 CHECK(stencil); 176 RefPtr<const js::frontend::CompilationStencil> initial = 177 stencil->getInitial(); 178 CHECK(initial->scriptExtra.size() == 1); 179 CHECK(initial->scriptExtra[0].extent.sourceStart == 0); 180 CHECK(initial->scriptExtra[0].extent.sourceEnd == 15); 181 CHECK(initial->scriptData.size() == 1); 182 CHECK(initial->scriptData[0].hasSharedData()); // has generated bytecode 183 CHECK(initial->scriptData[0].gcThingsLength == 1); 184 } 185 186 { // propagates failures 187 static constexpr std::string_view badSrc = "{ a: 1, b: 3\n"; 188 JS::SourceText<mozilla::Utf8Unit> srcBuf; 189 CHECK(srcBuf.init(cx, badSrc.data(), badSrc.length(), 190 JS::SourceOwnership::Borrowed)); 191 192 RefPtr<JS::Stencil> stencil = 193 CompileModuleScriptToStencil(fc, options, srcBuf); 194 CHECK(!stencil); 195 CHECK(fc->maybeError().isSome()); 196 const js::CompileError& error = fc->maybeError().ref(); 197 CHECK(JSEXN_SYNTAXERR == error.exnType); 198 CHECK(error.lineno == 1); 199 CHECK(error.column.oneOriginValue() == 10); 200 } 201 202 return true; 203 } 204 205 bool testPrepareForInstantiate() { 206 static constexpr std::u16string_view src_16 = 207 u"function f() { return {'field': 42};}; f()['field']\n"; 208 209 JS::CompileOptions options(cx); 210 211 JS::SourceText<char16_t> buf16; 212 CHECK(buf16.init(cx, src_16.data(), src_16.length(), 213 JS::SourceOwnership::Borrowed)); 214 215 JS::FrontendContext* fc = JS::NewFrontendContext(); 216 CHECK(fc); 217 auto destroyFc = 218 mozilla::MakeScopeExit([fc] { JS::DestroyFrontendContext(fc); }); 219 220 RefPtr<JS::Stencil> stencil = 221 CompileGlobalScriptToStencil(fc, options, buf16); 222 CHECK(stencil); 223 RefPtr<const js::frontend::CompilationStencil> initial = 224 stencil->getInitial(); 225 CHECK(initial->scriptData.size() == 2); 226 CHECK(initial->scopeData.size() == 1); // function f 227 CHECK(initial->parserAtomData.size() == 1); // 'field' 228 229 JS::InstantiationStorage storage; 230 CHECK(JS::PrepareForInstantiate(fc, *stencil, storage)); 231 CHECK(storage.isValid()); 232 // TODO storage.gcOutput_ is private, so there isn't a good way to check the 233 // scriptData and scopeData capacities 234 235 return true; 236 } 237 END_TEST(testCompileScript);