@bufbuild/cel
Version:
A CEL evaluator for ECMAScript
205 lines (204 loc) • 9.8 kB
JavaScript
;
// Copyright 2024-2025 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.isOverflowInt = isOverflowInt;
exports.isOverflowIntNum = isOverflowIntNum;
exports.isOverflowUint = isOverflowUint;
exports.isOverflowUintNum = isOverflowUintNum;
exports.addMath = addMath;
const protobuf_1 = require("@bufbuild/protobuf");
const wkt_1 = require("@bufbuild/protobuf/wkt");
const func_js_1 = require("../func.js");
const opc = require("../gen/dev/cel/expr/operator_const.js");
const type_js_1 = require("../type.js");
const list_js_1 = require("../list.js");
const uint_js_1 = require("../uint.js");
const duration_js_1 = require("../duration.js");
const timestamp_js_1 = require("../timestamp.js");
const MAX_INT = 9223372036854775807n;
// biome-ignore lint/correctness/noPrecisionLoss: No symbol exists in the std.
const MAX_INT_NUM = 9223372036854775807.0;
const MIN_INT = -9223372036854775808n;
// biome-ignore lint/correctness/noPrecisionLoss: No symbol exists in the std.
const MIN_INT_NUM = -9223372036854775808.0;
const MAX_UINT = 18446744073709551615n;
// biome-ignore lint/correctness/noPrecisionLoss: No symbol exists in the std.
const MAX_UINT_NUM = 18446744073709551616.0;
const MIN_UINT = 0n;
const MIN_UINT_NUM = 0.0;
function isOverflowInt(val) {
return val < MIN_INT || val > MAX_INT;
}
function isOverflowIntNum(val) {
return Number.isNaN(val) || val <= MIN_INT_NUM || val >= MAX_INT_NUM;
}
function isOverflowUint(val) {
return val < MIN_UINT || val > MAX_UINT;
}
function isOverflowUintNum(val) {
return Number.isNaN(val) || val < MIN_UINT_NUM || val > MAX_UINT_NUM;
}
function addMath(funcs) {
funcs.add(add);
funcs.add(subtract);
funcs.add(multiply);
funcs.add(divide);
funcs.add(modulo);
funcs.add(negate);
}
function addTimestamp(lhs, rhs) {
let seconds = lhs.message.seconds + rhs.message.seconds;
let nanos = lhs.message.nanos + rhs.message.nanos;
if (nanos > 999999999) {
seconds += BigInt(Math.floor(nanos / 1000000000));
nanos = nanos % 1000000000;
}
if (seconds > 253402300799 || seconds < -62135596800) {
throw overflow(opc.ADD, type_js_1.TIMESTAMP);
}
return (0, protobuf_1.create)(wkt_1.TimestampSchema, { seconds: seconds, nanos: nanos });
}
function addDuration(lhs, rhs) {
let seconds = lhs.message.seconds + rhs.message.seconds;
let nanos = lhs.message.nanos + rhs.message.nanos;
if (nanos > 999999999) {
seconds += BigInt(Math.floor(nanos / 1000000000));
nanos = nanos % 1000000000;
}
if (seconds > 315576000000 || seconds < -315576000000) {
throw overflow(opc.ADD, type_js_1.DURATION);
}
return (0, protobuf_1.create)(wkt_1.DurationSchema, { seconds: seconds, nanos: nanos });
}
function subtractDurationOrTimestamp(lhs, rhs) {
return (0, duration_js_1.createDuration)(lhs.message.seconds - rhs.message.seconds, lhs.message.nanos - rhs.message.nanos);
}
const add = (0, func_js_1.celFunc)(opc.ADD, [
(0, func_js_1.celOverload)([type_js_1.CelScalar.INT, type_js_1.CelScalar.INT], type_js_1.CelScalar.INT, (lhs, rhs) => {
const val = lhs + rhs;
if (isOverflowInt(val)) {
throw overflow(opc.SUBTRACT, type_js_1.CelScalar.INT);
}
return val;
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.UINT, type_js_1.CelScalar.UINT], type_js_1.CelScalar.UINT, (lhs, rhs) => {
const val = lhs.value + rhs.value;
if (isOverflowUint(val)) {
throw overflow(opc.SUBTRACT, type_js_1.CelScalar.UINT);
}
return (0, uint_js_1.celUint)(val);
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.DOUBLE, type_js_1.CelScalar.DOUBLE], type_js_1.CelScalar.DOUBLE, (lhs, rhs) => lhs + rhs),
(0, func_js_1.celOverload)([type_js_1.CelScalar.STRING, type_js_1.CelScalar.STRING], type_js_1.CelScalar.STRING, (lhs, rhs) => lhs + rhs),
(0, func_js_1.celOverload)([type_js_1.CelScalar.BYTES, type_js_1.CelScalar.BYTES], type_js_1.CelScalar.BYTES, (lhs, rhs) => {
const val = new Uint8Array(lhs.length + rhs.length);
val.set(lhs);
val.set(rhs, lhs.length);
return val;
}),
(0, func_js_1.celOverload)([type_js_1.TIMESTAMP, type_js_1.TIMESTAMP], type_js_1.TIMESTAMP, addTimestamp),
(0, func_js_1.celOverload)([type_js_1.TIMESTAMP, type_js_1.DURATION], type_js_1.TIMESTAMP, addTimestamp),
(0, func_js_1.celOverload)([type_js_1.DURATION, type_js_1.TIMESTAMP], type_js_1.TIMESTAMP, (lhs, rhs) => addTimestamp(rhs, lhs)),
(0, func_js_1.celOverload)([type_js_1.DURATION, type_js_1.DURATION], type_js_1.DURATION, addDuration),
(0, func_js_1.celOverload)([(0, type_js_1.listType)(type_js_1.CelScalar.DYN), (0, type_js_1.listType)(type_js_1.CelScalar.DYN)], (0, type_js_1.listType)(type_js_1.CelScalar.DYN), list_js_1.celListConcat),
]);
const subtract = (0, func_js_1.celFunc)(opc.SUBTRACT, [
(0, func_js_1.celOverload)([type_js_1.CelScalar.INT, type_js_1.CelScalar.INT], type_js_1.CelScalar.INT, (lhs, rhs) => {
const val = lhs - rhs;
if (isOverflowInt(val)) {
throw overflow(opc.SUBTRACT, type_js_1.CelScalar.INT);
}
return val;
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.UINT, type_js_1.CelScalar.UINT], type_js_1.CelScalar.UINT, (lhs, rhs) => {
const val = lhs.value - rhs.value;
if (isOverflowUint(val)) {
throw overflow(opc.SUBTRACT, type_js_1.CelScalar.UINT);
}
return (0, uint_js_1.celUint)(val);
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.DOUBLE, type_js_1.CelScalar.DOUBLE], type_js_1.CelScalar.DOUBLE, (lhs, rhs) => lhs - rhs),
(0, func_js_1.celOverload)([type_js_1.TIMESTAMP, type_js_1.TIMESTAMP], type_js_1.DURATION, subtractDurationOrTimestamp),
(0, func_js_1.celOverload)([type_js_1.DURATION, type_js_1.DURATION], type_js_1.DURATION, subtractDurationOrTimestamp),
(0, func_js_1.celOverload)([type_js_1.TIMESTAMP, type_js_1.DURATION], type_js_1.TIMESTAMP, (lhs, rhs) => (0, timestamp_js_1.createTimestamp)(lhs.message.seconds - rhs.message.seconds, lhs.message.nanos - rhs.message.nanos)),
]);
const multiply = (0, func_js_1.celFunc)(opc.MULTIPLY, [
(0, func_js_1.celOverload)([type_js_1.CelScalar.INT, type_js_1.CelScalar.INT], type_js_1.CelScalar.INT, (lhs, rhs) => {
const product = lhs * rhs;
if (isOverflowInt(product)) {
throw overflow(opc.MULTIPLY, type_js_1.CelScalar.INT);
}
return product;
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.UINT, type_js_1.CelScalar.UINT], type_js_1.CelScalar.UINT, (lhs, rhs) => {
const product = lhs.value * rhs.value;
if (isOverflowUint(product)) {
throw overflow(opc.MULTIPLY, type_js_1.CelScalar.UINT);
}
return (0, uint_js_1.celUint)(product);
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.DOUBLE, type_js_1.CelScalar.DOUBLE], type_js_1.CelScalar.DOUBLE, (lhs, rhs) => lhs * rhs),
]);
const divide = (0, func_js_1.celFunc)(opc.DIVIDE, [
(0, func_js_1.celOverload)([type_js_1.CelScalar.INT, type_js_1.CelScalar.INT], type_js_1.CelScalar.INT, (lhs, rhs) => {
if (rhs === 0n) {
throw divisionByZero(type_js_1.CelScalar.INT);
}
if (lhs === MIN_INT && rhs === -1n) {
throw overflow(opc.DIVIDE, type_js_1.CelScalar.INT);
}
return lhs / rhs;
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.DOUBLE, type_js_1.CelScalar.DOUBLE], type_js_1.CelScalar.DOUBLE, (lhs, rhs) => lhs / rhs),
(0, func_js_1.celOverload)([type_js_1.CelScalar.UINT, type_js_1.CelScalar.UINT], type_js_1.CelScalar.UINT, (lhs, rhs) => {
if (rhs.value === 0n) {
throw divisionByZero(type_js_1.CelScalar.UINT);
}
return (0, uint_js_1.celUint)(lhs.value / rhs.value);
}),
]);
const modulo = (0, func_js_1.celFunc)(opc.MODULO, [
(0, func_js_1.celOverload)([type_js_1.CelScalar.INT, type_js_1.CelScalar.INT], type_js_1.CelScalar.INT, (lhs, rhs) => {
if (rhs === 0n) {
throw moduloByZero(type_js_1.CelScalar.INT);
}
return lhs % rhs;
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.UINT, type_js_1.CelScalar.UINT], type_js_1.CelScalar.UINT, (lhs, rhs) => {
if (rhs.value === 0n) {
throw moduloByZero(type_js_1.CelScalar.UINT);
}
return (0, uint_js_1.celUint)(lhs.value % rhs.value);
}),
]);
const negate = (0, func_js_1.celFunc)(opc.NEGATE, [
(0, func_js_1.celOverload)([type_js_1.CelScalar.INT], type_js_1.CelScalar.INT, (arg) => {
const val = -arg;
if (isOverflowInt(val)) {
throw overflow(opc.NEGATE, type_js_1.CelScalar.INT);
}
return val;
}),
(0, func_js_1.celOverload)([type_js_1.CelScalar.DOUBLE], type_js_1.CelScalar.DOUBLE, (arg) => -arg),
]);
function overflow(op, type) {
return new Error(`${type.name} return error for overflow during ${op}`);
}
function divisionByZero(type) {
return new Error(`${type.name} divide by zero`);
}
function moduloByZero(type) {
return new Error(`${type.name} modulus by zero`);
}