
com.oracle.truffle.api.staticobject.ArrayBasedShapeGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of truffle-api Show documentation
Show all versions of truffle-api Show documentation
Truffle is a multi-language framework for executing dynamic languages
that achieves high performance when combined with Graal.
/*
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.api.staticobject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleOptions;
import com.oracle.truffle.api.impl.asm.AnnotationVisitor;
import com.oracle.truffle.api.impl.asm.ClassVisitor;
import com.oracle.truffle.api.impl.asm.ClassWriter;
import com.oracle.truffle.api.impl.asm.Label;
import com.oracle.truffle.api.impl.asm.MethodVisitor;
import com.oracle.truffle.api.impl.asm.Opcodes;
import com.oracle.truffle.api.impl.asm.Type;
import org.graalvm.collections.Pair;
import org.graalvm.nativeimage.ImageInfo;
import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_FINAL;
import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_PRIVATE;
import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_PUBLIC;
import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_STATIC;
import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_SUPER;
import static com.oracle.truffle.api.impl.asm.Opcodes.ACC_SYNTHETIC;
import static com.oracle.truffle.api.impl.asm.Opcodes.ACONST_NULL;
import static com.oracle.truffle.api.impl.asm.Opcodes.ALOAD;
import static com.oracle.truffle.api.impl.asm.Opcodes.ANEWARRAY;
import static com.oracle.truffle.api.impl.asm.Opcodes.ARETURN;
import static com.oracle.truffle.api.impl.asm.Opcodes.ASTORE;
import static com.oracle.truffle.api.impl.asm.Opcodes.ATHROW;
import static com.oracle.truffle.api.impl.asm.Opcodes.CHECKCAST;
import static com.oracle.truffle.api.impl.asm.Opcodes.DOUBLE;
import static com.oracle.truffle.api.impl.asm.Opcodes.DUP;
import static com.oracle.truffle.api.impl.asm.Opcodes.FLOAT;
import static com.oracle.truffle.api.impl.asm.Opcodes.F_FULL;
import static com.oracle.truffle.api.impl.asm.Opcodes.GETFIELD;
import static com.oracle.truffle.api.impl.asm.Opcodes.GETSTATIC;
import static com.oracle.truffle.api.impl.asm.Opcodes.GOTO;
import static com.oracle.truffle.api.impl.asm.Opcodes.IFEQ;
import static com.oracle.truffle.api.impl.asm.Opcodes.IFLE;
import static com.oracle.truffle.api.impl.asm.Opcodes.IFNE;
import static com.oracle.truffle.api.impl.asm.Opcodes.IFNONNULL;
import static com.oracle.truffle.api.impl.asm.Opcodes.IFNULL;
import static com.oracle.truffle.api.impl.asm.Opcodes.ILOAD;
import static com.oracle.truffle.api.impl.asm.Opcodes.INTEGER;
import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKESPECIAL;
import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKESTATIC;
import static com.oracle.truffle.api.impl.asm.Opcodes.INVOKEVIRTUAL;
import static com.oracle.truffle.api.impl.asm.Opcodes.LONG;
import static com.oracle.truffle.api.impl.asm.Opcodes.NEW;
import static com.oracle.truffle.api.impl.asm.Opcodes.NEWARRAY;
import static com.oracle.truffle.api.impl.asm.Opcodes.POP;
import static com.oracle.truffle.api.impl.asm.Opcodes.PUTFIELD;
import static com.oracle.truffle.api.impl.asm.Opcodes.RETURN;
import static com.oracle.truffle.api.impl.asm.Opcodes.T_BYTE;
import static com.oracle.truffle.api.impl.asm.Opcodes.V1_8;
final class ArrayBasedShapeGenerator extends ShapeGenerator {
private static final ConcurrentHashMap, Class>>, Object> generatorCache = TruffleOptions.AOT ? new ConcurrentHashMap<>() : null;
private static final String STATIC_SHAPE_INTERNAL_NAME = Type.getInternalName(ArrayBasedStaticShape.class);
private static final String STATIC_SHAPE_DESCRIPTOR = Type.getDescriptor(ArrayBasedStaticShape.class);
private final Class> generatedStorageClass;
private final Class extends T> generatedFactoryClass;
@CompilationFinal private int byteArrayOffset;
@CompilationFinal private int objectArrayOffset;
@CompilationFinal private int shapeOffset;
private ArrayBasedShapeGenerator(Class> generatedStorageClass, Class extends T> generatedFactoryClass) {
this(
generatedStorageClass,
generatedFactoryClass,
getObjectFieldOffset(generatedStorageClass, "primitive"),
getObjectFieldOffset(generatedStorageClass, "object"),
getObjectFieldOffset(generatedStorageClass, "shape"));
}
private ArrayBasedShapeGenerator(Class> generatedStorageClass, Class extends T> generatedFactoryClass, int byteArrayOffset, int objectArrayOffset, int shapeOffset) {
this.generatedStorageClass = generatedStorageClass;
this.generatedFactoryClass = generatedFactoryClass;
this.byteArrayOffset = byteArrayOffset;
this.objectArrayOffset = objectArrayOffset;
this.shapeOffset = shapeOffset;
}
int getByteArrayOffset() {
return byteArrayOffset;
}
int getObjectArrayOffset() {
return objectArrayOffset;
}
int getShapeOffset() {
return shapeOffset;
}
// Invoked also from TruffleBaseFeature.StaticObjectSupport
@SuppressWarnings("unchecked")
static ArrayBasedShapeGenerator getShapeGenerator(TruffleLanguage> language, GeneratorClassLoaders gcls, Class> storageSuperClass, Class storageFactoryInterface,
String storageClassName) {
ConcurrentHashMap, Class>>, Object> cache;
if (TruffleOptions.AOT) {
cache = generatorCache;
} else {
cache = SomAccessor.ENGINE.getGeneratorCache(SomAccessor.LANGUAGE.getPolyglotLanguageInstance(language));
}
Pair, Class>> pair = Pair.create(storageSuperClass, storageFactoryInterface);
ArrayBasedShapeGenerator sg = (ArrayBasedShapeGenerator) cache.get(pair);
if (sg == null) {
if (ImageInfo.inImageRuntimeCode()) {
throw new IllegalStateException("This code should not be executed at Native Image run time. Please report this issue");
}
Class> generatedStorageClass = generateStorage(gcls, storageSuperClass, storageClassName);
Class extends T> generatedFactoryClass = generateFactory(gcls, generatedStorageClass, storageFactoryInterface);
sg = new ArrayBasedShapeGenerator<>(generatedStorageClass, generatedFactoryClass);
ArrayBasedShapeGenerator prevSg = (ArrayBasedShapeGenerator) cache.putIfAbsent(pair, sg);
if (prevSg != null) {
sg = prevSg;
}
}
return sg;
}
@SuppressWarnings("deprecation"/* JDK-8277863 */)
private static int getObjectFieldOffset(Class> c, String fieldName) {
try {
return Math.toIntExact(UNSAFE.objectFieldOffset(c.getField(fieldName)));
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
@Override
StaticShape generateShape(StaticShape parentShape, Map staticProperties, boolean safetyChecks, String storageClassName) {
return ArrayBasedStaticShape.create(this, generatedStorageClass, generatedFactoryClass, (ArrayBasedStaticShape) parentShape, staticProperties.values(), safetyChecks);
}
// Invoked from TruffleBaseFeature$StaticObjectSupport
void patchOffsets(int nativeByteArrayOffset, int nativeObjectArrayOffset, int nativeShapeOffset) {
assert TruffleOptions.AOT;
CompilerAsserts.neverPartOfCompilation();
byteArrayOffset = nativeByteArrayOffset;
objectArrayOffset = nativeObjectArrayOffset;
shapeOffset = nativeShapeOffset;
}
private static String getStorageConstructorDescriptor(Constructor> superConstructor) {
StringBuilder sb = new StringBuilder();
sb.append('(');
for (Class> parameter : superConstructor.getParameterTypes()) {
sb.append(Type.getDescriptor(parameter));
}
sb.append(STATIC_SHAPE_DESCRIPTOR); // ArrayBasedStaticShape shape
sb.append("[B"); // byte[] primitive
sb.append("[Ljava/lang/Object;"); // Object[] object
return sb.append(")V").toString();
}
private static void addStorageConstructors(ClassVisitor cv, String storageName, Class> storageSuperClass, String storageSuperName) {
for (Constructor> superConstructor : storageSuperClass.getDeclaredConstructors()) {
String storageConstructorDescriptor = getStorageConstructorDescriptor(superConstructor);
String superConstructorDescriptor = Type.getConstructorDescriptor(superConstructor);
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", storageConstructorDescriptor, null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
int maxStack = 1; // this
int maxLocals = 1; // ALOAD_0
Class>[] constructorParameters = superConstructor.getParameterTypes();
for (Class> constructorParameter : constructorParameters) {
Type parameterType = Type.getType(constructorParameter);
int loadOpcode = parameterType.getOpcode(ILOAD);
mv.visitVarInsn(loadOpcode, maxLocals);
int parameterSize = parameterType.getSize();
maxStack += parameterSize;
maxLocals += parameterSize;
}
mv.visitMethodInsn(INVOKESPECIAL, storageSuperName, "", superConstructorDescriptor, false);
// this.shape = shape;
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, maxLocals++);
mv.visitFieldInsn(PUTFIELD, storageName, "shape", STATIC_SHAPE_DESCRIPTOR);
// this.primitive = primitive;
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, maxLocals++);
mv.visitFieldInsn(PUTFIELD, storageName, "primitive", "[B");
// this.object = object;
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, maxLocals++);
mv.visitFieldInsn(PUTFIELD, storageName, "object", "[Ljava/lang/Object;");
mv.visitInsn(RETURN);
mv.visitMaxs(Math.max(maxStack, 2), maxLocals);
mv.visitEnd();
}
}
private static Method getCloneMethod(Class> storageSuperClass) {
for (Class> clazz = storageSuperClass; clazz != null; clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredMethod("clone");
} catch (NoSuchMethodException e) {
// Swallow the error, check the super class
}
}
throw new RuntimeException("Should not reach here");
}
private static String[] getCloneMethodExceptions(Method cloneMethod) {
return Arrays.stream(cloneMethod.getExceptionTypes()).map(c -> Type.getInternalName(c)).toArray(String[]::new);
}
private static void addCloneMethod(Class> storageSuperClass, ClassVisitor cv, String className) {
// Prepare array of frame locals for jumps
Object[] frameLocals = new Object[]{className, className};
Method superCloneMethod = getCloneMethod(storageSuperClass);
String superCloneMethodDescriptor = Type.getMethodDescriptor(superCloneMethod);
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "clone", superCloneMethodDescriptor, null, getCloneMethodExceptions(superCloneMethod));
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(storageSuperClass), "clone", superCloneMethodDescriptor, false);
mv.visitTypeInsn(CHECKCAST, className);
mv.visitVarInsn(ASTORE, 1);
// clone.primitive = (primitive == null ? null : (byte[]) primitive.clone());
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "primitive", "[B");
Label lHasPrimitives = new Label();
mv.visitJumpInsn(IFNONNULL, lHasPrimitives);
mv.visitInsn(ACONST_NULL);
Label lSetPrimitive = new Label();
mv.visitJumpInsn(GOTO, lSetPrimitive);
mv.visitLabel(lHasPrimitives);
mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[]{className});
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "primitive", "[B");
mv.visitMethodInsn(INVOKEVIRTUAL, "[B", "clone", "()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, "[B");
mv.visitTypeInsn(CHECKCAST, "[B");
mv.visitLabel(lSetPrimitive);
mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[]{className, "[B"});
mv.visitFieldInsn(PUTFIELD, className, "primitive", "[B");
// clone.object = (object == null ? null : (Object[]) object.clone());
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;");
Label lHasObjects = new Label();
mv.visitJumpInsn(IFNONNULL, lHasObjects);
mv.visitInsn(ACONST_NULL);
Label lSetObject = new Label();
mv.visitJumpInsn(GOTO, lSetObject);
mv.visitLabel(lHasObjects);
mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 1, new Object[]{className});
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "object", "[Ljava/lang/Object;");
mv.visitMethodInsn(INVOKEVIRTUAL, "[Ljava/lang/Object;", "clone", "()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
mv.visitLabel(lSetObject);
mv.visitFrame(Opcodes.F_FULL, 2, frameLocals, 2, new Object[]{className, "[Ljava/lang/Object;"});
mv.visitFieldInsn(PUTFIELD, className, "object", "[Ljava/lang/Object;");
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
private static void addFactoryFields(ClassVisitor cv) {
cv.visitField(ACC_PUBLIC | ACC_STATIC, "replacements", "Ljava/util/concurrent/ConcurrentHashMap;",
"Ljava/util/concurrent/ConcurrentHashMap;", null).visitEnd();
cv.visitField(ACC_PUBLIC | ACC_FINAL, "shape", STATIC_SHAPE_DESCRIPTOR, null, null).visitEnd();
cv.visitField(ACC_PUBLIC | ACC_FINAL, "primitiveArraySize", "I", null, null).visitEnd();
cv.visitField(ACC_PUBLIC | ACC_FINAL, "objectArraySize", "I", null, null).visitEnd();
cv.visitField(ACC_PUBLIC | ACC_FINAL, "doRegister", "Z", null, null).visitEnd();
}
private static void addFactoryConstructor(ClassVisitor cv, String factoryName) {
/**
* Example:
*
* Interface provided by the user:
*
*
* public interface StaticObjectFactory {
* StaticObject create(Klass klass);
* }
*
*
* Generated factory fields and constructor:
*
*
* public final class GeneratedStaticObject$$1$$Factory implements ArrayBasedFactory, StaticObjectFactory {
* static final ConcurrentHashMap
*/
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "", "(" + STATIC_SHAPE_DESCRIPTOR + "IIZ)V", null, null);
mv.visitCode();
/**
*
* this.shape = shape;
* this.primitiveArraySize = primitiveArraySize;
* this.objectArraySize = objectArraySize;
* this.doRegister = doRegister;
*
*/
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, factoryName, "shape", STATIC_SHAPE_DESCRIPTOR);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 2);
mv.visitFieldInsn(PUTFIELD, factoryName, "primitiveArraySize", "I");
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 3);
mv.visitFieldInsn(PUTFIELD, factoryName, "objectArraySize", "I");
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 4);
mv.visitFieldInsn(PUTFIELD, factoryName, "doRegister", "Z");
/**
*
* if (doRegister) {
* register(this);
* }
*
*/
mv.visitVarInsn(ILOAD, 4);
Label doNotRegister = new Label();
mv.visitJumpInsn(IFEQ, doNotRegister);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, factoryName, "register", "(Ljava/lang/Object;)V", false);
mv.visitLabel(doNotRegister);
mv.visitFrame(Opcodes.F_FULL, 5, new Object[]{factoryName, STATIC_SHAPE_INTERNAL_NAME, Opcodes.INTEGER, Opcodes.INTEGER, Opcodes.INTEGER}, 0, new Object[]{});
/**
* Implicit return statement
*/
mv.visitInsn(RETURN);
mv.visitMaxs(2, 5);
mv.visitEnd();
}
private static Object getFrameLocal(Class> clazz) {
if (clazz.isPrimitive()) {
if (clazz == Boolean.TYPE || clazz == Byte.TYPE || clazz == Character.TYPE || clazz == Integer.TYPE || clazz == Short.TYPE) {
return INTEGER;
} else if (clazz == Double.TYPE) {
return DOUBLE;
} else if (clazz == Float.TYPE) {
return FLOAT;
} else if (clazz == Long.TYPE) {
return LONG;
} else {
throw new AssertionError();
}
} else {
return Type.getInternalName(clazz);
}
}
private static ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy