/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
/**
* This class represents constant names of variables, methods and fields in
* the compiler
*/
public enum CompilerConstants {
/** the __FILE__ variable */
__FILE__,
/** the __DIR__ variable */
__DIR__,
/** the __LINE__ variable */
__LINE__,
/** constructor name */
INIT("<init>"),
/** static initializer name */
CLINIT("<clinit>"),
/** eval name */
EVAL("eval"),
/** source name and class */
SOURCE("source", Source.class),
/** constants name and class */
CONSTANTS("constants", Object[].class),
/** strict mode field name and type */
STRICT_MODE("strictMode", boolean.class),
/** default script name */
DEFAULT_SCRIPT_NAME("Script"),
/** function prefix for anonymous functions */
ANON_FUNCTION_PREFIX("L:"),
/** separator for method names of nested functions */
NESTED_FUNCTION_SEPARATOR("#"),
/** separator for making method names unique by appending numeric ids */
ID_FUNCTION_SEPARATOR("-"),
/** method name for Java method that is the program entry point */
PROGRAM(":program"),
/** method name for Java method that creates the script function for the program */
CREATE_PROGRAM_FUNCTION(":createProgramFunction"),
/**
* "this" name symbol for a parameter representing ECMAScript "this" in static methods that are compiled
* representations of ECMAScript functions. It is not assigned a slot, as its position in the method signature is
* dependent on other factors (most notably, callee can precede it).
*/
THIS("this", Object.class),
/** this debugger symbol */
THIS_DEBUGGER(":this"),
/** scope name, type and slot */
SCOPE(":scope", ScriptObject.class, 2),
/** the return value variable name were intermediate results are stored for scripts */
RETURN(":return"),
/** the callee value variable when necessary */
CALLEE(":callee", ScriptFunction.class),
/** the varargs variable when necessary */
VARARGS(":varargs", Object[].class),
/** the arguments variable (visible to function body). Initially set to ARGUMENTS, but can be reassigned by code in
* the function body.*/
ARGUMENTS_VAR("arguments", Object.class),
/** the internal arguments object, when necessary (not visible to scripts, can't be reassigned). */
ARGUMENTS(":arguments", ScriptObject.class),
/** prefix for apply-to-call exploded arguments */
EXPLODED_ARGUMENT_PREFIX(":xarg"),
/** prefix for iterators for for (x in ...) */
ITERATOR_PREFIX(":i", Iterator.class),
/** prefix for tag variable used for switch evaluation */
SWITCH_TAG_PREFIX(":s"),
/** prefix for JVM exceptions */
EXCEPTION_PREFIX(":e", Throwable.class),
/** prefix for quick slots generated in Store */
QUICK_PREFIX(":q"),
/** prefix for temporary variables */
TEMP_PREFIX(":t"),
/** prefix for literals */
LITERAL_PREFIX(":l"),
/** prefix for regexps */
REGEX_PREFIX(":r"),
/** "this" used in non-static Java methods; always in slot 0 */
JAVA_THIS(null, 0),
/** Map parameter in scope object constructors; always in slot 1 */
INIT_MAP(null, 1),
/** Parent scope parameter in scope object constructors; always in slot 2 */
INIT_SCOPE(null, 2),
/** Arguments parameter in scope object constructors; in slot 3 when present */
INIT_ARGUMENTS(null, 3),
/** prefix for all ScriptObject subclasses with dual object/primitive fields, see {@link ObjectClassGenerator} */
JS_OBJECT_DUAL_FIELD_PREFIX("JD"),
/** prefix for all ScriptObject subclasses with object fields only, see {@link ObjectClassGenerator} */
JS_OBJECT_SINGLE_FIELD_PREFIX("JO"),
/** name for allocate method in JO objects */
ALLOCATE("allocate"),
/** prefix for split methods, @see Splitter */
SPLIT_PREFIX(":split"),
/** prefix for split array method and slot */
SPLIT_ARRAY_ARG(":split_array", 3),
/** get string from constant pool */
GET_STRING(":getString"),
/** get map */
GET_MAP(":getMap"),
/** set map */
SET_MAP(":setMap"),
/** get array prefix */
GET_ARRAY_PREFIX(":get"),
/** get array suffix */
GET_ARRAY_SUFFIX("$array");
/** To save memory - intern the compiler constant symbol names, as they are frequently reused */
static {
for (final CompilerConstants c : values()) {
final String symbolName = c.symbolName();
if (symbolName != null) {
symbolName.intern();
}
}
}
private static Set<String> symbolNames;
/**
* Prefix used for internal methods generated in script classes.
*/
private static final String INTERNAL_METHOD_PREFIX = ":";
private final String symbolName;
private final Class<?> type;
private final int slot;
private CompilerConstants() {
this.symbolName = name();
this.type = null;
this.slot = -1;
}
private CompilerConstants(final String symbolName) {
this(symbolName, -1);
}
private CompilerConstants(final String symbolName, final int slot) {
this(symbolName, null, slot);
}
private CompilerConstants(final String symbolName, final Class<?> type) {
this(symbolName, type, -1);
}
private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
this.symbolName = symbolName;
this.type = type;
this.slot = slot;
}
/**
* Check whether a name is that of a reserved compiler constant
* @param name name
* @return true if compiler constant name
*/
public static boolean isCompilerConstant(final String name) {
ensureSymbolNames();
return symbolNames.contains(name);
}
private static void ensureSymbolNames() {
if(symbolNames == null) {
symbolNames = new HashSet<>();
for(final CompilerConstants cc: CompilerConstants.values()) {
symbolNames.add(cc.symbolName);
}
}
}
/**
* Return the tag for this compile constant. Deliberately avoiding "name" here
* not to conflate with enum implementation. This is the master string for the
* constant - every constant has one.
*
* @return the tag
*/
public final String symbolName() {
return symbolName;
}
/**
* Return the type for this compile constant
*
* @return type for this constant's instances, or null if N/A
*/
public final Class<?> type() {
return type;
}
/**
* Return the slot for this compile constant
*
* @return byte code slot where constant is stored or -1 if N/A
*/
public final int slot() {
return slot;
}
/**
* Return a descriptor for this compile constant. Only relevant if it has
* a type
*
* @return descriptor the descriptor
*/
public final String descriptor() {
assert type != null : " asking for descriptor of typeless constant";
return typeDescriptor(type);
}
/**
* Get the internal class name for a type
*
* @param type a type
* @return the internal name for this type
*/
public static String className(final Class<?> type) {
return Type.getInternalName(type);
}
/**
* Get the method descriptor for a given method type collection
*
* @param rtype return type
* @param ptypes parameter types
*
* @return internal descriptor for this method
*/
public static String methodDescriptor(final Class<?> rtype, final Class<?>... ptypes) {
return Type.getMethodDescriptor(rtype, ptypes);
}
/**
* Get the type descriptor for a type
*
* @param clazz a type
*
* @return the internal descriptor for this type
*/
public static String typeDescriptor(final Class<?> clazz) {
return Type.typeFor(clazz).getDescriptor();
}
/**
* Create a call representing a void constructor for a given type. Don't
* attempt to look this up at compile time
*
* @param clazz the class
*
* @return Call representing void constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz) {
return specialCallNoLookup(clazz, INIT.symbolName(), void.class);
}
/**
* Create a call representing a constructor for a given type. Don't
* attempt to look this up at compile time
*
* @param className the type class name
* @param ptypes the parameter types for the constructor
*
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final String className, final Class<?>... ptypes) {
return specialCallNoLookup(className, INIT.symbolName(), methodDescriptor(void.class, ptypes));
}
/**
* Create a call representing a constructor for a given type. Don't
* attempt to look this up at compile time
*
* @param clazz the class name
* @param ptypes the parameter types for the constructor
*
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) {
return specialCallNoLookup(clazz, INIT.symbolName(), void.class, ptypes);
}
/**
* Create a call representing an invokespecial to a given method. Don't
* attempt to look this up at compile time
*
* @param className the class name
* @param name the method name
* @param desc the descriptor
*
* @return Call representing specified invokespecial call
*/
public static Call specialCallNoLookup(final String className, final String name, final String desc) {
return new Call(null, className, name, desc) {
@Override
MethodEmitter invoke(final MethodEmitter method) {
return method.invokespecial(className, name, descriptor);
}
@Override
public void invoke(final MethodVisitor mv) {
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, className, name, desc, false);
}
};
}
/**
* Create a call representing an invokespecial to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokespecial call
*/
public static Call specialCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return specialCallNoLookup(className(clazz), name, methodDescriptor(rtype, ptypes));
}
/**
* Create a call representing an invokestatic to a given method. Don't
* attempt to look this up at compile time
*
* @param className the class name
* @param name the method name
* @param desc the descriptor
*
* @return Call representing specified invokestatic call
*/
public static Call staticCallNoLookup(final String className, final String name, final String desc) {
return new Call(null, className, name, desc) {
@Override
MethodEmitter invoke(final MethodEmitter method) {
return method.invokestatic(className, name, descriptor);
}
@Override
public void invoke(final MethodVisitor mv) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, name, desc, false);
}
};
}
/**
* Create a call representing an invokestatic to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokestatic call
*/
public static Call staticCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return staticCallNoLookup(className(clazz), name, methodDescriptor(rtype, ptypes));
}
/**
* Create a call representing an invokevirtual to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokevirtual call
*/
public static Call virtualCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(null, className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
MethodEmitter invoke(final MethodEmitter method) {
return method.invokevirtual(className, name, descriptor);
}
@Override
public void invoke(final MethodVisitor mv) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, name, descriptor, false);
}
};
}
/**
* Create a call representing an invokeinterface to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokeinterface call
*/
public static Call interfaceCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(null, className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
MethodEmitter invoke(final MethodEmitter method) {
return method.invokeinterface(className, name, descriptor);
}
@Override
public void invoke(final MethodVisitor mv) {
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, className, name, descriptor, true);
}
};
}
/**
* Create a FieldAccess representing a virtual field, that can be subject to put
* or get operations
*
* @param className name of the class where the field is a member
* @param name name of the field
* @param desc type descriptor of the field
*
* @return a field access object giving access code generation method for the virtual field
*/
public static FieldAccess virtualField(final String className, final String name, final String desc) {
return new FieldAccess(className, name, desc) {
@Override
public MethodEmitter get(final MethodEmitter method) {
return method.getField(className, name, descriptor);
}
@Override
public void put(final MethodEmitter method) {
method.putField(className, name, descriptor);
}
};
}
/**
* Create a FieldAccess representing a virtual field, that can be subject to put
* or get operations
*
* @param clazz class where the field is a member
* @param name name of the field
* @param type type of the field
*
* @return a field access object giving access code generation method for the virtual field
*/
public static FieldAccess virtualField(final Class<?> clazz, final String name, final Class<?> type) {
return virtualField(className(clazz), name, typeDescriptor(type));
}
/**
* Create a FieldAccess representing a static field, that can be subject to put
* or get operations
*
* @param className name of the class where the field is a member
* @param name name of the field
* @param desc type descriptor of the field
*
* @return a field access object giving access code generation method for the static field
*/
public static FieldAccess staticField(final String className, final String name, final String desc) {
return new FieldAccess(className, name, desc) {
@Override
public MethodEmitter get(final MethodEmitter method) {
return method.getStatic(className, name, descriptor);
}
@Override
public void put(final MethodEmitter method) {
method.putStatic(className, name, descriptor);
}
};
}
/**
* Create a FieldAccess representing a static field, that can be subject to put
* or get operations
*
* @param clazz class where the field is a member
* @param name name of the field
* @param type type of the field
*
* @return a field access object giving access code generation method for the virtual field
*/
public static FieldAccess staticField(final Class<?> clazz, final String name, final Class<?> type) {
return staticField(className(clazz), name, typeDescriptor(type));
}
/**
* Create a static call, given an explicit lookup, looking up the method handle for it at the same time
*
* @param lookup the lookup
* @param clazz the class
* @param name the name of the method
* @param rtype the return type
* @param ptypes the parameter types
*
* @return the call object representing the static call
*/
public static Call staticCall(final MethodHandles.Lookup lookup, final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(MH.findStatic(lookup, clazz, name, MH.type(rtype, ptypes)), className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
MethodEmitter invoke(final MethodEmitter method) {
return method.invokestatic(className, name, descriptor);
}
@Override
public void invoke(final MethodVisitor mv) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, name, descriptor, false);
}
};
}
/**
* Create a virtual call, given an explicit lookup, looking up the method handle for it at the same time
*
* @param lookup the lookup
* @param clazz the class
* @param name the name of the method
* @param rtype the return type
* @param ptypes the parameter types
*
* @return the call object representing the virtual call
*/
public static Call virtualCall(final MethodHandles.Lookup lookup, final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(MH.findVirtual(lookup, clazz, name, MH.type(rtype, ptypes)), className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
MethodEmitter invoke(final MethodEmitter method) {
return method.invokevirtual(className, name, descriptor);
}
@Override
public void invoke(final MethodVisitor mv) {
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, name, descriptor, false);
}
};
}
/**
* Create a special call, given an explicit lookup, looking up the method handle for it at the same time.
* clazz is used as this class
*
* @param lookup the lookup
* @param clazz the class
* @param name the name of the method
* @param rtype the return type
* @param ptypes the parameter types
*
* @return the call object representing the virtual call
*/
public static Call specialCall(final MethodHandles.Lookup lookup, final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(MH.findSpecial(lookup, clazz, name, MH.type(rtype, ptypes), clazz), className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
MethodEmitter invoke(final MethodEmitter method) {
return method.invokespecial(className, name, descriptor);
}
@Override
public void invoke(final MethodVisitor mv) {
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, className, name, descriptor, false);
}
};
}
/**
* Returns true if the passed string looks like a method name of an internally generated Nashorn method. Basically,
* if it starts with a colon character {@code :} but is not the name of the program method {@code :program}.
* Program function is not considered internal as we want it to show up in exception stack traces.
* @param methodName the name of a method
* @return true if it looks like an internal Nashorn method name.
* @throws NullPointerException if passed null
*/
public static boolean isInternalMethodName(final String methodName) {
return methodName.startsWith(INTERNAL_METHOD_PREFIX) && !methodName.equals(PROGRAM.symbolName);
}
/**
* Private class representing an access. This can generate code into a method code or
* a field access.
*/
private abstract static class Access {
protected final MethodHandle methodHandle;
protected final String className;
protected final String name;
protected final String descriptor;
/**
* Constructor
*
* @param methodHandle methodHandle or null if none
* @param className class name for access
* @param name field or method name for access
* @param descriptor descriptor for access field or method
*/
protected Access(final MethodHandle methodHandle, final String className, final String name, final String descriptor) {
this.methodHandle = methodHandle;
this.className = className;
this.name = name;
this.descriptor = descriptor;
}
/**
* Get the method handle, or null if access hasn't been looked up
*
* @return method handle
*/
public MethodHandle methodHandle() {
return methodHandle;
}
/**
* Get the class name of the access
*
* @return the class name
*/
public String className() {
return className;
}
/**
* Get the field name or method name of the access
*
* @return the name
*/
public String name() {
/**代码未完, 请加载全部代码(NowJava.com).**/