[vm] Report an error when illegally accessing class through API in AOT mode.

Move all pragma documentation into runtime/docs and reformat it a bit.

Fixes https://github.com/dart-lang/sdk/issues/35170

Change-Id: I2ab4d6d598f115efaa3192135cb838fb85cbbb01
Reviewed-on: https://dart-review.googlesource.com/c/84435
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
Auto-Submit: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Samir Jindel <sjindel@google.com>
diff --git a/runtime/vm/compiler/aot/entry_points_pragma.md b/runtime/docs/compiler/aot/entry_point_pragma.md
similarity index 75%
rename from runtime/vm/compiler/aot/entry_points_pragma.md
rename to runtime/docs/compiler/aot/entry_point_pragma.md
index dfdecfe..69cf760 100644
--- a/runtime/vm/compiler/aot/entry_points_pragma.md
+++ b/runtime/docs/compiler/aot/entry_point_pragma.md
@@ -1,15 +1,21 @@
-# Entry points @pragma annotations
+# `vm:entry-point` pragma
+
+The annotation `@pragma("vm:entry-point", ...)` **must** be placed on a class or
+member to indicate that it may be resolved, allocated or invoked directly from
+native or VM code _in AOT mode_.
+
+## Background
 
 Dart VM precompiler (AOT compiler) performs whole-program optimizations such as
-tree shaking in order to decrease size of the resulting compiled apps and
-improve their performance. Such optimizations assume that compiler can see the
-whole Dart program, and is able to discover and analyze all Dart functions and
-members which can be potentially executed at run time. While the Dart code is
-fully available for precompiler, native code of the embedder and native methods
-are out of reach of the compiler. Such native code can call back to Dart via
-native Dart API.
+tree shaking and type flow analysis (TFA) in order to decrease size of the
+resulting compiled apps and improve their performance. Such optimizations
+assume that compiler can see the whole Dart program, and is able to discover
+and analyse all Dart functions and members which can be potentially executed at
+run time. While the Dart code is fully available for precompiler, native code
+of the embedder and native methods are out of reach of the compiler. Such
+native code can call back to Dart via native Dart API.
 
-In order to aid precompiler, programmer can explicitly list entry points
+In order to guide precompiler, programmer **must** explicitly list entry points
 (roots) - Dart classes and members which are accessed from native code. Note
 that listing entry points is not optional: as long as program defines native
 methods which call into Dart, the entry points are required for the correctness
@@ -18,15 +24,11 @@
 In addition, when obfuscation is enabled, the precompiler needs to know which
 symbols need to be preserved to ensure they can be resolved from native code.
 
-# Pragma annotation
-
-The annotation `@pragma("vm:entry-point", ...)` can be placed on a class or
-member to indicate that it may be resolved, allocated or invoked directly from
-native or VM code.
+## Syntax
 
 The allowed uses of the annotation are as follows.
 
-## Classes
+### Classes
 
 Any one of the following forms may be attached to a class:
 
@@ -44,7 +46,7 @@
 this case, their name will survive obfuscation, but they won't have any
 allocation stubs.
 
-## Procedures
+### Procedures
 
 Any one of the following forms may be attached to a procedure (including
 getters, setters and constructors):
@@ -61,7 +63,7 @@
 procedure is a *generative* constructor, the enclosing class will also be marked
 for allocation from native or VM code.
 
-## Fields
+### Fields
 
 Any one of the following forms may be attached to a non-static field. The first
 three forms may be attached to static fields.
@@ -72,7 +74,7 @@
 @pragma("vm:entry-point", true/false)
 @pragma("vm:entry-point", !const bool.formEnvironment("dart.vm.product"))
 @pragma("vm:entry-point", "get"/"set")
-void foo() { ... }
+int foo;
 ```
 
 If the second parameter is missing, `null` or `true, the field is marked for
diff --git a/runtime/docs/compiler/frontend/testing_trace_entrypoints_pragma.md b/runtime/docs/compiler/frontend/testing_trace_entrypoints_pragma.md
new file mode 100644
index 0000000..fe4e8b0
--- /dev/null
+++ b/runtime/docs/compiler/frontend/testing_trace_entrypoints_pragma.md
@@ -0,0 +1,31 @@
+# `vm:testing.unsafe.trace-entrypoints-fn` pragma
+
+This pragma is used for testing purposes in the test suite.
+
+Example:
+
+```dart
+void hook(String functionName, int entryPointId) {
+  // ...
+}
+
+class C<T> {
+  @pragma('vm:testing.unsafe.trace-entrypoints-fn', hook)
+  void foo(T x) {
+    // ...
+  }
+}
+```
+
+When `foo` is invoked, `hook` will be called in `foo`'s prologue if `foo` was
+compiled with multiple entry points. `hook` will be passed the name of the
+function it was called for and the ID of the entry point used for the
+invocation:
+
+- 0: Normal entry.
+
+- 1: Unchecked entry: prologue was short so separate prologues for normal and
+  unchecked entry were compiled.
+
+- 2: Unchecked shared entry: prologue was long, so normal and unchecked entry
+  set a temporary and type-checks are predicated on the temporary.
diff --git a/runtime/docs/compiler/result_type_pragma.md b/runtime/docs/compiler/result_type_pragma.md
new file mode 100644
index 0000000..2bc5032
--- /dev/null
+++ b/runtime/docs/compiler/result_type_pragma.md
@@ -0,0 +1,42 @@
+# `vm:exact-result-type` pragma
+
+To facilitate type-flow analysis and other optimizations, Dart methods may use
+the pragma `vm:exact-result-type` to declare an exact return type different than
+the return type in the signature of the method. There are three limitations on
+this pragma:
+
+0. The Dart object returned by the method at runtime must have exactly the type
+specified in the annotation (not a subtype).
+
+1. The exact return type declared in the pragma must be a subtype of the return
+   type declared in the method signature.
+   Note that this restriction is not enforced automatically by the compiler.
+
+2. `vm:exact-result-type` may only be attached to methods in the `dart:*`
+   libraries.
+   This pragma can introduce unsafe behavior since it allows the compiler to
+   make stronger assumptions during optimization than what the sound strong-mode
+   type system allows, so it is only allowed in the core library where the Dart
+   VM team can ensure that it is not misused.
+
+If limitations 0 or 1 are violated, undefined behavior may result.
+Note that since `null` is an instance of the `Null` type, which is a subtype of any other, exactness of the annotated result type implies that the result must be non-null.
+
+## Syntax
+
+### Reference to type via type literal
+
+```dart
+class A {}
+class B extends A {}
+
+@pragma('vm:exact-result-type', B)
+A foo() native 'foo_impl';
+```
+
+### Reference to type via path
+
+```dart
+@pragma('vm:exact-result-type', 'dart:core#_Smi');
+int foo() native 'foo_impl';
+```
diff --git a/runtime/docs/pragmas.md b/runtime/docs/pragmas.md
index c739826..a896592 100644
--- a/runtime/docs/pragmas.md
+++ b/runtime/docs/pragmas.md
@@ -4,19 +4,17 @@
 
 These pragmas are part of the VM's API and are safe for use in external code.
 
-- **vm:entry-point**
-
-  [Defining entry-points into Dart code for an embedder or native methods]
-  (file://../vm/compiler/aot/entry_points_pragma.md)
+| Pragma | Meaning |
+| --- | --- |
+| `vm:entry-point` | [Defining entry-points into Dart code for an embedder or native methods](compiler/aot/entry_point_pragma.md) |
 
 ## Pragmas for internal use
 
 These pragmas can cause unsound behavior if used incorrectly and therefore are only allowed within the core SDK libraries.
 
-- **vm:exact-result-type**
-
-  [Declaring an exact result type of a method]
-  (file://../vm/compiler/result_type_pragma.md)
+| Pragma | Meaning |
+| --- | --- |
+| `vm:exact-result-type` | [Declaring an exact result type of a method](compiler/result_type_pragma.md) |
 
 ## Pragmas for internal testing
 
@@ -25,7 +23,6 @@
 The names of these pragmas are prefixed with "testing".
 Additionally, they are categorized into "safe" and "unsafe" forms: "safe" pragmas should not affect the behavior of the program and can be safely added anywhere, whereas "unsafe" pragmas may change the code's behavior or may cause the VM to crash if used improperly.
 
-- **vm:testing.unsafe.trace-entrypoints-fn**
-
-  [Observing which flow-graph-level entry-point was used when a function was called]
-  (file://../vm/compiler/frontend/entrypoints_pragma.md)
+| Pragma | Meaning |
+| --- | --- |
+| `vm:testing.unsafe.trace-entrypoints-fn` | [Observing which flow-graph-level entry-point was used when a function was called](compiler/frontend/testing_trace_entrypoints_pragma.md) |
diff --git a/runtime/vm/compiler/frontend/entrypoints_pragma.md b/runtime/vm/compiler/frontend/entrypoints_pragma.md
deleted file mode 100644
index 3fcb451..0000000
--- a/runtime/vm/compiler/frontend/entrypoints_pragma.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Pragma for testing multiple entrypoints
-
-Example:
-
-```dart
-void hook(String functionName, int entryPointId) {
-  // ...
-}
-
-class C<T> {
-  @pragma("vm:testing.unsafe.trace-entrypoints-fn", hook)
-  void foo(T x) {
-    // ...
-  }
-}
-```
-
-When `foo` is invoked, `hook` will be called in `foo`'s prologue if `foo` was
-compiled with multiple entrypoints. `hook` will be passed the name of the
-function it was called for and the ID of the entrypoint used for the invocation:
-
-- 0: Normal entry.
-
-- 1: Unchecked entry: prologue was short so separate prologues for normal and
-  unchecked entry were compiled.
-
-- 2: Unchecked shared entry: prologue was long, so normal and unchecked entry
-  set a temporary and type-checks are predicated on the temporary.
diff --git a/runtime/vm/compiler/result_type_pragma.md b/runtime/vm/compiler/result_type_pragma.md
deleted file mode 100644
index debb523..0000000
--- a/runtime/vm/compiler/result_type_pragma.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Result type @pragma annotations
-
-To facilitate type-flow analysis and other optimizations, Dart methods may use the pragma `vm:exact-result-type` to declare an exact return type different than the return type in the signature of the method. There are three limitations on this pragma:
-
-0. The Dart object returned by the method at runtime must have exactly the type specified in the annotation (not a subtype).
-
-1. The exact return type declared in the pragma must be a subtype of the return type declared in the method signature.
-   Note that this restriction is not enforced automatically by the compiler.
-
-2. `vm:exact-result-type` may only be attached to methods in the core library.
-   This pragma can introduce unsafe behavior since it allows the compiler to make stronger assumptions during optimization than what the sound strong-mode type system allows, so it is only allowed in the core library where the Dart VM team can ensure that it is not misused.
-
-If limitations 0 or 1 are violated, undefined behavior may result.
-Note that since `null` is an instance of the `Null` type, which is a subtype of any other, exactness of the annotated result type implies that the result must be non-null.
-
-## Syntax
-
-### Reference to type via type literal
-
-```dart
-class A {}
-class B extends A {}
-
-@pragma("vm:exact-result-type", B)
-A foo() native "foo_impl";
-```
-
-### Reference to type via path
-
-```dart
-@pragma("vm:exact-result-type", "dart:core#_Smi");
-int foo() native "foo_impl";
-```
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 4d3b8be..7fe36e5 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -5042,7 +5042,16 @@
 #if defined(DART_PRECOMPILED_RUNTIME)
   // This is not a completely thorough check that the class is an entry-point,
   // but it catches most missing annotations.
-  RELEASE_ASSERT(klass.has_pragma());
+  if (!klass.has_pragma()) {
+    OS::PrintErr(
+        "ERROR: It is illegal to access class %s through Dart C API "
+        "because it was not annotated with @pragma('vm:entry-point').\n"
+        "ERROR: See "
+        "https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/"
+        "aot/entry_point_pragma.md",
+        String::Handle(klass.UserVisibleName()).ToCString());
+    UNREACHABLE();
+  }
 #endif
 }