builtins-proxy-gen.cc 15.8 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/builtins/builtins-proxy-gen.h"
6 7 8 9
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"

10
#include "src/logging/counters.h"
11
#include "src/objects/js-proxy.h"
12
#include "src/objects/objects-inl.h"
13

14 15
#include "torque-generated/exported-macros-assembler-tq.h"

16 17 18
namespace v8 {
namespace internal {

19
TNode<JSProxy> ProxiesCodeStubAssembler::AllocateProxy(
20 21
    TNode<Context> context, TNode<JSReceiver> target,
    TNode<JSReceiver> handler) {
22
  TVARIABLE(Map, map);
23

24 25
  Label callable_target(this), constructor_target(this), none_target(this),
      create_proxy(this);
26

27
  TNode<NativeContext> nativeContext = LoadNativeContext(context);
28

29
  Branch(IsCallable(target), &callable_target, &none_target);
30

31 32 33 34 35
  BIND(&callable_target);
  {
    // Every object that is a constructor is implicitly callable
    // so it's okay to nest this check here
    GotoIf(IsConstructor(target), &constructor_target);
36
    map = CAST(
37 38
        LoadContextElement(nativeContext, Context::PROXY_CALLABLE_MAP_INDEX));
    Goto(&create_proxy);
39
  }
40 41
  BIND(&constructor_target);
  {
42 43
    map = CAST(LoadContextElement(nativeContext,
                                  Context::PROXY_CONSTRUCTOR_MAP_INDEX));
44
    Goto(&create_proxy);
45
  }
46 47
  BIND(&none_target);
  {
48
    map = CAST(LoadContextElement(nativeContext, Context::PROXY_MAP_INDEX));
49 50 51 52
    Goto(&create_proxy);
  }

  BIND(&create_proxy);
53
  TNode<HeapObject> proxy = Allocate(JSProxy::kSize);
54 55
  StoreMapNoWriteBarrier(proxy, map.value());
  StoreObjectFieldRoot(proxy, JSProxy::kPropertiesOrHashOffset,
56
                       RootIndex::kEmptyPropertyDictionary);
57 58 59
  StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kTargetOffset, target);
  StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kHandlerOffset, handler);

60
  return CAST(proxy);
61 62
}

63 64 65 66
TNode<Context> ProxiesCodeStubAssembler::CreateProxyRevokeFunctionContext(
    TNode<JSProxy> proxy, TNode<NativeContext> native_context) {
  const TNode<Context> context =
      AllocateSyntheticFunctionContext(native_context, kProxyContextLength);
67
  StoreContextElementNoWriteBarrier(context, kProxySlot, proxy);
68 69 70
  return context;
}

71 72
TNode<JSFunction> ProxiesCodeStubAssembler::AllocateProxyRevokeFunction(
    TNode<Context> context, TNode<JSProxy> proxy) {
73
  const TNode<NativeContext> native_context = LoadNativeContext(context);
74

75
  const TNode<Context> proxy_context =
76
      CreateProxyRevokeFunctionContext(proxy, native_context);
77 78
  const TNode<Map> revoke_map = CAST(LoadContextElement(
      native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX));
79
  const TNode<SharedFunctionInfo> revoke_info = ProxyRevokeSharedFunConstant();
80 81 82

  return AllocateFunctionWithMapAndContext(revoke_map, revoke_info,
                                           proxy_context);
83 84
}

85
TF_BUILTIN(CallProxy, ProxiesCodeStubAssembler) {
86 87
  TNode<Int32T> argc =
      UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
88 89
  TNode<IntPtrT> argc_ptr = ChangeInt32ToIntPtr(argc);
  TNode<JSProxy> proxy = CAST(Parameter(Descriptor::kFunction));
90
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
91 92 93

  CSA_ASSERT(this, IsCallable(proxy));

94
  PerformStackCheck(context);
95

96
  Label throw_proxy_handler_revoked(this, Label::kDeferred),
97
      trap_undefined(this);
98 99

  // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
100 101
  TNode<HeapObject> handler =
      CAST(LoadObjectField(proxy, JSProxy::kHandlerOffset));
102 103

  // 2. If handler is null, throw a TypeError exception.
104
  CSA_ASSERT(this, IsNullOrJSReceiver(handler));
105
  GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
106 107 108 109 110

  // 3. Assert: Type(handler) is Object.
  CSA_ASSERT(this, IsJSReceiver(handler));

  // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
111
  TNode<Object> target = LoadObjectField(proxy, JSProxy::kTargetOffset);
112 113 114

  // 5. Let trap be ? GetMethod(handler, "apply").
  // 6. If trap is undefined, then
115
  Handle<Name> trap_name = factory()->apply_string();
116
  TNode<Object> trap = GetMethod(context, handler, trap_name, &trap_undefined);
117

118
  CodeStubArguments args(this, argc_ptr);
119
  TNode<Object> receiver = args.GetReceiver();
120

121
  // 7. Let argArray be CreateArrayFromList(argumentsList).
122
  TNode<JSArray> array =
123 124 125
      EmitFastNewAllArguments(UncheckedCast<Context>(context),
                              UncheckedCast<RawPtrT>(LoadFramePointer()),
                              UncheckedCast<IntPtrT>(argc_ptr));
126

127
  // 8. Return Call(trap, handler, «target, thisArgument, argArray»).
128
  TNode<Object> result = Call(context, trap, handler, target, receiver, array);
129
  args.PopAndReturn(result);
130 131 132 133 134 135 136 137

  BIND(&trap_undefined);
  {
    // 6.a. Return Call(target, thisArgument, argumentsList).
    TailCallStub(CodeFactory::Call(isolate()), context, target, argc);
  }

  BIND(&throw_proxy_handler_revoked);
138 139 140 141
  { ThrowTypeError(context, MessageTemplate::kProxyRevoked, "apply"); }
}

TF_BUILTIN(ConstructProxy, ProxiesCodeStubAssembler) {
142 143
  TNode<Int32T> argc =
      UncheckedCast<Int32T>(Parameter(Descriptor::kActualArgumentsCount));
144
  TNode<IntPtrT> argc_ptr = ChangeInt32ToIntPtr(argc);
145 146 147
  TNode<JSProxy> proxy = CAST(Parameter(Descriptor::kTarget));
  TNode<Object> new_target = CAST(Parameter(Descriptor::kNewTarget));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
148 149 150 151 152 153 154

  CSA_ASSERT(this, IsCallable(proxy));

  Label throw_proxy_handler_revoked(this, Label::kDeferred),
      trap_undefined(this), not_an_object(this, Label::kDeferred);

  // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
155 156
  TNode<HeapObject> handler =
      CAST(LoadObjectField(proxy, JSProxy::kHandlerOffset));
157 158 159

  // 2. If handler is null, throw a TypeError exception.
  CSA_ASSERT(this, IsNullOrJSReceiver(handler));
160
  GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
161 162 163 164 165

  // 3. Assert: Type(handler) is Object.
  CSA_ASSERT(this, IsJSReceiver(handler));

  // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
166
  TNode<Object> target = LoadObjectField(proxy, JSProxy::kTargetOffset);
167 168 169 170

  // 5. Let trap be ? GetMethod(handler, "construct").
  // 6. If trap is undefined, then
  Handle<Name> trap_name = factory()->construct_string();
171
  TNode<Object> trap = GetMethod(context, handler, trap_name, &trap_undefined);
172 173 174 175

  CodeStubArguments args(this, argc_ptr);

  // 7. Let argArray be CreateArrayFromList(argumentsList).
176
  TNode<JSArray> array =
177 178 179
      EmitFastNewAllArguments(UncheckedCast<Context>(context),
                              UncheckedCast<RawPtrT>(LoadFramePointer()),
                              UncheckedCast<IntPtrT>(argc_ptr));
180 181

  // 8. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
182 183
  TNode<Object> new_obj =
      Call(context, trap, handler, target, array, new_target);
184 185 186

  // 9. If Type(newObj) is not Object, throw a TypeError exception.
  GotoIf(TaggedIsSmi(new_obj), &not_an_object);
187
  GotoIfNot(IsJSReceiver(CAST(new_obj)), &not_an_object);
188 189 190 191 192 193 194 195 196 197

  // 10. Return newObj.
  args.PopAndReturn(new_obj);

  BIND(&not_an_object);
  {
    ThrowTypeError(context, MessageTemplate::kProxyConstructNonObject, new_obj);
  }

  BIND(&trap_undefined);
198
  {
199
    // 6.a. Assert: target has a [[Construct]] internal method.
200
    CSA_ASSERT(this, IsConstructor(CAST(target)));
201 202 203 204

    // 6.b. Return ? Construct(target, argumentsList, newTarget).
    TailCallStub(CodeFactory::Construct(isolate()), context, target, new_target,
                 argc);
205
  }
206 207 208

  BIND(&throw_proxy_handler_revoked);
  { ThrowTypeError(context, MessageTemplate::kProxyRevoked, "construct"); }
209 210
}

211 212 213
void ProxiesCodeStubAssembler::CheckGetSetTrapResult(
    TNode<Context> context, TNode<JSReceiver> target, TNode<JSProxy> proxy,
    TNode<Name> name, TNode<Object> trap_result,
214
    JSProxy::AccessKind access_kind) {
215
  // TODO(mslekova): Think of a better name for the trap_result param.
216
  TNode<Map> map = LoadMap(target);
217 218 219
  TVARIABLE(Object, var_value);
  TVARIABLE(Uint32T, var_details);
  TVARIABLE(Object, var_raw_value);
220

221 222
  Label if_found_value(this), check_in_runtime(this, Label::kDeferred),
      check_passed(this);
223

224
  GotoIfNot(IsUniqueNameNoIndex(name), &check_in_runtime);
225
  TNode<Uint16T> instance_type = LoadInstanceType(target);
226 227
  TryGetOwnProperty(context, target, target, map, instance_type, name,
                    &if_found_value, &var_value, &var_details, &var_raw_value,
228
                    &check_passed, &check_in_runtime, kReturnAccessorPair);
229 230 231 232 233 234 235 236 237 238 239

  BIND(&if_found_value);
  {
    Label throw_non_configurable_data(this, Label::kDeferred),
        throw_non_configurable_accessor(this, Label::kDeferred),
        check_accessor(this), check_data(this);

    // If targetDesc is not undefined and targetDesc.[[Configurable]] is
    // false, then:
    GotoIfNot(IsSetWord32(var_details.value(),
                          PropertyDetails::kAttributesDontDeleteMask),
240
              &check_passed);
241 242 243 244 245 246 247

    // If IsDataDescriptor(targetDesc) is true and
    // targetDesc.[[Writable]] is false, then:
    BranchIfAccessorPair(var_raw_value.value(), &check_accessor, &check_data);

    BIND(&check_data);
    {
248 249
      TNode<BoolT> read_only = IsSetWord32(
          var_details.value(), PropertyDetails::kAttributesReadOnlyMask);
250
      GotoIfNot(read_only, &check_passed);
251 252 253

      // If SameValue(trapResult, targetDesc.[[Value]]) is false,
      // throw a TypeError exception.
254
      BranchIfSameValue(trap_result, var_value.value(), &check_passed,
255
                        &throw_non_configurable_data);
256 257 258 259
    }

    BIND(&check_accessor);
    {
260
      TNode<HeapObject> accessor_pair = CAST(var_raw_value.value());
261 262 263 264 265

      if (access_kind == JSProxy::kGet) {
        Label continue_check(this, Label::kDeferred);
        // 10.b. If IsAccessorDescriptor(targetDesc) is true and
        // targetDesc.[[Get]] is undefined, then:
266
        TNode<Object> getter =
267 268 269 270 271
            LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
        // Here we check for null as well because if the getter was never
        // defined it's set as null.
        GotoIf(IsUndefined(getter), &continue_check);
        GotoIf(IsNull(getter), &continue_check);
272
        Goto(&check_passed);
273 274 275 276 277 278 279

        // 10.b.i. If trapResult is not undefined, throw a TypeError exception.
        BIND(&continue_check);
        GotoIfNot(IsUndefined(trap_result), &throw_non_configurable_accessor);
      } else {
        // 11.b.i. If targetDesc.[[Set]] is undefined, throw a TypeError
        // exception.
280
        TNode<Object> setter =
281 282 283 284
            LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
        GotoIf(IsUndefined(setter), &throw_non_configurable_accessor);
        GotoIf(IsNull(setter), &throw_non_configurable_accessor);
      }
285
      Goto(&check_passed);
286 287 288 289
    }

    BIND(&throw_non_configurable_data);
    {
290 291 292 293 294 295
      if (access_kind == JSProxy::kGet) {
        ThrowTypeError(context, MessageTemplate::kProxyGetNonConfigurableData,
                       name, var_value.value(), trap_result);
      } else {
        ThrowTypeError(context, MessageTemplate::kProxySetFrozenData, name);
      }
296 297 298 299
    }

    BIND(&throw_non_configurable_accessor);
    {
300 301 302 303 304 305 306
      if (access_kind == JSProxy::kGet) {
        ThrowTypeError(context,
                       MessageTemplate::kProxyGetNonConfigurableAccessor, name,
                       trap_result);
      } else {
        ThrowTypeError(context, MessageTemplate::kProxySetFrozenAccessor, name);
      }
307
    }
308 309 310 311 312 313 314 315 316

    BIND(&check_in_runtime);
    {
      CallRuntime(Runtime::kCheckProxyGetSetTrapResult, context, name, target,
                  trap_result, SmiConstant(access_kind));
      Goto(&check_passed);
    }

    BIND(&check_passed);
317 318 319
  }
}

320 321 322 323
void ProxiesCodeStubAssembler::CheckHasTrapResult(TNode<Context> context,
                                                  TNode<JSReceiver> target,
                                                  TNode<JSProxy> proxy,
                                                  TNode<Name> name) {
324
  TNode<Map> target_map = LoadMap(target);
325 326 327
  TVARIABLE(Object, var_value);
  TVARIABLE(Uint32T, var_details);
  TVARIABLE(Object, var_raw_value);
328

329
  Label if_found_value(this, Label::kDeferred),
330
      throw_non_configurable(this, Label::kDeferred),
331 332
      throw_non_extensible(this, Label::kDeferred), check_passed(this),
      check_in_runtime(this, Label::kDeferred);
333 334

  // 9.a. Let targetDesc be ? target.[[GetOwnProperty]](P).
335
  GotoIfNot(IsUniqueNameNoIndex(name), &check_in_runtime);
336
  TNode<Uint16T> instance_type = LoadInstanceType(target);
337 338
  TryGetOwnProperty(context, target, target, target_map, instance_type, name,
                    &if_found_value, &var_value, &var_details, &var_raw_value,
339
                    &check_passed, &check_in_runtime, kReturnAccessorPair);
340 341 342 343 344 345

  // 9.b. If targetDesc is not undefined, then (see 9.b.i. below).
  BIND(&if_found_value);
  {
    // 9.b.i. If targetDesc.[[Configurable]] is false, throw a TypeError
    // exception.
346
    TNode<BoolT> non_configurable = IsSetWord32(
347 348 349 350
        var_details.value(), PropertyDetails::kAttributesDontDeleteMask);
    GotoIf(non_configurable, &throw_non_configurable);

    // 9.b.ii. Let extensibleTarget be ? IsExtensible(target).
351
    TNode<BoolT> target_extensible = IsExtensibleMap(target_map);
352 353 354

    // 9.b.iii. If extensibleTarget is false, throw a TypeError exception.
    GotoIfNot(target_extensible, &throw_non_extensible);
355
    Goto(&check_passed);
356 357 358 359 360 361 362
  }

  BIND(&throw_non_configurable);
  { ThrowTypeError(context, MessageTemplate::kProxyHasNonConfigurable, name); }

  BIND(&throw_non_extensible);
  { ThrowTypeError(context, MessageTemplate::kProxyHasNonExtensible, name); }
363 364 365 366 367 368 369 370

  BIND(&check_in_runtime);
  {
    CallRuntime(Runtime::kCheckProxyHasTrapResult, context, name, target);
    Goto(&check_passed);
  }

  BIND(&check_passed);
371 372
}

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
void ProxiesCodeStubAssembler::CheckDeleteTrapResult(TNode<Context> context,
                                                     TNode<JSReceiver> target,
                                                     TNode<JSProxy> proxy,
                                                     TNode<Name> name) {
  TNode<Map> target_map = LoadMap(target);
  TVARIABLE(Object, var_value);
  TVARIABLE(Uint32T, var_details);
  TVARIABLE(Object, var_raw_value);

  Label if_found_value(this, Label::kDeferred),
      throw_non_configurable(this, Label::kDeferred),
      throw_non_extensible(this, Label::kDeferred), check_passed(this),
      check_in_runtime(this, Label::kDeferred);

  // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).
  GotoIfNot(IsUniqueNameNoIndex(name), &check_in_runtime);
389
  TNode<Uint16T> instance_type = LoadInstanceType(target);
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
  TryGetOwnProperty(context, target, target, target_map, instance_type, name,
                    &if_found_value, &var_value, &var_details, &var_raw_value,
                    &check_passed, &check_in_runtime, kReturnAccessorPair);

  // 11. If targetDesc is undefined, return true.
  BIND(&if_found_value);
  {
    // 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
    TNode<BoolT> non_configurable = IsSetWord32(
        var_details.value(), PropertyDetails::kAttributesDontDeleteMask);
    GotoIf(non_configurable, &throw_non_configurable);

    // 13. Let extensibleTarget be ? IsExtensible(target).
    TNode<BoolT> target_extensible = IsExtensibleMap(target_map);

    // 14. If extensibleTarget is false, throw a TypeError exception.
    GotoIfNot(target_extensible, &throw_non_extensible);
    Goto(&check_passed);
  }

  BIND(&throw_non_configurable);
  {
    ThrowTypeError(context,
                   MessageTemplate::kProxyDeletePropertyNonConfigurable, name);
  }

  BIND(&throw_non_extensible);
  {
    ThrowTypeError(context, MessageTemplate::kProxyDeletePropertyNonExtensible,
                   name);
  }

  BIND(&check_in_runtime);
  {
    CallRuntime(Runtime::kCheckProxyDeleteTrapResult, context, name, target);
    Goto(&check_passed);
  }

  BIND(&check_passed);
}

431 432
}  // namespace internal
}  // namespace v8