This commit is contained in:
Lana Steuck 2015-02-12 16:39:22 -08:00
commit 06fb272637
21 changed files with 793 additions and 130 deletions

View file

@ -504,7 +504,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes" <testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}"> verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/> <jvmarg line="${boot.class.path}"/>
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} -Dbuild.dir=${build.dir}"/> <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} -Dbuild.dir=${build.dir}"/>
<propertyset> <propertyset>
<propertyref prefix="testjfx-test-sys-prop."/> <propertyref prefix="testjfx-test-sys-prop."/>
@ -524,7 +524,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes" <testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}"> verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/> <jvmarg line="${boot.class.path}"/>
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/> <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
<propertyset> <propertyset>
<propertyref prefix="testmarkdown-test-sys-prop."/> <propertyref prefix="testmarkdown-test-sys-prop."/>
@ -543,7 +543,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes" <testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}"> verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/> <jvmarg line="${boot.class.path}"/>
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/> <jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
<propertyset> <propertyset>
<propertyref prefix="nashorn."/> <propertyref prefix="nashorn."/>

View file

@ -0,0 +1,122 @@
#// Usage: jjs getclassnpe.js -- <directory>
/*
* Copyright (c) 2015, 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.
*/
/*
* java.lang.Object.getClass() is sometimes used to do null check. This
* obfuscating Object.getClass() check relies on non-related intrinsic
* performance, which is potentially not available everywhere.
* See also http://cr.openjdk.java.net/~shade/scratch/NullChecks.java
* This nashorn script checks for such uses in your .java files in the
* given directory (recursively).
*/
if (arguments.length == 0) {
print("Usage: jjs getclassnpe.js -- <directory>");
exit(1);
}
// Java types used
var File = Java.type("java.io.File");
var Files = Java.type("java.nio.file.Files");
var StringArray = Java.type("java.lang.String[]");
var ToolProvider = Java.type("javax.tools.ToolProvider");
var MethodInvocationTree = Java.type("com.sun.source.tree.MethodInvocationTree");
var TreeScanner = Java.type("com.sun.source.util.TreeScanner");
// parse a specific .java file to check if it uses
// Object.getClass() for null check.
function checkGetClassNPE() {
// get the system compiler tool
var compiler = ToolProvider.systemJavaCompiler;
// get standard file manager
var fileMgr = compiler.getStandardFileManager(null, null, null);
// Using Java.to convert script array (arguments) to a Java String[]
var compUnits = fileMgr.getJavaFileObjects(
Java.to(arguments, StringArray));
// create a new compilation task
var task = compiler.getTask(null, fileMgr, null, null, null, compUnits);
// subclass SimpleTreeVisitor - to check for obj.getClass(); statements
var GetClassNPEChecker = Java.extend(TreeScanner);
var visitor = new GetClassNPEChecker() {
lineMap: null,
sourceFile: null,
// save compilation unit details for reporting
visitCompilationUnit: function(node, p) {
this.sourceFile = node.sourceFile;
this.lineMap = node.lineMap;
return Java.super(visitor).visitCompilationUnit(node, p);
},
// look for "foo.getClass();" expression statements
visitExpressionStatement: function(node, p) {
var expr = node.expression;
if (expr instanceof MethodInvocationTree) {
var name = String(expr.methodSelect.identifier);
// will match any "getClass" call with zero arguments!
if (name == "getClass" && expr.arguments.size() == 0) {
print(this.sourceFile.getName()
+ " @ "
+ this.lineMap.getLineNumber(node.pos)
+ ":"
+ this.lineMap.getColumnNumber(node.pos));
print("\t", node);
}
}
}
}
for each (var cu in task.parse()) {
cu.accept(visitor, null);
}
}
// for each ".java" file in the directory (recursively)
function main(dir) {
Files.walk(dir.toPath()).
forEach(function(p) {
var name = p.toFile().absolutePath;
if (name.endsWith(".java")) {
try {
checkGetClassNPE(p.toFile().getAbsolutePath());
} catch (e) {
print(e);
}
}
});
}
main(new File(arguments[0]));

View file

@ -40,7 +40,6 @@
var Arrays = Java.type("java.util.Arrays"); var Arrays = Java.type("java.util.Arrays");
var BufferedReader = Java.type("java.io.BufferedReader"); var BufferedReader = Java.type("java.io.BufferedReader");
var FileWriter = Java.type("java.io.FileWriter"); var FileWriter = Java.type("java.io.FileWriter");
var List = Java.type("java.util.List");
var LocalDateTime = Java.type("java.time.LocalDateTime"); var LocalDateTime = Java.type("java.time.LocalDateTime");
var InputStreamReader = Java.type("java.io.InputStreamReader"); var InputStreamReader = Java.type("java.io.InputStreamReader");
var PrintWriter = Java.type("java.io.PrintWriter"); var PrintWriter = Java.type("java.io.PrintWriter");
@ -122,7 +121,7 @@ EOF
// execute code command // execute code command
function exec(args) { function exec(args) {
// build child process and start it! // build child process and start it!
new ProcessBuilder(Java.to(args.split(' '), List)) new ProcessBuilder(Arrays.asList(args.split(' ')))
.inheritIO() .inheritIO()
.start() .start()
.waitFor(); .waitFor();

View file

@ -42,7 +42,6 @@
var Arrays = Java.type("java.util.Arrays"); var Arrays = Java.type("java.util.Arrays");
var BufferedReader = Java.type("java.io.BufferedReader"); var BufferedReader = Java.type("java.io.BufferedReader");
var InputStreamReader = Java.type("java.io.InputStreamReader"); var InputStreamReader = Java.type("java.io.InputStreamReader");
var List = Java.type("java.util.List");
var ProcessBuilder = Java.type("java.lang.ProcessBuilder"); var ProcessBuilder = Java.type("java.lang.ProcessBuilder");
var System = Java.type("java.lang.System"); var System = Java.type("java.lang.System");
@ -67,7 +66,7 @@
} }
} else { } else {
// build child process and start it! // build child process and start it!
new ProcessBuilder(Java.to(args, List)) new ProcessBuilder(Arrays.asList(args))
.inheritIO() .inheritIO()
.start() .start()
.waitFor(); .waitFor();

View file

@ -97,6 +97,8 @@ import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.MethodHandleTransformer;
import jdk.internal.dynalink.linker.MethodTypeConversionStrategy; import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
import jdk.internal.dynalink.support.AutoDiscovery; import jdk.internal.dynalink.support.AutoDiscovery;
import jdk.internal.dynalink.support.BottomGuardingDynamicLinker; import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
@ -132,6 +134,7 @@ public class DynamicLinkerFactory {
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD; private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
private GuardedInvocationFilter prelinkFilter; private GuardedInvocationFilter prelinkFilter;
private MethodTypeConversionStrategy autoConversionStrategy; private MethodTypeConversionStrategy autoConversionStrategy;
private MethodHandleTransformer internalObjectsFilter;
/** /**
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread * Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
@ -283,6 +286,15 @@ public class DynamicLinkerFactory {
this.autoConversionStrategy = autoConversionStrategy; this.autoConversionStrategy = autoConversionStrategy;
} }
/**
* Sets a method handle transformer that is supposed to act as the implementation of this linker factory's linkers'
* services {@link LinkerServices#filterInternalObjects(java.lang.invoke.MethodHandle)} method.
* @param internalObjectsFilter a method handle transformer filtering out internal objects, or null.
*/
public void setInternalObjectsFilter(final MethodHandleTransformer internalObjectsFilter) {
this.internalObjectsFilter = internalObjectsFilter;
}
/** /**
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as * Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
* the pre-link filter. * the pre-link filter.
@ -350,8 +362,8 @@ public class DynamicLinkerFactory {
} }
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters, return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
autoConversionStrategy), composite), prelinkFilter, runtimeContextArgCount, syncOnRelink, autoConversionStrategy), composite, internalObjectsFilter), prelinkFilter, runtimeContextArgCount,
unstableRelinkThreshold); syncOnRelink, unstableRelinkThreshold);
} }
private static ClassLoader getThreadContextClassLoader() { private static ClassLoader getThreadContextClassLoader() {

View file

@ -570,7 +570,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments( private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments(
MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class); MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class);
private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class, private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class,
"getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class)); "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class, LinkerServices.class));
private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)); private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor, private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
@ -593,7 +593,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType( final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType(
AnnotatedDynamicMethod.class)); AnnotatedDynamicMethod.class));
final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments( final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments(
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup()); GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup(), linkerServices);
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0, final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
callSiteBoundMethodGetter); callSiteBoundMethodGetter);
// Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0) // Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
@ -873,8 +873,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
MethodHandle getTarget(final MethodHandles.Lookup lookup) { MethodHandle getTarget(final MethodHandles.Lookup lookup, final LinkerServices linkerServices) {
final MethodHandle inv = method.getTarget(lookup); final MethodHandle inv = linkerServices.filterInternalObjects(method.getTarget(lookup));
assert inv != null; assert inv != null;
return inv; return inv;
} }

View file

@ -165,6 +165,10 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
private static MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class); private static MethodHandle LIST_GUARD = Guards.getInstanceOfGuard(List.class);
private static MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class); private static MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class);
private enum CollectionType {
ARRAY, LIST, MAP
};
private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor, private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor,
final LinkerServices linkerServices, final List<String> operations) throws Exception { final LinkerServices linkerServices, final List<String> operations) throws Exception {
final MethodType callSiteType = callSiteDescriptor.getMethodType(); final MethodType callSiteType = callSiteDescriptor.getMethodType();
@ -178,27 +182,27 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
// Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
// in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
final GuardedInvocationComponent gic; final GuardedInvocationComponent gic;
final boolean isMap; final CollectionType collectionType;
if(declaredType.isArray()) { if(declaredType.isArray()) {
gic = new GuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType)); gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementGetter(declaredType), linkerServices);
isMap = false; collectionType = CollectionType.ARRAY;
} else if(List.class.isAssignableFrom(declaredType)) { } else if(List.class.isAssignableFrom(declaredType)) {
gic = new GuardedInvocationComponent(GET_LIST_ELEMENT); gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, linkerServices);
isMap = false; collectionType = CollectionType.LIST;
} else if(Map.class.isAssignableFrom(declaredType)) { } else if(Map.class.isAssignableFrom(declaredType)) {
gic = new GuardedInvocationComponent(GET_MAP_ELEMENT); gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, linkerServices);
isMap = true; collectionType = CollectionType.MAP;
} else if(clazz.isArray()) { } else if(clazz.isArray()) {
gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementGetter(clazz), callSiteType); gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(MethodHandles.arrayElementGetter(clazz)), callSiteType);
isMap = false; collectionType = CollectionType.ARRAY;
} else if(List.class.isAssignableFrom(clazz)) { } else if(List.class.isAssignableFrom(clazz)) {
gic = new GuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, gic = createInternalFilteredGuardedInvocationComponent(GET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
ValidationType.INSTANCE_OF); linkerServices);
isMap = false; collectionType = CollectionType.LIST;
} else if(Map.class.isAssignableFrom(clazz)) { } else if(Map.class.isAssignableFrom(clazz)) {
gic = new GuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, gic = createInternalFilteredGuardedInvocationComponent(GET_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, ValidationType.INSTANCE_OF,
ValidationType.INSTANCE_OF); linkerServices);
isMap = true; collectionType = CollectionType.MAP;
} else { } else {
// Can't retrieve elements for objects that are neither arrays, nor list, nor maps. // Can't retrieve elements for objects that are neither arrays, nor list, nor maps.
return nextComponent; return nextComponent;
@ -208,7 +212,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
final String fixedKey = getFixedKey(callSiteDescriptor); final String fixedKey = getFixedKey(callSiteDescriptor);
// Convert the key to a number if we're working with a list or array // Convert the key to a number if we're working with a list or array
final Object typedFixedKey; final Object typedFixedKey;
if(!isMap && fixedKey != null) { if(collectionType != CollectionType.MAP && fixedKey != null) {
typedFixedKey = convertKeyToInteger(fixedKey, linkerServices); typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
if(typedFixedKey == null) { if(typedFixedKey == null) {
// key is not numeric, it can never succeed // key is not numeric, it can never succeed
@ -227,15 +231,21 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
} }
final MethodHandle checkGuard; final MethodHandle checkGuard;
if(invocation == GET_LIST_ELEMENT) { switch(collectionType) {
case LIST:
checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor); checkGuard = convertArgToInt(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
} else if(invocation == GET_MAP_ELEMENT) { break;
case MAP:
// TODO: A more complex solution could be devised for maps, one where we do a get() first, and fold it // TODO: A more complex solution could be devised for maps, one where we do a get() first, and fold it
// into a GWT that tests if it returned null, and if it did, do another GWT with containsKey() // into a GWT that tests if it returned null, and if it did, do another GWT with containsKey()
// that returns constant null (on true), or falls back to next component (on false) // that returns constant null (on true), or falls back to next component (on false)
checkGuard = CONTAINS_MAP; checkGuard = linkerServices.filterInternalObjects(CONTAINS_MAP);
} else { break;
case ARRAY:
checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor); checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
break;
default:
throw new AssertionError();
} }
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation), final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
nextComponent.getGuardedInvocation().getInvocation()); nextComponent.getGuardedInvocation().getInvocation());
@ -243,6 +253,18 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
gic.getValidatorClass(), gic.getValidationType()); gic.getValidatorClass(), gic.getValidationType());
} }
private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
final MethodHandle invocation, final LinkerServices linkerServices) {
return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation));
}
private static GuardedInvocationComponent createInternalFilteredGuardedInvocationComponent(
final MethodHandle invocation, final MethodHandle guard, final Class<?> validatorClass,
final ValidationType validationType, final LinkerServices linkerServices) {
return new GuardedInvocationComponent(linkerServices.filterInternalObjects(invocation), guard,
validatorClass, validationType);
}
private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) { private static String getFixedKey(final CallSiteDescriptor callSiteDescriptor) {
return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken( return callSiteDescriptor.getNameTokenCount() == 2 ? null : callSiteDescriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND); CallSiteDescriptor.NAME_OPERAND);
@ -381,37 +403,38 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
// dealing with an array, or a list or map, but hey... // dealing with an array, or a list or map, but hey...
// Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers // Note that for arrays and lists, using LinkerServices.asType() will ensure that any language specific linkers
// in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices. // in use will get a chance to perform any (if there's any) implicit conversion to integer for the indices.
final boolean isMap; final CollectionType collectionType;
if(declaredType.isArray()) { if(declaredType.isArray()) {
gic = new GuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType)); gic = createInternalFilteredGuardedInvocationComponent(MethodHandles.arrayElementSetter(declaredType), linkerServices);
isMap = false; collectionType = CollectionType.ARRAY;
} else if(List.class.isAssignableFrom(declaredType)) { } else if(List.class.isAssignableFrom(declaredType)) {
gic = new GuardedInvocationComponent(SET_LIST_ELEMENT); gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, linkerServices);
isMap = false; collectionType = CollectionType.LIST;
} else if(Map.class.isAssignableFrom(declaredType)) { } else if(Map.class.isAssignableFrom(declaredType)) {
gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT); gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, linkerServices);
isMap = true; collectionType = CollectionType.MAP;
} else if(clazz.isArray()) { } else if(clazz.isArray()) {
gic = getClassGuardedInvocationComponent(MethodHandles.arrayElementSetter(clazz), callSiteType); gic = getClassGuardedInvocationComponent(linkerServices.filterInternalObjects(
isMap = false; MethodHandles.arrayElementSetter(clazz)), callSiteType);
collectionType = CollectionType.ARRAY;
} else if(List.class.isAssignableFrom(clazz)) { } else if(List.class.isAssignableFrom(clazz)) {
gic = new GuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, gic = createInternalFilteredGuardedInvocationComponent(SET_LIST_ELEMENT, Guards.asType(LIST_GUARD, callSiteType), List.class, ValidationType.INSTANCE_OF,
ValidationType.INSTANCE_OF); linkerServices);
isMap = false; collectionType = CollectionType.LIST;
} else if(Map.class.isAssignableFrom(clazz)) { } else if(Map.class.isAssignableFrom(clazz)) {
gic = new GuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType), Map.class, gic = createInternalFilteredGuardedInvocationComponent(PUT_MAP_ELEMENT, Guards.asType(MAP_GUARD, callSiteType),
ValidationType.INSTANCE_OF); Map.class, ValidationType.INSTANCE_OF, linkerServices);
isMap = true; collectionType = CollectionType.MAP;
} else { } else {
// Can't set elements for objects that are neither arrays, nor list, nor maps. // Can't set elements for objects that are neither arrays, nor list, nor maps.
gic = null; gic = null;
isMap = false; collectionType = null;
} }
// In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map, // In contrast to, say, getElementGetter, we only compute the nextComponent if the target object is not a map,
// as maps will always succeed in setting the element and will never need to fall back to the next component // as maps will always succeed in setting the element and will never need to fall back to the next component
// operation. // operation.
final GuardedInvocationComponent nextComponent = isMap ? null : getGuardedInvocationComponent( final GuardedInvocationComponent nextComponent = collectionType == CollectionType.MAP ? null : getGuardedInvocationComponent(
callSiteDescriptor, linkerServices, operations); callSiteDescriptor, linkerServices, operations);
if(gic == null) { if(gic == null) {
return nextComponent; return nextComponent;
@ -421,7 +444,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
final String fixedKey = getFixedKey(callSiteDescriptor); final String fixedKey = getFixedKey(callSiteDescriptor);
// Convert the key to a number if we're working with a list or array // Convert the key to a number if we're working with a list or array
final Object typedFixedKey; final Object typedFixedKey;
if(!isMap && fixedKey != null) { if(collectionType != CollectionType.MAP && fixedKey != null) {
typedFixedKey = convertKeyToInteger(fixedKey, linkerServices); typedFixedKey = convertKeyToInteger(fixedKey, linkerServices);
if(typedFixedKey == null) { if(typedFixedKey == null) {
// key is not numeric, it can never succeed // key is not numeric, it can never succeed
@ -439,7 +462,8 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
return gic.replaceInvocation(binder.bind(invocation)); return gic.replaceInvocation(binder.bind(invocation));
} }
final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST : assert collectionType == CollectionType.LIST || collectionType == CollectionType.ARRAY;
final MethodHandle checkGuard = convertArgToInt(collectionType == CollectionType.LIST ? RANGE_CHECK_LIST :
RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor); RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation), final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
nextComponent.getGuardedInvocation().getInvocation()); nextComponent.getGuardedInvocation().getInvocation());

View file

@ -139,7 +139,8 @@ class OverloadedMethod {
final MethodHandle bound = SELECT_METHOD.bindTo(this); final MethodHandle bound = SELECT_METHOD.bindTo(this);
final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType( final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
callSiteType.changeReturnType(MethodHandle.class)); callSiteType.changeReturnType(MethodHandle.class));
invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(this.callSiteType), collecting); invoker = linkerServices.asTypeLosslessReturn(MethodHandles.foldArguments(
MethodHandles.exactInvoker(this.callSiteType), collecting), callSiteType);
} }
MethodHandle getInvoker() { MethodHandle getInvoker() {

View file

@ -165,10 +165,11 @@ abstract class SingleDynamicMethod extends DynamicMethod {
* @return the adapted method handle. * @return the adapted method handle.
*/ */
static MethodHandle getInvocation(final MethodHandle target, final MethodType callSiteType, final LinkerServices linkerServices) { static MethodHandle getInvocation(final MethodHandle target, final MethodType callSiteType, final LinkerServices linkerServices) {
final MethodType methodType = target.type(); final MethodHandle filteredTarget = linkerServices.filterInternalObjects(target);
final MethodType methodType = filteredTarget.type();
final int paramsLen = methodType.parameterCount(); final int paramsLen = methodType.parameterCount();
final boolean varArgs = target.isVarargsCollector(); final boolean varArgs = target.isVarargsCollector();
final MethodHandle fixTarget = varArgs ? target.asFixedArity() : target; final MethodHandle fixTarget = varArgs ? filteredTarget.asFixedArity() : filteredTarget;
final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen; final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen;
final int argsLen = callSiteType.parameterCount(); final int argsLen = callSiteType.parameterCount();
if(argsLen < fixParamsLen) { if(argsLen < fixParamsLen) {
@ -204,7 +205,7 @@ abstract class SingleDynamicMethod extends DynamicMethod {
if(varArgType.isAssignableFrom(callSiteLastArgType)) { if(varArgType.isAssignableFrom(callSiteLastArgType)) {
// Call site signature guarantees we'll always be passed a single compatible array; just link directly // Call site signature guarantees we'll always be passed a single compatible array; just link directly
// to the method, introducing necessary conversions. Also, preserve it being a variable arity method. // to the method, introducing necessary conversions. Also, preserve it being a variable arity method.
return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector( return createConvertingInvocation(filteredTarget, linkerServices, callSiteType).asVarargsCollector(
callSiteLastArgType); callSiteLastArgType);
} }

View file

@ -180,6 +180,15 @@ public interface LinkerServices {
*/ */
public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2); public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);
/**
* Modifies the method handle so that any parameters that can receive potentially internal language runtime objects
* will have a filter added on them to prevent them from escaping, potentially by wrapping them.
* It can also potentially add an unwrapping filter to the return value.
* @param target the target method handle
* @return a method handle with parameters and/or return type potentially filtered for wrapping and unwrapping.
*/
public MethodHandle filterInternalObjects(final MethodHandle target);
/** /**
* If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default * If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default
* implementation. Since we can't do that, we extract common default implementations into this static class. * implementation. Since we can't do that, we extract common default implementations into this static class.

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2015, 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.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file, and Oracle licenses the original version of this file under the BSD
* license:
*/
/*
Copyright 2009-2015 Attila Szegedi
Licensed under both the Apache License, Version 2.0 (the "Apache License")
and the BSD License (the "BSD License"), with licensee being free to
choose either of the two at their discretion.
You may not use this file except in compliance with either the Apache
License or the BSD License.
If you choose to use this file in compliance with the Apache License, the
following notice applies to you:
You may obtain a copy of the Apache License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
If you choose to use this file in compliance with the BSD License, the
following notice applies to you:
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 the copyright holder nor the names of
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 COPYRIGHT HOLDER
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.internal.dynalink.linker;
import java.lang.invoke.MethodHandle;
/**
* A generic interface describing operations that transform method handles.
*/
public interface MethodHandleTransformer {
/**
* Transforms a method handle.
* @param target the method handle being transformed.
* @return transformed method handle.
*/
public MethodHandle transform(final MethodHandle target);
}

View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2015, 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.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file, and Oracle licenses the original version of this file under the BSD
* license:
*/
/*
Copyright 2009-2015 Attila Szegedi
Licensed under both the Apache License, Version 2.0 (the "Apache License")
and the BSD License (the "BSD License"), with licensee being free to
choose either of the two at their discretion.
You may not use this file except in compliance with either the Apache
License or the BSD License.
If you choose to use this file in compliance with the Apache License, the
following notice applies to you:
You may obtain a copy of the Apache License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
If you choose to use this file in compliance with the BSD License, the
following notice applies to you:
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 the copyright holder nor the names of
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 COPYRIGHT HOLDER
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.internal.dynalink.support;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.DynamicLinkerFactory;
import jdk.internal.dynalink.linker.MethodHandleTransformer;
/**
* Default implementation for a {@link DynamicLinkerFactory#setInternalObjectsFilter(MethodHandleTransformer)}.
* Given a method handle of {@code Object(Object)} type for filtering parameter and another one of the same type for
* filtering return values, applies them to passed method handles where their parameter types and/or return value types
* are declared to be {@link Object}.
*/
public class DefaultInternalObjectFilter implements MethodHandleTransformer {
private static final MethodHandle FILTER_VARARGS = new Lookup(MethodHandles.lookup()).findStatic(
DefaultInternalObjectFilter.class, "filterVarArgs", MethodType.methodType(Object[].class, MethodHandle.class, Object[].class));
private final MethodHandle parameterFilter;
private final MethodHandle returnFilter;
private final MethodHandle varArgFilter;
/**
* Creates a new filter.
* @param parameterFilter the filter for method parameters. Must be of type {@code Object(Object)}, or null.
* @param returnFilter the filter for return values. Must be of type {@code Object(Object)}, or null.
* @throws IllegalArgumentException if one or both filters are not of the expected type.
*/
public DefaultInternalObjectFilter(final MethodHandle parameterFilter, final MethodHandle returnFilter) {
this.parameterFilter = checkHandle(parameterFilter, "parameterFilter");
this.returnFilter = checkHandle(returnFilter, "returnFilter");
this.varArgFilter = parameterFilter == null ? null : FILTER_VARARGS.bindTo(parameterFilter);
}
@Override
public MethodHandle transform(final MethodHandle target) {
assert target != null;
MethodHandle[] filters = null;
final MethodType type = target.type();
final boolean isVarArg = target.isVarargsCollector();
final int paramCount = type.parameterCount();
final MethodHandle paramsFiltered;
// Filter parameters
if (parameterFilter != null) {
int firstFilter = -1;
// Ignore receiver, start from argument 1
for(int i = 1; i < paramCount; ++i) {
final Class<?> paramType = type.parameterType(i);
final boolean filterVarArg = isVarArg && i == paramCount - 1 && paramType == Object[].class;
if (filterVarArg || paramType == Object.class) {
if (filters == null) {
firstFilter = i;
filters = new MethodHandle[paramCount - firstFilter];
}
filters[i - firstFilter] = filterVarArg ? varArgFilter : parameterFilter;
}
}
paramsFiltered = filters != null ? MethodHandles.filterArguments(target, firstFilter, filters) : target;
} else {
paramsFiltered = target;
}
// Filter return value if needed
final MethodHandle returnFiltered = returnFilter != null && type.returnType() == Object.class ? MethodHandles.filterReturnValue(paramsFiltered, returnFilter) : paramsFiltered;
// Preserve varargs collector state
return isVarArg && !returnFiltered.isVarargsCollector() ? returnFiltered.asVarargsCollector(type.parameterType(paramCount - 1)) : returnFiltered;
}
private static MethodHandle checkHandle(final MethodHandle handle, final String handleKind) {
if (handle != null) {
final MethodType objectObjectType = MethodType.methodType(Object.class, Object.class);
if (!handle.type().equals(objectObjectType)) {
throw new IllegalArgumentException("Method type for " + handleKind + " must be " + objectObjectType);
}
}
return handle;
}
@SuppressWarnings("unused")
private static Object[] filterVarArgs(final MethodHandle parameterFilter, final Object[] args) throws Throwable {
Object[] newArgs = null;
for(int i = 0; i < args.length; ++i) {
final Object arg = args[i];
final Object newArg = parameterFilter.invokeExact(arg);
if (arg != newArg) {
if (newArgs == null) {
newArgs = args.clone();
}
newArgs[i] = newArg;
}
}
return newArgs == null ? args : newArgs;
}
}

View file

@ -90,6 +90,7 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.MethodHandleTransformer;
/** /**
* Default implementation of the {@link LinkerServices} interface. * Default implementation of the {@link LinkerServices} interface.
@ -103,17 +104,21 @@ public class LinkerServicesImpl implements LinkerServices {
private final TypeConverterFactory typeConverterFactory; private final TypeConverterFactory typeConverterFactory;
private final GuardingDynamicLinker topLevelLinker; private final GuardingDynamicLinker topLevelLinker;
private final MethodHandleTransformer internalObjectsFilter;
/** /**
* Creates a new linker services object. * Creates a new linker services object.
* *
* @param typeConverterFactory the type converter factory exposed by the services. * @param typeConverterFactory the type converter factory exposed by the services.
* @param topLevelLinker the top level linker used by the services. * @param topLevelLinker the top level linker used by the services.
* @param internalObjectsFilter a method handle transformer that is supposed to act as the implementation of this
* services' {@link #filterInternalObjects(java.lang.invoke.MethodHandle)} method.
*/ */
public LinkerServicesImpl(final TypeConverterFactory typeConverterFactory, public LinkerServicesImpl(final TypeConverterFactory typeConverterFactory,
final GuardingDynamicLinker topLevelLinker) { final GuardingDynamicLinker topLevelLinker, final MethodHandleTransformer internalObjectsFilter) {
this.typeConverterFactory = typeConverterFactory; this.typeConverterFactory = typeConverterFactory;
this.topLevelLinker = topLevelLinker; this.topLevelLinker = topLevelLinker;
this.internalObjectsFilter = internalObjectsFilter;
} }
@Override @Override
@ -152,6 +157,11 @@ public class LinkerServicesImpl implements LinkerServices {
} }
} }
@Override
public MethodHandle filterInternalObjects(final MethodHandle target) {
return internalObjectsFilter != null ? internalObjectsFilter.transform(target) : target;
}
/** /**
* Returns the currently processed link request, or null if the method is invoked outside of the linking process. * Returns the currently processed link request, or null if the method is invoked outside of the linking process.
* @return the currently processed link request, or null. * @return the currently processed link request, or null.

View file

@ -448,7 +448,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
checkKey(key); checkKey(key);
return inGlobal(new Callable<Object>() { return inGlobal(new Callable<Object>() {
@Override public Object call() { @Override public Object call() {
return wrap(sobj.remove(key, strict), global); return translateUndefined(wrap(sobj.remove(key, strict), global));
} }
}); });
} }

View file

@ -38,6 +38,7 @@ import java.util.IdentityHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* Contains utility methods for calculating the memory usage of objects. It * Contains utility methods for calculating the memory usage of objects. It
@ -150,7 +151,7 @@ public final class ObjectSizeCalculator {
* @param memoryLayoutSpecification a description of the JVM memory layout. * @param memoryLayoutSpecification a description of the JVM memory layout.
*/ */
public ObjectSizeCalculator(final MemoryLayoutSpecification memoryLayoutSpecification) { public ObjectSizeCalculator(final MemoryLayoutSpecification memoryLayoutSpecification) {
memoryLayoutSpecification.getClass(); Objects.requireNonNull(memoryLayoutSpecification);
arrayHeaderSize = memoryLayoutSpecification.getArrayHeaderSize(); arrayHeaderSize = memoryLayoutSpecification.getArrayHeaderSize();
objectHeaderSize = memoryLayoutSpecification.getObjectHeaderSize(); objectHeaderSize = memoryLayoutSpecification.getObjectHeaderSize();
objectPadding = memoryLayoutSpecification.getObjectPadding(); objectPadding = memoryLayoutSpecification.getObjectPadding();

View file

@ -119,6 +119,7 @@ public final class Bootstrap {
return unboxReturnType(target, newType); return unboxReturnType(target, newType);
} }
}); });
factory.setInternalObjectsFilter(NashornBeansLinker.createHiddenObjectFilter());
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD); final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
if (relinkThreshold > -1) { if (relinkThreshold > -1) {
factory.setUnstableRelinkThreshold(relinkThreshold); factory.setUnstableRelinkThreshold(relinkThreshold);

View file

@ -40,10 +40,11 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.MethodHandleTransformer;
import jdk.internal.dynalink.support.DefaultInternalObjectFilter;
import jdk.internal.dynalink.support.Guards; import jdk.internal.dynalink.support.Guards;
import jdk.internal.dynalink.support.Lookup; import jdk.internal.dynalink.support.Lookup;
import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.api.scripting.ScriptUtils;
import jdk.nashorn.internal.objects.NativeArray;
import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptObject;
@ -52,10 +53,14 @@ import jdk.nashorn.internal.runtime.options.Options;
/** /**
* This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified
* {@code asType} method that will ensure that we never pass internal engine objects that should not be externally * {@code compareConversion} method that favors conversion of {@link ConsString} to either {@link String} or
* observable (currently ConsString and ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add * {@link CharSequence}. It also provides a {@link #createHiddenObjectFilter()} method for use with bootstrap that will
* this functionality as custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when * ensure that we never pass internal engine objects that should not be externally observable (currently ConsString and
* the target method handle parameter signature is {@code Object}. * ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add this functionality as
* custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when
* the target method handle parameter signature is {@code Object}. This linker also makes sure that primitive
* {@link String} operations can be invoked on a {@link ConsString}, and allows invocation of objects implementing
* the {@link FunctionalInterface} attribute.
*/ */
public class NashornBeansLinker implements GuardingDynamicLinker { public class NashornBeansLinker implements GuardingDynamicLinker {
// System property to control whether to wrap ScriptObject->ScriptObjectMirror for // System property to control whether to wrap ScriptObject->ScriptObjectMirror for
@ -63,16 +68,12 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true); private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
private static final MethodHandle EXPORT_ARGUMENT; private static final MethodHandle EXPORT_ARGUMENT;
private static final MethodHandle EXPORT_NATIVE_ARRAY;
private static final MethodHandle EXPORT_SCRIPT_OBJECT;
private static final MethodHandle IMPORT_RESULT; private static final MethodHandle IMPORT_RESULT;
private static final MethodHandle FILTER_CONSSTRING; private static final MethodHandle FILTER_CONSSTRING;
static { static {
final Lookup lookup = new Lookup(MethodHandles.lookup()); final Lookup lookup = new Lookup(MethodHandles.lookup());
EXPORT_ARGUMENT = lookup.findOwnStatic("exportArgument", Object.class, Object.class); EXPORT_ARGUMENT = lookup.findOwnStatic("exportArgument", Object.class, Object.class);
EXPORT_NATIVE_ARRAY = lookup.findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
EXPORT_SCRIPT_OBJECT = lookup.findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
IMPORT_RESULT = lookup.findOwnStatic("importResult", Object.class, Object.class); IMPORT_RESULT = lookup.findOwnStatic("importResult", Object.class, Object.class);
FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class); FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class);
} }
@ -115,9 +116,10 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
} }
return new GuardedInvocation( return new GuardedInvocation(
// drop 'thiz' passed from the script. // drop 'thiz' passed from the script.
MH.dropArguments(desc.getLookup().unreflect(m), 1, callType.parameterType(1)), MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1,
Guards.getInstanceOfGuard(m.getDeclaringClass())).asTypeSafeReturn( callType.parameterType(1)), Guards.getInstanceOfGuard(
new NashornBeansLinkerServices(linkerServices), callType); m.getDeclaringClass())).asTypeSafeReturn(
new NashornBeansLinkerServices(linkerServices), callType);
} }
} }
return getGuardedInvocation(beansLinker, linkRequest, linkerServices); return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
@ -141,21 +143,6 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return exportArgument(arg, MIRROR_ALWAYS); return exportArgument(arg, MIRROR_ALWAYS);
} }
@SuppressWarnings("unused")
private static Object exportNativeArray(final NativeArray arg) {
return exportArgument(arg, MIRROR_ALWAYS);
}
@SuppressWarnings("unused")
private static Object exportScriptObject(final ScriptObject arg) {
return exportArgument(arg, MIRROR_ALWAYS);
}
@SuppressWarnings("unused")
private static Object exportScriptArray(final NativeArray arg) {
return exportArgument(arg, MIRROR_ALWAYS);
}
static Object exportArgument(final Object arg, final boolean mirrorAlways) { static Object exportArgument(final Object arg, final boolean mirrorAlways) {
if (arg instanceof ConsString) { if (arg instanceof ConsString) {
return arg.toString(); return arg.toString();
@ -208,6 +195,10 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
return FUNCTIONAL_IFACE_METHOD.get(clazz); return FUNCTIONAL_IFACE_METHOD.get(clazz);
} }
static MethodHandleTransformer createHiddenObjectFilter() {
return new DefaultInternalObjectFilter(EXPORT_ARGUMENT, MIRROR_ALWAYS ? IMPORT_RESULT : null);
}
private static class NashornBeansLinkerServices implements LinkerServices { private static class NashornBeansLinkerServices implements LinkerServices {
private final LinkerServices linkerServices; private final LinkerServices linkerServices;
@ -217,50 +208,7 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
@Override @Override
public MethodHandle asType(final MethodHandle handle, final MethodType fromType) { public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
final MethodType handleType = handle.type(); return linkerServices.asType(handle, fromType);
final int paramCount = handleType.parameterCount();
assert fromType.parameterCount() == handleType.parameterCount();
MethodType newFromType = fromType;
MethodHandle[] filters = null;
for(int i = 0; i < paramCount; ++i) {
final MethodHandle filter = argConversionFilter(handleType.parameterType(i), fromType.parameterType(i));
if (filter != null) {
if (filters == null) {
filters = new MethodHandle[paramCount];
}
// "erase" specific type with Object type or else we'll get filter mismatch
newFromType = newFromType.changeParameterType(i, Object.class);
filters[i] = filter;
}
}
final MethodHandle typed = linkerServices.asType(handle, newFromType);
MethodHandle result = filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed;
// Filter Object typed return value for possible ScriptObjectMirror. We convert
// ScriptObjectMirror as ScriptObject (if it is mirror from current global).
if (MIRROR_ALWAYS && areBothObjects(handleType.returnType(), fromType.returnType())) {
result = MethodHandles.filterReturnValue(result, IMPORT_RESULT);
}
return result;
}
private static MethodHandle argConversionFilter(final Class<?> handleType, final Class<?> fromType) {
if (handleType == Object.class) {
if (fromType == Object.class) {
return EXPORT_ARGUMENT;
} else if (fromType == NativeArray.class) {
return EXPORT_NATIVE_ARRAY;
} else if (fromType == ScriptObject.class) {
return EXPORT_SCRIPT_OBJECT;
}
}
return null;
}
private static boolean areBothObjects(final Class<?> handleType, final Class<?> fromType) {
return handleType == Object.class && fromType == Object.class;
} }
@Override @Override
@ -296,5 +244,10 @@ public class NashornBeansLinker implements GuardingDynamicLinker {
} }
return linkerServices.compareConversion(sourceType, targetType1, targetType2); return linkerServices.compareConversion(sourceType, targetType1, targetType2);
} }
@Override
public MethodHandle filterInternalObjects(MethodHandle target) {
return linkerServices.filterInternalObjects(target);
}
} }
} }

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2015 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.
*
* 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.
*/
/**
* JDK-8072596: Arrays.asList results in ClassCastException with a JS array
*
* @test
* @run
*/
var arr = java.util.Arrays.asList("hello world".split(' '));
// We split it into a list of two elements: [hello, world]
Assert.assertTrue(arr instanceof java.util.List);
Assert.assertEquals(arr.length, 2);
Assert.assertEquals(arr[0], "hello");
Assert.assertEquals(arr[1], "world");
var Jdk8072596TestSubject = Java.type("jdk.nashorn.test.models.Jdk8072596TestSubject");
var testSubject = new Jdk8072596TestSubject({bar: 0});
testSubject.test1(true, {foo: 1}, {bar: 2});
testSubject.test2(true, {foo: 1}, {bar: 2}, {baz: 3}, {bing: 4});
var h = "h";
var ello = "ello";
testSubject.test3(true, {foo: 5}, /* ConsString, why not */ h + ello, [6, 7], 8);
Jdk8072596TestSubject.test4({foo: 9});
// Test wrapping setters arguments and unwrapping getters return values on list.
var list = new java.util.ArrayList();
list.add(null);
var obj0 = {valueOf: function() { return 0; }};
var obj1 = {foo: 10};
list[obj0] = obj1;
testSubject.testListHasWrappedObject(list);
// NOTE: can't use Assert.assertSame(obj1, list[obj0]), as the arguments would end up being wrapped...
Assert.assertTrue(obj1 === list[obj0]);
// Test wrapping setters arguments and unwrapping getters return values on array.
var arr2 = new (Java.type("java.lang.Object[]"))(1);
var obj2 = {bar: 11};
arr2[obj0] = obj2;
testSubject.testArrayHasWrappedObject(arr2);
Assert.assertTrue(obj2 === arr2[obj0]);
// Test wrapping setters index and arguments and getters index, and unwrapping getters return values on map.
// Since ScriptObjectMirror.equals() uses underlying ScriptObject identity, using them as map keys works.
var map = new java.util.HashMap();
var obj3 = {bar: 12};
map[obj0] = obj3;
testSubject.testMapHasWrappedObject(map, obj0);
Assert.assertTrue(obj3 === map[obj0]);

View file

@ -706,4 +706,74 @@ public class ScopeTest {
} }
} }
// @bug 8071678: NashornScriptEngine returns javax.script.ScriptContext instance
// with get/setAttribute methods insonsistent for GLOBAL_SCOPE
@Test
public void testGlobalScopeSearch() throws Exception {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final ScriptContext c = e.getContext();
c.setAttribute("name1234", "value", ScriptContext.GLOBAL_SCOPE);
assertEquals(c.getAttribute("name1234"), "value");
assertEquals(c.getAttributesScope("name1234"),
ScriptContext.GLOBAL_SCOPE);
}
// @bug 8071594: NashornScriptEngine returns javax.script.ScriptContext instance
// which doesn't completely conform to the spec regarding exceptions throwing
@Test
public void testScriptContext_NPE_IAE() throws Exception {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final ScriptContext c = e.getContext();
try {
c.getAttribute("");
throw new AssertionError("should have thrown IAE");
} catch (IllegalArgumentException iae1) {}
try {
c.getAttribute(null);
throw new AssertionError("should have thrown NPE");
} catch (NullPointerException npe1) {}
try {
c.getAttribute("", ScriptContext.ENGINE_SCOPE);
throw new AssertionError("should have thrown IAE");
} catch (IllegalArgumentException iae2) {}
try {
c.getAttribute(null, ScriptContext.ENGINE_SCOPE);
throw new AssertionError("should have thrown NPE");
} catch (NullPointerException npe2) {}
try {
c.removeAttribute("", ScriptContext.ENGINE_SCOPE);
throw new AssertionError("should have thrown IAE");
} catch (IllegalArgumentException iae3) {}
try {
c.removeAttribute(null, ScriptContext.ENGINE_SCOPE);
throw new AssertionError("should have thrown NPE");
} catch (NullPointerException npe3) {}
try {
c.setAttribute("", "value", ScriptContext.ENGINE_SCOPE);
throw new AssertionError("should have thrown IAE");
} catch (IllegalArgumentException iae4) {}
try {
c.setAttribute(null, "value", ScriptContext.ENGINE_SCOPE);
throw new AssertionError("should have thrown NPE");
} catch (NullPointerException npe4) {}
try {
c.getAttributesScope("");
throw new AssertionError("should have thrown IAE");
} catch (IllegalArgumentException iae5) {}
try {
c.getAttributesScope(null);
throw new AssertionError("should have thrown NPE");
} catch (NullPointerException npe5) {}
}
} }

View file

@ -852,6 +852,17 @@ public class ScriptEngineTest {
} }
} }
// @bug 8071989: NashornScriptEngine returns javax.script.ScriptContext instance
// with insonsistent get/remove methods behavior for undefined attributes
@Test
public void testScriptContextGetRemoveUndefined() throws Exception {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine e = manager.getEngineByName("nashorn");
final ScriptContext ctx = e.getContext();
assertNull(ctx.getAttribute("undefinedname", ScriptContext.ENGINE_SCOPE));
assertNull(ctx.removeAttribute("undefinedname", ScriptContext.ENGINE_SCOPE));
}
private static void checkProperty(final ScriptEngine e, final String name) private static void checkProperty(final ScriptEngine e, final String name)
throws ScriptException { throws ScriptException {
final String value = System.getProperty(name); final String value = System.getProperty(name);

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2015, 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.test.models;
import java.util.List;
import java.util.Map;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.runtime.ScriptObject;
import org.testng.Assert;
public class Jdk8072596TestSubject {
public Jdk8072596TestSubject(final Object x) {
Assert.assertTrue(x instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)x).get("bar"), 0);
}
// Test having to wrap some arguments but not others
public void test1(final String x, final Object y, final ScriptObject w) {
Assert.assertEquals(x, "true");
Assert.assertTrue(y instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)y).get("foo"), 1);
Assert.assertEquals(w.get("bar"), 2);
}
// Test having to wrap some arguments but not others, and a vararg array
public void test2(String x, final Object y, final ScriptObject w, final Object... z) {
test1(x, y, w);
Assert.assertEquals(z.length, 2);
Assert.assertTrue(z[0] instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)z[0]).get("baz"), 3);
Assert.assertTrue(z[1] instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)z[1]).get("bing"), 4);
}
// Test mixed (wrappable and non-wrappable) elements in a vararg array
public void test3(final Object... z) {
Assert.assertEquals(z.length, 5);
Assert.assertEquals(z[0], true);
Assert.assertTrue(z[1] instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)z[1]).get("foo"), 5);
Assert.assertEquals(z[2], "hello");
Assert.assertTrue(z[3] instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)z[3]).getSlot(0), 6);
Assert.assertEquals(((ScriptObjectMirror)z[3]).getSlot(1), 7);
Assert.assertEquals(z[4], 8);
}
// test wrapping the first argument of a static method
public static void test4(final Object x) {
Assert.assertTrue(x instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)x).get("foo"), 9);
}
public void testListHasWrappedObject(final List<?> l) {
Assert.assertEquals(l.size(), 1);
Assert.assertTrue(l.get(0) instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)l.get(0)).get("foo"), 10);
}
public void testArrayHasWrappedObject(final Object[] a) {
Assert.assertEquals(a.length, 1);
Assert.assertTrue(a[0] instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)a[0]).get("bar"), 11);
}
public void testMapHasWrappedObject(final Map<?, ?> m, final Object key) {
Assert.assertEquals(m.size(), 1);
Assert.assertTrue(key instanceof ScriptObjectMirror);
Assert.assertTrue(m.get(key) instanceof ScriptObjectMirror);
Assert.assertEquals(((ScriptObjectMirror)m.get(key)).get("bar"), 12);
}
}