Last update March 7, 2012

DMDScript /
Tutorials



Difference (last change) (no other diffs, normal page display)

Changed: 446,557c446
]
jam tangan
jam tangan murah
jam tangan kw
hostgator coupon
kata mutiara
Jasa SEO
EZido
RDAnet
pioneer deh-1300mp
asus a53e-xa2
asus tf101-b1
asus tf101-a1
asus n53sv-eh72
asus republic of gamers g74sx
asus acer a5250
acer chromebook ac700
asus asus 53u
lg infinia 55lw5600
Sonicview 360 premier
asus 7 cu ft freezer
asus 30 single wall oven
brother cs6000i sewing machine
brother 1034d serger
brother sewing machines
Yokohama Geolandar H/T-S
crib tent tots in mind
kidco peapod plus
foscam fi8910w
samsung pl120 review
gopro helmet cam
Canon SX130IS
powershot s100
ContourHD 1080p
canon vixia hf r21
digital picture frame
canon ef 50mm f1.4
canon ef 70-300mm review
wide angle lenses
moving comfort sports bra
moving comfort bra
womens argyle sweater
bebe dresses
ViewSonic VX2250WM
Le Pan TC 970
Apple MacBook Air MC965LL
Sennheiser CX 880
plantronics cs540
ultrasonic jewelry cleaner
Sennheiser RS120
bose quietcomfort 15 acoustic noise cancelling headphones
logitech harmony one remote
logitech harmony 900
sony mhc-ec69i
sony mhcec909ip
bose wave music system
sony htss380
logitech squeezebox touch
sony dvp-fx970
onkyo tx-nr509
onkyo tx - nr609
onkyo ht-s3400
energy 5.1 take classic home theater system
polk audio psw505
onkyo ht-s5400
onkyo tx-nr709
belkin pf60
onkyo ht-rc360
denon avr-1912
Yamaha YHT-S400BL
fujitsu scansnap s1500
brother hl-2270dw
epson workforce 545
hp laserjet p2055dn
bushnell 8mp trophy cam
toshiba 32c110u
panasonic viera tc-p60s30
VIZIO E220VA
hauppauge wintv dcr-2650
Acer AM3970-U5022
Acer AspireRevo AR3700-U3002
Dell Inspiron i570
Dell GX620
Gateway FX6860-UR20P
Western Digital My Passport Essential SE 1 TB USB 3.0
Fujitsu ScanSnap S1300
Epson Perfection V300
Fujitsu SCANSNAP S1100
NeatDesk Desktop Scanner and Digital Filing System
Epson WorkForce Pro GT-S50
Kodak P811BK
Epson Perfection V330
Viewsonic VX2453MH
Asus VE228H
ViewSonic VA2431WM
Samsung B2230
HP 2711x
ASUS ML228H
Epson PowerLite Home Cinema 8350
Optoma PK301
Epson EX7210
Epson EX5210
ViewSonic PJD5133
Acer X1161P
FAVI RioHD-LED-2
Epson EX3210
ViewSonic PJD6531w
Trinity 360 Breville 800JEXL
Skil 3320-02
Delta 46-460
Grizzly G0555
Delta 18-900L
]

DMDScript Tutorials

Building objects and passing them to script functions

This article originally appeared in the DigitalMars newsgroup "DMDScript" by 'nobody' Post:DMDScript/339

Introduction

Before starting I wanted to thank Walter Bright for creating DMDScript. It felt like a huge stroke of luck as I tend to use JavaScript on an almost weekly basis for all kinds of little things. When DMDScript was released I knew it was only a matter of time before I would find a way to make use of it.

I have noticed in this newsgroup there is a fair amount of information about implementing new object types but very little about interacting with DMDScript data structures. In fact the Program class is missing a version of execute which allows you to pass in a custom Darray and this omission seems to suggest that building a Darray oneself is too much trouble. This tutorial is designed to help people with minimal ECMAScript background understand how simple it is to interact with ECMASCript objects from D.

It is my hope that this tutorial will help people considering using DMDScript understand what they might get out of it and help people who have decided to use DMDScript get more out of it.

ECMAScript overview/refresher

ECMAScript is a loosely typed language; all variables are declared using the "var" keyword as if it were a C type:
var i = 0, pi = Math.PI, str = "Hello Script!";

Functions need only declare the variable's name:

   function foo(i,pi,str)
   {
     println(str + (i+pi));
   }

Functions are variadic; every function call has an implicit parameter array named arguments:

   function bar()
   {
     if(arguments.length == 3)
       return foo(arguments[0], arguments[1], arguments[2])
   }

   bar(i,pi,str); // Hello Script!4.141592653589793

When a script is evaluated the entire text makes up the body of the unnamed global function:

   function <unamedGlobalFunction>()
   {
   // Script's first line

   // Script's body here

   // Script's last line
   }

One of the more interesting aspects of scripts is properties:

   var struct_ish =
   {
   	width: 8,
   	height: 12,
   	cells: new Array(8*12)
   };

At the core objects map properties to their corresponding values. Array objects illustrate this well; they have a length property whose value indicates its size and the things populating the array are also accessed by properties:

   vary arrEx = new Array();
   arrEx[0] = 1;
   arrEx["apple"] = Math.PI;
   arrEx[Math.E] = "mc^2";

   Math.E*arrEx["apple"] + arrEx[0]; // 9.539734222673566

Even more interesting is taking a look at how functions are used to script OOP functionality with prototypes. However, this section was meant to be just long enough to ensure a solid enough base for following the rest of the tutorial.

Building ECMAScript Object objects

Now as promised it is time to start working on building objects in D and passing them as parameters to a script function. The first thing to cover is loading a script file, compiling it and executing it:
   char[] buff = cast(char[]) read(scriptFilename);
   Program prong = new Program();
   prog.compile(scriptFilename, buff, null);
   apogee);

Three steps in four lines! Of course there are tons of ECMAScript interpreters out there so just loading a script and running it isn't yet worth the extra effort. The first thing to try is automatically calling a script function:

   char[] buff = cast(char[]) read(scriptFilename) ~ 
                    "smain(arguments);";

This addition will take the arguments passed to the global function and pass them right along to the smain function. The null passed to the Program's execute function can be replaced with an array of strings and those will be passed to smain:

   void main(char[][] args)
   {
     char[] scriptFilename = "script.js";
     char[] buff = cast(char[]) read(scriptFilename) ~ 
                      "smain(arguments);";
     Program prog = new Program();
     prog.compile(scriptFilename, buff, null);
     prog.execute(args);
   }

The drawback with this simple approach is that if you wanted to pass some particular value to smain then unless it happens to be an array of strings you can't. Now consider the following:

   {
   	width: 8,
   	height: 12,
   	cells: new Array(8*12)
   }

This is simply an object with three properties: width, height and cells. Actually constructing this means looking through the dobject.d, darray.d and value.d to discover how they are instantiated and used. Here are the interesting bits:

   // from script.d
   alias char tchar;
   alias tchar[] d_string;
   alias double d_number;

   // from property.d
   DontDelete = 0x004,

   class Dobject
   {
     this(Dobject prototype) { }

     Put(d_string PropertyName, Value* value, uint attributes) { }

     Put(d_string PropertyName, Dobject o, uint attributes) { }

     Get(d_string PropertyName)
   }


   class Darray : Dobject
   {
     this()

     this(Dobject prototype) { }

     Put(d_uint32 index, Value* value, uint attributes)

     Get(d_uint32 index)
   }


   struct Value
   {
     void putVnumber(d_number n) { }

     void putVobject(Dobject o) { }

     void putVstring(d_string s) { }
   }

So Dobject's Put function can be used to attach the numbers to the first two properties:

   Dobject dobj = new Dobject(null);
   dobj.Put("width",  new Value, DontDelete);
   dobj.Get("width").putVnumber(8);
   dobj.Put("height", new Value, DontDelete);
   dobj.Get("height").putVnumber(12);

Since Darray is a subclass of Dobject the other Put function can attach the Darray to the last property. Likewise Darray's Put function can attach Values to an Array object:

   Darray darr = new Darray();
   for(d_uint32 i = 0; i < 8*12; i+=1)
   {
     darr.Put(i, new Value, DontDelete);
     darr.Get(i).putVstring("x");
   }

   dobj.Put("cells", darr, DontDelete);

Now dobj is constructed and ready to be passed to smain. Since the actual argument being passed (arguments) will be a Darray there remains the issue of stuffing dobj into a Darray:

   Darray arguments = new Darray();
   arguments.Put(0, new Value, DontDelete);
   arguments.Get(0).putVobject(dobj);

Executing ECMAScripts with custom tailored arguments

Now with the arguments actually created the only obstacle that remains is actually getting the script to execute with the custom arguments. The ideal solution would be for the Program class to overload execute:
   class Program
   {
     void execute(char[][] args)
     void execute(Darray args)
   }

The problem is that overloading requires making a change to program.d and every time a new version of DMDScript is installed the code has to be changed and the library recompiled. That sort of change is more of a feature request. Instead it is much simpler to just copy the code into a new function. After replacing all the implicit and explicit references to this the only remaining change is to comment out a few lines and Put args directly into dglobal:

   void execute(Program p, Darray args)
   {
     // SNIP
     //  Darray arguments;
     Dobject dglobal = cc.global;
     Program program_save;

	// Set argv and argc for execute
        //  arguments = new Darray();
     dglobal.Put(TEXT_arguments, args, DontDelete | DontEnum);
    //  arguments.length.putVnumber(args.length);
    //  for (d_uint32 i = 0; i < args.length; i++)
    //  {
    //    arguments.Put(i, &args[i], DontEnum);
    //  }
     // SNIP
   }

Uses of arguments

The ability to pass custom tailored arguments to a script function opens a few doors. The most obvious is that one should be able to pass arbitrarily complex data structures into a script function.

The most interesting door that is opened is a direct result of the fact function arguments are passed by reference. This means that once an arguments Darray is created then any changes made by the script are visible from D. Since changes are visible in D they can be passed along from one script to another, or to the same script between multiple executions. So it is fully possible to write a script to initialize the arguments and have another script operate on them:

   void main(char[][] args)
   {
     char[] iFilename = "init.js";
     char[] sFilename = "script.js";
     char[] buffI = cast(char[]) read(iFilename) ~ 
                         "sinit(arguments);";
     char[] buffS = cast(char[]) read(sFilename) ~ 
                         "smain(arguments);";
 
     Program pI = new Program(), pS = new Program();
     prog.compile(iFilename, buffI, null);
     prog.compile(iFilename, buffS, null);
 
     Darray arguments = new Darray();
 
     execute(pI, arguments);
     execute(pS, arguments);
   }

Finally, a word of warning:

 Be warned when multithreading, threads are subtle and quick to anger.

Source listing

   import dmdscript.darray;
   import dmdscript.program;
   import dmdscript.dobject;
   import dmdscript.property;
   import dmdscript.script;
   import dmdscript.opcodes;
   import dmdscript.text;
   import dmdscript.value;

   import std.file;
   import std.stdio;

   import std.c.stdlib;

   void main(char[][] args)
   {
     char[] scriptFilename = "script.js";
     char[] buff = cast(char[]) read(scriptFilename) ~ 
           "smain(arguments);";

     Program prog = new Program();
     prog.compile(scriptFilename, buff, null);

     Dobject dobj = new Dobject(null);
     dobj.Put("width",  new Value, DontDelete);
     dobj.Get("width").putVnumber(8);
     dobj.Put("height", new Value, DontDelete);
     dobj.Get("height").putVnumber(12);

     Darray darr = new Darray();
     for(d_uint32 i = 0; i < 8*12; i+=1)
     {
       darr.Put(i, new Value, DontDelete);
       darr.Get(i).putVstring("x");
     }

     dobj.Put("cells", darr, DontDelete);

     Darray arguments = new Darray();
     arguments.Put(0, new Value, DontDelete);
     arguments.Get(0).putVobject(dobj);

     execute(prog, arguments);
   }


   void execute(Program p, Darray args)
   {
     // ECMA 10.2.1
     //writef("Program.execute(argc = %d, argv = %p)\n", argc,
                    argv);
     //writef("Program.execute()\n");

     p.initContext();

     Value[] locals;
     Value ret;
     Value* result;
     CallContext *cc = p.callcontext;
     //  Darray arguments;
     Dobject dglobal = cc.global;
     Program program_save;

     // Set argv and argc for execute
     //  arguments = new Darray();
     dglobal.Put(TEXT_arguments, args, DontDelete | DontEnum);
     //  arguments.length.putVnumber(args.length);
     //  for (d_uint32 i = 0; i < args.length; i++)
     //  {
     //    arguments.Put(i, &args[i], DontEnum);
     //  }

     Value[] p1;
     Value* v;
     version (Win32)    // eh and alloca() not working under linux
     {
       if (p.globalfunction.nlocals < 128)
         v = cast(Value*)alloca(p.globalfunction.nlocals * 
                  Value.sizeof);
     }
     if (v)
         locals = v[0 .. p.globalfunction.nlocals];
     else
     {
         p1 = new Value[p.globalfunction.nlocals];
         locals = p1;
     }

     // Instantiate global variables as properties of global
     // object with 0 attributes
     p.globalfunction.instantiate(cc.variable, 0);


     program_save = p.getProgram();
     try
     {
       p.setProgram(p);
       ret.putVundefined();
       result = cast(Value*)IR.call(cc, cc.global, 
           p.globalfunction.code, &ret, locals.ptr);
     }
     finally
     {
       p.setProgram(program_save);
     }

     if (result)
     {
       ErrInfo errinfo;

       result.getErrInfo(&errinfo, cc.linnum);
       cc.linnum = 0;
       delete p1;
       throw new ScriptException(&errinfo);
     }

     delete p1;
   }


script.js
   function smain(args)
   {
     println("|arguments|: " + arguments.length);
     println("|args|: " + args.length);

     println("----------------");

     for(key in args[0])
       println("args[0]."+key+": " + args[0][key]);

     println("----------------");

     println("args[0].cells: " + args[0].cells);
     println("args[0].width: " + args[0].width);
     println("args[0].height: " + args[0].height);

   }

FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: March 7, 2012 19:08 (diff))