Skip to content

Commit

Permalink
property internals upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmonkey4eva committed Mar 22, 2023
1 parent 2077849 commit c08c44d
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.denizenscript.denizencore.exceptions;

public class Unreachable extends RuntimeException {

private static final long serialVersionUID = 3159108944857792068L;

/**
* Indicates that a code section is known to be unreachable at runtime, but the compiler does not know this.
*/
public Unreachable() {
}

@Override
public String getMessage() {
return "This code is unreachable.";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.denizenscript.denizencore.objects.properties;

import com.denizenscript.denizencore.objects.ObjectTag;

public abstract class ObjectProperty<T extends ObjectTag> implements Property {

public T object;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,30 @@

import com.denizenscript.denizencore.objects.Mechanism;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.tags.Attribute;

public interface Property {

String getPropertyString();
default ObjectTag getPropertyValue() {
String str = getPropertyString();
return str == null ? null : new ElementTag(str);
}

@Deprecated
default String getPropertyString() {
ObjectTag res = getPropertyValue();
return res == null ? null : getPropertyValue().identify();
}

String getPropertyId();

@Deprecated
default ObjectTag getObjectAttribute(Attribute attribute) {
return null;
}

@Deprecated
default void adjust(Mechanism mechanism) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,35 @@
import com.denizenscript.denizencore.tags.ObjectTagProcessor;
import com.denizenscript.denizencore.utilities.AsciiMatcher;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.ReflectionHelper;
import com.denizenscript.denizencore.utilities.codegen.CodeGenUtil;
import com.denizenscript.denizencore.utilities.codegen.MethodGenerator;
import com.denizenscript.denizencore.utilities.debugging.Debug;
import com.denizenscript.denizencore.utilities.debugging.DebugInternals;
import com.denizenscript.denizencore.utilities.text.StringHolder;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.lang.invoke.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

public class PropertyParser {

@FunctionalInterface
public interface PropertyGetter {
public interface PropertyGetter<T extends ObjectTag> {

Property get(ObjectTag obj);
Property get(T obj);
}

public static class ClassPropertiesInfo {

public ObjectType<? extends ObjectTag> objectType;

public List<PropertyGetter> allProperties = new ArrayList<>();

public List<PropertyGetter> propertiesWithMechs = new ArrayList<>();
Expand Down Expand Up @@ -86,7 +96,7 @@ public static <T extends Property, R extends ObjectTag> void registerTag(Class<T

public static <T extends Property, R extends ObjectTag> void registerTagInternal(Class<T> propType, Class<R> returnType, String name, PropertyTagWithReturn<T, R> runnable, String[] variants, boolean isStatic) {
final PropertyParser.PropertyGetter getter = PropertyParser.currentlyRegisteringProperty;
ObjectTagProcessor<?> tagProcessor = PropertyParser.currentlyRegisteringObjectType.tagProcessor;
ObjectTagProcessor<?> tagProcessor = PropertyParser.currentlyRegisteringPropertyInfo.objectType.tagProcessor;
tagProcessor.registerTagInternal(returnType, name, (attribute, object) -> {
Property prop = getter.get(object);
if (prop == null) {
Expand All @@ -110,8 +120,9 @@ public interface PropertyMechanismWithParam<T extends Property, P extends Object
}

public static <T extends Property> void registerMechanism(Class<T> propType, String name, PropertyMechanism<T> runner, String... deprecatedVariants) {
final PropertyParser.PropertyGetter getter = PropertyParser.currentlyRegisteringProperty;
ObjectTagProcessor<?> tagProcessor = PropertyParser.currentlyRegisteringObjectType.tagProcessor;
final PropertyParser.PropertyGetter getter = currentlyRegisteringProperty;
currentlyRegisteringPropertyInfo.propertiesByMechanism.put(name, getter);
ObjectTagProcessor<?> tagProcessor = currentlyRegisteringPropertyInfo.objectType.tagProcessor;
tagProcessor.registerMechanism(name, true, (object, mechanism) -> {
Property prop = getter.get(object);
if (prop == null) {
Expand All @@ -137,16 +148,19 @@ public static <T extends Property, P extends ObjectTag> void registerMechanism(C
}, deprecatedVariants);
}

public static Class currentlyRegisteringPropertyClass;
public static PropertyGetter<? extends ObjectTag> currentlyRegisteringProperty;

public static PropertyGetter currentlyRegisteringProperty;
public static ClassPropertiesInfo currentlyRegisteringPropertyInfo;

public static ObjectType currentlyRegisteringObjectType;

public static void registerPropertyGetter(PropertyGetter getter, Class<? extends ObjectTag> object, String[] tags, String[] mechs, Class property) {
currentlyRegisteringPropertyClass = property;
public static <T extends ObjectTag> void registerPropertyGetter(PropertyGetter<T> getter, Class<T> objectType, String[] tags, String[] mechs, Class<? extends Property> property) {
ClassPropertiesInfo propInfo = propertiesByClass.get(objectType);
if (propInfo == null) {
propInfo = new ClassPropertiesInfo();
propInfo.objectType = ObjectFetcher.getType(objectType);
propertiesByClass.put(objectType, propInfo);
}
currentlyRegisteringPropertyInfo = propInfo;
currentlyRegisteringProperty = getter;
currentlyRegisteringObjectType = ObjectFetcher.getType(object);
try {
for (Method registerMethod : property.getDeclaredMethods()) {
if ((registerMethod.getName().equals("register") || registerMethod.getName().equals("registerTags")) && registerMethod.getParameterCount() == 0) {
Expand All @@ -159,13 +173,7 @@ public static void registerPropertyGetter(PropertyGetter getter, Class<? extends
Debug.echoError(ex);
}
currentlyRegisteringProperty = null;
currentlyRegisteringObjectType = null;
currentlyRegisteringPropertyClass = null;
ClassPropertiesInfo propInfo = propertiesByClass.get(object);
if (propInfo == null) {
propInfo = new ClassPropertiesInfo();
propertiesByClass.put(object, propInfo);
}
currentlyRegisteringPropertyInfo = null;
propInfo.allProperties.add(getter);
// TODO: warn/remove legacy name-based tag/mechanism registrations
if (tags != null) {
Expand Down Expand Up @@ -198,20 +206,64 @@ public static String[] getStringField(Class property, String fieldName) {
return null;
}

public static void registerProperty(final Class property, Class<? extends ObjectTag> object, PropertyGetter getter) {
registerPropertyGetter(getter, object, getStringField(property, "handledTags"), getStringField(property, "handledMechs"), property);
public static <T extends ObjectTag> void registerProperty(final Class<? extends Property> property, Class<T> objectType, PropertyGetter<T> getter) {
registerPropertyGetter(getter, objectType, getStringField(property, "handledTags"), getStringField(property, "handledMechs"), property);
}

public static void registerProperty(final Class property, Class<? extends ObjectTag> object) {
public static long totalGenerated = 0;
public static final String GETTER_PATH = Type.getInternalName(PropertyGetter.class);
public static final Method GETTER_GET_METHOD = ReflectionHelper.getMethod(PropertyGetter.class, "get", ObjectTag.class);
public static final String GETTER_GET_DESCRIPTOR = Type.getMethodDescriptor(GETTER_GET_METHOD);
public static final Field OBJECT_PROPERTY_OBJECT_FIELD = ReflectionHelper.getFields(ObjectProperty.class).get("object", ObjectTag.class);

public static <T extends ObjectTag> void registerProperty(final Class<? extends Property> property, Class<T> objectType) {
try {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
CallSite site = LambdaMetafactory.metafactory(lookup, "get", // PropertyGetter#get
MethodType.methodType(PropertyGetter.class), // Signature of invoke method
MethodType.methodType(Property.class, ObjectTag.class), // signature of PropertyGetter#get
lookup.findStatic(property, "getFrom", MethodType.methodType(property, ObjectTag.class)), // signature of getFrom
MethodType.methodType(property, ObjectTag.class)); // Signature of getFrom again
PropertyGetter getter = (PropertyGetter) site.getTarget().invoke();
registerProperty(property, object, getter);
Method describesMethod = Arrays.stream(property.getMethods()).filter(m -> m.getName().equals("describes") && (m.getModifiers() & Modifier.STATIC) != 0).findFirst().get();
// Gen mini-class
String cleanName = CodeGenUtil.cleanName(DebugInternals.getClassNameOpti(property).replace('.', '_'));
String className = CodeGenUtil.CORE_GEN_PACKAGE + "Properties/Prop" + (totalGenerated++) + "_" + cleanName;
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", new String[] {GETTER_PATH});
cw.visitSource("GENERATED_PROP_GETTER", null);
MethodGenerator.genDefaultConstructor(cw, className);
// ====== Gen 'get' method ======
{
MethodGenerator gen = MethodGenerator.generateMethod(className, cw, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "get", GETTER_GET_DESCRIPTOR);
MethodGenerator.Local objectLocal = gen.addLocal("object", ObjectTag.class);
Label returnLabel = new Label();
gen.loadLocal(objectLocal);
gen.cast(objectType);
gen.invokeStatic(describesMethod);
gen.jumpIfFalseTo(returnLabel);
// Construct new
Constructor<?> constructor = property.getDeclaredConstructors()[0];
gen.constructNew(property);
gen.stackDuplicate();
if (constructor.getParameters().length == 1) {
gen.loadLocal(objectLocal);
gen.cast(objectType);
}
constructor.setAccessible(true);
gen.invokeSpecial(constructor);
if (ObjectProperty.class.isAssignableFrom(property)) {
gen.stackDuplicate();
gen.loadLocal(objectLocal);
gen.setInstanceField(OBJECT_PROPERTY_OBJECT_FIELD);
}
gen.returnValue(ObjectTag.class);
// Not described so return null
gen.advanceAndLabel(returnLabel);
gen.loadNull();
gen.returnValue(ObjectTag.class);
gen.end();
}
// ====== Compile and register the result ======
cw.visitEnd();
byte[] compiled = cw.toByteArray();
Class<?> generatedClass = CodeGenUtil.loader.define(className.replace('/', '.'), compiled);
Object result = generatedClass.getConstructors()[0].newInstance();
PropertyGetter<T> getter = (PropertyGetter<T>) result;
registerProperty(property, objectType, getter);
}
catch (Throwable e) {
Debug.echoError("Unable to register property '" + DebugInternals.getClassNameOpti(property) + "'!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,7 @@ public static CommandExecutor generateExecutorFor(Class<? extends AbstractComman
}
// ====== Gen class ======
String cmdCleanName = CodeGenUtil.cleanName(DebugInternals.getClassNameOpti(cmdClass).replace('.', '_'));
if (cmdCleanName.length() > 50) {
cmdCleanName = cmdCleanName.substring(0, 50);
}
String className = CodeGenUtil.COMMAND_GEN_PACKAGE + "Cmd" + (totalGenerated++) + "_" + cmdCleanName;
String className = CodeGenUtil.CORE_GEN_PACKAGE + "Commands/Cmd" + (totalGenerated++) + "_" + cmdCleanName;
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, COMMAND_EXECUTOR_PATH, new String[]{});
cw.visitSource("GENERATED_CMD_EXEC", null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ public class CodeGenUtil {

public static AsciiMatcher PERMITTED_NAME_CHARS = new AsciiMatcher(AsciiMatcher.LETTERS_LOWER + AsciiMatcher.LETTERS_UPPER + AsciiMatcher.DIGITS + "_");

public static final String COMMAND_GEN_PACKAGE = "com/denizenscript/_generated_/commands/";
public static final String TAG_GEN_PACKAGE = "com/denizenscript/_generated_/tags/";

public static final String OBJECT_LOCAL_TYPE = "L" + Type.getInternalName(ObjectTag.class) + ";";
public static final String CORE_GEN_PACKAGE = "com/denizenscript/_generated_/";
public static final String OBJECT_TAG_TYPE = Type.getInternalName(ObjectTag.class);
public static final String OBJECT_LOCAL_TYPE = "L" + OBJECT_TAG_TYPE + ";";

public static DynamicClassLoader loader = new DynamicClassLoader(CodeGenUtil.class.getClassLoader());

public static String cleanName(String text) {
return PERMITTED_NAME_CHARS.trimToMatches(text);
String result = PERMITTED_NAME_CHARS.trimToMatches(text);
if (result.length() > 50) {
result = result.substring(0, 50);
}
return result;
}

public static class DynamicClassLoader extends ClassLoader {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ public void autoBox(String typeDescriptor) {
}
}

/** Sets the value given instance field from the value on top of the stack to the object next on stack. */
public void setInstanceField(Field f) {
setInstanceField(Type.getInternalName(f.getDeclaringClass()), f.getName(), Type.getDescriptor(f.getType()));
}

/** Sets the value given instance field from the value on top of the stack to the object next on stack. */
public void setInstanceField(String className, String field, String descriptor) {
mv.visitFieldInsn(Opcodes.PUTFIELD, className, field, descriptor);
}

/** Loads the given instance field from the object on top of the stack. */
public void loadInstanceField(Field f) {
loadInstanceField(Type.getInternalName(f.getDeclaringClass()), f.getName(), Type.getDescriptor(f.getType()));
Expand Down Expand Up @@ -206,6 +216,11 @@ public void finalizeLocalVar(String name, String descriptor, int index) {
mv.visitLocalVariable(name, descriptor, null, startLabel, curLabel, index);
}

/** Invokes the given constructor. */
public void constructNew(Class<?> type) {
mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(type));
}

/** Invokes the given constructor. */
public void invokeSpecial(Constructor<?> ctor) {
invokeSpecial(Type.getInternalName(ctor.getDeclaringClass()), "<init>", Type.getConstructorDescriptor(ctor));
Expand Down Expand Up @@ -261,6 +276,11 @@ public void loadInt(int intVal) {
mv.visitIntInsn(Opcodes.SIPUSH, intVal);
}

/** Loads a null value directly into the stack. */
public void loadNull() {
mv.visitInsn(Opcodes.ACONST_NULL);
}

/** Duplicates the value on top of the stack. */
public void stackDuplicate() {
mv.visitInsn(Opcodes.DUP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,7 @@ public static TagRunnable.BaseInterface<? extends ObjectTag> generatePartialTag(
try {
// ====== Gen class ======
String tagFullName = CodeGenUtil.cleanName(data.rawTag.replace('.', '_'));
if (tagFullName.length() > 50) {
tagFullName = tagFullName.substring(0, 50);
}
String className = CodeGenUtil.TAG_GEN_PACKAGE + "UserTag" + (totalGenerated++) + "_" + tagFullName;
String className = CodeGenUtil.CORE_GEN_PACKAGE + "UserTags/UserTag" + (totalGenerated++) + "_" + tagFullName;
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", new String[] {TagNamer.BASE_INTERFACE_PATH});
cw.visitSource("GENERATED_TAG", null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static <R extends ObjectTag> TagRunnable.BaseInterface<R> nameBaseInterfa
public static Object nameInternal(String fullTagName, Object tag, String typePath, String typeDescription, boolean hasObject, String runDescriptor) {
try {
// ====== Gen class ======
String className = CodeGenUtil.TAG_GEN_PACKAGE + "Tag" + (tagsGenerated++) + "_" + CodeGenUtil.cleanName(fullTagName);
String className = CodeGenUtil.CORE_GEN_PACKAGE + "Tags/Tag" + (tagsGenerated++) + "_" + CodeGenUtil.cleanName(fullTagName);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", new String[] { typePath });
cw.visitSource("GENERATED_TAG", null);
Expand Down

0 comments on commit c08c44d

Please sign in to comment.