8150731: Nashorn JSObject linker should be exposed as a service provider

Reviewed-by: jlaskey, hannesw
This commit is contained in:
Athijegannathan Sundararajan 2016-05-06 20:27:20 +05:30
parent 19f9ce673f
commit 89cdc7ca82
5 changed files with 140 additions and 2 deletions

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jdk.nashorn.api.linker;
import java.util.List;
import jdk.dynalink.linker.GuardingDynamicLinker;
import jdk.dynalink.linker.GuardingDynamicLinkerExporter;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
* This linker exporter is a service provider that exports Nashorn Dynalink
* linkers to external users. Other languague runtimes that use Dynalink
* can use the linkers exported by this provider to support tight integration
* of Nashorn objects.
*/
public final class NashornLinkerExporter extends GuardingDynamicLinkerExporter {
/**
* The default constructor.
*/
public NashornLinkerExporter() {}
/**
* Returns a list of exported nashorn specific linkers.
*
* @return list of exported nashorn specific linkers
*/
@Override
public List<GuardingDynamicLinker> get() {
return Bootstrap.getExposedLinkers();
}
}

View File

@ -33,6 +33,8 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.Collections;
import java.util.List;
import jdk.dynalink.CallSiteDescriptor;
import jdk.dynalink.DynamicLinker;
import jdk.dynalink.DynamicLinkerFactory;
@ -70,6 +72,7 @@ public final class Bootstrap {
private static final BeansLinker beansLinker = new BeansLinker(Bootstrap::createMissingMemberHandler);
private static final GuardingDynamicLinker[] prioritizedLinkers;
private static final GuardingDynamicLinker[] fallbackLinkers;
static {
final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker(beansLinker);
prioritizedLinkers = new GuardingDynamicLinker[] {
@ -89,6 +92,19 @@ public final class Bootstrap {
private Bootstrap() {
}
/**
* Returns a list of exposed nashorn dynalink linkers.
*
* @return a list of exposed nashorn dynalink linkers.
*/
public static List<GuardingDynamicLinker> getExposedLinkers() {
// we have to create BeansLinker without nashorn specific missing member handler!
// Or else, we'd return values such as 'undefined' to the external world!
final NashornBeansLinker nbl = new NashornBeansLinker(new BeansLinker());
final JSObjectLinker linker = new JSObjectLinker(nbl);
return Collections.singletonList(linker);
}
/**
* Creates a Nashorn dynamic linker with the given app class loader.
* @param appLoader the app class loader. It will be used to discover

View File

@ -71,6 +71,9 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception {
final Object self = request.getReceiver();
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
if (self == null || !canLinkTypeStatic(self.getClass())) {
return null;
}
GuardedInvocation inv;
if (self instanceof JSObject) {
@ -82,7 +85,7 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
inv = new GuardedInvocation(beanInv.getInvocation(),
NashornGuards.combineGuards(beanInv.getGuard(), NashornGuards.getNotJSObjectGuard()));
} else {
throw new AssertionError(); // Should never reach here.
throw new AssertionError("got instanceof: " + self.getClass()); // Should never reach here.
}
return Bootstrap.asTypeSafeReturn(inv, linkerServices, desc);

View File

@ -38,6 +38,10 @@ module jdk.scripting.nashorn {
exports jdk.nashorn.tools to
jdk.scripting.nashorn.shell;
provides javax.script.ScriptEngineFactory with jdk.nashorn.api.scripting.NashornScriptEngineFactory;
provides javax.script.ScriptEngineFactory
with jdk.nashorn.api.scripting.NashornScriptEngineFactory;
provides jdk.dynalink.linker.GuardingDynamicLinkerExporter
with jdk.nashorn.api.linker.NashornLinkerExporter;
}

View File

@ -30,10 +30,13 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.ServiceConfigurationError;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import jdk.dynalink.CallSiteDescriptor;
import jdk.dynalink.DynamicLinker;
import jdk.dynalink.DynamicLinkerFactory;
import jdk.dynalink.NoSuchDynamicMethodException;
import jdk.dynalink.NamedOperation;
import jdk.dynalink.Operation;
import jdk.dynalink.StandardOperation;
import jdk.dynalink.linker.GuardingDynamicLinker;
@ -41,6 +44,7 @@ import jdk.dynalink.linker.LinkRequest;
import jdk.dynalink.linker.LinkerServices;
import jdk.dynalink.support.SimpleRelinkableCallSite;
import jdk.dynalink.linker.GuardedInvocation;
import jdk.nashorn.api.scripting.AbstractJSObject;
import org.testng.Assert;
import org.testng.annotations.Test;
@ -254,4 +258,55 @@ public class DynamicLinkerFactoryTest {
Assert.assertTrue(reachedAutoLinker);
}
@Test
public void nashornExportedLinkerJSObjectTest() {
final DynamicLinkerFactory factory = newDynamicLinkerFactory(false);
final DynamicLinker linker = factory.createLinker();
final MethodType mt = MethodType.methodType(Object.class, Object.class);
final NamedOperation op = new NamedOperation(StandardOperation.GET_PROPERTY, "foo");
final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
MethodHandles.publicLookup(), op, mt)));
final boolean[] reachedGetMember = new boolean[1];
// check that the nashorn exported linker can be used for user defined JSObject
Object obj = new AbstractJSObject() {
@Override
public Object getMember(String name) {
reachedGetMember[0] = true;
return name.equals("foo")? "bar" : "<unknown>";
}
};
Object value = null;
try {
value = cs.getTarget().invoke(obj);
} catch (Throwable th) {
throw new RuntimeException(th);
}
Assert.assertTrue(reachedGetMember[0]);
Assert.assertEquals(value, "bar");
}
@Test
public void nashornExportedLinkerScriptObjectMirrorTest() {
final DynamicLinkerFactory factory = newDynamicLinkerFactory(false);
final DynamicLinker linker = factory.createLinker();
// check that the nashorn exported linker can be used for ScriptObjectMirror
final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
final MethodType mt = MethodType.methodType(Object.class, Object.class);
final NamedOperation op = new NamedOperation(StandardOperation.GET_PROPERTY, "foo");
final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
MethodHandles.publicLookup(), op, mt)));
Object value = null;
try {
final Object obj = engine.eval("({ foo: 'hello' })");
value = cs.getTarget().invoke(obj);
} catch (Throwable th) {
throw new RuntimeException(th);
}
Assert.assertEquals(value, "hello");
}
}