// Copyright 2020 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/ast/ast.h"

namespace runtime {
extern runtime CreateArrayLiteral(
    Context, Undefined | FeedbackVector, TaggedIndex,
    ArrayBoilerplateDescription, Smi): HeapObject;
extern runtime CreateObjectLiteral(
    Context, Undefined | FeedbackVector, TaggedIndex,
    ObjectBoilerplateDescription, Smi): HeapObject;
}

namespace constructor {

extern builtin FastNewClosure(
    Context, SharedFunctionInfo, FeedbackCell): JSFunction;
extern builtin FastNewObject(Context, JSFunction, JSReceiver): JSObject;

extern enum AllocationSiteMode {
  DONT_TRACK_ALLOCATION_SITE,
  TRACK_ALLOCATION_SITE
}

const kIsShallow: constexpr int31
    generates 'AggregateLiteral::Flags::kIsShallow';
const kEvalScope: constexpr ScopeType generates 'ScopeType::EVAL_SCOPE';
const kFunctionScope:
    constexpr ScopeType generates 'ScopeType::FUNCTION_SCOPE';

extern macro ConstructorBuiltinsAssembler::FastNewFunctionContext(
    ScopeInfo, uint32, Context, constexpr ScopeType): Context;
extern macro ConstructorBuiltinsAssembler::CreateRegExpLiteral(
    HeapObject, TaggedIndex, Object, Smi, Context): JSRegExp;
extern macro ConstructorBuiltinsAssembler::CreateShallowArrayLiteral(
    FeedbackVector, TaggedIndex, Context,
    constexpr AllocationSiteMode): HeapObject labels CallRuntime;
extern macro ConstructorBuiltinsAssembler::CreateEmptyArrayLiteral(
    FeedbackVector, TaggedIndex, Context): HeapObject;
extern macro ConstructorBuiltinsAssembler::CreateShallowObjectLiteral(
    FeedbackVector, TaggedIndex): HeapObject labels CallRuntime;
extern macro ConstructorBuiltinsAssembler::CreateEmptyObjectLiteral(Context):
    JSObject;

extern macro LoadContextFromBaseline(): Context;

builtin FastNewClosureBaseline(
    sharedFunctionInfo: SharedFunctionInfo,
    feedbackCell: FeedbackCell): JSFunction {
  const context = LoadContextFromBaseline();
  tail FastNewClosure(context, sharedFunctionInfo, feedbackCell);
}

builtin FastNewFunctionContextEval(implicit context: Context)(
    scopeInfo: ScopeInfo, slots: uint32): Context {
  return FastNewFunctionContext(scopeInfo, slots, context, kEvalScope);
}

builtin FastNewFunctionContextFunction(implicit context: Context)(
    scopeInfo: ScopeInfo, slots: uint32): Context {
  return FastNewFunctionContext(scopeInfo, slots, context, kFunctionScope);
}

builtin CreateRegExpLiteral(implicit context: Context)(
    maybeFeedbackVector: HeapObject, slot: TaggedIndex, pattern: Object,
    flags: Smi): JSRegExp {
  return CreateRegExpLiteral(
      maybeFeedbackVector, slot, pattern, flags, context);
}

builtin CreateShallowArrayLiteral(implicit context: Context)(
    maybeFeedbackVector: Undefined|FeedbackVector, slot: TaggedIndex,
    constantElements: ArrayBoilerplateDescription, flags: Smi): HeapObject {
  try {
    const vector = Cast<FeedbackVector>(maybeFeedbackVector)
        otherwise CallRuntime;
    return CreateShallowArrayLiteral(
        vector, slot, context, AllocationSiteMode::TRACK_ALLOCATION_SITE)
        otherwise CallRuntime;
  } label CallRuntime deferred {
    tail runtime::CreateArrayLiteral(
        context, maybeFeedbackVector, slot, constantElements, flags);
  }
}

builtin CreateEmptyArrayLiteral(implicit context: Context)(
    feedbackVector: FeedbackVector, slot: TaggedIndex): HeapObject {
  return CreateEmptyArrayLiteral(feedbackVector, slot, context);
}

builtin CreateShallowObjectLiteral(implicit context: Context)(
    maybeFeedbackVector: Undefined|FeedbackVector, slot: TaggedIndex,
    desc: ObjectBoilerplateDescription, flags: Smi): HeapObject {
  try {
    const feedbackVector = Cast<FeedbackVector>(maybeFeedbackVector)
        otherwise CallRuntime;
    return CreateShallowObjectLiteral(feedbackVector, slot)
        otherwise CallRuntime;
  } label CallRuntime deferred {
    tail runtime::CreateObjectLiteral(
        context, maybeFeedbackVector, slot, desc, flags);
  }
}

// ES #sec-object-constructor
transitioning javascript builtin
ObjectConstructor(
    js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
    target: JSFunction)(...arguments): JSAny {
  if (newTarget == Undefined || newTarget == target) {
    // Not Subclass.
    const value = arguments[0];
    if (arguments.length <= 0 || value == Undefined || value == Null) {
      // New object.
      return CreateEmptyObjectLiteral(context);
    } else {
      return ToObject(context, value);
    }
  } else {
    // Subclass.
    return FastNewObject(context, target, UnsafeCast<JSReceiver>(newTarget));
  }
}

builtin CreateEmptyLiteralObject(implicit context: Context)(): JSAny {
  return CreateEmptyObjectLiteral(context);
}

// ES #sec-number-constructor
transitioning javascript builtin
NumberConstructor(
    js-implicit context: NativeContext, receiver: JSAny, newTarget: JSAny,
    target: JSFunction)(...arguments): JSAny {
  // 1. If no arguments were passed to this function invocation, let n be +0.
  let n: Number = 0;
  if (arguments.length > 0) {
    // 2. Else,
    //    a. Let prim be ? ToNumeric(value).
    //    b. If Type(prim) is BigInt, let n be the Number value for prim.
    //    c. Otherwise, let n be prim.
    const value = arguments[0];
    n = ToNumber(value, BigIntHandling::kConvertToNumber);
  }

  // 3. If NewTarget is undefined, return n.
  if (newTarget == Undefined) return n;

  // 4. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
  //    "%NumberPrototype%", « [[NumberData]] »).
  // 5. Set O.[[NumberData]] to n.
  // 6. Return O.

  // We ignore the normal target parameter and load the value from the
  // current frame here in order to reduce register pressure on the fast path.
  const target: JSFunction = LoadTargetFromFrame();
  const result = UnsafeCast<JSPrimitiveWrapper>(
      FastNewObject(context, target, UnsafeCast<JSReceiver>(newTarget)));
  result.value = n;
  return result;
}

javascript builtin
GenericLazyDeoptContinuation(js-implicit context: NativeContext)(result: JSAny):
    JSAny {
  return result;
}

}  // namespace constructor