Skip to content

Commit 0a0e04d

Browse files
committed
[GR-31483] Writing a foreign array index that is not insertable or writable should throw in strict mode.
PullRequest: js/2017
2 parents 80ffc81 + 6560d55 commit 0a0e04d

File tree

3 files changed

+212
-24
lines changed

3 files changed

+212
-24
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.js.test.interop;
42+
43+
import static org.junit.Assert.assertEquals;
44+
import static org.junit.Assert.assertFalse;
45+
import static org.junit.Assert.assertTrue;
46+
import static org.junit.Assert.fail;
47+
48+
import java.util.ArrayList;
49+
import java.util.List;
50+
51+
import org.graalvm.polyglot.Context;
52+
import org.graalvm.polyglot.HostAccess;
53+
import org.graalvm.polyglot.PolyglotException;
54+
import org.graalvm.polyglot.Value;
55+
import org.junit.Test;
56+
57+
import com.oracle.truffle.js.runtime.JSContextOptions;
58+
59+
public class ForeignObjectNotWritableTest {
60+
61+
@Test
62+
public void testJavaArrayPush() {
63+
try (Context context = Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowExperimentalOptions(true).//
64+
option(JSContextOptions.FOREIGN_OBJECT_PROTOTYPE_NAME, "true").build()) {
65+
Value b = context.getBindings("js");
66+
67+
b.putMember("array", new Object[]{"a"});
68+
69+
assertThrowsTypeError(() -> context.eval("js", "array.push('b');"));
70+
assertEquals(1, b.getMember("array").getArraySize());
71+
}
72+
}
73+
74+
@Test
75+
public void testJavaArrayOOB() {
76+
try (Context context = Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowExperimentalOptions(true).build()) {
77+
Value b = context.getBindings("js");
78+
79+
b.putMember("array", new Object[]{"a"});
80+
81+
assertThrowsTypeError(() -> context.eval("js", "'use strict'; array[1] = 'b';"));
82+
assertEquals(1, b.getMember("array").getArraySize());
83+
84+
context.eval("js", "array[1] = 'b';");
85+
assertEquals(1, b.getMember("array").getArraySize());
86+
}
87+
}
88+
89+
@Test
90+
public void testJavaArrayIncompatibleType() {
91+
try (Context context = Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowExperimentalOptions(true).build()) {
92+
Value b = context.getBindings("js");
93+
94+
b.putMember("array", new int[]{42, 43, 44});
95+
96+
assertThrowsTypeError(() -> context.eval("js", "'use strict'; array[2] = {};"));
97+
context.eval("js", "array[2] = {};");
98+
99+
assertThrowsTypeError(() -> context.eval("js", "'use strict'; array[4] = {};"));
100+
context.eval("js", "array[4] = {};");
101+
}
102+
}
103+
104+
@Test
105+
public void testJavaListOOB() {
106+
try (Context context = Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowExperimentalOptions(true).build()) {
107+
Value b = context.getBindings("js");
108+
109+
List<Object> list = new ArrayList<>();
110+
list.add("a");
111+
b.putMember("array", list);
112+
113+
context.eval("js", "'use strict'; array[1] = 'b';");
114+
assertEquals(2, b.getMember("array").getArraySize());
115+
context.eval("js", "array[2] = 'c';");
116+
assertEquals(3, b.getMember("array").getArraySize());
117+
118+
assertThrowsTypeError(() -> context.eval("js", "'use strict'; array[4] = 'd';"));
119+
context.eval("js", "array[4] = 'd';");
120+
assertEquals(3, b.getMember("array").getArraySize());
121+
}
122+
}
123+
124+
@Test
125+
public void testJavaObjectSetMember() {
126+
try (Context context = Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowExperimentalOptions(true).build()) {
127+
Value b = context.getBindings("js");
128+
129+
Object list = new Object();
130+
b.putMember("object", list);
131+
132+
assertThrowsTypeError(() -> context.eval("js", "'use strict'; object['new'] = 'b';"));
133+
134+
context.eval("js", "object['new'] = 'b';");
135+
assertFalse(b.getMember("object").hasMember("new"));
136+
}
137+
}
138+
139+
@Test
140+
public void testJavaObjectFieldIncompatibleType() {
141+
try (Context context = Context.newBuilder("js").allowHostAccess(HostAccess.ALL).allowExperimentalOptions(true).build()) {
142+
Value b = context.getBindings("js");
143+
144+
Object obj = new ObjectWithField();
145+
b.putMember("object", obj);
146+
147+
assertTrue(b.getMember("object").hasMember("field"));
148+
149+
assertThrowsTypeError(() -> context.eval("js", "'use strict'; object['field'] = 'b';"));
150+
context.eval("js", "object['field'] = 'b';");
151+
152+
context.eval("js", "'use strict'; object['field'] = 42;");
153+
assertEquals(42, b.getMember("object").getMember("field").asInt());
154+
155+
assertThrowsTypeError(() -> context.eval("js", "'use strict'; object.field = 'b';"));
156+
context.eval("js", "object.field = 'b';");
157+
158+
context.eval("js", "'use strict'; object.field = 43;");
159+
assertEquals(43, b.getMember("object").getMember("field").asInt());
160+
}
161+
}
162+
163+
public static class ObjectWithField {
164+
public int field;
165+
}
166+
167+
private static void assertThrowsTypeError(Runnable runnable) {
168+
try {
169+
runnable.run();
170+
fail("Expected TypeError");
171+
} catch (PolyglotException e) {
172+
assertTrue(e.isGuestException());
173+
assertTrue(e.getMessage().startsWith("TypeError"));
174+
}
175+
}
176+
177+
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/access/PropertySetNode.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,8 +1099,10 @@ private boolean performWriteMember(Object truffleObject, Object value, PropertyS
10991099
try {
11001100
interop.writeHashEntry(truffleObject, stringKey, value);
11011101
} catch (UnknownKeyException | UnsupportedMessageException | UnsupportedTypeException e) {
1102-
errorBranch.enter();
1103-
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeHashEntry", this);
1102+
if (root.isStrict) {
1103+
errorBranch.enter();
1104+
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeHashEntry", this);
1105+
}
11041106
}
11051107
}
11061108

@@ -1109,27 +1111,34 @@ private boolean performWriteMember(Object truffleObject, Object value, PropertyS
11091111
return true;
11101112
}
11111113
}
1112-
if (optimistic) {
1114+
// strict mode always throws if the member is not writable
1115+
if (root.isStrict || optimistic) {
11131116
try {
11141117
interop.writeMember(truffleObject, stringKey, value);
1115-
} catch (UnknownIdentifierException e) {
1116-
CompilerDirectives.transferToInterpreterAndInvalidate();
1117-
optimistic = false;
1118-
} catch (UnsupportedTypeException | UnsupportedMessageException e) {
1119-
errorBranch.enter();
1120-
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeMember", stringKey, this);
1118+
return true;
1119+
} catch (UnknownIdentifierException | UnsupportedTypeException | UnsupportedMessageException e) {
1120+
if (root.isStrict) {
1121+
errorBranch.enter();
1122+
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeMember", stringKey, this);
1123+
} else if (e instanceof UnknownIdentifierException) {
1124+
CompilerDirectives.transferToInterpreterAndInvalidate();
1125+
optimistic = false;
1126+
}
1127+
return false;
11211128
}
11221129
} else {
1130+
assert !root.isStrict;
11231131
if (interop.isMemberWritable(truffleObject, stringKey)) {
11241132
try {
11251133
interop.writeMember(truffleObject, stringKey, value);
1134+
return true;
11261135
} catch (UnknownIdentifierException | UnsupportedTypeException | UnsupportedMessageException e) {
1127-
errorBranch.enter();
1128-
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeMember", stringKey, this);
1136+
return false;
11291137
}
1138+
} else {
1139+
return false;
11301140
}
11311141
}
1132-
return true;
11331142
}
11341143

11351144
// in nashorn-compat mode, `javaObj.xyz = a` can mean `javaObj.setXyz(a)`.

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/access/WriteElementNode.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,18 +1850,20 @@ protected void executeWithTargetAndIndexUnguarded(Object target, Object index, O
18501850
if (interop.hasArrayElements(truffleObject) && keyInterop.fitsInLong(convertedKey)) {
18511851
try {
18521852
interop.writeArrayElement(truffleObject, keyInterop.asLong(convertedKey), exportedValue);
1853-
} catch (InvalidArrayIndexException e) {
1854-
// do nothing
1855-
} catch (UnsupportedTypeException | UnsupportedMessageException e) {
1856-
errorBranch.enter();
1857-
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeArrayElement", this);
1853+
} catch (InvalidArrayIndexException | UnsupportedTypeException | UnsupportedMessageException e) {
1854+
if (root.isStrict) {
1855+
errorBranch.enter();
1856+
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeArrayElement", this);
1857+
}
18581858
}
18591859
} else if (root.context.getContextOptions().hasForeignHashProperties() && interop.hasHashEntries(truffleObject)) {
18601860
try {
18611861
interop.writeHashEntry(truffleObject, convertedKey, exportedValue);
18621862
} catch (UnknownKeyException | UnsupportedMessageException | UnsupportedTypeException e) {
1863-
errorBranch.enter();
1864-
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeHashEntry", this);
1863+
if (root.isStrict) {
1864+
errorBranch.enter();
1865+
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeHashEntry", this);
1866+
}
18651867
}
18661868
} else {
18671869
String propertyKey = toStringNode.executeString(convertedKey);
@@ -1872,11 +1874,11 @@ protected void executeWithTargetAndIndexUnguarded(Object target, Object index, O
18721874
}
18731875
try {
18741876
interop.writeMember(truffleObject, propertyKey, exportedValue);
1875-
} catch (UnknownIdentifierException e) {
1876-
// do nothing
1877-
} catch (UnsupportedTypeException | UnsupportedMessageException e) {
1878-
errorBranch.enter();
1879-
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeMember", this);
1877+
} catch (UnknownIdentifierException | UnsupportedTypeException | UnsupportedMessageException e) {
1878+
if (root.isStrict) {
1879+
errorBranch.enter();
1880+
throw Errors.createTypeErrorInteropException(truffleObject, e, "writeMember", this);
1881+
}
18801882
}
18811883
}
18821884
}

0 commit comments

Comments
 (0)