/// `util` module support bindings for SyNode
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SyNodeBinding_util;

interface
{$I Synopse.inc}
{$I SyNode.inc}
uses
  SysUtils,
  SynCommons,
  SyNode, SpiderMonkey;

function HasInstance(cx: PJSContext; argc: uintN; var vp: jsargRec; proto: JSProtoKey): Boolean; {$IFNDEF FPC}{$ifdef HASINLINE}inline;{$endif}{$ENDIF}// << this couses internal error on FPC 3.0.2

implementation

///This is dirty hack using Spider Monkey internals
function HasInstance(cx: PJSContext; argc: uintN; var vp: jsargRec; proto: JSProtoKey): Boolean; {$IFNDEF FPC}{$ifdef HASINLINE}inline;{$endif}{$ENDIF}// << this couses internal error on FPC 3.0.2
var
  in_argv: PjsvalVector;
  slotIndex: uint32;
const
  sInvalidCall = 'One argunent required';
begin
  Result := False;
  try
//    {$IFNDEF SM45}
//    raise ENotImplemented.Create('Check that slots order is correct and you can continue using this internal');
//    {$ENDIF}
    in_argv := vp.argv;
    if (argc <> 1) then
      raise ESMException.Create(sInvalidCall);
    slotIndex := JSCLASS_GLOBAL_APPLICATION_SLOTS + Ord(proto);
    vp.rval := SimpleVariantToJSval(cx, cx.CurrentGlobalOrNull.ReservedSlot[slotIndex].asObject.HasInstance(cx, in_argv[0]));
    Result := True;
  except
    on E: Exception do begin
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;
end;

function isArrayBuffer(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := HasInstance(cx, argc, vp, JSProto_ArrayBuffer);
end;

function isDataView(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := HasInstance(cx, argc, vp, JSProto_DataView);
end;

function isDate(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := HasInstance(cx, argc, vp, JSProto_Date);
end;

function isMap(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := HasInstance(cx, argc, vp, JSProto_Map);
end;

///This is dirty hack using Spider Monkey internals
function isMapIterator(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
  sInvalidCall = 'One argunent required';
begin
  Result := False;
  try
//    {$IFNDEF SM45}
//    raise ENotImplemented.Create('Check that Class_ name is correct and you can continue using this internal');
//    {$ENDIF}
    if (argc <> 1) then
      raise ESMException.Create(sInvalidCall);
    vp.rval := SimpleVariantToJSval(cx,
      vp.argv[0].isObject and (vp.argv[0].asObject.Class_.name = 'Map Iterator')
    );
    Result := True;
  except
    on E: Exception do begin
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;
end;

/// SyNode not use prommise yet
function isPromise(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := True;
  vp.rval := SimpleVariantToJSval(cx,false);
end;

function isRegExp(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := HasInstance(cx, argc, vp, JSProto_RegExp);
end;

function isTypedArray(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := HasInstance(cx, argc, vp, JSProto_Set);
end;

///This is dirty hack using Spider Monkey internals
function isSetIterator(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
const
  sInvalidCall = 'One argunent required';
begin
  Result := False;
  try
//    {$IFNDEF SM45}
//    raise ENotImplemented.Create('Check that Class_ name is correct and you can continue using this internal');
//    {$ENDIF}
    if (argc <> 1) then
      raise ESMException.Create(sInvalidCall);
    vp.rval := SimpleVariantToJSval(cx,
      vp.argv[0].isObject and (vp.argv[0].asObject.Class_.name = 'Set Iterator')
    );
    Result := True;
  except
    on E: Exception do begin
      vp.rval := JSVAL_VOID;
      JSError(cx, E);
    end;
  end;
end;

function isSet(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
begin
  Result := HasInstance(cx, argc, vp, JSProto_TypedArray);
end;

function SyNodeBindingProc_util(const aEngine: TSMEngine; const bindingNamespaceName: SynUnicode): jsval;
var
  obj: PJSRootedObject;
  cx: PJSContext;
const
  props = JSPROP_ENUMERATE or JSPROP_ENUMERATE or JSPROP_PERMANENT;
begin
  cx := aEngine.cx;
  obj := cx.NewRootedObject(cx.NewObject(nil));
  try
    obj.ptr.DefineFunction(cx, 'isArrayBuffer', isArrayBuffer, 1, props);
    obj.ptr.DefineFunction(cx, 'isDataView', isDataView, 1, props);
    obj.ptr.DefineFunction(cx, 'isDate', isDate, 1, props);
    obj.ptr.DefineFunction(cx, 'isMap', isMap, 1, props);
    obj.ptr.DefineFunction(cx, 'isMapIterator', isMapIterator, 1, props);
    obj.ptr.DefineFunction(cx, 'isPromise', isPromise, 1, props);
    obj.ptr.DefineFunction(cx, 'isRegExp', isRegExp, 1, props);
    obj.ptr.DefineFunction(cx, 'isSet', isArrayBuffer, 1, props);
    obj.ptr.DefineFunction(cx, 'isSetIterator', isSetIterator, 1, props);
    obj.ptr.DefineFunction(cx, 'isTypedArray', isTypedArray, 1, props);

  // todo
  //  obj.ptr.DefineFunction(cx, 'getHiddenValue', isArrayBuffer, 1, props);
  //  obj.ptr.DefineFunction(cx, 'setHiddenValue', isArrayBuffer, 1, props);
  //  obj.ptr.DefineFunction(cx, 'getProxyDetails', isArrayBuffer, 1, props);
  //
  //  obj.ptr.DefineFunction(cx, 'startSigintWatchdog', isArrayBuffer, 1, props);
  //  obj.ptr.DefineFunction(cx, 'stopSigintWatchdog', isArrayBuffer, 1, props);
  //  obj.ptr.DefineFunction(cx, 'watchdogHasPendingSigint', isArrayBuffer, 1, props);

    Result := obj.ptr.ToJSValue;
  finally
    cx.FreeRootedObject(obj);
  end;
end;

initialization
  TSMEngineManager.RegisterBinding('util', SyNodeBindingProc_util);

end.
