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.codegen.types;
  27 
  28 import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
  29 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
  30 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
  31 import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
  32 import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
  33 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
  34 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
  35 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
  36 
  37 import java.lang.invoke.MethodHandle;
  38 import jdk.internal.org.objectweb.asm.Handle;
  39 import jdk.internal.org.objectweb.asm.MethodVisitor;
  40 import jdk.nashorn.internal.codegen.CompilerConstants;
  41 import jdk.nashorn.internal.runtime.JSType;
  42 import jdk.nashorn.internal.runtime.ScriptRuntime;
  43 import jdk.nashorn.internal.runtime.Undefined;
  44 
  45 /**
  46  * Type class: OBJECT This is the object type, used for all object types. It can
  47  * contain a class that is a more specialized object
  48  */
  49 class ObjectType extends Type {
  50     private static final long serialVersionUID = 1L;
  51 
  52     protected ObjectType() {
  53         this(Object.class);
  54     }
  55 
  56     protected ObjectType(final Class<?> clazz) {
  57         super("object",
  58                 clazz,
  59                 clazz == Object.class ? Type.MAX_WEIGHT : 10,
  60                 1);
  61     }
  62 
  63     @Override
  64     public String toString() {
  65         return "object" + (getTypeClass() != Object.class ? "<type=" + getTypeClass().getSimpleName() + '>' : "");
  66     }
  67 
  68     @Override
  69     public String getShortDescriptor() {
  70         return getTypeClass() == Object.class ? "Object" : getTypeClass().getSimpleName();
  71     }
  72 
  73     @Override
  74     public Type add(final MethodVisitor method, final int programPoint) {
  75         invokestatic(method, ScriptRuntime.ADD);
  76         return Type.OBJECT;
  77     }
  78 
  79     @Override
  80     public Type load(final MethodVisitor method, final int slot) {
  81         assert slot != -1;
  82         method.visitVarInsn(ALOAD, slot);
  83         return this;
  84     }
  85 
  86     @Override
  87     public void store(final MethodVisitor method, final int slot) {
  88         assert slot != -1;
  89         method.visitVarInsn(ASTORE, slot);
  90     }
  91 
  92     @Override
  93     public Type loadUndefined(final MethodVisitor method) {
  94         method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "UNDEFINED", typeDescriptor(Undefined.class));
  95         return UNDEFINED;
  96     }
  97 
  98     @Override
  99     public Type loadForcedInitializer(final MethodVisitor method) {
 100         method.visitInsn(ACONST_NULL);
 101         // TODO: do we need a special type for null, e.g. Type.NULL? It should be assignable to any other object type
 102         // without a checkast in convert.
 103         return OBJECT;
 104     }
 105 
 106     @Override
 107     public Type loadEmpty(final MethodVisitor method) {
 108         method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "EMPTY", typeDescriptor(Undefined.class));
 109         return UNDEFINED;
 110     }
 111 
 112     @Override
 113     public Type ldc(final MethodVisitor method, final Object c) {
 114         if (c == null) {
 115             method.visitInsn(ACONST_NULL);
 116         } else if (c instanceof Undefined) {
 117             return loadUndefined(method);
 118         } else if (c instanceof String) {
 119             method.visitLdcInsn(c);
 120             return STRING;
 121         } else if (c instanceof Handle) {
 122             method.visitLdcInsn(c);
 123             return Type.typeFor(MethodHandle.class);
 124         } else {
 125             throw new UnsupportedOperationException("implementation missing for class " + c.getClass() + " value=" + c);
 126         }
 127 
 128         return Type.OBJECT;
 129     }
 130 
 131     @Override
 132     public Type convert(final MethodVisitor method, final Type to) {
 133         final boolean toString = to.isString();
 134         if (!toString) {
 135             if (to.isArray()) {
 136                 final Type elemType = ((ArrayType)to).getElementType();
 137 
 138                 //note that if this an array, things won't work. see {link @ArrayType} subclass.
 139                 //we also have the unpleasant case of NativeArray which looks like an Object, but is
 140                 //an array to the type system. This is treated specially at the known load points
 141 
 142                 if (elemType.isString()) {
 143                     method.visitTypeInsn(CHECKCAST, CompilerConstants.className(String[].class));
 144                 } else if (elemType.isNumber()) {
 145                     method.visitTypeInsn(CHECKCAST, CompilerConstants.className(double[].class));
 146                 } else if (elemType.isLong()) {
 147                     method.visitTypeInsn(CHECKCAST, CompilerConstants.className(long[].class));
 148                 } else if (elemType.isInteger()) {
 149                     method.visitTypeInsn(CHECKCAST, CompilerConstants.className(int[].class));
 150                 } else {
 151                     method.visitTypeInsn(CHECKCAST, CompilerConstants.className(Object[].class));
 152                 }
 153                 return to;
 154             } else if (to.isObject()) {
 155                 final Class<?> toClass = to.getTypeClass();
 156                 if(!toClass.isAssignableFrom(getTypeClass())) {
 157                     method.visitTypeInsn(CHECKCAST, CompilerConstants.className(toClass));
 158                 }
 159                 return to;
 160             }
 161         } else if (isString()) {
 162             return to;
 163         }
 164 
 165         if (to.isInteger()) {
 166             invokestatic(method, JSType.TO_INT32);
 167         } else if (to.isNumber()) {
 168             invokestatic(method, JSType.TO_NUMBER);
 169         } else if (to.isLong()) {
 170             invokestatic(method, JSType.TO_LONG);
 171         } else if (to.isBoolean()) {
 172             invokestatic(method, JSType.TO_BOOLEAN);
 173         } else if (to.isString()) {
 174             invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
 175         } else if (to.isCharSequence()) {
 176             invokestatic(method, JSType.TO_PRIMITIVE_TO_CHARSEQUENCE);
 177         } else {
 178             throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString);
 179         }
 180 
 181         return to;
 182     }
 183 
 184     @Override
 185     public void _return(final MethodVisitor method) {
 186         method.visitInsn(ARETURN);
 187     }
 188 
 189     @Override
 190     public char getBytecodeStackType() {
 191         return 'A';
 192     }
 193 }