Search in sources :

Example 1 with FieldReference

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

the class ProcedureCompilation method compileFunction.

/**
 * Generates code for a user-defined function.
 * <p>
 * Given a user-defined function defined by
 *
 * <pre>
 *     class MyClass {
 *       {@literal @}Context
 *        public Log log;
 *
 *       {@literal @}UserFunction
 *        public double addPi(long value) {
 *            return value + Math.PI;
 *        }
 *     }
 * </pre>
 * <p>
 * we will generate something like
 *
 * <pre>
 *     class GeneratedAddPi implements CallableUserFunction {
 *         public static UserFunctionSignature SIGNATURE;
 *         public static FieldSetter SETTER_0;
 *
 *         public AnyValue apply(Context ctx, AnyValue[] input) {
 *              try {
 *                  MyClass userClass = new MyClass();
 *                  userClass.log = (Log) SETTER_0.get(ctx);
 *                  return Values.doubleValue(userClass.addPi( ((NumberValue) input[0]).longValue() );
 *              } catch (Throwable T) {
 *                  throw new ProcedureException([appropriate error msg], T);
 *              }
 *         }
 *
 *         public UserFunctionSignature signature() {return SIGNATURE;}
 *     }
 * </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 methodToCall the method to call
 * @return a CallableUserFunction delegating to the underlying user-defined function.
 * @throws ProcedureException if something went wrong when compiling the user-defined function.
 */
static CallableUserFunction compileFunction(UserFunctionSignature signature, List<FieldSetter> fieldSetters, Method methodToCall) throws ProcedureException {
    ClassHandle handle;
    try {
        CodeGenerator codeGenerator = codeGenerator();
        try (ClassGenerator generator = codeGenerator.generateClass(PACKAGE, className(signature), CallableUserFunction.class)) {
            // static fields
            FieldReference signatureField = generator.publicStaticField(typeReference(UserFunctionSignature.class), SIGNATURE_NAME);
            List<FieldReference> fieldsToSet = createContextSetters(fieldSetters, generator);
            // CallableUserFunction::apply
            try (CodeBlock method = generator.generate(USER_FUNCTION)) {
                method.tryCatch(body -> functionBody(body, fieldSetters, fieldsToSet, methodToCall), 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, methodToCall, clazz);
        return (CallableUserFunction) clazz.getConstructor().newInstance();
    } catch (Throwable e) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, e, "Failed to compile function defined in `%s`: %s", methodToCall.getDeclaringClass().getSimpleName(), e.getMessage());
    }
}
Also used : CallableUserFunction(org.neo4j.kernel.api.procedure.CallableUserFunction) ClassGenerator(org.neo4j.codegen.ClassGenerator) FieldReference(org.neo4j.codegen.FieldReference) 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 2 with FieldReference

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

the class ProcedureCompilation method generateIterator.

/**
 * Generates a tailored iterator mapping from the user-stream to the internal RawIterator
 *
 * The generated iterator extends {@link BaseStreamIterator} and generates a tailored
 * {@link BaseStreamIterator#map(Object)} method based on the signature of the user procedure.
 *
 * @param codeGenerator the current generator
 * @param outputType the output type of the user procedure
 * @return a tailored iterator with appropriate code mappings
 */
private static Class<?> generateIterator(CodeGenerator codeGenerator, Class<?> outputType) {
    if (outputType.equals(void.class)) {
        return VOID_ITERATOR.getClass();
    }
    ClassHandle handle;
    try (ClassGenerator generator = codeGenerator.generateClass(BaseStreamIterator.class, PACKAGE, iteratorName(outputType))) {
        FieldReference context = generator.field(Context.class, "ctx");
        try (CodeBlock constructor = generator.generateConstructor(param(Stream.class, "stream"), param(ResourceTracker.class, "tracker"), param(ProcedureSignature.class, "signature"), param(Context.class, "ctx"))) {
            constructor.expression(invokeSuper(typeReference(BaseStreamIterator.class), constructor.load("stream"), constructor.load("tracker"), constructor.load("signature")));
            constructor.put(constructor.self(), context, constructor.load("ctx"));
        }
        try (CodeBlock method = generator.generate(method(AnyValue[].class, "map", param(Object.class, "in")))) {
            method.assign(outputType, "casted", cast(outputType, method.load("in")));
            // we know all fields are properly typed
            List<Field> fields = ProcedureOutputSignatureCompiler.instanceFields(outputType);
            Expression[] mapped = new Expression[fields.size()];
            for (int i = 0; i < fields.size(); i++) {
                Field f = fields.get(i);
                mapped[i] = toAnyValue(get(method.load("casted"), field(f)), f.getType(), get(method.self(), context));
            }
            method.returns(Expression.newInitializedArray(typeReference(AnyValue.class), mapped));
        }
        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) FieldReference(org.neo4j.codegen.FieldReference) ResourceTracker(org.neo4j.kernel.api.ResourceTracker) CodeBlock(org.neo4j.codegen.CodeBlock) Point(org.neo4j.graphdb.spatial.Point) CompilationFailureException(org.neo4j.codegen.CompilationFailureException) ProcedureSignature(org.neo4j.internal.kernel.api.procs.ProcedureSignature) Field(java.lang.reflect.Field) ClassGenerator(org.neo4j.codegen.ClassGenerator) Expression(org.neo4j.codegen.Expression) Stream(java.util.stream.Stream) ClassHandle(org.neo4j.codegen.ClassHandle)

Example 3 with FieldReference

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

the class ProcedureCompilation method compileProcedure.

/**
 * Generates code for a user-defined procedure.
 * <p>
 * Given a user-defined function defined by
 *
 * <pre>
 *     class MyClass {
 *       {@literal @}Context
 *        public Log log;
 *
 *       {@literal @}Procedure
 *        public Stream<MyOut> myStream(long value) {
 *            ...
 *        }
 *     }
 * </pre>
 * <p>
 * we will generate something like
 *
 * <pre>
 *     class GeneratedMyStream implements CallableProcedure {
 *         public static ProcedureSignature SIGNATURE;
 *         public static FieldSetter SETTER_0;
 *
 *          RawIterator<AnyValue[], ProcedureException> apply( Context ctx, AnyValue[] in, ResourceTracker tracker ) throws ProcedureException {
 *              try {
 *                  MyClass userClass = new MyClass();
 *                  userClass.log = (Log) SETTER_0.get(ctx);
 *                  Stream<MyOut> fromUser = userClass.myStream(((NumberValue) input[0]).longValue() );
 *                  return new GeneratedIterator(fromUser, tracker, SIGNATURE);
 *              } catch (Throwable T) {
 *                  throw new ProcedureException([appropriate error msg], T);
 *              }
 *         }
 *
 *         public ProcedureSignature signature() {return SIGNATURE;}
 *     }
 * </pre>
 * <p>
 * where the static fields are set once during loading via reflection and where the <tt>GeneratedIterator</tt>
 * implements the appropriate mapping from user-types Object[] to AnyValue[].
 *
 * @param signature the signature of the procedure
 * @param fieldSetters the fields to set before each call.
 * @param methodToCall the method to call
 * @return a CallableProcedure delegating to the underlying procedure method.
 * @throws ProcedureException if something went wrong when compiling the user-defined function.
 */
static CallableProcedure compileProcedure(ProcedureSignature signature, List<FieldSetter> fieldSetters, Method methodToCall) throws ProcedureException {
    ClassHandle handle;
    try {
        CodeGenerator codeGenerator = codeGenerator();
        Class<?> iterator = generateIterator(codeGenerator, procedureType(methodToCall));
        try (ClassGenerator generator = codeGenerator.generateClass(PACKAGE, className(signature), CallableProcedure.class)) {
            // static fields
            FieldReference signatureField = generator.publicStaticField(typeReference(ProcedureSignature.class), SIGNATURE_NAME);
            List<FieldReference> fieldsToSet = createContextSetters(fieldSetters, generator);
            // CallableProcedure::apply
            try (CodeBlock method = generator.generate(USER_PROCEDURE)) {
                method.tryCatch(body -> procedureBody(body, fieldSetters, fieldsToSet, signatureField, methodToCall, iterator), onError -> onError(onError, format("procedure `%s`", signature.name())), param(Throwable.class, "T"));
            }
            // CallableUserFunction::signature
            try (CodeBlock method = generator.generateMethod(ProcedureSignature.class, "signature")) {
                method.returns(getStatic(signatureField));
            }
            handle = generator.handle();
        }
        Class<?> clazz = handle.loadClass();
        // set all static fields
        setAllStaticFields(signature, fieldSetters, methodToCall, clazz);
        return (CallableProcedure) clazz.getConstructor().newInstance();
    } catch (Throwable e) {
        throw new ProcedureException(Status.Procedure.ProcedureRegistrationFailed, e, "Failed to compile procedure defined in `%s`: %s", methodToCall.getDeclaringClass().getSimpleName(), e.getMessage());
    }
}
Also used : ProcedureSignature(org.neo4j.internal.kernel.api.procs.ProcedureSignature) ClassGenerator(org.neo4j.codegen.ClassGenerator) FieldReference(org.neo4j.codegen.FieldReference) CodeBlock(org.neo4j.codegen.CodeBlock) CallableProcedure(org.neo4j.kernel.api.procedure.CallableProcedure) CodeGenerator(org.neo4j.codegen.CodeGenerator) ProcedureException(org.neo4j.internal.kernel.api.exceptions.ProcedureException) ClassHandle(org.neo4j.codegen.ClassHandle)

Example 4 with FieldReference

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

the class ByteCodeClassWriter method done.

@Override
public void done() {
    if (!staticFields.isEmpty()) {
        MethodVisitor methodVisitor = classVisitor.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
        ByteCodeExpressionVisitor expressionVisitor = new ByteCodeExpressionVisitor(methodVisitor);
        methodVisitor.visitCode();
        for (Map.Entry<FieldReference, Expression> entry : staticFields.entrySet()) {
            FieldReference field = entry.getKey();
            Expression value = entry.getValue();
            value.accept(expressionVisitor);
            methodVisitor.visitFieldInsn(PUTSTATIC, byteCodeName(field.owner()), field.name(), typeName(field.type()));
        }
        methodVisitor.visitInsn(RETURN);
        methodVisitor.visitMaxs(0, 0);
        methodVisitor.visitEnd();
    }
    classVisitor.visitEnd();
}
Also used : FieldReference(org.neo4j.codegen.FieldReference) Expression(org.neo4j.codegen.Expression) HashMap(java.util.HashMap) Map(java.util.Map) MethodVisitor(org.objectweb.asm.MethodVisitor)

Example 5 with FieldReference

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

the class ClassByteCodeWriter method done.

@Override
public void done() {
    if (!staticFields.isEmpty()) {
        MethodVisitor methodVisitor = classVisitor.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
        ByteCodeExpressionVisitor expressionVisitor = new ByteCodeExpressionVisitor(methodVisitor);
        methodVisitor.visitCode();
        for (Map.Entry<FieldReference, Expression> entry : staticFields.entrySet()) {
            FieldReference field = entry.getKey();
            Expression value = entry.getValue();
            value.accept(expressionVisitor);
            methodVisitor.visitFieldInsn(PUTSTATIC, byteCodeName(field.owner()), field.name(), typeName(field.type()));
        }
        methodVisitor.visitInsn(RETURN);
        methodVisitor.visitMaxs(0, 0);
        methodVisitor.visitEnd();
    }
    classVisitor.visitEnd();
}
Also used : FieldReference(org.neo4j.codegen.FieldReference) Expression(org.neo4j.codegen.Expression) HashMap(java.util.HashMap) Map(java.util.Map) MethodVisitor(org.objectweb.asm.MethodVisitor)

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