Skip to content

Commit 83c8c32

Browse files
authored
fix logic for obtaining TypeInfoFactory
Fix TypeInfoFactory used in FunctionObject to properly initialize using the scope instead of "this".
1 parent b32e575 commit 83c8c32

File tree

8 files changed

+154
-35
lines changed

8 files changed

+154
-35
lines changed

rhino/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ decycle {
8989
ignoring from: "org.mozilla.javascript.ScriptableObject", to: "org.mozilla.javascript.lc.type.TypeInfoFactory"
9090
ignoring from: "org.mozilla.javascript.FunctionObject", to: "org.mozilla.javascript.lc.type.TypeInfo*"
9191
ignoring from: "org.mozilla.javascript.Context", to: "org.mozilla.javascript.lc.type.TypeInfo*"
92+
ignoring from: "org.mozilla.javascript.ScriptRuntime", to: "org.mozilla.javascript.lc.type.TypeInfoFactory"
93+
ignoring from: "org.mozilla.javascript.ScriptRuntime", to: "org.mozilla.javascript.lc.type.impl.factory.ConcurrentFactory"
9294

9395
// currently accepted cycles (may be resolved, when it is clear, how to separate the liveconnect stuff)
9496
ignoring from: "org.mozilla.javascript.lc.type.TypeInfoFactory", to: "org.mozilla.javascript.lc.type.impl.factory.*"

rhino/src/main/java/org/mozilla/javascript/FunctionObject.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ public class FunctionObject extends BaseFunction {
8080
* @see org.mozilla.javascript.Scriptable
8181
*/
8282
public FunctionObject(String name, Member methodOrConstructor, Scriptable scope) {
83-
var typeInfoFactory = TypeInfoFactory.get(this);
83+
// fallback to global factory for compatibility with old behaviour, where the `scope` can be
84+
// an object not yet initialized via `initStandardObject(...)`
85+
var typeInfoFactory = TypeInfoFactory.getOrElse(scope, TypeInfoFactory.GLOBAL);
8486

8587
if (methodOrConstructor instanceof Constructor) {
8688
member = new MemberBox((Constructor<?>) methodOrConstructor, typeInfoFactory);

rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.function.BiConsumer;
2323
import java.util.function.Consumer;
2424
import org.mozilla.javascript.ast.FunctionNode;
25+
import org.mozilla.javascript.lc.type.impl.factory.ConcurrentFactory;
2526
import org.mozilla.javascript.typedarrays.NativeArrayBuffer;
2627
import org.mozilla.javascript.typedarrays.NativeBigInt64Array;
2728
import org.mozilla.javascript.typedarrays.NativeBigUint64Array;
@@ -167,6 +168,7 @@ public static ScriptableObject initSafeStandardObjects(
167168

168169
scope.associateValue(LIBRARY_SCOPE_KEY, scope);
169170
new ClassCache().associate(scope);
171+
new ConcurrentFactory().associate(scope);
170172

171173
LambdaConstructor function = BaseFunction.init(cx, scope, sealed);
172174
LambdaConstructor obj = NativeObject.init(cx, scope, sealed);

rhino/src/main/java/org/mozilla/javascript/ScriptableObject.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,9 +1499,11 @@ public void defineProperty(String propertyName, Class<?> clazz, int attributes)
14991499
*/
15001500
public void defineProperty(
15011501
String propertyName, Object delegateTo, Method getter, Method setter, int attributes) {
1502+
var typeFactory = TypeInfoFactory.getOrElse(this, TypeInfoFactory.GLOBAL);
1503+
15021504
MemberBox getterBox = null;
15031505
if (getter != null) {
1504-
getterBox = new MemberBox(getter, TypeInfoFactory.get(this));
1506+
getterBox = new MemberBox(getter, typeFactory);
15051507

15061508
boolean delegatedForm;
15071509
if (!Modifier.isStatic(getter.getModifiers())) {
@@ -1542,7 +1544,7 @@ public void defineProperty(
15421544
if (setter.getReturnType() != Void.TYPE)
15431545
throw Context.reportRuntimeErrorById("msg.setter.return", setter.toString());
15441546

1545-
setterBox = new MemberBox(setter, TypeInfoFactory.get(this));
1547+
setterBox = new MemberBox(setter, typeFactory);
15461548

15471549
boolean delegatedForm;
15481550
if (!Modifier.isStatic(setter.getModifiers())) {

rhino/src/main/java/org/mozilla/javascript/lc/type/TypeInfoFactory.java

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.function.Supplier;
2222
import org.mozilla.javascript.Scriptable;
2323
import org.mozilla.javascript.ScriptableObject;
24-
import org.mozilla.javascript.lc.type.impl.factory.ConcurrentFactory;
2524
import org.mozilla.javascript.lc.type.impl.factory.NoCacheFactory;
2625
import org.mozilla.javascript.lc.type.impl.factory.WeakReferenceFactory;
2726

@@ -259,45 +258,52 @@ static TypeInfo matchPredefined(Class<?> clazz) {
259258
/**
260259
* Associate this TypeInfoFactory object with the given top-level scope.
261260
*
261+
* <p>NOTE: If you're about associate a custom TypeInfoFactory to a scope, call this method
262+
* before {@code initStandardObjects(...)} or {@code initSafeStandardObjects(...)}
263+
*
262264
* @param topScope scope to associate this TypeInfoFactory object with.
263-
* @return {@code true} if no previous TypeInfoFactory object were associated with the scope and
264-
* this TypeInfoFactory were successfully associated, false otherwise.
265+
* @return {@code this} if no previous TypeInfoFactory object was associated with the scope and
266+
* this TypeInfoFactory is successfully associated, or the old associated factory otherwise.
265267
* @throws IllegalArgumentException if provided scope is not top scope
266268
* @see #get(Scriptable scope)
267269
*/
268-
default boolean associate(ScriptableObject topScope) {
270+
default TypeInfoFactory associate(ScriptableObject topScope) {
269271
if (topScope.getParentScope() != null) {
270272
throw new IllegalArgumentException("provided scope not top scope");
271273
}
272-
return this == topScope.associateValue("TypeInfoFactory", this);
274+
return (TypeInfoFactory) topScope.associateValue("TypeInfoFactory", this);
273275
}
274276

275277
/**
276-
* Search for TypeInfoFactory in the given scope.If none was found, it will try to associate a
277-
* new ClassCache object to the top scope.
278+
* Search for TypeInfoFactory in the given scope.
278279
*
279280
* @param scope scope to search for TypeInfoFactory object.
280-
* @return previously associated TypeInfoFactory object, or a new instance of TypeInfoFactory if
281-
* none was found
281+
* @return previously associated TypeInfoFactory object.
282282
* @throws IllegalArgumentException if the top scope of provided scope have no associated
283-
* TypeInfoFactory, and cannot have TypeInfoFactory associated due to the top scope not
284-
* being a {@link ScriptableObject}
283+
* TypeInfoFactory.
285284
* @see #associate(ScriptableObject topScope)
286285
*/
287286
static TypeInfoFactory get(Scriptable scope) {
288-
TypeInfoFactory got =
289-
(TypeInfoFactory) ScriptableObject.getTopScopeValue(scope, "TypeInfoFactory");
287+
var got = getOrElse(scope, null);
290288
if (got == null) {
291-
// we expect this to not happen frequently, so computing top scope twice is acceptable
292-
var topScope = ScriptableObject.getTopLevelScope(scope);
293-
if (!(topScope instanceof ScriptableObject)) {
294-
// Note: it's originally a RuntimeException, the super class of
295-
// IllegalArgumentException, so this will not break error catching
296-
throw new IllegalArgumentException(
297-
"top scope have no associated TypeInfoFactory and cannot have TypeInfoFactory associated due to not being a ScriptableObject");
298-
}
299-
got = new ConcurrentFactory();
300-
got.associate(((ScriptableObject) topScope));
289+
throw new IllegalArgumentException("top scope have no associated TypeInfoFactory");
290+
}
291+
return got;
292+
}
293+
294+
/**
295+
* Search for TypeInfoFactory in the given scope. When none was found, {@code fallback} is
296+
* returned instead
297+
*
298+
* @param scope scope to search for TypeInfoFactory object.
299+
* @return previously associated TypeInfoFactory object, or {@code fallback} if none was found
300+
* @see #get(Scriptable)
301+
* @see #associate(ScriptableObject topScope)
302+
*/
303+
static TypeInfoFactory getOrElse(Scriptable scope, TypeInfoFactory fallback) {
304+
var got = (TypeInfoFactory) ScriptableObject.getTopScopeValue(scope, "TypeInfoFactory");
305+
if (got == null) {
306+
return fallback;
301307
}
302308
return got;
303309
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.mozilla.javascript.tests.type_info;
2+
3+
import java.lang.reflect.GenericArrayType;
4+
import java.lang.reflect.ParameterizedType;
5+
import java.lang.reflect.TypeVariable;
6+
import java.lang.reflect.WildcardType;
7+
import java.util.List;
8+
import org.mozilla.javascript.lc.type.TypeInfo;
9+
import org.mozilla.javascript.lc.type.TypeInfoFactory;
10+
11+
/**
12+
* @author ZZZank
13+
*/
14+
enum AlwaysFailFactory implements TypeInfoFactory {
15+
INSTANCE;
16+
17+
public static final String MESSAGE = "attempting to create TypeInfo on AlwaysFailFactory";
18+
19+
@Override
20+
public TypeInfo create(Class<?> clazz) {
21+
throw new AssertionError(MESSAGE);
22+
}
23+
24+
@Override
25+
public TypeInfo create(GenericArrayType genericArrayType) {
26+
throw new AssertionError(MESSAGE);
27+
}
28+
29+
@Override
30+
public TypeInfo create(TypeVariable<?> typeVariable) {
31+
throw new AssertionError(MESSAGE);
32+
}
33+
34+
@Override
35+
public TypeInfo create(ParameterizedType parameterizedType) {
36+
throw new AssertionError(MESSAGE);
37+
}
38+
39+
@Override
40+
public TypeInfo create(WildcardType wildcardType) {
41+
throw new AssertionError(MESSAGE);
42+
}
43+
44+
@Override
45+
public TypeInfo toArray(TypeInfo component) {
46+
throw new AssertionError(MESSAGE);
47+
}
48+
49+
@Override
50+
public TypeInfo attachParam(TypeInfo base, List<TypeInfo> params) {
51+
throw new AssertionError(MESSAGE);
52+
}
53+
}

tests/src/test/java/org/mozilla/javascript/tests/type_info/CustomTypeInfoFactoryTest.java

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package org.mozilla.javascript.tests.type_info;
22

33
import java.io.*;
4+
import java.lang.reflect.Modifier;
5+
import java.util.Arrays;
46
import org.junit.jupiter.api.Assertions;
57
import org.junit.jupiter.api.Test;
6-
import org.mozilla.javascript.ContextFactory;
7-
import org.mozilla.javascript.ScriptableObject;
8+
import org.mozilla.javascript.*;
89
import org.mozilla.javascript.lc.type.TypeInfoFactory;
910
import org.mozilla.javascript.lc.type.impl.factory.ConcurrentFactory;
1011

@@ -13,13 +14,63 @@
1314
*/
1415
public class CustomTypeInfoFactoryTest {
1516

17+
/**
18+
* @see #exampleFunctionObjectMethod(Context, Scriptable, Object[], Function)
19+
* @see AlwaysFailFactory
20+
*/
1621
@Test
17-
public void associate() throws Exception {
22+
public void functionObject() {
23+
var contextFactory = new ContextFactory();
24+
25+
try (var cx = contextFactory.enterContext()) {
26+
var scope = new NativeObject();
27+
AlwaysFailFactory.INSTANCE.associate(scope);
28+
cx.initStandardObjects(scope);
29+
30+
var method =
31+
Arrays.stream(CustomTypeInfoFactoryTest.class.getDeclaredMethods())
32+
.filter(
33+
m -> {
34+
var mod = m.getModifiers();
35+
return Modifier.isPublic(mod) && Modifier.isStatic(mod);
36+
})
37+
.filter(m -> m.getName().equals("exampleFunctionObjectMethod"))
38+
.findFirst()
39+
.orElseThrow();
40+
Assertions.assertThrowsExactly(
41+
AssertionError.class,
42+
() -> new FunctionObject("test", method, scope),
43+
AlwaysFailFactory.MESSAGE);
44+
}
45+
}
46+
47+
public static void exampleFunctionObjectMethod(
48+
Context cx, Scriptable scope, Object[] args, Function fn) {
49+
throw new AssertionError("method for test purpose only, do not invoke");
50+
}
51+
52+
@Test
53+
public void associateAfterInit() throws Exception {
1854
var contextFactory = new ContextFactory();
1955

2056
try (var cx = contextFactory.enterContext()) {
2157
var scope = cx.initStandardObjects();
58+
59+
var toAssociate = AlwaysFailFactory.INSTANCE;
60+
var associated = toAssociate.associate(scope);
61+
62+
Assertions.assertNotSame(toAssociate, associated);
63+
}
64+
}
65+
66+
@Test
67+
public void associate() throws Exception {
68+
var contextFactory = new ContextFactory();
69+
70+
try (var cx = contextFactory.enterContext()) {
71+
var scope = new NativeObject();
2272
new NoGenericNoCacheFactory().associate(scope);
73+
cx.initStandardObjects(scope);
2374

2475
var typeFactory = TypeInfoFactory.get(scope);
2576

@@ -45,8 +96,9 @@ public void serdeCustom() throws Exception {
4596
var contextFactory = new ContextFactory();
4697
byte[] data;
4798
try (var cx = contextFactory.enterContext()) {
48-
var scope = cx.initStandardObjects();
99+
var scope = new NativeObject();
49100
new NoGenericNoCacheFactory().associate(scope);
101+
cx.initStandardObjects(scope);
50102

51103
data = simulateSer(scope);
52104
}
@@ -64,8 +116,9 @@ public void serdeGlobal() throws Exception {
64116
var contextFactory = new ContextFactory();
65117
byte[] data;
66118
try (var cx = contextFactory.enterContext()) {
67-
var scope = cx.initStandardObjects();
119+
var scope = new NativeObject();
68120
TypeInfoFactory.GLOBAL.associate(scope);
121+
cx.initStandardObjects(scope);
69122

70123
data = simulateSer(scope);
71124
}

tests/src/test/java/org/mozilla/javascript/tests/type_info/NoGenericNoCacheFactory.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.lang.reflect.TypeVariable;
44
import java.util.List;
55
import java.util.Map;
6-
import java.util.function.Function;
6+
import org.mozilla.javascript.ScriptableObject;
77
import org.mozilla.javascript.lc.type.TypeInfo;
88
import org.mozilla.javascript.lc.type.TypeInfoFactory;
99
import org.mozilla.javascript.lc.type.VariableTypeInfo;
@@ -13,9 +13,8 @@
1313
import org.mozilla.javascript.lc.type.impl.factory.FactoryBase;
1414

1515
/**
16-
* {@link org.mozilla.javascript.lc.type.TypeInfoFactory} implementation with no cache, and no
17-
* generic support, as an example usage of custom type factory via {@link
18-
* org.mozilla.javascript.ContextFactory#setTypeFactoryProvider(Function)}
16+
* {@link TypeInfoFactory} implementation with no cache, and no generic support, as an example usage
17+
* of custom type factory via {@link TypeInfoFactory#associate(ScriptableObject)}
1918
*
2019
* @author ZZZank
2120
*/

0 commit comments

Comments
 (0)