diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/constantpool/Resolution.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/constantpool/Resolution.java index 239052f3c1ca..c96eaaa59eb0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/constantpool/Resolution.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/constantpool/Resolution.java @@ -22,29 +22,15 @@ */ package com.oracle.truffle.espresso.constantpool; +import java.util.logging.Level; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.EspressoOptions; -import com.oracle.truffle.espresso.classfile.descriptors.Signatures; -import com.oracle.truffle.espresso.classfile.descriptors.Symbol; -import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Descriptor; -import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Name; -import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Signature; -import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Type; -import com.oracle.truffle.espresso.impl.ClassRegistry; -import com.oracle.truffle.espresso.impl.Field; -import com.oracle.truffle.espresso.impl.Klass; -import com.oracle.truffle.espresso.impl.Member; -import com.oracle.truffle.espresso.impl.Method; -import com.oracle.truffle.espresso.impl.ObjectKlass; -import com.oracle.truffle.espresso.meta.Meta; -import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode; -import com.oracle.truffle.espresso.nodes.methodhandle.MHLinkToNode; import com.oracle.truffle.espresso.classfile.ConstantPool.Tag; -import com.oracle.truffle.espresso.meta.EspressoError; import com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute; import com.oracle.truffle.espresso.classfile.constantpool.ClassConstant; import com.oracle.truffle.espresso.classfile.constantpool.ClassMethodRefConstant; @@ -58,7 +44,23 @@ import com.oracle.truffle.espresso.classfile.constantpool.MethodTypeConstant; import com.oracle.truffle.espresso.classfile.constantpool.Resolvable; import com.oracle.truffle.espresso.classfile.constantpool.StringConstant; +import com.oracle.truffle.espresso.classfile.descriptors.Signatures; +import com.oracle.truffle.espresso.classfile.descriptors.Symbol; +import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Descriptor; +import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Name; +import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Signature; +import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Type; import com.oracle.truffle.espresso.classfile.perf.DebugCounter; +import com.oracle.truffle.espresso.impl.ClassRegistry; +import com.oracle.truffle.espresso.impl.Field; +import com.oracle.truffle.espresso.impl.Klass; +import com.oracle.truffle.espresso.impl.Member; +import com.oracle.truffle.espresso.impl.Method; +import com.oracle.truffle.espresso.impl.ObjectKlass; +import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.meta.Meta; +import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode; +import com.oracle.truffle.espresso.nodes.methodhandle.MHLinkToNode; import com.oracle.truffle.espresso.redefinition.ClassRedefinition; import com.oracle.truffle.espresso.resolver.LinkResolver; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -66,8 +68,6 @@ import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; import com.oracle.truffle.espresso.substitutions.JavaType; -import java.util.logging.Level; - public final class Resolution { static final DebugCounter CLASS_RESOLVE_COUNT = DebugCounter.create("ClassConstant.resolve calls"); static final DebugCounter FIELDREF_RESOLVE_COUNT = DebugCounter.create("FieldRef.resolve calls"); @@ -195,87 +195,32 @@ public static ResolvedClassConstant resolveClassConstant(ClassConstant.WithStrin } } - /** - *

5.4.3.2. Field Resolution

- * - * To resolve an unresolved symbolic reference from D to a field in a class or interface C, the - * symbolic reference to C given by the field reference must first be resolved (§5.4.3.1). - * Therefore, any exception that can be thrown as a result of failure of resolution of a class - * or interface reference can be thrown as a result of failure of field resolution. If the - * reference to C can be successfully resolved, an exception relating to the failure of - * resolution of the field reference itself can be thrown. - * - * When resolving a field reference, field resolution first attempts to look up the referenced - * field in C and its superclasses: - *
    - *
  1. If C declares a field with the name and descriptor specified by the field reference, - * field lookup succeeds. The declared field is the result of the field lookup. - *
  2. Otherwise, field lookup is applied recursively to the direct superinterfaces of the - * specified class or interface C. - *
  3. Otherwise, if C has a superclass S, field lookup is applied recursively to S. - *
  4. Otherwise, field lookup fails. - *
- * - * Then: - * - */ - private static Field lookupField(Klass seed, Symbol name, Symbol type) { - Field f = seed.lookupDeclaredField(name, type); - if (f != null) { - return f; - } - for (Klass i : seed.getSuperInterfaces()) { - f = lookupField(i, name, type); - if (f != null) { - return f; - } - } - if (seed.getSuperKlass() != null) { - return lookupField(seed.getSuperKlass(), name, type); - } - return null; - } - public static Resolvable.ResolvedConstant resolveFieldRefConstant(FieldRefConstant.Indexes thiz, RuntimeConstantPool pool, @SuppressWarnings("unused") int thisIndex, ObjectKlass accessingKlass) { FIELDREF_RESOLVE_COUNT.inc(); Klass holderKlass = getResolvedHolderKlass(thiz, pool, accessingKlass); Symbol name = thiz.getName(pool); Symbol type = thiz.getType(pool); - Field field = lookupField(holderKlass, name, type); - if (field == null) { - ClassRedefinition classRedefinition = pool.getContext().getClassRedefinition(); - if (classRedefinition != null) { - // could be due to ongoing redefinition - classRedefinition.check(); - field = lookupField(holderKlass, name, type); - } - if (field == null) { - Meta meta = pool.getContext().getMeta(); - EspressoException failure = EspressoException.wrap(Meta.initExceptionWithMessage(meta.java_lang_NoSuchFieldError, name.toString()), meta); - Assumption missingFieldAssumption; + Meta meta = pool.getContext().getMeta(); + Field field; + ClassRedefinition classRedefinition = null; + try { + try { + field = LinkResolver.resolveFieldSymbol(meta, accessingKlass, name, type, holderKlass, true, true); + } catch (EspressoException e) { + classRedefinition = pool.getContext().getClassRedefinition(); if (classRedefinition != null) { - missingFieldAssumption = classRedefinition.getMissingFieldAssumption(); + // could be due to ongoing redefinition + classRedefinition.check(); + field = LinkResolver.resolveFieldSymbol(meta, accessingKlass, name, type, holderKlass, true, true); } else { - missingFieldAssumption = Assumption.ALWAYS_VALID; + throw e; } - return new MissingFieldRefConstant(failure, missingFieldAssumption); } + } catch (EspressoException e) { + return new MissingFieldRefConstant(e, classRedefinition == null ? Assumption.ALWAYS_VALID : classRedefinition.getMissingFieldAssumption()); } - memberDoAccessCheck(accessingKlass, holderKlass, field, pool.getContext().getMeta()); - - field.checkLoadingConstraints(accessingKlass.getDefiningClassLoader(), field.getDeclaringKlass().getDefiningClassLoader()); - return new ResolvedFieldRefConstant(field); } @@ -411,7 +356,7 @@ public static Resolvable.ResolvedConstant resolveInterfaceMethodRefConstant(Inte Symbol name = thiz.getName(pool); Symbol signature = thiz.getSignature(pool); - Method method = LinkResolver.resolveSymbol(pool.getContext().getMeta(), accessingKlass, name, signature, holderInterface, true, true, true); + Method method = LinkResolver.resolveMethodSymbol(pool.getContext().getMeta(), accessingKlass, name, signature, holderInterface, true, true, true); return new ResolvedInterfaceMethodRefConstant(method); } @@ -533,7 +478,7 @@ public static Resolvable.ResolvedConstant resolveClassMethodRefConstant(ClassMet Symbol name = thiz.getName(pool); Symbol signature = thiz.getSignature(pool); - Method method = LinkResolver.resolveSymbol(meta, accessingKlass, name, signature, holderKlass, false, true, true); + Method method = LinkResolver.resolveMethodSymbol(meta, accessingKlass, name, signature, holderKlass, false, true, true); if (method.isInvokeIntrinsic()) { MHInvokeGenericNode.MethodHandleInvoker invoker = MHInvokeGenericNode.linkMethod(meta.getLanguage(), meta, accessingKlass, method, name, signature); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java index c637fbeb6553..76d835909ecb 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jni/JniEnv.java @@ -50,7 +50,7 @@ import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.espresso.EspressoLanguage; -import com.oracle.truffle.espresso.ffi.NativeAccess; +import com.oracle.truffle.espresso.classfile.JavaKind; import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence; import com.oracle.truffle.espresso.classfile.descriptors.ModifiedUtf8; import com.oracle.truffle.espresso.classfile.descriptors.Signatures; @@ -59,6 +59,7 @@ import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Signature; import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Type; import com.oracle.truffle.espresso.classfile.descriptors.Validation; +import com.oracle.truffle.espresso.ffi.NativeAccess; import com.oracle.truffle.espresso.ffi.NativeSignature; import com.oracle.truffle.espresso.ffi.NativeType; import com.oracle.truffle.espresso.ffi.Pointer; @@ -70,7 +71,6 @@ import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.meta.EspressoError; -import com.oracle.truffle.espresso.classfile.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.nodes.EspressoRootNode; import com.oracle.truffle.espresso.nodes.bytecodes.ArrayLength; @@ -446,7 +446,7 @@ private WeakHandles methodIds() { if (fieldType != null) { // Lookup only if name and type are known symbols. klass.safeInitialize(); - field = klass.lookupField(fieldName, fieldType); + field = klass.lookupField(fieldName, fieldType, Klass.LookupMode.INSTANCE_ONLY); assert field == null || field.getType().equals(fieldType); } } diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java index d3d45e68d890..287431e99fd0 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/BytecodeNode.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.espresso.nodes; -import static com.oracle.truffle.espresso.EspressoOptions.SpecComplianceMode.STRICT; import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.AALOAD; import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.AASTORE; import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.ACONST_NULL; @@ -305,19 +304,16 @@ import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.espresso.EspressoLanguage; import com.oracle.truffle.espresso.analysis.liveness.LivenessAnalysis; +import com.oracle.truffle.espresso.bytecode.MapperBCI; +import com.oracle.truffle.espresso.classfile.ExceptionHandler; +import com.oracle.truffle.espresso.classfile.JavaKind; +import com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute; +import com.oracle.truffle.espresso.classfile.attributes.LineNumberTableAttribute; import com.oracle.truffle.espresso.classfile.bytecode.BytecodeLookupSwitch; import com.oracle.truffle.espresso.classfile.bytecode.BytecodeStream; import com.oracle.truffle.espresso.classfile.bytecode.BytecodeTableSwitch; import com.oracle.truffle.espresso.classfile.bytecode.Bytecodes; -import com.oracle.truffle.espresso.bytecode.MapperBCI; -import com.oracle.truffle.espresso.constantpool.CallSiteLink; -import com.oracle.truffle.espresso.constantpool.Resolution; -import com.oracle.truffle.espresso.constantpool.ResolvedDynamicConstant; -import com.oracle.truffle.espresso.constantpool.ResolvedWithInvokerClassMethodRefConstant; -import com.oracle.truffle.espresso.classfile.ClassfileParser; -import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool; -import com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute; -import com.oracle.truffle.espresso.classfile.attributes.LineNumberTableAttribute; +import com.oracle.truffle.espresso.classfile.bytecode.VolatileArrayAccess; import com.oracle.truffle.espresso.classfile.constantpool.ClassConstant; import com.oracle.truffle.espresso.classfile.constantpool.DoubleConstant; import com.oracle.truffle.espresso.classfile.constantpool.DynamicConstant; @@ -332,6 +328,12 @@ import com.oracle.truffle.espresso.classfile.descriptors.Signatures; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Type; +import com.oracle.truffle.espresso.classfile.perf.DebugCounter; +import com.oracle.truffle.espresso.constantpool.CallSiteLink; +import com.oracle.truffle.espresso.constantpool.Resolution; +import com.oracle.truffle.espresso.constantpool.ResolvedDynamicConstant; +import com.oracle.truffle.espresso.constantpool.ResolvedWithInvokerClassMethodRefConstant; +import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool; import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Klass; @@ -339,8 +341,6 @@ import com.oracle.truffle.espresso.impl.Method.MethodVersion; import com.oracle.truffle.espresso.impl.ObjectKlass; import com.oracle.truffle.espresso.meta.EspressoError; -import com.oracle.truffle.espresso.classfile.ExceptionHandler; -import com.oracle.truffle.espresso.classfile.JavaKind; import com.oracle.truffle.espresso.meta.Meta; import com.oracle.truffle.espresso.nodes.helper.EspressoReferenceArrayStoreNode; import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode.MethodHandleInvoker; @@ -348,7 +348,6 @@ import com.oracle.truffle.espresso.nodes.quick.CheckCastQuickNode; import com.oracle.truffle.espresso.nodes.quick.InstanceOfQuickNode; import com.oracle.truffle.espresso.nodes.quick.QuickNode; -import com.oracle.truffle.espresso.classfile.bytecode.VolatileArrayAccess; import com.oracle.truffle.espresso.nodes.quick.interop.ArrayLengthQuickNode; import com.oracle.truffle.espresso.nodes.quick.interop.ByteArrayLoadQuickNode; import com.oracle.truffle.espresso.nodes.quick.interop.ByteArrayStoreQuickNode; @@ -377,9 +376,9 @@ import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeStaticQuickNode; import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeVirtualQuickNode; import com.oracle.truffle.espresso.nodes.quick.invoke.inline.InlinedMethodNode; -import com.oracle.truffle.espresso.classfile.perf.DebugCounter; import com.oracle.truffle.espresso.resolver.CallKind; import com.oracle.truffle.espresso.resolver.CallSiteType; +import com.oracle.truffle.espresso.resolver.FieldAccessType; import com.oracle.truffle.espresso.resolver.LinkResolver; import com.oracle.truffle.espresso.resolver.ResolvedCall; import com.oracle.truffle.espresso.runtime.EspressoContext; @@ -1308,17 +1307,28 @@ private Object executeBodyFromBCI(VirtualFrame frame, int startBCI, int startTop curBCI = returnValueBci; continue loop; } - // @formatter:off // TODO(peterssen): Order shuffled. - case GETSTATIC : // fall through - case GETFIELD : top += getField(frame, top, - resolveField(curOpcode, /* Quickenable -> read from original code for thread safety */ readOriginalCPI(curBCI)), - curBCI, curOpcode, statementIndex); break; - case PUTSTATIC : // fall through - case PUTFIELD : top += putField(frame, top, - resolveField(curOpcode, /* Quickenable -> read from original code for thread safety */ readOriginalCPI(curBCI)), - curBCI, curOpcode, statementIndex); break; - + case GETSTATIC: + top += getField(frame, top, + resolveField(curOpcode, /*- Quickenable -> read from original code for thread safety */ readOriginalCPI(curBCI)), + curBCI, curOpcode, statementIndex, FieldAccessType.GetStatic); + break; + case GETFIELD: + top += getField(frame, top, + resolveField(curOpcode, /*- Quickenable -> read from original code for thread safety */ readOriginalCPI(curBCI)), + curBCI, curOpcode, statementIndex, FieldAccessType.GetInstance); + break; + case PUTSTATIC: + top += putField(frame, top, + resolveField(curOpcode, /*- Quickenable -> read from original code for thread safety */ readOriginalCPI(curBCI)), + curBCI, curOpcode, statementIndex, FieldAccessType.PutStatic); + break; + case PUTFIELD: + top += putField(frame, top, + resolveField(curOpcode, /*- Quickenable -> read from original code for thread safety */ readOriginalCPI(curBCI)), + curBCI, curOpcode, statementIndex, FieldAccessType.PutInstance); + break; + // @formatter:off case INVOKEVIRTUAL: // fall through case INVOKESPECIAL: // fall through case INVOKESTATIC: // fall through @@ -2691,72 +2701,19 @@ private long checkNonZero(long value) { * curBCI = bs.next(curBCI); * */ - private int putField(VirtualFrame frame, int top, Field field, int curBCI, int opcode, int statementIndex) { + private int putField(VirtualFrame frame, int top, Field field, int curBCI, int opcode, int statementIndex, FieldAccessType mode) { assert opcode == PUTFIELD || opcode == PUTSTATIC; CompilerAsserts.partialEvaluationConstant(field); + CompilerAsserts.partialEvaluationConstant(mode); - /* - * PUTFIELD: Otherwise, if the resolved field is a static field, putfield throws an - * IncompatibleClassChangeError. - * - * PUTSTATIC: Otherwise, if the resolved field is not a static (class) field or an interface - * field, putstatic throws an IncompatibleClassChangeError. - */ - if (field.isStatic() != (opcode == PUTSTATIC)) { - enterLinkageExceptionProfile(); - throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, - "Expected %s field %s.%s", - (opcode == PUTSTATIC) ? "static" : "non-static", - field.getDeclaringKlass().getName(), - field.getName()); - } - - /* - * PUTFIELD: Otherwise, if the field is final, it must be declared in the current class, and - * the instruction must occur in an instance initialization method () of the current - * class. Otherwise, an IllegalAccessError is thrown. - * - * PUTSTATIC: Otherwise, if the field is final, it must be declared in the current class, - * and the instruction must occur in the method of the current class. Otherwise, an - * IllegalAccessError is thrown. - */ - if (field.isFinalFlagSet()) { - if (field.getDeclaringKlass() != getDeclaringKlass()) { - enterLinkageExceptionProfile(); - throw throwBoundary(getMethod().getMeta().java_lang_IllegalAccessError, - "Update to %s final field %s.%s attempted from a different class (%s) than the field's declaring class", - (opcode == PUTSTATIC) ? "static" : "non-static", - field.getDeclaringKlass().getName(), - field.getName(), - getDeclaringKlass().getName()); - } - - boolean enforceInitializerCheck = (getLanguage().getSpecComplianceMode() == STRICT) || - // HotSpot enforces this only for >= Java 9 (v53) .class files. - field.getDeclaringKlass().getMajorVersion() >= ClassfileParser.JAVA_9_VERSION; - - if (enforceInitializerCheck && - ((opcode == PUTFIELD && !getMethod().isConstructor()) || - (opcode == PUTSTATIC && !getMethod().isClassInitializer()))) { - enterLinkageExceptionProfile(); - throw throwBoundary(getMethod().getMeta().java_lang_IllegalAccessError, - "Update to %s final field %s.%s attempted from a different method (%s) than the initializer method %s ", - (opcode == PUTSTATIC) ? "static" : "non-static", - field.getDeclaringKlass().getName(), - field.getName(), - getMethod().getName(), - (opcode == PUTSTATIC) ? "" : ""); - } - } - - assert field.isStatic() == (opcode == PUTSTATIC); + LinkResolver.resolveFieldAccess(getMeta(), getDeclaringKlass(), getMethod(), field, mode); byte typeHeader = field.getType().byteAt(0); int slotCount = (typeHeader == 'J' || typeHeader == 'D') ? 2 : 1; assert slotCount == field.getKind().getSlotCount(); int slot = top - slotCount - 1; // -receiver StaticObject receiver; - if (opcode == PUTSTATIC) { + if (mode.isStatic()) { receiver = initializeAndGetStatics(field); } else { if (!noForeignObjects.isValid()) { @@ -2857,31 +2814,16 @@ private int putField(VirtualFrame frame, int top, Field field, int curBCI, int o * curBCI = bs.next(curBCI); * */ - private int getField(VirtualFrame frame, int top, Field field, int curBCI, int opcode, int statementIndex) { + private int getField(VirtualFrame frame, int top, Field field, int curBCI, int opcode, int statementIndex, FieldAccessType mode) { assert opcode == GETFIELD || opcode == GETSTATIC; CompilerAsserts.partialEvaluationConstant(field); - /* - * GETFIELD: Otherwise, if the resolved field is a static field, getfield throws an - * IncompatibleClassChangeError. - * - * GETSTATIC: Otherwise, if the resolved field is not a static (class) field or an interface - * field, getstatic throws an IncompatibleClassChangeError. - */ - if (field.isStatic() != (opcode == GETSTATIC)) { - enterLinkageExceptionProfile(); - throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, - "Expected %s field %s.%s", - (opcode == GETSTATIC) ? "static" : "non-static", - field.getDeclaringKlass().getNameAsString(), - field.getNameAsString()); - } - assert field.isStatic() == (opcode == GETSTATIC); + LinkResolver.resolveFieldAccess(getMeta(), getDeclaringKlass(), getMethod(), field, mode); int slot = top - 1; StaticObject receiver; - if (opcode == GETSTATIC) { + if (mode.isStatic()) { receiver = initializeAndGetStatics(field); } else { if (!noForeignObjects.isValid()) { @@ -2903,7 +2845,7 @@ private int getField(VirtualFrame frame, int top, Field field, int curBCI, int o instrumentation.notifyFieldAccess(frame, statementIndex, field, receiver); } - int resultAt = field.isStatic() ? top : (top - 1); + int resultAt = mode.isStatic() ? top : (top - 1); // @formatter:off byte typeHeader = field.getType().byteAt(0); switch (typeHeader) { diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/resolver/FieldAccessType.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/resolver/FieldAccessType.java new file mode 100644 index 000000000000..b8b13fb13886 --- /dev/null +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/resolver/FieldAccessType.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, 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. + * + * 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 com.oracle.truffle.espresso.resolver; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.espresso.classfile.Constants; +import com.oracle.truffle.espresso.classfile.bytecode.Bytecodes; +import com.oracle.truffle.espresso.meta.EspressoError; + +public enum FieldAccessType { + GetStatic(true, false), + PutStatic(true, true), + GetInstance(false, false), + PutInstance(false, true); + + private final boolean isStatic; + private final boolean isPut; + + FieldAccessType(boolean isStatic, boolean isPut) { + this.isStatic = isStatic; + this.isPut = isPut; + } + + public boolean isStatic() { + return isStatic; + } + + public boolean isPut() { + return isPut; + } + + public static FieldAccessType fromOpCode(int opcode) { + switch (opcode) { + case Bytecodes.GETSTATIC: + return GetStatic; + case Bytecodes.PUTSTATIC: + return PutStatic; + case Bytecodes.GETFIELD: + return GetInstance; + case Bytecodes.PUTFIELD: + return PutInstance; + default: + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere(Bytecodes.nameOf(opcode)); + } + } + + public static FieldAccessType fromRefKind(int refKind) { + switch (refKind) { + case Constants.REF_getField: + return GetInstance; + case Constants.REF_getStatic: + return GetStatic; + case Constants.REF_putField: + return PutInstance; + case Constants.REF_putStatic: + return PutStatic; + default: + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("refkind: " + refKind); + } + } +} diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/resolver/LinkResolver.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/resolver/LinkResolver.java index 44c754b35356..3943f4f8c687 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/resolver/LinkResolver.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/resolver/LinkResolver.java @@ -23,16 +23,19 @@ package com.oracle.truffle.espresso.resolver; +import static com.oracle.truffle.espresso.EspressoOptions.SpecComplianceMode.STRICT; import static com.oracle.truffle.espresso.meta.EspressoError.cat; import java.util.Locale; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.espresso.constantpool.Resolution; +import com.oracle.truffle.espresso.classfile.ClassfileParser; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Name; import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Signature; +import com.oracle.truffle.espresso.constantpool.Resolution; +import com.oracle.truffle.espresso.impl.Field; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.Method; import com.oracle.truffle.espresso.impl.ObjectKlass; @@ -40,10 +43,42 @@ import com.oracle.truffle.espresso.meta.Meta; public final class LinkResolver { + + /** + * Symbolically resolves a field. + * + * @param accessingKlass The class requesting resolution. + * @param name The name of the field. + * @param type The type of the field. + * @param symbolicHolder The holder of the field, as described in the constant pool. + * @param accessCheck Whether to perform access checks on the resolved field. + * @param loadingConstraints Whether to check loading constraints on the resolved field. + */ + public static Field resolveFieldSymbol(Meta meta, ObjectKlass accessingKlass, + Symbol name, Symbol type, Klass symbolicHolder, + boolean accessCheck, boolean loadingConstraints) { + return LinkResolverImpl.resolveFieldSymbol(meta, accessingKlass, name, type, symbolicHolder, accessCheck, loadingConstraints); + } + + /** + * Resolve a field access site, given the symbolic resolution of the method. This ensures the + * access is valid for the given site. In particular, this checks that static fields are only + * accessed with static accesses, and that field writes to final fields are done only in the + * constructor or class initializer. + * + * @param currentKlass The class in which the field access appears. + * @param currentMethod The method in which the field access appears. + * @param symbolicResolution The result of symbolic resolution of the field declared in the + * access site. + * @param fieldAccessType The {@link FieldAccessType} representing the access site to resolve. + */ + public static Field resolveFieldAccess(Meta meta, Klass currentKlass, Method currentMethod, Field symbolicResolution, FieldAccessType fieldAccessType) { + return LinkResolverImpl.resolveFieldAccess(meta, symbolicResolution, fieldAccessType, currentKlass, currentMethod); + } + /** * Symbolically resolves a method. * - * @param meta * @param accessingKlass The class requesting resolution. * @param name The name of the method. * @param signature The signature of the method. @@ -51,17 +86,16 @@ public final class LinkResolver { * @param accessCheck Whether to perform access checks on the resolved method. * @param loadingConstraints Whether to check loading constraints on the resolved method. */ - public static Method resolveSymbol(Meta meta, ObjectKlass accessingKlass, + public static Method resolveMethodSymbol(Meta meta, ObjectKlass accessingKlass, Symbol name, Symbol signature, Klass symbolicHolder, boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints) { - return LinkResolverImpl.resolveSymbol(meta, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); + return LinkResolverImpl.resolveMethodSymbol(meta, accessingKlass, name, signature, symbolicHolder, interfaceLookup, accessCheck, loadingConstraints); } /** * Resolve a call-site given the symbolic resolution of the method in the constant pool. * - * @param meta * @param currentKlass The class in which the call site to resolve appears. * @param symbolicResolution The result of the symbolic resolution of the method declared in the * call site. @@ -82,9 +116,86 @@ final class LinkResolverImpl { private static final String AN_INTERFACE = "an interface"; private static final String A_CLASS = "a class"; + private static final String STATIC = "static"; + private static final String NON_STATIC = "non-static"; + private static final String INIT = ""; + private static final String CLINIT = ""; @TruffleBoundary - public static Method resolveSymbol(Meta meta, ObjectKlass accessingKlass, Symbol name, Symbol signature, Klass symbolicHolder, + public static Field resolveFieldSymbol(Meta meta, ObjectKlass accessingKlass, + Symbol name, Symbol type, Klass symbolicHolder, + boolean accessCheck, boolean loadingConstraints) { + Field f = symbolicHolder.lookupField(name, type); + if (f == null) { + throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchFieldError, name.toString()); + } + if (accessCheck) { + Resolution.memberDoAccessCheck(accessingKlass, symbolicHolder, f, meta); + } + if (loadingConstraints) { + f.checkLoadingConstraints(accessingKlass.getDefiningClassLoader(), f.getDeclaringKlass().getDefiningClassLoader()); + } + return f; + } + + public static Field resolveFieldAccess(Meta meta, Field field, FieldAccessType fieldAccessType, + Klass currentKlass, Method currentMethod) { + /* + * PUTFIELD/GETFIELD: Otherwise, if the resolved field is a static field, putfield throws an + * IncompatibleClassChangeError. + * + * PUTSTATIC/GETSTATIC: Otherwise, if the resolved field is not a static (class) field or an + * interface field, putstatic throws an IncompatibleClassChangeError. + */ + + if (fieldAccessType.isStatic() != field.isStatic()) { + throw throwBoundary(meta, meta.java_lang_IncompatibleClassChangeError, + "Expected %s field %s.%s", + (fieldAccessType.isStatic()) ? STATIC : NON_STATIC, + field.getDeclaringKlass().getName(), + field.getName()); + } + if (fieldAccessType.isPut()) { + /* + * PUTFIELD: Otherwise, if the field is final, it must be declared in the current class, + * and the instruction must occur in an instance initialization method () of the + * current class. Otherwise, an IllegalAccessError is thrown. + * + * PUTSTATIC: Otherwise, if the field is final, it must be declared in the current + * class, and the instruction must occur in the method of the current class. + * Otherwise, an IllegalAccessError is thrown. + */ + if (field.isFinalFlagSet()) { + if (field.getDeclaringKlass() != currentKlass) { + throw throwBoundary(meta, meta.java_lang_IllegalAccessError, + "Update to %s final field %s.%s attempted from a different class (%s) than the field's declaring class", + (fieldAccessType.isStatic()) ? STATIC : NON_STATIC, + field.getDeclaringKlass().getName(), + field.getName(), + currentKlass.getName()); + } + boolean enforceInitializerCheck = (meta.getLanguage().getSpecComplianceMode() == STRICT) || + // HotSpot enforces this only for >= Java 9 (v53) .class files. + field.getDeclaringKlass().getMajorVersion() >= ClassfileParser.JAVA_9_VERSION; + if (enforceInitializerCheck) { + if (!((fieldAccessType.isStatic() && currentMethod.isClassInitializer()) || + (!fieldAccessType.isStatic() && currentMethod.isConstructor()))) { + throw throwBoundary(meta, meta.java_lang_IllegalAccessError, + "Update to %s final field %s.%s attempted from a different method (%s) than the initializer method %s ", + (fieldAccessType.isStatic()) ? STATIC : NON_STATIC, + field.getDeclaringKlass().getName(), + field.getName(), + currentMethod.getName(), + (fieldAccessType.isStatic()) ? CLINIT : INIT); + } + } + } + } + return field; + } + + @TruffleBoundary + public static Method resolveMethodSymbol(Meta meta, ObjectKlass accessingKlass, Symbol name, Symbol signature, Klass symbolicHolder, boolean interfaceLookup, boolean accessCheck, boolean loadingConstraints) { Method resolved; @@ -132,7 +243,7 @@ public static ResolvedCall resolveCallSite(Meta meta, Klass currentKlass, Method // invokeinterface instruction throws an IncompatibleClassChangeError. if (resolved.isStatic() || (meta.getJavaVersion().java8OrEarlier() && resolved.isPrivate())) { - throw throwBoundary(meta, meta.java_lang_IncompatibleClassChangeError, "Expected instance method '%s.%s%s'", + throw throwBoundary(meta, meta.java_lang_IncompatibleClassChangeError, "Expected instance not static method '%s.%s%s'", resolved.getDeclaringKlass().getName(), resolved.getName(), resolved.getRawSignature()); @@ -155,7 +266,7 @@ public static ResolvedCall resolveCallSite(Meta meta, Klass currentKlass, Method // Otherwise, if the resolved method is a class (static) method, the invokevirtual // instruction throws an IncompatibleClassChangeError. if (resolved.isStatic()) { - throw throwBoundary(meta, meta.java_lang_IncompatibleClassChangeError, "Expected instance not static method '%s.%s%s'", + throw throwBoundary(meta, meta.java_lang_IncompatibleClassChangeError, "Expected instance method '%s.%s%s'", resolved.getDeclaringKlass().getName(), resolved.getName(), resolved.getRawSignature()); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_invoke_MethodHandleNatives.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_invoke_MethodHandleNatives.java index 7ad863eb1564..7e0a2d95072c 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_invoke_MethodHandleNatives.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_java_lang_invoke_MethodHandleNatives.java @@ -22,6 +22,38 @@ */ package com.oracle.truffle.espresso.substitutions; +import static com.oracle.truffle.espresso.classfile.Constants.ACC_STATIC; +import static com.oracle.truffle.espresso.classfile.Constants.REF_LIMIT; +import static com.oracle.truffle.espresso.classfile.Constants.REF_NONE; +import static com.oracle.truffle.espresso.classfile.Constants.REF_getField; +import static com.oracle.truffle.espresso.classfile.Constants.REF_getStatic; +import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeInterface; +import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeSpecial; +import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeStatic; +import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeVirtual; +import static com.oracle.truffle.espresso.classfile.Constants.REF_newInvokeSpecial; +import static com.oracle.truffle.espresso.classfile.Constants.REF_putField; +import static com.oracle.truffle.espresso.classfile.Constants.REF_putStatic; +import static com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics.PolySigIntrinsics.InvokeGeneric; +import static com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics.PolySigIntrinsics.None; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.ALL_KINDS; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.CONSTANTS; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.CONSTANTS_BEFORE_16; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.LM_UNCONDITIONAL; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_CALLER_SENSITIVE; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_IS_CONSTRUCTOR; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_IS_FIELD; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_IS_METHOD; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_REFERENCE_KIND_MASK; +import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_REFERENCE_KIND_SHIFT; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.util.ArrayList; +import java.util.List; + +import org.graalvm.collections.Pair; + import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; @@ -29,7 +61,6 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.espresso.EspressoLanguage; -import com.oracle.truffle.espresso.constantpool.Resolution; import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence; import com.oracle.truffle.espresso.classfile.descriptors.Symbol; import com.oracle.truffle.espresso.classfile.descriptors.Symbol.Name; @@ -51,38 +82,6 @@ import com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics; import com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics.PolySigIntrinsics; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; -import org.graalvm.collections.Pair; - -import java.lang.invoke.CallSite; -import java.lang.invoke.MethodHandle; -import java.util.ArrayList; -import java.util.List; - -import static com.oracle.truffle.espresso.meta.EspressoError.cat; -import static com.oracle.truffle.espresso.classfile.Constants.ACC_STATIC; -import static com.oracle.truffle.espresso.classfile.Constants.REF_LIMIT; -import static com.oracle.truffle.espresso.classfile.Constants.REF_NONE; -import static com.oracle.truffle.espresso.classfile.Constants.REF_getField; -import static com.oracle.truffle.espresso.classfile.Constants.REF_getStatic; -import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeInterface; -import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeSpecial; -import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeStatic; -import static com.oracle.truffle.espresso.classfile.Constants.REF_invokeVirtual; -import static com.oracle.truffle.espresso.classfile.Constants.REF_newInvokeSpecial; -import static com.oracle.truffle.espresso.classfile.Constants.REF_putField; -import static com.oracle.truffle.espresso.classfile.Constants.REF_putStatic; -import static com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics.PolySigIntrinsics.InvokeGeneric; -import static com.oracle.truffle.espresso.runtime.MethodHandleIntrinsics.PolySigIntrinsics.None; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.ALL_KINDS; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.CONSTANTS; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.CONSTANTS_BEFORE_16; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.LM_UNCONDITIONAL; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_CALLER_SENSITIVE; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_IS_CONSTRUCTOR; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_IS_FIELD; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_IS_METHOD; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_REFERENCE_KIND_MASK; -import static com.oracle.truffle.espresso.substitutions.Target_java_lang_invoke_MethodHandleNatives.Constants.MN_REFERENCE_KIND_SHIFT; @EspressoSubstitutions public final class Target_java_lang_invoke_MethodHandleNatives { @@ -465,7 +464,13 @@ StaticObject doCached( if (Constants.flagHas(flags, MN_IS_FIELD)) { isFieldProfile.enter(); Symbol t = lookupType(meta, desc); - plantFieldMemberName(memberName, resolutionKlass, name, t, refKind, callerKlass, doAccessChecks, doConstraintsChecks, meta, getLanguage()); + // Field member name resolution skips several checks: + // - Access checks + // - Static fields are accessed statically + // - Final fields and ref_put* + // These are done when needed by JDK code. + Field f = LinkResolver.resolveFieldSymbol(meta, callerKlass, name, t, resolutionKlass, false, doConstraintsChecks); + plantResolvedField(memberName, f, refKind, meta, meta.getLanguage()); return memberName; } @@ -494,7 +499,7 @@ StaticObject doCached( } Symbol sig = lookupSignature(meta, desc, mhMethodId); - Method m = LinkResolver.resolveSymbol(meta, callerKlass, name, sig, resolutionKlass, resolutionKlass.isInterface(), doAccessChecks, doConstraintsChecks); + Method m = LinkResolver.resolveMethodSymbol(meta, callerKlass, name, sig, resolutionKlass, resolutionKlass.isInterface(), doAccessChecks, doConstraintsChecks); ResolvedCall resolvedCall = LinkResolver.resolveCallSite(meta, callerKlass, m, CallSiteType.fromRefKind(refKind), resolutionKlass); plantResolvedMethod(memberName, resolvedCall, meta); @@ -614,26 +619,6 @@ private static void plant(StaticObject memberName, Method target, Meta meta, int meta.java_lang_invoke_MemberName_clazz.setObject(memberName, target.getDeclaringKlass().mirror()); } - private static void plantFieldMemberName(StaticObject memberName, - Klass resolutionKlass, Symbol name, Symbol type, - int refKind, - ObjectKlass callerKlass, - boolean accessCheck, - boolean constraintsCheck, - Meta meta, EspressoLanguage language) { - Field field = doFieldLookup(resolutionKlass, name, type); - if (field == null) { - throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchFieldError, cat("Failed lookup for field ", resolutionKlass.getName(), "#", name, ":", type)); - } - if (accessCheck) { - Resolution.memberDoAccessCheck(callerKlass, field.getDeclaringKlass(), field, meta); - } - if (constraintsCheck) { - field.checkLoadingConstraints(callerKlass.getDefiningClassLoader(), resolutionKlass.getDefiningClassLoader()); - } - plantResolvedField(memberName, field, refKind, meta, language); - } - private static void plantResolvedField(StaticObject memberName, Field field, int refKind, Meta meta, EspressoLanguage language) { meta.HIDDEN_VMTARGET.setHiddenObject(memberName, field.getDeclaringKlass()); meta.HIDDEN_VMINDEX.setHiddenObject(memberName, Target_sun_misc_Unsafe.slotToGuestOffset(field.getSlot(), field.isStatic(), language)); @@ -713,23 +698,6 @@ private static int getFieldFlags(int refKind, Field fd) { return res; } - private static Field doFieldLookup(Klass resolutionKlass, Symbol name, Symbol sig) { - if (CompilerDirectives.isPartialEvaluationConstant(resolutionKlass)) { - return lookupField(resolutionKlass, name, sig); - } else { - return lookupFieldBoundary(resolutionKlass, name, sig); - } - } - - private static Field lookupField(Klass resolutionKlass, Symbol name, Symbol sig) { - return resolutionKlass.lookupField(name, sig); - } - - @TruffleBoundary - private static Field lookupFieldBoundary(Klass resolutionKlass, Symbol name, Symbol sig) { - return lookupField(resolutionKlass, name, sig); - } - // endregion MemberName planting // region Helper methods