Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
// Copyright 2023 Google LLC
//
// 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
//
// https://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.
package dev.cel.runtime;
import dev.cel.expr.CheckedExpr;
import dev.cel.expr.Value;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.Immutable;
import javax.annotation.concurrent.ThreadSafe;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelErrorCode;
import dev.cel.common.CelOptions;
import dev.cel.common.CelProtoAbstractSyntaxTree;
import dev.cel.common.annotations.Internal;
import dev.cel.common.ast.CelConstant;
import dev.cel.common.ast.CelExpr;
import dev.cel.common.ast.CelExpr.CelCall;
import dev.cel.common.ast.CelExpr.CelComprehension;
import dev.cel.common.ast.CelExpr.CelCreateList;
import dev.cel.common.ast.CelExpr.CelCreateMap;
import dev.cel.common.ast.CelExpr.CelCreateStruct;
import dev.cel.common.ast.CelExpr.CelIdent;
import dev.cel.common.ast.CelExpr.CelSelect;
import dev.cel.common.ast.CelExpr.ExprKind;
import dev.cel.common.ast.CelReference;
import dev.cel.common.types.CelKind;
import dev.cel.common.types.CelType;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Default implementation of the CEL interpreter.
*
*
Use as in:
*
*
* MessageFactory messageFactory = new LinkedMessageFactory();
* RuntimeTypeProvider typeProvider = new DescriptorMessageProvider(messageFactory);
* Dispatcher dispatcher = DefaultDispatcher.create();
* Interpreter interpreter = new DefaultInterpreter(typeProvider, dispatcher);
* Interpretable interpretable = interpreter.createInterpretable(checkedExpr);
* Object result = interpretable.eval(Activation.of("name", value));
*
*
*
Extensions functions can be added in addition to standard functions to the dispatcher as
* needed.
*
*
Note: {MessageFactory} instances may be combined using the {@link
* MessageFactory.CombinedMessageFactory}.
*
*
Note: On Android, the {@code DescriptorMessageProvider} is not supported as proto lite does
* not support descriptors. Instead, implement the {@code MessageProvider} interface directly.
*
*
CEL Library Internals. Do Not Use.
*/
@ThreadSafe
@Internal
public final class DefaultInterpreter implements Interpreter {
private final RuntimeTypeProvider typeProvider;
private final Dispatcher dispatcher;
private final CelOptions celOptions;
/**
* Internal representation of an intermediate evaluation result.
*
*
Represents a partial result of evaluating a sub expression and the corresponding CEL
* attribute (or Empty if not applicable). Top level results of evaluation should unwrap the
* underlying result (.value()).
*/
@AutoValue
abstract static class IntermediateResult {
abstract CelAttribute attribute();
abstract Object value();
static IntermediateResult create(CelAttribute attr, Object value) {
Preconditions.checkArgument(
!(value instanceof IntermediateResult),
"Recursive intermediate results are not supported.");
return new AutoValue_DefaultInterpreter_IntermediateResult(attr, value);
}
static IntermediateResult create(Object value) {
return create(CelAttribute.EMPTY, value);
}
}
public DefaultInterpreter(RuntimeTypeProvider typeProvider, Dispatcher dispatcher) {
this(typeProvider, dispatcher, CelOptions.LEGACY);
}
/**
* Creates a new interpreter
*
* @param typeProvider object which allows to construct and inspect messages.
* @param dispatcher a method dispatcher.
* @param celOptions the configurable flags for adjusting evaluation behavior.
*/
public DefaultInterpreter(
RuntimeTypeProvider typeProvider, Dispatcher dispatcher, CelOptions celOptions) {
this.typeProvider = Preconditions.checkNotNull(typeProvider);
this.dispatcher = Preconditions.checkNotNull(dispatcher);
this.celOptions = celOptions;
}
@Override
@Deprecated
public Interpretable createInterpretable(CheckedExpr checkedExpr) {
return createInterpretable(CelProtoAbstractSyntaxTree.fromCheckedExpr(checkedExpr).getAst());
}
@Override
public Interpretable createInterpretable(CelAbstractSyntaxTree ast) {
return new DefaultInterpretable(typeProvider, dispatcher, ast, celOptions);
}
@Immutable
private static final class DefaultInterpretable
implements Interpretable, UnknownTrackingInterpretable {
private final RuntimeTypeProvider typeProvider;
private final Dispatcher.ImmutableCopy dispatcher;
private final Metadata metadata;
private final CelAbstractSyntaxTree ast;
private final CelOptions celOptions;
DefaultInterpretable(
RuntimeTypeProvider typeProvider,
Dispatcher dispatcher,
CelAbstractSyntaxTree ast,
CelOptions celOptions) {
this.typeProvider = Preconditions.checkNotNull(typeProvider);
this.dispatcher = Preconditions.checkNotNull(dispatcher).immutableCopy();
this.ast = Preconditions.checkNotNull(ast);
this.metadata = new DefaultMetadata(ast);
this.celOptions = Preconditions.checkNotNull(celOptions);
}
@Override
public Object eval(GlobalResolver resolver) throws InterpreterException {
// Result is already unwrapped from IntermediateResult.
return eval(resolver, CelEvaluationListener.noOpListener());
}
@Override
public Object eval(GlobalResolver resolver, CelEvaluationListener listener)
throws InterpreterException {
return evalTrackingUnknowns(RuntimeUnknownResolver.fromResolver(resolver), listener);
}
@Override
public Object evalTrackingUnknowns(
RuntimeUnknownResolver resolver, CelEvaluationListener listener)
throws InterpreterException {
ExecutionFrame frame =
new ExecutionFrame(listener, resolver, celOptions.comprehensionMaxIterations());
IntermediateResult internalResult = evalInternal(frame, ast.getExpr());
Object result = internalResult.value();
// TODO: remove support for IncompleteData.
return InterpreterUtil.completeDataOnly(
result, "Incomplete data cannot be returned as a result.");
}
private IntermediateResult evalInternal(ExecutionFrame frame, CelExpr expr)
throws InterpreterException {
try {
ExprKind.Kind exprKind = expr.exprKind().getKind();
IntermediateResult result;
switch (exprKind) {
case CONSTANT:
result = IntermediateResult.create(evalConstant(frame, expr, expr.constant()));
break;
case IDENT:
result = evalIdent(frame, expr, expr.ident());
break;
case SELECT:
result = evalSelect(frame, expr, expr.select());
break;
case CALL:
result = evalCall(frame, expr, expr.call());
break;
case CREATE_LIST:
result = evalList(frame, expr, expr.createList());
break;
case CREATE_STRUCT:
result = evalStruct(frame, expr, expr.createStruct());
break;
case CREATE_MAP:
result = evalMap(frame, expr.createMap());
break;
case COMPREHENSION:
result = evalComprehension(frame, expr, expr.comprehension());
break;
default:
throw new IllegalStateException(
"unexpected expression kind: " + expr.exprKind().getKind());
}
frame.getEvaluationListener().callback(expr, result.value());
return result;
} catch (RuntimeException e) {
throw new InterpreterException.Builder(e, e.getMessage())
.setLocation(metadata, expr.id())
.build();
}
}
private boolean isUnknownValue(Object value) {
return value instanceof CelUnknownSet || InterpreterUtil.isUnknown(value);
}
private Object evalConstant(
ExecutionFrame unusedFrame, CelExpr unusedExpr, CelConstant constExpr) {
switch (constExpr.getKind()) {
case NULL_VALUE:
return constExpr.nullValue();
case BOOLEAN_VALUE:
return constExpr.booleanValue();
case INT64_VALUE:
return constExpr.int64Value();
case UINT64_VALUE:
if (celOptions.enableUnsignedLongs()) {
return constExpr.uint64Value();
}
// Legacy users without the unsigned longs option turned on
return constExpr.uint64Value().longValue();
case DOUBLE_VALUE:
return constExpr.doubleValue();
case STRING_VALUE:
return constExpr.stringValue();
case BYTES_VALUE:
return constExpr.bytesValue();
default:
throw new IllegalStateException("unsupported constant case: " + constExpr.getKind());
}
}
private IntermediateResult evalIdent(ExecutionFrame frame, CelExpr expr, CelIdent unusedIdent)
throws InterpreterException {
CelReference reference = ast.getReferenceOrThrow(expr.id());
if (reference.value().isPresent()) {
return IntermediateResult.create(evalConstant(frame, expr, reference.value().get()));
}
return resolveIdent(frame, expr, reference.name());
}
private IntermediateResult resolveIdent(ExecutionFrame frame, CelExpr expr, String name)
throws InterpreterException {
// Check whether the type exists in the type check map as a 'type'.
Optional checkedType = ast.getType(expr.id());
if (checkedType.isPresent() && checkedType.get().kind() == CelKind.TYPE) {
Object typeValue = typeProvider.adaptType(checkedType.get());
return IntermediateResult.create(typeValue);
}
IntermediateResult rawResult = frame.resolveSimpleName(name, expr.id());
// Value resolved from Binding, it could be Message, PartialMessage or unbound(null)
Object value = InterpreterUtil.strict(typeProvider.adapt(rawResult.value()));
return IntermediateResult.create(rawResult.attribute(), value);
}
private IntermediateResult evalSelect(ExecutionFrame frame, CelExpr expr, CelSelect selectExpr)
throws InterpreterException {
Optional referenceOptional = ast.getReference(expr.id());
if (referenceOptional.isPresent()) {
CelReference reference = referenceOptional.get();
// This indicates it's a qualified name.
if (reference.value().isPresent()) {
// If the value is identified as a constant, skip attribute tracking.
return IntermediateResult.create(evalConstant(frame, expr, reference.value().get()));
}
return resolveIdent(frame, expr, reference.name());
}
return evalFieldSelect(
frame, expr, selectExpr.operand(), selectExpr.field(), selectExpr.testOnly());
}
private IntermediateResult evalFieldSelect(
ExecutionFrame frame, CelExpr expr, CelExpr operandExpr, String field, boolean isTestOnly)
throws InterpreterException {
// This indicates this is a field selection on the operand.
IntermediateResult operandResult = evalInternal(frame, operandExpr);
Object operand = operandResult.value();
CelAttribute attribute =
operandResult.attribute().qualify(CelAttribute.Qualifier.ofString(field));
Optional