// Copyright 2019 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.

namespace runtime {
  extern transitioning runtime
  ObjectIsExtensible(implicit context: Context)(JSAny): JSAny;

  extern transitioning runtime
  JSReceiverPreventExtensionsThrow(implicit context: Context)(JSReceiver):
      JSAny;

  extern transitioning runtime
  JSReceiverPreventExtensionsDontThrow(implicit context: Context)(JSReceiver):
      JSAny;

  extern transitioning runtime
  JSReceiverGetPrototypeOf(implicit context: Context)(JSReceiver): JSAny;

  extern transitioning runtime
  JSReceiverSetPrototypeOfThrow(implicit context: Context)(JSReceiver, JSAny):
      JSAny;

  extern transitioning runtime
  JSReceiverSetPrototypeOfDontThrow(implicit context:
                                        Context)(JSReceiver, JSAny): JSAny;
}  // namespace runtime

namespace object {
  transitioning macro
  ObjectIsExtensibleImpl(implicit context: Context)(object: JSAny): JSAny {
    const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
    const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
        otherwise return runtime::ObjectIsExtensible(objectJSReceiver);
    return proxy::ProxyIsExtensible(objectJSProxy);
  }

  transitioning macro
  ObjectPreventExtensionsThrow(implicit context: Context)(object: JSAny):
      JSAny {
    const objectJSReceiver = Cast<JSReceiver>(object) otherwise return object;
    const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
        otherwise return runtime::JSReceiverPreventExtensionsThrow(
        objectJSReceiver);
    proxy::ProxyPreventExtensions(objectJSProxy, True);
    return objectJSReceiver;
  }

  transitioning macro
  ObjectPreventExtensionsDontThrow(implicit context: Context)(object: JSAny):
      JSAny {
    const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
    const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
        otherwise return runtime::JSReceiverPreventExtensionsDontThrow(
        objectJSReceiver);
    return proxy::ProxyPreventExtensions(objectJSProxy, False);
  }

  transitioning macro
  ObjectGetPrototypeOfImpl(implicit context: Context)(object: JSAny): JSAny {
    const objectJSReceiver: JSReceiver = ToObject_Inline(context, object);
    return object::JSReceiverGetPrototypeOf(objectJSReceiver);
  }

  transitioning macro
  JSReceiverGetPrototypeOf(implicit context: Context)(object: JSReceiver):
      JSAny {
    const objectJSProxy = Cast<JSProxy>(object)
        otherwise return runtime::JSReceiverGetPrototypeOf(object);
    return proxy::ProxyGetPrototypeOf(objectJSProxy);
  }

  transitioning macro
  ObjectSetPrototypeOfThrow(implicit context: Context)(
      object: JSAny, proto: JSReceiver|Null): JSAny {
    const objectJSReceiver = Cast<JSReceiver>(object) otherwise return object;
    const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
        otherwise return runtime::JSReceiverSetPrototypeOfThrow(
        objectJSReceiver, proto);
    proxy::ProxySetPrototypeOf(objectJSProxy, proto, True);
    return objectJSReceiver;
  }

  transitioning macro
  ObjectSetPrototypeOfDontThrow(implicit context: Context)(
      object: JSAny, proto: JSReceiver|Null): JSAny {
    const objectJSReceiver = Cast<JSReceiver>(object) otherwise return False;
    const objectJSProxy = Cast<JSProxy>(objectJSReceiver)
        otherwise return runtime::JSReceiverSetPrototypeOfDontThrow(
        objectJSReceiver, proto);
    return proxy::ProxySetPrototypeOf(objectJSProxy, proto, False);
  }

  // ES6 section 19.1.2.11 Object.isExtensible ( O )
  transitioning javascript builtin
  ObjectIsExtensible(js-implicit context: Context)(object: JSAny): JSAny {
    return object::ObjectIsExtensibleImpl(object);
  }

  // ES6 section 19.1.2.18 Object.preventExtensions ( O )
  transitioning javascript builtin
  ObjectPreventExtensions(js-implicit context: Context)(object: JSAny): JSAny {
    return object::ObjectPreventExtensionsThrow(object);
  }

  // ES6 section 19.1.2.9 Object.getPrototypeOf ( O )
  transitioning javascript builtin
  ObjectGetPrototypeOf(js-implicit context: Context)(object: JSAny): JSAny {
    return object::ObjectGetPrototypeOfImpl(object);
  }

  // ES6 section 19.1.2.21 Object.setPrototypeOf ( O, proto )
  transitioning javascript builtin ObjectSetPrototypeOf(
      js-implicit context: Context)(object: JSAny, proto: JSAny): JSAny {
    // 1. Set O to ? RequireObjectCoercible(O).
    RequireObjectCoercible(object, 'Object.setPrototypeOf');

    // 2. If Type(proto) is neither Object nor Null, throw a TypeError
    // exception.
    // 3. If Type(O) is not Object, return O.
    // 4. Let status be ? O.[[SetPrototypeOf]](proto).
    // 5. If status is false, throw a TypeError exception.
    // 6. Return O.
    typeswitch (proto) {
      case (proto: JSReceiver|Null): {
        return object::ObjectSetPrototypeOfThrow(object, proto);
      }
      case (JSAny): {
        ThrowTypeError(kProtoObjectOrNull, proto);
      }
    }
  }
}  // namespace object