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());
}
}
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);
}
}
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());
}
}
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();
}
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();
}
Aggregations