# HG changeset patch # User attila # Date 1358762636 -3600 # Node ID bea4e75d192cda066fadc25272ee66d499b20aa4 # Parent b9578bc4cd689adfd07520f462282cc51d381a83 8006525: Give StaticClass objects their own linker Reviewed-by: hannesw, lagergren diff -r b9578bc4cd68 -r bea4e75d192c nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Sat Jan 19 22:35:43 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Mon Jan 21 11:03:56 2013 +0100 @@ -54,7 +54,8 @@ private static final DynamicLinker dynamicLinker; static { final DynamicLinkerFactory factory = new DynamicLinkerFactory(); - factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new JSObjectLinker()); + factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(), + new JSObjectLinker()); factory.setFallbackLinkers(new BeansLinker(), new NashornBottomLinker()); factory.setSyncOnRelink(true); final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1); diff -r b9578bc4cd68 -r bea4e75d192c nashorn/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java Sat Jan 19 22:35:43 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java Mon Jan 21 11:03:56 2013 +0100 @@ -31,44 +31,28 @@ import java.lang.invoke.MethodHandles; import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; -import jdk.nashorn.internal.runtime.ECMAErrors; import jdk.nashorn.internal.runtime.GlobalObject; -import org.dynalang.dynalink.beans.BeansLinker; -import org.dynalang.dynalink.beans.StaticClass; import org.dynalang.dynalink.linker.ConversionComparator; import org.dynalang.dynalink.linker.GuardedInvocation; -import org.dynalang.dynalink.linker.GuardingDynamicLinker; import org.dynalang.dynalink.linker.GuardingTypeConverterFactory; import org.dynalang.dynalink.linker.LinkRequest; import org.dynalang.dynalink.linker.LinkerServices; import org.dynalang.dynalink.linker.TypeBasedGuardingDynamicLinker; -import org.dynalang.dynalink.support.Guards; import org.dynalang.dynalink.support.TypeUtilities; /** - * Internal linker for String, Boolean, Number, and {@link StaticClass} objects, only ever used by Nashorn engine and - * not exposed to other engines. It is used for treatment of strings, boolean, and numbers, as JavaScript primitives, - * as well as for extending the "new" operator on StaticClass in order to be able to instantiate interfaces and abstract - * classes by passing a ScriptObject or ScriptFunction as their implementation, e.g.: - *
- *   var r = new Runnable() { run: function() { print("Hello World" } }
- * 
- * or even, for SAM types: - *
- *   var r = new Runnable(function() { print("Hello World" })
- * 
+ * Internal linker for String, Boolean, and Number objects, only ever used by Nashorn engine and not exposed to other + * engines. It is used for treatment of strings, boolean, and numbers as JavaScript primitives. Also provides ECMAScript + * primitive type conversions for these types when linking to Java methods. */ class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator { - private static final GuardingDynamicLinker staticClassLinker = BeansLinker.getLinkerForClass(StaticClass.class); - @Override public boolean canLinkType(final Class type) { return canLinkTypeStatic(type); } private static boolean canLinkTypeStatic(final Class type) { - return type == String.class || type == Boolean.class || type == StaticClass.class || - type == ConsString.class || Number.class.isAssignableFrom(type); + return type == String.class || type == Boolean.class || type == ConsString.class || Number.class.isAssignableFrom(type); } @Override @@ -86,41 +70,10 @@ return Bootstrap.asType(global.stringLookup(desc, (CharSequence) self), linkerServices, desc); } else if (self instanceof Boolean) { return Bootstrap.asType(global.booleanLookup(desc, (Boolean) self), linkerServices, desc); - } else if (self instanceof StaticClass) { - // We intercept "new" on StaticClass instances to provide additional capabilities - if ("new".equals(desc.getOperator())) { - final Class receiverClass = ((StaticClass) self).getRepresentedClass(); - // Is the class abstract? (This includes interfaces.) - if (JavaAdapterFactory.isAbstractClass(receiverClass)) { - // Change this link request into a link request on the adapter class. - final Object[] args = request.getArguments(); - args[0] = JavaAdapterFactory.getAdapterClassFor(new Class[] { receiverClass }); - final LinkRequest adapterRequest = request.replaceArguments(request.getCallSiteDescriptor(), args); - final GuardedInvocation gi = checkNullConstructor( - staticClassLinker.getGuardedInvocation(adapterRequest, linkerServices), receiverClass); - // Finally, modify the guard to test for the original abstract class. - return gi.replaceMethods(gi.getInvocation(), Guards.getIdentityGuard(self)); - } - // If the class was not abstract, just link to the ordinary constructor. Make an additional check to - // ensure we have a constructor. We could just fall through to the next "return" statement, except we - // also insert a call to checkNullConstructor() which throws an error with a more intuitive message when - // no suitable constructor is found. - return checkNullConstructor(staticClassLinker.getGuardedInvocation(request, linkerServices), receiverClass); - } - // In case this was not a "new" operation, just link with the standard StaticClass linker. - return staticClassLinker.getGuardedInvocation(request, linkerServices); } - throw new AssertionError(); // Can't reach here } - private static GuardedInvocation checkNullConstructor(final GuardedInvocation ctorInvocation, final Class receiverClass) { - if(ctorInvocation == null) { - ECMAErrors.typeError(Context.getGlobal(), "no.constructor.matches.args", receiverClass.getName()); - } - return ctorInvocation; - } - /** * This implementation of type converter factory will pretty much allow implicit conversions of anything to anything * else that's allowed among JavaScript primitive types (string to number, boolean to string, etc.) @@ -144,7 +97,8 @@ * @param sourceType the source type to convert from * @param targetType1 one candidate target type * @param targetType2 another candidate target type - * @return one of {@link org.dynalang.dynalink.linker.ConversionComparator.Comparison} values signifying which target type should be favored for conversion. + * @return one of {@link org.dynalang.dynalink.linker.ConversionComparator.Comparison} values signifying which + * target type should be favored for conversion. */ @Override public Comparison compareConversion(final Class sourceType, final Class targetType1, final Class targetType2) { diff -r b9578bc4cd68 -r bea4e75d192c nashorn/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Mon Jan 21 11:03:56 2013 +0100 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.runtime.linker; + +import jdk.nashorn.internal.runtime.Context; +import jdk.nashorn.internal.runtime.ECMAErrors; +import org.dynalang.dynalink.CallSiteDescriptor; +import org.dynalang.dynalink.beans.BeansLinker; +import org.dynalang.dynalink.beans.StaticClass; +import org.dynalang.dynalink.linker.GuardedInvocation; +import org.dynalang.dynalink.linker.GuardingDynamicLinker; +import org.dynalang.dynalink.linker.LinkRequest; +import org.dynalang.dynalink.linker.LinkerServices; +import org.dynalang.dynalink.linker.TypeBasedGuardingDynamicLinker; +import org.dynalang.dynalink.support.Guards; + +/** + * Internal linker for {@link StaticClass} objects, only ever used by Nashorn engine and not exposed to other engines. + * It is used for extending the "new" operator on StaticClass in order to be able to instantiate interfaces and abstract + * classes by passing a ScriptObject or ScriptFunction as their implementation, e.g.: + *
+ *   var r = new Runnable() { run: function() { print("Hello World" } }
+ * 
+ * or for SAM types, even just passing a function: + *
+ *   var r = new Runnable(function() { print("Hello World" })
+ * 
+ */ +class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker { + private static final GuardingDynamicLinker staticClassLinker = BeansLinker.getLinkerForClass(StaticClass.class); + + @Override + public boolean canLinkType(Class type) { + return type == StaticClass.class; + } + + @Override + public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices) throws Exception { + final LinkRequest request = linkRequest.withoutRuntimeContext(); // Nashorn has no runtime context + final Object self = request.getReceiver(); + if (self.getClass() != StaticClass.class) { + return null; + } + final CallSiteDescriptor desc = request.getCallSiteDescriptor(); + // We intercept "new" on StaticClass instances to provide additional capabilities + if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) { + final Class receiverClass = ((StaticClass) self).getRepresentedClass(); + // Is the class abstract? (This includes interfaces.) + if (JavaAdapterFactory.isAbstractClass(receiverClass)) { + // Change this link request into a link request on the adapter class. + final Object[] args = request.getArguments(); + args[0] = JavaAdapterFactory.getAdapterClassFor(new Class[] { receiverClass }); + final LinkRequest adapterRequest = request.replaceArguments(request.getCallSiteDescriptor(), args); + final GuardedInvocation gi = checkNullConstructor(delegate(linkerServices, adapterRequest), receiverClass); + // Finally, modify the guard to test for the original abstract class. + return gi.replaceMethods(gi.getInvocation(), Guards.getIdentityGuard(self)); + } + // If the class was not abstract, just delegate linking to the standard StaticClass linker. Make an + // additional check to ensure we have a constructor. We could just fall through to the next "return" + // statement, except we also insert a call to checkNullConstructor() which throws an ECMAScript TypeError + // with a more intuitive message when no suitable constructor is found. + return checkNullConstructor(delegate(linkerServices, request), receiverClass); + } + // In case this was not a "new" operation, just delegate to the the standard StaticClass linker. + return delegate(linkerServices, request); + } + + private static GuardedInvocation delegate(LinkerServices linkerServices, final LinkRequest request) throws Exception { + return staticClassLinker.getGuardedInvocation(request, linkerServices); + } + + private static GuardedInvocation checkNullConstructor(final GuardedInvocation ctorInvocation, final Class receiverClass) { + if(ctorInvocation == null) { + ECMAErrors.typeError(Context.getGlobal(), "no.constructor.matches.args", receiverClass.getName()); + } + return ctorInvocation; + } +}