2017-09-14 17:34:15 +00:00
|
|
|
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2019-02-14 15:19:33 +00:00
|
|
|
// Flags: --wasm-test-streaming --expose-wasm
|
2017-09-14 17:34:15 +00:00
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2021-06-01 12:46:36 +00:00
|
|
|
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
2017-09-14 17:34:15 +00:00
|
|
|
|
2019-10-30 15:31:42 +00:00
|
|
|
function testErrorPositionAsyncOnly(bytes, pos, message) {
|
|
|
|
let buffer = bytes.trunc_buffer();
|
|
|
|
// Only test the streaming decoder since this kind of error is out of sync
|
|
|
|
// with the non-streaming decoder, hence errors cannot be compared.
|
2019-02-06 13:09:43 +00:00
|
|
|
assertThrowsAsync(
|
2019-10-30 15:31:42 +00:00
|
|
|
WebAssembly.compile(buffer), WebAssembly.CompileError,
|
|
|
|
new RegExp(message + '.*@\\+' + pos));
|
|
|
|
}
|
|
|
|
|
|
|
|
function testErrorPosition(bytes, pos, message) {
|
|
|
|
let buffer = bytes.trunc_buffer();
|
|
|
|
// First check the non-streaming decoder as a reference.
|
|
|
|
assertThrows(
|
|
|
|
() => new WebAssembly.Module(buffer), WebAssembly.CompileError,
|
|
|
|
new RegExp(message + '.*@\\+' + pos));
|
|
|
|
// Next test the actual streaming decoder.
|
|
|
|
assertThrowsAsync(
|
|
|
|
WebAssembly.compile(buffer), WebAssembly.CompileError,
|
|
|
|
new RegExp(message + '.*@\\+' + pos));
|
2017-09-14 17:34:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(function testInvalidMagic() {
|
|
|
|
let bytes = new Binary;
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
|
|
|
kWasmH0, kWasmH1 + 1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3
|
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Error at pos==0 because that's where the magic word is.
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPosition(bytes, 0, 'expected magic word');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testInvalidVersion() {
|
|
|
|
let bytes = new Binary;
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
|
|
|
kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1 + 1, kWasmV2, kWasmV3
|
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Error at pos==4 because that's where the version word is.
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPosition(bytes, 4, 'expected version');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testSectionLengthInvalidVarint() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
|
|
|
bytes.emit_u8(kTypeSectionCode);
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([0x80, 0x80, 0x80, 0x80, 0x80, 0x00]);
|
2017-09-14 17:34:15 +00:00
|
|
|
let pos = bytes.length - 1 - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPosition(bytes, pos, 'expected section length');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testSectionLengthTooBig() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
|
|
|
bytes.emit_u8(kTypeSectionCode);
|
|
|
|
bytes.emit_u32v(0xffffff23);
|
|
|
|
let pos = bytes.length - 1;
|
2021-06-07 08:06:33 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'section length');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testFunctionsCountInvalidVarint() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
1, // section length
|
|
|
|
0 // number of types
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
1, // section length
|
|
|
|
0 // number of functions
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (arbitrary value > 6)
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Functions count
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([0x80, 0x80, 0x80, 0x80, 0x80, 0x00]);
|
2017-09-14 17:34:15 +00:00
|
|
|
|
|
|
|
let pos = bytes.length - 1 - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'expected functions count');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testFunctionsCountTooBig() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
1, // section length
|
|
|
|
0 // number of types
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
1, // section length
|
|
|
|
0 // number of functions
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (arbitrary value > 6)
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Functions count
|
|
|
|
bytes.emit_u32v(0xffffff23);
|
|
|
|
|
|
|
|
let pos = bytes.length - 1;
|
2021-06-07 08:06:33 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'functions count');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testFunctionsCountDoesNotMatch() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
1, // section length
|
|
|
|
0 // number of types
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
1, // section length
|
|
|
|
0 // number of functions
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (arbitrary value > 6)
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Functions count (different than the count in the functions section.
|
|
|
|
bytes.emit_u32v(5);
|
|
|
|
|
|
|
|
let pos = bytes.length - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'function body count 5 mismatch');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testBodySizeInvalidVarint() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (arbitrary value > 6)
|
|
|
|
1 // functions count
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Invalid function body size.
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([0x80, 0x80, 0x80, 0x80, 0x80, 0x00]);
|
2017-09-14 17:34:15 +00:00
|
|
|
|
|
|
|
let pos = bytes.length - 1 - 1;
|
2021-06-07 08:06:33 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'function body size');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testBodySizeTooBig() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (arbitrary value > 6)
|
|
|
|
1 // functions count
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Invalid function body size.
|
|
|
|
bytes.emit_u32v(0xffffff23);
|
|
|
|
|
|
|
|
let pos = bytes.length - 1;
|
2021-06-07 08:06:33 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'function body size');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testBodySizeDoesNotFit() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (arbitrary value > 6)
|
|
|
|
1 // functions count
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Invalid function body size (does not fit into the code section).
|
|
|
|
bytes.emit_u32v(20);
|
|
|
|
|
|
|
|
let pos = bytes.length - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'not enough code section bytes');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testBodySizeIsZero() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (arbitrary value > 6)
|
|
|
|
1 // functions count
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
// Invalid function body size (body size of 0 is invalid).
|
|
|
|
bytes.emit_u32v(0);
|
|
|
|
|
|
|
|
let pos = bytes.length - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'invalid function length');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testStaleCodeSectionBytes() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
|
|
|
20, // section length (too big)
|
|
|
|
1, // functions count
|
|
|
|
2, // body size
|
|
|
|
0, // locals count
|
|
|
|
kExprEnd // body
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
|
|
|
|
let pos = bytes.length - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'not all code section bytes were used');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testInvalidCode() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
2019-10-30 14:51:58 +00:00
|
|
|
6, // section length
|
2017-09-14 17:34:15 +00:00
|
|
|
1, // functions count
|
|
|
|
4, // body size
|
|
|
|
0, // locals count
|
2019-10-08 12:38:48 +00:00
|
|
|
kExprLocalGet, 0, // Access a non-existing local
|
2017-09-14 17:34:15 +00:00
|
|
|
kExprEnd // --
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
|
2019-10-08 12:38:48 +00:00
|
|
|
// Find error at the index of kExprLocalGet.
|
2017-09-14 17:34:15 +00:00
|
|
|
let pos = bytes.length - 1 - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPosition(bytes, pos, 'invalid local index');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
2019-10-30 14:51:58 +00:00
|
|
|
(function testCodeSectionRepeats() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
|
|
|
bytes.emit_bytes([
|
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
|
|
|
kCodeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // functions count
|
|
|
|
2, // body size
|
|
|
|
0, // locals count
|
|
|
|
kExprEnd // body
|
|
|
|
]);
|
|
|
|
let pos = bytes.length;
|
|
|
|
bytes.emit_bytes([
|
|
|
|
kCodeSectionCode, // section id (repeating)
|
|
|
|
4, // section length
|
|
|
|
1, // functions count
|
|
|
|
2, // body size
|
|
|
|
0, // locals count
|
|
|
|
kExprEnd // body
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Find error at the second kCodeSectionCode.
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'code section can only appear once');
|
2019-10-30 14:51:58 +00:00
|
|
|
})();
|
|
|
|
|
2017-09-14 17:34:15 +00:00
|
|
|
(function testCodeSectionSizeZero() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
4, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
0, // number of parameter
|
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kFunctionSectionCode, // section id
|
|
|
|
2, // section length
|
|
|
|
1, // number of functions
|
|
|
|
0 // signature index
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kCodeSectionCode, // section id
|
2019-10-30 14:51:58 +00:00
|
|
|
0, // section length (empty)
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
|
2019-10-30 14:51:58 +00:00
|
|
|
// Find error at the code section length.
|
2017-09-14 17:34:15 +00:00
|
|
|
let pos = bytes.length - 1;
|
2019-10-30 15:31:42 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'code section cannot have size 0');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|
|
|
|
|
|
|
|
(function testInvalidSection() {
|
|
|
|
let bytes = new Binary;
|
|
|
|
bytes.emit_header();
|
2019-03-07 14:06:13 +00:00
|
|
|
bytes.emit_bytes([
|
2017-09-14 17:34:15 +00:00
|
|
|
kTypeSectionCode, // section id
|
|
|
|
5, // section length
|
|
|
|
1, // number of types
|
|
|
|
kWasmFunctionTypeForm, // type
|
|
|
|
1, // number of parameter
|
2021-03-22 06:56:01 +00:00
|
|
|
kWasmVoid, // invalid type
|
2017-09-14 17:34:15 +00:00
|
|
|
0 // number of returns
|
2019-03-07 14:06:13 +00:00
|
|
|
]);
|
2017-09-14 17:34:15 +00:00
|
|
|
|
|
|
|
let pos = bytes.length - 1 - 1;
|
2020-05-06 13:39:45 +00:00
|
|
|
testErrorPositionAsyncOnly(bytes, pos, 'invalid value type');
|
2017-09-14 17:34:15 +00:00
|
|
|
})();
|