From bf09d79eea640adce8cceb86200502e5cb974eab Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sat, 22 Feb 2020 20:53:47 +0100 Subject: [PATCH] Use the ESLint `no-restricted-syntax` rule to prevent direct usage of `new Cmd()`/`new Name()`/`new Ref()` Given that all of these primitives implement caching, to avoid unnecessarily duplicating those objects *a lot* during parsing, it would thus be good to actually enforce usage of `Cmd.get()`/`Name.get()`/`Ref.get()` in the code-base. Luckily it turns out that there's an ESLint rule, which is fairly easy to use, that can be used to disallow arbitrary JavaScript syntax. Please find additional details about the ESLint rule at https://eslint.org/docs/rules/no-restricted-syntax --- .eslintrc | 14 ++++++++++++++ src/core/primitives.js | 3 +++ test/unit/annotation_spec.js | 30 +++++++++++++++--------------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/.eslintrc b/.eslintrc index adbb2fae6..df1226625 100644 --- a/.eslintrc +++ b/.eslintrc @@ -143,6 +143,20 @@ "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0, "maxBOF": 1, }], "no-nested-ternary": "error", "no-new-object": "error", + "no-restricted-syntax": ["error", + { + "selector": "NewExpression[callee.name='Cmd']", + "message": "Use `Cmd.get()` rather than `new Cmd()`.", + }, + { + "selector": "NewExpression[callee.name='Name']", + "message": "Use `Name.get()` rather than `new Name()`.", + }, + { + "selector": "NewExpression[callee.name='Ref']", + "message": "Use `Ref.get()` rather than `new Ref()`.", + }, + ], "no-unneeded-ternary": "error", "spaced-comment": ["error", "always", { "block": { diff --git a/src/core/primitives.js b/src/core/primitives.js index 58ce1c54e..6ec9e4eec 100644 --- a/src/core/primitives.js +++ b/src/core/primitives.js @@ -29,6 +29,7 @@ var Name = (function NameClosure() { Name.get = function Name_get(name) { var nameValue = nameCache[name]; + // eslint-disable-next-line no-restricted-syntax return nameValue ? nameValue : (nameCache[name] = new Name(name)); }; @@ -50,6 +51,7 @@ var Cmd = (function CmdClosure() { Cmd.get = function Cmd_get(cmd) { var cmdValue = cmdCache[cmd]; + // eslint-disable-next-line no-restricted-syntax return cmdValue ? cmdValue : (cmdCache[cmd] = new Cmd(cmd)); }; @@ -195,6 +197,7 @@ var Ref = (function RefClosure() { Ref.get = function(num, gen) { const key = gen === 0 ? `${num}R` : `${num}R${gen}`; const refValue = refCache[key]; + // eslint-disable-next-line no-restricted-syntax return refValue ? refValue : (refCache[key] = new Ref(num, gen)); }; diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index 48b6f48af..1a5a37707 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -461,12 +461,12 @@ describe("annotation", function() { }); it("should parse IRT and set default RT when not defined.", function(done) { - const annotationRef = new Ref(819, 0); + const annotationRef = Ref.get(819, 0); const annotationDict = new Dict(); annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); - const replyRef = new Ref(820, 0); + const replyRef = Ref.get(820, 0); const replyDict = new Dict(); replyDict.set("Type", Name.get("Annot")); replyDict.set("Subtype", Name.get("Text")); @@ -492,7 +492,7 @@ describe("annotation", function() { }); it("should parse IRT/RT for a group type", function(done) { - const annotationRef = new Ref(819, 0); + const annotationRef = Ref.get(819, 0); const annotationDict = new Dict(); annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); @@ -502,14 +502,14 @@ describe("annotation", function() { annotationDict.set("M", "D:20190423"); annotationDict.set("C", [0, 0, 1]); - const popupRef = new Ref(820, 0); + const popupRef = Ref.get(820, 0); const popupDict = new Dict(); popupDict.set("Type", Name.get("Annot")); popupDict.set("Subtype", Name.get("Popup")); popupDict.set("Parent", annotationRef); annotationDict.set("Popup", popupRef); - const replyRef = new Ref(821, 0); + const replyRef = Ref.get(821, 0); const replyDict = new Dict(); replyDict.set("Type", Name.get("Annot")); replyDict.set("Subtype", Name.get("Text")); @@ -549,7 +549,7 @@ describe("annotation", function() { }); it("should parse IRT/RT for a reply type", function(done) { - const annotationRef = new Ref(819, 0); + const annotationRef = Ref.get(819, 0); const annotationDict = new Dict(); annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); @@ -559,14 +559,14 @@ describe("annotation", function() { annotationDict.set("M", "D:20190423"); annotationDict.set("C", [0, 0, 1]); - const popupRef = new Ref(820, 0); + const popupRef = Ref.get(820, 0); const popupDict = new Dict(); popupDict.set("Type", Name.get("Annot")); popupDict.set("Subtype", Name.get("Popup")); popupDict.set("Parent", annotationRef); annotationDict.set("Popup", popupRef); - const replyRef = new Ref(821, 0); + const replyRef = Ref.get(821, 0); const replyDict = new Dict(); replyDict.set("Type", Name.get("Annot")); replyDict.set("Subtype", Name.get("Text")); @@ -608,13 +608,13 @@ describe("annotation", function() { describe("TextAnnotation", function() { it("should not parse state model and state when not defined", function(done) { - const annotationRef = new Ref(819, 0); + const annotationRef = Ref.get(819, 0); const annotationDict = new Dict(); annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); annotationDict.set("Contents", "TestText"); - const replyRef = new Ref(820, 0); + const replyRef = Ref.get(820, 0); const replyDict = new Dict(); replyDict.set("Type", Name.get("Annot")); replyDict.set("Subtype", Name.get("Text")); @@ -642,12 +642,12 @@ describe("annotation", function() { }); it("should correctly parse state model and state when defined", function(done) { - const annotationRef = new Ref(819, 0); + const annotationRef = Ref.get(819, 0); const annotationDict = new Dict(); annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); - const replyRef = new Ref(820, 0); + const replyRef = Ref.get(820, 0); const replyDict = new Dict(); replyDict.set("Type", Name.get("Annot")); replyDict.set("Subtype", Name.get("Text")); @@ -2109,7 +2109,7 @@ describe("annotation", function() { "should correctly inherit Contents from group-master annotation " + "if parent has ReplyType == Group", function(done) { - const annotationRef = new Ref(819, 0); + const annotationRef = Ref.get(819, 0); const annotationDict = new Dict(); annotationDict.set("Type", Name.get("Annot")); annotationDict.set("Subtype", Name.get("Text")); @@ -2118,7 +2118,7 @@ describe("annotation", function() { annotationDict.set("M", "D:20190423"); annotationDict.set("C", [0, 0, 1]); - const replyRef = new Ref(820, 0); + const replyRef = Ref.get(820, 0); const replyDict = new Dict(); replyDict.set("Type", Name.get("Annot")); replyDict.set("Subtype", Name.get("Text")); @@ -2129,7 +2129,7 @@ describe("annotation", function() { replyDict.set("M", "D:20190523"); replyDict.set("C", [0.4]); - const popupRef = new Ref(821, 0); + const popupRef = Ref.get(821, 0); const popupDict = new Dict(); popupDict.set("Type", Name.get("Annot")); popupDict.set("Subtype", Name.get("Popup"));