protected void buildConstructor() throws CannotCompileException, NotFoundException { // Constructor(Class targetClass, Template[] templates) CtConstructor newCtCons = CtNewConstructor.make( new CtClass[] { director.getCtClass(Class.class.getName()), director.getCtClass(Template.class.getName() + "[]") }, new CtClass[0], tmplCtClass); tmplCtClass.addConstructor(newCtCons); }
/** * @param pool The AOPClassPool to create the optimized invocation class in * @param makeInnerClass If true creates the new class as an inner class of className * @param outerClass The class to create the invocation class as an inner class of if * makeInnerClass==true * @param className The full class name (including package info) of the invocation class to be * created * @param superInvocation The super class of this invocation * @return The created invocation class */ public static CtClass makeInvocationClass( AOPClassPool pool, boolean makeInnerClass, CtClass outerClass, String className, CtClass superInvocation) throws CannotCompileException, NotFoundException { CtClass invocation = makeInvocationClassNoCtors(pool, makeInnerClass, outerClass, className, superInvocation); // Add the invocation constructor CtConstructor[] cons = superInvocation.getDeclaredConstructors(); for (int i = 0; i < cons.length; i++) { CtConstructor conTemplate = superInvocation.getDeclaredConstructors()[i]; CtConstructor icon = CtNewConstructor.make( conTemplate.getParameterTypes(), conTemplate.getExceptionTypes(), invocation); invocation.addConstructor(icon); } return invocation; }
@Override public void enhanceThisClass(ApplicationClass applicationClass) throws Exception { final CtClass ctClass = makeClass(applicationClass); if (ctClass.isInterface()) { return; } if (ctClass.getName().endsWith(".package")) { return; } // Add a default constructor if needed try { boolean hasDefaultConstructor = false; for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { hasDefaultConstructor = true; break; } } if (!hasDefaultConstructor && !ctClass.isInterface()) { CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } if (isScala(applicationClass)) { // Temporary hack for Scala. Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); return; } for (CtField ctField : ctClass.getDeclaredFields()) { try { if (isProperty(ctField)) { // Property name String propertyName = ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1); String getter = "get" + propertyName; String setter = "set" + propertyName; try { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); if (ctMethod.getParameterTypes().length > 0 || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a getter !"); } } catch (NotFoundException noGetter) { // Créé le getter String code = "public " + ctField.getType().getName() + " " + getter + "() { return this." + ctField.getName() + "; }"; CtMethod getMethod = CtMethod.make(code, ctClass); getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(getMethod); } if (!isFinal(ctField)) { try { CtMethod ctMethod = ctClass.getDeclaredMethod(setter); if (ctMethod.getParameterTypes().length != 1 || !ctMethod.getParameterTypes()[0].equals(ctField.getType()) || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a setter !"); } } catch (NotFoundException noSetter) { // Créé le setter CtMethod setMethod = CtMethod.make( "public void " + setter + "(" + ctField.getType().getName() + " value) { this." + ctField.getName() + " = value; }", ctClass); setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(setMethod); createAnnotation(getAnnotations(setMethod), PlayPropertyAccessor.class); } } } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } } // Add a default constructor if needed try { boolean hasDefaultConstructor = false; for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { hasDefaultConstructor = true; break; } } if (!hasDefaultConstructor) { CtConstructor defaultConstructor = CtNewConstructor.defaultConstructor(ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } // Intercept all fields access for (final CtBehavior ctMethod : ctClass.getDeclaredBehaviors()) { ctMethod.instrument( new ExprEditor() { @Override public void edit(FieldAccess fieldAccess) throws CannotCompileException { try { // Acces à une property ? if (isProperty(fieldAccess.getField())) { // TODO : vérifier que c'est bien un champ d'une classe de l'application // (fieldAccess.getClassName()) // Si c'est un getter ou un setter String propertyName = null; if (fieldAccess .getField() .getDeclaringClass() .equals(ctMethod.getDeclaringClass()) || ctMethod .getDeclaringClass() .subclassOf(fieldAccess.getField().getDeclaringClass())) { if ((ctMethod.getName().startsWith("get") || (!isFinal(fieldAccess.getField()) && ctMethod.getName().startsWith("set"))) && ctMethod.getName().length() > 3) { propertyName = ctMethod.getName().substring(3); propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); } } // On n'intercepte pas le getter de sa propre property if (propertyName == null || !propertyName.equals(fieldAccess.getFieldName())) { String invocationPoint = ctClass.getName() + "." + ctMethod.getName() + ", line " + fieldAccess.getLineNumber(); if (fieldAccess.isReader()) { // Réécris l'accés en lecture à la property fieldAccess.replace( "$_ = ($r)play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeReadProperty($0, \"" + fieldAccess.getFieldName() + "\", \"" + fieldAccess.getClassName() + "\", \"" + invocationPoint + "\");"); } else if (!isFinal(fieldAccess.getField()) && fieldAccess.isWriter()) { // Réécris l'accés en ecriture à la property fieldAccess.replace( "play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeWriteProperty($0, \"" + fieldAccess.getFieldName() + "\", " + fieldAccess.getField().getType().getName() + ".class, $1, \"" + fieldAccess.getClassName() + "\", \"" + invocationPoint + "\");"); } } } } catch (Exception e) { throw new UnexpectedException("Error in PropertiesEnhancer", e); } } }); } // Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); }
@Override public void enhanceThisClass(ApplicationClass applicationClass) throws Exception { CtClass ctClass = makeClass(applicationClass); String entityName = ctClass.getName(); Logger.debug("Enhance class " + entityName); // Only enhance Neo4jModel classes. if (!ctClass.subtypeOf(classPool.get("play.modules.neo4j.model.Neo4jModel"))) { return; } // Add a default constructor if needed try { for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { ctClass.removeConstructor(constructor); } if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0].getClass().isInstance(Node.class)) { ctClass.removeConstructor(constructor); } } if (!ctClass.isInterface()) { Logger.debug("Adding default constructor"); CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() { super();}", ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } // for all field, we add getter / setter for (CtField ctField : ctClass.getDeclaredFields()) { try { // Property name String propertyName = ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1); String getter = "get" + propertyName; String setter = "set" + propertyName; Logger.debug("Field " + ctField.getName() + " is a property ?"); if (isProperty(ctField)) { Logger.debug("true"); // ~~~~~~~~~ // GETTER // ~~~~~~~ try { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); if (!ctMethod.getName().equalsIgnoreCase("getShouldBeSave")) { ctClass.removeMethod(ctMethod); throw new NotFoundException("it's not a true getter !"); } } catch (NotFoundException noGetter) { // create getter Logger.debug("Adding getter " + getter + " for class " + entityName); // @formatter:off String code = "public " + ctField.getType().getName() + " " + getter + "() {" + "if(this.shouldBeSave == Boolean.FALSE && this.node != null){" + "return ((" + ctField.getType().getName() + ") play.modules.neo4j.util.Binder.bindFromNeo4jFormat(this.node.getProperty(\"" + ctField.getName() + "\", null)," + ctField.getType().getName() + ".class ));" + "}else{" + "return " + ctField.getName() + ";" + "}" + "}"; // @formatter:on Logger.debug(code); CtMethod getMethod = CtMethod.make(code, ctClass); ctClass.addMethod(getMethod); } // ~~~~~~~~~ // SETTER // ~~~~~~~ try { CtMethod ctMethod = ctClass.getDeclaredMethod(setter); if (ctMethod.getParameterTypes().length != 1 || !ctMethod.getParameterTypes()[0].equals(ctField.getType()) || Modifier.isStatic(ctMethod.getModifiers()) || hasPlayPropertiesAccessorAnnotation(ctMethod)) { if (hasPlayPropertiesAccessorAnnotation(ctMethod)) { ctClass.removeMethod(ctMethod); } throw new NotFoundException("it's not a true setter !"); } } catch (NotFoundException noSetter) { // create setter Logger.debug("Adding setter " + setter + " for class " + entityName); // @formatter:off String code = "public void " + setter + "(" + ctField.getType().getName() + " value) { " + "this." + ctField.getName() + " = value;" + "this.shouldBeSave = Boolean.TRUE;" + "}"; // formatter:on CtMethod setMethod = CtMethod.make(code, ctClass); Logger.debug(code); ctClass.addMethod(setMethod); } } else { // ~~~~~~~~~ // GETTER for neo4j relation property // ~~~~~~~ if (hasNeo4jRelationAnnotation(ctField)) { // test for related annotation Neo4jRelatedTo relatedTo = getRelatedAnnotation(ctField); if (relatedTo != null) { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); ctClass.removeMethod(ctMethod); String code; if (relatedTo.lazy()) { // @formatter:off code = "public " + ctField.getType().getName() + " " + getter + "() {" + "if(this." + ctField.getName() + " == null){" + "java.lang.reflect.Field field = this.getClass().getField(\"" + ctField.getName() + "\");" + "this." + ctField.getName() + "=play.modules.neo4j.relationship.Neo4jRelationFactory.getModelsFromRelation(\"" + relatedTo.value() + "\", \"" + relatedTo.direction() + "\", field, this.node);" + "}" + "return " + ctField.getName() + ";" + "}"; // @formatter:on } else { // @formatter:off code = "public " + ctField.getType().getName() + " " + getter + "() {" + "return " + ctField.getName() + ";" + "}"; // @formatter:on } Logger.debug(code); CtMethod method = CtMethod.make(code, ctClass); ctClass.addMethod(method); } // test for unique relation annotation Neo4jUniqueRelation uniqueRelation = getUniqueRelationAnnotation(ctField); if (uniqueRelation != null) { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); ctClass.removeMethod(ctMethod); String code; // @formatter:off code = "public " + ctField.getType().getName() + " " + getter + "() {" + "return (" + ctField.getType().getName() + ")" + ctField.getName() + ";" + "}"; // @formatter:on Logger.debug(code); CtMethod method = CtMethod.make(code, ctClass); ctClass.addMethod(method); } } } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } } // Adding getByKey() method Logger.debug("Adding getByKey() method for class " + entityName); // @formatter:off String codeGetByKey = "public static play.modules.neo4j.model.Neo4jModel getByKey(Long key) throws play.modules.neo4j.exception.Neo4jException {" + "return (" + entityName + ")_getByKey(key, \"" + entityName + "\");" + "}"; // @formatter:on Logger.debug(codeGetByKey); CtMethod getByKeyMethod = CtMethod.make(codeGetByKey, ctClass); ctClass.addMethod(getByKeyMethod); // ~~~~~~~~~~~~~~~ // Adding findAll() method // @formatter:off String codeFindAll = "public static java.util.List findAll() {" + "return " + entityName + "._findAll(\"" + entityName + "\");" + "}"; // @formatter:on Logger.debug(codeFindAll); CtMethod findAllMethod = CtMethod.make(codeFindAll, ctClass); ctClass.addMethod(findAllMethod); // ~~~~~~~~~~~~~~~ // Adding queryIndex() method // @formatter:off String queryIndex = "public static java.util.List queryIndex(String indexname, String query) {" + "return " + entityName + "._queryIndex(indexname, query);" + "}"; // @formatter:on Logger.debug(queryIndex); CtMethod queryIndexMethod = CtMethod.make(queryIndex, ctClass); ctClass.addMethod(queryIndexMethod); // Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); }
private static CtClass makeExpressionClass( Source.Staged staged, String superClassName, String memberClassName) { // Here we make the language class accessible! if (!Modifier.isPublic(staged.getLanguage().getModifiers())) { staged.getLanguage().setModifiers(Modifier.setPublic(staged.getLanguage().getModifiers())); } CtClass clazz; try { ClassPool cp = ClassPool.getDefault(); CtClass superClass = cp.getCtClass(superClassName); clazz = cp.makeClass( superClass.getName() + "$Generated" + id + "$" + staged.getMember().getDeclaringClass().getName().replace(".", "_") + "$" + staged.getMember().getName().replace(".", "_"), superClass); id++; // Make accepted language static fields HashMap<CtClass, String> acceptedLanguageMap = new HashMap<>(); for (Use.Argument a : staged.getArguments()) { for (CtClass l : a.getAcceptedLanguages()) { if (!acceptedLanguageMap.containsKey(l) && !l.equals(Util.LANGUAGE_CLASS)) { String lName = "acceptedLanguage" + acceptedLanguageMap.size(); int lPersistentId = Dispatcher.addPersistent(l); CtField lField = CtField.make( "private static final " + CtClass.class.getName() + " " + lName + " = (" + CtClass.class.getName() + ") " + Util.DISPATCHER_CLASS.getName() + ".removePersistent(" + lPersistentId + ");", clazz); clazz.addField(lField); acceptedLanguageMap.put(l, lName); } } } // Make language field String languageName = "language"; int languagePersistentId = Dispatcher.addPersistent(staged.getLanguage()); CtField languageField = CtField.make( "private static final " + CtClass.class.getName() + " " + languageName + " = (" + CtClass.class.getName() + ") " + Util.DISPATCHER_CLASS.getName() + ".removePersistent(" + languagePersistentId + ");", clazz); clazz.addField(languageField); // Make member field String memberName = "member"; int memberPersistentId = Dispatcher.addPersistent(staged.getMember()); CtField memberField = CtField.make( "private static final " + memberClassName + " " + memberName + " = (" + memberClassName + ")" + Util.DISPATCHER_CLASS.getName() + ".removePersistent(" + memberPersistentId + ");", clazz); clazz.addField(memberField); // Make constructor String constructorSource = "private constructor(" + Util.EXPRESSION_CLASS.getName() + "[] arguments, " + Util.STATIC_INFO_CLASS.getName() + " staticInfo, " + Util.CLOSURE_HOLDER_CLASS.getName() + " closureHolder) {\n" + " super(arguments, staticInfo, closureHolder);\n" + "}"; CtConstructor constructor = CtNewConstructor.make(constructorSource, clazz); clazz.addConstructor(constructor); // Make factory-like invoke methods ImmutableList<Use.Argument> args = staged.getArguments(); StringBuilder invokeSource = new StringBuilder(); if (staged.isStrict() || Util.isCarrier(staged.getType())) { invokeSource.append( "public static " + staged.getType().getCtClass().getName() + " invoke("); } else if (staged.getType().isReference()) { invokeSource.append("public static " + Util.LOCAL_CARRIER_CLASS.getName() + " invoke("); } else { invokeSource.append("public static " + clazz.getName() + " invoke("); } if (staged.getArguments().size() > 253) { invokeSource.append("Object[] objectArguments, "); if (!staged.getStaticInfoElements().isEmpty()) { invokeSource.append(Util.STATIC_INFO_CLASS.getName() + " staticInfo, "); } invokeSource.append(Util.CLOSURE_HOLDER_CLASS.getName() + " closureHolder) {\n"); for (int i = 0; i < args.size(); i++) { if (Util.isGlobalCarrier(args.get(i).getType())) { invokeSource .append(" ") .append(Util.GLOBAL_CARRIER_CLASS.getName()) .append(" argument") .append(i) .append(" = (") .append(Util.GLOBAL_CARRIER_CLASS.getName()) .append(") objectArguments[") .append(i) .append("];\n"); } else if (Util.isLocalCarrier(args.get(i).getType()) || (args.get(i).getType().isReference() && !Util.couldBeGlobalCarrier(args.get(i).getType()))) { invokeSource .append(" ") .append(Util.LOCAL_CARRIER_CLASS.getName()) .append(" argument") .append(i) .append(" = (") .append(Util.LOCAL_CARRIER_CLASS.getName()) .append(") objectArguments[") .append(i) .append("];\n"); } else if (args.get(i).getType().isReference()) { invokeSource .append(" ") .append(Type.OBJECT.getCtClass().getName()) .append(" argument") .append(i) .append(" = objectArguments[") .append(i) .append("];\n"); } else { invokeSource .append(" ") .append(Util.EXPRESSION_CLASS.getName()) .append(" argument") .append(i) .append(" = (") .append(Util.EXPRESSION_CLASS.getName()) .append(") objectArguments[") .append(i) .append("];\n"); } } } else { for (int i = 0; i < args.size(); i++) { if (Util.isGlobalCarrier(args.get(i).getType())) { invokeSource.append(Util.GLOBAL_CARRIER_CLASS.getName()).append(" argument").append(i); } else if (Util.isLocalCarrier(args.get(i).getType()) || (args.get(i).getType().isReference() && !Util.couldBeGlobalCarrier(args.get(i).getType()))) { invokeSource.append(Util.LOCAL_CARRIER_CLASS.getName()).append(" argument").append(i); } else if (args.get(i).getType().isReference()) { invokeSource.append(Type.OBJECT.getCtClass().getName()).append(" argument").append(i); } else { invokeSource.append(Util.EXPRESSION_CLASS.getName()).append(" argument").append(i); } invokeSource.append(", "); } if (!staged.getStaticInfoElements().isEmpty()) { invokeSource.append(Util.STATIC_INFO_CLASS.getName() + " staticInfo, "); } invokeSource.append(Util.CLOSURE_HOLDER_CLASS.getName() + " closureHolder) {\n"); } invokeSource.append( " " + Util.EXPRESSION_CLASS.getName() + " payload;\n" + " " + Util.EXPRESSION_CLASS.getName() + "[] arguments = new " + Util.EXPRESSION_CLASS.getName() + "[" + args.size() + "];\n"); for (int i = 0; i < args.size(); i++) { if (Util.isGlobalCarrier(args.get(i).getType())) { invokeSource.append( " payload = " + Util.DISPATCHER_CLASS.getName() + ".unloadGlobalCarrierChecked(argument" + i + ");\n" + " if (!(" + generateLanguageAcceptCheck( "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap) + ")) {\n" + " arguments[" + i + "] = " + Util.DISPATCHER_CLASS.getName() + ".selfLiftGlobalCarrier(argument" + i + ");\n" + " } else {\n" + " arguments[" + i + "] = payload;\n" + " }\n"); } else if (Util.isLocalCarrier(args.get(i).getType()) || (args.get(i).getType().isReference() && !Util.couldBeGlobalCarrier(args.get(i).getType()))) { invokeSource.append( " payload = " + Util.DISPATCHER_CLASS.getName() + ".unloadLocalCarrierChecked(argument" + i + ");\n" + " if (" + generateLanguageAcceptCheck( "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap) + ") {\n" + " arguments[" + i + "] = payload.getRaw();\n" + " } else {\n" + " payload.evaluate();\n" // This handles the weird case when the materialized argument could be globally // carried and might need unloading and checking // We know that it is an ObjectValue instance + " " + Util.EXPRESSION_CLASS.getName() + " value = payload.asValueIfEvaluated();\n" + " Object obj = value.materializeAsObject(); \n" + " if (obj instanceof " + Util.GLOBAL_CARRIER_CLASS.getName() + ") {\n" + " payload = " + Util.DISPATCHER_CLASS.getName() + ".unloadGlobalCarrier((" + Util.GLOBAL_CARRIER_CLASS.getName() + ") obj);\n" + " if (payload == null || !(" + generateLanguageAcceptCheck( "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap) + ")) {\n" + " arguments[" + i + "] = value;\n" + " } else {\n" + " arguments[" + i + "] = payload;\n" + " }\n" + " } else {\n" + " arguments[" + i + "] = value;\n" + " }\n" + " }\n"); } else if (args.get(i).getType().isReference()) { invokeSource.append( " if (argument" + i + " instanceof " + Util.LOCAL_CARRIER_CLASS.getName() + ") {\n" + " payload = " + Util.DISPATCHER_CLASS.getName() + ".unloadLocalCarrierChecked((" + Util.LOCAL_CARRIER_CLASS.getName() + ") argument" + i + ");\n" + " if (" + generateLanguageAcceptCheck( "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap) + ") {\n" + " arguments[" + i + "] = payload.getRaw();\n" + " } else {\n" + " payload.evaluate();\n" // This handles the weird case when the materialized argument could be globally // carried and might need unloading and checking // We know that it is an ObjectValue instance + " " + Util.EXPRESSION_CLASS.getName() + " value = payload.asValueIfEvaluated();\n" + " Object obj = value.materializeAsObject(); \n" + " if (obj instanceof " + Util.GLOBAL_CARRIER_CLASS.getName() + ") {\n" + " payload = " + Util.DISPATCHER_CLASS.getName() + ".unloadGlobalCarrier((" + Util.GLOBAL_CARRIER_CLASS.getName() + ") obj);\n" + " if (payload == null || !(" + generateLanguageAcceptCheck( "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap) + ")) {\n" + " arguments[" + i + "] = value;\n" + " } else {\n" + " arguments[" + i + "] = payload;\n" + " }\n" + " } else {\n" + " arguments[" + i + "] = value;\n" + " }\n" + " }\n" + " } else if (argument" + i + " instanceof " + Util.GLOBAL_CARRIER_CLASS.getName() + ") {\n" + " payload = " + Util.DISPATCHER_CLASS.getName() + ".unloadGlobalCarrierChecked((" + Util.GLOBAL_CARRIER_CLASS.getName() + ") argument" + i + ");\n" + " if (!(" + generateLanguageAcceptCheck( "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap) + ")) {\n" + " arguments[" + i + "] = " + Util.DISPATCHER_CLASS.getName() + ".selfLiftGlobalCarrier((" + Util.GLOBAL_CARRIER_CLASS.getName() + ") argument" + i + ");\n" + " } else {\n" + " arguments[" + i + "] = payload;\n" + " }\n" + " } else {\n" + " arguments[" + i + "] = " + Util.DISPATCHER_CLASS.getName() + "." + Util.getLiftMethodName(Type.OBJECT) + "(argument" + i + ");\n" + " }\n"); } else { invokeSource.append( " if (" + generateLanguageAcceptCheck( "argument" + i, args.get(i).getAcceptedLanguages(), acceptedLanguageMap) + ") {\n" + " arguments[" + i + "] = argument" + i + generateConversionSuffix(args.get(i).getType()) + ".getRaw();\n" + " } else {\n" + " argument" + i + ".evaluate();\n" + " arguments[" + i + "] = argument" + i + ".asValueIfEvaluated();\n" + " }\n"); } } String construction; if (!staged.getStaticInfoElements().isEmpty()) { construction = "new " + clazz.getName() + "(arguments, staticInfo, closureHolder)"; } else { construction = "new " + clazz.getName() + "(arguments, null, closureHolder)"; } if (staged.isStrict()) { Type type = staged.getType(); if (type.isReference()) { invokeSource.append( " return (" + type.getCtClass().getName() + ") (" + construction + ").materializeAsObject();\n"); } else if (type.equals(Type.BOOLEAN)) { invokeSource.append(" return (" + construction + ").materializeAsBoolean();\n"); } else if (type.equals(Type.INT)) { invokeSource.append(" return (" + construction + ").materializeAsInteger();\n"); } else if (type.equals(Type.LONG)) { invokeSource.append(" return (" + construction + ").materializeAsLong();\n"); } else if (type.equals(Type.FLOAT)) { invokeSource.append(" return (" + construction + ").materializeAsFloat();\n"); } else if (type.equals(Type.DOUBLE)) { invokeSource.append(" return (" + construction + ").materializeAsDouble();\n"); } else if (type.equals(Type.BYTE)) { invokeSource.append(" return (" + construction + ").materializeAsByte();\n"); } else if (type.equals(Type.CHAR)) { invokeSource.append(" return (" + construction + ").materializeAsCharacter();\n"); } else if (type.equals(Type.SHORT)) { invokeSource.append(" return (" + construction + ").materializeAsShort();\n"); } else if (type.equals(Type.VOID)) { invokeSource.append(" return (" + construction + ").evaluate();\n"); } } else { if (Util.isGlobalCarrier(staged.getType())) { if (staged.getType().getCtClass().equals(Util.GLOBAL_CARRIER_CLASS)) { invokeSource.append( " return new " + Util.GLOBAL_CARRIER_CLASS.getName() + "(" + construction + ");\n"); } else { CarrierTransformer.transformCarrierChecked(staged.getType().getCtClass()); invokeSource.append( " return new " + staged.getType().getCtClass().getName() + "((" + Util.DISAMBIGUATION_PARAMETER_CLASS.getName() + ") null, " + construction + ");\n"); } } else if (Util.isLocalCarrier(staged.getType())) { if (staged.getType().getCtClass().equals(Util.LOCAL_CARRIER_CLASS)) { invokeSource.append( " return new " + Util.LOCAL_CARRIER_CLASS.getName() + "(" + construction + ");\n"); } else { CarrierTransformer.transformCarrierChecked(staged.getType().getCtClass()); invokeSource.append( " return new " + staged.getType().getCtClass().getName() + "((" + Util.DISAMBIGUATION_PARAMETER_CLASS.getName() + ") null, " + construction + ");\n"); } } else if (staged.getType().isReference()) { invokeSource.append( " return new " + Util.LOCAL_CARRIER_CLASS.getName() + "(" + construction + ");\n"); } else { invokeSource.append(" return " + construction + ";\n"); } } invokeSource.append("}"); CtMethod invoke = CtMethod.make(invokeSource.toString(), clazz); clazz.addMethod(invoke); // Make isomorphic hash code method String isomorphicHashCodeSource = "int isomorphicHashCode() {\n" + " if (!$0.isomorphicHashCodeHasBeenCalculated) {\n" + " super.isomorphicHashCode(" + staged.getMember().hashCode() + ");\n" + " }" + " return $0.isomorphicHashCode;" + "}"; CtMethod isomorphicHashCode = CtMethod.make(isomorphicHashCodeSource, clazz); clazz.addMethod(isomorphicHashCode); // Make isomorphism check method String isIsomorphicToSource = "boolean isIsomorphicTo(java.util.IdentityHashMap identityMap, " + Util.EXPRESSION_CLASS.getName() + " expression) {\n" + " if (!(expression instanceof " + clazz.getName() + ")) { return false; }\n" + " return super.isIsomorphicTo(identityMap, expression);\n" + "}"; CtMethod isIsomorphicTo = CtMethod.make(isIsomorphicToSource, clazz); clazz.addMethod(isIsomorphicTo); // Make cache clone (with empty leaves to save memory) creation method String cacheCloneSource = Util.EXPRESSION_CLASS.getName() + " cacheClone(java.util.IdentityHashMap identityMap) {\n" + " " + Util.EXPRESSION_CLASS.getName() + " e = (" + Util.EXPRESSION_CLASS.getName() + ") identityMap.get(this);\n" + " if (e != null) {\n" + " return e;\n" + " } else {\n" + " " + Util.EXPRESSION_CLASS.getName() + "[] clonedArguments = super.cacheCloneArguments(identityMap);\n" + " if (clonedArguments != null) {\n" + " " + clazz.getName() + " s = new " + clazz.getName() + "(clonedArguments, $0.staticInfo, null);\n" + " s.isomorphicHashCode = $0.isomorphicHashCode();\n" + " s.isomorphicHashCodeHasBeenCalculated = true;\n" + " identityMap.put(this, s);\n" + " return s;\n" + " } else {\n" + " identityMap.put(this, this);\n" + " return this;\n" + " }\n" + " }\n" + "}"; CtMethod cacheClone = CtMethod.make(cacheCloneSource, clazz); clazz.addMethod(cacheClone); // Make language acceptance check method String isAcceptedBySource = "boolean isAcceptedBy(" + CtClass.class.getName() + " language) { return $0.language.equals(language); }"; CtMethod isAcceptedBy = CtMethod.make(isAcceptedBySource, clazz); clazz.addMethod(isAcceptedBy); // Make polymorphic member retrieval method String getMemberSource = "public " + memberClassName + " getMember() { return $0." + memberName + "; }"; CtMethod getMember = CtMethod.make(getMemberSource, clazz); clazz.addMethod(getMember); // Make evaluation method Class<?> closureInterface = getClosureInterface(staged.getType()); String evaluateSource; if (Util.isGlobalCarrier(staged.getType()) && !staged.isStrict()) { evaluateSource = "public void evaluate() { throw new UnsupportedOperationException(); }"; } else { evaluateSource = "public void evaluate() {\n" + " if ($0.value != null) { return; }\n" + " " + closureInterface.getName() + " closure;\n" + " " + Util.ENVIRONMENT_CLASS.getName() + " environment;\n" + " if ($0.closureHolder == null) {\n" + " " + Util.CLOSURE_HOLDER_CLASS.getName() + " cachedClosureHolder = " + GlobalCache.class.getName() + ".getCachedClosureHolder($0);\n" + " if (cachedClosureHolder == null) {\n" + " " + Util.BINDER_CLASS.getName() + " binder = new " + Util.BINDER_CLASS.getName() + "($0);\n" + " closure = " + staged.getLanguage().getName() + ".make" + closureInterface.getSimpleName() + "($0, binder, false);\n" + " environment = new " + Util.ENVIRONMENT_CLASS.getName() + "($0, binder.getBoundCount());\n" + " if (!binder.inspectionOccurred()) {\n" + " " + GlobalCache.class.getName() + ".cache($0, closure, binder.getBoundCount());\n" + " }\n" + " } else {\n" + " closure = (" + closureInterface.getName() + ") cachedClosureHolder.getClosure();\n" + " environment = new " + Util.ENVIRONMENT_CLASS.getName() + "($0, cachedClosureHolder.getEnvironmentSize());\n" + " }\n" + " } else {\n" + " closure = (" + closureInterface.getName() + ") $0.closureHolder.getClosure();\n" + " if (closure == null) {\n" + " " + Util.CLOSURE_HOLDER_CLASS.getName() + " cachedClosureHolder = " + GlobalCache.class.getName() + ".getCachedClosureHolder($0);\n" + " if (cachedClosureHolder == null) {\n" + " " + Util.BINDER_CLASS.getName() + " binder = new " + Util.BINDER_CLASS.getName() + "($0);\n" + " closure = " + staged.getLanguage().getName() + ".make" + closureInterface.getSimpleName() + "($0, binder, $0.closureHolder.isPermanent());\n" + " environment = new " + Util.ENVIRONMENT_CLASS.getName() + "($0, binder.getBoundCount());\n" + " if (!binder.inspectionOccurred()) {\n" + " $0.closureHolder.set(closure, binder.getBoundCount());\n" + " " + GlobalCache.class.getName() + ".cache($0, closure, binder.getBoundCount());\n" + " }\n" + " } else {\n" + " closure = (" + closureInterface.getName() + ") cachedClosureHolder.getClosure();\n" + " environment = new " + Util.ENVIRONMENT_CLASS.getName() + "($0, cachedClosureHolder.getEnvironmentSize());\n" + " $0.closureHolder.set(closure, cachedClosureHolder.getEnvironmentSize());\n" + " }\n" + " } else {\n" + " environment = new " + Util.ENVIRONMENT_CLASS.getName() + "($0, $0.closureHolder.getEnvironmentSize());\n" + " }\n" + " }\n" + (staged.getType().equals(Type.VOID) ? " closure.evaluate(environment);\n" : " $0.value = " + Util.DISPATCHER_CLASS.getName() + "." + Util.getLiftMethodName(staged.getType()) + "(closure.evaluate(environment));\n") + "}"; } CtMethod evaluate = CtMethod.make(evaluateSource, clazz); clazz.addMethod(evaluate); clazz.toClass(); } catch (CannotCompileException | NotFoundException e) { throw new RuntimeException(e); } return clazz; }