Odoo18-Base/addons/web/static/src/core/context.js
2025-01-06 10:57:38 +07:00

81 lines
2.5 KiB
JavaScript

import { evaluateExpr, parseExpr } from "./py_js/py";
import { BUILTINS } from "./py_js/py_builtin";
import { evaluate } from "./py_js/py_interpreter";
/**
* @typedef {{[key: string]: any}} Context
* @typedef {Context | string | undefined} ContextDescription
*/
/**
* Create an evaluated context from an arbitrary list of context representations.
* The evaluated context in construction is used along the way to evaluate further parts.
*
* @param {ContextDescription[]} contexts
* @param {Context} [initialEvaluationContext] optional evaluation context to start from.
* @returns {Context}
*/
export function makeContext(contexts, initialEvaluationContext) {
const evaluationContext = Object.assign({}, initialEvaluationContext);
const context = {};
for (let ctx of contexts) {
if (ctx !== "") {
ctx = typeof ctx === "string" ? evaluateExpr(ctx, evaluationContext) : ctx;
Object.assign(context, ctx);
Object.assign(evaluationContext, context); // is this behavior really wanted ?
}
}
return context;
}
/**
* Extract a partial list of variable names found in the AST.
* Note that it is not complete. It is used as an heuristic to avoid
* evaluating expressions that we know for sure will fail.
*
* @param {AST} ast
* @returns string[]
*/
function getPartialNames(ast) {
if (ast.type === 5) {
return [ast.value];
}
if (ast.type === 6) {
return getPartialNames(ast.right);
}
if (ast.type === 14 || ast.type === 7) {
return getPartialNames(ast.left).concat(getPartialNames(ast.right));
}
if (ast.type === 15) {
return getPartialNames(ast.obj);
}
return [];
}
/**
* Allow to evaluate a context with an incomplete evaluation context. The evaluated context only
* contains keys whose values are static or can be evaluated with the given evaluation context.
*
* @param {string} context
* @param {Object} [evaluationContext={}]
* @returns {Context}
*/
export function evalPartialContext(_context, evaluationContext = {}) {
const ast = parseExpr(_context);
const context = {};
for (const key in ast.value) {
const value = ast.value[key];
if (
getPartialNames(value).some((name) => !(name in evaluationContext || name in BUILTINS))
) {
continue;
}
try {
context[key] = evaluate(value, evaluationContext);
} catch {
// ignore this key as we can't evaluate its value
}
}
return context;
}