Skip to content

Commit 17b5b77

Browse files
committed
Introduce a Scala-agnostic API for Reflect.
The new API is offered as part of the `javalibintf`. It only uses classes from the javalib, which makes it Scala-agnostic. The old binary API used by the codegen calls the new (public) API in javalibintf. We deprecate them, but keep them for backward binary compatibility. The public API of `scala.scalajs.reflect.Reflect` is left untouched, and is still the recommended way to access the public features from user-land Scala code. In this commit, we do not change the compiler yet, to test that the deprecated methods are correct.
1 parent 6b83864 commit 17b5b77

File tree

4 files changed

+360
-34
lines changed

4 files changed

+360
-34
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Scala.js (https://www.scala-js.org/)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package org.scalajs.javalibintf
14+
15+
import java.util.Map.Entry
16+
import java.util.Optional
17+
import java.util.function.{Function, Supplier}
18+
19+
object Reflect {
20+
private val loadableModuleClasses =
21+
new java.util.HashMap[String, LoadableModuleClass[_]]()
22+
23+
private val instantiatableClasses =
24+
new java.util.HashMap[String, InstantiatableClass[_]]()
25+
26+
// Public API (documented in javalibintf/.../Reflect.java)
27+
28+
def registerLoadableModuleClass[T](fqcn: String, runtimeClass: Class[T],
29+
moduleSupplier: Supplier[T]): Unit = {
30+
loadableModuleClasses.put(fqcn,
31+
new LoadableModuleClassImpl(runtimeClass, moduleSupplier))
32+
}
33+
34+
def registerInstantiatableClass[T](fqcn: String, runtimeClass: Class[T],
35+
constructors: Array[Entry[Array[Class[_]], Function[Array[Object], T]]]): Unit = {
36+
val ctorLen = constructors.length
37+
val invokableConstructors = new Array[InvokableConstructor[T]](ctorLen)
38+
var i = 0
39+
while (i != ctorLen) {
40+
val entry = constructors(i)
41+
invokableConstructors(i) =
42+
new InvokableConstructorImpl(entry.getKey().clone(), entry.getValue())
43+
i += 1
44+
}
45+
instantiatableClasses.put(fqcn,
46+
new InstantiatableClassImpl(runtimeClass, invokableConstructors))
47+
}
48+
49+
def lookupLoadableModuleClass(fqcn: String): Optional[LoadableModuleClass[_]] =
50+
Optional.ofNullable(loadableModuleClasses.get(fqcn))
51+
52+
def lookupInstantiatableClass(fqcn: String): Optional[InstantiatableClass[_]] =
53+
Optional.ofNullable(instantiatableClasses.get(fqcn))
54+
55+
trait LoadableModuleClass[T] {
56+
def getRuntimeClass(): Class[T]
57+
58+
def loadModule(): T
59+
}
60+
61+
trait InstantiatableClass[T] {
62+
def getRuntimeClass(): Class[T]
63+
64+
def getDeclaredConstructors(): Array[InvokableConstructor[T]]
65+
}
66+
67+
trait InvokableConstructor[T] {
68+
def getParameterTypes(): Array[Class[_]]
69+
70+
def newInstance(args: Array[Object]): T
71+
}
72+
73+
// Private implementation
74+
75+
private final class LoadableModuleClassImpl[T](
76+
runtimeClass: Class[T],
77+
moduleSupplier: Supplier[T]
78+
) extends LoadableModuleClass[T] {
79+
def getRuntimeClass(): Class[T] = runtimeClass
80+
81+
def loadModule(): T = moduleSupplier.get()
82+
}
83+
84+
private final class InstantiatableClassImpl[T](
85+
runtimeClass: Class[T],
86+
declaredConstructors: Array[InvokableConstructor[T]]
87+
) extends InstantiatableClass[T] {
88+
def getRuntimeClass(): Class[T] = runtimeClass
89+
90+
def getDeclaredConstructors(): Array[InvokableConstructor[T]] =
91+
declaredConstructors.clone()
92+
}
93+
94+
private final class InvokableConstructorImpl[T](
95+
parameterTypes: Array[Class[_]],
96+
newInstanceFun: Function[Array[Object], T]
97+
) extends InvokableConstructor[T] {
98+
def getParameterTypes(): Array[Class[_]] = parameterTypes.clone()
99+
100+
def newInstance(args: Array[Object]): T = {
101+
/* Check the number of actual arguments. We let the casts and unbox
102+
* operations inside `newInstanceFun` take care of the rest.
103+
*/
104+
if (args.length != parameterTypes.length)
105+
throw new IllegalArgumentException(s"Argument count mismatch: ${args.length}")
106+
newInstanceFun.apply(args)
107+
}
108+
}
109+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Scala.js (https://www.scala-js.org/)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package org.scalajs.javalibintf;
14+
15+
import java.util.Map.Entry;
16+
import java.util.Optional;
17+
import java.util.function.Function;
18+
import java.util.function.Supplier;
19+
20+
/**
21+
* Scala.js-specific reflection API.
22+
*
23+
* This class offers static methods and static inner interfaces for the
24+
* reflection capabilities of Scala.js.
25+
*/
26+
public class Reflect {
27+
private Reflect() {}
28+
29+
/**
30+
* Registers a module class whose singleton instance can be loaded.
31+
*
32+
* This method is not intended to be called from user code. Compilers insert
33+
* calls to this method to automatically register loadable module classes,
34+
* according to the source language's rules.
35+
*
36+
* The Scala.js compiler calls this method for static objects that are
37+
* annotated with
38+
* <code>@scala.scalajs.reflect.annotation.EnableReflectiveInstantiation</code>,
39+
* or that have a super type (class or trait) with that annotation.
40+
*
41+
* @param <T>
42+
* Type of the instances loaded from this module class.
43+
* @param fqcn
44+
* Fully qualified name of the module class, including the trailing <code>$</code>.
45+
* @param runtimeClass
46+
* {@link java.lang.Class} instance representing the module class.
47+
* @param moduleSupplier
48+
* A supplier that returns the singleton instance of the module class.
49+
*/
50+
public static <T> void registerLoadableModuleClass(
51+
String fqcn, Class<T> runtimeClass, Supplier<T> moduleSupplier) {
52+
throw new AssertionError("stub");
53+
}
54+
55+
/**
56+
* Registers a class for reflective instantiation.
57+
*
58+
* This method is not intended to be called from user code. Compilers insert
59+
* calls to this method to automatically register instantiatable classes,
60+
* according to the source language's rules.
61+
*
62+
* The Scala.js compiler calls this method for classes that satisfy the
63+
* following requirements:
64+
*
65+
* <ul>
66+
* <li>the class or one of its super types (class or trait) is annotated
67+
* with <code>@scala.scalajs.reflect.annotation.EnableReflectiveInstantiation</code>,</li>
68+
* <li>it is not abstract, and</li>
69+
* <li>it is not a local class (i.e., a class defined inside a <code>def</code>).</li>
70+
* </ul>
71+
*
72+
* Inner classes (defined inside another class) are registered.
73+
*
74+
* @param <T>
75+
* Type of the instantiatable class to register.
76+
* @param fqcn
77+
* Fully qualified name of the class.
78+
* @param runtimeClass
79+
* {@link java.lang.Class} instance representing the class.
80+
* @param constructors
81+
* An array describing the available constructors. Each entry represents
82+
* one public constructor. The key of the entry is an array of the
83+
* parameter types. The value is a function that, when called with an array
84+
* of arguments, instantiates the class by passing the arguments to the
85+
* corresponding constructor.
86+
*/
87+
public static <T> void registerInstantiatableClass(
88+
String fqcn, Class<T> runtimeClass,
89+
Entry<Class<?>[], Function<Object[], T>>[] constructors) {
90+
throw new AssertionError("stub");
91+
}
92+
93+
/**
94+
* Reflectively looks up a loadable module class.
95+
*
96+
* A module class is the technical term referring to the class of a Scala
97+
* <code>object</code>.
98+
*
99+
* The module class must have been registered with
100+
* {@link registerLoadableModuleClass}. This is usually automatically done
101+
* by compilers according to the rules of the source language.
102+
*
103+
* If the module class cannot be found, this method returns an empty
104+
* {@link java.util.Optional}.
105+
*
106+
* @param fqcn
107+
* Fully-qualified name of the module class, including its trailing <code>$</code>
108+
*/
109+
public static Optional<LoadableModuleClass<?>> lookupLoadableModuleClass(String fqcn) {
110+
throw new AssertionError("stub");
111+
}
112+
113+
/**
114+
* Reflectively looks up an instantiable class.
115+
*
116+
* The module class must have been registered with
117+
* {@link registerInstantiatableClass}. This is usually automatically done
118+
* by compilers according to the rules of the source language.
119+
*
120+
* If the class cannot be found, either because it does not exist,
121+
* was not <code>@EnableReflectiveInstantiation</code> or was abstract or
122+
* local, this method returns an empty {@link java.util.Optional}.
123+
*
124+
* @param fqcn
125+
* Fully-qualified name of the class
126+
*/
127+
public static Optional<InstantiatableClass<?>> lookupInstantiatableClass(String fqcn) {
128+
throw new AssertionError("stub");
129+
}
130+
131+
/**
132+
* A handle to a loadable module class.
133+
*
134+
* @param <T>
135+
* Type of the represented module class.
136+
*/
137+
public static interface LoadableModuleClass<T> {
138+
/** The {@link java.lang.Class} instance representing the module class. */
139+
public Class<T> getRuntimeClass();
140+
141+
/** Loads the singleton instance of the module class. */
142+
public T loadModule();
143+
}
144+
145+
/**
146+
* A handle to an instantiatable class.
147+
*
148+
* @param <T>
149+
* Type of the represented class.
150+
*/
151+
public static interface InstantiatableClass<T> {
152+
/** The {@link java.lang.Class} instance representing the slass. */
153+
public Class<T> getRuntimeClass();
154+
155+
/**
156+
* Array of all the constructors that can be reflectively invoked.
157+
*
158+
* The result is a fresh array.
159+
*/
160+
public InvokableConstructor<T>[] getDeclaredConstructors();
161+
}
162+
163+
/**
164+
* A handle to an invokable constructor of an instantiatable class.
165+
*
166+
* @param <T>
167+
* Type of instances created by this constructor.
168+
*/
169+
public static interface InvokableConstructor<T> {
170+
/**
171+
* Array of the parameter types required by this constructor.
172+
*
173+
* The result is a fresh array.
174+
*/
175+
public Class<?>[] getParameterTypes();
176+
177+
/**
178+
* Calls the constructor to create a new instance of the class.
179+
*
180+
* @param args
181+
* An array of the arguments to pass to the constructor.
182+
* @return
183+
* The newly created instance.
184+
*/
185+
public T newInstance(Object... args);
186+
}
187+
}

0 commit comments

Comments
 (0)