65.9K
CodeProject is changing. Read more.
Home

JavaScript Reflection

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (7 votes)

Jul 27, 2007

CPOL

2 min read

viewsIcon

28460

downloadIcon

198

Reflection of JavaScript Object through Text Parsing of *.js files

Introduction

I was writing an application for creating a manifest for JavaScript. I was unable to do it using DOM; instead I wrote my own application for parsing JavaScript files. It is slightly tough to peek into JavaScript objects, initially I could only get the members and fields using for-in. but that was not enough, what if someone stored a file locally and I have to write code to execute it, I would be able to get only the members, just executing members without passing the required input parameter if any will not work. The code below will parse the file and give me the options as to what input is required and if something is returned, what it could possibly be returning except primitive types. Knowing these things in advance makes it easier to execute the code. Well it worked for me; let's see how it works for you. I know many people are looking for something like this.

Using the Code

The code might not be perfect or might not do everything for you, since I wrote it to cater to my needs. But the basic utilities are implemented, you can add things according to your needs. I am using the code from my project, therefore you will not see a lot of other code that the project refers to, but you can input your JavaScript file to see the results or use the sample file that's provided in the project.

Some assumptions that I made; it will only look for 'prototype' and 'this' keywords in the JS code.

The Core Code

This code finds all the objects, their members and inputs for each member:

   1:        x = content.IndexOf(".prototype.", x);
   2:
   3:        for (int i = 0; x != -1; i++)
   4:        {
   5:           int j = 0;
   6:           int k = 0;
   7:
   8:           for (j = x - 1; (' ' != content[j] && '\n' != content[j]); j--) { }
   9:           for (k = x + 10; (' ' != content[k] && '=' != content[k]); k++) { }
  10:
  11:           int y = content.IndexOf("function(", k);
  12:
  13:           if (y == -1)
  14:              break;
  15:
  16:           int z = content.IndexOf(')', y);
  17:           string[] inputs = content.Substring
                                  (y + 8 + 1, z - (y + 8) - 1).Split(chArr);
  18:           string className = content.Substring(j + 1, x - j - 1);
  19:           string member = content.Substring(x + 10 + 1, k - (x + 10) - 1);
  20:
  21:           JSObject jsObj = null;
  22:
  23:           if (!jsObjs.TryGetValue(className, out jsObj))
  24:           {
  25:              jsObj = new JSObject(className);
  26:              jsObjs.Add(jsObj.Name, jsObj);
  27:           }
  28:
  29:           JSOperation operation = jsObj.addOperation(member);
  30:
  31:           for (j = 0; j < inputs.Length; j++)
  32:           {
  33:              if (inputs[j] != string.Empty)
  34:                 operation.addInput(inputs[j].Trim());
  35:           }
  36:
  37:           x = content.IndexOf(".prototype.", x + 1);
  38:        }

The code below finds out all the methods that could be potential outputs for any of the operations, if the operation is not returning primitive data types then most likely it could be one of these types. The problem with associating the output to the operation is to find out what the return type is, since JavaScript supports loosely typed data, no one can be sure of the output data type unless the code is executed. Therefore this code at least looks up all the non-primitive data types that could be one of the output types.

   1:        x = content.IndexOf("this.", 0);
   2:
   3:        for (int i = 0; x != -1; i++)
   4:        {
   5:            int j = 0;
   6:            int k = 0;
   7:            string strObj = string.Empty;
   8:
   9:            for (j = x - 1; '{' != content[j]; j--) { }
  10:
  11:            for (k = j - 1; ; k--)
  12:            {
  13:                string fun = content.Substring(k, 8);
  14:
  15:                if (fun == "function")
  16:                    break;
  17:            }
  18:
  19:            int y = 0;
  20:
  21:            for (y = k + 8; content[y] != '('; y++) { }
  22:
  23:            if ((k + 8) < y)
  24:            {
  25:                string objName = content.Substring(k + 9, y - (k + 9));
  26:                JSOutputFunction obj = new JSOutputFunction();
  27:                obj.ObjName = objName;
  28:                int cnt = 1;
  29:
  30:                for (k = j + 1; cnt != 0; k++)
  31:                {
  32:                    if (content[k] == '{')
  33:                        cnt++;
  34:
  35:                    if (content[k] == '}')
  36:                        cnt--;
  37:                }
  38:
  39:                strObj = content.Substring(j, k - j);
  40:                k = strObj.IndexOf("this.", 0);
  41:
  42:                for (; k != -1; )
  43:                {
  44:                    y = strObj.IndexOfAny(ch, k + 1);
  45:                    string fieldName = strObj.Substring(k + 5, y - k - 5);
  46:
  47:                    if (fieldName.Trim() != "toString")
  48:                        obj.AddField(fieldName.Trim());
  49:
  50:                    k = strObj.IndexOf("this.", k + 1);
  51:                }
  52:                outputUtil.AddOutput(obj);
  53:            }
  54:            else
  55:                j = x + 1;
  56:            x = content.IndexOf("this.", j + strObj.Length);
  57:        }

After running the application, it would look something like this:

Screenshot - JSRefle.jpg

If I missed something or you have any questions, please email me. If you have any feedback or comments, please do write. I would definitely appreciate it.

History

  • 27th July, 2007: Initial post

About the Author

I am currently working as a Program Manager at Microsoft.

Shreyans Kothari (MCAD)
Program Manager
Aditi @ Microsoft
Feedback/Comments