Fri, 05 Jun 2015 14:46:52 +0530
8081809: Missing final modifier in method parameters (nashorn code convention)
Reviewed-by: attila, lagergren
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package jdk.nashorn.internal.runtime.linker;
28 import static jdk.nashorn.internal.lookup.Lookup.MH;
29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
31 import java.lang.invoke.MethodHandle;
32 import java.lang.invoke.MethodHandles;
33 import java.lang.invoke.MethodType;
34 import java.lang.reflect.Method;
35 import java.lang.reflect.Modifier;
36 import jdk.internal.dynalink.CallSiteDescriptor;
37 import jdk.internal.dynalink.beans.BeansLinker;
38 import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
39 import jdk.internal.dynalink.linker.GuardedInvocation;
40 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
41 import jdk.internal.dynalink.linker.LinkRequest;
42 import jdk.internal.dynalink.linker.LinkerServices;
43 import jdk.internal.dynalink.linker.MethodHandleTransformer;
44 import jdk.internal.dynalink.support.DefaultInternalObjectFilter;
45 import jdk.internal.dynalink.support.Guards;
46 import jdk.internal.dynalink.support.Lookup;
47 import jdk.nashorn.api.scripting.ScriptUtils;
48 import jdk.nashorn.internal.runtime.ConsString;
49 import jdk.nashorn.internal.runtime.Context;
50 import jdk.nashorn.internal.runtime.ScriptObject;
51 import jdk.nashorn.internal.runtime.ScriptRuntime;
52 import jdk.nashorn.internal.runtime.options.Options;
54 /**
55 * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified
56 * {@code compareConversion} method that favors conversion of {@link ConsString} to either {@link String} or
57 * {@link CharSequence}. It also provides a {@link #createHiddenObjectFilter()} method for use with bootstrap that will
58 * ensure that we never pass internal engine objects that should not be externally observable (currently ConsString and
59 * ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add this functionality as
60 * custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when
61 * the target method handle parameter signature is {@code Object}. This linker also makes sure that primitive
62 * {@link String} operations can be invoked on a {@link ConsString}, and allows invocation of objects implementing
63 * the {@link FunctionalInterface} attribute.
64 */
65 public class NashornBeansLinker implements GuardingDynamicLinker {
66 // System property to control whether to wrap ScriptObject->ScriptObjectMirror for
67 // Object type arguments of Java method calls, field set and array set.
68 private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
70 private static final MethodHandle EXPORT_ARGUMENT;
71 private static final MethodHandle IMPORT_RESULT;
72 private static final MethodHandle FILTER_CONSSTRING;
74 static {
75 final Lookup lookup = new Lookup(MethodHandles.lookup());
76 EXPORT_ARGUMENT = lookup.findOwnStatic("exportArgument", Object.class, Object.class);
77 IMPORT_RESULT = lookup.findOwnStatic("importResult", Object.class, Object.class);
78 FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class);
79 }
81 // cache of @FunctionalInterface method of implementor classes
82 private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() {
83 @Override
84 protected Method computeValue(final Class<?> type) {
85 return findFunctionalInterfaceMethod(type);
86 }
87 };
89 private final BeansLinker beansLinker = new BeansLinker();
91 @Override
92 public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
93 final Object self = linkRequest.getReceiver();
94 final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
95 if (self instanceof ConsString) {
96 // In order to treat ConsString like a java.lang.String we need a link request with a string receiver.
97 final Object[] arguments = linkRequest.getArguments();
98 arguments[0] = "";
99 final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(desc, arguments);
100 final GuardedInvocation invocation = getGuardedInvocation(beansLinker, forgedLinkRequest, linkerServices);
101 // If an invocation is found we add a filter that makes it work for both Strings and ConsStrings.
102 return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING);
103 }
105 if (self != null && "call".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
106 // Support dyn:call on any object that supports some @FunctionalInterface
107 // annotated interface. This way Java method, constructor references or
108 // implementations of java.util.function.* interfaces can be called as though
109 // those are script functions.
110 final Method m = getFunctionalInterfaceMethod(self.getClass());
111 if (m != null) {
112 final MethodType callType = desc.getMethodType();
113 // 'callee' and 'thiz' passed from script + actual arguments
114 if (callType.parameterCount() != m.getParameterCount() + 2) {
115 throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
116 }
117 return new GuardedInvocation(
118 // drop 'thiz' passed from the script.
119 MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1,
120 callType.parameterType(1)), Guards.getInstanceOfGuard(
121 m.getDeclaringClass())).asTypeSafeReturn(
122 new NashornBeansLinkerServices(linkerServices), callType);
123 }
124 }
125 return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
126 }
128 /**
129 * Delegates to the specified linker but injects its linker services wrapper so that it will apply all special
130 * conversions that this class does.
131 * @param delegateLinker the linker to which the actual work is delegated to.
132 * @param linkRequest the delegated link request
133 * @param linkerServices the original link services that will be augmented with special conversions
134 * @return the guarded invocation from the delegate, possibly augmented with special conversions
135 * @throws Exception if the delegate throws an exception
136 */
137 public static GuardedInvocation getGuardedInvocation(final GuardingDynamicLinker delegateLinker, final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
138 return delegateLinker.getGuardedInvocation(linkRequest, new NashornBeansLinkerServices(linkerServices));
139 }
141 @SuppressWarnings("unused")
142 private static Object exportArgument(final Object arg) {
143 return exportArgument(arg, MIRROR_ALWAYS);
144 }
146 static Object exportArgument(final Object arg, final boolean mirrorAlways) {
147 if (arg instanceof ConsString) {
148 return arg.toString();
149 } else if (mirrorAlways && arg instanceof ScriptObject) {
150 return ScriptUtils.wrap((ScriptObject)arg);
151 } else {
152 return arg;
153 }
154 }
156 @SuppressWarnings("unused")
157 private static Object importResult(final Object arg) {
158 return ScriptUtils.unwrap(arg);
159 }
161 @SuppressWarnings("unused")
162 private static Object consStringFilter(final Object arg) {
163 return arg instanceof ConsString ? arg.toString() : arg;
164 }
166 private static Method findFunctionalInterfaceMethod(final Class<?> clazz) {
167 if (clazz == null) {
168 return null;
169 }
171 for (final Class<?> iface : clazz.getInterfaces()) {
172 // check accessiblity up-front
173 if (! Context.isAccessibleClass(iface)) {
174 continue;
175 }
177 // check for @FunctionalInterface
178 if (iface.isAnnotationPresent(FunctionalInterface.class)) {
179 // return the first abstract method
180 for (final Method m : iface.getMethods()) {
181 if (Modifier.isAbstract(m.getModifiers())) {
182 return m;
183 }
184 }
185 }
186 }
188 // did not find here, try super class
189 return findFunctionalInterfaceMethod(clazz.getSuperclass());
190 }
192 // Returns @FunctionalInterface annotated interface's single abstract
193 // method. If not found, returns null.
194 static Method getFunctionalInterfaceMethod(final Class<?> clazz) {
195 return FUNCTIONAL_IFACE_METHOD.get(clazz);
196 }
198 static MethodHandleTransformer createHiddenObjectFilter() {
199 return new DefaultInternalObjectFilter(EXPORT_ARGUMENT, MIRROR_ALWAYS ? IMPORT_RESULT : null);
200 }
202 private static class NashornBeansLinkerServices implements LinkerServices {
203 private final LinkerServices linkerServices;
205 NashornBeansLinkerServices(final LinkerServices linkerServices) {
206 this.linkerServices = linkerServices;
207 }
209 @Override
210 public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
211 return linkerServices.asType(handle, fromType);
212 }
214 @Override
215 public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) {
216 return Implementation.asTypeLosslessReturn(this, handle, fromType);
217 }
219 @Override
220 public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
221 return linkerServices.getTypeConverter(sourceType, targetType);
222 }
224 @Override
225 public boolean canConvert(final Class<?> from, final Class<?> to) {
226 return linkerServices.canConvert(from, to);
227 }
229 @Override
230 public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest) throws Exception {
231 return linkerServices.getGuardedInvocation(linkRequest);
232 }
234 @Override
235 public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
236 if (sourceType == ConsString.class) {
237 if (String.class == targetType1 || CharSequence.class == targetType1) {
238 return Comparison.TYPE_1_BETTER;
239 }
241 if (String.class == targetType2 || CharSequence.class == targetType2) {
242 return Comparison.TYPE_2_BETTER;
243 }
244 }
245 return linkerServices.compareConversion(sourceType, targetType1, targetType2);
246 }
248 @Override
249 public MethodHandle filterInternalObjects(final MethodHandle target) {
250 return linkerServices.filterInternalObjects(target);
251 }
252 }
253 }