Removes the ScriptInterface and SpiderMonkey dependencies from Atlas.

Replaces the code for JSON parsing and writing with an implementation
using the JSON Spirit library.

Fixes #2434
Refs #1886

This was SVN commit r14783.
This commit is contained in:
Yves 2014-02-24 20:22:02 +00:00
parent 33918a5c41
commit 17f8f09399
22 changed files with 1923 additions and 1291 deletions

View File

@ -1021,31 +1021,19 @@ function setup_atlas_projects()
setup_atlas_project("AtlasObject", "StaticLib",
{ -- src
"."
".",
"../../../third_party/jsonspirit"
},{ -- include
"../../../third_party/jsonspirit"
},{ -- extern_libs
"boost",
"libxml2",
"spidermonkey",
"wxwidgets"
},{ -- extra_params
no_pch = 1
})
setup_atlas_project("AtlasScript", "StaticLib",
{ -- src
"."
},{ -- include
".."
},{ -- extern_libs
"boost",
"spidermonkey",
"valgrind",
"wxwidgets",
},{ -- extra_params
no_pch = 1
})
atlas_src = {
"ActorEditor",
"CustomControls/Buttons",
@ -1076,8 +1064,7 @@ function setup_atlas_projects()
"ScenarioEditor/Tools/Common",
}
atlas_extra_links = {
"AtlasObject",
"AtlasScript",
"AtlasObject"
}
atlas_extern_libs = {
@ -1087,7 +1074,6 @@ function setup_atlas_projects()
--"ffmpeg", -- disabled for now because it causes too many build difficulties
"libxml2",
"sdl", -- key definitions
"spidermonkey",
"wxwidgets",
"zlib",
}
@ -1117,10 +1103,6 @@ function setup_atlas_frontend_project (project_name)
local target_type = get_main_project_target_type()
project_create(project_name, target_type)
project_add_extern_libs({
"spidermonkey",
},
target_type)
project_add_x11_dirs()
local source_root = rootdir.."/source/tools/atlas/AtlasFrontends/"

View File

@ -0,0 +1,24 @@
The MIT License
Copyright (c) 2007 - 2010 John W. Wilkinson
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,11 @@
This separate JSON library is used for Atlas to avoid the SpiderMonkey dependency.
SpiderMonkey is a fully featured JS engine and even though we already use it for the main engine, it's too heavy-weight to use it in Atlas.
The SpiderMonkey API also changes frequently and we hope that the JSON parsing code needs less changes when we use this separate library.
Get the library from here:
http://www.codeproject.com/Articles/20027/JSON-Spirit-A-C-JSON-Parser-Generator-Implemented
The currently used version was released on the 23th of May 2013.
Search for this comment in json_spirit_value.h and uncomment the lines we don't need:
// comment out the value types you don't need to reduce build times and intermediate file sizes

View File

@ -0,0 +1,54 @@
#ifndef JSON_SPIRIT_ERROR_POSITION
#define JSON_SPIRIT_ERROR_POSITION
// Copyright John W. Wilkinson 2007 - 2013
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.06
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include <string>
namespace json_spirit
{
// An Error_position exception is thrown by the "read_or_throw" functions below on finding an error.
// Note the "read_or_throw" functions are around 3 times slower than the standard functions "read"
// functions that return a bool.
//
struct Error_position
{
Error_position();
Error_position( unsigned int line, unsigned int column, const std::string& reason );
bool operator==( const Error_position& lhs ) const;
unsigned int line_;
unsigned int column_;
std::string reason_;
};
inline Error_position::Error_position()
: line_( 0 )
, column_( 0 )
{
}
inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason )
: line_( line )
, column_( column )
, reason_( reason )
{
}
inline bool Error_position::operator==( const Error_position& lhs ) const
{
if( this == &lhs ) return true;
return ( reason_ == lhs.reason_ ) &&
( line_ == lhs.line_ ) &&
( column_ == lhs.column_ );
}
}
#endif

View File

@ -0,0 +1,650 @@
#ifndef JSON_SPIRIT_READER_TEMPLATE
#define JSON_SPIRIT_READER_TEMPLATE
// Copyright John W. Wilkinson 2007 - 2013
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.06
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include "json_spirit_value.h"
#include "json_spirit_error_position.h"
//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION >= 103800
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_confix.hpp>
#include <boost/spirit/include/classic_escape_char.hpp>
#include <boost/spirit/include/classic_multi_pass.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#define spirit_namespace boost::spirit::classic
#else
#include <boost/spirit/core.hpp>
#include <boost/spirit/utility/confix.hpp>
#include <boost/spirit/utility/escape_char.hpp>
#include <boost/spirit/iterator/multi_pass.hpp>
#include <boost/spirit/iterator/position_iterator.hpp>
#define spirit_namespace boost::spirit
#endif
namespace json_spirit
{
const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >();
const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >();
template< class Iter_type >
bool is_eq( Iter_type first, Iter_type last, const char* c_str )
{
for( Iter_type i = first; i != last; ++i, ++c_str )
{
if( *c_str == 0 ) return false;
if( *i != *c_str ) return false;
}
return true;
}
template< class Char_type >
Char_type hex_to_num( const Char_type c )
{
if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0';
if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10;
if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10;
return 0;
}
template< class Char_type, class Iter_type >
Char_type hex_str_to_char( Iter_type& begin )
{
const Char_type c1( *( ++begin ) );
const Char_type c2( *( ++begin ) );
return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 );
}
template< class Char_type, class Iter_type >
Char_type unicode_str_to_char( Iter_type& begin )
{
const Char_type c1( *( ++begin ) );
const Char_type c2( *( ++begin ) );
const Char_type c3( *( ++begin ) );
const Char_type c4( *( ++begin ) );
return ( hex_to_num( c1 ) << 12 ) +
( hex_to_num( c2 ) << 8 ) +
( hex_to_num( c3 ) << 4 ) +
hex_to_num( c4 );
}
template< class String_type >
void append_esc_char_and_incr_iter( String_type& s,
typename String_type::const_iterator& begin,
typename String_type::const_iterator end )
{
typedef typename String_type::value_type Char_type;
const Char_type c2( *begin );
switch( c2 )
{
case 't': s += '\t'; break;
case 'b': s += '\b'; break;
case 'f': s += '\f'; break;
case 'n': s += '\n'; break;
case 'r': s += '\r'; break;
case '\\': s += '\\'; break;
case '/': s += '/'; break;
case '"': s += '"'; break;
case 'x':
{
if( end - begin >= 3 ) // expecting "xHH..."
{
s += hex_str_to_char< Char_type >( begin );
}
break;
}
case 'u':
{
if( end - begin >= 5 ) // expecting "uHHHH..."
{
s += unicode_str_to_char< Char_type >( begin );
}
break;
}
}
}
template< class String_type >
String_type substitute_esc_chars( typename String_type::const_iterator begin,
typename String_type::const_iterator end )
{
typedef typename String_type::const_iterator Iter_type;
if( end - begin < 2 ) return String_type( begin, end );
String_type result;
result.reserve( end - begin );
const Iter_type end_minus_1( end - 1 );
Iter_type substr_start = begin;
Iter_type i = begin;
for( ; i < end_minus_1; ++i )
{
if( *i == '\\' )
{
result.append( substr_start, i );
++i; // skip the '\'
append_esc_char_and_incr_iter( result, i, end );
substr_start = i + 1;
}
}
result.append( substr_start, end );
return result;
}
template< class String_type >
String_type get_str_( typename String_type::const_iterator begin,
typename String_type::const_iterator end )
{
assert( end - begin >= 2 );
typedef typename String_type::const_iterator Iter_type;
Iter_type str_without_quotes( ++begin );
Iter_type end_without_quotes( --end );
return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes );
}
inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end )
{
return get_str_< std::string >( begin, end );
}
inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end )
{
return get_str_< std::wstring >( begin, end );
}
template< class String_type, class Iter_type >
String_type get_str( Iter_type begin, Iter_type end )
{
const String_type tmp( begin, end ); // convert multipass iterators to string iterators
return get_str( tmp.begin(), tmp.end() );
}
// this class's methods get called by the spirit parse resulting
// in the creation of a JSON object or array
//
// NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator
//
template< class Value_type, class Iter_type >
class Semantic_actions
{
public:
typedef typename Value_type::Config_type Config_type;
typedef typename Config_type::String_type String_type;
typedef typename Config_type::Object_type Object_type;
typedef typename Config_type::Array_type Array_type;
typedef typename String_type::value_type Char_type;
Semantic_actions( Value_type& value )
: value_( value )
, current_p_( 0 )
{
}
void begin_obj( Char_type c )
{
assert( c == '{' );
begin_compound< Object_type >();
}
void end_obj( Char_type c )
{
assert( c == '}' );
end_compound();
}
void begin_array( Char_type c )
{
assert( c == '[' );
begin_compound< Array_type >();
}
void end_array( Char_type c )
{
assert( c == ']' );
end_compound();
}
void new_name( Iter_type begin, Iter_type end )
{
assert( current_p_->type() == obj_type );
name_ = get_str< String_type >( begin, end );
}
void new_str( Iter_type begin, Iter_type end )
{
add_to_current( get_str< String_type >( begin, end ) );
}
void new_true( Iter_type begin, Iter_type end )
{
assert( is_eq( begin, end, "true" ) );
add_to_current( true );
}
void new_false( Iter_type begin, Iter_type end )
{
assert( is_eq( begin, end, "false" ) );
add_to_current( false );
}
void new_null( Iter_type begin, Iter_type end )
{
assert( is_eq( begin, end, "null" ) );
add_to_current( Value_type() );
}
void new_int( boost::int64_t i )
{
add_to_current( i );
}
void new_uint64( boost::uint64_t ui )
{
add_to_current( ui );
}
void new_real( double d )
{
add_to_current( d );
}
private:
Semantic_actions& operator=( const Semantic_actions& );
// to prevent "assignment operator could not be generated" warning
Value_type* add_first( const Value_type& value )
{
assert( current_p_ == 0 );
value_ = value;
current_p_ = &value_;
return current_p_;
}
template< class Array_or_obj >
void begin_compound()
{
if( current_p_ == 0 )
{
add_first( Array_or_obj() );
}
else
{
stack_.push_back( current_p_ );
Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place
current_p_ = add_to_current( new_array_or_obj );
}
}
void end_compound()
{
if( current_p_ != &value_ )
{
current_p_ = stack_.back();
stack_.pop_back();
}
}
Value_type* add_to_current( const Value_type& value )
{
if( current_p_ == 0 )
{
return add_first( value );
}
else if( current_p_->type() == array_type )
{
current_p_->get_array().push_back( value );
return &current_p_->get_array().back();
}
assert( current_p_->type() == obj_type );
return &Config_type::add( current_p_->get_obj(), name_, value );
}
Value_type& value_; // this is the object or array that is being created
Value_type* current_p_; // the child object or array that is currently being constructed
std::vector< Value_type* > stack_; // previous child objects and arrays
String_type name_; // of current name/value pair
};
template< typename Iter_type >
void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason )
{
throw Error_position( i.get_position().line, i.get_position().column, reason );
}
template< typename Iter_type >
void throw_error( Iter_type i, const std::string& reason )
{
throw reason;
}
// the spirit grammer
//
template< class Value_type, class Iter_type >
class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > >
{
public:
typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t;
Json_grammer( Semantic_actions_t& semantic_actions )
: actions_( semantic_actions )
{
}
static void throw_not_value( Iter_type begin, Iter_type end )
{
throw_error( begin, "not a value" );
}
static void throw_not_array( Iter_type begin, Iter_type end )
{
throw_error( begin, "not an array" );
}
static void throw_not_object( Iter_type begin, Iter_type end )
{
throw_error( begin, "not an object" );
}
static void throw_not_pair( Iter_type begin, Iter_type end )
{
throw_error( begin, "not a pair" );
}
static void throw_not_colon( Iter_type begin, Iter_type end )
{
throw_error( begin, "no colon in pair" );
}
static void throw_not_string( Iter_type begin, Iter_type end )
{
throw_error( begin, "not a string" );
}
template< typename ScannerT >
class definition
{
public:
definition( const Json_grammer& self )
{
using namespace spirit_namespace;
typedef typename Value_type::String_type::value_type Char_type;
// first we convert the semantic action class methods to functors with the
// parameter signature expected by spirit
typedef boost::function< void( Char_type ) > Char_action;
typedef boost::function< void( Iter_type, Iter_type ) > Str_action;
typedef boost::function< void( double ) > Real_action;
typedef boost::function< void( boost::int64_t ) > Int_action;
typedef boost::function< void( boost::uint64_t ) > Uint64_action;
Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) );
Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) );
Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) );
Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) );
Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) );
Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) );
Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) );
Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) );
Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) );
Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) );
Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) );
Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) );
// actual grammer
json_
= value_ | eps_p[ &throw_not_value ]
;
value_
= string_[ new_str ]
| number_
| object_
| array_
| str_p( "true" ) [ new_true ]
| str_p( "false" )[ new_false ]
| str_p( "null" ) [ new_null ]
;
object_
= ch_p('{')[ begin_obj ]
>> !members_
>> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] )
;
members_
= pair_ >> *( ',' >> pair_ )
;
pair_
= string_[ new_name ]
>> ( ':' | eps_p[ &throw_not_colon ] )
>> ( value_ | eps_p[ &throw_not_value ] )
;
array_
= ch_p('[')[ begin_array ]
>> !elements_
>> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] )
;
elements_
= value_ >> *( ',' >> value_ )
;
string_
= lexeme_d // this causes white space and what would appear to be comments inside a string to be retained
[
confix_p
(
'"',
*lex_escape_ch_p,
'"'
)
]
;
number_
= strict_real_p[ new_real ]
| int64_p [ new_int ]
| uint64_p [ new_uint64 ]
;
}
spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_;
const spirit_namespace::rule< ScannerT >& start() const { return json_; }
};
private:
Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning
Semantic_actions_t& actions_;
};
template< class Iter_type, class Value_type >
void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
{
typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t;
const Posn_iter_t posn_begin( begin, end );
const Posn_iter_t posn_end( end, end );
read_range_or_throw( posn_begin, posn_end, value );
}
template< class Istream_type >
struct Multi_pass_iters
{
typedef typename Istream_type::char_type Char_type;
typedef std::istream_iterator< Char_type, Char_type > istream_iter;
typedef spirit_namespace::multi_pass< istream_iter > Mp_iter;
Multi_pass_iters( Istream_type& is )
{
is.unsetf( std::ios::skipws );
begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) );
end_ = spirit_namespace::make_multi_pass( istream_iter() );
}
Mp_iter begin_;
Mp_iter end_;
};
// reads a JSON Value from a pair of input iterators throwing an exception on invalid input, e.g.
//
// string::const_iterator start = str.begin();
// const string::const_iterator next = read_range_or_throw( str.begin(), str.end(), value );
//
// The iterator 'next' will point to the character past the
// last one read.
//
template< class Iter_type, class Value_type >
Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
{
Semantic_actions< Value_type, Iter_type > semantic_actions( value );
const spirit_namespace::parse_info< Iter_type > info =
spirit_namespace::parse( begin, end,
Json_grammer< Value_type, Iter_type >( semantic_actions ),
spirit_namespace::space_p |
spirit_namespace::comment_p("//") |
spirit_namespace::comment_p("/*", "*/") );
if( !info.hit )
{
assert( false ); // in theory exception should already have been thrown
throw_error( info.stop, "error" );
}
return info.stop;
}
// reads a JSON Value from a pair of input iterators, e.g.
//
// string::const_iterator start = str.begin();
// const bool success = read_string( start, str.end(), value );
//
// The iterator 'start' will point to the character past the
// last one read.
//
template< class Iter_type, class Value_type >
bool read_range( Iter_type& begin, Iter_type end, Value_type& value )
{
try
{
begin = read_range_or_throw( begin, end, value );
return true;
}
catch( ... )
{
return false;
}
}
// reads a JSON Value from a string, e.g.
//
// const bool success = read_string( str, value );
//
template< class String_type, class Value_type >
bool read_string( const String_type& s, Value_type& value )
{
typename String_type::const_iterator begin = s.begin();
return read_range( begin, s.end(), value );
}
// reads a JSON Value from a string throwing an exception on invalid input, e.g.
//
// read_string_or_throw( is, value );
//
template< class String_type, class Value_type >
void read_string_or_throw( const String_type& s, Value_type& value )
{
add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value );
}
// reads a JSON Value from a stream, e.g.
//
// const bool success = read_stream( is, value );
//
template< class Istream_type, class Value_type >
bool read_stream( Istream_type& is, Value_type& value )
{
Multi_pass_iters< Istream_type > mp_iters( is );
return read_range( mp_iters.begin_, mp_iters.end_, value );
}
// reads a JSON Value from a stream throwing an exception on invalid input, e.g.
//
// read_stream_or_throw( is, value );
//
template< class Istream_type, class Value_type >
void read_stream_or_throw( Istream_type& is, Value_type& value )
{
const Multi_pass_iters< Istream_type > mp_iters( is );
add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value );
}
}
#endif

View File

@ -0,0 +1,605 @@
#ifndef JSON_SPIRIT_VALUE
#define JSON_SPIRIT_VALUE
// Copyright John W. Wilkinson 2007 - 2013
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.06
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include <vector>
#include <map>
#include <string>
#include <cassert>
#include <sstream>
#include <stdexcept>
#include <boost/config.hpp>
#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/variant.hpp>
// comment out the value types you don't need to reduce build times and intermediate file sizes
#define JSON_SPIRIT_VALUE_ENABLED
//#define JSON_SPIRIT_WVALUE_ENABLED
//#define JSON_SPIRIT_MVALUE_ENABLED
//#define JSON_SPIRIT_WMVALUE_ENABLED
namespace json_spirit
{
enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type };
static std::string value_type_to_string( Value_type vtype );
struct Null{};
template< class Config > // Config determines whether the value uses std::string or std::wstring and
// whether JSON Objects are represented as vectors or maps
class Value_impl
{
public:
typedef Config Config_type;
typedef typename Config::String_type String_type;
typedef typename Config::Object_type Object;
typedef typename Config::Array_type Array;
typedef typename String_type::const_pointer Const_str_ptr; // eg const char*
Value_impl(); // creates null value
Value_impl( Const_str_ptr value );
Value_impl( const String_type& value );
Value_impl( const Object& value );
Value_impl( const Array& value );
Value_impl( bool value );
Value_impl( int value );
Value_impl( boost::int64_t value );
Value_impl( boost::uint64_t value );
Value_impl( double value );
template< class Iter >
Value_impl( Iter first, Iter last ); // constructor from containers, e.g. std::vector or std::list
template< BOOST_VARIANT_ENUM_PARAMS( typename T ) >
Value_impl( const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& variant ); // constructor for compatible variant types
Value_impl( const Value_impl& other );
bool operator==( const Value_impl& lhs ) const;
Value_impl& operator=( const Value_impl& lhs );
Value_type type() const;
bool is_uint64() const;
bool is_null() const;
const String_type& get_str() const;
const Object& get_obj() const;
const Array& get_array() const;
bool get_bool() const;
int get_int() const;
boost::int64_t get_int64() const;
boost::uint64_t get_uint64() const;
double get_real() const;
Object& get_obj();
Array& get_array();
template< typename T > T get_value() const; // example usage: int i = value.get_value< int >();
// or double d = value.get_value< double >();
static const Value_impl null;
private:
void check_type( const Value_type vtype ) const;
typedef boost::variant< boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >,
String_type, bool, boost::int64_t, double, Null, boost::uint64_t > Variant;
Variant v_;
class Variant_converter_visitor : public boost::static_visitor< Variant >
{
public:
template< typename T, typename A, template< typename, typename > class Cont >
Variant operator()( const Cont< T, A >& cont ) const
{
return Array( cont.begin(), cont.end() );
}
Variant operator()( int i ) const
{
return static_cast< boost::int64_t >( i );
}
template<class T>
Variant operator()( const T& t ) const
{
return t;
}
};
};
// vector objects
template< class Config >
struct Pair_impl
{
typedef typename Config::String_type String_type;
typedef typename Config::Value_type Value_type;
Pair_impl()
{
}
Pair_impl( const String_type& name, const Value_type& value );
bool operator==( const Pair_impl& lhs ) const;
String_type name_;
Value_type value_;
};
#if defined( JSON_SPIRIT_VALUE_ENABLED ) || defined( JSON_SPIRIT_WVALUE_ENABLED )
template< class String >
struct Config_vector
{
typedef String String_type;
typedef Value_impl< Config_vector > Value_type;
typedef Pair_impl < Config_vector > Pair_type;
typedef std::vector< Value_type > Array_type;
typedef std::vector< Pair_type > Object_type;
static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
{
obj.push_back( Pair_type( name , value ) );
return obj.back().value_;
}
static const String_type& get_name( const Pair_type& pair )
{
return pair.name_;
}
static const Value_type& get_value( const Pair_type& pair )
{
return pair.value_;
}
};
#endif
// typedefs for ASCII
#ifdef JSON_SPIRIT_VALUE_ENABLED
typedef Config_vector< std::string > Config;
typedef Config::Value_type Value;
typedef Config::Pair_type Pair;
typedef Config::Object_type Object;
typedef Config::Array_type Array;
#endif
// typedefs for Unicode
#if defined( JSON_SPIRIT_WVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING )
typedef Config_vector< std::wstring > wConfig;
typedef wConfig::Value_type wValue;
typedef wConfig::Pair_type wPair;
typedef wConfig::Object_type wObject;
typedef wConfig::Array_type wArray;
#endif
// map objects
#if defined( JSON_SPIRIT_MVALUE_ENABLED ) || defined( JSON_SPIRIT_WMVALUE_ENABLED )
template< class String >
struct Config_map
{
typedef String String_type;
typedef Value_impl< Config_map > Value_type;
typedef std::vector< Value_type > Array_type;
typedef std::map< String_type, Value_type > Object_type;
typedef std::pair< const String_type, Value_type > Pair_type;
static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
{
return obj[ name ] = value;
}
static const String_type& get_name( const Pair_type& pair )
{
return pair.first;
}
static const Value_type& get_value( const Pair_type& pair )
{
return pair.second;
}
};
#endif
// typedefs for ASCII
#ifdef JSON_SPIRIT_MVALUE_ENABLED
typedef Config_map< std::string > mConfig;
typedef mConfig::Value_type mValue;
typedef mConfig::Object_type mObject;
typedef mConfig::Array_type mArray;
#endif
// typedefs for Unicode
#if defined( JSON_SPIRIT_WMVALUE_ENABLED ) && !defined( BOOST_NO_STD_WSTRING )
typedef Config_map< std::wstring > wmConfig;
typedef wmConfig::Value_type wmValue;
typedef wmConfig::Object_type wmObject;
typedef wmConfig::Array_type wmArray;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////
//
// implementation
inline bool operator==( const Null&, const Null& )
{
return true;
}
template< class Config >
const Value_impl< Config > Value_impl< Config >::null;
template< class Config >
Value_impl< Config >::Value_impl()
: v_( Null() )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Const_str_ptr value )
: v_( String_type( value ) )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const String_type& value )
: v_( value )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Object& value )
: v_( value )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Array& value )
: v_( value )
{
}
template< class Config >
Value_impl< Config >::Value_impl( bool value )
: v_( value )
{
}
template< class Config >
Value_impl< Config >::Value_impl( int value )
: v_( static_cast< boost::int64_t >( value ) )
{
}
template< class Config >
Value_impl< Config >::Value_impl( boost::int64_t value )
: v_( value )
{
}
template< class Config >
Value_impl< Config >::Value_impl( boost::uint64_t value )
: v_( value )
{
}
template< class Config >
Value_impl< Config >::Value_impl( double value )
: v_( value )
{
}
template< class Config >
Value_impl< Config >::Value_impl( const Value_impl< Config >& other )
: v_( other.v_ )
{
}
template< class Config >
template< class Iter >
Value_impl< Config >::Value_impl( Iter first, Iter last )
: v_( Array( first, last ) )
{
}
template< class Config >
template< BOOST_VARIANT_ENUM_PARAMS( typename T ) >
Value_impl< Config >::Value_impl( const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& variant )
: v_( boost::apply_visitor( Variant_converter_visitor(), variant) )
{
}
template< class Config >
Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs )
{
Value_impl tmp( lhs );
std::swap( v_, tmp.v_ );
return *this;
}
template< class Config >
bool Value_impl< Config >::operator==( const Value_impl& lhs ) const
{
if( this == &lhs ) return true;
if( type() != lhs.type() ) return false;
return v_ == lhs.v_;
}
template< class Config >
Value_type Value_impl< Config >::type() const
{
if( is_uint64() )
{
return int_type;
}
return static_cast< Value_type >( v_.which() );
}
template< class Config >
bool Value_impl< Config >::is_uint64() const
{
return v_.which() == null_type + 1;
}
template< class Config >
bool Value_impl< Config >::is_null() const
{
return type() == null_type;
}
template< class Config >
void Value_impl< Config >::check_type( const Value_type vtype ) const
{
if( type() != vtype )
{
std::ostringstream os;
os << "get_value< " << value_type_to_string( vtype ) << " > called on " << value_type_to_string( type() ) << " Value";
throw std::runtime_error( os.str() );
}
}
template< class Config >
const typename Config::String_type& Value_impl< Config >::get_str() const
{
check_type( str_type );
return *boost::get< String_type >( &v_ );
}
template< class Config >
const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const
{
check_type( obj_type );
return *boost::get< Object >( &v_ );
}
template< class Config >
const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const
{
check_type( array_type );
return *boost::get< Array >( &v_ );
}
template< class Config >
bool Value_impl< Config >::get_bool() const
{
check_type( bool_type );
return boost::get< bool >( v_ );
}
template< class Config >
int Value_impl< Config >::get_int() const
{
check_type( int_type );
return static_cast< int >( get_int64() );
}
template< class Config >
boost::int64_t Value_impl< Config >::get_int64() const
{
check_type( int_type );
if( is_uint64() )
{
return static_cast< boost::int64_t >( get_uint64() );
}
return boost::get< boost::int64_t >( v_ );
}
template< class Config >
boost::uint64_t Value_impl< Config >::get_uint64() const
{
check_type( int_type );
if( !is_uint64() )
{
return static_cast< boost::uint64_t >( get_int64() );
}
return boost::get< boost::uint64_t >( v_ );
}
template< class Config >
double Value_impl< Config >::get_real() const
{
if( type() == int_type )
{
return is_uint64() ? static_cast< double >( get_uint64() )
: static_cast< double >( get_int64() );
}
check_type( real_type );
return boost::get< double >( v_ );
}
template< class Config >
typename Value_impl< Config >::Object& Value_impl< Config >::get_obj()
{
check_type( obj_type );
return *boost::get< Object >( &v_ );
}
template< class Config >
typename Value_impl< Config >::Array& Value_impl< Config >::get_array()
{
check_type( array_type );
return *boost::get< Array >( &v_ );
}
template< class Config >
Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value )
: name_( name )
, value_( value )
{
}
template< class Config >
bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const
{
if( this == &lhs ) return true;
return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ );
}
// converts a C string, ie. 8 bit char array, to a string object
//
template < class String_type >
String_type to_str( const char* c_str )
{
String_type result;
for( const char* p = c_str; *p != 0; ++p )
{
result += *p;
}
return result;
}
//
namespace internal_
{
template< typename T >
struct Type_to_type
{
};
template< class Value >
int get_value( const Value& value, Type_to_type< int > )
{
return value.get_int();
}
template< class Value >
boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > )
{
return value.get_int64();
}
template< class Value >
boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > )
{
return value.get_uint64();
}
template< class Value >
double get_value( const Value& value, Type_to_type< double > )
{
return value.get_real();
}
template< class Value >
typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > )
{
return value.get_str();
}
template< class Value >
typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > )
{
return value.get_array();
}
template< class Value >
typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > )
{
return value.get_obj();
}
template< class Value >
bool get_value( const Value& value, Type_to_type< bool > )
{
return value.get_bool();
}
}
template< class Config >
template< typename T >
T Value_impl< Config >::get_value() const
{
return internal_::get_value( *this, internal_::Type_to_type< T >() );
}
static std::string value_type_to_string( const Value_type vtype )
{
switch( vtype )
{
case obj_type: return "Object";
case array_type: return "Array";
case str_type: return "string";
case bool_type: return "boolean";
case int_type: return "integer";
case real_type: return "real";
case null_type: return "null";
}
assert( false );
return "unknown type";
}
}
#endif

View File

@ -0,0 +1,33 @@
#ifndef JSON_SPIRIT_WRITER_OPTIONS
#define JSON_SPIRIT_WRITER_OPTIONS
// Copyright John W. Wilkinson 2007 - 2013
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.06
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
namespace json_spirit
{
enum Output_options{ pretty_print = 0x01, // Add whitespace to format the output nicely.
raw_utf8 = 0x02, // This prevents non-printable characters from being escapted using "\uNNNN" notation.
// Note, this is an extension to the JSON standard. It disables the escaping of
// non-printable characters allowing UTF-8 sequences held in 8 bit char strings
// to pass through unaltered.
remove_trailing_zeros = 0x04,
// outputs e.g. "1.200000000000000" as "1.2"
single_line_arrays = 0x08,
// pretty printing except that arrays printed on single lines unless they contain
// composite elements, i.e. objects or arrays
always_escape_nonascii = 0x10,
// all unicode wide characters are escaped, i.e. outputed as "\uXXXX", even if they are
// printable under the current locale, ascii printable chars are not escaped
};
}
#endif

View File

@ -0,0 +1,387 @@
#ifndef JSON_SPIRIT_WRITER_TEMPLATE
#define JSON_SPIRIT_WRITER_TEMPLATE
// Copyright John W. Wilkinson 2007 - 2013
// Distributed under the MIT License, see accompanying file LICENSE.txt
// json spirit version 4.06
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include "json_spirit_value.h"
#include "json_spirit_writer_options.h"
#include <cassert>
#include <sstream>
#include <iomanip>
#include <boost/io/ios_state.hpp>
namespace json_spirit
{
inline char to_hex_char( unsigned int c )
{
assert( c <= 0xF );
const char ch = static_cast< char >( c );
if( ch < 10 ) return '0' + ch;
return 'A' - 10 + ch;
}
template< class String_type >
String_type non_printable_to_string( unsigned int c )
{
typedef typename String_type::value_type Char_type;
String_type result( 6, '\\' );
result[1] = 'u';
result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4;
result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4;
result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4;
result[ 2 ] = to_hex_char( c & 0x000F );
return result;
}
template< typename Char_type, class String_type >
bool add_esc_char( Char_type c, String_type& s )
{
switch( c )
{
case '"': s += to_str< String_type >( "\\\"" ); return true;
case '\\': s += to_str< String_type >( "\\\\" ); return true;
case '\b': s += to_str< String_type >( "\\b" ); return true;
case '\f': s += to_str< String_type >( "\\f" ); return true;
case '\n': s += to_str< String_type >( "\\n" ); return true;
case '\r': s += to_str< String_type >( "\\r" ); return true;
case '\t': s += to_str< String_type >( "\\t" ); return true;
}
return false;
}
template< class String_type >
String_type add_esc_chars( const String_type& s, bool raw_utf8, bool esc_nonascii )
{
typedef typename String_type::const_iterator Iter_type;
typedef typename String_type::value_type Char_type;
String_type result;
const Iter_type end( s.end() );
for( Iter_type i = s.begin(); i != end; ++i )
{
const Char_type c( *i );
if( add_esc_char( c, result ) ) continue;
if( raw_utf8 )
{
result += c;
}
else
{
const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c );
if( !esc_nonascii && iswprint( unsigned_c ) )
{
result += c;
}
else
{
result += non_printable_to_string< String_type >( unsigned_c );
}
}
}
return result;
}
template< class Ostream >
void append_double( Ostream& os, const double d, const int precision )
{
os << std::showpoint << std::setprecision( precision ) << d;
}
template< class String_type >
void erase_and_extract_exponent( String_type& str, String_type& exp )
{
const typename String_type::size_type exp_start= str.find( 'e' );
if( exp_start != String_type::npos )
{
exp = str.substr( exp_start );
str.erase( exp_start );
}
}
template< class String_type >
typename String_type::size_type find_first_non_zero( const String_type& str )
{
typename String_type::size_type result = str.size() - 1;
for( ; result != 0; --result )
{
if( str[ result ] != '0' )
{
break;
}
}
return result;
}
template< class String_type >
void remove_trailing( String_type& str )
{
String_type exp;
erase_and_extract_exponent( str, exp );
const typename String_type::size_type first_non_zero = find_first_non_zero( str );
if( first_non_zero != 0 )
{
const int offset = str[first_non_zero] == '.' ? 2 : 1; // note zero digits following a decimal point is non standard
str.erase( first_non_zero + offset );
}
str += exp;
}
// this class generates the JSON text,
// it keeps track of the indentation level etc.
//
template< class Value_type, class Ostream_type >
class Generator
{
typedef typename Value_type::Config_type Config_type;
typedef typename Config_type::String_type String_type;
typedef typename Config_type::Object_type Object_type;
typedef typename Config_type::Array_type Array_type;
typedef typename String_type::value_type Char_type;
typedef typename Object_type::value_type Obj_member_type;
public:
Generator( const Value_type& value, Ostream_type& os, unsigned int options )
: os_( os )
, indentation_level_( 0 )
, pretty_( ( options & pretty_print ) != 0 || ( options & single_line_arrays ) != 0 )
, raw_utf8_( ( options & raw_utf8 ) != 0 )
, esc_nonascii_( ( options & always_escape_nonascii ) != 0 )
, remove_trailing_zeros_( ( options & remove_trailing_zeros ) != 0 )
, single_line_arrays_( ( options & single_line_arrays ) != 0 )
, ios_saver_( os )
{
output( value );
}
private:
void output( const Value_type& value )
{
switch( value.type() )
{
case obj_type: output( value.get_obj() ); break;
case array_type: output( value.get_array() ); break;
case str_type: output( value.get_str() ); break;
case bool_type: output( value.get_bool() ); break;
case real_type: output( value.get_real() ); break;
case int_type: output_int( value ); break;
case null_type: os_ << "null"; break;
default: assert( false );
}
}
void output( const Object_type& obj )
{
output_array_or_obj( obj, '{', '}' );
}
void output( const Obj_member_type& member )
{
output( Config_type::get_name( member ) ); space();
os_ << ':'; space();
output( Config_type::get_value( member ) );
}
void output_int( const Value_type& value )
{
if( value.is_uint64() )
{
os_ << value.get_uint64();
}
else
{
os_ << value.get_int64();
}
}
void output( const String_type& s )
{
os_ << '"' << add_esc_chars( s, raw_utf8_, esc_nonascii_ ) << '"';
}
void output( bool b )
{
os_ << to_str< String_type >( b ? "true" : "false" );
}
void output( double d )
{
if( remove_trailing_zeros_ )
{
std::basic_ostringstream< Char_type > os;
append_double( os, d, 16 ); // note precision is 16 so that we get some trailing space that we can remove,
// otherwise, 0.1234 gets converted to "0.12399999..."
String_type str = os.str();
remove_trailing( str );
os_ << str;
}
else
{
append_double( os_, d, 17 );
}
}
static bool contains_composite_elements( const Array_type& arr )
{
for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )
{
const Value_type& val = *i;
if( val.type() == obj_type ||
val.type() == array_type )
{
return true;
}
}
return false;
}
template< class Iter >
void output_composite_item( Iter i, Iter last )
{
output( *i );
if( ++i != last )
{
os_ << ',';
}
}
void output( const Array_type& arr )
{
if( single_line_arrays_ && !contains_composite_elements( arr ) )
{
os_ << '['; space();
for( typename Array_type::const_iterator i = arr.begin(); i != arr.end(); ++i )
{
output_composite_item( i, arr.end() );
space();
}
os_ << ']';
}
else
{
output_array_or_obj( arr, '[', ']' );
}
}
template< class T >
void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char )
{
os_ << start_char; new_line();
++indentation_level_;
for( typename T::const_iterator i = t.begin(); i != t.end(); ++i )
{
indent();
output_composite_item( i, t.end() );
new_line();
}
--indentation_level_;
indent(); os_ << end_char;
}
void indent()
{
if( !pretty_ ) return;
for( int i = 0; i < indentation_level_; ++i )
{
os_ << " ";
}
}
void space()
{
if( pretty_ ) os_ << ' ';
}
void new_line()
{
if( pretty_ ) os_ << '\n';
}
Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning
Ostream_type& os_;
int indentation_level_;
bool pretty_;
bool raw_utf8_;
bool esc_nonascii_;
bool remove_trailing_zeros_;
bool single_line_arrays_;
boost::io::basic_ios_all_saver< Char_type > ios_saver_; // so that ostream state is reset after control is returned to the caller
};
// writes JSON Value to a stream, e.g.
//
// write_stream( value, os, pretty_print );
//
template< class Value_type, class Ostream_type >
void write_stream( const Value_type& value, Ostream_type& os, unsigned int options = 0 )
{
os << std::dec;
Generator< Value_type, Ostream_type >( value, os, options );
}
// writes JSON Value to a stream, e.g.
//
// const string json_str = write( value, pretty_print );
//
template< class Value_type >
typename Value_type::String_type write_string( const Value_type& value, unsigned int options = 0 )
{
typedef typename Value_type::String_type::value_type Char_type;
std::basic_ostringstream< Char_type > os;
write_stream( value, os, options );
return os.str();
}
}
#endif

View File

@ -29,8 +29,6 @@
class wxString;
typedef struct JSContext JSContext;
//////////////////////////////////////////////////////////////////////////
// Mostly-private bits:
@ -193,7 +191,7 @@ namespace AtlasObject
AtObj LoadFromXML(const std::string& xml);
// Returns AtObj() on failure - test with AtObj::defined()
AtObj LoadFromJSON(JSContext* cx, const std::string& json);
AtObj LoadFromJSON(const std::string& json);
// Returns UTF-8-encoded XML document string.
// Returns empty string on failure.
@ -201,7 +199,7 @@ namespace AtlasObject
// Returns UTF-8-encoded JSON string.
// Returns empty string on failure.
std::string SaveToJSON(JSContext* cx, AtObj& obj);
std::string SaveToJSON(AtObj& obj);
AtObj TrimEmptyChildren(AtObj& obj);
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,7 +18,7 @@
#include "AtlasObject.h"
#include "AtlasObjectImpl.h"
#include "../AtlasScript/ScriptInterface.h"
#include "JSONSpiritInclude.h"
#if defined(_MSC_VER)
# pragma warning(disable:4996) // deprecated CRT
@ -28,146 +28,94 @@
#include <sstream>
static AtSmartPtr<AtNode> ConvertNode(JSContext* cx, jsval node);
static AtSmartPtr<AtNode> ConvertNode(json_spirit::Value node);
AtObj AtlasObject::LoadFromJSON(JSContext* cx, const std::string& json)
AtObj AtlasObject::LoadFromJSON(const std::string& json)
{
// Convert UTF8 to UTF16
wxString jsonW(json.c_str(), wxConvUTF8);
size_t json16len;
wxCharBuffer json16 = wxMBConvUTF16().cWC2MB(jsonW.c_str(), jsonW.Length(), &json16len);
jsval vp = JSVAL_NULL;
JSONParser* parser = JS_BeginJSONParse(cx, &vp);
if (!parser)
{
wxLogError(_T("ParseJSON failed to begin"));
return AtObj();
}
if (!JS_ConsumeJSONText(cx, parser, reinterpret_cast<const jschar*>(json16.data()), (uint32)(json16len/2)))
{
wxLogError(_T("ParseJSON failed to consume"));
return AtObj();
}
if (!JS_FinishJSONParse(cx, parser, JSVAL_NULL))
{
wxLogError(_T("ParseJSON failed to finish"));
return AtObj();
}
json_spirit::Value rootnode;
json_spirit::read_string(json, rootnode);
AtObj obj;
obj.p = ConvertNode(cx, vp);
obj.p = ConvertNode(rootnode);
return obj;
}
// Convert from a jsval to an AtNode
static AtSmartPtr<AtNode> ConvertNode(JSContext* cx, jsval node)
// Convert from a JSON to an AtNode
static AtSmartPtr<AtNode> ConvertNode(json_spirit::Value node)
{
AtSmartPtr<AtNode> obj (new AtNode());
// Non-objects get converted into strings
if (!JSVAL_IS_OBJECT(node))
if (node.type() == json_spirit::str_type)
{
JSString* str = JS_ValueToString(cx, node);
if (!str)
return obj; // error
size_t valueLen;
const jschar* valueChars = JS_GetStringCharsAndLength(cx, str, &valueLen);
if (!valueChars)
return obj; // error
wxString valueWx(reinterpret_cast<const char*>(valueChars), wxMBConvUTF16(), valueLen*2);
obj->value = valueWx.c_str();
// Annotate numbers/booleans specially, to allow round-tripping
if (JSVAL_IS_NUMBER(node))
{
obj->children.insert(AtNode::child_pairtype(
"@number", AtSmartPtr<AtNode>(new AtNode())
));
}
else if (JSVAL_IS_BOOLEAN(node))
{
obj->children.insert(AtNode::child_pairtype(
"@boolean", AtSmartPtr<AtNode>(new AtNode())
));
}
return obj;
obj->value = std::wstring(node.get_str().begin(),node.get_str().end());
}
JSObject* it = JS_NewPropertyIterator(cx, JSVAL_TO_OBJECT(node));
if (!it)
return obj; // error
while (true)
else if (node.type() == json_spirit::int_type || node.type() == json_spirit::real_type)
{
jsid idp;
jsval val;
if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &val))
return obj; // error
if (val == JSVAL_VOID)
break; // end of iteration
if (! JSVAL_IS_STRING(val))
continue; // ignore integer properties
JSString* name = JSVAL_TO_STRING(val);
size_t len;
const jschar* chars = JS_GetStringCharsAndLength(cx, name, &len);
wxString nameWx(reinterpret_cast<const char*>(chars), wxMBConvUTF16(), len*2);
std::string nameStr(nameWx.ToUTF8().data());
jsval vp;
if (!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(node), idp, &vp))
return obj; // error
// Unwrap arrays into a special format like <$name><item>$i0</item><item>...
// (This assumes arrays aren't nested)
if (JSVAL_IS_OBJECT(vp) && JS_IsArrayObject(cx, JSVAL_TO_OBJECT(vp)))
{
AtSmartPtr<AtNode> child(new AtNode());
child->children.insert(AtNode::child_pairtype(
"@array", AtSmartPtr<AtNode>(new AtNode())
));
jsuint arrayLength;
if (!JS_GetArrayLength(cx, JSVAL_TO_OBJECT(vp), &arrayLength))
return obj; // error
for (jsuint i = 0; i < arrayLength; ++i)
{
jsval val;
if (!JS_GetElement(cx, JSVAL_TO_OBJECT(vp), i, &val))
return obj; // error
child->children.insert(AtNode::child_pairtype(
"item", ConvertNode(cx, val)
));
}
obj->children.insert(AtNode::child_pairtype(
nameStr, child
));
}
std::wstringstream stream;
if (node.type() == json_spirit::int_type)
stream << node.get_int();
if (node.type() == json_spirit::real_type)
stream << node.get_real();
obj->value = stream.str().c_str();
obj->children.insert(AtNode::child_pairtype(
"@number", AtSmartPtr<AtNode>(new AtNode())
));
}
else if (node.type() == json_spirit::bool_type)
{
if (node.get_bool())
obj->value = L"true";
else
obj->value = L"false";
obj->children.insert(AtNode::child_pairtype(
"@boolean", AtSmartPtr<AtNode>(new AtNode())
));
}
else if (node.type() == json_spirit::array_type)
{
obj->children.insert(AtNode::child_pairtype(
"@array", AtSmartPtr<AtNode>(new AtNode())
));
json_spirit::Array nodeChildren = node.get_array();
json_spirit::Array::iterator itr = nodeChildren.begin();
for (; itr != nodeChildren.end(); itr++)
{
obj->children.insert(AtNode::child_pairtype(
nameStr, ConvertNode(cx, vp)
"item", ConvertNode(*itr)
));
}
}
else if (node.type() == json_spirit::obj_type)
{
json_spirit::Object objectProperties = node.get_obj();
json_spirit::Object::iterator itr = objectProperties.begin();
for (; itr != objectProperties.end(); itr++)
{
obj->children.insert(AtNode::child_pairtype(
itr->name_, ConvertNode(itr->value_)
));
}
}
else
{
assert(! "Unimplemented type found when parsing JSON!");
}
return obj;
}
jsval BuildJSVal(JSContext* cx, AtNode::Ptr p)
json_spirit::Value BuildJSONNode(AtNode::Ptr p)
{
if (!p)
return JSVAL_VOID;
{
json_spirit::Value rval;
return rval;
}
// Special case for numbers/booleans to allow round-tripping
if (p->children.count("@number"))
@ -178,9 +126,7 @@ jsval BuildJSVal(JSContext* cx, AtNode::Ptr p)
double val = 0;
str >> val;
jsval rval;
if (!JS_NewNumberValue(cx, val, &rval))
return JSVAL_VOID; // error
json_spirit::Value rval(val);
return rval;
}
else if (p->children.count("@boolean"))
@ -188,85 +134,54 @@ jsval BuildJSVal(JSContext* cx, AtNode::Ptr p)
bool val = false;
if (p->value == L"true")
val = true;
return BOOLEAN_TO_JSVAL(val);
json_spirit::Value rval(val);
return rval;
}
// If no children, then use the value string instead
if (p->children.empty())
{
size_t val16len;
wxCharBuffer val16 = wxMBConvUTF16().cWC2MB(p->value.c_str(), p->value.length(), &val16len);
JSString* str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar*>(val16.data()), (uint32)(val16len/2));
if (!str)
return JSVAL_VOID; // error
return STRING_TO_JSVAL(str);
json_spirit::Value rval(std::string(p->value.begin(), p->value.end()));
return rval;
}
if (p->children.find("@array") != p->children.end())
{
JSObject* obj = JS_NewArrayObject(cx, 0, NULL);
if (!obj)
return JSVAL_VOID; // error
json_spirit::Array rval;
// Find the <item> children
AtNode::child_maptype::const_iterator lower = p->children.lower_bound("item");
AtNode::child_maptype::const_iterator upper = p->children.upper_bound("item");
jsint idx = 0;
uint32_t idx = 0;
for (AtNode::child_maptype::const_iterator it = lower; it != upper; ++it)
{
jsval val = BuildJSVal(cx, it->second);
if (!JS_SetElement(cx, obj, idx, &val))
return JSVAL_VOID; // error
json_spirit::Value child = BuildJSONNode(it->second);
rval.push_back(child);
++idx;
}
return OBJECT_TO_JSVAL(obj);
return rval;
}
else
{
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
if (!obj)
return JSVAL_VOID; // error
json_spirit::Object rval;
for (AtNode::child_maptype::const_iterator it = p->children.begin(); it != p->children.end(); ++it)
{
jsval val = BuildJSVal(cx, it->second);
if (!JS_SetProperty(cx, obj, it->first.c_str(), &val))
return JSVAL_VOID; // error
json_spirit::Value child = BuildJSONNode(it->second);
rval.push_back(json_spirit::Pair(it->first.c_str(), child));
}
return OBJECT_TO_JSVAL(obj);
return rval;
}
}
struct Stringifier
std::string AtlasObject::SaveToJSON(AtObj& obj)
{
static JSBool callback(const jschar* buf, uint32 len, void* data)
{
wxString textWx(reinterpret_cast<const char*>(buf), wxMBConvUTF16(), len*2);
std::string textStr(textWx.ToUTF8().data());
static_cast<Stringifier*>(data)->stream << textStr;
return JS_TRUE;
}
std::stringstream stream;
};
std::string AtlasObject::SaveToJSON(JSContext* cx, AtObj& obj)
{
jsval root = BuildJSVal(cx, obj.p);
Stringifier str;
if (!JS_Stringify(cx, &root, NULL, JSVAL_VOID, &Stringifier::callback, &str))
{
wxLogError(_T("SaveToJSON failed"));
return "";
}
return str.stream.str();
json_spirit::Value root = BuildJSONNode(obj.p);
std::string ret = json_spirit::write_string(root, 0);
return ret;
}

View File

@ -0,0 +1,49 @@
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// We use this special header for including the JSONSpirit header because some tweaking is needed to disable warnings.
#ifndef JSON_SPIRIT_INCLUDE_H
#define JSON_SPIRIT_INCLUDE_H
#ifdef __GNUC__
# define GCC_VERSION (__GNUC__*100 + __GNUC_MINOR__)
#else
# define GCC_VERSION 0
#endif
#if GCC_VERSION >= 402 // (older GCCs don't support this pragma)
# pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#ifdef _MSC_VER
#pragma warning(disable: 4100)
#pragma warning(disable: 4512)
#endif
# include "json_spirit_writer_template.h"
# include "json_spirit_reader_template.h"
#ifdef _MSC_VER
#pragma warning(default: 4100)
#pragma warning(default: 4512)
#endif
#if GCC_VERSION >= 402
# pragma GCC diagnostic warning "-Wunused-parameter"
#endif
#endif // JSON_SPIRIT_INCLUDE_H

View File

@ -1,129 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
// Set the maximum number of function arguments that can be handled
#define MAX_ARGS 3
// (This is included inside the definition of class ScriptInterface)
public:
// Varieties of comma-separated list to fit on the head/tail/whole of another comma-separated list
#define NUMBERED_LIST_TAIL(z, i, data) ,data##i
#define NUMBERED_LIST_HEAD(z, i, data) data##i,
#define NUMBERED_LIST_BALANCED(z, i, data) BOOST_PP_COMMA_IF(i) data##i
// Some other things
#define TYPED_ARGS(z, i, data) , T##i a##i
#define CONVERT_ARG(z, i, data) T##i a##i; if (! ScriptInterface::FromJSVal<T##i>(cx, i < argc ? JS_ARGV(cx, vp)[i] : JSVAL_VOID, a##i)) return JS_FALSE;
// List-generating macros, named roughly after their first list item
#define TYPENAME_T0_HEAD(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_HEAD, typename T)
#define TYPENAME_T0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, typename T)
#define T0(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED, T)
#define T0_HEAD(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_HEAD, T)
#define T0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, T)
#define T0_A0(z, i) BOOST_PP_REPEAT_##z (i, TYPED_ARGS, ~)
#define A0(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_BALANCED, a)
#define A0_TAIL(z, i) BOOST_PP_REPEAT_##z (i, NUMBERED_LIST_TAIL, a)
// Define RegisterFunction<TR, T0..., f>
#define OVERLOADS(z, i, data) \
template <typename TR, TYPENAME_T0_HEAD(z,i) TR (*fptr) ( void* T0_TAIL(z,i) )> \
void RegisterFunction(const char* name) { \
Register(name, call<TR, T0_HEAD(z,i) fptr>, nargs<0 T0_TAIL(z,i)>()); \
}
BOOST_PP_REPEAT(MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
private:
// JSNative-compatible function that wraps the function identified in the template argument list
// (Definition comes later, since it depends on some things we haven't defined yet)
#define OVERLOADS(z, i, data) \
template <typename TR, TYPENAME_T0_HEAD(z,i) TR (*fptr) ( void* T0_TAIL(z,i) )> \
static JSBool call(JSContext* cx, uintN argc, jsval* vp);
BOOST_PP_REPEAT(MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
// Argument-number counter
#define OVERLOADS(z, i, data) \
template <int dummy TYPENAME_T0_TAIL(z,i)> /* add a dummy parameter so we still compile with 0 template args */ \
static size_t nargs() { return i; }
BOOST_PP_REPEAT(MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
}; // end of class ScriptInterface, because the following specialised structs
// are not permitted inside non-namespace scopes
// ScriptInterface_NativeWrapper<T>::call(cx, rval, fptr, args...) will call fptr(cbdata, args),
// and if T != void then it will store the result in rval:
// Templated on the return type so void can be handled separately
template <typename TR>
struct ScriptInterface_NativeWrapper {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename f> \
static void call(JSContext* cx, jsval& rval, f fptr T0_A0(z,i)) { \
rval = ScriptInterface::ToJSVal<TR>(cx, fptr(ScriptInterface::GetCallbackData(cx) A0_TAIL(z,i))); \
}
BOOST_PP_REPEAT(MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
};
// Overloaded to ignore the return value from void functions
template <>
struct ScriptInterface_NativeWrapper<void> {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename f> \
static void call(JSContext* cx, jsval& /*rval*/, f fptr T0_A0(z,i)) { \
fptr(ScriptInterface::GetCallbackData(cx) A0_TAIL(z,i)); \
}
BOOST_PP_REPEAT(MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
};
// JSNative-compatible function that wraps the function identified in the template argument list
#define OVERLOADS(z, i, data) \
template <typename TR, TYPENAME_T0_HEAD(z,i) TR (*fptr) ( void* T0_TAIL(z,i) )> \
JSBool ScriptInterface::call(JSContext* cx, uintN argc, jsval* vp) { \
(void)cx; (void)argc; /* avoid 'unused parameter' warnings */ \
BOOST_PP_REPEAT_##z (i, CONVERT_ARG, ~) \
jsval rval = JSVAL_VOID; \
ScriptInterface_NativeWrapper<TR>::call(cx, rval, fptr A0_TAIL(z,i)); \
JS_SET_RVAL(cx, vp, rval); \
return JS_TRUE; \
}
BOOST_PP_REPEAT(MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
// Clean up our mess
#undef NUMBERED_LIST_TAIL
#undef NUMBERED_LIST_HEAD
#undef NUMBERED_LIST_BALANCED
#undef TYPED_ARGS
#undef CONVERT_ARG
#undef TYPENAME_T0_HEAD
#undef TYPENAME_T0_TAIL
#undef T0
#undef T0_HEAD
#undef T0_TAIL
#undef T0_A0
#undef A0
#undef A0_TAIL

View File

@ -1,727 +0,0 @@
/* Copyright (C) 2011 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptInterface.h"
#include <cassert>
#ifndef _WIN32
# include <typeinfo>
# include <cxxabi.h>
#endif
#if defined(_MSC_VER)
# pragma warning(disable:4996) // deprecated CRT
#endif
#if defined(__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__) >= 402 // (older GCCs don't support this pragma)
# pragma GCC diagnostic ignored "-Wredundant-decls" // triggered by wx/geometry.h
#endif
#include "wx/wx.h"
#include "GameInterface/Shareable.h"
#include "GameInterface/Messages.h"
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include "valgrind.h"
#define FAIL(msg) do { JS_ReportError(cx, msg); return false; } while (false)
const int RUNTIME_SIZE = 4*1024*1024; // TODO: how much memory is needed?
const int STACK_CHUNK_SIZE = 8192;
SubmitCommand g_SubmitCommand; // TODO: globals are ugly
////////////////////////////////////////////////////////////////
namespace
{
template<typename T>
void ReportError(JSContext* cx, const char* title)
{
// TODO: SetPendingException turns the error into a JS-catchable exception,
// but the error report doesn't say anything useful like the line number,
// so I'm just using ReportError instead for now (and failures are uncatchable
// and will terminate the whole script)
//JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "%s: Unhandled type", title)));
#ifdef _WIN32
JS_ReportError(cx, "%s: Unhandled type", title);
#else
// Give a more informative message on GCC
int status;
char* name = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
JS_ReportError(cx, "%s: Unhandled type '%s'", title, name);
free(name);
#endif
}
// Report runtime errors for unhandled types, so we don't have to bother
// defining all the types in advance. (TODO: at some point we should
// define all the types and then remove this bit so the errors are found
// at link-time.)
template<typename T> struct FromJSVal
{
static bool Convert(JSContext* cx, jsval WXUNUSED(v), T& WXUNUSED(out))
{
ReportError<T>(cx, "FromJSVal");
return false;
}
};
template<> struct FromJSVal<bool>
{
static bool Convert(JSContext* cx, jsval v, bool& out)
{
JSBool ret;
if (! JS_ValueToBoolean(cx, v, &ret)) return false;
out = (ret ? true : false);
return true;
}
};
template<> struct FromJSVal<float>
{
static bool Convert(JSContext* cx, jsval v, float& out)
{
jsdouble ret;
if (! JS_ValueToNumber(cx, v, &ret)) return false;
out = ret;
return true;
}
};
template<> struct FromJSVal<int>
{
static bool Convert(JSContext* cx, jsval v, int& out)
{
int32 ret;
if (! JS_ValueToECMAInt32(cx, v, &ret)) return false;
out = ret;
return true;
}
};
template<> struct FromJSVal<size_t>
{
static bool Convert(JSContext* cx, jsval v, size_t& out)
{
uint32 ret;
if (! JS_ValueToECMAUint32(cx, v, &ret)) return false;
out = ret;
return true;
}
};
template<> struct FromJSVal<CScriptVal>
{
static bool Convert(JSContext* WXUNUSED(cx), jsval v, CScriptVal& out)
{
out = v;
return true;
}
};
template<> struct FromJSVal<std::wstring>
{
static bool Convert(JSContext* cx, jsval v, std::wstring& out)
{
JSString* ret = JS_ValueToString(cx, v);
if (! ret)
FAIL("Argument must be convertible to a string");
size_t len;
const jschar* ch = JS_GetStringCharsAndLength(cx, ret, &len);
if (! ch)
FAIL("JS_GetStringsCharsAndLength failed"); // probably out of memory
out = std::wstring(ch, ch + len);
return true;
}
};
template<> struct FromJSVal<std::string>
{
static bool Convert(JSContext* cx, jsval v, std::string& out)
{
JSString* ret = JS_ValueToString(cx, v);
if (! ret)
FAIL("Argument must be convertible to a string");
size_t len = JS_GetStringEncodingLength(cx, ret);
if (len == (size_t)-1)
FAIL("JS_GetStringEncodingLength failed");
char* ch = JS_EncodeString(cx, ret); // chops off high byte of each jschar
if (! ch)
FAIL("JS_EncodeString failed"); // probably out of memory
out = std::string(ch, ch + len);
JS_free(cx, ch);
return true;
}
};
template<> struct FromJSVal<wxString>
{
static bool Convert(JSContext* cx, jsval v, wxString& out)
{
JSString* ret = JS_ValueToString(cx, v);
size_t len;
if (! ret)
FAIL("Argument must be convertible to a string");
const jschar* ch = JS_GetStringCharsAndLength(cx, ret, &len);
if (! ch)
FAIL("JS_GetStringsCharsAndLength failed"); // probably out of memory
out = wxString((const char*)ch, wxMBConvUTF16(), len*2);
return true;
}
};
template<typename T> struct FromJSVal<std::vector<T> >
{
static bool Convert(JSContext* cx, jsval v, std::vector<T>& out)
{
JSObject* obj;
if (! JS_ValueToObject(cx, v, &obj) || obj == NULL || !JS_IsArrayObject(cx, obj))
FAIL("Argument must be an array");
jsuint length;
if (! JS_GetArrayLength(cx, obj, &length))
FAIL("Failed to get array length");
out.reserve(length);
for (jsuint i = 0; i < length; ++i)
{
jsval el;
if (! JS_GetElement(cx, obj, i, &el))
FAIL("Failed to read array element");
T el2;
if (! FromJSVal<T>::Convert(cx, el, el2))
return false;
out.push_back(el2);
}
return true;
}
};
////////////////////////////////////////////////////////////////
// Report runtime errors for unhandled types, so we don't have to bother
// defining all the types in advance. (TODO: at some point we should
// define all the types and then remove this bit so the errors are found
// at link-time.)
template<typename T> struct ToJSVal
{
static jsval Convert(JSContext* cx, const T& WXUNUSED(val))
{
ReportError<T>(cx, "ToJSVal");
return JSVAL_VOID;
}
};
////////////////////////////////////////////////////////////////
// Primitive types:
template<> struct ToJSVal<bool>
{
static jsval Convert(JSContext* WXUNUSED(cx), const bool& val)
{
return val ? JSVAL_TRUE : JSVAL_FALSE;
}
};
template<> struct ToJSVal<float>
{
static jsval Convert(JSContext* cx, const float& val)
{
jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val, &rval); // ignore return value
return rval;
}
};
template<> struct ToJSVal<int>
{
static jsval Convert(JSContext* WXUNUSED(cx), const int& val)
{
return INT_TO_JSVAL(val);
}
};
template<> struct ToJSVal<size_t>
{
static jsval Convert(JSContext* cx, const size_t& val)
{
if (val <= JSVAL_INT_MAX)
return INT_TO_JSVAL(val);
jsval rval = JSVAL_VOID;
JS_NewNumberValue(cx, val, &rval); // ignore return value
return rval;
}
};
template<> struct ToJSVal<wxString>
{
static jsval Convert(JSContext* cx, const wxString& val)
{
wxMBConvUTF16 conv;
size_t length;
wxCharBuffer utf16 = conv.cWC2MB(val.c_str(), val.length()+1, &length);
JSString* str = JS_NewUCStringCopyN(cx, reinterpret_cast<jschar*>(utf16.data()), length/2);
if (str)
return STRING_TO_JSVAL(str);
else
return JSVAL_VOID;
}
};
template<> struct ToJSVal<std::wstring>
{
static jsval Convert(JSContext* cx, const std::wstring& val)
{
wxMBConvUTF16 conv;
size_t length;
wxCharBuffer utf16 = conv.cWC2MB(val.c_str(), val.length()+1, &length);
JSString* str = JS_NewUCStringCopyN(cx, reinterpret_cast<jschar*>(utf16.data()), length/2);
if (str)
return STRING_TO_JSVAL(str);
else
return JSVAL_VOID;
}
};
template<> struct ToJSVal<std::string>
{
static jsval Convert(JSContext* cx, const std::string& val)
{
JSString* str = JS_NewStringCopyN(cx, val.c_str(), val.length());
if (str)
return STRING_TO_JSVAL(str);
return JSVAL_VOID;
}
};
////////////////////////////////////////////////////////////////
// Compound types:
template<typename T> struct ToJSVal<std::vector<T> >
{
static jsval Convert(JSContext* cx, const std::vector<T>& val)
{
JSObject* obj = JS_NewArrayObject(cx, 0, NULL);
if (! obj) return JSVAL_VOID;
for (size_t i = 0; i < val.size(); ++i)
{
jsval el = ToJSVal<T>::Convert(cx, val[i]);
JS_SetElement(cx, obj, (jsint)i, &el);
}
return OBJECT_TO_JSVAL(obj);
}
};
template<typename T> struct ToJSVal<AtlasMessage::Shareable<T> >
{
static jsval Convert(JSContext* cx, const AtlasMessage::Shareable<T>& val)
{
return ToJSVal<T>::Convert(cx, val._Unwrap());
}
};
}
template<typename T> bool ScriptInterface::FromJSVal(JSContext* cx, jsval v, T& out)
{
return ::FromJSVal<T>::Convert(cx, v, out);
}
template<typename T> jsval ScriptInterface::ToJSVal(JSContext* cx, const T& v)
{
return ::ToJSVal<T>::Convert(cx, v);
}
// Explicit instantiation of functions that would otherwise be unused in this file
// but are required for linking with other files
template bool ScriptInterface::FromJSVal<std::string>(JSContext*, jsval, std::string&);
template bool ScriptInterface::FromJSVal<wxString>(JSContext*, jsval, wxString&);
template bool ScriptInterface::FromJSVal<bool>(JSContext*, jsval, bool&);
template bool ScriptInterface::FromJSVal<float>(JSContext*, jsval, float&);
template bool ScriptInterface::FromJSVal<CScriptVal>(JSContext*, jsval, CScriptVal&);
template jsval ScriptInterface::ToJSVal<wxString>(JSContext*, wxString const&);
template jsval ScriptInterface::ToJSVal<int>(JSContext*, int const&);
template jsval ScriptInterface::ToJSVal<float>(JSContext*, float const&);
template jsval ScriptInterface::ToJSVal<std::vector<int> >(JSContext*, std::vector<int> const&);
template jsval ScriptInterface::ToJSVal<size_t>(JSContext*, size_t const&);
template jsval ScriptInterface::ToJSVal<std::vector<wxString> >(JSContext*, std::vector<wxString> const&);
////////////////////////////////////////////////////////////////
struct AtlasScriptInterface_impl
{
AtlasScriptInterface_impl();
~AtlasScriptInterface_impl();
static JSBool LoadScript(JSContext* cx, const jschar* chars, uintN length, const char* filename, jsval* rval);
void RegisterMessages(JSObject* parent);
void Register(const char* name, JSNative fptr, uintN nargs);
JSRuntime* m_rt;
JSContext* m_cx;
JSObject* m_glob; // global scope object
JSObject* m_atlas; // Atlas scope object
};
namespace
{
JSClass global_class = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
};
void ErrorReporter(JSContext* WXUNUSED(cx), const char* message, JSErrorReport* report)
{
bool isWarning = JSREPORT_IS_WARNING(report->flags);
wxString logMessage(isWarning ? _T("JavaScript warning: ") : _T("JavaScript error: "));
if (report->filename)
{
logMessage << wxString::FromAscii(report->filename);
logMessage << _T(" line ") << report->lineno << _T("\n");
}
logMessage << wxString::FromAscii(message);
if (isWarning)
wxLogWarning(_T("%s"), logMessage.c_str());
else
wxLogError(_T("%s"), logMessage.c_str());
// When running under Valgrind, print more information in the error message
VALGRIND_PRINTF_BACKTRACE("->");
wxPrintf(_T("wxJS %s: %s\n--------\n"), isWarning ? _T("warning") : _T("error"), logMessage.c_str());
}
// Functions in the Atlas.* namespace:
JSBool ForceGC(JSContext* cx, uintN WXUNUSED(argc), jsval* vp)
{
JS_GC(cx);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
JSBool LoadScript(JSContext* cx, uintN argc, jsval* vp)
{
if (argc < 2 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0]) || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[1]))
return JS_FALSE;
std::string name;
if (!ScriptInterface::FromJSVal(cx, JS_ARGV(cx, vp)[0], name))
return JS_FALSE;
JSString* code = JSVAL_TO_STRING(JS_ARGV(cx, vp)[1]);
size_t len;
const jschar* ch = JS_GetStringCharsAndLength(cx, code, &len);
if (!ch)
return JS_FALSE;
jsval rval = JSVAL_VOID;
if (!AtlasScriptInterface_impl::LoadScript(cx,
ch, (uintN)len,
name.c_str(), &rval))
return JS_FALSE;
JS_SET_RVAL(cx, vp, rval);
return JS_TRUE;
}
// Functions in the global namespace:
JSBool print(JSContext* cx, uintN argc, jsval* vp)
{
for (uintN i = 0; i < argc; ++i)
{
std::string str;
if (! ScriptInterface::FromJSVal(cx, JS_ARGV(cx, vp)[i], str))
return JS_FALSE;
printf("%s", str.c_str());
}
fflush(stdout);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
}
AtlasScriptInterface_impl::AtlasScriptInterface_impl()
{
m_rt = JS_NewRuntime(RUNTIME_SIZE);
assert(m_rt); // TODO: error handling
m_cx = JS_NewContext(m_rt, STACK_CHUNK_SIZE);
assert(m_cx);
JS_BeginRequest(m_cx); // if you get linker errors, see the comment in ScriptInterface.h about JS_THREADSAFE
// (TODO: are we using requests correctly? (Probably not; how much does it matter?))
JS_SetContextPrivate(m_cx, NULL);
JS_SetErrorReporter(m_cx, ErrorReporter);
JS_SetOptions(m_cx,
JSOPTION_STRICT // "warn on dubious practice"
| JSOPTION_XML // "ECMAScript for XML support: parse <!-- --> as a token"
);
JS_SetVersion(m_cx, JSVERSION_LATEST);
m_glob = JS_NewCompartmentAndGlobalObject(m_cx, &global_class, NULL);
JS_InitStandardClasses(m_cx, m_glob);
JS_DefineProperty(m_cx, m_glob, "global", OBJECT_TO_JSVAL(m_glob), NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
m_atlas = JS_DefineObject(m_cx, m_glob, "Atlas", NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_atlas, "ForceGC", ::ForceGC, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_atlas, "LoadScript", ::LoadScript, 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
JS_DefineObject(m_cx, m_atlas, "State", NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
RegisterMessages(m_atlas);
}
AtlasScriptInterface_impl::~AtlasScriptInterface_impl()
{
JS_EndRequest(m_cx);
JS_DestroyContext(m_cx);
JS_DestroyRuntime(m_rt);
}
JSBool AtlasScriptInterface_impl::LoadScript(JSContext* cx, const jschar* chars, uintN length, const char* filename, jsval* rval)
{
JSObject* scriptObj = JS_NewObject(cx, NULL, NULL, NULL);
if (! scriptObj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(scriptObj);
jsval scriptRval;
JSBool ok = JS_EvaluateUCScript(cx, scriptObj, chars, length, filename, 1, &scriptRval);
if (! ok)
return JS_FALSE;
return JS_TRUE;
}
void AtlasScriptInterface_impl::Register(const char* name, JSNative fptr, uintN nargs)
{
JS_DefineFunction(m_cx, m_atlas, name, fptr, nargs, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
}
ScriptInterface::ScriptInterface(SubmitCommand submitCommand)
: m(new AtlasScriptInterface_impl())
{
g_SubmitCommand = submitCommand;
}
ScriptInterface::~ScriptInterface()
{
}
void ScriptInterface::SetCallbackData(void* cbdata)
{
JS_SetContextPrivate(m->m_cx, cbdata);
}
void* ScriptInterface::GetCallbackData(JSContext* cx)
{
return JS_GetContextPrivate(cx);
}
void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs)
{
m->Register(name, fptr, (uintN)nargs);
}
JSContext* ScriptInterface::GetContext()
{
return m->m_cx;
}
bool ScriptInterface::AddRoot(jsval* ptr)
{
return JS_AddValueRoot(m->m_cx, ptr) ? true : false;
}
bool ScriptInterface::RemoveRoot(jsval* ptr)
{
return JS_RemoveValueRoot(m->m_cx, ptr) ? true : false;
}
bool ScriptInterface::SetValue_(const wxString& name, jsval val)
{
jsval jsName = ToJSVal(m->m_cx, name);
const uintN argc = 2;
jsval argv[argc] = { jsName, val };
jsval rval;
JSBool ok = JS_CallFunctionName(m->m_cx, m->m_glob, "setValue", argc, argv, &rval);
return ok ? true : false;
}
bool ScriptInterface::GetValue_(const wxString& name, jsval& ret)
{
jsval jsName = ToJSVal(m->m_cx, name);
const uintN argc = 1;
jsval argv[argc] = { jsName };
JSBool ok = JS_CallFunctionName(m->m_cx, m->m_glob, "getValue", argc, argv, &ret);
return ok ? true : false;
}
bool ScriptInterface::CallFunction(jsval val, const char* name)
{
jsval jsRet;
std::vector<jsval> argv;
return CallFunction_(val, name, argv, jsRet);
}
bool ScriptInterface::CallFunction_(jsval val, const char* name, std::vector<jsval>& args, jsval& ret)
{
const uintN argc = (uintN)args.size();
jsval* argv = NULL;
if (argc)
argv = &args[0];
wxCHECK(JSVAL_IS_OBJECT(val), false);
JSBool found;
wxCHECK(JS_HasProperty(m->m_cx, JSVAL_TO_OBJECT(val), name, &found), false);
if (! found)
return false;
JSBool ok = JS_CallFunctionName(m->m_cx, JSVAL_TO_OBJECT(val), name, argc, argv, &ret);
return ok ? true : false;
}
bool ScriptInterface::Eval(const wxString& script)
{
jsval rval;
JSBool ok = JS_EvaluateScript(m->m_cx, m->m_glob, script.mb_str(), (uintN)script.length(), NULL, 0, &rval);
return ok ? true : false;
}
bool ScriptInterface::Eval_(const wxString& script, jsval& rval)
{
JSBool ok = JS_EvaluateScript(m->m_cx, m->m_glob, script.mb_str(), (uintN)script.length(), NULL, 0, &rval);
return ok ? true : false;
}
void ScriptInterface::LoadScript(const wxString& filename, const wxString& code)
{
size_t codeLength;
wxMBConvUTF16 conv;
wxCharBuffer codeUTF16 = conv.cWC2MB(code.c_str(), code.length()+1, &codeLength);
jsval rval;
m->LoadScript(m->m_cx, reinterpret_cast<jschar*>(codeUTF16.data()), (uintN)(codeLength/2), filename.ToAscii(), &rval);
}
////////////////////////////////////////////////////////////////
#define TYPE(elem) BOOST_PP_TUPLE_ELEM(2, 0, elem)
#define NAME(elem) BOOST_PP_TUPLE_ELEM(2, 1, elem)
#define MAKE_STR_(s) #s
#define MAKE_STR(s) MAKE_STR_(s)
#define CONVERT_ARGS(r, data, i, elem) \
TYPE(elem) a##i; \
if (! ScriptInterface::FromJSVal< TYPE(elem) >(cx, i < argc ? JS_ARGV(cx, vp)[i] : JSVAL_VOID, a##i)) \
return JS_FALSE;
#define CONVERT_OUTPUTS(r, data, i, elem) \
JS_DefineProperty(cx, ret, MAKE_STR(NAME(elem)), ScriptInterface::ToJSVal(cx, q.NAME(elem)), \
NULL, NULL, JSPROP_ENUMERATE);
#define ARG_LIST(r, data, i, elem) BOOST_PP_COMMA_IF(i) a##i
#define MESSAGE(name, vals) \
JSBool call_##name(JSContext* cx, uintN argc, jsval* vp) \
{ \
(void)cx; (void)argc; /* avoid 'unused parameter' warnings */ \
BOOST_PP_SEQ_FOR_EACH_I(CONVERT_ARGS, ~, vals) \
g_MessagePasser->Add(SHAREABLE_NEW(m##name, ( BOOST_PP_SEQ_FOR_EACH_I(ARG_LIST, ~, vals) ))); \
JS_SET_RVAL(cx, vp, JSVAL_VOID); \
return JS_TRUE; \
}
#define COMMAND(name, merge, vals) \
JSBool call_##name(JSContext* cx, uintN argc, jsval* vp) \
{ \
(void)cx; (void)argc; /* avoid 'unused parameter' warnings */ \
BOOST_PP_SEQ_FOR_EACH_I(CONVERT_ARGS, ~, vals) \
g_SubmitCommand(new AtlasMessage::m##name (AtlasMessage::d##name ( BOOST_PP_SEQ_FOR_EACH_I(ARG_LIST, ~, vals) ))); \
JS_SET_RVAL(cx, vp, JSVAL_VOID); \
return JS_TRUE; \
}
#define QUERY(name, in_vals, out_vals) \
JSBool call_##name(JSContext* cx, uintN argc, jsval* vp) \
{ \
(void)cx; (void)argc; /* avoid 'unused parameter' warnings */ \
BOOST_PP_SEQ_FOR_EACH_I(CONVERT_ARGS, ~, in_vals) \
q##name q = q##name( BOOST_PP_SEQ_FOR_EACH_I(ARG_LIST, ~, in_vals) ); \
q.Post(); \
JSObject* ret = JS_NewObject(cx, NULL, NULL, NULL); \
if (! ret) return JS_FALSE; \
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(ret)); \
BOOST_PP_SEQ_FOR_EACH_I(CONVERT_OUTPUTS, ~, out_vals) \
return JS_TRUE; \
}
#define MESSAGES_SKIP_SETUP
#define MESSAGES_SKIP_STRUCTS
// We want to include Messages.h again, with some different definitions,
// so cheat and undefine its include-guard
#undef INCLUDED_MESSAGES
namespace
{
using namespace AtlasMessage;
#include "GameInterface/Messages.h"
}
#undef MESSAGE
#undef COMMAND
#undef QUERY
void AtlasScriptInterface_impl::RegisterMessages(JSObject* parent)
{
using namespace AtlasMessage;
JSObject* obj = JS_DefineObject(m_cx, parent, "Message", NULL, NULL, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
#define MESSAGE(name, vals) \
JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)vals)-1, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
#define COMMAND(name, merge, vals) \
JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)vals)-1, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
#define QUERY(name, in_vals, out_vals) \
JS_DefineFunction(m_cx, obj, #name, call_##name, BOOST_PP_SEQ_SIZE((~)in_vals)-1, \
JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
#undef INCLUDED_MESSAGES
#include "GameInterface/Messages.h"
#undef MESSAGE
#undef COMMAND
#undef QUERY
}

View File

@ -1,191 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include <memory>
#include <vector>
#ifdef _WIN32
# define XP_WIN
# define WIN32 // SpiderMonkey expects this
// The jsval struct type causes crashes due to weird miscompilation
// issues in (at least) VC2008, so force it to be the less-type-safe
// non-struct type instead
# define JS_NO_JSVAL_JSID_STRUCT_TYPES
#else
# define XP_UNIX
// (See comment in scriptinterface/ScriptTypes.h)
# if defined(DEBUG) && defined(WITH_SYSTEM_MOZJS185)
# define JS_NO_JSVAL_JSID_STRUCT_TYPES
# endif
#endif // (we don't support XP_OS2 or XP_BEOS)
#ifdef __GNUC__
# define GCC_VERSION (__GNUC__*100 + __GNUC_MINOR__)
#else
# define GCC_VERSION 0
#endif
#ifdef _MSC_VER
# define MSC_VERSION _MSC_VER
#else
# define MSC_VERSION 0
#endif
// Ignore some harmless warnings triggered by jsapi.h
#if GCC_VERSION >= 402 // (older GCCs don't support this pragma)
# pragma GCC diagnostic ignored "-Wunused-parameter"
# pragma GCC diagnostic ignored "-Wredundant-decls"
#endif
#if MSC_VERSION
# pragma warning(push)
# pragma warning(disable:4480) // "nonstandard extension used: specifying underlying type for enum"
# pragma warning(disable:4100) // "unreferenced formal parameter"
#endif
#include "js/jsapi.h"
#if MSC_VERSION
# pragma warning(pop)
#endif
#if GCC_VERSION >= 402
# pragma GCC diagnostic warning "-Wunused-parameter"
# pragma GCC diagnostic warning "-Wredundant-decls"
#endif
class wxString;
namespace AtlasMessage { struct mWorldCommand; }
typedef void (*SubmitCommand)(AtlasMessage::mWorldCommand* command);
struct AtlasScriptInterface_impl;
class ScriptInterface
{
public:
ScriptInterface(SubmitCommand submitCommand);
~ScriptInterface();
void SetCallbackData(void* cbdata);
static void* GetCallbackData(JSContext* cx);
template <typename T> bool SetValue(const wxString& name, const T& val);
template <typename T> bool GetValue(const wxString& name, T& ret);
bool CallFunction(jsval val, const char* name);
template <typename T, typename R>
bool CallFunction(jsval val, const char* name, const T& a, R& ret);
template <typename T, typename S, typename R>
bool CallFunction(jsval val, const char* name, const T& a, const S& b, R& ret);
bool Eval(const wxString& script);
template <typename T> bool Eval(const wxString& script, T& ret);
// Defined elsewhere:
// template <TR, T0..., TR (*fptr) (void* cbdata, T0...)>
// void RegisterFunction(const char* functionName);
// (NOTE: The return type must be defined as a ToJSVal<TR> specialisation
// in ScriptInterface.cpp, else you'll end up with linker errors.)
void LoadScript(const wxString& filename, const wxString& code);
// Convert a jsval to a C++ type. (This might trigger GC.)
template <typename T> static bool FromJSVal(JSContext* cx, jsval val, T& ret);
// Convert a C++ type to a jsval. (This might trigger GC. The return
// value must be rooted if you don't want it to be collected.)
template <typename T> static jsval ToJSVal(JSContext* cx, const T& val);
bool AddRoot(jsval* ptr);
bool RemoveRoot(jsval* ptr);
JSContext* GetContext();
private:
bool SetValue_(const wxString& name, jsval val);
bool GetValue_(const wxString& name, jsval& ret);
bool CallFunction_(jsval val, const char* name, std::vector<jsval>& args, jsval& ret);
bool Eval_(const wxString& name, jsval& ret);
void Register(const char* name, JSNative fptr, size_t nargs);
std::auto_ptr<AtlasScriptInterface_impl> m;
// The nasty macro/template bits are split into a separate file so you don't have to look at them
#include "NativeWrapper.inl"
template <typename T>
bool ScriptInterface::SetValue(const wxString& name, const T& val)
{
return SetValue_(name, ToJSVal(GetContext(), val));
}
template <typename T>
bool ScriptInterface::GetValue(const wxString& name, T& ret)
{
jsval jsRet;
if (! GetValue_(name, jsRet)) return false;
return FromJSVal(GetContext(), jsRet, ret);
}
template <typename T, typename R>
bool ScriptInterface::CallFunction(jsval val, const char* name, const T& a, R& ret)
{
jsval jsRet;
std::vector<jsval> argv;
argv.push_back(ToJSVal(GetContext(), a));
bool ok = CallFunction_(val, name, argv, jsRet);
if (! ok) return false;
return FromJSVal(GetContext(), jsRet, ret);
}
template <typename T, typename S, typename R>
bool ScriptInterface::CallFunction(jsval val, const char* name, const T& a, const S& b, R& ret)
{
jsval jsRet;
std::vector<jsval> argv;
argv.push_back(ToJSVal(GetContext(), a));
argv.push_back(ToJSVal(GetContext(), b));
bool ok = CallFunction_(val, name, argv, jsRet);
if (! ok) return false;
return FromJSVal(GetContext(), jsRet, ret);
}
template <typename T>
bool ScriptInterface::Eval(const wxString& script, T& ret)
{
jsval jsRet;
if (! Eval_(script, jsRet)) return false;
return FromJSVal(GetContext(), jsRet, ret);
}
/**
* A trivial wrapper around a jsval. Used to avoid template overload ambiguities
* with jsval (which is just an integer), for any code that uses
* ScriptInterface::ToJSVal or ScriptInterface::FromJSVal
*/
class CScriptVal
{
public:
CScriptVal() : m_Val(JSVAL_VOID) { }
CScriptVal(jsval val) : m_Val(val) { }
jsval get() const { return m_Val; }
private:
jsval m_Val;
};

View File

@ -29,8 +29,6 @@
#include "GameInterface/MessagePasser.h"
#include "AtlasScript/ScriptInterface.h"
#include "wx/config.h"
#include "wx/debugrpt.h"
#include "wx/file.h"
@ -173,23 +171,9 @@ ATLASDLLIMPEXP void Atlas_ReportError()
///ReportError(); // janwas: disabled until ErrorReporter.cpp compiles
}
void ScenarioEditorSubmitCommand(AtlasMessage::mWorldCommand* command)
{
ScenarioEditor::GetCommandProc().Submit(new WorldCommand(command));
}
class AtlasDLLApp : public wxApp
{
public:
AtlasDLLApp()
: m_ScriptInterface(NULL)
{
}
~AtlasDLLApp()
{
delete m_ScriptInterface;
}
virtual bool OnInit()
{
@ -242,8 +226,7 @@ public:
}
else if (g_InitialWindowType == _T("ScenarioEditor"))
{
m_ScriptInterface = new ScriptInterface(&ScenarioEditorSubmitCommand);
frame = new ScenarioEditor(NULL, *m_ScriptInterface);
frame = new ScenarioEditor(NULL);
}
else
{
@ -315,7 +298,6 @@ public:
*/
private:
ScriptInterface* m_ScriptInterface;
bool OpenDirectory(const wxString& dir)
{

View File

@ -40,8 +40,6 @@
#include "GameInterface/MessagePasser.h"
#include "GameInterface/Messages.h"
#include "AtlasScript/ScriptInterface.h"
#include "Misc/KeyMap.h"
#include "Tools/Common/Tools.h"
@ -358,9 +356,9 @@ END_EVENT_TABLE()
static AtlasWindowCommandProc g_CommandProc;
AtlasWindowCommandProc& ScenarioEditor::GetCommandProc() { return g_CommandProc; }
ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterface)
ScenarioEditor::ScenarioEditor(wxWindow* parent)
: wxFrame(parent, wxID_ANY, _T(""), wxDefaultPosition, wxSize(1024, 768))
, m_FileHistory(_T("Scenario Editor")), m_ScriptInterface(scriptInterface)
, m_FileHistory(_T("Scenario Editor"))
, m_ObjectSettings(g_SelectedObjects, AtlasMessage::eRenderView::GAME)
, m_ToolManager(this)
{

View File

@ -27,12 +27,10 @@
#include "CustomControls/FileHistory/FileHistory.h"
#include "SectionLayout.h"
class ScriptInterface;
class ScenarioEditor : public wxFrame
{
public:
ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterface);
ScenarioEditor(wxWindow* parent);
void OnClose(wxCloseEvent& event);
void OnTimer(wxTimerEvent& event);
void OnIdle(wxIdleEvent& event);
@ -66,7 +64,6 @@ public:
static float GetSpeedModifier();
ScriptInterface& GetScriptInterface() const { return m_ScriptInterface; }
Observable<ObjectSettings>& GetObjectSettings() { return m_ObjectSettings; }
Observable<AtObj>& GetMapSettings() { return m_MapSettings; }
@ -75,7 +72,6 @@ public:
void SelectPage(const wxString& classname) { m_SectionLayout.SelectPage(classname); }
private:
ScriptInterface& m_ScriptInterface;
ToolManager m_ToolManager;

View File

@ -25,7 +25,6 @@
#include "CustomControls/SnapSplitterWindow/SnapSplitterWindow.h"
#include "ScenarioEditor.h"
#include "AtlasScript/ScriptInterface.h"
#include "Sections/Environment/Environment.h"
#include "Sections/Map/Map.h"

View File

@ -20,7 +20,6 @@
#include "Map.h"
#include "AtlasObject/AtlasObject.h"
#include "AtlasScript/ScriptInterface.h"
#include "GameInterface/Messages.h"
#include "ScenarioEditor/ScenarioEditor.h"
#include "ScenarioEditor/Tools/Common/Tools.h"
@ -186,7 +185,7 @@ void MapSettingsControl::ReadFromEngine()
if (!(*qry.settings).empty())
{
// Prevent error if there's no map settings to parse
m_MapSettings = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qry.settings);
m_MapSettings = AtlasObject::LoadFromJSON(*qry.settings);
}
// map name
@ -275,7 +274,7 @@ void MapSettingsControl::SendToEngine()
{
UpdateSettingsObject();
std::string json = AtlasObject::SaveToJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), m_MapSettings);
std::string json = AtlasObject::SaveToJSON(m_MapSettings);
// TODO: would be nice if we supported undo for settings changes
@ -370,7 +369,7 @@ void MapSidebar::OnFirstDisplay()
// Load the map sizes list
AtlasMessage::qGetMapSizes qrySizes;
qrySizes.Post();
AtObj sizes = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qrySizes.sizes);
AtObj sizes = AtlasObject::LoadFromJSON(*qrySizes.sizes);
wxChoice* sizeChoice = wxDynamicCast(FindWindow(ID_RandomSize), wxChoice);
for (AtIter s = sizes["Sizes"]["item"]; s.defined(); ++s)
{
@ -388,7 +387,7 @@ void MapSidebar::OnFirstDisplay()
scriptChoice->Clear();
for (size_t i = 0; i < scripts.size(); ++i)
{
AtObj data = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), scripts[i]);
AtObj data = AtlasObject::LoadFromJSON(scripts[i]);
wxString name(data["settings"]["Name"]);
scriptChoice->Append(name, new AtObjClientData(*data["settings"]));
}
@ -528,7 +527,7 @@ void MapSidebar::OnRandomGenerate(wxCommandEvent& WXUNUSED(evt))
settings.setInt("Seed", wxAtoi(wxDynamicCast(FindWindow(ID_RandomSeed), wxTextCtrl)->GetValue()));
std::string json = AtlasObject::SaveToJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), settings);
std::string json = AtlasObject::SaveToJSON(settings);
wxBusyInfo busy(_("Generating map"));
wxBusyCursor busyc;

View File

@ -24,7 +24,6 @@
#include "ScenarioEditor/ScenarioEditor.h"
#include "ScenarioEditor/Tools/Common/ObjectSettings.h"
#include "ScenarioEditor/Tools/Common/MiscState.h"
#include "AtlasScript/ScriptInterface.h"
#include "VariationControl.h"
#include "GameInterface/Messages.h"
@ -479,7 +478,7 @@ void ObjectBottomBar::OnFirstDisplay()
wxArrayString players;
AtlasMessage::qGetPlayerDefaults qryPlayers;
qryPlayers.Post();
AtObj playerData = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qryPlayers.defaults);
AtObj playerData = AtlasObject::LoadFromJSON(*qryPlayers.defaults);
AtObj playerDefs = *playerData["PlayerData"];
for (AtIter p = playerDefs["item"]; p.defined(); ++p)
{

View File

@ -20,7 +20,6 @@
#include "Player.h"
#include "AtlasObject/AtlasObject.h"
#include "AtlasScript/ScriptInterface.h"
#include "CustomControls/ColourDialog/ColourDialog.h"
#include "ScenarioEditor/ScenarioEditor.h"
@ -591,7 +590,7 @@ void PlayerSettingsControl::CreateWidgets()
std::vector<std::string> civData = *qryCiv.data;
for (size_t i = 0; i < civData.size(); ++i)
{
AtObj civ = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), civData[i]);
AtObj civ = AtlasObject::LoadFromJSON(civData[i]);
civNames.Add(wxString(civ["Name"]));
civCodes.Add(wxString(civ["Code"]));
}
@ -600,7 +599,7 @@ void PlayerSettingsControl::CreateWidgets()
ArrayOfAIData ais(AIData::CompareAIData);
AtlasMessage::qGetAIData qryAI;
qryAI.Post();
AtObj aiData = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qryAI.data);
AtObj aiData = AtlasObject::LoadFromJSON(*qryAI.data);
for (AtIter a = aiData["AIData"]["item"]; a.defined(); ++a)
{
ais.Add(new AIData(wxString(a["id"]), wxString(a["data"]["name"])));
@ -646,7 +645,7 @@ void PlayerSettingsControl::LoadDefaults()
{
AtlasMessage::qGetPlayerDefaults qryPlayers;
qryPlayers.Post();
AtObj playerData = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qryPlayers.defaults);
AtObj playerData = AtlasObject::LoadFromJSON(*qryPlayers.defaults);
m_PlayerDefaults = *playerData["PlayerData"];
}
@ -658,7 +657,7 @@ void PlayerSettingsControl::ReadFromEngine()
if (!(*qry.settings).empty())
{
// Prevent error if there's no map settings to parse
m_MapSettings = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qry.settings);
m_MapSettings = AtlasObject::LoadFromJSON(*qry.settings);
}
else
{
@ -946,7 +945,7 @@ void PlayerSettingsControl::SendToEngine()
{
UpdateSettingsObject();
std::string json = AtlasObject::SaveToJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), m_MapSettings);
std::string json = AtlasObject::SaveToJSON(m_MapSettings);
// TODO: would be nice if we supported undo for settings changes

View File

@ -23,7 +23,6 @@
#include "ScenarioEditor/ScenarioEditor.h"
#include "ScenarioEditor/Tools/Common/Brushes.h"
#include "ScenarioEditor/Tools/Common/MiscState.h"
#include "AtlasScript/ScriptInterface.h"
#include "GameInterface/Messages.h"
@ -286,7 +285,7 @@ void TerrainSidebar::OnResizeMap(wxCommandEvent& WXUNUSED(evt))
// Load the map sizes list
AtlasMessage::qGetMapSizes qrySizes;
qrySizes.Post();
AtObj sizes = AtlasObject::LoadFromJSON(m_ScenarioEditor.GetScriptInterface().GetContext(), *qrySizes.sizes);
AtObj sizes = AtlasObject::LoadFromJSON(*qrySizes.sizes);
for (AtIter s = sizes["Sizes"]["item"]; s.defined(); ++s)
{
long tiles = 0;