builtins-weak-refs.cc 8.1 KB
Newer Older
1 2 3 4 5
// Copyright 2018 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.

#include "src/builtins/builtins-utils-inl.h"
6
#include "src/logging/counters.h"
7 8 9 10 11
#include "src/objects/js-weak-refs-inl.h"

namespace v8 {
namespace internal {

12
BUILTIN(FinalizationRegistryConstructor) {
13 14 15 16 17
  HandleScope scope(isolate);
  Handle<JSFunction> target = args.target();
  if (args.new_target()->IsUndefined(isolate)) {  // [[Call]]
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
18
                              handle(target->shared().Name(), isolate)));
19 20 21 22 23
  }
  // [[Construct]]
  Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
  Handle<Object> cleanup = args.atOrUndefined(isolate, 1);

24 25 26 27 28
  if (!cleanup->IsCallable()) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kWeakRefsCleanupMustBeCallable));
  }

29 30 31 32 33
  Handle<JSObject> result;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
      isolate, result,
      JSObject::New(target, new_target, Handle<AllocationSite>::null()));

34 35 36 37 38 39 40 41 42 43 44
  Handle<JSFinalizationRegistry> finalization_registry =
      Handle<JSFinalizationRegistry>::cast(result);
  finalization_registry->set_native_context(*isolate->native_context());
  finalization_registry->set_cleanup(*cleanup);
  finalization_registry->set_flags(
      JSFinalizationRegistry::ScheduledForCleanupField::encode(false));

  DCHECK(finalization_registry->active_cells().IsUndefined(isolate));
  DCHECK(finalization_registry->cleared_cells().IsUndefined(isolate));
  DCHECK(finalization_registry->key_map().IsUndefined(isolate));
  return *finalization_registry;
45 46
}

47
BUILTIN(FinalizationRegistryRegister) {
48
  HandleScope scope(isolate);
49
  const char* method_name = "FinalizationRegistry.prototype.register";
50

51 52 53 54 55 56 57
  //  1. Let finalizationGroup be the this value.
  //
  //  2. If Type(finalizationGroup) is not Object, throw a TypeError
  //  exception.
  //
  //  4. If finalizationGroup does not have a [[Cells]] internal slot,
  //  throw a TypeError exception.
58
  CHECK_RECEIVER(JSFinalizationRegistry, finalization_registry, method_name);
59

60
  Handle<Object> target = args.atOrUndefined(isolate, 1);
61 62

  //  3. If Type(target) is not Object, throw a TypeError exception.
63 64
  if (!target->IsJSReceiver()) {
    THROW_NEW_ERROR_RETURN_FAILURE(
65
        isolate,
66
        NewTypeError(MessageTemplate::kWeakRefsRegisterTargetMustBeObject));
67
  }
68

69
  Handle<Object> holdings = args.atOrUndefined(isolate, 2);
70 71 72
  if (target->SameValue(*holdings)) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate,
73
        NewTypeError(
74
            MessageTemplate::kWeakRefsRegisterTargetAndHoldingsMustNotBeSame));
75
  }
76

77
  Handle<Object> unregister_token = args.atOrUndefined(isolate, 3);
78

79 80 81 82 83 84 85 86
  //  5. If Type(unregisterToken) is not Object,
  //    a. If unregisterToken is not undefined, throw a TypeError exception.
  if (!unregister_token->IsJSReceiver() && !unregister_token->IsUndefined()) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate,
        NewTypeError(MessageTemplate::kWeakRefsUnregisterTokenMustBeObject,
                     unregister_token));
  }
87 88
  // TODO(marja): Realms.

89 90 91
  JSFinalizationRegistry::Register(finalization_registry,
                                   Handle<JSReceiver>::cast(target), holdings,
                                   unregister_token, isolate);
92 93 94
  return ReadOnlyRoots(isolate).undefined_value();
}

95
BUILTIN(FinalizationRegistryUnregister) {
96
  HandleScope scope(isolate);
97
  const char* method_name = "FinalizationRegistry.prototype.unregister";
98

99 100 101 102 103 104 105
  // 1. Let finalizationGroup be the this value.
  //
  // 2. If Type(finalizationGroup) is not Object, throw a TypeError
  //    exception.
  //
  // 3. If finalizationGroup does not have a [[Cells]] internal slot,
  //    throw a TypeError exception.
106
  CHECK_RECEIVER(JSFinalizationRegistry, finalization_registry, method_name);
107

108 109 110 111 112 113 114 115 116 117
  Handle<Object> unregister_token = args.atOrUndefined(isolate, 1);

  // 4. If Type(unregisterToken) is not Object, throw a TypeError exception.
  if (!unregister_token->IsJSReceiver()) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate,
        NewTypeError(MessageTemplate::kWeakRefsUnregisterTokenMustBeObject,
                     unregister_token));
  }

118 119 120
  bool success = JSFinalizationRegistry::Unregister(
      finalization_registry, Handle<JSReceiver>::cast(unregister_token),
      isolate);
121 122

  return *isolate->factory()->ToBoolean(success);
123 124
}

125
BUILTIN(FinalizationRegistryCleanupSome) {
126
  HandleScope scope(isolate);
127
  const char* method_name = "FinalizationRegistry.prototype.cleanupSome";
128

129 130 131 132 133 134 135
  // 1. Let finalizationGroup be the this value.
  //
  // 2. If Type(finalizationGroup) is not Object, throw a TypeError
  //    exception.
  //
  // 3. If finalizationGroup does not have a [[Cells]] internal slot,
  //    throw a TypeError exception.
136
  CHECK_RECEIVER(JSFinalizationRegistry, finalization_registry, method_name);
137

138
  Handle<Object> callback(finalization_registry->cleanup(), isolate);
139 140 141 142 143 144 145 146 147 148 149 150
  Handle<Object> callback_obj = args.atOrUndefined(isolate, 1);

  // 4. If callback is not undefined and IsCallable(callback) is
  //    false, throw a TypeError exception.
  if (!callback_obj->IsUndefined(isolate)) {
    if (!callback_obj->IsCallable()) {
      THROW_NEW_ERROR_RETURN_FAILURE(
          isolate,
          NewTypeError(MessageTemplate::kWeakRefsCleanupMustBeCallable));
    }
    callback = callback_obj;
  }
151

152 153
  // Don't do set_scheduled_for_cleanup(false); we still have the task
  // scheduled.
154
  if (JSFinalizationRegistry::Cleanup(isolate, finalization_registry, callback)
155 156 157 158
          .IsNothing()) {
    DCHECK(isolate->has_pending_exception());
    return ReadOnlyRoots(isolate).exception();
  }
159 160 161
  return ReadOnlyRoots(isolate).undefined_value();
}

162
BUILTIN(FinalizationRegistryCleanupIteratorNext) {
163
  HandleScope scope(isolate);
164
  CHECK_RECEIVER(JSFinalizationRegistryCleanupIterator, iterator, "next");
165

166 167 168
  Handle<JSFinalizationRegistry> finalization_registry(
      iterator->finalization_registry(), isolate);
  if (!finalization_registry->NeedsCleanup()) {
169 170 171
    return *isolate->factory()->NewJSIteratorResult(
        handle(ReadOnlyRoots(isolate).undefined_value(), isolate), true);
  }
172 173 174 175
  Handle<Object> holdings =
      handle(JSFinalizationRegistry::PopClearedCellHoldings(
                 finalization_registry, isolate),
             isolate);
176

177
  return *isolate->factory()->NewJSIteratorResult(holdings, false);
178 179
}

180 181 182 183 184 185
BUILTIN(WeakRefConstructor) {
  HandleScope scope(isolate);
  Handle<JSFunction> target = args.target();
  if (args.new_target()->IsUndefined(isolate)) {  // [[Call]]
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
186
                              handle(target->shared().Name(), isolate)));
187 188 189 190 191 192 193 194 195 196
  }
  // [[Construct]]
  Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
  Handle<Object> target_object = args.atOrUndefined(isolate, 1);
  if (!target_object->IsJSReceiver()) {
    THROW_NEW_ERROR_RETURN_FAILURE(
        isolate,
        NewTypeError(
            MessageTemplate::kWeakRefsWeakRefConstructorTargetMustBeObject));
  }
197 198
  Handle<JSReceiver> target_receiver =
      handle(JSReceiver::cast(*target_object), isolate);
199
  isolate->heap()->KeepDuringJob(target_receiver);
200 201 202 203 204 205 206 207 208

  // TODO(marja): Realms.

  Handle<JSObject> result;
  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
      isolate, result,
      JSObject::New(target, new_target, Handle<AllocationSite>::null()));

  Handle<JSWeakRef> weak_ref = Handle<JSWeakRef>::cast(result);
209
  weak_ref->set_target(*target_receiver);
210 211 212
  return *weak_ref;
}

213 214 215
BUILTIN(WeakRefDeref) {
  HandleScope scope(isolate);
  CHECK_RECEIVER(JSWeakRef, weak_ref, "WeakRef.prototype.deref");
216
  if (weak_ref->target().IsJSReceiver()) {
217 218
    Handle<JSReceiver> target =
        handle(JSReceiver::cast(weak_ref->target()), isolate);
219
    // KeepDuringJob might allocate and cause a GC, but it won't clear
220
    // weak_ref since we hold a Handle to its target.
221
    isolate->heap()->KeepDuringJob(target);
222
  } else {
223
    DCHECK(weak_ref->target().IsUndefined(isolate));
224 225 226 227
  }
  return weak_ref->target();
}

228 229
}  // namespace internal
}  // namespace v8