diff --git a/test/built-ins/JSON/isRawJSON/builtin.js b/test/built-ins/JSON/isRawJSON/builtin.js index 391908309ca..00f1600b83f 100644 --- a/test/built-ins/JSON/isRawJSON/builtin.js +++ b/test/built-ins/JSON/isRawJSON/builtin.js @@ -4,41 +4,42 @@ /*--- esid: sec-json.israwjson description: > - JSON.isRawJSON meets the requirements for built-in objects + JSON.isRawJSON meets the requirements for standard built-in objects info: | - JSON.isRawJSON ( O ) - - 18 ECMAScript Standard Built-in Objects + ECMAScript Standard Built-in Objects ... - Unless specified otherwise, a built-in object that is callable as a function - is a built-in function object with the characteristics described in 10.3. Unless specified otherwise, the [[Extensible]] internal slot of a built-in - object initially has the value true. Every built-in function object has a - [[Realm]] internal slot whose value is the Realm Record of the realm for - which the object was initially created. + object initially has the value true. ... Unless otherwise specified every built-in function and every built-in constructor has the Function prototype object, which is the initial value of the expression Function.prototype (20.2.3), as the value of its [[Prototype]] internal slot. ... - Built-in function objects that are not identified as constructors do not - implement the [[Construct]] internal method unless otherwise specified in the - description of a particular function. + Built-in function objects that are not constructors do not have a "prototype" + property unless otherwise specified in the description of a particular + function. + ... + Each built-in function defined in this specification is created by calling the + CreateBuiltinFunction abstract operation (10.3.4). The values of the length + and name parameters are the initial values of the "length" and "name" + properties as discussed below. The values of the prefix parameter are + similarly discussed below. +includes: [propertyHelper.js] features: [json-parse-with-source] ---*/ -assert(Object.isExtensible(JSON.isRawJSON), "JSON.isRawJSON is extensible"); assert.sameValue( - typeof JSON.isRawJSON, - 'function', - 'The value of `typeof JSON.isRawJSON` is "function"' + Object.isExtensible(JSON.isRawJSON), + Object.isExtensible(Object.prototype), + "JSON.isRawJSON extensibility matches Object.prototype", ); + assert.sameValue( Object.getPrototypeOf(JSON.isRawJSON), Function.prototype, - 'Object.getPrototypeOf(JSON.isRawJSON) must return the value of "Function.prototype"' + "JSON.isRawJSON [[Prototype]] is Function.prototype" ); assert.sameValue( @@ -46,3 +47,5 @@ assert.sameValue( undefined, "JSON.isRawJSON has no own prototype property" ); + +verifyPrimordialCallableProperty(JSON, "isRawJSON", "isRawJSON", 1); diff --git a/test/built-ins/JSON/isRawJSON/length.js b/test/built-ins/JSON/isRawJSON/length.js deleted file mode 100644 index 170653a910c..00000000000 --- a/test/built-ins/JSON/isRawJSON/length.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2024 Igalia S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-built-in-function-objects -description: > - JSON.isRawJSON.length value and descriptor. -info: | - Every built-in function object, including constructors, has a *"length"* - property whose value is a non-negative integral Number. Unless otherwise - specified, this value is the number of required parameters shown in the - subclause heading for the function description. Optional parameters and rest - parameters are not included in the parameter count. - - Unless otherwise specified, the *"length"* property of a built-in function - object has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, - [[Configurable]]: *true* }. -includes: [propertyHelper.js] -features: [json-parse-with-source] ----*/ - -verifyProperty(JSON.isRawJSON, 'length', { - value: 1, - writable: false, - enumerable: false, - configurable: true -}); diff --git a/test/built-ins/JSON/isRawJSON/name.js b/test/built-ins/JSON/isRawJSON/name.js deleted file mode 100644 index 08168bf94e4..00000000000 --- a/test/built-ins/JSON/isRawJSON/name.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2024 Igalia S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-built-in-function-objects -description: > - JSON.isRawJSON.name value and descriptor. -info: | - Every built-in function object, including constructors, has a *"name"* - property whose value is a String. Unless otherwise specified, this value is - the name that is given to the function in this specification. Functions that - are identified as anonymous functions use the empty String as the value of - the *"name"* property. For functions that are specified as properties of - objects, the name value is the property name string used to access the - function. Functions that are specified as get or set accessor functions of - built-in properties have *"get"* or *"set"* (respectively) passed to the - prefix parameter when calling CreateBuiltinFunction. - - The value of the *"name"* property is explicitly specified for each built-in - function whose property key is a Symbol value. If such an explicitly - specified value starts with the prefix *"get "* or *"set "* and the function - for which it is specified is a get or set accessor function of a built-in - property, the value without the prefix is passed to the name parameter, and - the value *"get"* or *"set"* (respectively) is passed to the prefix - parameter when calling CreateBuiltinFunction. - - Unless otherwise specified, the *"name"* property of a built-in function - object has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, - [[Configurable]]: *true* }. - -includes: [propertyHelper.js] -features: [json-parse-with-source] ----*/ - -verifyProperty(JSON.isRawJSON, 'name', { - value: "isRawJSON", - enumerable: false, - writable: false, - configurable: true -}); diff --git a/test/built-ins/JSON/isRawJSON/prop-desc.js b/test/built-ins/JSON/isRawJSON/prop-desc.js deleted file mode 100644 index 1b041ff7085..00000000000 --- a/test/built-ins/JSON/isRawJSON/prop-desc.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2024 Igalia S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.israwjson -description: > - Property type and descriptor. -info: | - JSON.isRawJSON ( O ) - - 18 ECMAScript Standard Built-in Objects - ... - Every other data property described in clauses 19 through 28 and in Annex B.2 - has the attributes { [[Writable]]: true, [[Enumerable]]: false, - [[Configurable]]: true } unless otherwise specified. - -includes: [propertyHelper.js] -features: [json-parse-with-source] ----*/ - -verifyProperty(JSON, 'isRawJSON', { - enumerable: false, - writable: true, - configurable: true -}); diff --git a/test/built-ins/JSON/parse/reviver-call-args-after-forward-modification.js b/test/built-ins/JSON/parse/reviver-call-args-after-forward-modification.js deleted file mode 100644 index b7e2892c375..00000000000 --- a/test/built-ins/JSON/parse/reviver-call-args-after-forward-modification.js +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.parse -description: > - JSON.parse reviver is called with the correct arguments when the object is - modified -includes: [compareArray.js] -features: [json-parse-with-source] ----*/ - -// Test Array append -{ - let log = []; - const o = JSON.parse('[1,[]]', function reviver(k, v, { source }) { - log.push(`key: |${k}| value: ${JSON.stringify(v)} source: |${source}|`); - if (v === 1) { - this[1].push('barf'); - } - return this[k]; - }); - assert.compareArray(log, [ - 'key: |0| value: 1 source: |1|', - 'key: |0| value: "barf" source: |undefined|', - 'key: |1| value: ["barf"] source: |undefined|', - 'key: || value: [1,["barf"]] source: |undefined|', - ]); -} - -// Test Object add property -{ - let log = []; - const o = JSON.parse('{"p":1,"q":{}}', function (k, v, { source }) { - log.push(`key: |${k}| value: ${JSON.stringify(v)} source: |${source}|`); - if (v === 1) { - this.q.added = 'barf'; - } - return this[k]; - }); - assert.compareArray(log, [ - 'key: |p| value: 1 source: |1|', - 'key: |added| value: "barf" source: |undefined|', - 'key: |q| value: {"added":"barf"} source: |undefined|', - 'key: || value: {"p":1,"q":{"added":"barf"}} source: |undefined|', - ]); -} diff --git a/test/built-ins/JSON/parse/reviver-context-source-after-forward-modification.js b/test/built-ins/JSON/parse/reviver-context-source-after-forward-modification.js new file mode 100644 index 00000000000..d6d11c36399 --- /dev/null +++ b/test/built-ins/JSON/parse/reviver-context-source-after-forward-modification.js @@ -0,0 +1,231 @@ +// Copyright (C) 2025 Richard Gibson. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-json.parse +description: > + JSON.parse reviver is called without "source" for modified properties unless + they have been restored to original values. +info: | + JSON.parse ( _text_ [ , _reviver_ ] ) + 1. Let _jsonString_ be ? ToString(_text_). + 2. Let _parseResult_ be ? ParseJSON(_jsonString_). + 3. Let _unfiltered_ be _parseResult_.[[Value]]. + 4. If IsCallable(_reviver_) is *true*, then + a. Let _root_ be OrdinaryObjectCreate(%Object.prototype%). + b. Let _rootName_ be the empty String. + c. Perform ! CreateDataPropertyOrThrow(_root_, _rootName_, _unfiltered_). + d. Let _snapshot_ be CreateJSONParseRecord(_parseResult_.[[ParseNode]], _rootName_, _unfiltered_). + e. Return ? InternalizeJSONProperty(_root_, _rootName_, _reviver_, _snapshot_). + + InternalizeJSONProperty ( _holder_, _name_, _reviver_, _parseRecord_ ) + 1. Let _val_ be ? Get(_holder_, _name_). + 2. Let _context_ be OrdinaryObjectCreate(%Object.prototype%). + 3. If _parseRecord_ is a JSON Parse Record and SameValue(_parseRecord_.[[Value]], _val_) is *true*, then + a. If _val_ is not an Object, then + i. Let _parseNode_ be _parseRecord_.[[ParseNode]]. + ii. Assert: _parseNode_ is not an |ArrayLiteral| Parse Node and not an |ObjectLiteral| Parse Node. + iii. Let _sourceText_ be the source text matched by _parseNode_. + iv. Perform ! CreateDataPropertyOrThrow(_context_, *"source"*, CodePointsToString(_sourceText_)). + b. Let _elementRecords_ be _parseRecord_.[[Elements]]. + c. Let _entryRecords_ be _parseRecord_.[[Entries]]. + 4. Else, + a. Let _elementRecords_ be a new empty List. + b. Let _entryRecords_ be a new empty List. + 5. If _val_ is an Object, then + a. Let _isArray_ be ? IsArray(_val_). + b. If _isArray_ is *true*, then + ... + iv. Repeat, while _I_ < _len_, + 1. Let _prop_ be ! ToString(𝔽(_I_)). + 2. If _I_ < _elementRecordsLen_, let _elementRecord_ be _elementRecords_[_I_]. Otherwise, let _elementRecord_ be ~empty~. + 3. Let _newElement_ be ? InternalizeJSONProperty(_val_, _prop_, _reviver_, _elementRecord_). + ... + c. Else, + i. Let _keys_ be ? EnumerableOwnProperties(_val_, ~key~). + ii. For each String _P_ of _keys_, do + 1. Let _entryRecord_ be the element of _entryRecords_ whose [[Key]] field is _P_. If there is no such element, set _entryRecord_ to ~empty~. + 2. Let _newElement_ be ? InternalizeJSONProperty(_val_, _P_, _reviver_, _entryRecord_). + ... + 6. Return ? Call(_reviver_, _holder_, « _name_, _val_, _context_ »). + +includes: [compareArray.js] +features: [json-parse-with-source] +---*/ + +var log = []; +function overwritingReviver(key, value, context) { + log.push('[' + key + ']: ' + JSON.stringify(value) + ' from |' + context.source + '|'); + if (value === 'start') { + // Add new properties. + this[1].added = true; + this[2].push('added'); + } else if (value === 'obj') { + // Replace properties of this object. + this.toObj = {}; + this.toPrim = 2; + this.toClone = this.toClone.slice(); + this.toOtherPrim = 4; + } else if (value === 'arr') { + // Replace elements of this array. + this[1] = {}; + this[2] = 2; + this[3] = this[3].slice(); + this[4] = 4; + } + return value; +} +var overwriteResult = JSON.parse( + '[ ' + + '"start", ' + + '{ "0": "obj", "toObj": 1, "toPrim": {}, "toClone": ["clone me"], "toOtherPrim": "four" }, ' + + '["arr", 1, {}, ["clone me"], "four"] ' + + ']', + overwritingReviver +); +var expectObjJson = '{"0":"obj","toObj":{},"toPrim":2,"toClone":["clone me"],"toOtherPrim":4,"added":true}'; +var expectArrJson = '["arr",{},2,["clone me"],4,"added"]'; +assert.compareArray(log, [ + '[0]: "start" from |"start"|', + + '[0]: "obj" from |"obj"|', + '[toObj]: {} from |undefined|', + '[toPrim]: 2 from |undefined|', + '[0]: "clone me" from |undefined|', + '[toClone]: ["clone me"] from |undefined|', + '[toOtherPrim]: 4 from |undefined|', + '[added]: true from |undefined|', + '[1]: ' + expectObjJson + ' from |undefined|', + + '[0]: "arr" from |"arr"|', + '[1]: {} from |undefined|', + '[2]: 2 from |undefined|', + '[0]: "clone me" from |undefined|', + '[3]: ["clone me"] from |undefined|', + '[4]: 4 from |undefined|', + '[5]: "added" from |undefined|', + '[2]: ' + expectArrJson + ' from |undefined|', + + '[]: ["start",' + expectObjJson + ',' + expectArrJson + '] from |undefined|' +], 'overwrite result'); +assert.sameValue( + JSON.stringify(overwriteResult), + '["start",' + expectObjJson + ',' + expectArrJson + ']', + 'overwrite result' +); + +log = []; +var cache = {}; +function replacingReviver(key, value, context) { + log.push('[' + key + ']: ' + JSON.stringify(value) + ' from |' + context.source + '|'); + if (value === 'start') { + // Remove properties from the upcoming object, caching and truncating its array. + cache.objNonPrim = this[2].nonPrim; + this[2].nonPrim.length = 0; + delete this[2].prim; + delete this[2].nonPrim; + + // Remove elements from the upcoming array, caching and truncating its array. + cache.arrNonPrim = this[3][2]; + this[3][2].length = 0; + this[3].length = 1; + + assert.sameValue(JSON.stringify(this), '["start","continue",{"0":"obj"},["arr"]]', + 'reviver forward removal'); + } else if (value === 'continue') { + // Restore properties of the upcoming object, but in reverse order. + // Then freeze the object to mutate attributes of those properties. + this[2].nonPrim = cache.objNonPrim; + this[2].prim = 1; + Object.freeze(this[2]); + + // Restore elements of the upcoming array, then freeze it. + this[3].push(1, cache.arrNonPrim); + Object.freeze(this[3]); + + assert.sameValue( + JSON.stringify(this), + '["start","continue",{"0":"obj","nonPrim":[],"prim":1},["arr",1,[]]]', + 'reviver forward restoration'); + } else if (value === 'obj') { + // Restore the element of the upcoming array and freeze it. + this.nonPrim.push('string'); + Object.freeze(this.nonPrim); + } else if (value === 'arr') { + // Restore the element of the upcoming array and freeze it. + this[2].push('string'); + Object.freeze(this[2]); + } + return value; +} +var replacedResult = JSON.parse( + '[ ' + + '"start", ' + + '"continue", ' + + '{ "0": "obj", "prim": 1e0, "nonPrim": ["string"] }, ' + + '["arr", 1e0, ["string"]] ' + + ']', + replacingReviver +); +expectObjJson = '{"0":"obj","nonPrim":["string"],"prim":1}'; +expectArrJson = '["arr",1,["string"]]'; +assert.compareArray(log, [ + '[0]: "start" from |"start"|', + '[1]: "continue" from |"continue"|', + + '[0]: "obj" from |"obj"|', + '[0]: "string" from |"string"|', + '[nonPrim]: ["string"] from |undefined|', + '[prim]: 1 from |1e0|', + '[2]: ' + expectObjJson + ' from |undefined|', + + '[0]: "arr" from |"arr"|', + '[1]: 1 from |1e0|', + '[0]: "string" from |"string"|', + '[2]: ["string"] from |undefined|', + '[3]: ' + expectArrJson + ' from |undefined|', + + '[]: ["start","continue",' + expectObjJson + ',' + expectArrJson + '] from |undefined|' +], 'replaced result'); +assert.sameValue( + JSON.stringify(replacedResult), + '["start","continue",' + expectObjJson + ',' + expectArrJson + ']', + 'replaced result' +); + +log = []; +function splicingReviver(key, value, context) { + log.push('[' + key + ']: ' + JSON.stringify(value) + ' from |' + context.source + '|'); + if (value === 'start') { + // Remove the "x" at index 1, throwing off the index for values 1+. + this.splice(1, 1); + } else if (value === 1) { + // Insert a following element, restoring the index for values 2+. + this.splice(+key + 1, 0, 1.5); + } else if (value === 2) { + // Insert a following element, throwing off the index for values 3+. + this.splice(+key + 1, 0, 'pre 3'); + } else if (value === 'pre 3') { + // Remove the following element (the original 3e0), restoring the index for values 4+. + this.splice(+key, 1); + } else if (value === 4) { + // Add a new final element. + this.push("end"); + } + return value; +} +var replacedResult = JSON.parse('["start", "x", 1e0, 2e0, 3e0, 4e0]', splicingReviver); +assert.compareArray(log, [ + '[0]: "start" from |"start"|', + '[1]: 1 from |undefined|', + '[2]: 1.5 from |undefined|', + '[3]: 2 from |2e0|', + '[4]: "pre 3" from |undefined|', + '[5]: 4 from |4e0|', + '[]: ["start",1,1.5,2,"pre 3",4,"end"] from |undefined|' +], 'spliced result'); +assert.sameValue( + JSON.stringify(replacedResult), + '["start",1,1.5,2,"pre 3",4,"end"]', + 'spliced result' +); diff --git a/test/built-ins/JSON/parse/reviver-context-source-array-literal.js b/test/built-ins/JSON/parse/reviver-context-source-array-literal.js deleted file mode 100644 index 45b2aa34314..00000000000 --- a/test/built-ins/JSON/parse/reviver-context-source-array-literal.js +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.parse -description: > - Context argument and its source property behave as expected when parsing an - ArrayLiteral JSON string -includes: [compareArray.js, propertyHelper.js] -features: [json-parse-with-source] ----*/ - -function assertOnlyOwnProperties(object, props, message) { - assert.compareArray(Object.getOwnPropertyNames(object), props, `${message}: object should have no other properties than expected`); - assert.compareArray(Object.getOwnPropertySymbols(object), [], `${message}: object should have no own symbol properties`); -} - -function reviverWithExpectedSources(expectedSources) { - let i = 0; - return function reviver(key, value, context) { - assert.sameValue(typeof context, "object", "context should be an object"); - assert.sameValue(Object.getPrototypeOf(context), Object.prototype, "context should be a plain object"); - if (expectedSources[i] !== undefined) { - assertOnlyOwnProperties(context, ["source"], - "the JSON value is a primitve value, its context should only have a source property"); - verifyProperty(context, "source", { - value: expectedSources[i++], - configurable: true, - enumerable: true, - writable: true, - }, { restore: true }); - } else { - assertOnlyOwnProperties(context, [], - "the JSON value is an Array or Object, its context should have no property"); - i++; - } - return value; - }; -} - -assert.compareArray(JSON.parse('[1.0]', reviverWithExpectedSources(['1.0'])), [1]); -assert.compareArray( - JSON.parse('[1.1]', reviverWithExpectedSources(['1.1'])), - [1.1] -); -assert.compareArray(JSON.parse('[]', reviverWithExpectedSources([])), []); - -const longArray = JSON.parse( - '[1, "2", true, null, {"x": 1, "y": 1}]', - reviverWithExpectedSources(['1', '"2"', 'true', 'null', '1', '1']) -); -assert.sameValue(longArray[0], 1, "array, element 0"); -assert.sameValue(longArray[1], "2", "array, element 1"); -assert.sameValue(longArray[2], true, "array, element 2"); -assert.sameValue(longArray[3], null, "array, element 3"); -assertOnlyOwnProperties(longArray[4], ["x", "y"], "array, element 5"); -assert.sameValue(longArray[4].x, 1, "array, element 5, prop x"); -assert.sameValue(longArray[4].y, 1, "array, element 5, prop y"); diff --git a/test/built-ins/JSON/parse/reviver-context-source-object-literal.js b/test/built-ins/JSON/parse/reviver-context-source-object-literal.js deleted file mode 100644 index 5fcf01df30d..00000000000 --- a/test/built-ins/JSON/parse/reviver-context-source-object-literal.js +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.parse -description: > - Context argument and its source property behave as expected when parsing an - ObjectLiteral JSON string -includes: [compareArray.js, propertyHelper.js] -features: [json-parse-with-source] ----*/ - -function assertOnlyOwnProperties(object, props, message) { - assert.compareArray(Object.getOwnPropertyNames(object), props, `${message}: object should have no other properties than expected`); - assert.compareArray(Object.getOwnPropertySymbols(object), [], `${message}: object should have no own symbol properties`); -} - -function reviverWithExpectedSources(expectedSources) { - let i = 0; - return function reviver(key, value, context) { - assert.sameValue(typeof context, "object", "context should be an object"); - assert.sameValue(Object.getPrototypeOf(context), Object.prototype, "context should be a plain object"); - if (expectedSources[i] !== undefined) { - assertOnlyOwnProperties(context, ["source"], - "the JSON value is a primitve value, its context should only have a source property"); - verifyProperty(context, "source", { - value: expectedSources[i++], - configurable: true, - enumerable: true, - writable: true, - }, { restore: true }); - } else { - assertOnlyOwnProperties(context, [], - "the JSON value is an Array or Object, its context should have no property"); - i++; - } - return value; - }; -} - -assertOnlyOwnProperties( - JSON.parse('{}', reviverWithExpectedSources([])), - [], - "empty object" -); - -const singleProp = JSON.parse('{"42":37}', reviverWithExpectedSources(['37'])); -assertOnlyOwnProperties(singleProp, ["42"], "single numeric property key"); -assert.sameValue(singleProp[42], 37, "value of single numeric property key"); - -const multipleProps = JSON.parse('{"x": 1, "y": 2}', reviverWithExpectedSources(['1', '2'])); -assertOnlyOwnProperties(multipleProps, ["x", "y"], "multiple properties"); -assert.sameValue(multipleProps.x, 1, "multiple properties, value of x"); -assert.sameValue(multipleProps.y, 2, "multiple properties, value of y"); - -// undefined means the json value is JSObject or JSArray and the passed -// context to the reviver function has no source property. -const arrayProps = JSON.parse( - '{"x": [1,2], "y": [2,3]}', - reviverWithExpectedSources(['1', '2', undefined, '2', '3', undefined]) -); -assertOnlyOwnProperties(arrayProps, ["x", "y"], "array-valued properties"); -assert.compareArray(arrayProps.x, [1, 2], "array-valued properties, value of x"); -assert.compareArray(arrayProps.y, [2, 3], "array-valued properties, value of y"); - -const objectProps = JSON.parse( - '{"x": {"x": 1, "y": 2}}', - reviverWithExpectedSources(['1', '2', undefined, undefined]) -); -assertOnlyOwnProperties(objectProps, ["x"], "object-valued properties"); -assertOnlyOwnProperties(objectProps.x, ["x", "y"], "object-valued properties, value of x"); -assert.sameValue(objectProps.x.x, 1, "object-valued properties, value of x.x"); -assert.sameValue(objectProps.x.y, 2, "object-valued properties, value of x.y"); diff --git a/test/built-ins/JSON/parse/reviver-context-source-primitive-literal.js b/test/built-ins/JSON/parse/reviver-context-source-primitive-literal.js deleted file mode 100644 index b2d324f67e6..00000000000 --- a/test/built-ins/JSON/parse/reviver-context-source-primitive-literal.js +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.parse -description: > - Context argument and its source property behave as expected when parsing a - NumericLiteral, NullLiteral, BoolLiteral, or StringLiteral JSON string -includes: [compareArray.js, propertyHelper.js] -features: [json-parse-with-source] ----*/ - -function assertOnlyOwnProperties(object, props, message) { - assert.compareArray(Object.getOwnPropertyNames(object), props, `${message}: object should have no other properties than expected`); - assert.compareArray(Object.getOwnPropertySymbols(object), [], `${message}: object should have no own symbol properties`); -} - -function reviverWithExpectedSources(expectedSources) { - let i = 0; - return function reviver(key, value, context) { - assert.sameValue(typeof context, "object", "context should be an object"); - assert.sameValue(Object.getPrototypeOf(context), Object.prototype, "context should be a plain object"); - if (expectedSources[i] !== undefined) { - assertOnlyOwnProperties(context, ["source"], - "the JSON value is a primitve value, its context should only have a source property"); - verifyProperty(context, "source", { - value: expectedSources[i++], - configurable: true, - enumerable: true, - writable: true, - }, { restore: true }); - } else { - assertOnlyOwnProperties(context, [], - "the JSON value is an Array or Object, its context should have no property"); - i++; - } - return value; - }; -} - -assert.sameValue(1, JSON.parse('1', reviverWithExpectedSources(['1']))); -assert.sameValue(1.1, JSON.parse('1.1', reviverWithExpectedSources(['1.1']))); -assert.sameValue(-1, JSON.parse('-1', reviverWithExpectedSources(['-1']))); -assert.sameValue( - -1.1, - JSON.parse('-1.1', reviverWithExpectedSources(['-1.1'])) -); -assert.sameValue( - 11, - JSON.parse('1.1e1', reviverWithExpectedSources(['1.1e1'])) -); -assert.sameValue( - 11, - JSON.parse('1.1e+1', reviverWithExpectedSources(['1.1e+1'])) -); -assert.sameValue( - 0.11, - JSON.parse('1.1e-1', reviverWithExpectedSources(['1.1e-1'])) -); -assert.sameValue( - 11, - JSON.parse('1.1E1', reviverWithExpectedSources(['1.1E1'])) -); -assert.sameValue( - 11, - JSON.parse('1.1E+1', reviverWithExpectedSources(['1.1E+1'])) -); -assert.sameValue( - 0.11, - JSON.parse('1.1E-1', reviverWithExpectedSources(['1.1E-1'])) -); - -// Test NullLiteral, BoolLiteral, StringLiteral -assert.sameValue( - JSON.parse('null', reviverWithExpectedSources(['null'])), - null -); -assert.sameValue( - JSON.parse('true', reviverWithExpectedSources(['true'])), - true -); -assert.sameValue( - JSON.parse('false', reviverWithExpectedSources(['false'])), - false -); -assert.sameValue( - JSON.parse('"foo"', reviverWithExpectedSources(['"foo"'])), - "foo" -); diff --git a/test/built-ins/JSON/parse/reviver-context-source.js b/test/built-ins/JSON/parse/reviver-context-source.js new file mode 100644 index 00000000000..14af19fdf84 --- /dev/null +++ b/test/built-ins/JSON/parse/reviver-context-source.js @@ -0,0 +1,132 @@ +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-json.parse +description: > + Context argument and its source property behave as expected +info: | + JSON.parse ( _text_ [ , _reviver_ ] ) + 1. Let _jsonString_ be ? ToString(_text_). + 2. Let _parseResult_ be ? ParseJSON(_jsonString_). + 3. Let _unfiltered_ be _parseResult_.[[Value]]. + 4. If IsCallable(_reviver_) is *true*, then + a. Let _root_ be OrdinaryObjectCreate(%Object.prototype%). + b. Let _rootName_ be the empty String. + c. Perform ! CreateDataPropertyOrThrow(_root_, _rootName_, _unfiltered_). + d. Let _snapshot_ be CreateJSONParseRecord(_parseResult_.[[ParseNode]], _rootName_, _unfiltered_). + e. Return ? InternalizeJSONProperty(_root_, _rootName_, _reviver_, _snapshot_). + + InternalizeJSONProperty ( _holder_, _name_, _reviver_, _parseRecord_ ) + 1. Let _val_ be ? Get(_holder_, _name_). + 2. Let _context_ be OrdinaryObjectCreate(%Object.prototype%). + 3. If _parseRecord_ is a JSON Parse Record and SameValue(_parseRecord_.[[Value]], _val_) is *true*, then + a. If _val_ is not an Object, then + i. Let _parseNode_ be _parseRecord_.[[ParseNode]]. + ii. Assert: _parseNode_ is not an |ArrayLiteral| Parse Node and not an |ObjectLiteral| Parse Node. + iii. Let _sourceText_ be the source text matched by _parseNode_. + iv. Perform ! CreateDataPropertyOrThrow(_context_, *"source"*, CodePointsToString(_sourceText_)). + b. Let _elementRecords_ be _parseRecord_.[[Elements]]. + c. Let _entryRecords_ be _parseRecord_.[[Entries]]. + ... + 6. Return ? Call(_reviver_, _holder_, « _name_, _val_, _context_ »). + +includes: [compareArray.js, propertyHelper.js] +features: [json-parse-with-source] +---*/ + +function assertOnlyOwnProperties(object, expectNames, message) { + assert.compareArray(Object.getOwnPropertyNames(object), expectNames, message); + assert.compareArray(Object.getOwnPropertySymbols(object), [], message); +} + +function reviverWithExpectedSources(label, expectedSources) { + var i = -1; + return function reviver(key, value, context) { + i++; + var prefix = label + " invocation index " + i + " context "; + assert.sameValue(typeof context, "object", prefix + "should be an object"); + assert.sameValue(Object.getPrototypeOf(context), Object.prototype, + prefix + "should be a plain object"); + var expectedSource = expectedSources[i]; + if (expectedSource === undefined) { + assertOnlyOwnProperties(context, [], prefix + "should have no properties"); + return value; + } + assertOnlyOwnProperties(context, ["source"], prefix + "should have a source property"); + assert.sameValue(context.source, expectedSource, prefix + "source"); + verifyProperty(context, "source", { + value: expectedSource, + configurable: true, + enumerable: true, + writable: true, + }); + return value; + }; +} + +function verifyParsedPrimitive(jsonText, expectedResult) { + var reviver = reviverWithExpectedSources(jsonText, [jsonText]); + var parseResult = JSON.parse(' ' + jsonText + '\t', reviver); + assert.sameValue(parseResult, expectedResult, jsonText); +} + +verifyParsedPrimitive('null', null); +verifyParsedPrimitive('false', false); +verifyParsedPrimitive('true', true); + +verifyParsedPrimitive('""', ''); +verifyParsedPrimitive('"foo"', 'foo'); +verifyParsedPrimitive('"\\u00bD"', '\u00bd'); + +verifyParsedPrimitive('1', 1); +verifyParsedPrimitive('-1', -1); +verifyParsedPrimitive('-1.2', -1.2); +verifyParsedPrimitive('-0', -0); +verifyParsedPrimitive('1e0', 1); +verifyParsedPrimitive('1E1', 10); +verifyParsedPrimitive('1.23e+1', 12.3); +verifyParsedPrimitive('1.23E-1', 0.123); + +function verifyParsedArray(jsonText, expectedSources, expectedResult) { + var reviver = reviverWithExpectedSources(jsonText, expectedSources); + var parseResult = JSON.parse(jsonText, reviver); + assert(parseResult instanceof Array, jsonText); + for (var i = 0; i < parseResult.length; i++) { + var element = parseResult[i]; + // compareArray only works with primitives, so stringify objects. + if (element && typeof element === 'object') { + parseResult[i] = JSON.stringify(element); + } + } + assert.compareArray(parseResult, expectedResult, jsonText); +} + +verifyParsedArray('[ ]', [], []); +verifyParsedArray('[ -1.00, -0.00, -0 ]', ['-1.00', '-0.00', '-0'], [-1, -0, -0]); +verifyParsedArray( + '[1e3, "2e2", "\\u00bD", null, false, true, {"x": 3e+2, "y": 0.40}, []]', + ['1e3', '"2e2"', '"\\u00bD"', 'null', 'false', 'true', '3e+2', '0.40'], + [1000, '2e2', '\u00bd', null, false, true, '{"x":300,"y":0.4}', '[]'] +); + +function verifyParsedObject(jsonText, expectedSources, expectedKeys) { + var reviver = reviverWithExpectedSources(jsonText, expectedSources); + var parseResult = JSON.parse(jsonText, reviver); + assertOnlyOwnProperties(parseResult, expectedKeys, jsonText); + return parseResult; +} + +verifyParsedObject('{}', [], []); +var singleProp = verifyParsedObject('{ "42": 42E-1 }', ["42E-1"], ["42"]); +assert.sameValue(singleProp['42'], 4.2, 'singleProp[onlyKey]'); +var deep = verifyParsedObject( + '{ "x": [1.0, 2.0], "y": [{ "value": 3.0 }, 4.0] }', + ['1.0', '2.0', /* [1.0, 2.0] */ undefined, '3.0', /* { value: 3.0 } */ undefined, '4.0'], + ['x', 'y'] +); +assert.compareArray(deep.x, [1, 2], 'deep.x'); +assert.sameValue(deep.y.length, 2, 'deep.y'); +assertOnlyOwnProperties(deep.y[0], ['value'], 'deep.y[0]'); +assert.sameValue(deep.y[1], 4, 'deep.y[1]'); +assert.sameValue(JSON.stringify(deep), '{"x":[1,2],"y":[{"value":3},4]}', 'deep'); diff --git a/test/built-ins/JSON/parse/reviver-forward-modifies-object.js b/test/built-ins/JSON/parse/reviver-forward-modifies-object.js deleted file mode 100644 index 5577009a74c..00000000000 --- a/test/built-ins/JSON/parse/reviver-forward-modifies-object.js +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.parse -description: Codepaths involving InternaliseJSONProperty behave as expected - -includes: [compareArray.js] -features: [json-parse-with-source] ----*/ - -function assertOnlyOwnProperties(object, props, message) { - assert.compareArray(Object.getOwnPropertyNames(object), props, `${message}: object should have no other properties than expected`); - assert.compareArray(Object.getOwnPropertySymbols(object), [], `${message}: object should have no own symbol properties`); -} - -const replacements = [ - 42, - ["foo"], - { foo: "bar" }, - "foo" -]; - -// Test Array forward modify -for (const replacement of replacements) { - let alreadyReplaced = false; - let expectedKeys = ["0", "1", ""]; - // if the replacement is an object, add its keys to the expected keys - if (typeof replacement === "object") { - expectedKeys.splice(1, 0, ...Object.keys(replacement)); - } - const o = JSON.parse("[1, 2]", function (k, v, { source }) { - assert.sameValue(k, expectedKeys.shift()); - if (k === "0") { - if (!alreadyReplaced) { - this[1] = replacement; - alreadyReplaced = true; - } - } else if (k !== "") { - assert.sameValue(source, undefined); - } - return this[k]; - }); - assert.sameValue(expectedKeys.length, 0); - assert.compareArray(o, [1, replacement], `array forward-modified with ${replacement}`); -} - -function assertOnlyOwnProperties(object, props, message) { - assert.compareArray(Object.getOwnPropertyNames(object), props, `${message}: object should have no other properties than expected`); - assert.compareArray(Object.getOwnPropertySymbols(object), [], `${message}: object should have no own symbol properties`); -} - -// Test Object forward modify -for (const replacement of replacements) { - let alreadyReplaced = false; - let expectedKeys = ["p", "q", ""]; - if (typeof replacement === "object") { - expectedKeys.splice(1, 0, ...Object.keys(replacement)); - } - const o = JSON.parse('{"p":1, "q":2}', function (k, v, { source }) { - assert.sameValue(k, expectedKeys.shift()); - if (k === 'p') { - if (!alreadyReplaced) { - this.q = replacement; - alreadyReplaced = true; - } - } else if (k !== "") { - assert.sameValue(source, undefined); - } - return this[k]; - }); - assert.sameValue(expectedKeys.length, 0); - assertOnlyOwnProperties(o, ["p", "q"], `object forward-modified with ${replacement}`); - assert.sameValue(o.p, 1, "property p should not be replaced"); - assert.sameValue(o.q, replacement, `property q should be replaced with ${replacement}`); -} - -// Test combinations of possible JSON input with multiple forward modifications - -{ - let reviverCallIndex = 0; - const expectedKeys = ["a", "b", "c", ""]; - const reviver = function(key, value, {source}) { - assert.sameValue(key, expectedKeys[reviverCallIndex++]); - if (key === "a") { - this.b = 2; - assert.sameValue(source, "0"); - } else if (key === "b") { - this.c = 3; - assert.sameValue(value, 2); - assert.sameValue(source, undefined); - } else if (key === "c") { - assert.sameValue(value, 3); - assert.sameValue(source, undefined); - } - return value; - } - const parsed = JSON.parse('{"a": 0, "b": 1, "c": [1, 2]}', reviver); - assertOnlyOwnProperties(parsed, ["a", "b", "c"], "object with forward-modified properties"); - assert.sameValue(parsed.a, 0, "'a' property should be unmodified"); - assert.sameValue(parsed.b, 2, "'b' property should be modified to 2"); - assert.sameValue(parsed.c, 3, "'c' property should be modified to 3"); -} - -{ - let reviverCallIndex = 0; - const expectedKeys = ["0", "1", "2", "3", ""]; - const reviver = function(key, value, {source}) { - assert.sameValue(key, expectedKeys[reviverCallIndex++]); - if (key === "0") { - this[1] = 3; - assert.sameValue(value, 1); - assert.sameValue(source, "1"); - } else if (key === "1") { - this[2] = 4; - assert.sameValue(value, 3); - assert.sameValue(source, undefined); - } else if(key === "2") { - this[3] = 5; - assert.sameValue(value, 4); - assert.sameValue(source, undefined); - } else if(key === "5") { - assert.sameValue(value, 5); - assert.sameValue(source, undefined); - } - return value; - } - assert.compareArray(JSON.parse('[1, 2, 3, {"a": 1}]', reviver), [1, 3, 4, 5], "array with forward-modified elements"); -} diff --git a/test/built-ins/JSON/rawJSON/basic.js b/test/built-ins/JSON/rawJSON/basic.js index 26720737126..3a2140440d0 100644 --- a/test/built-ins/JSON/rawJSON/basic.js +++ b/test/built-ins/JSON/rawJSON/basic.js @@ -6,51 +6,71 @@ esid: sec-json.rawjson description: Basic functionality of JSON.rawJSON(). info: | JSON.rawJSON ( text ) - - 1. Let jsonString be ? ToString(text). ... - 4. Let internalSlotsList be « [[IsRawJSON]] ». - 5. Let obj be OrdinaryObjectCreate(null, internalSlotsList). - 6. Perform ! CreateDataPropertyOrThrow(obj, "rawJSON", jsonString). - 7. Perform ! SetIntegrityLevel(obj, frozen). - 8. Return obj. + 9. Let _obj_ be OrdinaryObjectCreate(*null*, _internalSlotsList_). + 10. Perform ! CreateDataPropertyOrThrow(_obj_, *"rawJSON"*, _jsonString_). + 11. Perform ! SetIntegrityLevel(_obj_, ~frozen~). + 12. Return _obj_. +includes: [propertyHelper.js] features: [json-parse-with-source] ---*/ -assert.sameValue(JSON.stringify(JSON.rawJSON(1)), '1'); -assert.sameValue(JSON.stringify(JSON.rawJSON(1.1)), '1.1'); -assert.sameValue(JSON.stringify(JSON.rawJSON(-1)), '-1'); -assert.sameValue(JSON.stringify(JSON.rawJSON(-1.1)), '-1.1'); -assert.sameValue(JSON.stringify(JSON.rawJSON(1.1e1)), '11'); -assert.sameValue(JSON.stringify(JSON.rawJSON(1.1e-1)), '0.11'); +function verifyRawJSON(primitive) { + var entries = [ + ['primitive', JSON.rawJSON(primitive)], + ['string', JSON.rawJSON(String(primitive))], + ['toString', JSON.rawJSON({ toString: function() { return primitive; } })] + ]; + for (var i = 0; i < entries.length; i++) { + var label = 'rawJSON of ' + entries[i][0] + ' ' + (typeof primitive) + ' ' + primitive; + var raw = entries[i][1]; + assert.sameValue(Object.getPrototypeOf(raw), null, label + ' has a null prototype'); + assert.sameValue(Object.isFrozen(raw), true, label + ' is frozen'); + assert.sameValue( + Reflect.defineProperty(raw, 'expando', { value: true }), + false, + label + ' rejects new properties' + ); + assert.sameValue( + Object.getOwnPropertyDescriptor(raw, 'expando'), + undefined, + label + ' does not define new properties' + ); + assert.compareArray(Reflect.ownKeys(raw), ['rawJSON'], label + ' own properties'); + verifyProperty(raw, 'rawJSON', { + value: String(primitive), + enumerable: true, + configurable: false, + writable: false + }); + assert.sameValue(JSON.stringify(raw), String(primitive), label + ' JSON.stringify'); + } + var composite = { + primitive: entries[0][1], + strings: [entries[1][1], entries[2][1]] + }; + assert.sameValue( + JSON.stringify(composite), + '{"primitive":%s,"strings":[%s,%s]}'.replace(/%s/g, primitive), + 'JSON.stringify a structure containing rawJSON for ' + primitive + ); +} + +verifyRawJSON(null); +verifyRawJSON(false); +verifyRawJSON(true); -assert.sameValue(JSON.stringify(JSON.rawJSON(null)), 'null'); -assert.sameValue(JSON.stringify(JSON.rawJSON(true)), 'true'); -assert.sameValue(JSON.stringify(JSON.rawJSON(false)), 'false'); -assert.sameValue(JSON.stringify(JSON.rawJSON('"foo"')), '"foo"'); +verifyRawJSON('""'); +verifyRawJSON('"foo"'); +verifyRawJSON('"\\u00bD"'); -assert.sameValue(JSON.stringify({ 42: JSON.rawJSON(37) }), '{"42":37}'); -assert.sameValue( - JSON.stringify({ x: JSON.rawJSON(1), y: JSON.rawJSON(2) }), - '{"x":1,"y":2}' -); -assert.sameValue( - JSON.stringify({ x: { x: JSON.rawJSON(1), y: JSON.rawJSON(2) } }), - '{"x":{"x":1,"y":2}}' -); +verifyRawJSON(1); +verifyRawJSON(-1); +verifyRawJSON(-1.2); -assert.sameValue(JSON.stringify([JSON.rawJSON(1), JSON.rawJSON(1.1)]), '[1,1.1]'); -assert.sameValue( - JSON.stringify([ - JSON.rawJSON('"1"'), - JSON.rawJSON(true), - JSON.rawJSON(null), - JSON.rawJSON(false), - ]), - '["1",true,null,false]' -); -assert.sameValue( - JSON.stringify([{ x: JSON.rawJSON(1), y: JSON.rawJSON(1) }]), - '[{"x":1,"y":1}]' -); +verifyRawJSON('-0'); +verifyRawJSON('1e0'); +verifyRawJSON('1E1'); +verifyRawJSON('1.23e+1'); +verifyRawJSON('1.23E-1'); diff --git a/test/built-ins/JSON/rawJSON/builtin.js b/test/built-ins/JSON/rawJSON/builtin.js index 658328291d5..3b23bc17761 100644 --- a/test/built-ins/JSON/rawJSON/builtin.js +++ b/test/built-ins/JSON/rawJSON/builtin.js @@ -4,42 +4,42 @@ /*--- esid: sec-json.rawjson description: > - JSON.rawJSON meets the requirements for built-in objects + JSON.rawJSON meets the requirements for standard built-in objects info: | - JSON.isRawJSON ( O ) - - 18 ECMAScript Standard Built-in Objects + ECMAScript Standard Built-in Objects ... - Unless specified otherwise, a built-in object that is callable as a function - is a built-in function object with the characteristics described in 10.3. Unless specified otherwise, the [[Extensible]] internal slot of a built-in - object initially has the value true. Every built-in function object has a - [[Realm]] internal slot whose value is the Realm Record of the realm for - which the object was initially created. + object initially has the value true. ... Unless otherwise specified every built-in function and every built-in constructor has the Function prototype object, which is the initial value of the expression Function.prototype (20.2.3), as the value of its [[Prototype]] internal slot. ... - Built-in function objects that are not identified as constructors do not - implement the [[Construct]] internal method unless otherwise specified in the - description of a particular function. + Built-in function objects that are not constructors do not have a "prototype" + property unless otherwise specified in the description of a particular + function. + ... + Each built-in function defined in this specification is created by calling the + CreateBuiltinFunction abstract operation (10.3.4). The values of the length + and name parameters are the initial values of the "length" and "name" + properties as discussed below. The values of the prefix parameter are + similarly discussed below. +includes: [propertyHelper.js] features: [json-parse-with-source] ---*/ -assert(Object.isExtensible(JSON.rawJSON), "JSON.rawJSON is extensible"); assert.sameValue( - typeof JSON.rawJSON, - 'function', - 'The value of `typeof JSON.rawJSON` is "function"' + Object.isExtensible(JSON.rawJSON), + Object.isExtensible(Object.prototype), + "JSON.rawJSON extensibility matches Object.prototype", ); assert.sameValue( Object.getPrototypeOf(JSON.rawJSON), Function.prototype, - "Prototype of JSON.rawJSON is Function.prototype" + "JSON.rawJSON [[Prototype]] is Function.prototype" ); assert.sameValue( @@ -47,3 +47,5 @@ assert.sameValue( undefined, "JSON.rawJSON has no own prototype property" ); + +verifyPrimordialCallableProperty(JSON, "rawJSON", "rawJSON", 1); diff --git a/test/built-ins/JSON/rawJSON/illegal-empty-and-start-end-chars.js b/test/built-ins/JSON/rawJSON/illegal-empty-and-start-end-chars.js deleted file mode 100644 index e7af3828ee4..00000000000 --- a/test/built-ins/JSON/rawJSON/illegal-empty-and-start-end-chars.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.rawjson -description: Throw SyntaxError on empty string, or illegal start/end chars -info: | - JSON.rawJSON ( text ) - - 1. Let jsonString be ? ToString(text). - 2. Throw a SyntaxError exception if jsonString is the empty String, or if - either the first or last code unit of jsonString is any of 0x0009 - (CHARACTER TABULATION), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), or - 0x0020 (SPACE). - -features: [json-parse-with-source] ----*/ - -const ILLEGAL_END_CHARS = ['\n', '\t', '\r', ' ']; -for (const char of ILLEGAL_END_CHARS) { - assert.throws(SyntaxError, () => { - JSON.rawJSON(`${char}123`); - }); - assert.throws(SyntaxError, () => { - JSON.rawJSON(`123${char}`); - }); -} - -assert.throws(SyntaxError, () => { - JSON.rawJSON(''); -}); diff --git a/test/built-ins/JSON/rawJSON/invalid-JSON-text.js b/test/built-ins/JSON/rawJSON/invalid-JSON-text.js deleted file mode 100644 index 5fc5e3f8b73..00000000000 --- a/test/built-ins/JSON/rawJSON/invalid-JSON-text.js +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.rawjson -description: > - Inputs not convertible to string, or convertible to invalid JSON string -info: | - JSON.rawJSON ( text ) - - 1. Let jsonString be ? ToString(text). - ... - 3. Parse StringToCodePoints(jsonString) as a JSON text as specified in - ECMA-404. Throw a SyntaxError exception if it is not a valid JSON text as - defined in that specification, or if its outermost value is an object or - array as defined in that specification. - -features: [json-parse-with-source] ----*/ - -assert.throws(TypeError, () => { - JSON.rawJSON(Symbol('123')); -}); - -assert.throws(SyntaxError, () => { - JSON.rawJSON(undefined); -}); - -assert.throws(SyntaxError, () => { - JSON.rawJSON({}); -}); - -assert.throws(SyntaxError, () => { - JSON.rawJSON([]); -}); diff --git a/test/built-ins/JSON/rawJSON/length.js b/test/built-ins/JSON/rawJSON/length.js deleted file mode 100644 index 73afda194cd..00000000000 --- a/test/built-ins/JSON/rawJSON/length.js +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2024 Igalia S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-built-in-function-objects -description: > - JSON.rawJSON.length value and descriptor. -info: | - Every built-in function object, including constructors, has a *"length"* - property whose value is a non-negative integral Number. Unless otherwise - specified, this value is the number of required parameters shown in the - subclause heading for the function description. Optional parameters and rest - parameters are not included in the parameter count. - - Unless otherwise specified, the *"length"* property of a built-in function - object has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, - [[Configurable]]: *true* }. - -includes: [propertyHelper.js] -features: [json-parse-with-source] ----*/ - -verifyProperty(JSON.rawJSON, 'length', { - value: 1, - writable: false, - enumerable: false, - configurable: true -}); diff --git a/test/built-ins/JSON/rawJSON/name.js b/test/built-ins/JSON/rawJSON/name.js deleted file mode 100644 index ae864d86fcb..00000000000 --- a/test/built-ins/JSON/rawJSON/name.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2024 Igalia S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-built-in-function-objects -description: > - JSON.rawJSON.name value and descriptor. -info: | - Every built-in function object, including constructors, has a *"name"* - property whose value is a String. Unless otherwise specified, this value is - the name that is given to the function in this specification. Functions that - are identified as anonymous functions use the empty String as the value of - the *"name"* property. For functions that are specified as properties of - objects, the name value is the property name string used to access the - function. Functions that are specified as get or set accessor functions of - built-in properties have *"get"* or *"set"* (respectively) passed to the - prefix parameter when calling CreateBuiltinFunction. - - The value of the *"name"* property is explicitly specified for each built-in - function whose property key is a Symbol value. If such an explicitly - specified value starts with the prefix *"get "* or *"set "* and the function - for which it is specified is a get or set accessor function of a built-in - property, the value without the prefix is passed to the name parameter, and - the value *"get"* or *"set"* (respectively) is passed to the prefix - parameter when calling CreateBuiltinFunction. - - Unless otherwise specified, the *"name"* property of a built-in function - object has the attributes { [[Writable]]: *false*, [[Enumerable]]: *false*, - [[Configurable]]: *true* }. - -includes: [propertyHelper.js] -features: [json-parse-with-source] ----*/ - -verifyProperty(JSON.rawJSON, 'name', { - value: "rawJSON", - enumerable: false, - writable: false, - configurable: true -}); diff --git a/test/built-ins/JSON/rawJSON/prop-desc.js b/test/built-ins/JSON/rawJSON/prop-desc.js deleted file mode 100644 index 9ab33f27d5c..00000000000 --- a/test/built-ins/JSON/rawJSON/prop-desc.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2024 Igalia S.L. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.rawjson -description: > - Property type and descriptor. -info: | - JSON.rawJSON ( text ) - - 18 ECMAScript Standard Built-in Objects - ... - Every other data property described in clauses 19 through 28 and in Annex B.2 - has the attributes { [[Writable]]: true, [[Enumerable]]: false, - [[Configurable]]: true } unless otherwise specified. - -includes: [propertyHelper.js] -features: [json-parse-with-source] ----*/ - -verifyProperty(JSON, 'rawJSON', { - enumerable: false, - writable: true, - configurable: true -}); diff --git a/test/built-ins/JSON/rawJSON/returns-expected-object.js b/test/built-ins/JSON/rawJSON/returns-expected-object.js deleted file mode 100644 index 76f7165a5e0..00000000000 --- a/test/built-ins/JSON/rawJSON/returns-expected-object.js +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2023 the V8 project authors. All rights reserved. -// This code is governed by the BSD license found in the LICENSE file. - -/*--- -esid: sec-json.rawjson -description: Object returned from JSON.rawJSON() is the expected shape. -info: | - JSON.rawJSON ( text ) - - 5. Let _obj_ be OrdinaryObjectCreate(*null*, _internalSlotsList_). - 6. Perform ! CreateDataPropertyOrThrow(_obj_, *"rawJSON"*, _jsonString_). - ... - 8. Return _obj_. - -includes: [compareArray.js] -features: [json-parse-with-source] ----*/ - -function assertIsRawJSON(rawJSON, expectedRawJSONValue) { - assert.sameValue(Object.getPrototypeOf(rawJSON), null, "RawJSON object should have null prototype"); - assert(Object.hasOwn(rawJSON, "rawJSON"), "RawJSON object should have rawJSON own property"); - assert.compareArray(Object.getOwnPropertyNames(rawJSON), ["rawJSON"], "RawJSON object should have only rawJSON own property"); - assert.compareArray(Object.getOwnPropertySymbols(rawJSON), [], "RawJSON object should have no own property symbols"); - assert.sameValue(rawJSON.rawJSON, expectedRawJSONValue, "rawJSON value"); -} - -assertIsRawJSON(JSON.rawJSON(1), "1"); -assertIsRawJSON(JSON.rawJSON(null), "null"); -assertIsRawJSON(JSON.rawJSON(true), "true"); -assertIsRawJSON(JSON.rawJSON(false), "false"); -assertIsRawJSON(JSON.rawJSON('"foo"'), '"foo"'); diff --git a/test/built-ins/JSON/rawJSON/text-invalid-json.js b/test/built-ins/JSON/rawJSON/text-invalid-json.js new file mode 100644 index 00000000000..a1c8798e750 --- /dev/null +++ b/test/built-ins/JSON/rawJSON/text-invalid-json.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-json.rawjson +description: > + JSON.rawJSON throws a SyntaxError for invalid JSON text +info: | + JSON.rawJSON ( text ) + + 1. Let _jsonString_ be ? ToString(_text_). + 2. If _jsonString_ is the empty String, throw a *SyntaxError* exception. + 3. If either the first or last code unit of _jsonString_ is any of 0x0009 + (CHARACTER TABULATION), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), + 0x0020 (SPACE), 0x005B (LEFT SQUARE BRACKET), or 0x007B (LEFT CURLY + BRACKET), throw a *SyntaxError* exception. + 4. Let _parseResult_ be ? ParseJSON(_jsonString_). + +features: [json-parse-with-source] +---*/ + +var NON_JSON_INPUTS = [ + '', + 'undefined', + ':', + ',', + 'True', + 'FALSE', + '+1', + '01', + '00', + '1.', + '.1', + '1.e0', + '.1e0', + '1n', + '1_0', + '"', + "''", + '\\"', + '\\x41', + '\\0' +]; +for (var i = 0; i < NON_JSON_INPUTS.length; i++) { + var text = NON_JSON_INPUTS[i]; + assert.throws(SyntaxError, function() { + JSON.rawJSON(text); + }, 'JSON.rawJSON(' + JSON.stringify(text) + ')'); +} diff --git a/test/built-ins/JSON/rawJSON/text-invalid-start-end.js b/test/built-ins/JSON/rawJSON/text-invalid-start-end.js new file mode 100644 index 00000000000..17e39c2de97 --- /dev/null +++ b/test/built-ins/JSON/rawJSON/text-invalid-start-end.js @@ -0,0 +1,40 @@ +// Copyright (C) 2023 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-json.rawjson +description: > + JSON.rawJSON throws a SyntaxError for text with disallowed start/end chars +info: | + JSON.rawJSON ( text ) + + 1. Let _jsonString_ be ? ToString(_text_). + 2. If _jsonString_ is the empty String, throw a *SyntaxError* exception. + 3. If either the first or last code unit of _jsonString_ is any of 0x0009 + (CHARACTER TABULATION), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), + 0x0020 (SPACE), 0x005B (LEFT SQUARE BRACKET), or 0x007B (LEFT CURLY + BRACKET), throw a *SyntaxError* exception. + +features: [json-parse-with-source] +---*/ + +var DISALLOWED_TEXTS = ['[]', '[0]', '{}', '{"foo":"bar"}']; +for (var i = 0; i < DISALLOWED_TEXTS.length; i++) { + var text = DISALLOWED_TEXTS[i]; + assert.throws(SyntaxError, function() { + JSON.rawJSON(text); + }, 'JSON.rawJSON(' + JSON.stringify(text) + ')'); +} + +const DISALLOWED_CHARS = ['\t', '\n', '\r', ' ', '[', '{']; +for (const char of DISALLOWED_CHARS) { + var text = char + '123'; + assert.throws(SyntaxError, function() { + JSON.rawJSON(text); + }, 'JSON.rawJSON(' + JSON.stringify(text) + ')'); + + text = '123' + char; + assert.throws(SyntaxError, function() { + JSON.rawJSON(text); + }, 'JSON.rawJSON(' + JSON.stringify(text) + ')'); +} diff --git a/test/built-ins/JSON/rawJSON/text-not-stringable.js b/test/built-ins/JSON/rawJSON/text-not-stringable.js new file mode 100644 index 00000000000..e57a3f6fe0a --- /dev/null +++ b/test/built-ins/JSON/rawJSON/text-not-stringable.js @@ -0,0 +1,21 @@ +// Copyright (C) 2025 Richard Gibson. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-json.rawjson +description: > + JSON.rawJSON rejects text not convertible to string +info: | + JSON.rawJSON ( text ) + + 1. Let _jsonString_ be ? ToString(_text_). + +includes: [typeCoercion.js] +features: [json-parse-with-source] +---*/ + +testNotCoercibleToString(function(expectedErrorConstructor, value) { + assert.throws(expectedErrorConstructor, function() { + JSON.rawJSON(value); + }); +});