tor-browser

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

globals.js (19111B)


      1 const { Instance, Module, LinkError } = WebAssembly;
      2 
      3 // Locally-defined globals
      4 assertErrorMessage(() => wasmEvalText(`(module (global))`), SyntaxError, /wasm text error/);
      5 // A global field in the text format is valid with an empty expression, but this produces an invalid module
      6 assertErrorMessage(() => wasmEvalText(`(module (global i32))`), WebAssembly.CompileError, /popping value/);
      7 assertErrorMessage(() => wasmEvalText(`(module (global (mut i32)))`), WebAssembly.CompileError, /popping value/);
      8 
      9 // Initializer expressions.
     10 wasmFailValidateText(`(module (global i32 (f32.const 13.37)))`, /type mismatch/);
     11 wasmFailValidateText(`(module (global f64 (f32.const 13.37)))`, /type mismatch/);
     12 
     13 wasmFailValidateText(`(module (global i32 (global.get 0)))`, /out of range/);
     14 wasmFailValidateText(`(module (global i32 (global.get 1)) (global i32 (i32.const 1)))`, /out of range/);
     15 
     16 // Test a well-defined global section.
     17 function testInner(type, initialValue, nextValue, coercion)
     18 {
     19    var module = wasmEvalText(`(module
     20        (global (mut ${type}) (${type}.const ${initialValue}))
     21        (global ${type} (${type}.const ${initialValue}))
     22 
     23        (func $get (result ${type}) (global.get 0))
     24        (func $set (param ${type}) (global.set 0 (local.get 0)))
     25 
     26        (func $get_cst (result ${type}) (global.get 1))
     27 
     28        (export "get" (func $get))
     29        (export "get_cst" (func $get_cst))
     30 
     31        (export "set" (func $set))
     32    )`).exports;
     33 
     34    assertEq(module.get(), coercion(initialValue));
     35    assertEq(module.set(coercion(nextValue)), undefined);
     36    assertEq(module.get(), coercion(nextValue));
     37 
     38    assertEq(module.get_cst(), coercion(initialValue));
     39 }
     40 
     41 testInner('i32', 13, 37, x => x|0);
     42 testInner('f32', 13.37, 0.1989, Math.fround);
     43 testInner('f64', 13.37, 0.1989, x => +x);
     44 
     45 // Basic global shenanigans
     46 {
     47    const module = wasmEvalText(`(module
     48        ;; -2 * (5 - (-10 + 20)) = 10
     49        (global i32 (i32.mul (i32.const -2) (i32.sub (i32.const 5) (i32.add (i32.const -10) (i32.const 20)))))
     50        ;; ((1 + 2) - (3 * 4)) = -9
     51        (global i64 (i64.sub (i64.add (i64.const 1) (i64.const 2)) (i64.mul (i64.const 3) (i64.const 4))))
     52 
     53        (func (export "get0") (result i32) global.get 0)
     54        (func (export "get1") (result i64) global.get 1)
     55    )`).exports;
     56 
     57    assertEq(module.get0(), 10);
     58    assertEq(module.get1(), -9n);
     59 }
     60 
     61 // Example use of dynamic linking
     62 {
     63    // Make a memory for two dynamically-linked modules to share. Each module gets five pages.
     64    const mem = new WebAssembly.Memory({ initial: 15, maximum: 15 });
     65 
     66    const mod1 = new WebAssembly.Module(wasmTextToBinary(`(module
     67        (memory (import "env" "memory") 15 15)
     68        (global $memBase (import "env" "__memory_base") i32)
     69        (data (offset (global.get $memBase)) "Hello from module 1.")
     70        (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 1.")
     71    )`));
     72    const instance1 = new WebAssembly.Instance(mod1, {
     73        env: {
     74            memory: mem,
     75            __memory_base: 65536 * 5, // this module's memory starts at page 5
     76        },
     77    });
     78 
     79    const mod2 = new WebAssembly.Module(wasmTextToBinary(`(module
     80        (memory (import "env" "memory") 15 15)
     81        (global $memBase (import "env" "__memory_base") i32)
     82        (data (offset (global.get $memBase)) "Hello from module 2.")
     83        (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 2.")
     84    )`));
     85    const instance2 = new WebAssembly.Instance(mod2, {
     86        env: {
     87            memory: mem,
     88            __memory_base: 65536 * 10, // this module's memory starts at page 10
     89        },
     90    });
     91 
     92    // All four strings should now be present in the memory.
     93 
     94    function assertStringInMem(mem, str, addr) {
     95        const bytes = new Uint8Array(mem.buffer).slice(addr, addr + str.length);
     96        let memStr = String.fromCharCode(...bytes);
     97        assertEq(memStr, str);
     98    }
     99 
    100    assertStringInMem(mem, "Hello from module 1.", 65536 * 5);
    101    assertStringInMem(mem, "Goodbye from module 1.", 65536 * 6);
    102    assertStringInMem(mem, "Hello from module 2.", 65536 * 10);
    103    assertStringInMem(mem, "Goodbye from module 2.", 65536 * 11);
    104 }
    105 
    106 // Semantic errors.
    107 wasmFailValidateText(`(module (global (mut i32) (i32.const 1337)) (func (global.set 1 (i32.const 0))))`, /(out of range)|(global index out of bounds)/);
    108 wasmFailValidateText(`(module (global i32 (i32.const 1337)) (func (global.set 0 (i32.const 0))))`, /(can't write an immutable global)|(global is immutable)/);
    109 
    110 // Big module with many variables: test that setting one doesn't overwrite the
    111 // other ones.
    112 function get_set(i, type) {
    113    return `
    114        (func $get_${i} (result ${type}) (global.get ${i}))
    115        (func $set_${i} (param ${type}) (global.set ${i} (local.get 0)))
    116    `;
    117 }
    118 
    119 var module = wasmEvalText(`(module
    120    (global (mut i32) (i32.const 42))
    121    (global (mut i32) (i32.const 10))
    122    (global (mut f32) (f32.const 13.37))
    123    (global (mut f64) (f64.const 13.37))
    124    (global (mut i32) (i32.const -18))
    125 
    126    ${get_set(0, 'i32')}
    127    ${get_set(1, 'i32')}
    128    ${get_set(2, 'f32')}
    129    ${get_set(3, 'f64')}
    130    ${get_set(4, 'i32')}
    131 
    132    (export "get0" (func $get_0)) (export "set0" (func $set_0))
    133    (export "get1" (func $get_1)) (export "set1" (func $set_1))
    134    (export "get2" (func $get_2)) (export "set2" (func $set_2))
    135    (export "get3" (func $get_3)) (export "set3" (func $set_3))
    136    (export "get4" (func $get_4)) (export "set4" (func $set_4))
    137 )`).exports;
    138 
    139 let values = [42, 10, Math.fround(13.37), 13.37, -18];
    140 let nextValues = [13, 37, Math.fround(-17.89), 9.3, -13];
    141 for (let i = 0; i < 5; i++) {
    142    assertEq(module[`get${i}`](), values[i]);
    143    assertEq(module[`set${i}`](nextValues[i]), undefined);
    144    assertEq(module[`get${i}`](), nextValues[i]);
    145    for (let j = 0; j < 5; j++) {
    146        if (i === j)
    147            continue;
    148        assertEq(module[`get${j}`](), values[j]);
    149    }
    150    assertEq(module[`set${i}`](values[i]), undefined);
    151    assertEq(module[`get${i}`](), values[i]);
    152 }
    153 
    154 // Initializer expressions can also be used in elem section initializers.
    155 wasmFailValidateText(`(module (import "globals" "a" (global f32)) (table 4 funcref) (elem (global.get 0) $f) (func $f))`, /type mismatch/);
    156 
    157 module = wasmEvalText(`(module
    158    (import "globals" "a" (global i32))
    159    (table (export "tbl") 4 funcref)
    160    (elem (global.get 0) $f)
    161    (func $f)
    162    (export "f" (func $f))
    163 )`, {
    164    globals: {
    165        a: 1
    166    }
    167 }).exports;
    168 assertEq(module.f, module.tbl.get(1));
    169 
    170 // Import/export semantics.
    171 module = wasmEvalText(`(module
    172 (import "globals" "x" (global $g i32))
    173 (func $get (result i32) (global.get $g))
    174 (export "getter" (func $get))
    175 (export "value" (global 0))
    176 )`, { globals: {x: 42} }).exports;
    177 
    178 assertEq(module.getter(), 42);
    179 
    180 // assertEq() will not trigger @@toPrimitive, so we must have a cast here.
    181 assertEq(Number(module.value), 42);
    182 
    183 // Can only import numbers (no implicit coercions).
    184 module = new Module(wasmTextToBinary(`(module
    185    (global (import "globs" "i32") i32)
    186    (global (import "globs" "f32") f32)
    187    (global (import "globs" "f64") f32)
    188 )`));
    189 
    190 const assertLinkFails = (m, imp, err) => {
    191    assertErrorMessage(() => new Instance(m, imp), LinkError, err);
    192 }
    193 
    194 var imp = {
    195    globs: {
    196        i32: 0,
    197        f32: Infinity,
    198        f64: NaN
    199    }
    200 };
    201 
    202 let i = new Instance(module, imp);
    203 
    204 for (let v of [
    205    null,
    206    {},
    207    "42",
    208    /not a number/,
    209    false,
    210    undefined,
    211    Symbol(),
    212    { valueOf() { return 42; } }
    213 ]) {
    214    imp.globs.i32 = v;
    215    assertLinkFails(module, imp, /not a Number/);
    216 
    217    imp.globs.i32 = 0;
    218    imp.globs.f32 = v;
    219    assertLinkFails(module, imp, /not a Number/);
    220 
    221    imp.globs.f32 = Math.fround(13.37);
    222    imp.globs.f64 = v;
    223    assertLinkFails(module, imp, /not a Number/);
    224 
    225    imp.globs.f64 = 13.37;
    226 }
    227 
    228 // Imported globals and locally defined globals use the same index space.
    229 module = wasmEvalText(`(module
    230 (import "globals" "x" (global i32))
    231 (global i32 (i32.const 1337))
    232 (export "imported" (global 0))
    233 (export "defined" (global 1))
    234 )`, { globals: {x: 42} }).exports;
    235 
    236 assertEq(Number(module.imported), 42);
    237 assertEq(Number(module.defined), 1337);
    238 
    239 wasmFailValidateText(`(module (import "globals" "a" (global f32)) (global i32 (global.get 0)))`, /type mismatch/);
    240 
    241 function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) {
    242    var module = wasmEvalText(`(module
    243        (import "globals" "a" (global ${type}))
    244 
    245        (global $glob_mut (mut ${type}) (global.get 0))
    246        (global $glob_imm ${type} (global.get 0))
    247 
    248        (func $get0 (result ${type}) (global.get 0))
    249 
    250        (func $get1 (result ${type}) (global.get 1))
    251        (func $set1 (param ${type}) (global.set 1 (local.get 0)))
    252 
    253        (func $get_cst (result ${type}) (global.get 2))
    254 
    255        (export "get0" (func $get0))
    256        (export "get1" (func $get1))
    257        (export "get_cst" (func $get_cst))
    258 
    259        (export "set1" (func $set1))
    260        (export "global_imm" (global $glob_imm))
    261    )`, {
    262        globals: {
    263            a: coercion(initialValue)
    264        }
    265    }).exports;
    266 
    267    assertFunc(module.get0(), coercion(initialValue));
    268    assertFunc(module.get1(), coercion(initialValue));
    269    assertFunc(Number(module.global_imm), coercion(initialValue));
    270 
    271    assertEq(module.set1(coercion(nextValue)), undefined);
    272    assertFunc(module.get1(), coercion(nextValue));
    273    assertFunc(module.get0(), coercion(initialValue));
    274    assertFunc(Number(module.global_imm), coercion(initialValue));
    275 
    276    assertFunc(module.get_cst(), coercion(initialValue));
    277 }
    278 
    279 testInitExpr('i32', 13, 37, x => x|0);
    280 testInitExpr('f32', 13.37, 0.1989, Math.fround);
    281 testInitExpr('f64', 13.37, 0.1989, x => +x);
    282 
    283 // Int64.
    284 
    285 // Import and export
    286 
    287 // Test inner
    288 var initialValue = '0x123456789abcdef0';
    289 var nextValue = '0x531642753864975F';
    290 wasmAssert(`(module
    291    (global (mut i64) (i64.const ${initialValue}))
    292    (global i64 (i64.const ${initialValue}))
    293    (func $get (result i64) (global.get 0))
    294    (func $set (param i64) (global.set 0 (local.get 0)))
    295    (func $get_cst (result i64) (global.get 1))
    296    (export "get" (func $get))
    297    (export "get_cst" (func $get_cst))
    298    (export "set" (func $set))
    299 )`, [
    300    {type: 'i64', func: '$get', expected: initialValue},
    301    {type: 'i64', func: '$set', args: [`i64.const ${nextValue}`]},
    302    {type: 'i64', func: '$get', expected: nextValue},
    303    {type: 'i64', func: '$get_cst', expected: initialValue},
    304 ]);
    305 
    306 // Custom NaN.
    307 {
    308    let dv = new DataView(new ArrayBuffer(8));
    309    module = wasmEvalText(`(module
    310     (global $g f64 (f64.const -nan:0xe7ffff1591120))
    311     (global $h f32 (f32.const -nan:0x651234))
    312     (export "nan64" (global $g))(export "nan32" (global $h))
    313    )`, {}).exports;
    314 
    315    dv.setFloat64(0, module.nan64, true);
    316    assertEq(dv.getUint32(4, true), 0x7ff80000);
    317    assertEq(dv.getUint32(0, true), 0x00000000);
    318 
    319    dv.setFloat32(0, module.nan32, true);
    320    assertEq(dv.getUint32(0, true), 0x7fc00000);
    321 }
    322 
    323 // WebAssembly.Global
    324 {
    325    const Global = WebAssembly.Global;
    326 
    327    // These types should work:
    328    assertEq(new Global({value: "i32"}) instanceof Global, true);
    329    assertEq(new Global({value: "f32"}) instanceof Global, true);
    330    assertEq(new Global({value: "f64"}) instanceof Global, true);
    331    assertEq(new Global({value: "i64"}) instanceof Global, true); // No initial value works
    332 
    333    // Coercion of init value; ".value" accessor
    334    assertEq((new Global({value: "i32"}, 3.14)).value, 3);
    335    assertEq((new Global({value: "f32"}, { valueOf: () => 33.5 })).value, 33.5);
    336    assertEq((new Global({value: "f64"}, "3.25")).value, 3.25);
    337 
    338    // Nothing special about NaN, it coerces just fine
    339    assertEq((new Global({value: "i32"}, NaN)).value, 0);
    340 
    341    // The default init value is zero.
    342    assertEq((new Global({value: "i32"})).value, 0);
    343    assertEq((new Global({value: "f32"})).value, 0);
    344    assertEq((new Global({value: "f64"})).value, 0);
    345    let mod = wasmEvalText(`(module
    346                             (import "" "g" (global i64))
    347                             (func (export "f") (result i32)
    348                              (i64.eqz (global.get 0))))`,
    349                           {"":{g: new Global({value: "i64"})}});
    350    assertEq(mod.exports.f(), 1);
    351 
    352    {
    353        // "value" is enumerable
    354        let x = new Global({value: "i32"});
    355        let s = "";
    356        for ( let i in x )
    357            s = s + i + ",";
    358        if (getBuildConfiguration("release_or_beta")) {
    359            assertEq(s, "valueOf,value,");
    360        } else {
    361            assertEq(s, "type,valueOf,value,");
    362        }
    363    }
    364 
    365    // "value" is defined on the prototype, not on the object
    366    assertEq("value" in Global.prototype, true);
    367 
    368    // Can't set the value of an immutable global
    369    assertErrorMessage(() => (new Global({value: "i32"})).value = 10,
    370                       TypeError,
    371                       /can't set value of immutable global/);
    372 
    373    {
    374        // Can set the value of a mutable global
    375        let g = new Global({value: "i32", mutable: true}, 37);
    376        g.value = 10;
    377        assertEq(g.value, 10);
    378    }
    379 
    380    {
    381        // Misc internal conversions
    382        let g = new Global({value: "i32"}, 42);
    383 
    384        // valueOf
    385        assertEq(g - 5, 37);
    386 
    387        // @@toStringTag
    388        assertEq(g.toString(), "[object WebAssembly.Global]");
    389    }
    390 
    391    {
    392        // An exported global should appear as a Global instance:
    393        let i = wasmEvalText(`(module (global (export "g") i32 (i32.const 42)))`);
    394 
    395        assertEq(typeof i.exports.g, "object");
    396        assertEq(i.exports.g instanceof Global, true);
    397 
    398        // An exported global can be imported into another instance even if
    399        // it is an object:
    400        let j = wasmEvalText(`(module
    401                               (global (import "" "g") i32)
    402                               (func (export "f") (result i32)
    403                                (global.get 0)))`,
    404                             { "": { "g": i.exports.g }});
    405 
    406        // And when it is then accessed it has the right value:
    407        assertEq(j.exports.f(), 42);
    408    }
    409 
    410    // Identity of Global objects (independent of mutablity).
    411    {
    412        // When a global is exported twice, the two objects are the same.
    413        let i = wasmEvalText(`(module
    414                               (global i32 (i32.const 0))
    415                               (export "a" (global 0))
    416                               (export "b" (global 0)))`);
    417        assertEq(i.exports.a, i.exports.b);
    418 
    419        // When a global is imported and then exported, the exported object is
    420        // the same as the imported object.
    421        let j = wasmEvalText(`(module
    422                               (import "" "a" (global i32))
    423                               (export "x" (global 0)))`,
    424                             { "": {a: i.exports.a}});
    425 
    426        assertEq(i.exports.a, j.exports.x);
    427 
    428        // When a global is imported twice (ie aliased) and then exported twice,
    429        // the exported objects are the same, and are also the same as the
    430        // imported object.
    431        let k = wasmEvalText(`(module
    432                               (import "" "a" (global i32))
    433                               (import "" "b" (global i32))
    434                               (export "x" (global 0))
    435                               (export "y" (global 1)))`,
    436                             { "": {a: i.exports.a,
    437                                    b: i.exports.a}});
    438 
    439        assertEq(i.exports.a, k.exports.x);
    440        assertEq(k.exports.x, k.exports.y);
    441    }
    442 
    443    // Mutability
    444    {
    445        let i = wasmEvalText(`(module
    446                               (global (export "g") (mut i32) (i32.const 37))
    447                               (func (export "getter") (result i32)
    448                                (global.get 0))
    449                               (func (export "setter") (param i32)
    450                                (global.set 0 (local.get 0))))`);
    451 
    452        let j = wasmEvalText(`(module
    453                               (import "" "g" (global (mut i32)))
    454                               (func (export "getter") (result i32)
    455                                (global.get 0))
    456                               (func (export "setter") (param i32)
    457                                (global.set 0 (local.get 0))))`,
    458                             {"": {g: i.exports.g}});
    459 
    460        // Initial values
    461        assertEq(i.exports.g.value, 37);
    462        assertEq(i.exports.getter(), 37);
    463        assertEq(j.exports.getter(), 37);
    464 
    465        // Set in i, observe everywhere
    466        i.exports.setter(42);
    467 
    468        assertEq(i.exports.g.value, 42);
    469        assertEq(i.exports.getter(), 42);
    470        assertEq(j.exports.getter(), 42);
    471 
    472        // Set in j, observe everywhere
    473        j.exports.setter(78);
    474 
    475        assertEq(i.exports.g.value, 78);
    476        assertEq(i.exports.getter(), 78);
    477        assertEq(j.exports.getter(), 78);
    478 
    479        // Set on global object, observe everywhere
    480        i.exports.g.value = 197;
    481 
    482        assertEq(i.exports.g.value, 197);
    483        assertEq(i.exports.getter(), 197);
    484        assertEq(j.exports.getter(), 197);
    485    }
    486 
    487    // Mutability of import declaration and imported value have to match
    488    {
    489        const mutErr = /imported global mutability mismatch/;
    490 
    491        let m1 = new Module(wasmTextToBinary(`(module
    492                                               (import "m" "g" (global i32)))`));
    493 
    494        // Mutable Global matched to immutable import
    495        let gm = new Global({value: "i32", mutable: true}, 42);
    496        assertErrorMessage(() => new Instance(m1, {m: {g: gm}}),
    497                           LinkError,
    498                           mutErr);
    499 
    500        let m2 = new Module(wasmTextToBinary(`(module
    501                                               (import "m" "g" (global (mut i32))))`));
    502 
    503        // Immutable Global matched to mutable import
    504        let gi = new Global({value: "i32", mutable: false}, 42);
    505        assertErrorMessage(() => new Instance(m2, {m: {g: gi}}),
    506                           LinkError,
    507                           mutErr);
    508 
    509        // Constant value is the same as immutable Global
    510        assertErrorMessage(() => new Instance(m2, {m: {g: 42}}),
    511                           LinkError,
    512                           mutErr);
    513    }
    514 
    515    // TEST THIS LAST
    516 
    517    // "value" is deletable
    518    assertEq(delete Global.prototype.value, true);
    519    assertEq("value" in Global.prototype, false);
    520 
    521    // ADD NO MORE TESTS HERE!
    522 }
    523 
    524 // Standard wat syntax: the parens around the initializer expression are
    525 // optional.
    526 {
    527    let i1 = wasmEvalText(
    528        `(module
    529           (global $g i32 i32.const 37)
    530           (func (export "f") (result i32) (global.get $g)))`);
    531    assertEq(i1.exports.f(), 37);
    532 
    533    let i2 = wasmEvalText(
    534        `(module
    535           (global $g (mut f64) f64.const 42.0)
    536           (func (export "f") (result f64) (global.get $g)))`);
    537    assertEq(i2.exports.f(), 42);
    538 
    539    let i3 = wasmEvalText(
    540        `(module
    541           (global $x (import "m" "x") i32)
    542           (global $g i32 global.get $x)
    543           (func (export "f") (result i32) (global.get $g)))`,
    544        {m:{x:86}});
    545    assertEq(i3.exports.f(), 86);
    546 }