adobe / jsonschema2md

Convert Complex JSON Schemas into Markdown Documentation
Apache License 2.0
590 stars 148 forks source link

Maximum call stack size exceeded #221

Open Finkman opened 4 years ago

Finkman commented 4 years ago

What did you do

What did you expect to happen

Conversion to mark down without errors

What happened

Since v4.0.4 it fails with an exception:

(Figured out, that it is not guaranteed to fail, sometimes it just works; I know, those are the worst bugs ever)

converter failed with exception ``` [build] (node:19698) UnhandledPromiseRejectionWarning: RangeError: Maximum call stack size exceeded [build] at Array.join (native) [build] at keyword (/usr/lib/node_modules/@adobe/jsonschema2md/lib/keywords.js:16:14) [build] at Object.meta.(anonymous function) (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:42:28) [build] at Object.get (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:104:26) [build] at Object.meta.(anonymous function) (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:46:20) [build] at Object.get (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:104:26) [build] at Object.meta.(anonymous function) (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:46:20) [build] at Object.get (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:104:26) [build] at Object.meta.(anonymous function) (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:46:20) [build] at Object.get (/usr/lib/node_modules/@adobe/jsonschema2md/lib/schemaProxy.js:104:26) ```
[build] [100%] Converting json schema files to markdown
[build] loading schemas

What's your environment

Do you have example files:

For this schema

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://tqswip.com/common/controlling/controller",
  "title": "Controller Configuration",
  "type": "object",
  "properties": {
    "ProcessData": {
      "title": "Named data variables used in jobs",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "RtpcMapping": {
      "title": "Mapping between ProcessData and Rtpc Parameter",
      "description": "Parameter are requested on Rtpc and will be assigned to the process data as configured.",
      "type": "object",
      "propertyNames": {
        "enum": [
          "Gewicht_Netto",
          "Gewicht_Avr",
          "Volumen"
        ]
      },
      "patternProperties": {
        "": {
          "type": "string"
        }
      }
    },
    "Segments": {
      "title": "Processing Segment Definitions",
      "type": "array",
      "items": {
        "$ref": "#/definitions/segment"
      }
    }
  },
  "required": [
    "Segments",
    "ProcessData",
    "RtpcMapping"
  ],
  "examples": [
    {
      "ProcessData": [
        "huhu"
      ],
      "RtpcMapping": {
        "Gewicht_Netto": "huhu"
      },
      "Segments": [
        {
          "id": 12,
          "Name": "huhu",
          "Sequence": [
            {
              "Type": "jolo",
              "huu": 12
            },
            {
              "Type": "jolo",
              "Inputs": {
                "Grading": "huhu"
              }
            },
            {
              "Sequence": [
                {
                  "Type": "123",
                  "maxV": 23
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "definitions": {
    "segmentSchema": {
      "type": "object",
      "properties": {
        "id": {
          "type": "number"
        },
        "Name": {
          "type": "string"
        }
      },
      "required": [
        "id"
      ]
    },
    "segmentNames": {
      "enum": [
        "id",
        "Name"
      ]
    },
    "jobSchema": {
      "type": "object",
      "properties": {
        "Type": {
          "type": "string"
        },
        "Inputs": {
          "type": "object",
          "patternProperties": {
            "": {
              "type": "string"
            }
          }
        },
        "Outputs": {
          "type": "object",
          "patternProperties": {
            "": {
              "type": "string"
            }
          }
        }
      },
      "required": [
        "Type"
      ],
      "not": {
        "anyOf": [
          {
            "required": [
              "Sequence"
            ]
          },
          {
            "required": [
              "Parallel"
            ]
          }
        ]
      }
    },
    "jobNames": true,
    "combinedJobSchema": {
      "type": "object",
      "oneOf": [
        {
          "properties": {
            "Sequence": {
              "type": "array",
              "items": {
                "type": "object",
                "allOf": [
                  {
                    "$ref": "#/definitions/anyJobSchema"
                  },
                  {
                    "if": {
                      "required": [
                        "Type"
                      ]
                    },
                    "then": {
                      "propertyNames": {
                        "$ref": "#/definitions/jobNames"
                      }
                    },
                    "else": {
                      "propertyNames": {
                        "$ref": "#/definitions/combinedJobNames"
                      }
                    }
                  }
                ]
              }
            }
          },
          "anyOf": [
            {
              "required": [
                "Sequence"
              ]
            },
            {
              "required": [
                "Parallel"
              ]
            }
          ]
        }
      ],
      "not": {
        "required": [
          "Type"
        ]
      }
    },
    "combinedJobNames": {
      "enum": [
        "Sequence",
        "Parallel"
      ]
    },
    "anyJobSchema": {
      "if": {
        "required": [
          "Type"
        ]
      },
      "then": {
        "$ref": "#/definitions/jobSchema"
      },
      "else": {
        "$ref": "#/definitions/combinedJobSchema"
      }
    },
    "segment": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/definitions/segmentSchema"
        },
        {
          "$ref": "#/definitions/anyJobSchema"
        },
        {
          "if": {
            "required": [
              "Type"
            ]
          },
          "then": {
            "propertyNames": {
              "anyOf": [
                {
                  "$ref": "#/definitions/segmentNames"
                },
                {
                  "$ref": "#/definitions/jobNames"
                }
              ]
            }
          },
          "else": {
            "propertyNames": {
              "anyOf": [
                {
                  "$ref": "#/definitions/segmentNames"
                },
                {
                  "$ref": "#/definitions/combinedJobNames"
                }
              ]
            }
          }
        }
      ]
    }
  }
}

trieloff commented 4 years ago

Do you have the issue on other node versions? The code that is failing looks like this:

function keyword(str) {
  used.add(str[0]);
  return str.join('');
}

and I'm having a very hard time visualizing what might cause the issue.

trieloff commented 4 years ago

I've added a speculative fix in the max-call-stack branch. Can you try this and report back?

Finkman commented 4 years ago

Same, but looks like different location throwing:

loading 1 schemas
(node:8415) UnhandledPromiseRejectionWarning: RangeError: Maximum call stack size exceeded
    at Set.add (<anonymous>)
    at keyword (/home/fins/Development/jsonschema2md/lib/keywords.js:15:8)
    at Object.meta.(anonymous function) (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:41:23)
    at Object.get (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:104:26)
    at Object.meta.(anonymous function) (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:46:20)
    at Object.get (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:104:26)
    at Object.meta.(anonymous function) (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:46:20)
    at Object.get (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:104:26)
    at Object.meta.(anonymous function) (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:46:20)
    at Object.get (/home/fins/Development/jsonschema2md/lib/schemaProxy.js:104:26)
(node:8415) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:8415) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Finkman commented 4 years ago

Can you reproduce the error?

trieloff commented 4 years ago

I haven't been able to look into this in depth, sorry.

eriesgo commented 4 years ago

Same issue with schemas including multiple objects in "definitions". With those files I have found a workaround by removing the "$id" keyword. Any hint?

eriesgo commented 4 years ago

@trieloff I have discovered that removing the recursive references in my code fixed the stack overflow.

Windol commented 4 years ago

This issue may be related to another issue #234

miscounting commented 4 years ago

I've had this bug, too, and I think it's because I have an object type that may contain itself. Recursion bad. Deleting the recursive property made it run fine. Probably need to handle recursive references.

sjovanovic commented 3 years ago

Recursion references are perfectly legit in JSON Schema. jsonschema2md should be smart enough to handle this

Aulig commented 3 years ago

Also just encountered this issue with recursion. Don't really want to change my schema because of this :/