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