Skip to content

Commit cc26df0

Browse files
committed
[MERGE #5886 @Cellule] WebAssembly.Memory & Table consctructor spec compliance
Merge pull request #5886 from Cellule:users/micfer/wasm-spec-constructor - Use TypeError instead of RangeError when initial/maximum have the wrong type (ie: not uint32). - Check for NaN, Infinity and -Infinity for NonWrappingUint32 conversion - Do not use HasProperty to see if the descriptor has a property, instead just use GetProperty and check against `undefined`. This is according to https://heycam.github.io/webidl/#dfn-present - Convert Table `descriptor.element` to string before doing the comparison <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/microsoft/chakracore/5886) <!-- Reviewable:end -->
2 parents e25c5ee + 4e69cf2 commit cc26df0

File tree

11 files changed

+144
-124
lines changed

11 files changed

+144
-124
lines changed

lib/Runtime/Library/WebAssembly.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,18 @@ Var WebAssembly::TryResolveResponse(RecyclableObject* function, Var thisArg, Var
288288
uint32
289289
WebAssembly::ToNonWrappingUint32(Var val, ScriptContext * ctx)
290290
{
291-
double i = JavascriptConversion::ToInteger(val, ctx);
292-
if (i < 0 || i > (double)UINT32_MAX)
291+
double i = JavascriptConversion::ToNumber(val, ctx);
292+
if (
293+
JavascriptNumber::IsNan(i) ||
294+
JavascriptNumber::IsPosInf(i) ||
295+
JavascriptNumber::IsNegInf(i) ||
296+
i < 0 ||
297+
i > (double)UINT32_MAX
298+
)
293299
{
294-
JavascriptError::ThrowRangeError(ctx, JSERR_ArgumentOutOfRange);
300+
JavascriptError::ThrowTypeError(ctx, JSERR_NeedNumber);
295301
}
296-
return (uint32)i;
302+
return (uint32)JavascriptConversion::ToInteger(i);
297303
}
298304

299305
void

lib/Runtime/Library/WebAssemblyMemory.cpp

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,33 @@ WebAssemblyMemory::WebAssemblyMemory(ArrayBufferBase* buffer, uint32 initial, ui
2222
}
2323

2424

25-
_Must_inspect_result_ bool WebAssemblyMemory::AreLimitsValid(uint32 initial, uint32 maximum)
25+
void WebAssemblyMemory::CheckLimits(ScriptContext * scriptContext, uint32 initial, uint32 maximum)
2626
{
27-
return initial <= maximum && initial <= Wasm::Limits::GetMaxMemoryInitialPages() && maximum <= Wasm::Limits::GetMaxMemoryMaximumPages();
27+
if (maximum < initial)
28+
{
29+
JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange);
30+
}
31+
if (initial > Wasm::Limits::GetMaxMemoryInitialPages())
32+
{
33+
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid, _u("descriptor.initial"));
34+
}
35+
if (maximum > Wasm::Limits::GetMaxMemoryMaximumPages())
36+
{
37+
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid, _u("descriptor.maximum"));
38+
}
2839
}
2940

3041

31-
_Must_inspect_result_ bool WebAssemblyMemory::AreLimitsValid(uint32 initial, uint32 maximum, uint32 bufferLength)
42+
void WebAssemblyMemory::CheckLimits(ScriptContext * scriptContext, uint32 initial, uint32 maximum, uint32 bufferLength)
3243
{
33-
if (!AreLimitsValid(initial, maximum))
34-
{
35-
return false;
36-
}
44+
CheckLimits(scriptContext, initial, maximum);
3745
// Do the mul after initial checks to avoid potential unneeded OOM exception
3846
const uint32 initBytes = UInt32Math::Mul<WebAssembly::PageSize>(initial);
3947
const uint32 maxBytes = UInt32Math::Mul<WebAssembly::PageSize>(maximum);
40-
return initBytes <= bufferLength && bufferLength <= maxBytes;
48+
if (initBytes > bufferLength || bufferLength > maxBytes)
49+
{
50+
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid);
51+
}
4152
}
4253

4354
Var
@@ -60,31 +71,38 @@ WebAssemblyMemory::NewInstance(RecyclableObject* function, CallInfo callInfo, ..
6071

6172
if (args.Info.Count < 2 || !JavascriptOperators::IsObject(args[1]))
6273
{
63-
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject, _u("memoryDescriptor"));
74+
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject, _u("descriptor"));
6475
}
6576
DynamicObject * memoryDescriptor = VarTo<DynamicObject>(args[1]);
6677

6778
Var initVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, PropertyIds::initial, scriptContext);
79+
if (Js::JavascriptOperators::IsUndefined(initVar))
80+
{
81+
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedNumber, _u("descriptor.initial"));
82+
}
6883
uint32 initial = WebAssembly::ToNonWrappingUint32(initVar, scriptContext);
6984

7085
uint32 maximum = Wasm::Limits::GetMaxMemoryMaximumPages();
7186
bool hasMaximum = false;
72-
if (JavascriptOperators::OP_HasProperty(memoryDescriptor, PropertyIds::maximum, scriptContext))
87+
Var maxVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, PropertyIds::maximum, scriptContext);
88+
if (!Js::JavascriptOperators::IsUndefined(maxVar))
7389
{
7490
hasMaximum = true;
75-
Var maxVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, PropertyIds::maximum, scriptContext);
7691
maximum = WebAssembly::ToNonWrappingUint32(maxVar, scriptContext);
7792
}
7893

7994
bool isShared = false;
80-
if (Wasm::Threads::IsEnabled() && JavascriptOperators::OP_HasProperty(memoryDescriptor, PropertyIds::shared, scriptContext))
95+
if (Wasm::Threads::IsEnabled())
8196
{
82-
if (!hasMaximum)
97+
Var sharedVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, PropertyIds::shared, scriptContext);
98+
if (!Js::JavascriptOperators::IsUndefined(sharedVar))
8399
{
84-
JavascriptError::ThrowTypeError(scriptContext, WASMERR_SharedNoMaximum);
100+
isShared = JavascriptConversion::ToBool(sharedVar, scriptContext);
101+
if (!hasMaximum)
102+
{
103+
JavascriptError::ThrowTypeError(scriptContext, WASMERR_SharedNoMaximum);
104+
}
85105
}
86-
Var sharedVar = JavascriptOperators::OP_GetProperty(memoryDescriptor, PropertyIds::shared, scriptContext);
87-
isShared = JavascriptConversion::ToBool(sharedVar, scriptContext);
88106
}
89107

90108
return CreateMemoryObject(initial, maximum, isShared, scriptContext);
@@ -271,10 +289,7 @@ WebAssemblyMemory::EntryGetterBuffer(RecyclableObject* function, CallInfo callIn
271289
WebAssemblyMemory *
272290
WebAssemblyMemory::CreateMemoryObject(uint32 initial, uint32 maximum, bool isShared, ScriptContext * scriptContext)
273291
{
274-
if (!AreLimitsValid(initial, maximum))
275-
{
276-
JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange);
277-
}
292+
CheckLimits(scriptContext, initial, maximum);
278293
uint32 byteLength = UInt32Math::Mul<WebAssembly::PageSize>(initial);
279294
ArrayBufferBase* buffer = nullptr;
280295
#ifdef ENABLE_WASM_THREADS
@@ -300,10 +315,7 @@ WebAssemblyMemory::CreateMemoryObject(uint32 initial, uint32 maximum, bool isSha
300315

301316
WebAssemblyMemory * WebAssemblyMemory::CreateForExistingBuffer(uint32 initial, uint32 maximum, uint32 currentByteLength, ScriptContext * scriptContext)
302317
{
303-
if (!AreLimitsValid(initial, maximum, currentByteLength))
304-
{
305-
JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange);
306-
}
318+
CheckLimits(scriptContext, initial, maximum, currentByteLength);
307319
ArrayBufferBase* buffer = scriptContext->GetLibrary()->CreateWebAssemblyArrayBuffer(currentByteLength);
308320
Assert(buffer);
309321
if (currentByteLength > 0 && buffer->GetByteLength() == 0)
@@ -317,10 +329,11 @@ WebAssemblyMemory * WebAssemblyMemory::CreateForExistingBuffer(uint32 initial, u
317329
#ifdef ENABLE_WASM_THREADS
318330
WebAssemblyMemory * WebAssemblyMemory::CreateFromSharedContents(uint32 initial, uint32 maximum, SharedContents* sharedContents, ScriptContext * scriptContext)
319331
{
320-
if (!sharedContents || !AreLimitsValid(initial, maximum, sharedContents->bufferLength))
332+
if (!sharedContents)
321333
{
322-
JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange);
334+
JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid);
323335
}
336+
CheckLimits(scriptContext, initial, maximum, sharedContents->bufferLength);
324337
ArrayBufferBase* buffer = scriptContext->GetLibrary()->CreateWebAssemblySharedArrayBuffer(sharedContents);
325338
return RecyclerNewFinalized(scriptContext->GetRecycler(), WebAssemblyMemory, buffer, initial, maximum, scriptContext->GetLibrary()->GetWebAssemblyMemoryType());
326339
}

lib/Runtime/Library/WebAssemblyMemory.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ namespace Js
5050
#endif
5151
private:
5252
WebAssemblyMemory(ArrayBufferBase* buffer, uint32 initial, uint32 maximum, DynamicType * type);
53-
static _Must_inspect_result_ bool AreLimitsValid(uint32 initial, uint32 maximum);
54-
static _Must_inspect_result_ bool AreLimitsValid(uint32 initial, uint32 maximum, uint32 bufferLength);
53+
static void CheckLimits(ScriptContext * scriptContext, uint32 initial, uint32 maximum);
54+
static void CheckLimits(ScriptContext * scriptContext, uint32 initial, uint32 maximum, uint32 bufferLength);
5555

5656
Field(ArrayBufferBase*) m_buffer;
5757

lib/Runtime/Library/WebAssemblyTable.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ WebAssemblyTable::NewInstance(RecyclableObject* function, CallInfo callInfo, ...
4646
DynamicObject * tableDescriptor = VarTo<DynamicObject>(args[1]);
4747

4848
Var elementVar = JavascriptOperators::OP_GetProperty(tableDescriptor, PropertyIds::element, scriptContext);
49-
if (!JavascriptOperators::StrictEqualString(elementVar, scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("anyfunc"))))
49+
auto elementStr = JavascriptConversion::ToString(elementVar, scriptContext);
50+
if (!JavascriptOperators::StrictEqualString(elementStr, scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("anyfunc"))))
5051
{
5152
JavascriptError::ThrowTypeError(scriptContext, WASMERR_ExpectedAnyFunc, _u("tableDescriptor.element"));
5253
}
@@ -55,9 +56,9 @@ WebAssemblyTable::NewInstance(RecyclableObject* function, CallInfo callInfo, ...
5556
uint32 initial = WebAssembly::ToNonWrappingUint32(initVar, scriptContext);
5657

5758
uint32 maximum = Wasm::Limits::GetMaxTableSize();
58-
if (JavascriptOperators::OP_HasProperty(tableDescriptor, PropertyIds::maximum, scriptContext))
59+
Var maxVar = JavascriptOperators::OP_GetProperty(tableDescriptor, PropertyIds::maximum, scriptContext);
60+
if (!JavascriptOperators::IsUndefined(maxVar))
5961
{
60-
Var maxVar = JavascriptOperators::OP_GetProperty(tableDescriptor, PropertyIds::maximum, scriptContext);
6162
maximum = WebAssembly::ToNonWrappingUint32(maxVar, scriptContext);
6263
}
6364
return Create(initial, maximum, scriptContext);

test/WasmSpec/baselines/testsuite/js-api/memory/constructor.any.baseline

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
Harness Status: OK
2-
Found 24 tests: Pass = 9 Fail = 15
2+
Found 24 tests: Pass = 24
33
Pass name
44
Pass length
55
Pass No arguments
66
Pass Calling
7-
Fail Invalid descriptor argument assert_throws: new Memory(object "[object Object]") function "() => new WebAssembly.Memory(invalidArgument)" did not throw
8-
Fail Undefined initial value in descriptor assert_throws: function "() => new WebAssembly.Memory({ "initial": undefined })" did not throw
9-
Fail Out-of-range initial value in descriptor: NaN assert_throws: function "() => new WebAssembly.Memory({ "initial": value })" did not throw
10-
Fail Out-of-range maximum value in descriptor: NaN assert_throws: function "() => new WebAssembly.Memory({ "initial": 0, "maximum": value })" did not throw
11-
Fail Out-of-range initial value in descriptor: Infinity assert_throws: function "() => new WebAssembly.Memory({ "initial": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
12-
Fail Out-of-range maximum value in descriptor: Infinity assert_throws: function "() => new WebAssembly.Memory({ "initial": 0, "maximum": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
13-
Fail Out-of-range initial value in descriptor: -Infinity assert_throws: function "() => new WebAssembly.Memory({ "initial": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
14-
Fail Out-of-range maximum value in descriptor: -Infinity assert_throws: function "() => new WebAssembly.Memory({ "initial": 0, "maximum": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
15-
Fail Out-of-range initial value in descriptor: -1 assert_throws: function "() => new WebAssembly.Memory({ "initial": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
16-
Fail Out-of-range maximum value in descriptor: -1 assert_throws: function "() => new WebAssembly.Memory({ "initial": 0, "maximum": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
17-
Fail Out-of-range initial value in descriptor: 4294967296 assert_throws: function "() => new WebAssembly.Memory({ "initial": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
18-
Fail Out-of-range maximum value in descriptor: 4294967296 assert_throws: function "() => new WebAssembly.Memory({ "initial": 0, "maximum": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
19-
Fail Out-of-range initial value in descriptor: 68719476736 assert_throws: function "() => new WebAssembly.Memory({ "initial": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
20-
Fail Out-of-range maximum value in descriptor: 68719476736 assert_throws: function "() => new WebAssembly.Memory({ "initial": 0, "maximum": value })" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
7+
Pass Invalid descriptor argument
8+
Pass Undefined initial value in descriptor
9+
Pass Out-of-range initial value in descriptor: NaN
10+
Pass Out-of-range maximum value in descriptor: NaN
11+
Pass Out-of-range initial value in descriptor: Infinity
12+
Pass Out-of-range maximum value in descriptor: Infinity
13+
Pass Out-of-range initial value in descriptor: -Infinity
14+
Pass Out-of-range maximum value in descriptor: -Infinity
15+
Pass Out-of-range initial value in descriptor: -1
16+
Pass Out-of-range maximum value in descriptor: -1
17+
Pass Out-of-range initial value in descriptor: 4294967296
18+
Pass Out-of-range maximum value in descriptor: 4294967296
19+
Pass Out-of-range initial value in descriptor: 68719476736
20+
Pass Out-of-range maximum value in descriptor: 68719476736
2121
Pass Initial value exceeds maximum
22-
Fail Proxy descriptor assert_unreached: Should not call [[HasProperty]] with maximum Reached unreachable code
22+
Pass Proxy descriptor
2323
Pass Order of evaluation for descriptor
2424
Pass Zero initial
2525
Pass Non-zero initial
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
Harness Status: OK
2-
Found 18 tests: Fail = 10 Pass = 8
3-
Fail Missing arguments assert_throws: function "() => memory.grow()" did not throw
2+
Found 18 tests: Pass = 18
3+
Pass Missing arguments
44
Pass Branding
55
Pass Zero initial
66
Pass Zero initial with valueOf
77
Pass Non-zero initial
88
Pass Zero initial with respected maximum
99
Pass Zero initial with respected maximum grown twice
1010
Pass Zero initial growing too much
11-
Fail Out-of-range argument: undefined assert_throws: function "() => memory.grow(value)" did not throw
12-
Fail Out-of-range argument: NaN assert_throws: function "() => memory.grow(value)" did not throw
13-
Fail Out-of-range argument: Infinity assert_throws: function "() => memory.grow(value)" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
14-
Fail Out-of-range argument: -Infinity assert_throws: function "() => memory.grow(value)" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
15-
Fail Out-of-range argument: -1 assert_throws: function "() => memory.grow(value)" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
16-
Fail Out-of-range argument: 4294967296 assert_throws: function "() => memory.grow(value)" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
17-
Fail Out-of-range argument: 68719476736 assert_throws: function "() => memory.grow(value)" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
18-
Fail Out-of-range argument: "0x100000000" assert_throws: function "() => memory.grow(value)" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
19-
Fail Out-of-range argument: object "[object Object]" assert_throws: function "() => memory.grow(value)" threw object "RangeError: argument out of range" ("RangeError") expected object "TypeError" ("TypeError")
11+
Pass Out-of-range argument: undefined
12+
Pass Out-of-range argument: NaN
13+
Pass Out-of-range argument: Infinity
14+
Pass Out-of-range argument: -Infinity
15+
Pass Out-of-range argument: -1
16+
Pass Out-of-range argument: 4294967296
17+
Pass Out-of-range argument: 68719476736
18+
Pass Out-of-range argument: "0x100000000"
19+
Pass Out-of-range argument: object "[object Object]"
2020
Pass Stray argument

0 commit comments

Comments
 (0)