tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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)