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