NativeJavaPackage.java revision 1033:c1f651636d9c
150472Speter/* 27130Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 378822Snik * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 450203Srgrimes * 57130Srgrimes * This code is free software; you can redistribute it and/or modify it 639161Sobrien * under the terms of the GNU General Public License version 2 only, as 78571Srgrimes * published by the Free Software Foundation. Oracle designates this 88571Srgrimes * particular file as subject to the "Classpath" exception as provided 97130Srgrimes * by Oracle in the LICENSE file that accompanied this code. 10122402Sharti * 11122402Sharti * This code is distributed in the hope that it will be useful, but WITHOUT 12123051Sru * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13123051Sru * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14123051Sru * version 2 for more details (a copy is included in the LICENSE file that 15123051Sru * accompanied this code). 16123051Sru * 17123051Sru * You should have received a copy of the GNU General Public License version 18123051Sru * 2 along with this work; if not, write to the Free Software Foundation, 19123051Sru * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20123051Sru * 21123051Sru * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2239250Sgibbs * or visit www.oracle.com if you need additional information or have any 2339250Sgibbs * questions. 2439250Sgibbs */ 2539250Sgibbs 26104489Ssampackage jdk.nashorn.internal.runtime; 27104489Ssam 2856583Sn_hibmaimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 2988748Sambriskoimport static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 3088748Sambrisko 31123051Sruimport java.lang.invoke.MethodHandle; 32123051Sruimport java.lang.invoke.MethodHandles; 3377756Sruimport java.lang.invoke.MethodType; 3477756Sruimport jdk.internal.dynalink.CallSiteDescriptor; 35103627Struckmanimport jdk.internal.dynalink.beans.BeansLinker; 36103627Struckmanimport jdk.internal.dynalink.beans.StaticClass; 37105400Stmmimport jdk.internal.dynalink.linker.GuardedInvocation; 38105400Stmmimport jdk.internal.dynalink.linker.LinkRequest; 3960724Speterimport jdk.internal.dynalink.support.Guards; 4060724Speterimport jdk.nashorn.internal.lookup.MethodHandleFactory; 41103627Struckmanimport jdk.nashorn.internal.lookup.MethodHandleFunctionality; 42103627Struckmanimport jdk.nashorn.internal.objects.annotations.Attribute; 4356583Sn_hibmaimport jdk.nashorn.internal.objects.annotations.Function; 4456583Sn_hibma 45116258Sharti/** 46116258Sharti * An object that exposes Java packages and classes as its properties. Packages are exposed as objects that have further 4770811Speter * sub-packages and classes as their properties. Normally, three instances of this class are exposed as built-in objects 4870811Speter * in Nashorn: {@code "Packages"}, {@code "java"}, and {@code "javax"}. Typical usages are: 4956583Sn_hibma * <pre> 5075415Sbp * var list = new java.util.ArrayList() 5188050Sgreen * var sprocket = new Packages.com.acme.Sprocket() 5288050Sgreen * </pre> 5377031Sru * or you can store the type objects in a variable for later reuse: 5477031Sru * <pre> 5577031Sru * var ArrayList = java.util.ArrayList 5677031Sru * var list = new ArrayList 5777162Sru * </pre> 5877162Sru * You can also use {@link jdk.nashorn.internal.objects.NativeJava#type(Object, Object)} to access Java classes. These two statements are mostly 5977223Sru * equivalent: 6077223Sru * <pre> 6177031Sru * var listType1 = java.util.ArrayList 6277031Sru * var listType2 = Java.type("java.util.ArrayList") 6377223Sru * </pre> 6477223Sru * The difference is that {@code Java.type()} will throw an error if the class does not exist, while the first 6577031Sru * expression will return an empty object, as it must treat all non-existent classes as potentially being further 6677031Sru * subpackages. As such, {@code Java.type()} has the potential to catch typos earlier. A further difference is that 6777031Sru * {@code Java.type()} doesn't recognize {@code .} (dot) as the separator between outer class name and inner class name, 6877031Sru * it only recognizes the dollar sign. These are equivalent: 6975461Sru * <pre> 7075461Sru * var ftype1 = java.awt.geom.Arc2D$Float 71123051Sru * var ftype2 = java.awt.geom.Arc2D.Float 72123051Sru * var ftype3 = Java.asType("java.awt.geom.Arc2D$Float") 7377031Sru * var ftype4 = Java.asType("java.awt.geom.Arc2D").Float 7477031Sru * </pre> 7577031Sru */ 7677031Srupublic final class NativeJavaPackage extends ScriptObject { 7775415Sbp private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality(); 78110542Sphk private static final MethodHandle CLASS_NOT_FOUND = findOwnMH("classNotFound", Void.TYPE, NativeJavaPackage.class); 79110542Sphk private static final MethodHandle TYPE_GUARD = Guards.getClassGuard(NativeJavaPackage.class); 8067523Sarchie 8167523Sarchie /** Full name of package (includes path.) */ 8226453Sache private final String name; 8326453Sache 8426453Sache /** 8526453Sache * Public constructor to be accessible from {@link jdk.nashorn.internal.objects.Global} 86120950Snectar * @param name package name 87120950Snectar * @param proto proto 8890804Sgshapiro */ 8990804Sgshapiro public NativeJavaPackage(final String name, final ScriptObject proto) { 9026453Sache super(proto, null); 9193229Sru // defense-in-path, check here for sensitive packages 9293229Sru Context.checkPackageAccess(name); 9326453Sache this.name = name; 9426453Sache } 9526453Sache 96116818Ssam @Override 97116818Ssam public String getClassName() { 9826453Sache return "JavaPackage"; 9926453Sache } 10039271Sphk 10193229Sru @Override 10293229Sru public boolean equals(final Object other) { 10393229Sru if (other instanceof NativeJavaPackage) { 10493229Sru return name.equals(((NativeJavaPackage)other).name); 10593229Sru } 10693229Sru return false; 10793229Sru } 10893229Sru 10939271Sphk @Override 11052419Sjulian public int hashCode() { 111116811Sharti return name == null ? 0 : name.hashCode(); 112116811Sharti } 113107123Sjulian 114107123Sjulian /** 115107123Sjulian * Get the full name of the package 116107123Sjulian * @return the name 11752419Sjulian */ 11826453Sache public String getName() { 11926453Sache return name; 12052905Sjlemon } 12152905Sjlemon 122105376Ssam @Override 123105376Ssam public String safeToString() { 12426453Sache return toString(); 12526453Sache } 12626453Sache 12726453Sache @Override 12867128Sbrian public String toString() { 129121949Sharti return "[JavaPackage " + name + "]"; 130121949Sharti } 131121337Sharti 132121337Sharti @Override 133122211Sharti public Object getDefaultValue(final Class<?> hint) { 134122211Sharti if (hint == String.class) { 13567128Sbrian return toString(); 13652228Sbp } 13752228Sbp 13875375Sbp return super.getDefaultValue(hint); 13975375Sbp } 14026453Sache 14126453Sache @Override 14283653Speter protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) { 14383653Speter return createClassNotFoundInvocation(desc); 14483653Speter } 14583653Speter 1468571Srgrimes @Override 1477130Srgrimes protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 14855097Skris return createClassNotFoundInvocation(desc); 14955097Skris } 15026453Sache 15126453Sache private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) { 15234030Sdufault // If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as 15334030Sdufault // we can assume the user attempted to instantiate a non-existent class. 1548571Srgrimes final MethodType type = desc.getMethodType(); 1557130Srgrimes return new GuardedInvocation( 1568571Srgrimes MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())), 1577130Srgrimes type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD); 1588571Srgrimes } 1597130Srgrimes 1608571Srgrimes @SuppressWarnings("unused") 1617130Srgrimes private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException { 16241230Sjdp throw new ClassNotFoundException(pkg.name); 163101192Srwatson } 164101192Srwatson 165101192Srwatson /** 166101192Srwatson * "No such property" call placeholder. 167107547Srwatson * 168107547Srwatson * This can never be called as we override {@link ScriptObject#noSuchProperty}. We do declare it here as it's a signal 169101192Srwatson * to {@link WithObject} that it's worth trying doing a {@code noSuchProperty} on this object. 170101192Srwatson * 171105875Srwatson * @param self self reference 172105875Srwatson * @param name property name 17341230Sjdp * @return never returns 17426453Sache */ 17526453Sache @Function(attributes = Attribute.NOT_ENUMERABLE) 17626453Sache public static Object __noSuchProperty__(final Object self, final Object name) { 17726453Sache throw new AssertionError("__noSuchProperty__ placeholder called"); 17826453Sache } 17926453Sache 18026453Sache /** 18126453Sache * "No such method call" placeholder 18226453Sache * 18326453Sache * This can never be called as we override {@link ScriptObject#noSuchMethod}. We do declare it here as it's a signal 1847130Srgrimes * 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} 277