Fail gracefully if |parameter| object has an invalid array topology

AudioWorkletProcessor::CopyParamValueMapToObject() assumed the array
topology is always valid (DCHECK), but if the user code actively mangles
the parameter descriptor getter the array to return invalid content
this assumption becomes invalid.

The fix is to fail gracefully when the object type or the array content
is not correct.

Bug: 1151069
Test: The repro case does not reproduce any more after 1 hour run.
Change-Id: I3f8decd3721e9b00ba201e2f76751e4bc941e05d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2569788
Reviewed-by: Raymond Toy <rtoy@chromium.org>
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834503}
diff --git a/webaudio/the-audio-api/the-audioworklet-interface/audioworkletprocessor-param-getter-overridden.https.html b/webaudio/the-audio-api/the-audioworklet-interface/audioworkletprocessor-param-getter-overridden.https.html
new file mode 100644
index 0000000..e3fb6e5
--- /dev/null
+++ b/webaudio/the-audio-api/the-audioworklet-interface/audioworkletprocessor-param-getter-overridden.https.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test if AudioWorkletProcessor with invalid parameters array getter
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let audit = Audit.createTaskRunner();
+
+      // Arbitrarily determined. Any numbers should work.
+      let sampleRate = 16000;
+      let renderLength = 1280;
+      let context;
+      let filePath = 'processors/invalid-param-array-processor.js';
+
+      audit.define('Initializing AudioWorklet and Context', async (task) => {
+        context = new OfflineAudioContext(1, renderLength, sampleRate);
+        await context.audioWorklet.addModule(filePath);
+        task.done();
+      });
+
+      audit.define('Verifying AudioParam in AudioWorkletNode',
+          async (task, should) => {
+            let buffer = context.createBuffer(1, 2, context.sampleRate);
+            buffer.getChannelData(0)[0] = 1;
+
+            let source = new AudioBufferSourceNode(context);
+            source.buffer = buffer;
+            source.loop = true;
+            source.start();
+
+            let workletNode1 =
+                new AudioWorkletNode(context, 'invalid-param-array-1');
+            let workletNode2 =
+                new AudioWorkletNode(context, 'invalid-param-array-2');
+            workletNode1.connect(workletNode2).connect(context.destination);
+
+            // Manually invoke the param getter.
+            source.connect(workletNode2.parameters.get('invalidParam'));
+
+            const renderedBuffer = await context.startRendering();
+
+            // |workletNode2| should be no-op after the parameter getter is
+            // invoked. Therefore, the rendered result should be silent.
+            should(renderedBuffer.getChannelData(0), 'The rendered buffer')
+                .beConstantValueOf(0);
+            task.done();
+          }
+      );
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/webaudio/the-audio-api/the-audioworklet-interface/processors/invalid-param-array-processor.js b/webaudio/the-audio-api/the-audioworklet-interface/processors/invalid-param-array-processor.js
new file mode 100644
index 0000000..e4a5dc3
--- /dev/null
+++ b/webaudio/the-audio-api/the-audioworklet-interface/processors/invalid-param-array-processor.js
@@ -0,0 +1,47 @@
+/**
+ * @class InvalidParamArrayProcessor
+ * @extends AudioWorkletProcessor
+ *
+ * This processor intentionally returns an array with an invalid size when the
+ * processor's getter is queried.
+ */
+let singleton = undefined;
+let secondFetch = false;
+let useDescriptor = false;
+let processCounter = 0;
+
+class InvalidParamArrayProcessor extends AudioWorkletProcessor {
+  static get parameterDescriptors() {
+    if (useDescriptor)
+      return [{name: 'invalidParam'}];
+    useDescriptor = true;
+    return [];
+  }
+
+  constructor() {
+    super();
+    if (singleton === undefined)
+      singleton = this;
+    return singleton;
+  }
+
+  process(inputs, outputs, parameters) {
+    const output = outputs[0];
+    for (let channel = 0; channel < output.length; ++channel)
+      output[channel].fill(1);
+    return false;
+  }
+}
+
+// This overridden getter is invoked under the hood before process() gets
+// called. After this gets called, process() method above will be invalidated,
+// and mark the worklet node non-functional. (i.e. in an error state)
+Object.defineProperty(Object.prototype, 'invalidParam', {'get': () => {
+  if (secondFetch)
+    return new Float32Array(256);
+  secondFetch = true;
+  return new Float32Array(128);
+}});
+
+registerProcessor('invalid-param-array-1', InvalidParamArrayProcessor);
+registerProcessor('invalid-param-array-2', InvalidParamArrayProcessor);