/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "jit/WarpBuilderShared.h"

#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"

using namespace js;
using namespace js::jit;

WarpBuilderShared::WarpBuilderShared(WarpSnapshot& snapshot,
                                     MIRGenerator& mirGen,
                                     MBasicBlock* current_)
    : snapshot_(snapshot),
      mirGen_(mirGen),
      alloc_(mirGen.alloc()),
      current(current_) {}

bool WarpBuilderShared::resumeAfter(MInstruction* ins, BytecodeLocation loc) {
  // resumeAfter should only be used with effectful instructions. The only
  // exception is MInt64ToBigInt, it's used to convert the result of a call into
  // Wasm code so we attach the resume point to that instead of to the call.
  MOZ_ASSERT(ins->isEffectful() || ins->isInt64ToBigInt());
  MOZ_ASSERT(!ins->isMovable());

  MResumePoint* resumePoint = MResumePoint::New(
      alloc(), ins->block(), loc.toRawBytecode(), ResumeMode::ResumeAfter);
  if (!resumePoint) {
    return false;
  }

  ins->setResumePoint(resumePoint);
  return true;
}

MConstant* WarpBuilderShared::constant(const Value& v) {
  MOZ_ASSERT_IF(v.isString(), v.toString()->isLinear());
  MOZ_ASSERT_IF(v.isGCThing(), !IsInsideNursery(v.toGCThing()));

  MConstant* cst = MConstant::New(alloc(), v);
  current->add(cst);
  return cst;
}

void WarpBuilderShared::pushConstant(const Value& v) {
  MConstant* cst = constant(v);
  current->push(cst);
}

MCall* WarpBuilderShared::makeCall(CallInfo& callInfo, bool needsThisCheck,
                                   WrappedFunction* target, bool isDOMCall) {
  auto addUndefined = [this]() -> MConstant* {
    return constant(UndefinedValue());
  };

  return MakeCall(alloc(), addUndefined, callInfo, needsThisCheck, target,
                  isDOMCall);
}

MInstruction* WarpBuilderShared::makeSpreadCall(CallInfo& callInfo,
                                                bool needsThisCheck,
                                                bool isSameRealm,
                                                WrappedFunction* target) {
  MOZ_ASSERT(callInfo.argFormat() == CallInfo::ArgFormat::Array);
  MOZ_ASSERT_IF(needsThisCheck, !target);

  // Load dense elements of the argument array.
  MElements* elements = MElements::New(alloc(), callInfo.arrayArg());
  current->add(elements);

  if (callInfo.constructing()) {
    auto* construct =
        MConstructArray::New(alloc(), target, callInfo.callee(), elements,
                             callInfo.thisArg(), callInfo.getNewTarget());
    if (isSameRealm) {
      construct->setNotCrossRealm();
    }
    if (needsThisCheck) {
      construct->setNeedsThisCheck();
    }
    return construct;
  }

  auto* apply = MApplyArray::New(alloc(), target, callInfo.callee(), elements,
                                 callInfo.thisArg());

  if (callInfo.ignoresReturnValue()) {
    apply->setIgnoresReturnValue();
  }
  if (isSameRealm) {
    apply->setNotCrossRealm();
  }
  MOZ_ASSERT(!needsThisCheck);
  return apply;
}
