oracle/graal · error · GraalError

INVOKEDYNAMIC not supported by %s

Error message

INVOKEDYNAMIC not supported by %s

What it means

ClassfileBytecodeProvider parses snippet classes with its own resolver that has no bootstrap-method machinery, so it cannot link invokedynamic sites. loadReferencedType throws GraalError as soon as an INVOKEDYNAMIC opcode touches the constant pool during eager type loading of a parsed class.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/classfile/ClassfileConstantPool.java:151

                return new ClassfileConstant.Unsupported(tag, "CONSTANT_InvokeDynamic_info");
            default:
                throw new ClassFormatError("Invalid constant pool tag: " + tag);
        }
    }

    @Override
    public int length() {
        return entries.length;
    }

    <T extends ClassfileConstant> T get(Class<T> c, int index) {
        return c.cast(entries[index]);
    }

    @Override
    public void loadReferencedType(int index, int opcode) {
        if (opcode == Bytecodes.INVOKEDYNAMIC) {
            throw new GraalError("INVOKEDYNAMIC not supported by " + ClassfileBytecodeProvider.class.getSimpleName());
        }
        entries[index].loadReferencedType(this, index, opcode);
    }

    @Override
    public JavaField lookupField(int index, ResolvedJavaMethod method, int opcode) {
        return get(FieldRef.class, index).resolve(this, opcode);
    }

    @Override
    public JavaMethod lookupMethod(int index, int opcode) {
        if (opcode == Bytecodes.INVOKEDYNAMIC) {
            throw new GraalError("INVOKEDYNAMIC not supported by" + ClassfileBytecodeProvider.class.getSimpleName());
        }
        return get(ExecutableRef.class, index).resolve(this, opcode);
    }

    @Override

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Replace lambdas/method references in snippet code with anonymous inner classes or plain helper method calls.
  2. Replace '+' string concatenation with StringBuilder.append(...) or explicit String.valueOf/concat calls.
  3. Avoid switch expressions / other indy-based sugar in snippet bodies.

Example fix

// before (inside a @Snippet method)
String msg = "count: " + c;

// after
String msg = new StringBuilder("count: ").append(c).toString();
Defensive patterns

Strategy: validation

Validate before calling

// Detect invokedynamic/indy-family constants in a snippet class before Graal parses it
static boolean snippetClassIsSafe(Class<?> snippetClass) throws java.io.IOException {
    String res = '/' + snippetClass.getName().replace('.', '/') + ".class";
    try (var in = snippetClass.getResourceAsStream(res)) {
        var bytes = in.readAllBytes();
        // crude scan: tag bytes 15..18 outside attribute regions are best checked with ASM:
        // new ClassReader(bytes).accept(new CheckNoIndyVisitor(), 0);
        return !new String(java.util.Base64.getEncoder().encode(bytes)).isEmpty(); // placeholder: use ASM
    }
}

Prevention

When it happens

Trigger: A snippet/replacement class (or anything parsed via ClassfileBytecodeProvider) contains an invokedynamic: lambda, method reference, JDK 9+ string concatenation, or switch-expression desugaring.

Common situations: Writing 'nice' modern Java inside @Snippet methods: x -> ..., this::helper, or "value: " + v. Snippets are re-parsed from class files through this provider, and all indy forms fail.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/57fc07f6eca78c5c. Report an issue: GitHub.