NativeJavaPackage.java revision 1551:f3b883bec2d0
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.runtime; 27 28import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 29import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 30 31import java.lang.invoke.MethodHandle; 32import java.lang.invoke.MethodHandles; 33import java.lang.invoke.MethodType; 34import jdk.dynalink.CallSiteDescriptor; 35import jdk.dynalink.beans.BeansLinker; 36import jdk.dynalink.beans.StaticClass; 37import jdk.dynalink.linker.GuardedInvocation; 38import jdk.dynalink.linker.LinkRequest; 39import jdk.dynalink.linker.support.Guards; 40import jdk.nashorn.internal.lookup.MethodHandleFactory; 41import jdk.nashorn.internal.lookup.MethodHandleFunctionality; 42import jdk.nashorn.internal.objects.annotations.Attribute; 43import jdk.nashorn.internal.objects.annotations.Function; 44import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 45 46/** 47 * An object that exposes Java packages and classes as its properties. Packages are exposed as objects that have further 48 * sub-packages and classes as their properties. Normally, three instances of this class are exposed as built-in objects 49 * in Nashorn: {@code "Packages"}, {@code "java"}, and {@code "javax"}. Typical usages are: 50 * <pre> 51 * var list = new java.util.ArrayList() 52 * var sprocket = new Packages.com.acme.Sprocket() 53 * </pre> 54 * or you can store the type objects in a variable for later reuse: 55 * <pre> 56 * var ArrayList = java.util.ArrayList 57 * var list = new ArrayList 58 * </pre> 59 * You can also use {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)} to access Java classes. These two statements are mostly 60 * equivalent: 61 * <pre> 62 * var listType1 = java.util.ArrayList 63 * var listType2 = Java.type("java.util.ArrayList") 64 * </pre> 65 * The difference is that {@code Java.type()} will throw an error if the class does not exist, while the first 66 * expression will return an empty object, as it must treat all non-existent classes as potentially being further 67 * subpackages. As such, {@code Java.type()} has the potential to catch typos earlier. A further difference is that 68 * {@code Java.type()} doesn't recognize {@code .} (dot) as the separator between outer class name and inner class name, 69 * it only recognizes the dollar sign. These are equivalent: 70 * <pre> 71 * var ftype1 = java.awt.geom.Arc2D$Float 72 * var ftype2 = java.awt.geom.Arc2D.Float 73 * var ftype3 = Java.asType("java.awt.geom.Arc2D$Float") 74 * var ftype4 = Java.asType("java.awt.geom.Arc2D").Float 75 * </pre> 76 */ 77public final class NativeJavaPackage extends ScriptObject { 78 private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality(); 79 private static final MethodHandle CLASS_NOT_FOUND = findOwnMH("classNotFound", Void.TYPE, NativeJavaPackage.class); 80 private static final MethodHandle TYPE_GUARD = Guards.getClassGuard(NativeJavaPackage.class); 81 82 /** Full name of package (includes path.) */ 83 private final String name; 84 85 /** 86 * Public constructor to be accessible from {@link jdk.nashorn.internal.objects.Global} 87 * @param name package name 88 * @param proto proto 89 */ 90 public NativeJavaPackage(final String name, final ScriptObject proto) { 91 super(proto, null); 92 // defense-in-path, check here for sensitive packages 93 Context.checkPackageAccess(name); 94 this.name = name; 95 } 96 97 @Override 98 public String getClassName() { 99 return "JavaPackage"; 100 } 101 102 @Override 103 public boolean equals(final Object other) { 104 if (other instanceof NativeJavaPackage) { 105 return name.equals(((NativeJavaPackage)other).name); 106 } 107 return false; 108 } 109 110 @Override 111 public int hashCode() { 112 return name == null ? 0 : name.hashCode(); 113 } 114 115 /** 116 * Get the full name of the package 117 * @return the name 118 */ 119 public String getName() { 120 return name; 121 } 122 123 @Override 124 public String safeToString() { 125 return toString(); 126 } 127 128 @Override 129 public String toString() { 130 return "[JavaPackage " + name + "]"; 131 } 132 133 @Override 134 public Object getDefaultValue(final Class<?> hint) { 135 if (hint == String.class) { 136 return toString(); 137 } 138 139 return super.getDefaultValue(hint); 140 } 141 142 @Override 143 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { 144 return createClassNotFoundInvocation(desc); 145 } 146 147 @Override 148 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 149 return createClassNotFoundInvocation(desc); 150 } 151 152 private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) { 153 // If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as 154 // we can assume the user attempted to instantiate a non-existent class. 155 final MethodType type = desc.getMethodType(); 156 return new GuardedInvocation( 157 MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())), 158 type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD); 159 } 160 161 @SuppressWarnings("unused") 162 private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException { 163 throw new ClassNotFoundException(pkg.name); 164 } 165 166 /** 167 * "No such property" call placeholder. 168 * 169 * This can never be called as we override {@link ScriptObject#noSuchProperty}. We do declare it here as it's a signal 170 * to {@link WithObject} that it's worth trying doing a {@code noSuchProperty} on this object. 171 * 172 * @param self self reference 173 * @param name property name 174 * @return never returns 175 */ 176 @Function(attributes = Attribute.NOT_ENUMERABLE) 177 public static Object __noSuchProperty__(final Object self, final Object name) { 178 throw new AssertionError("__noSuchProperty__ placeholder called"); 179 } 180 181 /** 182 * "No such method call" placeholder 183 * 184 * This can never be called as we override {@link ScriptObject#noSuchMethod}. We do declare it here as it's a signal 185 * to {@link WithObject} that it's worth trying doing a noSuchProperty on this object. 186 * 187 * @param self self reference 188 * @param args arguments to method 189 * @return never returns 190 */ 191 @Function(attributes = Attribute.NOT_ENUMERABLE) 192 public static Object __noSuchMethod__(final Object self, final Object... args) { 193 throw new AssertionError("__noSuchMethod__ placeholder called"); 194 } 195 196 /** 197 * Handle creation of new attribute. 198 * @param desc the call site descriptor 199 * @param request the link request 200 * @return Link to be invoked at call site. 201 */ 202 @Override 203 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { 204 final String propertyName = NashornCallSiteDescriptor.getOperand(desc); 205 createProperty(propertyName); 206 return super.lookup(desc, request); 207 } 208 209 @Override 210 protected Object invokeNoSuchProperty(final Object key, final boolean isScope, final int programPoint) { 211 if (!(key instanceof String)) { 212 return super.invokeNoSuchProperty(key, isScope, programPoint); 213 } 214 final Object retval = createProperty((String) key); 215 if (isValid(programPoint)) { 216 throw new UnwarrantedOptimismException(retval, programPoint); 217 } 218 return retval; 219 } 220 221 @Override 222 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { 223 return noSuchProperty(desc, request); 224 } 225 226 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 227 return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types)); 228 } 229 230 private Object createProperty(final String propertyName) { 231 final String fullName = name.isEmpty() ? propertyName : name + "." + propertyName; 232 final Context context = Context.getContextTrusted(); 233 234 Class<?> javaClass = null; 235 try { 236 javaClass = context.findClass(fullName); 237 } catch (final NoClassDefFoundError | ClassNotFoundException e) { 238 //ignored 239 } 240 241 // Check for explicit constructor signature use 242 // Example: new (java.awt["Color(int, int,int)"])(2, 3, 4); 243 final int openBrace = propertyName.indexOf('('); 244 final int closeBrace = propertyName.lastIndexOf(')'); 245 if (openBrace != -1 || closeBrace != -1) { 246 final int lastChar = propertyName.length() - 1; 247 if (openBrace == -1 || closeBrace != lastChar) { 248 throw typeError("improper.constructor.signature", propertyName); 249 } 250 251 // get the class name and try to load it 252 final String className = name + "." + propertyName.substring(0, openBrace); 253 try { 254 javaClass = context.findClass(className); 255 } catch (final NoClassDefFoundError | ClassNotFoundException e) { 256 throw typeError(e, "no.such.java.class", className); 257 } 258 259 // try to find a matching constructor 260 final Object constructor = BeansLinker.getConstructorMethod( 261 javaClass, propertyName.substring(openBrace + 1, lastChar)); 262 if (constructor != null) { 263 set(propertyName, constructor, 0); 264 return constructor; 265 } 266 // we didn't find a matching constructor! 267 throw typeError("no.such.java.constructor", propertyName); 268 } 269 270 final Object propertyValue; 271 if (javaClass == null) { 272 propertyValue = new NativeJavaPackage(fullName, getProto()); 273 } else { 274 propertyValue = StaticClass.forClass(javaClass); 275 } 276 277 set(propertyName, propertyValue, 0); 278 return propertyValue; 279 } 280} 281