Repository: apple/pkl
File: pkl-core/src/main/java/org/pkl/core/EvaluatorImpl.java (Java)

Code before:
    assert timeout != null;
    // TODO: use a different exception type so that clients can tell apart timeouts from other
    // errors
    throw new PklException(
        ErrorMessages.create(
            "evaluationTimedOut", (timeout.getSeconds() + timeout.getNano() / 1_000_000_000d)));
  }

  private VmException moduleOutputValueTypeMismatch(
      VmTyped module, PClassInfo<?> expectedClassInfo, Object value, VmTyped output) {
    var moduleUri = module.getModuleInfo().getModuleKey().getUri();
    var builder =
        new VmExceptionBuilder()
            .evalError(
                "invalidModuleOutputValue",
                expectedClassInfo.getDisplayName(),
                VmUtils.getClass(value).getPClassInfo().getDisplayName(),
                moduleUri);
    var outputValueMember = output.getMember(Identifier.VALUE);
    assert outputValueMember != null;
    var uriOfValueMember = outputValueMember.getSourceSection().getSource().getURI();
    // If `value` was explicitly re-assigned, show that in the stack trace.
    // Otherwise, show the module header.
    if (!uriOfValueMember.equals(PClassInfo.pklBaseUri)) {
      return builder
          .withSourceSection(outputValueMember.getBodySection())
          .withMemberName("value")
          .build();
    } else {
      // if the module does not extend or amend anything, suggest amending the module URI
      if (module.getParent() != null
          && module.getParent().getVmClass().equals(BaseModule.getModuleClass())
          && expectedClassInfo.isModuleClass()) {
        builder.withHint(
            String.format(
                "Try adding `amends %s` to the module header.",
                vmValueRenderer.render(expectedClassInfo.getModuleUri().toString())));
      }
      return builder
          .withSourceSection(module.getModuleInfo().getHeaderSection())
          .withMemberName(module.getModuleInfo().getModuleName())
          .build();
    }
  }

  private boolean isPklBug(VmStackOverflowException e) {
    // There's no good way to tell if a StackOverflowError came from Pkl, or from our
    // implementation.
    // This is a simple heuristic; it's pretty likely that any stack overflow error that occurs
    // if there's less than 100 truffle frames is due to our own doing.
    var truffleStackTraceElements = TruffleStackTrace.getStackTrace(e);

Review comment (suggestion):
There's a fair bit of logic here that's bespoke to reading `output.value`, so it's not too appropriate to be re-purposed. For example, it show different error messages based on whether `output.value` was explicitly assigned or not.

I think we should just copy some of the logic here that's useful, and inline that into `readModuleOutput`.

Code after:
    assert timeout != null;
    // TODO: use a different exception type so that clients can tell apart timeouts from other
    // errors
    throw new PklException(
        ErrorMessages.create(
            "evaluationTimedOut", (timeout.getSeconds() + timeout.getNano() / 1_000_000_000d)));
  }

  private VmTyped readModuleOutput(VmTyped module) {
    var value = VmUtils.readMember(module, Identifier.OUTPUT);
    if (value instanceof VmTyped typedOutput
        && typedOutput.getVmClass().getPClassInfo() == PClassInfo.ModuleOutput) {
      return typedOutput;
    }

    var moduleUri = module.getModuleInfo().getModuleKey().getUri();
    var builder =
        new VmExceptionBuilder()
            .evalError(
                "invalidModuleOutput",
                "output",
                PClassInfo.ModuleOutput.getDisplayName(),
                VmUtils.getClass(value).getPClassInfo().getDisplayName(),
                moduleUri);
    var outputMember = module.getMember(Identifier.OUTPUT);
    assert outputMember != null;
    var uriOfValueMember = outputMember.getSourceSection().getSource().getURI();
    // If `output` was explicitly re-assigned, show that in the stack trace.
    if (!uriOfValueMember.equals(PClassInfo.pklBaseUri)) {
      builder.withSourceSection(outputMember.getBodySection()).withMemberName("output");
    }
    throw builder.build();
  }

  private VmException moduleOutputValueTypeMismatch(
      VmTyped module, PClassInfo<?> expectedClassInfo, Object value, VmTyped output) {
    var moduleUri = module.getModuleInfo().getModuleKey().getUri();
    var builder =
        new VmExceptionBuilder()
            .evalError(
                "invalidModuleOutput",
                "output.value",
                expectedClassInfo.getDisplayName(),
                VmUtils.getClass(value).getPClassInfo().getDisplayName(),
                moduleUri);
    var outputValueMember = output.getMember(Identifier.VALUE);
    assert outputValueMember != null;
    var uriOfValueMember = outputValueMember.getSourceSection().getSource().getURI();
    // If `value` was explicitly re-assigned, show that in the stack trace.
    // Otherwise, show the module header.
    if (!uriOfValueMember.equals(PClassInfo.pklBaseUri)) {