Jump to content
Sign in to follow this  
Yves

[Discussion] Spidermonkey upgrade

Recommended Posts

I understand I've been doing a lot of javascript. But yeah When I was using using C not declaring pointer was a bad thing. But if I recall correctly, not initializing an array of values is bad because weird stuff happens sometimes.

Share this post


Link to post
Share on other sites

I too really like the fact that spidermonkey is stricter.

You seem to have a lot time if you wait for these kind of errors popping up at compile time.

Share this post


Link to post
Share on other sites

not initializing an array of values is bad because weird stuff happens sometimes.

Please be more general. :) Not initializing any variable before use causes pretty crazy problems. The problem is initially one thinks they might be in an undefined state. But just like FLHerne said, the value is random. That's why if you want to have an undefined variable in e.g. Java you have to assign it null .. despite you thinking no initialisation means null anyway. This misunderstanding can generate heaps of problems.

It's easier for a machine to not care for the content at this point of memory than to force it being null, undefined, 0 or any other fixed value. To initialise all variables at system/program startup has to be programmed explicitely for each hardware/program. One can't rely on it. It's not certain. So always initialise variables at some point. Best not directly then when declarating the variable - though the compiler/interpreter probably is able to optimise this away. This is not too relevant for high level programming but rather for your engineering studies, Stan. Just wanted to warn you of what I at first had a hard time to understand. Still I'm just a noob.

You seem to have a lot time if you wait for these kind of errors popping up at compile time.

I think this is a misunderstanding. We're mixing Error and Warning now. Warning is okay, Error might be too much.

Share this post


Link to post
Share on other sites

It might be helpful to see an example, before jumping into conclusions. Although JS has no namespaces they can be implemented easily. 0 A.D loads AI files alphabetacally, but it is better to assume no order, otherwise renaming files leads to rewiring the boilerplate code, very annoying. A common pattern looks like this (taken from Hannibal):

HANNIBAL = (function(H){  H.HTN = H.HTN || {};               // line L1  H.HTN.Blocks = H.HTN.Blocks || {}; // line L2    // code continues

Let's say this code sits in file b.js, H.HTN in file a.js and H.HTN.Blocks in c.js. So in line 1 the left part is taken and in line 2 the right part. Sweet, convenient and failsafe.

However, now this produces a warning. To avoid that one might write:

HANNIBAL = (function(H){  H.HTN = (H.HTN !== undefined) ? H.HTN : {};                       // line L1  H.HTN.Blocks = (H.HTN.Blocks !== undefined) ? H.HTN.Blocks || {}; // line L2    // code continues

Coming from browser hosted JS, I also had in in mind undefined might be anything, but apparently "use strict" makes undefined read-only, and that IS a good thing. All discussed here: http://www.kenneth-truyers.net/2013/04/27/javascript-namespaces-and-modules/

Another example are user facing functions with a variable length of arguments.

economy = {  request: function(/* arguments: [amount,] resource [,locResource] */){ // ....

locResource is given in case a building is requested and not in case of a unit. So the next function world be happy if called with locResource === undefined, but that doesn't work either.

Edit: words

Edited by agentx

Share this post


Link to post
Share on other sites

@agentx

Have you really tested these cases?

It doesn't always warn you when a value is undefined.

I've tried something very similar.

I've added this line to multiple files of aegis:

m.Val = m.Val || "filename.js";

It didn't cause any warnings.

Share this post


Link to post
Share on other sites

Sorry, was netbook only couple of days, so tested again, with this at the very beginning:

var m = {};var o = {};m.num = num || 5;m.str = str || "abc";m.obj = obj || {};m.arr = arr || [];m.prp = o.prp || "xyz"; 
Only the last line produces no warning.
  • Like 1

Share this post


Link to post
Share on other sites

On object-orientated programming vs procedural programming:

Conclusion: Procedural style isn't a big issue with most of our current interfaces, but in some distinct cases it's a strong case to use object-orientation.

On 5/1/2013 at 10:54 PM, Yves said:

The general approach would be providing global functions to scripts instead of custom C++ objects with GetProperty and SetProperty overloads, custom properties and custom methods.

The object-oriented approach is sometimes inevitable and hence already in use in some cases. For example:

let label = Engine.GetGUIObjectByName("label");
label.caption = "foo";
label.tooltip = "bar";

To write this as procedural style, it would have to be:

Engine.SetGuiObjectProperty("label", "caption", "foo");
Engine.SetGuiObjectProperty("label", "tooltip", "bar");

But the main problem with procedural style is that it becomes messy when handling object instances and separation of private and public data.

Also notice the requirement to use strings,  numbers (or hardcoded C++ globals like g_NetServer) to identify C++ objects, if one doesn't want to allocate JS objects that refer to C++ instances.

The Networking code for example would be much cleaner if it would use object-orentation style (#5321):

Procedural:

Engine.CreateNetServer(...);
Engine.SetNetworkGameAttributes();
Engine.SendChat(...);
Engine.KickPlayer(...);
Engine.DisconnectFromNetServer();
Engine.StopNetServer();

Object-oriented:

 

var g_NetServer1 = Engine.CreateNetServer(...);
var g_NetServer2 = Engine.CreateNetServer(...);
g_NetServer1.KickPlayer(...)

var g_NetClient = Engine.ConnectToServer(...);
g_NetClient.SendChat(...);

g_NetClient = undefined;
g_NetServer = undefined;

Equally it was arguable to instantiate other components such as XmppClient or even the simulation (there is no additional cost to gain these freedoms if one would use OOP since the beginning).

A third example would be one parent GUI Pages wanting to close or update one of it's child GUI pages (#5369):

Object-oriented:

g_ChildPage = Engine.PushGuiPage(...);
g_ChildPage.updatePage(newData);

Procedural:

g_ChildPageName = Engine.PushGuiPage(...);
Engine.CallFunction(g_ChildPageName, "updatePage", newData);

For the other JSInterfaces (see also #4772), there is no strong case for OOP with the current code (as the JS GUI is still overall messy due to excessive procedural spaghetti):

##./source/gui/scripting/JSInterface_GUITypes.cpp
##./source/gui/scripting/JSInterface_IGUIObject.cpp
##./source/lobby/scripting/JSInterface_Lobby.cpp
##./source/network/scripting/JSInterface_Network.cpp

./source/graphics/scripting/JSInterface_GameView.cpp
./source/gui/scripting/JSInterface_GUIManager.cpp
./source/i18n/scripting/JSInterface_L10n.cpp
./source/ps/scripting/JSInterface_ConfigDB.cpp
./source/ps/scripting/JSInterface_Console.cpp
./source/ps/scripting/JSInterface_Debug.cpp
./source/ps/scripting/JSInterface_Game.cpp
./source/ps/scripting/JSInterface_Main.cpp
./source/ps/scripting/JSInterface_Mod.cpp
./source/ps/scripting/JSInterface_ModIo.cpp
./source/ps/scripting/JSInterface_SavedGame.cpp
./source/ps/scripting/JSInterface_UserReport.cpp
./source/ps/scripting/JSInterface_VFS.cpp
./source/ps/scripting/JSInterface_VisualReplay.cpp
./source/renderer/scripting/JSInterface_Renderer.cpp
./source/simulation2/scripting/JSInterface_Simulation.cpp
./source/soundmanager/scripting/JSInterface_Sound.cpp

One difficulty with C++ JS objects is that they can't be copied to other contexts currently (but that should be practically solveable, because these JS objects may only store one void* and that can be cloned).

Quote

That's more or less the same approach Philip has chosen for the Simulation code and it seems to work quite well there.

Perhaps you refer to the GUI<->simulation interface in JSInterface_Simulation.cpp, that uses global procedures.
But the JS simulation <-> C++ simulation script interaction is an example of object-oriented implementation, not procedural!

Quote

Object oriented design is good, but I think it's enough to have an object oriented scripting language and an object oriented engine.

This argument seems to the contrary - if X and Y are object-oriented, it's typically easier to have them interact directly rather than adding an object-procedural and a procedural-object translation layer (indirection). (At last depends on implementation details which is easier to implement.)

Quote

The interface between JS and the engine benefits more from a clean separation than from OOP. If there are global functions that can be called like "Engine.DoWhatEver()" it's clear that it is an engine function. Properties on the other hand can be script properties or native properties with custom getter- and setter functions that do potentially unexpected things. It's not obvious when using an object if it's a JS object or a C++ object with some custom functionality.

While it is true that it is clear that `Engine.Foo` is an engine function and that it allows finding many but not all JS GUI <->C++ GUI interaction.
But it should not be unexpected to any programmer that the properties of `Engine.GetGuiObjectByName` objects or likewise interact with C++.

On the flipside, with a global object-oriented style it were easier to find all objects that are created in C++ and it becomes easier to see which Engine functions relate to which C++ object instance.
For example it's not clear which Engine.NetworkFoo functions relate to the NetClient and which to the NetServer.
In procedural style it would have to be solved adding the type to the name: Engine.NetServerDoFoo and Engine.NetClientDoFoo, but that's then already mimicing OOP.

Share this post


Link to post
Share on other sites

Between Spidermonkey 1.8.5 and 38 there have been many profound API changes and it took us countless hours to adapt the code to these changes. The GUI was especially difficult because it used a wider range of different API functions for the object oriented aspects. During the upgrade we have reduced the complexity of this interaction, which helped streamline the upgrade process in the future.

You have valid points for the object oriented approach and I'm not against it, in principle. I just advise to keep the upgrades in mind an avoid adding too much complexity and diversity to the C++ <-> JS interaction. I don't know how much the Spidermonkey API keeps changing nowadays, but given how much Javascript is still changing, I suspect that's still an issue.

  • Like 2
  • Thanks 3

Share this post


Link to post
Share on other sites
2 hours ago, Yves said:

Between Spidermonkey 1.8.5 and 38 there have been many profound API changes and it took us countless hours to adapt the code to these changes. The GUI was especially difficult because it used a wider range of different API functions for the object oriented aspects. During the upgrade we have reduced the complexity of this interaction, which helped streamline the upgrade process in the future.

You have valid points for the object oriented approach and I'm not against it, in principle. I just advise to keep the upgrades in mind an avoid adding too much complexity and diversity to the C++ <-> JS interaction. I don't know how much the Spidermonkey API keeps changing nowadays, but given how much Javascript is still changing, I suspect that's still an issue.

Hey Yves thanks for the insight, and if I might say, welcome back.

Share this post


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.

Sign in to follow this  

×
×
  • Create New...