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 }