Skip to content

Commit

Permalink
more detailed get trap handling
Browse files Browse the repository at this point in the history
  • Loading branch information
rbri committed Aug 18, 2023
1 parent 5c93f5f commit 6770f69
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 11 deletions.
13 changes: 10 additions & 3 deletions src/org/mozilla/javascript/IdScriptableObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -917,9 +917,16 @@ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id);
if (desc == null) {
if (id instanceof String) {
desc = getBuiltInDescriptor((String) id);
} else if (ScriptRuntime.isSymbol(id)) {
desc = getBuiltInDescriptor(((NativeSymbol) id).getKey());
return getBuiltInDescriptor((String) id);
}

if (ScriptRuntime.isSymbol(id)) {
if (id instanceof SymbolKey) {
NativeSymbol result = new NativeSymbol((SymbolKey) id);
return getBuiltInDescriptor(result.getKey());
}

return getBuiltInDescriptor(((NativeSymbol) id).getKey());
}
}
return desc;
Expand Down
216 changes: 211 additions & 5 deletions src/org/mozilla/javascript/NativeProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;

/**
* This class implements the Proxy object.
Expand Down Expand Up @@ -382,36 +383,169 @@ Object[] getIds(boolean getNonEnumerable, boolean getSymbols) {
return target.getIds(getNonEnumerable, getSymbols);
}

/**
* see
* https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
*/
@Override
public Object get(String name, Scriptable start) {
/*
* 1. Assert: IsPropertyKey(P) is true.
* 2. Let handler be O.[[ProxyHandler]].
* 3. If handler is null, throw a TypeError exception.
* 4. Assert: Type(handler) is Object.
* 5. Let target be O.[[ProxyTarget]].
* 6. Let trap be ? GetMethod(handler, "get").
* 7. If trap is undefined, then
* a. Return ? target.[[Get]](P, Receiver).
* 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
* 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
* 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
* a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
* i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
* b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then
* i. If trapResult is not undefined, throw a TypeError exception.
* 11. Return trapResult.
*/
assertNotRevoked();

Callable trap = getTrap(TRAP_GET);
if (trap != null) {
return callTrap(trap, new Object[] {target, name, this});
Object trapResult = callTrap(trap, new Object[] {target, name, this});

ScriptableObject targetDesc =
target.getOwnPropertyDescriptor(Context.getContext(), name);
if (targetDesc != null
&& !Undefined.isUndefined(targetDesc)
&& Boolean.FALSE.equals(targetDesc.get("configurable"))) {
if (ScriptableObject.isDataDescriptor(targetDesc)
&& Boolean.FALSE.equals(targetDesc.get("writable"))) {
if (!Objects.equals(trapResult, targetDesc.get("value"))) {
throw ScriptRuntime.typeError(
"proxy get has to return the same value as the plain call");
}
}
if (ScriptableObject.isAccessorDescriptor(targetDesc)
&& Undefined.isUndefined(targetDesc.get("get"))) {
if (!Undefined.isUndefined(trapResult)) {
throw ScriptRuntime.typeError(
"proxy get has to return the same value as the plain call");
}
}
}
return trapResult;
}

return ScriptRuntime.getObjectProp(target, name, Context.getContext());
}

/**
* see
* https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
*/
@Override
public Object get(int index, Scriptable start) {
/*
* 1. Assert: IsPropertyKey(P) is true.
* 2. Let handler be O.[[ProxyHandler]].
* 3. If handler is null, throw a TypeError exception.
* 4. Assert: Type(handler) is Object.
* 5. Let target be O.[[ProxyTarget]].
* 6. Let trap be ? GetMethod(handler, "get").
* 7. If trap is undefined, then
* a. Return ? target.[[Get]](P, Receiver).
* 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
* 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
* 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
* a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
* i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
* b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then
* i. If trapResult is not undefined, throw a TypeError exception.
* 11. Return trapResult.
*/
assertNotRevoked();

Callable trap = getTrap(TRAP_GET);
if (trap != null) {
return callTrap(trap, new Object[] {target, index, this});
Object trapResult = callTrap(trap, new Object[] {target, index, this});

ScriptableObject targetDesc =
target.getOwnPropertyDescriptor(Context.getContext(), index);
if (targetDesc != null
&& !Undefined.isUndefined(targetDesc)
&& Boolean.FALSE.equals(targetDesc.get("configurable"))) {
if (ScriptableObject.isDataDescriptor(targetDesc)
&& Boolean.FALSE.equals(targetDesc.get("writable"))) {
if (!Objects.equals(trapResult, targetDesc.get("value"))) {
throw ScriptRuntime.typeError(
"proxy get has to return the same value as the plain call");
}
}
if (ScriptableObject.isAccessorDescriptor(targetDesc)
&& Undefined.isUndefined(targetDesc.get("get"))) {
if (!Undefined.isUndefined(trapResult)) {
throw ScriptRuntime.typeError(
"proxy get has to return the same value as the plain call");
}
}
}
return trapResult;
}

return ScriptRuntime.getObjectIndex(target, index, Context.getContext());
}

/**
* see
* https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver
*/
@Override
public Object get(Symbol key, Scriptable start) {
/*
* 1. Assert: IsPropertyKey(P) is true.
* 2. Let handler be O.[[ProxyHandler]].
* 3. If handler is null, throw a TypeError exception.
* 4. Assert: Type(handler) is Object.
* 5. Let target be O.[[ProxyTarget]].
* 6. Let trap be ? GetMethod(handler, "get").
* 7. If trap is undefined, then
* a. Return ? target.[[Get]](P, Receiver).
* 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
* 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
* 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
* a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
* i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
* b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then
* i. If trapResult is not undefined, throw a TypeError exception.
* 11. Return trapResult.
*/
assertNotRevoked();

Callable trap = getTrap(TRAP_GET);
if (trap != null) {
return callTrap(trap, new Object[] {target, key, this});
Object trapResult = callTrap(trap, new Object[] {target, key, this});

ScriptableObject targetDesc =
target.getOwnPropertyDescriptor(Context.getContext(), key);
if (targetDesc != null
&& !Undefined.isUndefined(targetDesc)
&& Boolean.FALSE.equals(targetDesc.get("configurable"))) {
if (ScriptableObject.isDataDescriptor(targetDesc)
&& Boolean.FALSE.equals(targetDesc.get("writable"))) {
if (!Objects.equals(trapResult, targetDesc.get("value"))) {
throw ScriptRuntime.typeError(
"proxy get has to return the same value as the plain call");
}
}
if (ScriptableObject.isAccessorDescriptor(targetDesc)
&& Undefined.isUndefined(targetDesc.get("get"))) {
if (!Undefined.isUndefined(trapResult)) {
throw ScriptRuntime.typeError(
"proxy get has to return the same value as the plain call");
}
}
}
return trapResult;
}

if (start == this) {
Expand All @@ -421,8 +555,30 @@ public Object get(Symbol key, Scriptable start) {
return symbolScriptableTarget.get(key, start);
}

/**
* https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
*/
@Override
public void put(String name, Scriptable start, Object value) {
/*
* 1. Assert: IsPropertyKey(P) is true.
* 2. Let handler be O.[[ProxyHandler]].
* 3. If handler is null, throw a TypeError exception.
* 4. Assert: Type(handler) is Object.
* 5. Let target be O.[[ProxyTarget]].
* 6. Let trap be ? GetMethod(handler, "set").
* 7. If trap is undefined, then
* a. Return ? target.[[Set]](P, V, Receiver).
* 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).
* 9. If booleanTrapResult is false, return false.
* 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
* 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
* a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
* i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
* b. If IsAccessorDescriptor(targetDesc) is true, then
* i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
* 12. Return true.
*/
assertNotRevoked();

Callable trap = getTrap(TRAP_SET);
Expand All @@ -434,8 +590,30 @@ public void put(String name, Scriptable start, Object value) {
ScriptableObject.putProperty(target, name, value);
}

/**
* https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
*/
@Override
public void put(int index, Scriptable start, Object value) {
/*
* 1. Assert: IsPropertyKey(P) is true.
* 2. Let handler be O.[[ProxyHandler]].
* 3. If handler is null, throw a TypeError exception.
* 4. Assert: Type(handler) is Object.
* 5. Let target be O.[[ProxyTarget]].
* 6. Let trap be ? GetMethod(handler, "set").
* 7. If trap is undefined, then
* a. Return ? target.[[Set]](P, V, Receiver).
* 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).
* 9. If booleanTrapResult is false, return false.
* 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
* 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
* a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
* i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
* b. If IsAccessorDescriptor(targetDesc) is true, then
* i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
* 12. Return true.
*/
assertNotRevoked();

Callable trap = getTrap(TRAP_SET);
Expand All @@ -447,8 +625,30 @@ public void put(int index, Scriptable start, Object value) {
ScriptableObject.putProperty(target, index, value);
}

/**
* https://262.ecma-international.org/12.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver
*/
@Override
public void put(Symbol key, Scriptable start, Object value) {
/*
* 1. Assert: IsPropertyKey(P) is true.
* 2. Let handler be O.[[ProxyHandler]].
* 3. If handler is null, throw a TypeError exception.
* 4. Assert: Type(handler) is Object.
* 5. Let target be O.[[ProxyTarget]].
* 6. Let trap be ? GetMethod(handler, "set").
* 7. If trap is undefined, then
* a. Return ? target.[[Set]](P, V, Receiver).
* 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).
* 9. If booleanTrapResult is false, return false.
* 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
* 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
* a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
* i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
* b. If IsAccessorDescriptor(targetDesc) is true, then
* i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
* 12. Return true.
*/
assertNotRevoked();

Callable trap = getTrap(TRAP_SET);
Expand Down Expand Up @@ -852,9 +1052,15 @@ public Scriptable getPrototype() {
Object handlerProto = callTrap(trap, new Object[] {target});

Scriptable handlerProtoScriptable = Undefined.SCRIPTABLE_UNDEFINED;
if (!Undefined.isUndefined(handlerProto)) {
handlerProtoScriptable = ensureScriptable(handlerProto);
if (handlerProtoScriptable == null
|| Undefined.isUndefined(handlerProto)
|| ScriptRuntime.isSymbol(handlerProto)) {
throw ScriptRuntime.typeErrorById(
"msg.arg.not.object", ScriptRuntime.typeof(handlerProto));
}

handlerProtoScriptable = ensureScriptable(handlerProto);

if (target.isExtensible()) {
return handlerProtoScriptable;
}
Expand Down
5 changes: 2 additions & 3 deletions testsrc/org/mozilla/javascript/tests/es6/NativeProxyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -521,11 +521,10 @@ public void hasHandlerCallsIn() {
+ " _handler = this;\n"
+ " _target = t;\n"
+ " _prop = prop;\n"
// + " return prop in t;\n"
+ " return false;\n"
+ " return prop in t;\n"
+ " }\n"
+ "};\n"
+ "var p = new Proxy(target, handler);\r\n"
+ "var p = new Proxy(target, handler);\n"
+ "'' + (_handler === handler)\n"
+ "+ ' ' + (_target === target)"
+ "+ ' ' + ('attr' === _prop)"
Expand Down

0 comments on commit 6770f69

Please sign in to comment.