/src/hermes/lib/VM/JSLib/Proxy.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | //===----------------------------------------------------------------------===// |
9 | | /// \file |
10 | | /// ES9 26.2 Proxy Objects |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "JSLibInternal.h" |
14 | | |
15 | | #include "hermes/VM/JSCallableProxy.h" |
16 | | #include "hermes/VM/StackFrame-inline.h" |
17 | | |
18 | | namespace hermes { |
19 | | namespace vm { |
20 | | |
21 | | namespace { |
22 | | |
23 | | // Property storage slots. This implementation for storing internal |
24 | | // properties is not a great pattern, and may change. |
25 | | enum ProxySlotIndexes { revocableProxy, COUNT }; |
26 | | |
27 | | SmallHermesValue getRevocableProxySlot( |
28 | | NativeFunction *revoker, |
29 | 0 | Runtime &runtime) { |
30 | 0 | return NativeFunction::getAdditionalSlotValue( |
31 | 0 | revoker, runtime, ProxySlotIndexes::revocableProxy); |
32 | 0 | } |
33 | | |
34 | | void setRevocableProxySlot( |
35 | | NativeFunction *revoker, |
36 | | Runtime &runtime, |
37 | 0 | SmallHermesValue value) { |
38 | 0 | NativeFunction::setAdditionalSlotValue( |
39 | 0 | revoker, runtime, ProxySlotIndexes::revocableProxy, value); |
40 | 0 | } |
41 | | |
42 | | // This is shared code between the proxy constructor and the native |
43 | | // Proxy.revocable factory. |
44 | | // \param proxy is the newly created but not yet initialized object |
45 | | // used as the constructor's this. |
46 | | CallResult<Handle<JSObject>> proxyCreate( |
47 | | Runtime &runtime, |
48 | | Handle<JSObject> target, |
49 | | Handle<JSObject> handler, |
50 | 0 | Handle<JSObject> proxy) { |
51 | | // 1. If Type(target) is not Object, throw a TypeError exception. |
52 | 0 | if (!target) { |
53 | 0 | return runtime.raiseTypeError("new Proxy target must be an Object"); |
54 | 0 | } |
55 | | // 3. If Type(handler) is not Object, throw a TypeError exception. |
56 | 0 | if (!handler) { |
57 | 0 | return runtime.raiseTypeError("new Proxy handler must be an Object"); |
58 | 0 | } |
59 | | // 5. Let P be a newly created object. |
60 | | // 6. Set P’s essential internal methods (except for [[Call]] and |
61 | | // [[Construct]]) to the definitions specified in 9.5. |
62 | | // 7. If IsCallable(target) is true, then |
63 | 0 | if (vmisa<Callable>(*target)) { |
64 | | // a. Set P.[[Call]] as specified in 9.5.12. |
65 | | // b. If IsConstructor(target) is true, then |
66 | | // i. Set P.[[Construct]] as specified in 9.5.13. |
67 | | // We need to throw away the object passed as this, so we can create |
68 | | // a CallableProxy instead. Simply being a CallableProxy has the |
69 | | // effect of setting the [[Call]] and [[Construct]] internal methods. |
70 | 0 | proxy = runtime.makeHandle(JSCallableProxy::create(runtime)); |
71 | 0 | } |
72 | | |
73 | | // 8. Set the [[ProxyTarget]] internal slot of P to target. |
74 | | // 9. Set the [[ProxyHandler]] internal slot of P to handler. |
75 | |
|
76 | 0 | JSProxy::setTargetAndHandler(proxy, runtime, target, handler); |
77 | | |
78 | | // Return P. |
79 | 0 | return proxy; |
80 | 0 | } |
81 | | |
82 | | } // namespace |
83 | | |
84 | | CallResult<HermesValue> |
85 | 0 | proxyConstructor(void *, Runtime &runtime, NativeArgs args) { |
86 | | // 1. If NewTarget is undefined, throw a TypeError exception. |
87 | 0 | if (!args.isConstructorCall()) { |
88 | 0 | return runtime.raiseTypeError( |
89 | 0 | "Proxy() called in function context instead of constructor"); |
90 | 0 | } |
91 | | // 2. Return ? ProxyCreate(target, handler). |
92 | 0 | auto proxyRes = proxyCreate( |
93 | 0 | runtime, |
94 | 0 | args.dyncastArg<JSObject>(0), |
95 | 0 | args.dyncastArg<JSObject>(1), |
96 | 0 | args.vmcastThis<JSProxy>()); |
97 | 0 | if (proxyRes == ExecutionStatus::EXCEPTION) { |
98 | 0 | return ExecutionStatus::EXCEPTION; |
99 | 0 | } |
100 | 0 | return proxyRes->getHermesValue(); |
101 | 0 | } |
102 | | |
103 | | CallResult<HermesValue> |
104 | 0 | proxyRevocationSteps(void *, Runtime &runtime, NativeArgs args) { |
105 | | // 1. Let p be F.[[RevocableProxy]]. |
106 | 0 | auto revoker = vmcast<NativeFunction>( |
107 | 0 | runtime.getCurrentFrame()->getCalleeClosureUnsafe()); |
108 | 0 | SmallHermesValue proxyVal = getRevocableProxySlot(revoker, runtime); |
109 | | // 2. If p is null, return undefined. |
110 | 0 | if (proxyVal.isNull()) { |
111 | 0 | return HermesValue::encodeUndefinedValue(); |
112 | 0 | } |
113 | | // 3. Set F.[[RevocableProxy]] to null. |
114 | 0 | setRevocableProxySlot(revoker, runtime, SmallHermesValue::encodeNullValue()); |
115 | | // 4. Assert: p is a Proxy object. |
116 | 0 | JSObject *proxy = vmcast<JSObject>(proxyVal.getObject(runtime)); |
117 | 0 | assert(proxy->isProxyObject() && "[[RevocableProxy]] is not a Proxy"); |
118 | | // 5. Set p.[[ProxyTarget]] to null. |
119 | | // 6. Set p.[[ProxyHandler]] to null. |
120 | 0 | JSProxy::setTargetAndHandler( |
121 | 0 | runtime.makeHandle(proxy), |
122 | 0 | runtime, |
123 | 0 | runtime.makeNullHandle<JSObject>(), |
124 | 0 | runtime.makeNullHandle<JSObject>()); |
125 | | // 7. Return undefined. |
126 | 0 | return HermesValue::encodeUndefinedValue(); |
127 | 0 | } |
128 | | |
129 | | CallResult<HermesValue> |
130 | 0 | proxyRevocable(void *, Runtime &runtime, NativeArgs args) { |
131 | | // 1. Let p be ? ProxyCreate(target, handler). |
132 | 0 | CallResult<Handle<JSObject>> proxyRes = proxyCreate( |
133 | 0 | runtime, |
134 | 0 | args.dyncastArg<JSObject>(0), |
135 | 0 | args.dyncastArg<JSObject>(1), |
136 | 0 | runtime.makeHandle(vm::JSProxy::create(runtime))); |
137 | 0 | if (proxyRes == ExecutionStatus::EXCEPTION) { |
138 | 0 | return ExecutionStatus::EXCEPTION; |
139 | 0 | } |
140 | | // 2. Let steps be the algorithm steps defined in Proxy Revocation Functions. |
141 | | // 3. Let revoker be CreateBuiltinFunction(steps, « [[RevocableProxy]] »). |
142 | 0 | Handle<NativeFunction> revoker = NativeFunction::createWithoutPrototype( |
143 | 0 | runtime, |
144 | 0 | nullptr, |
145 | 0 | proxyRevocationSteps, |
146 | 0 | Predefined::getSymbolID(Predefined::emptyString), |
147 | 0 | 0, |
148 | 0 | ProxySlotIndexes::COUNT); |
149 | | // 4. Set revoker.[[RevocableProxy]] to p. |
150 | 0 | auto shv = |
151 | 0 | SmallHermesValue::encodeHermesValue(proxyRes->getHermesValue(), runtime); |
152 | 0 | setRevocableProxySlot(*revoker, runtime, shv); |
153 | | // 5. Let result be ObjectCreate(%ObjectPrototype%). |
154 | 0 | Handle<JSObject> result = runtime.makeHandle(JSObject::create(runtime)); |
155 | | // 6. Perform CreateDataProperty(result, "proxy", p). |
156 | 0 | auto res1 = JSObject::putNamed_RJS( |
157 | 0 | result, runtime, Predefined::getSymbolID(Predefined::proxy), *proxyRes); |
158 | 0 | if (res1 == ExecutionStatus::EXCEPTION) { |
159 | 0 | return ExecutionStatus::EXCEPTION; |
160 | 0 | } |
161 | 0 | assert(*res1 && "Failed to set proxy on Proxy.revocable return"); |
162 | | // 7. Perform CreateDataProperty(result, "revoke", revoker). |
163 | 0 | auto res2 = JSObject::putNamed_RJS( |
164 | 0 | result, runtime, Predefined::getSymbolID(Predefined::revoke), revoker); |
165 | 0 | if (res2 == ExecutionStatus::EXCEPTION) { |
166 | 0 | return ExecutionStatus::EXCEPTION; |
167 | 0 | } |
168 | 0 | assert(*res2 && "Failed to set revoke on Proxy.revocable return"); |
169 | | // 8. Return result. |
170 | 0 | return result.getHermesValue(); |
171 | 0 | } |
172 | | |
173 | 99 | Handle<JSObject> createProxyConstructor(Runtime &runtime) { |
174 | 99 | Handle<NativeConstructor> cons = defineSystemConstructor<JSProxy>( |
175 | 99 | runtime, |
176 | 99 | Predefined::getSymbolID(Predefined::Proxy), |
177 | 99 | proxyConstructor, |
178 | 99 | runtime.makeNullHandle<JSObject>(), |
179 | 99 | 2, |
180 | 99 | CellKind::JSProxyKind); |
181 | | |
182 | 99 | defineMethod( |
183 | 99 | runtime, |
184 | 99 | cons, |
185 | 99 | Predefined::getSymbolID(Predefined::revocable), |
186 | 99 | nullptr, |
187 | 99 | proxyRevocable, |
188 | 99 | 2); |
189 | | |
190 | 99 | return cons; |
191 | 99 | } |
192 | | |
193 | | } // namespace vm |
194 | | } // namespace hermes |