nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java
changeset 33343 23abd10384a5
parent 33338 faf6471e1cc8
child 33688 649d5d76f602
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Wed Oct 21 10:42:20 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Wed Oct 21 19:33:58 2015 +0200
@@ -92,12 +92,17 @@
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.CompositeOperation;
+import jdk.internal.dynalink.NamedOperation;
+import jdk.internal.dynalink.Operation;
+import jdk.internal.dynalink.StandardOperation;
 import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
 import jdk.internal.dynalink.internal.InternalTypeUtilities;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -342,17 +347,27 @@
     @Override
     public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
             throws Exception {
-        // BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn".
         final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor();
-        final String op = callSiteDescriptor.getNameToken(CallSiteDescriptor.OPERATOR);
-        // Either dyn:callMethod:name(this[,args]) or dyn:callMethod(this,name[,args]).
-        if("callMethod" == op) {
-            return getCallPropWithThis(callSiteDescriptor, linkerServices);
+
+        // Handle NamedOperation(CALL_METHOD, name) separately
+        final Operation operation = callSiteDescriptor.getOperation();
+        if (operation instanceof NamedOperation) {
+            final NamedOperation namedOperation = (NamedOperation)operation;
+            if (namedOperation.getBaseOperation() == StandardOperation.CALL_METHOD) {
+                return createGuardedDynamicMethodInvocation(callSiteDescriptor,
+                        linkerServices, namedOperation.getName().toString(), methods);
+            }
         }
-        List<String> operations = callSiteDescriptor.tokenizeOperators();
+
+        List<Operation> operations = Arrays.asList(
+                CompositeOperation.getOperations(
+                        NamedOperation.getBaseOperation(operation)));
+        final Object name = NamedOperation.getName(operation);
+
         while(!operations.isEmpty()) {
-            final GuardedInvocationComponent gic = getGuardedInvocationComponent(callSiteDescriptor, linkerServices,
-                    operations);
+            final GuardedInvocationComponent gic =
+                    getGuardedInvocationComponent(callSiteDescriptor,
+                            linkerServices, operations, name);
             if(gic != null) {
                 return gic.getGuardedInvocation();
             }
@@ -361,23 +376,26 @@
         return null;
     }
 
-    protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
-            final LinkerServices linkerServices, final List<String> operations) throws Exception {
+    protected GuardedInvocationComponent getGuardedInvocationComponent(
+            final CallSiteDescriptor callSiteDescriptor,
+            final LinkerServices linkerServices,
+            final List<Operation> operations, final Object name)
+    throws Exception {
         if(operations.isEmpty()) {
             return null;
         }
-        final String op = operations.get(0);
-        // Either dyn:getProp:name(this) or dyn:getProp(this, name)
-        if("getProp".equals(op)) {
-            return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations));
+        final Operation op = operations.get(0);
+        // Either GET_PROPERTY:name(this) or GET_PROPERTY(this, name)
+        if(op == StandardOperation.GET_PROPERTY) {
+            return getPropertyGetter(callSiteDescriptor, linkerServices, pop(operations), name);
         }
-        // Either dyn:setProp:name(this, value) or dyn:setProp(this, name, value)
-        if("setProp".equals(op)) {
-            return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations));
+        // Either SET_PROPERTY:name(this, value) or SET_PROPERTY(this, name, value)
+        if(op == StandardOperation.SET_PROPERTY) {
+            return getPropertySetter(callSiteDescriptor, linkerServices, pop(operations), name);
         }
-        // Either dyn:getMethod:name(this), or dyn:getMethod(this, name)
-        if("getMethod".equals(op)) {
-            return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations));
+        // Either GET_METHOD:name(this), or GET_METHOD(this, name)
+        if(op == StandardOperation.GET_METHOD) {
+            return getMethodGetter(callSiteDescriptor, linkerServices, pop(operations), name);
         }
         return null;
     }
@@ -406,18 +424,6 @@
         return Guards.asType(assignableGuard, type);
     }
 
-    private GuardedInvocation getCallPropWithThis(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
-        switch(callSiteDescriptor.getNameTokenCount()) {
-            case 3: {
-                return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
-                        callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods);
-            }
-            default: {
-                return null;
-            }
-        }
-    }
-
     private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
             final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){
         final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
@@ -480,84 +486,86 @@
             MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
 
     private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor,
-            final LinkerServices linkerServices, final List<String> operations) throws Exception {
-        switch(callSiteDescriptor.getNameTokenCount()) {
-            case 2: {
-                // Must have three arguments: target object, property name, and property value.
-                assertParameterCount(callSiteDescriptor, 3);
+            final LinkerServices linkerServices, final List<Operation> operations, final Object name) throws Exception {
+        if (name == null) {
+            return getUnnamedPropertySetter(callSiteDescriptor, linkerServices, operations);
+        }
+        return getNamedPropertySetter(callSiteDescriptor, linkerServices, operations, name);
+    }
 
-                // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
-                // valid for us to convert return values proactively. Also, since we don't know what setters will be
-                // invoked, we'll conservatively presume Object return type. The one exception is void return.
-                final MethodType origType = callSiteDescriptor.getMethodType();
-                final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class);
+    private GuardedInvocationComponent getUnnamedPropertySetter(final CallSiteDescriptor callSiteDescriptor,
+            final LinkerServices linkerServices, final List<Operation> operations) throws Exception {
+        // Must have three arguments: target object, property name, and property value.
+        assertParameterCount(callSiteDescriptor, 3);
+
+        // We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
+        // valid for us to convert return values proactively. Also, since we don't know what setters will be
+        // invoked, we'll conservatively presume Object return type. The one exception is void return.
+        final MethodType origType = callSiteDescriptor.getMethodType();
+        final MethodType type = origType.returnType() == void.class ? origType : origType.changeReturnType(Object.class);
 
-                // What's below is basically:
-                //   foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
-                //     get_setter_handle(type, linkerServices))
-                // only with a bunch of method signature adjustments. Basically, retrieve method setter
-                // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
-                // component's invocation.
+        // What's below is basically:
+        //   foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
+        //     get_setter_handle(type, linkerServices))
+        // only with a bunch of method signature adjustments. Basically, retrieve method setter
+        // MethodHandle; if it is non-null, invoke it, otherwise either return null, or delegate to next
+        // component's invocation.
 
-                // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
-                // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
-                // Object return type).
-                final MethodType setterType = type.dropParameterTypes(1, 2);
-                // Bind property setter handle to the expected setter type and linker services. Type is
-                // MethodHandle(Object, String, Object)
-                final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
-                        callSiteDescriptor.changeMethodType(setterType), linkerServices);
+        // Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
+        // abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
+        // Object return type).
+        final MethodType setterType = type.dropParameterTypes(1, 2);
+        // Bind property setter handle to the expected setter type and linker services. Type is
+        // MethodHandle(Object, String, Object)
+        final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0,
+                callSiteDescriptor.changeMethodType(setterType), linkerServices);
 
-                // Cast getter to MethodHandle(O, N, V)
-                final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
-                        MethodHandle.class));
+        // Cast getter to MethodHandle(O, N, V)
+        final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType(
+                MethodHandle.class));
 
-                // Handle to invoke the setter R(MethodHandle, O, V)
-                final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
-                // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
-                final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
-                        1));
-                final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
-                        linkerServices, operations);
+        // Handle to invoke the setter R(MethodHandle, O, V)
+        final MethodHandle invokeHandle = MethodHandles.exactInvoker(setterType);
+        // Handle to invoke the setter, dropping unnecessary fold arguments R(MethodHandle, O, N, V)
+        final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandle, 2, type.parameterType(
+                1));
+        final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
+                linkerServices, operations, null);
 
-                final MethodHandle fallbackFolded;
-                if(nextComponent == null) {
-                    // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
-                    fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
-                            type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
-                } else {
-                    // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
-                    // extra argument resulting from fold
-                    fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
-                            0, MethodHandle.class);
-                }
+        final MethodHandle fallbackFolded;
+        if(nextComponent == null) {
+            // Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
+            fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
+                    type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
+        } else {
+            // Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
+            // extra argument resulting from fold
+            fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
+                    0, MethodHandle.class);
+        }
 
-                // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
-                final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
-                            IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
-                if(nextComponent == null) {
-                    return getClassGuardedInvocationComponent(compositeSetter, type);
-                }
-                return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
-            }
-            case 3: {
-                // Must have two arguments: target object and property value
-                assertParameterCount(callSiteDescriptor, 2);
-                final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
-                        callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters);
-                // If we have a property setter with this name, this composite operation will always stop here
-                if(gi != null) {
-                    return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
-                }
-                // If we don't have a property setter with this name, always fall back to the next operation in the
-                // composite (if any)
-                return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations);
-            }
-            default: {
-                // More than two name components; don't know what to do with it.
-                return null;
-            }
+        // fold(R(MethodHandle, O, N, V), MethodHandle(O, N, V))
+        final MethodHandle compositeSetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
+                    IS_METHOD_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
+        if(nextComponent == null) {
+            return getClassGuardedInvocationComponent(compositeSetter, type);
         }
+        return nextComponent.compose(compositeSetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
+    }
+
+    private GuardedInvocationComponent getNamedPropertySetter(final CallSiteDescriptor callSiteDescriptor,
+            final LinkerServices linkerServices, final List<Operation> operations, final Object name) throws Exception {
+        // Must have two arguments: target object and property value
+        assertParameterCount(callSiteDescriptor, 2);
+        final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
+                name.toString(), propertySetters);
+        // If we have a property setter with this name, this composite operation will always stop here
+        if(gi != null) {
+            return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS);
+        }
+        // If we don't have a property setter with this name, always fall back to the next operation in the
+        // composite (if any)
+        return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, operations, name);
     }
 
     private static final Lookup privateLookup = new Lookup(MethodHandles.lookup());
@@ -571,87 +579,89 @@
     private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
 
     private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
-            final LinkerServices linkerServices, final List<String> ops) throws Exception {
-        switch(callSiteDescriptor.getNameTokenCount()) {
-            case 2: {
-                // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
-                // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
-                // runtime might not allow coercing at that call site.
-                final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
-                // Must have exactly two arguments: receiver and name
-                assertParameterCount(callSiteDescriptor, 2);
+            final LinkerServices linkerServices, final List<Operation> ops, final Object name) throws Exception {
+        if (name == null) {
+            return getUnnamedPropertyGetter(callSiteDescriptor, linkerServices, ops);
+        }
+
+        return getNamedPropertyGetter(callSiteDescriptor, linkerServices, ops, name);
+    }
 
-                // What's below is basically:
-                //   foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
-                // only with a bunch of method signature adjustments. Basically, retrieve method getter
-                // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
-                // or delegate to next component's invocation.
+    private GuardedInvocationComponent getUnnamedPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
+            final LinkerServices linkerServices, final List<Operation> ops) throws Exception {
+        // Since we can't know what kind of a getter we'll get back on different invocations, we'll just
+        // conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
+        // runtime might not allow coercing at that call site.
+        final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
+        // Must have exactly two arguments: receiver and name
+        assertParameterCount(callSiteDescriptor, 2);
 
-                final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
-                        AnnotatedDynamicMethod.class));
-                final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
-                        GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices);
-                final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
-                        callSiteBoundMethodGetter);
-                // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
-                final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
-                        MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
-                // Since it's in the target of a fold, drop the unnecessary second argument
-                // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
-                final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
-                        type.parameterType(1));
-                final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
-                        linkerServices, ops);
+        // What's below is basically:
+        //   foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle)
+        // only with a bunch of method signature adjustments. Basically, retrieve method getter
+        // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null,
+        // or delegate to next component's invocation.
+
+        final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
+                AnnotatedDynamicMethod.class));
+        final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
+                GET_ANNOTATED_METHOD, 1, callSiteDescriptor, linkerServices);
+        final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
+                callSiteBoundMethodGetter);
+        // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
+        final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
+                MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
+        // Since it's in the target of a fold, drop the unnecessary second argument
+        // Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
+        final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
+                type.parameterType(1));
+        final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
+                linkerServices, ops, null);
 
-                final MethodHandle fallbackFolded;
-                if(nextComponent == null) {
-                    // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
-                    fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
-                            type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
-                } else {
-                    // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
-                    // drop the extra argument resulting from fold and to change its return type to Object.
-                    final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
-                    final MethodType nextType = nextInvocation.type();
-                    fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
-                            nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
-                }
+        final MethodHandle fallbackFolded;
+        if(nextComponent == null) {
+            // Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
+            fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
+                    type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
+        } else {
+            // Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
+            // drop the extra argument resulting from fold and to change its return type to Object.
+            final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
+            final MethodType nextType = nextInvocation.type();
+            fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
+                    nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
+        }
 
-                // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
-                final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
-                            IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
-                if(nextComponent == null) {
-                    return getClassGuardedInvocationComponent(compositeGetter, type);
-                }
-                return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
-            }
-            case 3: {
-                // Must have exactly one argument: receiver
-                assertParameterCount(callSiteDescriptor, 1);
-                // Fixed name
-                final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken(
-                        CallSiteDescriptor.NAME_OPERAND));
-                if(annGetter == null) {
-                    // We have no such property, always delegate to the next component operation
-                    return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
-                }
-                final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices);
-                // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
-                // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
-                // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
-                // we're linking against a field getter, don't make the assumption.
-                // NOTE: No delegation to the next component operation if we have a property with this name, even if its
-                // value is null.
-                final ValidationType validationType = annGetter.validationType;
-                // TODO: we aren't using the type that declares the most generic getter here!
-                return new GuardedInvocationComponent(getter, getGuard(validationType,
-                        callSiteDescriptor.getMethodType()), clazz, validationType);
-            }
-            default: {
-                // Can't do anything with more than 3 name components
-                return null;
-            }
+        // fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
+        final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
+                    IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
+        if(nextComponent == null) {
+            return getClassGuardedInvocationComponent(compositeGetter, type);
         }
+        return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
+    }
+
+    private GuardedInvocationComponent getNamedPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
+            final LinkerServices linkerServices, final List<Operation> ops, final Object name) throws Exception {
+        // Must have exactly one argument: receiver
+        assertParameterCount(callSiteDescriptor, 1);
+        // Fixed name
+        final AnnotatedDynamicMethod annGetter = propertyGetters.get(name.toString());
+        if(annGetter == null) {
+            // We have no such property, always delegate to the next component operation
+            return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops, name);
+        }
+        final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices);
+        // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being
+        // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the
+        // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If
+        // we're linking against a field getter, don't make the assumption.
+        // NOTE: No delegation to the next component operation if we have a property with this name, even if its
+        // value is null.
+        final ValidationType validationType = annGetter.validationType;
+        // TODO: we aren't using the type that declares the most generic getter here!
+        return new GuardedInvocationComponent(getter, getGuard(validationType,
+                callSiteDescriptor.getMethodType()), clazz, validationType);
     }
 
     private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {
@@ -679,64 +689,67 @@
     private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
 
     private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor,
-            final LinkerServices linkerServices, final List<String> ops) throws Exception {
+            final LinkerServices linkerServices, final List<Operation> ops, final Object name) throws Exception {
         // The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
         // be visible outside of this linker, declare it to return Object.
         final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
-        switch(callSiteDescriptor.getNameTokenCount()) {
-            case 2: {
-                // Must have exactly two arguments: receiver and name
-                assertParameterCount(callSiteDescriptor, 2);
-                final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
-                        linkerServices, ops);
-                if(nextComponent == null || !InternalTypeUtilities.areAssignable(DynamicMethod.class,
-                        nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
-                    // No next component operation, or it can never produce a dynamic method; just return a component
-                    // for this operation.
-                    return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
-                }
+        if (name == null) {
+            return getUnnamedMethodGetter(callSiteDescriptor, linkerServices, ops, type);
+        }
+
+        return getNamedMethodGetter(callSiteDescriptor, linkerServices, ops, name, type);
+    }
 
-                // What's below is basically:
-                // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
-                // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
-                // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
+    private GuardedInvocationComponent getUnnamedMethodGetter(final CallSiteDescriptor callSiteDescriptor,
+            final LinkerServices linkerServices, final List<Operation> ops, final MethodType type) throws Exception {
+        // Must have exactly two arguments: receiver and name
+        assertParameterCount(callSiteDescriptor, 2);
+        final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
+                linkerServices, ops, null);
+        if(nextComponent == null || !InternalTypeUtilities.areAssignable(DynamicMethod.class,
+                nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
+            // No next component operation, or it can never produce a dynamic method; just return a component
+            // for this operation.
+            return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
+        }
+
+        // What's below is basically:
+        // foldArguments(guardWithTest(isNotNull, identity, nextComponent.invocation), getter) only with a
+        // bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
+        // DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
 
-                final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
-                // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
-                final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
-                        OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
-                final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
-                // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
-                // return type.
-                assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
-                // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
-                final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
-                        Object.class);
-                // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
-                final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
-                        IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
+        final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
+        // Since it is part of the foldArgument() target, it will have extra args that we need to drop.
+        final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
+                OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
+        final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
+        // The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
+        // return type.
+        assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
+        // Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
+        final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
+                Object.class);
+        // Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
+        final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
+                IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
 
-                return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
-            }
-            case 3: {
-                // Must have exactly one argument: receiver
-                assertParameterCount(callSiteDescriptor, 1);
-                final DynamicMethod method = getDynamicMethod(callSiteDescriptor.getNameToken(
-                        CallSiteDescriptor.NAME_OPERAND));
-                if(method == null) {
-                    // We have no such method, always delegate to the next component
-                    return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops);
-                }
-                // No delegation to the next component of the composite operation; if we have a method with that name,
-                // we'll always return it at this point.
-                return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
-                        MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
-            }
-            default: {
-                // Can't do anything with more than 3 name components
-                return null;
-            }
+        return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
+    }
+
+    private GuardedInvocationComponent getNamedMethodGetter(final CallSiteDescriptor callSiteDescriptor,
+            final LinkerServices linkerServices, final List<Operation> ops, final Object name, final MethodType type)
+            throws Exception {
+        // Must have exactly one argument: receiver
+        assertParameterCount(callSiteDescriptor, 1);
+        final DynamicMethod method = getDynamicMethod(name.toString());
+        if(method == null) {
+            // We have no such method, always delegate to the next component
+            return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops, name);
         }
+        // No delegation to the next component of the composite operation; if we have a method with that name,
+        // we'll always return it at this point.
+        return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
+                MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
     }
 
     static class MethodPair {
@@ -765,7 +778,7 @@
 
     private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
         if(descriptor.getMethodType().parameterCount() != paramCount) {
-            throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
+            throw new BootstrapMethodError(descriptor.getOperation() + " must have exactly " + paramCount + " parameters.");
         }
     }
 
@@ -804,7 +817,7 @@
     @SuppressWarnings("unused")
     // This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
     // want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
-    // "dyn:getMethod" linking).
+    // GET_METHOD linking).
     private Object getDynamicMethod(final Object name) {
         return getDynamicMethod(String.valueOf(name), methods);
     }