NashornBottomLinker.java revision 1477:dd36e980905b
155682Smarkm/* 257416Smarkm * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 355682Smarkm * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 455682Smarkm * 555682Smarkm * This code is free software; you can redistribute it and/or modify it 655682Smarkm * under the terms of the GNU General Public License version 2 only, as 755682Smarkm * published by the Free Software Foundation. Oracle designates this 855682Smarkm * particular file as subject to the "Classpath" exception as provided 955682Smarkm * by Oracle in the LICENSE file that accompanied this code. 1055682Smarkm * 1155682Smarkm * This code is distributed in the hope that it will be useful, but WITHOUT 1255682Smarkm * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1355682Smarkm * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1455682Smarkm * version 2 for more details (a copy is included in the LICENSE file that 1555682Smarkm * accompanied this code). 1655682Smarkm * 1755682Smarkm * You should have received a copy of the GNU General Public License version 1855682Smarkm * 2 along with this work; if not, write to the Free Software Foundation, 1955682Smarkm * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2055682Smarkm * 2155682Smarkm * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2255682Smarkm * or visit www.oracle.com if you need additional information or have any 2355682Smarkm * questions. 2455682Smarkm */ 2555682Smarkm 2655682Smarkmpackage jdk.nashorn.internal.runtime.linker; 2755682Smarkm 2855682Smarkmimport static jdk.nashorn.internal.lookup.Lookup.MH; 2955682Smarkmimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 3055682Smarkmimport static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED; 3155682Smarkmimport static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX; 3255682Smarkmimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 3355682Smarkm 3455682Smarkmimport java.lang.invoke.MethodHandle; 3555682Smarkmimport java.lang.invoke.MethodHandles; 3657416Smarkmimport java.util.HashMap; 3755682Smarkmimport java.util.Map; 3855682Smarkmimport java.util.function.Supplier; 3955682Smarkmimport jdk.internal.dynalink.CallSiteDescriptor; 4055682Smarkmimport jdk.internal.dynalink.beans.BeansLinker; 4155682Smarkmimport jdk.internal.dynalink.linker.GuardedInvocation; 4255682Smarkmimport jdk.internal.dynalink.linker.GuardingDynamicLinker; 4355682Smarkmimport jdk.internal.dynalink.linker.GuardingTypeConverterFactory; 4455682Smarkmimport jdk.internal.dynalink.linker.LinkRequest; 4555682Smarkmimport jdk.internal.dynalink.linker.LinkerServices; 4655682Smarkmimport jdk.internal.dynalink.linker.support.Guards; 4755682Smarkmimport jdk.nashorn.internal.codegen.types.Type; 4855682Smarkmimport jdk.nashorn.internal.runtime.JSType; 4955682Smarkmimport jdk.nashorn.internal.runtime.ScriptRuntime; 5055682Smarkmimport jdk.nashorn.internal.runtime.UnwarrantedOptimismException; 5155682Smarkm 5255682Smarkm/** 5355682Smarkm * Nashorn bottom linker; used as a last-resort catch-all linker for all linking requests that fall through all other 5455682Smarkm * linkers (see how {@link Bootstrap} class configures the dynamic linker in its static initializer). It will throw 5555682Smarkm * appropriate ECMAScript errors for attempts to invoke operations on {@code null}, link no-op property getters and 5655682Smarkm * setters for Java objects that couldn't be linked by any other linker, and throw appropriate ECMAScript errors for 5755682Smarkm * attempts to invoke arbitrary Java objects as functions or constructors. 5855682Smarkm */ 5955682Smarkmfinal class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeConverterFactory { 6055682Smarkm 6155682Smarkm @Override 6255682Smarkm public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) 6355682Smarkm throws Exception { 6455682Smarkm final Object self = linkRequest.getReceiver(); 6555682Smarkm 6655682Smarkm if (self == null) { 6755682Smarkm return linkNull(linkRequest); 6855682Smarkm } 6955682Smarkm 7055682Smarkm // None of the objects that can be linked by NashornLinker should ever reach here. Basically, anything below 7155682Smarkm // this point is a generic Java bean. Therefore, reaching here with a ScriptObject is a Nashorn bug. 7255682Smarkm assert isExpectedObject(self) : "Couldn't link " + linkRequest.getCallSiteDescriptor() + " for " + self.getClass().getName(); 7355682Smarkm 7455682Smarkm return linkBean(linkRequest, linkerServices); 7555682Smarkm } 7655682Smarkm 7755682Smarkm private static final MethodHandle EMPTY_PROP_GETTER = 7855682Smarkm MH.dropArguments(MH.constant(Object.class, UNDEFINED), 0, Object.class); 7955682Smarkm private static final MethodHandle EMPTY_ELEM_GETTER = 8055682Smarkm MH.dropArguments(EMPTY_PROP_GETTER, 0, Object.class); 8155682Smarkm private static final MethodHandle EMPTY_PROP_SETTER = 8255682Smarkm MH.asType(EMPTY_ELEM_GETTER, EMPTY_ELEM_GETTER.type().changeReturnType(void.class)); 8355682Smarkm private static final MethodHandle EMPTY_ELEM_SETTER = 8455682Smarkm MH.dropArguments(EMPTY_PROP_SETTER, 0, Object.class); 8555682Smarkm 8655682Smarkm private static GuardedInvocation linkBean(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { 8755682Smarkm final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor)linkRequest.getCallSiteDescriptor(); 8855682Smarkm final Object self = linkRequest.getReceiver(); 8955682Smarkm final String operator = desc.getFirstOperator(); 9055682Smarkm switch (operator) { 9155682Smarkm case "new": 9255682Smarkm if(BeansLinker.isDynamicConstructor(self)) { 9355682Smarkm throw typeError("no.constructor.matches.args", ScriptRuntime.safeToString(self)); 9455682Smarkm } 9555682Smarkm if(BeansLinker.isDynamicMethod(self)) { 9655682Smarkm throw typeError("method.not.constructor", ScriptRuntime.safeToString(self)); 9755682Smarkm } 9855682Smarkm throw typeError("not.a.function", desc.getFunctionErrorMessage(self)); 9955682Smarkm case "call": 10055682Smarkm if(BeansLinker.isDynamicConstructor(self)) { 10155682Smarkm throw typeError("constructor.requires.new", ScriptRuntime.safeToString(self)); 10255682Smarkm } 10355682Smarkm if(BeansLinker.isDynamicMethod(self)) { 10455682Smarkm throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self)); 10555682Smarkm } 10655682Smarkm throw typeError("not.a.function", desc.getFunctionErrorMessage(self)); 10755682Smarkm case "callMethod": 10855682Smarkm throw typeError("no.such.function", getArgument(linkRequest), ScriptRuntime.safeToString(self)); 10955682Smarkm case "getMethod": 11055682Smarkm // evaluate to undefined, later on Undefined will take care of throwing TypeError 11155682Smarkm return getInvocation(MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class), self, linkerServices, desc); 11255682Smarkm case "getProp": 11355682Smarkm case "getElem": 11455682Smarkm if(NashornCallSiteDescriptor.isOptimistic(desc)) { 11555682Smarkm throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT); 11655682Smarkm } 11755682Smarkm if (desc.getOperand() != null) { 11855682Smarkm return getInvocation(EMPTY_PROP_GETTER, self, linkerServices, desc); 11955682Smarkm } 12055682Smarkm return getInvocation(EMPTY_ELEM_GETTER, self, linkerServices, desc); 12155682Smarkm case "setProp": 12255682Smarkm case "setElem": { 12355682Smarkm final boolean strict = NashornCallSiteDescriptor.isStrict(desc); 12455682Smarkm if (strict) { 12555682Smarkm throw typeError("cant.set.property", getArgument(linkRequest), ScriptRuntime.safeToString(self)); 12655682Smarkm } 12755682Smarkm if (desc.getOperand() != null) { 12855682Smarkm return getInvocation(EMPTY_PROP_SETTER, self, linkerServices, desc); 12955682Smarkm } 13055682Smarkm return getInvocation(EMPTY_ELEM_SETTER, self, linkerServices, desc); 13155682Smarkm } 13255682Smarkm default: 13355682Smarkm break; 13455682Smarkm } 13555682Smarkm throw new AssertionError("unknown call type " + desc); 13655682Smarkm } 13755682Smarkm 13855682Smarkm @Override 13955682Smarkm public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType, final Supplier<MethodHandles.Lookup> lookupSupplier) throws Exception { 14055682Smarkm final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); 14155682Smarkm return gi == null ? null : gi.asType(MH.type(targetType, sourceType)); 14255682Smarkm } 14355682Smarkm 14455682Smarkm /** 14555682Smarkm * Main part of the implementation of {@link GuardingTypeConverterFactory#convertToType(Class, Class)} that doesn't 14655682Smarkm * care about adapting the method signature; that's done by the invoking method. Returns conversion from Object to String/number/boolean (JS primitive types). 14755682Smarkm * @param sourceType the source type 14855682Smarkm * @param targetType the target type 14955682Smarkm * @return a guarded invocation that converts from the source type to the target type. 15055682Smarkm * @throws Exception if something goes wrong 15157416Smarkm */ 15257416Smarkm private static GuardedInvocation convertToTypeNoCast(final Class<?> sourceType, final Class<?> targetType) throws Exception { 15355682Smarkm final MethodHandle mh = CONVERTERS.get(targetType); 15455682Smarkm if (mh != null) { 15555682Smarkm return new GuardedInvocation(mh); 15655682Smarkm } 15755682Smarkm 15857416Smarkm return null; 15955682Smarkm } 16055682Smarkm 16155682Smarkm private static GuardedInvocation getInvocation(final MethodHandle handle, final Object self, final LinkerServices linkerServices, final CallSiteDescriptor desc) { 16255682Smarkm return Bootstrap.asTypeSafeReturn(new GuardedInvocation(handle, Guards.getClassGuard(self.getClass())), linkerServices, desc); 16355682Smarkm } 16455682Smarkm 16555682Smarkm // Used solely in an assertion to figure out if the object we get here is something we in fact expect. Objects 16655682Smarkm // linked by NashornLinker should never reach here. 16755682Smarkm private static boolean isExpectedObject(final Object obj) { 16855682Smarkm return !(NashornLinker.canLinkTypeStatic(obj.getClass())); 16955682Smarkm } 17055682Smarkm 17155682Smarkm private static GuardedInvocation linkNull(final LinkRequest linkRequest) { 17255682Smarkm final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor)linkRequest.getCallSiteDescriptor(); 17355682Smarkm final String operator = desc.getFirstOperator(); 17455682Smarkm switch (operator) { 17555682Smarkm case "new": 17655682Smarkm case "call": 17755682Smarkm throw typeError("not.a.function", "null"); 17855682Smarkm case "callMethod": 17955682Smarkm case "getMethod": 18055682Smarkm throw typeError("no.such.function", getArgument(linkRequest), "null"); 18155682Smarkm case "getProp": 18257416Smarkm case "getElem": 18355682Smarkm throw typeError("cant.get.property", getArgument(linkRequest), "null"); 18455682Smarkm case "setProp": 18555682Smarkm case "setElem": 18655682Smarkm throw typeError("cant.set.property", getArgument(linkRequest), "null"); 18755682Smarkm default: 18855682Smarkm break; 18955682Smarkm } 19055682Smarkm throw new AssertionError("unknown call type " + desc); 19155682Smarkm } 19255682Smarkm 19355682Smarkm private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>(); 19455682Smarkm static { 19555682Smarkm CONVERTERS.put(boolean.class, JSType.TO_BOOLEAN.methodHandle()); 19655682Smarkm CONVERTERS.put(double.class, JSType.TO_NUMBER.methodHandle()); 197 CONVERTERS.put(int.class, JSType.TO_INTEGER.methodHandle()); 198 CONVERTERS.put(long.class, JSType.TO_LONG.methodHandle()); 199 CONVERTERS.put(String.class, JSType.TO_STRING.methodHandle()); 200 } 201 202 private static String getArgument(final LinkRequest linkRequest) { 203 final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor(); 204 if (desc.getNameTokenCount() > 2) { 205 return desc.getNameToken(2); 206 } 207 return ScriptRuntime.safeToString(linkRequest.getArguments()[1]); 208 } 209} 210