-
Notifications
You must be signed in to change notification settings - Fork 863
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SlotMap.compute can lead to deadlock in ThreadSafeSlotMapContainer #1746
Changes from 5 commits
d69afa8
466c6e8
0ba1f30
8681b19
0f4e856
c14ec1c
ad7d0a6
e20ecb0
d350e81
474c3ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -867,8 +867,8 @@ protected void defineOwnProperty( | |
delete(id); // it will be replaced with a slot | ||
} else { | ||
checkPropertyDefinition(desc); | ||
ScriptableObject current = getOwnPropertyDescriptor(cx, key); | ||
checkPropertyChange(name, current, desc); | ||
var slot = queryOrFakeSlot(cx, key); | ||
checkPropertyChangeForSlot(name, slot, desc); | ||
int attr = (info >>> 16); | ||
Object value = getProperty(desc, "value"); | ||
if (value != NOT_FOUND && ((attr & READONLY) == 0 || (attr & PERMANENT) == 0)) { | ||
|
@@ -889,8 +889,8 @@ protected void defineOwnProperty( | |
prototypeValues.delete(id); // it will be replaced with a slot | ||
} else { | ||
checkPropertyDefinition(desc); | ||
ScriptableObject current = getOwnPropertyDescriptor(cx, key); | ||
checkPropertyChange(name, current, desc); | ||
var slot = queryOrFakeSlot(cx, key); | ||
checkPropertyChangeForSlot(name, slot, desc); | ||
int attr = prototypeValues.getAttributes(id); | ||
Object value = getProperty(desc, "value"); | ||
if (value != NOT_FOUND && (attr & READONLY) == 0) { | ||
|
@@ -936,48 +936,82 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { | |
return desc; | ||
} | ||
|
||
private ScriptableObject getBuiltInDescriptor(String name) { | ||
Object value = null; | ||
int attr = EMPTY; | ||
private Slot queryOrFakeSlot(Context cx, Object id) { | ||
var slot = querySlot(cx, id); | ||
if (slot == null) { | ||
if (id instanceof String) { | ||
return getBuiltInSlot((String) id); | ||
} | ||
|
||
if (ScriptRuntime.isSymbol(id)) { | ||
if (id instanceof SymbolKey) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, looking at coverage, nothing from line 957 to 961 seems to get covered either. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one is a little more convoluted: as of right now, I think it's impossible for this code path to be executed, thus it will be challenging to test it. This is because it's only called from Having said that, this doesn't feel right and perhaps it's an issue with current implementation of One could also imagine, someday in the future, someone creating a new built-in I appreciate it's a little theoretical, but wanted to share this and ask for opinions on whether we should address this? Also if addressing this deserves a separate PR or should I add it here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, thanks -- I suppose one way we could deal with this could be to assert, and in the future if someone wants to implement a subclass that needs it, we'd fix it -- although in many cases we are trying to get rid of IdScriptableObject as it doesn't yield the performance benefit it once did. If you're not comfortable with that, however, I'd also be OK leaving this as it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I personally wouldn't mind leaving it as is for now, I don't see an obvious place to assert that wouldn't break existing functionality or be completely redundant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, then I'm satisfied, thanks! |
||
return getBuiltInSlot((SymbolKey) id); | ||
} | ||
|
||
return getBuiltInSlot(((NativeSymbol) id).getKey()); | ||
} | ||
} | ||
return slot; | ||
} | ||
|
||
private ScriptableObject getBuiltInDescriptor(String name) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe getBuiltInDataDescriptor is a better name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, and changed. |
||
Scriptable scope = getParentScope(); | ||
if (scope == null) { | ||
scope = this; | ||
} | ||
|
||
var slot = getBuiltInSlot(name); | ||
return slot == null ? null : buildDataDescriptor(scope, slot.value, slot.getAttributes()); | ||
} | ||
|
||
private Slot getBuiltInSlot(String name) { | ||
Object value = null; | ||
int attr = EMPTY; | ||
|
||
int info = findInstanceIdInfo(name); | ||
if (info != 0) { | ||
int id = (info & 0xFFFF); | ||
value = getInstanceIdValue(id); | ||
attr = (info >>> 16); | ||
return buildDataDescriptor(scope, value, attr); | ||
var slot = new Slot(name, 0, attr); | ||
slot.value = value; | ||
return slot; | ||
} | ||
if (prototypeValues != null) { | ||
int id = prototypeValues.findId(name); | ||
if (id != 0) { | ||
value = prototypeValues.get(id); | ||
attr = prototypeValues.getAttributes(id); | ||
return buildDataDescriptor(scope, value, attr); | ||
var slot = new Slot(name, 0, attr); | ||
slot.value = value; | ||
return slot; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private ScriptableObject getBuiltInDescriptor(Symbol key) { | ||
Object value = null; | ||
int attr = EMPTY; | ||
|
||
Scriptable scope = getParentScope(); | ||
if (scope == null) { | ||
scope = this; | ||
} | ||
|
||
var slot = getBuiltInSlot(key); | ||
return slot == null ? null : buildDataDescriptor(scope, slot.value, slot.getAttributes()); | ||
} | ||
|
||
private Slot getBuiltInSlot(Symbol key) { | ||
Object value = null; | ||
int attr = EMPTY; | ||
|
||
if (prototypeValues != null) { | ||
int id = prototypeValues.findId(key); | ||
if (id != 0) { | ||
value = prototypeValues.get(id); | ||
attr = prototypeValues.getAttributes(id); | ||
return buildDataDescriptor(scope, value, attr); | ||
var slot = new Slot(key, 0, attr); | ||
slot.value = value; | ||
return slot; | ||
} | ||
} | ||
return null; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.mozilla.javascript.tests.es6; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
import org.junit.Test; | ||
import org.mozilla.javascript.Context; | ||
import org.mozilla.javascript.ContextFactory; | ||
import org.mozilla.javascript.NativeObject; | ||
import org.mozilla.javascript.ScriptableObject; | ||
|
||
public class DeadlockReproTest { | ||
@Test | ||
public void redefinePropertyWithThreadSafeSlotMap() { | ||
final ContextFactory factory = | ||
new ContextFactory() { | ||
@Override | ||
protected boolean hasFeature(Context cx, int featureIndex) { | ||
if (featureIndex == Context.FEATURE_THREAD_SAFE_OBJECTS) { | ||
return true; | ||
} | ||
return super.hasFeature(cx, featureIndex); | ||
} | ||
}; | ||
|
||
try (Context cx = factory.enterContext()) { | ||
cx.setLanguageVersion(Context.VERSION_ES6); | ||
ScriptableObject scope = cx.initStandardObjects(); | ||
|
||
scope.put("o", scope, new NativeObject()); | ||
final String script = | ||
"Object.defineProperty(o, 'test', {value: '1', configurable: !0});" | ||
+ "Object.defineProperty(o, 'test', {value: 2});" | ||
+ "o.test"; | ||
|
||
var result = cx.evaluateString(scope, script, "myScript", 1, null); | ||
|
||
assertEquals(2, result); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a matter of taste - but there is no else needed because of the return
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.