Search in sources :

Example 6 with FieldReference

use of org.neo4j.codegen.FieldReference in project neo4j by neo4j.

the class ProcedureCompilation method compileAggregation.

/**
 * Generates code for a user-defined aggregation function.
 * <p>
 * Given a user-defined aggregation function defined by
 *
 * <pre>
 *     class MyClass {
 *       {@literal @}Context
 *        public Log log;
 *
 *       {@literal @}UserAggregationFunction
 *        public Adder create() {
 *            return new Adder;
 *        }
 *     }
 *
 *     class Adder {
 *       private long sum;
 *       {@literal @}UserAggregationUpdate
 *       public void update(long in) {
 *           sum += in;
 *       }
 *       {@literal @}UserAggregationResult
 *       public long result() {
 *         return sum;
 *       }
 *    }
 *     }
 * </pre>
 * <p>
 * we will generate two classe looking something like
 *
 * <pre>
 *     class Generated implements CallableUserAggregationFunction {
 *         public static UserFunctionSignature SIGNATURE;
 *         public static FieldSetter SETTER_0;
 *
 *         public UserAggregator create(Context ctx) {
 *              try {
 *                  MyClass userClass = new MyClass();
 *                  userClass.log = (Log) SETTER_0.get(ctx);
 *                  return new GeneratedAdder(userClass.create());
 *              } catch (Throwable T) {
 *                  throw new ProcedureException([appropriate error msg], T);
 *              }
 *         }
 *
 *         public UserFunctionSignature signature() {return SIGNATURE;}
 *     }
 *     class GeneratedAdder implements UserAggregator {
 *       private Adder adder;
 *
 *       GeneratedAdder(Adder adder) {
 *         this.adder = adder;
 *       }
 *
 *       void update(AnyValue[] in) {
 *          adder.update(((NumberValue) in).longValue());
 *       }
 *
 *       AnyValue result() {
 *          return adder.result(Values.longValue(adder.result());
 *       }
 *     }
 * </pre>
 * <p>
 * where the static fields are set once during loading via reflection.
 *
 * @param signature the signature of the user-defined function
 * @param fieldSetters the fields to set before each call.
 * @param create the method that creates the aggregator
 * @param update the update method of the aggregator
 * @param result the result method of the aggregator
 * @return a CallableUserFunction delegating to the underlying user-defined function.
 * @throws ProcedureException if something went wrong when compiling the user-defined function.
 */
static CallableUserAggregationFunction compileAggregation(UserFunctionSignature signature, List<FieldSetter> fieldSetters, Method create, Method update, Method result) throws ProcedureException {
    ClassHandle handle;
    try {
        CodeGenerator codeGenerator = codeGenerator();
        Class<?> aggregator = generateAggregator(codeGenerator, update, result, signature);
        try (ClassGenerator generator = codeGenerator.generateClass(PACKAGE, className(signature), CallableUserAggregationFunction.class)) {
            // static fields
            FieldReference signatureField = generator.publicStaticField(typeReference(UserFunctionSignature.class), SIGNATURE_NAME);
            List<FieldReference> fieldsToSet = createContextSetters(fieldSetters, generator);
            // CallableUserAggregationFunction::create
            try (CodeBlock method = generator.generate(AGGREGATION_CREATE)) {
                method.tryCatch(body -> createAggregationBody(body, fieldSetters, fieldsToSet, create, aggregator), onError -> onError(onError, format("function `%s`", signature.name())), param(Throwable.class, "T"));
            }
            // CallableUserFunction::signature
            try (CodeBlock method = generator.generateMethod(UserFunctionSignature.class, "signature")) {
                method.returns(getStatic(signatureField));
            }
            handle = generator.handle();
        }
        Class<?> clazz = handle.loadClass();
        // set all static fields
        setAllStaticFields(signature, fieldSetters, create, clazz);
        return (CallableUserAggregationFunction) clazz.getConstructor().newInstance();
    } catch (Throwable e) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, e, "Failed to compile function defined in `%s`: %s", create.getDeclaringClass().getSimpleName(), e.getMessage());
    }
}
Also used : ClassGenerator(org.neo4j.codegen.ClassGenerator) FieldReference(org.neo4j.codegen.FieldReference) CallableUserAggregationFunction(org.neo4j.kernel.api.procedure.CallableUserAggregationFunction) CodeBlock(org.neo4j.codegen.CodeBlock) CodeGenerator(org.neo4j.codegen.CodeGenerator) ProcedureException(org.neo4j.internal.kernel.api.exceptions.ProcedureException) UserFunctionSignature(org.neo4j.internal.kernel.api.procs.UserFunctionSignature) ClassHandle(org.neo4j.codegen.ClassHandle)

Example 7 with FieldReference

use of org.neo4j.codegen.FieldReference in project neo4j by neo4j.

the class ProcedureCompilation method generateAggregator.

private static Class<?> generateAggregator(CodeGenerator codeGenerator, Method update, Method result, UserFunctionSignature signature) {
    assert update.getDeclaringClass().equals(result.getDeclaringClass());
    Class<?> userAggregatorClass = update.getDeclaringClass();
    ClassHandle handle;
    try (ClassGenerator generator = codeGenerator.generateClass(PACKAGE, "Aggregator" + userAggregatorClass.getSimpleName() + System.nanoTime(), UserAggregator.class)) {
        FieldReference aggregator = generator.field(userAggregatorClass, "aggregator");
        FieldReference context = generator.field(Context.class, "ctx");
        // constructor
        try (CodeBlock constructor = generator.generateConstructor(param(userAggregatorClass, "aggregator"), param(Context.class, "ctx"))) {
            constructor.expression(invokeSuper(OBJECT));
            constructor.put(constructor.self(), aggregator, constructor.load("aggregator"));
            constructor.put(constructor.self(), context, constructor.load("ctx"));
        }
        // update
        try (CodeBlock block = generator.generate(AGGREGATION_UPDATE)) {
            block.tryCatch(onSuccess -> onSuccess.expression(invoke(get(onSuccess.self(), aggregator), methodReference(update), parameters(onSuccess, update, get(onSuccess.self(), context)))), onError -> onError(onError, format("function `%s`", signature.name())), param(Throwable.class, "T"));
        }
        // result
        try (CodeBlock block = generator.generate(AGGREGATION_RESULT)) {
            block.tryCatch(onSuccess -> onSuccess.returns(toAnyValue(invoke(get(onSuccess.self(), aggregator), methodReference(result)), result.getReturnType(), get(onSuccess.self(), context))), onError -> onError(onError, format("function `%s`", signature.name())), param(Throwable.class, "T"));
        }
        handle = generator.handle();
    }
    try {
        return handle.loadClass();
    } catch (CompilationFailureException e) {
        // We are being called from a lambda so it'll have to do with runtime exceptions here
        throw new RuntimeException("Failed to generate iterator", e);
    }
}
Also used : Context(org.neo4j.kernel.api.procedure.Context) ClassGenerator(org.neo4j.codegen.ClassGenerator) FieldReference(org.neo4j.codegen.FieldReference) CodeBlock(org.neo4j.codegen.CodeBlock) ClassHandle(org.neo4j.codegen.ClassHandle) CompilationFailureException(org.neo4j.codegen.CompilationFailureException)

Aggregations

FieldReference (org.neo4j.codegen.FieldReference)7 ClassGenerator (org.neo4j.codegen.ClassGenerator)5 ClassHandle (org.neo4j.codegen.ClassHandle)5 CodeBlock (org.neo4j.codegen.CodeBlock)5 CodeGenerator (org.neo4j.codegen.CodeGenerator)3 Expression (org.neo4j.codegen.Expression)3 ProcedureException (org.neo4j.internal.kernel.api.exceptions.ProcedureException)3 HashMap (java.util.HashMap)2 Map (java.util.Map)2 CompilationFailureException (org.neo4j.codegen.CompilationFailureException)2 ProcedureSignature (org.neo4j.internal.kernel.api.procs.ProcedureSignature)2 UserFunctionSignature (org.neo4j.internal.kernel.api.procs.UserFunctionSignature)2 Context (org.neo4j.kernel.api.procedure.Context)2 MethodVisitor (org.objectweb.asm.MethodVisitor)2 Field (java.lang.reflect.Field)1 Stream (java.util.stream.Stream)1 Point (org.neo4j.graphdb.spatial.Point)1 ResourceTracker (org.neo4j.kernel.api.ResourceTracker)1 CallableProcedure (org.neo4j.kernel.api.procedure.CallableProcedure)1 CallableUserAggregationFunction (org.neo4j.kernel.api.procedure.CallableUserAggregationFunction)1