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