From e69e8622a9ff67d9aa90e6ca655c1dde0847b74f Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Fri, 26 Feb 2021 13:15:07 +0100
Subject: [PATCH] Convert code in `src/core/function.js` to use "normal"
 classes

All of this code predates the existence of native JS classes, however we can now clean this up a bit. This patch thus let us remove some variable "shadowing" from the code.
---
 src/core/function.js | 682 ++++++++++++++++++++++---------------------
 1 file changed, 353 insertions(+), 329 deletions(-)

diff --git a/src/core/function.js b/src/core/function.js
index 4dcee828e..a1f05114d 100644
--- a/src/core/function.js
+++ b/src/core/function.js
@@ -635,26 +635,28 @@ var PostScriptStack = (function PostScriptStackClosure() {
   var MAX_STACK_SIZE = 100;
 
   // eslint-disable-next-line no-shadow
-  function PostScriptStack(initialStack) {
-    this.stack = !initialStack
-      ? []
-      : Array.prototype.slice.call(initialStack, 0);
-  }
+  class PostScriptStack {
+    constructor(initialStack) {
+      this.stack = !initialStack
+        ? []
+        : Array.prototype.slice.call(initialStack, 0);
+    }
 
-  PostScriptStack.prototype = {
-    push: function PostScriptStack_push(value) {
+    push(value) {
       if (this.stack.length >= MAX_STACK_SIZE) {
         throw new Error("PostScript function stack overflow.");
       }
       this.stack.push(value);
-    },
-    pop: function PostScriptStack_pop() {
+    }
+
+    pop() {
       if (this.stack.length <= 0) {
         throw new Error("PostScript function stack underflow.");
       }
       return this.stack.pop();
-    },
-    copy: function PostScriptStack_copy(n) {
+    }
+
+    copy(n) {
       if (this.stack.length + n >= MAX_STACK_SIZE) {
         throw new Error("PostScript function stack overflow.");
       }
@@ -662,12 +664,14 @@ var PostScriptStack = (function PostScriptStackClosure() {
       for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
         stack.push(stack[i]);
       }
-    },
-    index: function PostScriptStack_index(n) {
+    }
+
+    index(n) {
       this.push(this.stack[this.stack.length - n - 1]);
-    },
+    }
+
     // rotate the last n stack elements p times
-    roll: function PostScriptStack_roll(n, p) {
+    roll(n, p) {
       var stack = this.stack;
       var l = stack.length - n;
       var r = stack.length - 1,
@@ -690,246 +694,245 @@ var PostScriptStack = (function PostScriptStackClosure() {
         stack[i] = stack[j];
         stack[j] = t;
       }
-    },
-  };
+    }
+  }
+
   return PostScriptStack;
 })();
-var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
-  // eslint-disable-next-line no-shadow
-  function PostScriptEvaluator(operators) {
+
+class PostScriptEvaluator {
+  constructor(operators) {
     this.operators = operators;
   }
-  PostScriptEvaluator.prototype = {
-    execute: function PostScriptEvaluator_execute(initialStack) {
-      var stack = new PostScriptStack(initialStack);
-      var counter = 0;
-      var operators = this.operators;
-      var length = operators.length;
-      var operator, a, b;
-      while (counter < length) {
-        operator = operators[counter++];
-        if (typeof operator === "number") {
-          // Operator is really an operand and should be pushed to the stack.
-          stack.push(operator);
-          continue;
-        }
-        switch (operator) {
-          // non standard ps operators
-          case "jz": // jump if false
-            b = stack.pop();
-            a = stack.pop();
-            if (!a) {
-              counter = b;
-            }
-            break;
-          case "j": // jump
-            a = stack.pop();
-            counter = a;
-            break;
 
-          // all ps operators in alphabetical order (excluding if/ifelse)
-          case "abs":
-            a = stack.pop();
-            stack.push(Math.abs(a));
-            break;
-          case "add":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a + b);
-            break;
-          case "and":
-            b = stack.pop();
-            a = stack.pop();
-            if (isBool(a) && isBool(b)) {
-              stack.push(a && b);
-            } else {
-              stack.push(a & b);
-            }
-            break;
-          case "atan":
-            a = stack.pop();
-            stack.push(Math.atan(a));
-            break;
-          case "bitshift":
-            b = stack.pop();
-            a = stack.pop();
-            if (a > 0) {
-              stack.push(a << b);
-            } else {
-              stack.push(a >> b);
-            }
-            break;
-          case "ceiling":
-            a = stack.pop();
-            stack.push(Math.ceil(a));
-            break;
-          case "copy":
-            a = stack.pop();
-            stack.copy(a);
-            break;
-          case "cos":
-            a = stack.pop();
-            stack.push(Math.cos(a));
-            break;
-          case "cvi":
-            a = stack.pop() | 0;
-            stack.push(a);
-            break;
-          case "cvr":
-            // noop
-            break;
-          case "div":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a / b);
-            break;
-          case "dup":
-            stack.copy(1);
-            break;
-          case "eq":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a === b);
-            break;
-          case "exch":
-            stack.roll(2, 1);
-            break;
-          case "exp":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a ** b);
-            break;
-          case "false":
-            stack.push(false);
-            break;
-          case "floor":
-            a = stack.pop();
-            stack.push(Math.floor(a));
-            break;
-          case "ge":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a >= b);
-            break;
-          case "gt":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a > b);
-            break;
-          case "idiv":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push((a / b) | 0);
-            break;
-          case "index":
-            a = stack.pop();
-            stack.index(a);
-            break;
-          case "le":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a <= b);
-            break;
-          case "ln":
-            a = stack.pop();
-            stack.push(Math.log(a));
-            break;
-          case "log":
-            a = stack.pop();
-            stack.push(Math.log(a) / Math.LN10);
-            break;
-          case "lt":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a < b);
-            break;
-          case "mod":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a % b);
-            break;
-          case "mul":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a * b);
-            break;
-          case "ne":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a !== b);
-            break;
-          case "neg":
-            a = stack.pop();
-            stack.push(-a);
-            break;
-          case "not":
-            a = stack.pop();
-            if (isBool(a)) {
-              stack.push(!a);
-            } else {
-              stack.push(~a);
-            }
-            break;
-          case "or":
-            b = stack.pop();
-            a = stack.pop();
-            if (isBool(a) && isBool(b)) {
-              stack.push(a || b);
-            } else {
-              stack.push(a | b);
-            }
-            break;
-          case "pop":
-            stack.pop();
-            break;
-          case "roll":
-            b = stack.pop();
-            a = stack.pop();
-            stack.roll(a, b);
-            break;
-          case "round":
-            a = stack.pop();
-            stack.push(Math.round(a));
-            break;
-          case "sin":
-            a = stack.pop();
-            stack.push(Math.sin(a));
-            break;
-          case "sqrt":
-            a = stack.pop();
-            stack.push(Math.sqrt(a));
-            break;
-          case "sub":
-            b = stack.pop();
-            a = stack.pop();
-            stack.push(a - b);
-            break;
-          case "true":
-            stack.push(true);
-            break;
-          case "truncate":
-            a = stack.pop();
-            a = a < 0 ? Math.ceil(a) : Math.floor(a);
-            stack.push(a);
-            break;
-          case "xor":
-            b = stack.pop();
-            a = stack.pop();
-            if (isBool(a) && isBool(b)) {
-              stack.push(a !== b);
-            } else {
-              stack.push(a ^ b);
-            }
-            break;
-          default:
-            throw new FormatError(`Unknown operator ${operator}`);
-        }
+  execute(initialStack) {
+    var stack = new PostScriptStack(initialStack);
+    var counter = 0;
+    var operators = this.operators;
+    var length = operators.length;
+    var operator, a, b;
+    while (counter < length) {
+      operator = operators[counter++];
+      if (typeof operator === "number") {
+        // Operator is really an operand and should be pushed to the stack.
+        stack.push(operator);
+        continue;
       }
-      return stack.stack;
-    },
-  };
-  return PostScriptEvaluator;
-})();
+      switch (operator) {
+        // non standard ps operators
+        case "jz": // jump if false
+          b = stack.pop();
+          a = stack.pop();
+          if (!a) {
+            counter = b;
+          }
+          break;
+        case "j": // jump
+          a = stack.pop();
+          counter = a;
+          break;
+
+        // all ps operators in alphabetical order (excluding if/ifelse)
+        case "abs":
+          a = stack.pop();
+          stack.push(Math.abs(a));
+          break;
+        case "add":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a + b);
+          break;
+        case "and":
+          b = stack.pop();
+          a = stack.pop();
+          if (isBool(a) && isBool(b)) {
+            stack.push(a && b);
+          } else {
+            stack.push(a & b);
+          }
+          break;
+        case "atan":
+          a = stack.pop();
+          stack.push(Math.atan(a));
+          break;
+        case "bitshift":
+          b = stack.pop();
+          a = stack.pop();
+          if (a > 0) {
+            stack.push(a << b);
+          } else {
+            stack.push(a >> b);
+          }
+          break;
+        case "ceiling":
+          a = stack.pop();
+          stack.push(Math.ceil(a));
+          break;
+        case "copy":
+          a = stack.pop();
+          stack.copy(a);
+          break;
+        case "cos":
+          a = stack.pop();
+          stack.push(Math.cos(a));
+          break;
+        case "cvi":
+          a = stack.pop() | 0;
+          stack.push(a);
+          break;
+        case "cvr":
+          // noop
+          break;
+        case "div":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a / b);
+          break;
+        case "dup":
+          stack.copy(1);
+          break;
+        case "eq":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a === b);
+          break;
+        case "exch":
+          stack.roll(2, 1);
+          break;
+        case "exp":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a ** b);
+          break;
+        case "false":
+          stack.push(false);
+          break;
+        case "floor":
+          a = stack.pop();
+          stack.push(Math.floor(a));
+          break;
+        case "ge":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a >= b);
+          break;
+        case "gt":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a > b);
+          break;
+        case "idiv":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push((a / b) | 0);
+          break;
+        case "index":
+          a = stack.pop();
+          stack.index(a);
+          break;
+        case "le":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a <= b);
+          break;
+        case "ln":
+          a = stack.pop();
+          stack.push(Math.log(a));
+          break;
+        case "log":
+          a = stack.pop();
+          stack.push(Math.log(a) / Math.LN10);
+          break;
+        case "lt":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a < b);
+          break;
+        case "mod":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a % b);
+          break;
+        case "mul":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a * b);
+          break;
+        case "ne":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a !== b);
+          break;
+        case "neg":
+          a = stack.pop();
+          stack.push(-a);
+          break;
+        case "not":
+          a = stack.pop();
+          if (isBool(a)) {
+            stack.push(!a);
+          } else {
+            stack.push(~a);
+          }
+          break;
+        case "or":
+          b = stack.pop();
+          a = stack.pop();
+          if (isBool(a) && isBool(b)) {
+            stack.push(a || b);
+          } else {
+            stack.push(a | b);
+          }
+          break;
+        case "pop":
+          stack.pop();
+          break;
+        case "roll":
+          b = stack.pop();
+          a = stack.pop();
+          stack.roll(a, b);
+          break;
+        case "round":
+          a = stack.pop();
+          stack.push(Math.round(a));
+          break;
+        case "sin":
+          a = stack.pop();
+          stack.push(Math.sin(a));
+          break;
+        case "sqrt":
+          a = stack.pop();
+          stack.push(Math.sqrt(a));
+          break;
+        case "sub":
+          b = stack.pop();
+          a = stack.pop();
+          stack.push(a - b);
+          break;
+        case "true":
+          stack.push(true);
+          break;
+        case "truncate":
+          a = stack.pop();
+          a = a < 0 ? Math.ceil(a) : Math.floor(a);
+          stack.push(a);
+          break;
+        case "xor":
+          b = stack.pop();
+          a = stack.pop();
+          if (isBool(a) && isBool(b)) {
+            stack.push(a !== b);
+          } else {
+            stack.push(a ^ b);
+          }
+          break;
+        default:
+          throw new FormatError(`Unknown operator ${operator}`);
+      }
+    }
+    return stack.stack;
+  }
+}
 
 // Most of the PDFs functions consist of simple operations such as:
 //   roll, exch, sub, cvr, pop, index, dup, mul, if, gt, add.
@@ -938,84 +941,100 @@ var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
 // optimize some expressions using basic math properties. Keeping track of
 // min/max values will allow us to avoid extra Math.min/Math.max calls.
 var PostScriptCompiler = (function PostScriptCompilerClosure() {
-  function AstNode(type) {
-    this.type = type;
-  }
-  AstNode.prototype.visit = function (visitor) {
-    unreachable("abstract method");
-  };
+  class AstNode {
+    constructor(type) {
+      this.type = type;
+    }
 
-  function AstArgument(index, min, max) {
-    AstNode.call(this, "args");
-    this.index = index;
-    this.min = min;
-    this.max = max;
+    visit(visitor) {
+      unreachable("abstract method");
+    }
   }
-  AstArgument.prototype = Object.create(AstNode.prototype);
-  AstArgument.prototype.visit = function (visitor) {
-    visitor.visitArgument(this);
-  };
 
-  function AstLiteral(number) {
-    AstNode.call(this, "literal");
-    this.number = number;
-    this.min = number;
-    this.max = number;
-  }
-  AstLiteral.prototype = Object.create(AstNode.prototype);
-  AstLiteral.prototype.visit = function (visitor) {
-    visitor.visitLiteral(this);
-  };
+  class AstArgument extends AstNode {
+    constructor(index, min, max) {
+      super("args");
+      this.index = index;
+      this.min = min;
+      this.max = max;
+    }
 
-  function AstBinaryOperation(op, arg1, arg2, min, max) {
-    AstNode.call(this, "binary");
-    this.op = op;
-    this.arg1 = arg1;
-    this.arg2 = arg2;
-    this.min = min;
-    this.max = max;
+    visit(visitor) {
+      visitor.visitArgument(this);
+    }
   }
-  AstBinaryOperation.prototype = Object.create(AstNode.prototype);
-  AstBinaryOperation.prototype.visit = function (visitor) {
-    visitor.visitBinaryOperation(this);
-  };
 
-  function AstMin(arg, max) {
-    AstNode.call(this, "max");
-    this.arg = arg;
-    this.min = arg.min;
-    this.max = max;
-  }
-  AstMin.prototype = Object.create(AstNode.prototype);
-  AstMin.prototype.visit = function (visitor) {
-    visitor.visitMin(this);
-  };
+  class AstLiteral extends AstNode {
+    constructor(number) {
+      super("literal");
+      this.number = number;
+      this.min = number;
+      this.max = number;
+    }
 
-  function AstVariable(index, min, max) {
-    AstNode.call(this, "var");
-    this.index = index;
-    this.min = min;
-    this.max = max;
+    visit(visitor) {
+      visitor.visitLiteral(this);
+    }
   }
-  AstVariable.prototype = Object.create(AstNode.prototype);
-  AstVariable.prototype.visit = function (visitor) {
-    visitor.visitVariable(this);
-  };
 
-  function AstVariableDefinition(variable, arg) {
-    AstNode.call(this, "definition");
-    this.variable = variable;
-    this.arg = arg;
-  }
-  AstVariableDefinition.prototype = Object.create(AstNode.prototype);
-  AstVariableDefinition.prototype.visit = function (visitor) {
-    visitor.visitVariableDefinition(this);
-  };
+  class AstBinaryOperation extends AstNode {
+    constructor(op, arg1, arg2, min, max) {
+      super("binary");
+      this.op = op;
+      this.arg1 = arg1;
+      this.arg2 = arg2;
+      this.min = min;
+      this.max = max;
+    }
 
-  function ExpressionBuilderVisitor() {
-    this.parts = [];
+    visit(visitor) {
+      visitor.visitBinaryOperation(this);
+    }
   }
-  ExpressionBuilderVisitor.prototype = {
+
+  class AstMin extends AstNode {
+    constructor(arg, max) {
+      super("max");
+      this.arg = arg;
+      this.min = arg.min;
+      this.max = max;
+    }
+
+    visit(visitor) {
+      visitor.visitMin(this);
+    }
+  }
+
+  class AstVariable extends AstNode {
+    constructor(index, min, max) {
+      super("var");
+      this.index = index;
+      this.min = min;
+      this.max = max;
+    }
+
+    visit(visitor) {
+      visitor.visitVariable(this);
+    }
+  }
+
+  class AstVariableDefinition extends AstNode {
+    constructor(variable, arg) {
+      super("definition");
+      this.variable = variable;
+      this.arg = arg;
+    }
+
+    visit(visitor) {
+      visitor.visitVariableDefinition(this);
+    }
+  }
+
+  class ExpressionBuilderVisitor {
+    constructor() {
+      this.parts = [];
+    }
+
     visitArgument(arg) {
       this.parts.push(
         "Math.max(",
@@ -1026,36 +1045,42 @@ var PostScriptCompiler = (function PostScriptCompilerClosure() {
         arg.index,
         "]))"
       );
-    },
+    }
+
     visitVariable(variable) {
       this.parts.push("v", variable.index);
-    },
+    }
+
     visitLiteral(literal) {
       this.parts.push(literal.number);
-    },
+    }
+
     visitBinaryOperation(operation) {
       this.parts.push("(");
       operation.arg1.visit(this);
       this.parts.push(" ", operation.op, " ");
       operation.arg2.visit(this);
       this.parts.push(")");
-    },
+    }
+
     visitVariableDefinition(definition) {
       this.parts.push("var ");
       definition.variable.visit(this);
       this.parts.push(" = ");
       definition.arg.visit(this);
       this.parts.push(";");
-    },
+    }
+
     visitMin(max) {
       this.parts.push("Math.min(");
       max.arg.visit(this);
       this.parts.push(", ", max.max, ")");
-    },
+    }
+
     toString() {
       return this.parts.join("");
-    },
-  };
+    }
+  }
 
   function buildAddOperation(num1, num2) {
     if (num2.type === "literal" && num2.number === 0) {
@@ -1156,9 +1181,8 @@ var PostScriptCompiler = (function PostScriptCompilerClosure() {
   }
 
   // eslint-disable-next-line no-shadow
-  function PostScriptCompiler() {}
-  PostScriptCompiler.prototype = {
-    compile: function PostScriptCompiler_compile(code, domain, range) {
+  class PostScriptCompiler {
+    compile(code, domain, range) {
       var stack = [];
       var instructions = [];
       var inputSize = domain.length >> 1,
@@ -1337,8 +1361,8 @@ var PostScriptCompiler = (function PostScriptCompilerClosure() {
         result.push(out.join(""));
       });
       return result.join("\n");
-    },
-  };
+    }
+  }
 
   return PostScriptCompiler;
 })();