* @param {!ee.api.ValueNode} value * @param {number} depth * @return {!ee.api.ValueNode}
(value, depth)
| 556 | * @return {!ee.api.ValueNode} |
| 557 | */ |
| 558 | optimizeValue(value, depth) { |
| 559 | // Don't generate very deep expressions, as the backend rejects them. |
| 560 | // The backend's limit is 100, and we want to stay well away from that |
| 561 | // as a few extra levels of wrapping are always added. |
| 562 | const DEPTH_LIMIT = 50; |
| 563 | |
| 564 | const isConst = (v) => v.constantValue !== null; |
| 565 | const serializeConst = (v) => v === ee.apiclient.NULL_VALUE ? null : v; |
| 566 | // Ensure that any derived ValueNode from a parent node is associated with |
| 567 | // its parent's source frame, if available, in order to make sure that the |
| 568 | // final, top-level ValueNodes in the expression are contained in the |
| 569 | // sourceNodeMap. If the optimizer encounters duplicate ValueNodes, it will |
| 570 | // retain the reference to the first one found. |
| 571 | const storeInSourceMap = (parentValue, valueNode) => { |
| 572 | if (this.sourceNodeMap && this.sourceNodeMap.has(parentValue) && |
| 573 | !this.sourceNodeMap.has(valueNode)) { |
| 574 | this.sourceNodeMap.set(valueNode, this.sourceNodeMap.get(parentValue)); |
| 575 | } |
| 576 | return valueNode; |
| 577 | }; |
| 578 | |
| 579 | if (isConst(value) || value.integerValue != null || |
| 580 | value.bytesValue != null || value.argumentReference != null) { |
| 581 | return value; |
| 582 | } else if (value.valueReference != null) { |
| 583 | const referencedValue = this.values[value.valueReference]; |
| 584 | |
| 585 | if (this.referenceCounts === null || (depth < DEPTH_LIMIT && |
| 586 | this.referenceCounts[value.valueReference] === 1)) { |
| 587 | const optimized = this.optimizeValue(referencedValue, depth); |
| 588 | return storeInSourceMap(value, optimized); |
| 589 | } else if (ExpressionOptimizer.isAlwaysLiftable(referencedValue)) { |
| 590 | return storeInSourceMap(value, referencedValue); |
| 591 | } else { |
| 592 | const optimized = |
| 593 | ee.rpc_node.reference(this.optimizeReference(value.valueReference)); |
| 594 | return storeInSourceMap(value, optimized); |
| 595 | } |
| 596 | } else if (value.arrayValue != null) { |
| 597 | const arr = value.arrayValue.values.map( |
| 598 | v => this.optimizeValue(v, depth + 3)); |
| 599 | const optimized = |
| 600 | (arr.every(isConst) ? ee.rpc_node.constant(arr.map( |
| 601 | v => serializeConst(v.constantValue))) : |
| 602 | ee.rpc_node.array(arr)); |
| 603 | return storeInSourceMap(value, optimized); |
| 604 | } else if (value.dictionaryValue != null) { |
| 605 | const values = {}; |
| 606 | let constantValues = {}; |
| 607 | for (const [k, v] of Object.entries(value.dictionaryValue.values || {})) { |
| 608 | values[k] = this.optimizeValue( |
| 609 | /** @type {!ee.api.ValueNode} */ (v), depth + 3); |
| 610 | if (constantValues !== null && isConst(values[k])) { |
| 611 | constantValues[k] = serializeConst(values[k].constantValue); |
| 612 | } else { |
| 613 | constantValues = null; |
| 614 | } |
| 615 | } |
no test coverage detected