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 this.name = name; 89 } 90 91 @Override 92 public String getClassName() { 93 return "JavaPackage"; 94 } 95 96 @Override 97 public boolean equals(final Object other) { 98 if (other instanceof NativeJavaPackage) { 99 return name.equals(((NativeJavaPackage)other).name); 100 } 101 return false; 102 } 103 104 @Override 105 public int hashCode() { 106 return name == null ? 0 : name.hashCode(); 107 } 108 109 /** 110 * Get the full name of the package 111 * @return the name 112 */ 113 public String getName() { 114 return name; 115 } 116 117 @Override 118 public String safeToString() { 119 return toString(); 120 } 121 122 @Override 123 public String toString() { 124 return "[JavaPackage " + name + "]"; 125 } 126 127 @Override 128 public Object getDefaultValue(final Class<?> hint) { 129 if (hint == String.class) { 130 return toString(); 131 } 132 133 return super.getDefaultValue(hint); 134 } 135 136 @Override 137 protected GuardedInvocation findNewMethod(CallSiteDescriptor desc) { 138 return createClassNotFoundInvocation(desc); 139 } 140 141 @Override 142 protected GuardedInvocation findCallMethod(CallSiteDescriptor desc, LinkRequest request) { 143 return createClassNotFoundInvocation(desc); 144 } 145 146 private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) { 147 // If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as 148 // we can assume the user attempted to instantiate a non-existent class. 149 final MethodType type = desc.getMethodType(); 150 return new GuardedInvocation( 151 MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())), 152 type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD); 153 } 154 155 @SuppressWarnings("unused") 156 private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException { 157 throw new ClassNotFoundException(pkg.name); 158 } 159 160 /** 161 * "No such property" call placeholder. 162 * 163 * This can never be called as we override {@link ScriptObject#noSuchProperty}. We do declare it here as it's a signal 164 * to {@link WithObject} that it's worth trying doing a {@code noSuchProperty} on this object. 165 * 166 * @param self self reference 167 * @param name property name 168 * @return never returns 169 */ 170 @Function(attributes = Attribute.NOT_ENUMERABLE) 171 public static Object __noSuchProperty__(final Object self, final Object name) { 172 throw new AssertionError("__noSuchProperty__ placeholder called"); 173 } 174 175 /** 176 * "No such method call" placeholder 177 * 178 * This can never be called as we override {@link ScriptObject#noSuchMethod}. We do declare it here as it's a signal 179 * to {@link WithObject} that it's worth trying doing a noSuchProperty on this object. 180 * 181 * @param self self reference 182 * @param args arguments to method 183 * @return never returns 184 */ 185 @Function(attributes = Attribute.NOT_ENUMERABLE) 186 public static Object __noSuchMethod__(final Object self, final Object... args) { 187 throw new AssertionError("__noSuchMethod__ placeholder called"); 188 } 189 190 /** 191 * Handle creation of new attribute. 192 * @param desc the call site descriptor 193 * @param request the link request 194 * @return Link to be invoked at call site. 195 */ 196 @Override 197 public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) { 198 final String propertyName = desc.getNameToken(2); 199 final String fullName = name.isEmpty() ? propertyName : name + "." + propertyName; 200 201 final Context context = Context.getContextTrusted(); 202 203 Class<?> javaClass = null; 204 try { 205 javaClass = context.findClass(fullName); 206 } catch (final NoClassDefFoundError | ClassNotFoundException e) { 207 //ignored 208 } 209 210 if (javaClass == null) { 211 set(propertyName, new NativeJavaPackage(fullName, getProto()), false); 212 } else { 213 set(propertyName, StaticClass.forClass(javaClass), false); 214 } 215 216 return super.lookup(desc, request); 217 } 218 219 @Override 220 public GuardedInvocation noSuchMethod(final CallSiteDescriptor desc, final LinkRequest request) { 221 return noSuchProperty(desc, request); 222 } 223 224 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 225 return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types)); 226 } 227 }