3

I'm having trouble using meta classes in DWScript.

We are using scripting to enable VARs and end users to customize our application.

Our application data basically consist of a lot of small objects in a tree structure. Each object can either be "dumb" in that it just displays data or it can be intelligent in some way. The intelligence is implemented with scripting by associating different script classes with the tree objects.

The problem I'm having is that the script needs to communicate to the Delphi side framework what script class it should use to implement the object. Basically I need to pass a script meta class to the Delphi side and store the information there in a format that can be safely persisted (by type name, as a string probably). I also need to be able to go the other way; I.e. return the meta class to the script from the Delphi side.

TdwsUnit declaration

type
  // Base class of all tree objects
  TItem = class
    ...
  end;


  // The meta class
  // This is actually declared in code since TdwsUnit doesn't have design time support for meta classes.
  // Shown here for readability.
  TItemClass = class of TItem;


// The procedure that passes the meta class to the Delphi side.
// I cannot use a TItemClass parameter as that isn't declared until run time (after the TdwsUnit has initialized its tables).
procedure RegisterItemClass(AClass: TClass);

The script

type
  TMyItem = class(TItem)
    ...
  end;

begin
  // Pass the meta class to the Delphi side.
  // The Delphi side will use this to create a script object of the specified type
  // and attach it to the Delphi side object.

  RegisterItemClass(TMyItem);
end;

Delphi implementation

Declaration of the meta class, TItemClass. Done in TdwsUnit.OnAfterInitUnitTable.

procedure TMyDataModule.dwsUnitMyClassesAfterInitUnitTable(Sender: TObject);
var
  ItemClass: TClassSymbol;
  MetaClass: TClassOfSymbol;
begin
  // Find the base class symbol
  ItemClass := dwsUnitMyClasses.Table.FindTypeLocal('TItem') as TClassSymbol;
  // Create a meta class symbol
  MetaClass := TClassOfSymbol.Create('TItemClass', ItemClass);
  dwsUnitMyClasses.Table.AddSymbol(MetaClass);
end;

RegisterItemClass implementation

procedure TMyDataModule.dwsUnitMyClassesFunctionsRegisterItemClassEval(info: TProgramInfo);
var
  ItemClassSymbol: TSymbol;
  ItemClassName: string;
begin
  ItemClassSymbol := TSymbol(Info.Params[0].ValueAsInteger);
  ItemClassName := ItemClassSymbol.Name;
  ...
end;

So the question is How does one get a TSymbol from a meta class parameter?
Edit: I found the answer to one part of the problem in this old question.
In short the solution is to cast the parameter value to a TSymbol:

However...

Now assuming that I store the class name as a string. How do I get from this class name back to a symbol? I need this because, just as the script can set the item class (using the code above), the script can also ask for an items' class.

I have tried looking in the symbol table with any of the four different methods that seem to do what I need but none of them can find the symbol.

var
  ItemClassName: string;
  ItemClassSymbol: TSymbol;
...
  ItemClassName := 'TMyItem';
...
  ItemClassSymbol := Info.Table.FindTypeSymbol(ItemClassName, cvMagic);
  if (ItemClassSymbol = nil) then
    ItemClassSymbol := Info.Table.FindSymbol(ItemClassName, cvMagic);
  if (ItemClassSymbol = nil) then
    ItemClassSymbol := Info.Table.FindTypeLocal(ItemClassName);
  if (ItemClassSymbol = nil) then
    ItemClassSymbol := Info.Table.FindLocal(ItemClassName);

  // ItemClassSymbol is nil at this point :-(

So the question is Given the name of meta class, declared in script, how does one get the corresponding TSymbol from the Delphi side?

Edit: I have now found one possible solution to the last part.

The following seems to work but I'm unsure if that is the correct way to do it. I would have thought that I would need to limit the scope of the symbol search to the current script unit.

var
  ItemClassName: string;
  ItemClassSymbol: TSymbol;
...
  ItemClassName := 'TMyItem';
...
  ItemClassSymbol := Info.Execution.Prog.RootTable.FindSymbol(ItemClassName, cvMagic);
  if (ItemClassSymbol = nil) then
    raise EScriptException.CreateFmt('ItemClass not found: %s', [ItemClassName]);

  Info.ResultAsInteger := Int64(ItemClassSymbol);
4

1 回答 1

2

除非我误解了,否则您可能不应该为最后一部分查找符号表,而是在 dwsUnitMyClassesFunctionsRegisterItemClassEval 中维护已注册项目类的表。

其背后的基本原理可能是用户可以在两种不同的上下文中拥有两个“TMyItem”符号,但只有一个已注册。注册的那个是你想要的,我认为没有一种可靠的方法来找出相关的符号(因为重要的上下文不会是你试图将字符串解析回的上下文一个符号,但与符号和字符串相关联的符号,即它的注册位置)

于 2014-04-16T10:21:00.390 回答