Jump to content

VarType(aInteger) returns wrong value


Recommended Posts

Hello,

 

using VarType with an integer value returns 20, but varInteger = 3.

 

See Test code:

procedure TestVarType;
var
  i: Integer;
  f: Float;
  s: string;
  b: Boolean;
begin

  i := 999;
  f := 1.2;
  s := 'test';
  b := true;


  WriteLn(Format('VarType(Integer): %d, Actual: %d', [VarType(i), varInteger]));
  WriteLn(Format('VarType(Float): %d, Actual: %d', [VarType(f), varDouble]));
  WriteLn(Format('VarType(String): %d, Actual: %d', [VarType(s), varString]));
  WriteLn(Format('VarType(Boolean): %d, Actual: %d', [VarType(, varBoolean]));

end;
Link to post
Share on other sites

const varEmpty = 0
const varNull = 1
const varSmallint = 2
const varInteger = 3
const varSingle = 4
const varDouble = 5
const varCurrency = 6
const varDate = 7
const varOleStr = 8
const varDispatch = 9
const varError = 10
const varBoolean = 11
const varVariant = 12
const varUnknown = 13
const varShortInt = 16
const varByte = 17
const varWord = 18
const varLongWord = 19
const varInt64 = 20
const varStrArg = 72
const varAny = 257
const varString = 258
const varTypeMask = 4095
const varArray = 8192
const varByRef = 16384
Link to post
Share on other sites

In DWScript all integers are Int64 values. Likewise all floats are 64 bit doubles.

On the JS side both types map to the 64 bit number type. The JS specification writes:
 

"The type Number is a set of values representing numbers. In ECMAScript the set of values represent the double-precision 64-bit format IEEE 754 value along with a special “Not-a-Number” (NaN) value, positive infinity, and negative infinity."

Link to post
Share on other sites

Thanks for the explanation!

 

But the fact is, that this VarType function is not working as somebody would expect. This function only returns 4 types (string, integer64, float and boolean), but there about 25 consts from varEmpty .. varByRef as warleyalex has posted. How should somebody know how to use it?

 

Implemented VarType function:

function VarType(v) {
    switch (Object.prototype.toString.call(v)) {
        case "[object Undefined]": return 0;
        case "[object String]": return 258;
        case "[object Number]": return (parseInt(v)==v)?20:5;
        case "[object Boolean]": return 0xB;
        default : return 0xC;
    }
}

Everytime I want to use that function, after some struggling with the results, I gave up on that. So, for me, it would be better to remove that half backed function or to fix it. There is also no documentation on how to use it.

Link to post
Share on other sites

It does make sense that this function only returned only 6 types (including varEmpty and varVariant), since this is what JavaScript supports.

 

Perhaps returning varNull = 1 would make sense in addition to those 6 types.

 

My guess is that VarType() is here for compatibility with Delphi when working with variants, which supports much more types (as the var* constants extracted from Delphi System.pas unit). See http://docwiki.embarcadero.com/RADStudio/Berlin/en/Variant_Types_(Delphi) 

and http://www.delphibasics.co.uk/RTL.asp?Name=VarType 

Link to post
Share on other sites

I think we may be mixing eggs and apples here, but outside RTTI you might want to fall back on the system units.

 

If you have a look at

system.types.pas

You will see that the datatypes from SMS point of view, is to a large degree defined just to be "compatible" with some legacy code, but not binary compatible.

Javascript doesnt really have all the datatypes we are used to, and like christian says, integer will map to int64. Which also is the case for byte (!)

 

The types javascript exposes (except Microsoft who exposes more, but they live in their own galaxy) are:

  TW3VariantDataType = (
      vdUnknown  = 1,
      vdBoolean  = 2,
      vdInteger  = 3,
      vdFloat    = 4,
      vdString   = 5,
      vdSymbol   = 6,
      vdFunction = 7,
      vdObject   = 8,
      vdArray    = 9);

This is why the system.types and system.types.convert units are so important.

Without them SMS would suffer the same problems that haunts quite a few javascript libraries (even google gears got that part wrong), resorting to hacks like storing bytes as charcodes just to force data to behave as it should under native languages.

 

I solved it by pre-allocating a small untyped buffer when your application starts, this we use whenever we convert between javascript reality and native reality. You can create a data-view into untyped buffers of an intrinsic type, which is very very fast (!)

 

This allows us to support faster datatype conversion than common JS libraries. And with that we also got support for blue-pointers (fake pointers based on offsets into a view), allocmem, move(), Readmemory. All of these form the basis of streams as we know them under Delphi or Freepascal.

 

So you might want to check out these units while you are on the subject (and there is often comments of importance in the source):

  • System.Memory
  • System.Memory.Allocation
  • System.Memory.Buffer

 

But back to your problem

 

Like christian says the javascript virtual machine should default to a fixed value-type (int64).

But there have been cases where Firefox and Safari mobile builds have deviated from this.

Why I have no idea, but i presume to save memory perhaps under embedded platforms.

Could be they just forgot to disable a conditional switch, who knows.

 

The behavior firefox (early 2015) and Safari Mobile (mid 2015) demonstrated, was that they seemed to pick datatypes not exposed to the JSVM, based on the first value you assigned to a variable.

 

Ex:

 

  var bytevalue = 12; //should be int64 but was a single byte (!)

  var bytebreaker = 256; // Here it would clip bits beyond 0..15

  var wordbreaker = 65539; // Here it would clip bit-shifts beyond 0..31

  var longbreaker = 2147483648; // here is finally picks a signed int64 (!)

 

I only noticed this when implementing a codec class for RC4 and Blowfish encryption where bitshift is used.

You can imagine the havoc this caused when we started writing code that relied 100% on concurrent behavior regardless of browser.

 

And not a single note to be found in the mozilla repository or webkit.

 

When I upgraded my development machines around december, having coded around the problem, the behavior was finally gone.

Although Safari suddenly dropped support (read: will not allow you) to create Uint8ClampedArray, so we had to fall back on Uint8Array and make sure bits from one byte doesnt bleed into the next.

 

 

Stick to the VJL if you can

 

Unless your work involves RTTI (which i presume it does hence your post here), you could alternatively use Variant.Datatype() to get the JSVM type.

 

Please note that the code below is directly from my dev-machine, it's not yet in the repository or update channels.

It has to pass QA testing before it's commited to our servers.

 

But if it helps you out in some way, then feel free to use it at your own risk. I always test code on Firefox, chrome, Safari, IE, Spartan and opera (and the same on mobile devices when available).

 

Also, if you have a faster version or improvements in any way, feel free to post it here. We absolutely welcome your input!

Hopefully some of what i have written here helps you out, or at least gives you a better overview of related functionality.

 

 

VarType() however requires a deeper fix, since that is a "magic" function managed by the compiler.

But you are indeed correct that it should be in-sync with the rest, so I will forward it to Eric, our compiler wizard.

 

Using it is quite simple:

 

1. Include System.Types in your uses clause

2. Do a simple cast to get access to the helper (if its not a variant to begin with):

 

procedure TForm1.DataTypeTest;
var
 LTestValue: Integer;
begin
  writeln("------- Datatype test -------");
  writeln("Type = " + Variant(LTestValue).Datatype.ToString() );
end;

Here is the code, as you can see it does not take delphi or c# into account, but is only interested in SMS's point of view.

 

type

(* These are essentially the types common between
   browsers. Sadly the typeOf() method can have char-case differences.
  
   External references:
   ====================
   https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof *)

TW3VariantDataType = (
      vdUnknown  = 1,
      vdBoolean  = 2,
      vdInteger  = 3,
      vdFloat    = 4,
      vdString   = 5,
      vdSymbol   = 6,
      vdFunction = 7,
      vdObject   = 8,
      vdArray    = 9
      );

  TW3VariantHelper = helper for variant
    function  Valid: boolean;
    function  Defined: boolean;
    function  UnDefined: boolean;
    function  Equals(const Reference: variant):Boolean;
    function  IsObject: boolean;
    function  IsArray: boolean;
    function  IsInteger: boolean;
    function  IsFloat: boolean;
    function  IsBoolean: boolean;
    function  IsString: boolean;
    function  IsSymbol: boolean;
    function  IsFunction: boolean;
    function  DataType: TW3VariantDataType;
  end;

//#############################################################################
// TW3VariantHelper
//#############################################################################

function TW3VariantHelper.Valid: boolean;
begin
  asm
    @Result = !( (@self == undefined) || (@self == null) );
  end;
end;

function TW3VariantHelper.Equals(const Reference: variant):Boolean;
begin
  asm
    @result = (@self == @Reference);
  end;
end;

Function TW3VariantHelper.Defined: boolean;
begin
  asm
    @result = !(self == undefined);
  end;
end;

function TW3VariantHelper.UnDefined: boolean;
begin
  asm
    @result = (self == undefined);
  end;
end;

function TW3VariantHelper.DataType: TW3VariantDataType;
var
  LType: String;
begin
  if Valid then
  begin
    asm
      @LType = typeof @self;
    end;

    // Sooo want to use a lookup table here, but it wont work
    // due to the similarities between type-text. There is also a char-case
    // difference between IE and firefox that could make a 1:1 dictionary
    // lookup un-realiable. Hence the brute-force lowercase + case swich
    case LType.ToLower of
    'object':
      begin
        // If it has length, it's an array
        // JQuery actually screwed this up (LOL!)
        if not (self.length) then
        result := vdObject else
        result := vdArray;
      end;
    'function': result := vdFunction;
    'symbol':   result := vdSymbol;
    'boolean':  result := vdBoolean;
    'string':   result := vdString;
    'number':
      begin
        (* $NEXT-UPDATE: we should add single, double, int32 and so on to the checking.
           First of all to get rid of our near identical type-list in system.types.convert,
           but also to simplify documentation. *)
        case ( round(self) <> self) of
        true: result := vdFloat;
        false: result := vdInteger;
        end;
      end;
    'array':  
      begin
        (* $NEXT-UPDATE: I would suggest we expand datatype information
           into a record. Array doesnt really mean much, and each element in an
           array can have a different type. But we can at least detect if it's
           a native array (like Uint8Array) or a plain javascript array.

           Something like:

           TW3ArrayType defines if it's an array connected to a
           native, untyped buffer -- or just a normal javascript array

           type
           TW3ArrayType = (atNative, atJavaScript);

           TW3DatatypeInfo = record
            tiType:    TW3VariantDataType;
            tiArray:   boolean;
            tiArrayOf: TW3ArrayType; 
           end;
        *)
        result := vdArray;
      end
    else
      result := vdUnknown;
    end;
  end else
  result := vdUnknown;
end;

function TW3VariantHelper.IsObject:Boolean;
begin
  asm
    @result = ((@self) !== undefined)
      && (@self !== null)
      && (typeof @self  === "object")
      && ((@self).length === undefined);
  end;
end;

function TW3VariantHelper.IsSymbol:Boolean;
begin
  asm
    @result = ((@self) !== undefined)
      && (@self !== null)
      && (typeof @self === "symbol");
  end;
end;

function TW3VariantHelper.IsFunction:Boolean;
begin
  asm
    @result = ((@self) !== undefined)
      && (@self !== null)
      && (typeof @self === "function");
  end;
end;

function TW3VariantHelper.IsBoolean:Boolean;
begin
  asm
    @result = ((@self) !== undefined)
      && (@self !== null)
      && (typeof @self === "boolean");
  end;
end;

function TW3VariantHelper.IsString:Boolean;
Begin
  asm
    @result = (@self !== undefined)
      && (@self !== null)
      && (typeof @self  === "string");
  end;
end;

function TW3VariantHelper.IsFloat:Boolean;
begin
  asm
    @result = ((@self) !== undefined)
      && (@self !== null)
      && (typeof @self  === "number")
      && (Math.round(@self) != @self);
  end;
end;

function TW3VariantHelper.IsInteger:Boolean;
begin
  asm
    @result = ((@self) !== undefined)
      && (@self !== null)
      && (typeof @self  === "number")
      && (Math.round(@self) === @self);
  end;
end;

function TW3VariantHelper.IsArray:boolean;
begin
  asm
    @result = ((@self) !== undefined)
      && (@self !== null)
      && (typeof @self === "object")
      && ((@self).length !== undefined);
  end;
end;
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...