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);
+ });
+});