@adguard/agtree
Version:
Tool set for working with adblock filter lists
197 lines (194 loc) • 8.07 kB
JavaScript
/*
* AGTree v3.2.2 (build date: Tue, 08 Jul 2025 13:39:47 GMT)
* (c) 2025 Adguard Software Ltd.
* Released under the MIT license
* https://github.com/AdguardTeam/tsurlfilter/tree/master/packages/agtree#readme
*/
import { VariableNodeBinaryPropMarshallingMap, ParenthesisNodeBinaryPropMarshallingMap, OperatorNodeBinaryPropMarshallingMap, KNOWN_VARIABLES_SERIALIZATION_MAP, LOGICAL_EXPRESSION_OPERATOR_SERIALISATION_MAP } from '../../marshalling-utils/misc/logical-expression-common.js';
import { NodeType } from '../../serializer/misc/logical-expression-serializer.js';
import { NULL } from '../../utils/constants.js';
import { isUndefined } from '../../utils/type-guards.js';
import { BaseDeserializer } from '../base-deserializer.js';
import { BinaryTypeMarshallingMap } from '../../marshalling-utils/misc/binary-type-common.js';
let logicalExpressionOperatorMarshallingMapReverse;
const getOperatorBinaryMapReverse = () => {
if (!logicalExpressionOperatorMarshallingMapReverse) {
logicalExpressionOperatorMarshallingMapReverse = new Map(Array.from(LOGICAL_EXPRESSION_OPERATOR_SERIALISATION_MAP).map(([key, value]) => [value, key]));
}
return logicalExpressionOperatorMarshallingMapReverse;
};
/**
* Gets the string representation of the operator from the binary representation.
*
* @param binary Binary representation of the operator
* @returns String representation of the operator
* @throws If the operator is unknown
*/
const getOperatorOrFail = (binary) => {
const operator = getOperatorBinaryMapReverse().get(binary);
if (isUndefined(operator)) {
throw new Error(`Unknown operator: ${binary}`);
}
return operator;
};
/**
* Deserialization map for known variables.
*/
let knownVariablesMapReverse;
const getKnownVariablesMapReverse = () => {
if (!knownVariablesMapReverse) {
knownVariablesMapReverse = new Map(Array.from(KNOWN_VARIABLES_SERIALIZATION_MAP).map(([key, value]) => [value, key]));
}
return knownVariablesMapReverse;
};
/**
* Gets the frequent name of the variable from the binary representation.
*
* @param binary Binary representation of the variable
* @returns Frequent name of the variable
* @throws If the variable is unknown
*/
const getFrequentNameOrFail = (binary) => {
const name = getKnownVariablesMapReverse().get(binary);
if (isUndefined(name)) {
throw new Error(`Unknown frequent name: ${binary}`);
}
return name;
};
/**
* `LogicalExpressionDeserializer` is responsible for deserializing logical expressions.
*
* @example
* From the following rule:
* ```adblock
* !#if (adguard_ext_android_cb || adguard_ext_safari)
* ```
* this parser will parse the expression `(adguard_ext_android_cb || adguard_ext_safari)`.
*/
// TODO: Refactor this class
class LogicalExpressionDeserializer extends BaseDeserializer {
/**
* Deserializes a variable node from binary format.
*
* @param buffer ByteBuffer for reading binary data.
* @param node Destination node.
* @throws If the binary data is malformed.
*/
static deserializeVariableNode(buffer, node) {
buffer.assertUint8(BinaryTypeMarshallingMap.ExpressionVariableNode);
node.type = NodeType.Variable;
let prop = buffer.readUint8();
while (prop !== NULL) {
switch (prop) {
case VariableNodeBinaryPropMarshallingMap.Name:
node.name = buffer.readString();
break;
case VariableNodeBinaryPropMarshallingMap.FrequentName:
node.name = getFrequentNameOrFail(buffer.readUint8());
break;
case VariableNodeBinaryPropMarshallingMap.Start:
node.start = buffer.readUint32();
break;
case VariableNodeBinaryPropMarshallingMap.End:
node.end = buffer.readUint32();
break;
default:
throw new Error(`Invalid property: ${prop}`);
}
prop = buffer.readUint8();
}
}
/**
* Deserializes a parenthesis node from binary format.
*
* @param buffer ByteBuffer for reading binary data.
* @param node Destination node.
* @throws If the binary data is malformed.
*/
static deserializeParenthesisNode(buffer, node) {
buffer.assertUint8(BinaryTypeMarshallingMap.ExpressionParenthesisNode);
node.type = NodeType.Parenthesis;
let prop = buffer.readUint8();
while (prop !== NULL) {
switch (prop) {
case ParenthesisNodeBinaryPropMarshallingMap.Expression:
LogicalExpressionDeserializer.deserialize(buffer, node.expression = {});
break;
case ParenthesisNodeBinaryPropMarshallingMap.Start:
node.start = buffer.readUint32();
break;
case ParenthesisNodeBinaryPropMarshallingMap.End:
node.end = buffer.readUint32();
break;
default:
throw new Error(`Invalid property: ${prop}`);
}
prop = buffer.readUint8();
}
}
/**
* Deserializes an operator node from binary format.
*
* @param buffer ByteBuffer for reading binary data.
* @param node Destination node.
* @throws If the binary data is malformed.
*/
static deserializeOperatorNode(buffer, node) {
buffer.assertUint8(BinaryTypeMarshallingMap.ExpressionOperatorNode);
node.type = NodeType.Operator;
let prop = buffer.readUint8();
while (prop !== NULL) {
switch (prop) {
case OperatorNodeBinaryPropMarshallingMap.Operator:
node.operator = getOperatorOrFail(buffer.readUint8());
break;
case OperatorNodeBinaryPropMarshallingMap.Left:
LogicalExpressionDeserializer.deserialize(buffer, node.left = {});
break;
case OperatorNodeBinaryPropMarshallingMap.Right:
LogicalExpressionDeserializer.deserialize(buffer, node.right = {});
break;
case OperatorNodeBinaryPropMarshallingMap.Start:
node.start = buffer.readUint32();
break;
case OperatorNodeBinaryPropMarshallingMap.End:
node.end = buffer.readUint32();
break;
default:
throw new Error(`Invalid property: ${prop}`);
}
prop = buffer.readUint8();
}
}
/**
* Deserializes a logical expression node from binary format.
*
* @param buffer ByteBuffer for reading binary data.
* @param node Destination node.
* @throws If the binary data is malformed.
*/
static deserialize(buffer, node) {
// note: we just do a simple lookahead here, because advancing the buffer is done in the
// 'sub-deserialize' methods
let type = buffer.peekUint8();
while (type !== NULL) {
switch (type) {
case BinaryTypeMarshallingMap.ExpressionVariableNode:
LogicalExpressionDeserializer.deserializeVariableNode(buffer, node);
break;
case BinaryTypeMarshallingMap.ExpressionOperatorNode:
LogicalExpressionDeserializer.deserializeOperatorNode(buffer, node);
break;
case BinaryTypeMarshallingMap.ExpressionParenthesisNode:
LogicalExpressionDeserializer.deserializeParenthesisNode(buffer, node);
break;
default:
throw new Error(`Unexpected node type: ${type}`);
}
type = buffer.peekUint8();
}
// consume NULL
buffer.readUint8();
}
}
export { LogicalExpressionDeserializer, getFrequentNameOrFail };