8044000: Access to undefined property yields "null" instead of "undefined"
Reviewed-by: lagergren, jlaskey
This commit is contained in:
parent
f7940fec73
commit
d779eeab89
@ -61,9 +61,17 @@ public final class Bootstrap {
|
||||
private static final DynamicLinker dynamicLinker;
|
||||
static {
|
||||
final DynamicLinkerFactory factory = new DynamicLinkerFactory();
|
||||
factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(),
|
||||
new BoundDynamicMethodLinker(), new JavaSuperAdapterLinker(), new JSObjectLinker(), new ReflectionCheckLinker());
|
||||
factory.setFallbackLinkers(new NashornBeansLinker(), new NashornBottomLinker());
|
||||
final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker();
|
||||
final JSObjectLinker jsObjectLinker = new JSObjectLinker(nashornBeansLinker);
|
||||
factory.setPrioritizedLinkers(
|
||||
new NashornLinker(),
|
||||
new NashornPrimitiveLinker(),
|
||||
new NashornStaticClassLinker(),
|
||||
new BoundDynamicMethodLinker(),
|
||||
new JavaSuperAdapterLinker(),
|
||||
jsObjectLinker,
|
||||
new ReflectionCheckLinker());
|
||||
factory.setFallbackLinkers(nashornBeansLinker, new NashornBottomLinker());
|
||||
factory.setSyncOnRelink(true);
|
||||
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1);
|
||||
if (relinkThreshold > -1) {
|
||||
|
@ -30,6 +30,7 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.script.Bindings;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.GuardedTypeConversion;
|
||||
@ -48,14 +49,23 @@ import jdk.nashorn.internal.runtime.JSType;
|
||||
* as ScriptObjects from other Nashorn contexts.
|
||||
*/
|
||||
final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory {
|
||||
private final NashornBeansLinker nashornBeansLinker;
|
||||
|
||||
JSObjectLinker(final NashornBeansLinker nashornBeansLinker) {
|
||||
this.nashornBeansLinker = nashornBeansLinker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canLinkType(final Class<?> type) {
|
||||
return canLinkTypeStatic(type);
|
||||
}
|
||||
|
||||
static boolean canLinkTypeStatic(final Class<?> type) {
|
||||
// can link JSObject
|
||||
return JSObject.class.isAssignableFrom(type);
|
||||
// can link JSObject also handles Map, Bindings to make
|
||||
// sure those are not JSObjects.
|
||||
return Map.class.isAssignableFrom(type) ||
|
||||
Bindings.class.isAssignableFrom(type) ||
|
||||
JSObject.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -72,6 +82,11 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
|
||||
final GuardedInvocation inv;
|
||||
if (self instanceof JSObject) {
|
||||
inv = lookup(desc);
|
||||
} else if (self instanceof Map || self instanceof Bindings) {
|
||||
// guard to make sure the Map or Bindings does not turn into JSObject later!
|
||||
final GuardedInvocation beanInv = nashornBeansLinker.getGuardedInvocation(request, linkerServices);
|
||||
inv = new GuardedInvocation(beanInv.getInvocation(),
|
||||
NashornGuards.combineGuards(beanInv.getGuard(), NashornGuards.getNotJSObjectGuard()));
|
||||
} else {
|
||||
throw new AssertionError(); // Should never reach here.
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.ref.WeakReference;
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.nashorn.api.scripting.JSObject;
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.objects.Global;
|
||||
import jdk.nashorn.internal.runtime.Property;
|
||||
@ -43,6 +44,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
*/
|
||||
public final class NashornGuards {
|
||||
private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class);
|
||||
private static final MethodHandle IS_NOT_JSOBJECT = findOwnMH("isNotJSObject", boolean.class, Object.class);
|
||||
private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class);
|
||||
private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
|
||||
private static final MethodHandle SAME_OBJECT = findOwnMH("sameObject", boolean.class, Object.class, WeakReference.class);
|
||||
@ -60,6 +62,14 @@ public final class NashornGuards {
|
||||
return IS_SCRIPTOBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the guard that checks if an item is not a {@code JSObject}
|
||||
* @return method handle for guard
|
||||
*/
|
||||
public static MethodHandle getNotJSObjectGuard() {
|
||||
return IS_NOT_JSOBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the guard that checks if an item is a {@code ScriptFunction}
|
||||
* @return method handle for guard
|
||||
@ -156,6 +166,11 @@ public final class NashornGuards {
|
||||
return self instanceof ScriptObject;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isNotJSObject(final Object self) {
|
||||
return !(self instanceof JSObject);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isScriptFunction(final Object self) {
|
||||
return self instanceof ScriptFunction;
|
||||
|
@ -29,6 +29,8 @@ import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
@ -276,4 +278,31 @@ public class ScriptObjectMirrorTest {
|
||||
"({ toString: function() { return 'foo' } })");
|
||||
assertEquals("foo", obj.to(String.class));
|
||||
}
|
||||
|
||||
// @bug 8044000: Access to undefined property yields "null" instead of "undefined"
|
||||
@Test
|
||||
public void mapScriptObjectMirrorCallsiteTest() throws ScriptException {
|
||||
final ScriptEngineManager m = new ScriptEngineManager();
|
||||
final ScriptEngine engine = m.getEngineByName("nashorn");
|
||||
final String TEST_SCRIPT = "typeof obj.foo";
|
||||
|
||||
final Bindings global = engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
engine.eval("var obj = java.util.Collections.emptyMap()");
|
||||
// this will drive callsite "obj.foo" of TEST_SCRIPT
|
||||
// to use "obj instanceof Map" as it's guard
|
||||
engine.eval(TEST_SCRIPT, global);
|
||||
// redefine 'obj' to be a script object
|
||||
engine.eval("obj = {}");
|
||||
|
||||
final Bindings newGlobal = engine.createBindings();
|
||||
// transfer 'obj' from default global to new global
|
||||
// new global will get a ScriptObjectMirror wrapping 'obj'
|
||||
newGlobal.put("obj", global.get("obj"));
|
||||
|
||||
// Every ScriptObjectMirror is a Map! If callsite "obj.foo"
|
||||
// does not see the new 'obj' is a ScriptObjectMirror, it'll
|
||||
// continue to use Map's get("obj.foo") instead of ScriptObjectMirror's
|
||||
// getMember("obj.foo") - thereby getting null instead of undefined
|
||||
assertEquals("undefined", engine.eval(TEST_SCRIPT, newGlobal));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user