1- /* Copyright (c) 2020, 2025 , Oracle and/or its affiliates.
1+ /* Copyright (c) 2020, 2026 , Oracle and/or its affiliates.
22 * Copyright (C) 1996-2020 Python Software Foundation
33 *
44 * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -445,6 +445,7 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall
445445 final Object value ;
446446 ScannerState nextState = null ;
447447 if (state == ScannerState .dict ) {
448+ /* scanner is currently inside a dictionary */
448449 int c ;
449450 if (idx >= length || (c = codePointAtIndexNode .execute (string , idx )) != '"' && (c != '}' )) {
450451 throw decodeError (frame , boundaryCallData , inliningTarget , errorProfile , this , string , idx , ErrorMessages .EXPECTING_PROP_NAME_ECLOSED_IN_DBL_QUOTES );
@@ -457,6 +458,17 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall
457458 Object topOfStack = stack .pop ();
458459 TruffleString parentKey = null ;
459460 final Object dict ;
461+ /*
462+ * If no hooks are present, the stack contains only the parent dicts or
463+ * lists the current object is nested in. If a objectHook or objectPairsHook
464+ * is present, the stack also stores the property keys of nested dicts. For
465+ * example, when parsing the innermost dict of '{"a":{"b":{"c":"d"}}}', the
466+ * stack will contain (from bottom to top) [<dictStorage>, <dictStorage>,
467+ * "a", <dictStorage>, "b"]. This is necessary so after finishing parsing a
468+ * dict, we can call the hook and replace it with the hook's return value in
469+ * the parent dict, i.e. when in the previous example we finish parsing
470+ * '{"c":"d"}', we effectively execute parent["b"] = objectHook({"c":"d"}).
471+ */
460472 if (hasPairsHook ) {
461473 if (topOfStack instanceof TruffleString ) {
462474 parentKey = (TruffleString ) topOfStack ;
@@ -484,6 +496,9 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall
484496 nextState = parentKey == null ? ScannerState .list : ScannerState .dict ;
485497 if (nextState == ScannerState .dict ) {
486498 Object parent = stack .peek ();
499+ if (parent instanceof TruffleString ) {
500+ parent = stack .get (stack .size () - 2 );
501+ }
487502 if (hasPairsHook ) {
488503 currentPairsStorage = (ObjectSequenceStorage ) parent ;
489504 currentPairsStorage .setObjectItemNormalized (currentPairsStorage .length () - 1 , PFactory .createTuple (language , new Object []{parentKey , dict }));
@@ -518,6 +533,7 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall
518533 substringByteIndexNode ,
519534 appendCodePointNode ,
520535 appendSubstringByteIndexNode , builderToStringNode );
536+ /* force hash computation */
521537 hashCodeNode .execute (newKey , TS_ENCODING );
522538 TruffleString key = memoPutIfAbsent (memo , newKey );
523539 if (key == null ) {
@@ -624,12 +640,20 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall
624640 assert nextState == ScannerState .dict ;
625641 currentPairsStorage = (ObjectSequenceStorage ) value ;
626642 if (state == ScannerState .dict ) {
643+ /*
644+ * save the associated propertyKey so we can replace the current
645+ * value with the hook's result later
646+ */
627647 stack .add (propertyKey );
628648 }
629649 } else {
630650 assert nextState == ScannerState .dict ;
631651 currentDictStorage = (EconomicMapStorage ) ((PDict ) value ).getDictStorage ();
632652 if (hasObjectHook && state == ScannerState .dict ) {
653+ /*
654+ * save the associated propertyKey so we can replace the current
655+ * value with the hook's result later
656+ */
633657 stack .add (propertyKey );
634658 }
635659 }
0 commit comments