Initial release
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.vscode/
|
||||
build/
|
||||
build.*/
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
project (tiny-js)
|
||||
cmake_minimum_required (VERSION 2.6)
|
||||
|
||||
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set (CMAKE_BUILD_TYPE "Debug")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
else()
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
endif(NOT CMAKE_BUILD_TYPE)
|
||||
|
||||
if (NOT WIN32)
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
|
||||
if(COMPILER_SUPPORTS_CXX14)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall")
|
||||
else()
|
||||
message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER} has no C++14 support.")
|
||||
endif()
|
||||
endif(NOT WIN32)
|
||||
|
||||
FILE(GLOB TINY_JS_HEADER_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS.h
|
||||
)
|
||||
|
||||
FILE(GLOB TINY_JS_SOURCE_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS_Functions.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/TinyJS_MathFunctions.cpp
|
||||
)
|
||||
|
||||
add_library(tiny-js STATIC ${TINY_JS_HEADER_FILES} ${TINY_JS_SOURCE_FILES})
|
||||
|
||||
ADD_EXECUTABLE(tiny-js-cli Script.cpp ${TINY_JS_SOURCE_FILES})
|
||||
|
||||
ADD_EXECUTABLE(tiny-js-tests run_tests.cpp ${TINY_JS_SOURCE_FILES})
|
||||
|
||||
add_custom_command(
|
||||
TARGET tiny-js-tests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/tests
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tests)
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Gordon Williams
|
||||
|
||||
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.
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
CC=g++
|
||||
CFLAGS=-c -g -Wall -rdynamic -D_DEBUG
|
||||
LDFLAGS=-g -rdynamic
|
||||
|
||||
SOURCES= \
|
||||
TinyJS.cpp \
|
||||
TinyJS_Functions.cpp \
|
||||
TinyJS_MathFunctions.cpp
|
||||
|
||||
OBJECTS=$(SOURCES:.cpp=.o)
|
||||
|
||||
all: run_tests Script
|
||||
|
||||
run_tests: run_tests.o $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) run_tests.o $(OBJECTS) -o $@
|
||||
|
||||
Script: Script.o $(OBJECTS)
|
||||
$(CC) $(LDFLAGS) Script.o $(OBJECTS) -o $@
|
||||
|
||||
.cpp.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f run_tests Script run_tests.o Script.o $(OBJECTS)
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
tiny-js
|
||||
=======
|
||||
|
||||
(originally [on Google Code](https://code.google.com/p/tiny-js/))
|
||||
|
||||
This project aims to be an extremely simple (~2000 line) JavaScript interpreter, meant for
|
||||
inclusion in applications that require a simple, familiar script language that can be included
|
||||
with no dependencies other than normal C++ libraries. It currently consists of two source files:
|
||||
one containing the interpreter, another containing built-in functions such as String.substring.
|
||||
|
||||
TinyJS is not designed to be fast or full-featured. However it is great for scripting simple
|
||||
behaviour, or loading & saving settings.
|
||||
|
||||
I make absolutely no guarantees that this is compliant to JavaScript/EcmaScript standard.
|
||||
In fact I am sure it isn't. However I welcome suggestions for changes that will bring it
|
||||
closer to compliance without overly complicating the code, or useful test cases to add to
|
||||
the test suite.
|
||||
|
||||
Currently TinyJS supports:
|
||||
|
||||
* Variables, Arrays, Structures
|
||||
* JSON parsing and output
|
||||
* Functions
|
||||
* Calling C/C++ code from JavaScript
|
||||
* Objects with Inheritance (not fully implemented)
|
||||
|
||||
Please see [CodeExamples](https://github.com/gfwilliams/tiny-js/blob/wiki/CodeExamples.md) for examples of code that works...
|
||||
|
||||
For a list of known issues, please see the comments at the top of the TinyJS.cpp file, as well as the [GitHub issues](https://github.com/gfwilliams/tiny-js/issues)
|
||||
|
||||
There is also the [42tiny-js branch](https://github.com/gfwilliams/tiny-js/tree/42tiny-js) - this is maintained by Armin and provides a more full-featured JavaScript implementation than GitHub master.
|
||||
|
||||
TinyJS is released under an MIT licence.
|
||||
|
||||
Internal Structure
|
||||
------------------------
|
||||
|
||||
TinyJS uses a Recursive Descent Parser, so there is no 'Parser Generator' required. It does not
|
||||
compile to an intermediate code, and instead executes directly from source code. This makes it
|
||||
quite fast for code that is executed infrequently, and slow for loops.
|
||||
|
||||
Variables, Arrays and Objects are stored in a simple linked list tree structure (42tiny-js uses a C++ Map).
|
||||
This is simple, but relatively slow for large structures or arrays.
|
||||
|
||||
JavaScript for Microcontrollers
|
||||
--------------------------------
|
||||
|
||||
If you're after JavaScript for Microcontrollers, take a look at the
|
||||
[Espruino JavaScript Interpreter](http://www.espruino.com ) - it is a complete re-write of TinyJS
|
||||
targeted at processors with extremely low RAM (8kb or more). It is currently available for a range
|
||||
of STM32 ARM Microcontrollers, including [two boards that have it pre-installed](http://www.espruino.com/Order).
|
||||
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a simple program showing how to use TinyJS
|
||||
*/
|
||||
|
||||
#include "TinyJS.h"
|
||||
#include "TinyJS_Functions.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// const char *code = "var a = 5; if (a==5) a=4; else a=3;";
|
||||
// const char *code = "{ var a = 4; var b = 1; while (a>0) { b = b * 2; a = a - 1; } var c = 5; }";
|
||||
// const char *code = "{ var b = 1; for (var i=0;i<4;i=i+1) b = b * 2; }";
|
||||
const char* code = "function myfunc(x, y) { return x + y; } var a = myfunc(1,2); print(a);";
|
||||
|
||||
void js_print (CScriptVar* v, void* userdata)
|
||||
{
|
||||
printf ("> %s\n", v->getParameter ("text")->getString ().c_str ());
|
||||
}
|
||||
|
||||
void js_dump (CScriptVar* v, void* userdata)
|
||||
{
|
||||
CTinyJS* js = (CTinyJS*)userdata;
|
||||
js->root->trace ("> ");
|
||||
}
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
CTinyJS* js = new CTinyJS ();
|
||||
/* add the functions from TinyJS_Functions.cpp */
|
||||
registerFunctions (js);
|
||||
/* Add a native function */
|
||||
js->addNative ("function print(text)", &js_print, 0);
|
||||
js->addNative ("function dump()", &js_dump, js);
|
||||
/* Execute out bit of code - we could call 'evaluate' here if
|
||||
we wanted something returned */
|
||||
try
|
||||
{
|
||||
js->execute ("var lets_quit = 0; function quit() { lets_quit = 1; }");
|
||||
js->execute ("print(\"Interactive mode... Type quit(); to exit, or print(...); to print "
|
||||
"something, or dump() to dump the symbol table!\");");
|
||||
}
|
||||
catch (CScriptException* e)
|
||||
{
|
||||
printf ("ERROR: %s\n", e->text.c_str ());
|
||||
}
|
||||
|
||||
while (js->evaluate ("lets_quit") == "0")
|
||||
{
|
||||
char buffer[2048];
|
||||
fgets (buffer, sizeof (buffer), stdin);
|
||||
try
|
||||
{
|
||||
js->execute (buffer);
|
||||
}
|
||||
catch (CScriptException* e)
|
||||
{
|
||||
printf ("ERROR: %s\n", e->text.c_str ());
|
||||
}
|
||||
}
|
||||
delete js;
|
||||
#ifdef _WIN32
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks ();
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
+2789
File diff suppressed because it is too large
Load Diff
+526
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// If defined, this keeps a note of all calls and where from in memory. This is slower, but good for
|
||||
// debugging
|
||||
#define TINYJS_CALL_STACK
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <variant>
|
||||
#include <limits>
|
||||
#include <any>
|
||||
|
||||
#ifndef TRACE
|
||||
#define TRACE printf
|
||||
#endif // TRACE
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
constexpr int TINYJS_LOOP_MAX_ITERATIONS = 8192;
|
||||
|
||||
enum class LexType : int
|
||||
{
|
||||
Eof = 0,
|
||||
ID = 256,
|
||||
INT,
|
||||
FLOAT,
|
||||
STR,
|
||||
|
||||
EQUAL,
|
||||
TYPEEQUAL,
|
||||
NEQUAL,
|
||||
NTYPEEQUAL,
|
||||
LEQUAL,
|
||||
LSHIFT,
|
||||
LSHIFTEQUAL,
|
||||
GEQUAL,
|
||||
RSHIFT,
|
||||
RSHIFTUNSIGNED,
|
||||
RSHIFTEQUAL,
|
||||
PLUSEQUAL,
|
||||
MINUSEQUAL,
|
||||
PLUSPLUS,
|
||||
MINUSMINUS,
|
||||
ANDEQUAL,
|
||||
ANDAND,
|
||||
OREQUAL,
|
||||
OROR,
|
||||
XOREQUAL,
|
||||
|
||||
// reserved words
|
||||
R_LIST_START,
|
||||
R_IF = R_LIST_START,
|
||||
R_ELSE,
|
||||
R_DO,
|
||||
R_WHILE,
|
||||
R_FOR,
|
||||
R_BREAK,
|
||||
R_CONTINUE,
|
||||
R_FUNCTION,
|
||||
R_RETURN,
|
||||
R_VAR,
|
||||
R_TRUE,
|
||||
R_FALSE,
|
||||
R_NULL,
|
||||
R_UNDEFINED,
|
||||
R_NEW,
|
||||
|
||||
LIST_END /* always the last entry */
|
||||
};
|
||||
inline constexpr int asInteger (LexType t) { return static_cast<int> (t); }
|
||||
inline constexpr LexType asLexType (int t)
|
||||
{
|
||||
if (t < asInteger (LexType::ID) || t > (asInteger (LexType::LIST_END)))
|
||||
return LexType::Eof;
|
||||
return static_cast<LexType> (t);
|
||||
}
|
||||
|
||||
enum SCRIPTVAR_FLAGS
|
||||
{
|
||||
SCRIPTVAR_UNDEFINED = 0,
|
||||
SCRIPTVAR_FUNCTION = 1,
|
||||
SCRIPTVAR_OBJECT = 2,
|
||||
SCRIPTVAR_ARRAY = 4,
|
||||
SCRIPTVAR_DOUBLE = 8, // floating point double
|
||||
SCRIPTVAR_INTEGER = 16, // integer number
|
||||
SCRIPTVAR_STRING = 32, // string
|
||||
SCRIPTVAR_NULL = 64, // it seems null is its own data type
|
||||
|
||||
SCRIPTVAR_NATIVE = 128, // to specify this is a native function
|
||||
SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | SCRIPTVAR_DOUBLE | SCRIPTVAR_INTEGER,
|
||||
SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | SCRIPTVAR_INTEGER | SCRIPTVAR_STRING |
|
||||
SCRIPTVAR_FUNCTION | SCRIPTVAR_OBJECT | SCRIPTVAR_ARRAY |
|
||||
SCRIPTVAR_NULL,
|
||||
|
||||
};
|
||||
|
||||
static constexpr auto TINYJS_RETURN_VAR = "return";
|
||||
static constexpr auto TINYJS_PROTOTYPE_CLASS = "prototype";
|
||||
static constexpr auto TINYJS_TEMP_NAME = "";
|
||||
static constexpr auto TINYJS_BLANK_DATA = "";
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Custom memory allocator
|
||||
using AllocatorFunc = std::function<void*(size_t)>;
|
||||
using DeallocatorFunc = std::function<void (void*, size_t)>;
|
||||
extern AllocatorFunc allocator;
|
||||
extern DeallocatorFunc deallocator;
|
||||
|
||||
void setCustomAllocator (AllocatorFunc&& allocator, DeallocatorFunc&& deallocator);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
template<typename T>
|
||||
struct Allocator
|
||||
{
|
||||
using value_type = T;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
|
||||
Allocator () = default;
|
||||
|
||||
template<class U>
|
||||
constexpr Allocator (const Allocator<U>&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] T* allocate (std::size_t n)
|
||||
{
|
||||
return static_cast<T*> (allocator (n * sizeof (T)));
|
||||
}
|
||||
void deallocate (T* p, std::size_t n) noexcept { deallocator (p, n); }
|
||||
|
||||
bool operator== (const Allocator& other) const { return &other == this; }
|
||||
bool operator!= (const Allocator& other) const { return &other != this; }
|
||||
};
|
||||
|
||||
using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
|
||||
using ostringstream = std::basic_ostringstream<char, std::char_traits<char>, Allocator<char>>;
|
||||
|
||||
/** convert the given string into a quoted string suitable for javascript */
|
||||
string getJSString (std::string_view str);
|
||||
/** convert the given string to an 64 bit integer supporting hex and octal written numbers */
|
||||
int64_t stringToInteger (std::string_view str);
|
||||
|
||||
class CScriptException
|
||||
{
|
||||
public:
|
||||
string text;
|
||||
CScriptException (const string& exceptionText);
|
||||
CScriptException (string&& exceptionText);
|
||||
CScriptException (const CScriptException&) = default;
|
||||
CScriptException (CScriptException&&) = default;
|
||||
~CScriptException () noexcept;
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
};
|
||||
|
||||
class CScriptLex
|
||||
{
|
||||
public:
|
||||
CScriptLex (std::string_view input);
|
||||
~CScriptLex (void);
|
||||
|
||||
/** Get the string representation of the given token */
|
||||
static string getTokenStr (int token);
|
||||
|
||||
/** Lexical match wotsit */
|
||||
void match (int expected_tk);
|
||||
void match (LexType expected_tk);
|
||||
/** Reset this lex so we can start again */
|
||||
void reset ();
|
||||
|
||||
int getToken () const { return token; }
|
||||
size_t getTokenStart () const { return tokenStart; }
|
||||
size_t getTokenEnd () const { return tokenEnd; }
|
||||
const string& getTokenString () const { return tkStr; }
|
||||
|
||||
/** Return a sub-string from the given position up until right now */
|
||||
string getSubString (size_t pos) const;
|
||||
/** Return a sub-lexer from the given position up until right now */
|
||||
CScriptLex* getSubLex (size_t lastPosition) const;
|
||||
/** Return a string representing the position in lines and columns of the character pos given */
|
||||
string getPosition (size_t pos = std::numeric_limits<size_t>::max ()) const;
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
private:
|
||||
void getNextCh ();
|
||||
/** Get the text token from our text string */
|
||||
void getNextToken ();
|
||||
|
||||
/** The type of the token that we have */
|
||||
int token;
|
||||
/** Position in the data at the beginning of the token we have here */
|
||||
size_t tokenStart;
|
||||
/** Position in the data at the last character of the token we have here */
|
||||
size_t tokenEnd;
|
||||
/** Position in the data at the last character of the last token */
|
||||
size_t tokenLastEnd;
|
||||
/** Data contained in the token we have here */
|
||||
string tkStr;
|
||||
|
||||
char currCh, nextCh;
|
||||
|
||||
/** Data string to get tokens from */
|
||||
const char* data;
|
||||
/** Start and end position in data string */
|
||||
size_t dataEnd;
|
||||
/** Position in data (we CAN go past the end of the string here) */
|
||||
size_t dataPos;
|
||||
|
||||
std::vector<size_t, Allocator<size_t>> newLinePositions;
|
||||
};
|
||||
|
||||
class CScriptVar;
|
||||
|
||||
using JSCallback = std::function<void (CScriptVar* var)>;
|
||||
|
||||
class CScriptVarLink
|
||||
{
|
||||
public:
|
||||
CScriptVarLink (CScriptVar* var, const string& name = TINYJS_TEMP_NAME, bool own = false);
|
||||
/** Copy constructor */
|
||||
CScriptVarLink (const CScriptVarLink& link);
|
||||
~CScriptVarLink ();
|
||||
/** Replace the Variable pointed to */
|
||||
void replaceWith (CScriptVar* newVar);
|
||||
/** Replace the Variable pointed to (just dereferences) */
|
||||
void replaceWith (CScriptVarLink* newVar);
|
||||
/** Get the name as an integer (for arrays) */
|
||||
int getIntName () const;
|
||||
/** Set the name as an integer (for arrays) */
|
||||
void setIntName (int n);
|
||||
|
||||
const string& getName () const { return name; }
|
||||
|
||||
void setNextSibling (CScriptVarLink* s) { nextSibling = s; }
|
||||
void setPrevSibling (CScriptVarLink* s) { prevSibling = s; }
|
||||
CScriptVarLink* getNextSibling () const { return nextSibling; }
|
||||
CScriptVarLink* getPrevSibling () const { return prevSibling; }
|
||||
|
||||
void setVar (CScriptVar* v);
|
||||
CScriptVar* getVar () const { return var; }
|
||||
|
||||
bool owned () const { return isOwned; }
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
private:
|
||||
string name;
|
||||
CScriptVarLink* nextSibling {nullptr};
|
||||
CScriptVarLink* prevSibling {nullptr};
|
||||
CScriptVar* var {nullptr};
|
||||
bool isOwned {false};
|
||||
};
|
||||
|
||||
struct IScriptVarLifeTimeObserver
|
||||
{
|
||||
virtual ~IScriptVarLifeTimeObserver () noexcept = default;
|
||||
|
||||
virtual void onDestroy (CScriptVar* var) = 0;
|
||||
};
|
||||
|
||||
/** Variable class (containing a doubly-linked list of children) */
|
||||
class CScriptVar
|
||||
{
|
||||
public:
|
||||
/** Create undefined */
|
||||
CScriptVar ();
|
||||
/** User defined */
|
||||
CScriptVar (const string& varData, int varFlags);
|
||||
/** Create a string */
|
||||
CScriptVar (std::string_view str);
|
||||
/** Create a double */
|
||||
CScriptVar (double varData);
|
||||
/** Create an integer */
|
||||
CScriptVar (int64_t val);
|
||||
/** Create an integer */
|
||||
CScriptVar (bool val);
|
||||
virtual ~CScriptVar (void);
|
||||
|
||||
/** If this is a function, get the result value (for use by native functions) */
|
||||
CScriptVar* getReturnVar ();
|
||||
/** Set the result value. Use this when setting complex return data as it avoids a deepCopy() */
|
||||
void setReturnVar (CScriptVar* var);
|
||||
/** If this is a function, get the parameter with the given name (for use by native functions)
|
||||
*/
|
||||
CScriptVar* getParameter (std::string_view name);
|
||||
|
||||
/** Tries to find a child with the given name, may return 0 */
|
||||
CScriptVarLink* findChild (std::string_view childName);
|
||||
/** Tries to find a child with the given name, or will create it with the given flags */
|
||||
CScriptVarLink* findChildOrCreate (std::string_view childName,
|
||||
int varFlags = SCRIPTVAR_UNDEFINED);
|
||||
/** Tries to find a child with the given path (separated by dots) */
|
||||
CScriptVarLink* findChildOrCreateByPath (const string& path);
|
||||
/** add a child if not already exist */
|
||||
CScriptVarLink* addChild (std::string_view childName, CScriptVar* child = NULL);
|
||||
/** add a child overwriting any with the same name */
|
||||
CScriptVarLink* addChildNoDup (std::string_view childName, CScriptVar* child = NULL);
|
||||
/** remove the child */
|
||||
void removeChild (CScriptVar* child);
|
||||
/** Remove a specific link (this is faster than finding via a child) */
|
||||
void removeLink (CScriptVarLink* link);
|
||||
void removeAllChildren ();
|
||||
/** The the value at an array index */
|
||||
CScriptVar* getArrayIndex (int idx);
|
||||
/** Set the value at an array index */
|
||||
void setArrayIndex (int idx, CScriptVar* value);
|
||||
/** If this is an array, return the number of items in it (else 0) */
|
||||
int getArrayLength ();
|
||||
/** Get the number of children */
|
||||
int getChildren ();
|
||||
|
||||
int64_t getInt ();
|
||||
bool getBool () { return getInt () != 0; }
|
||||
double getDouble ();
|
||||
const string& getString ();
|
||||
/** get Data as a parsable javascript string */
|
||||
string getParsableString ();
|
||||
void setInt (int64_t num);
|
||||
void setDouble (double val);
|
||||
void setString (std::string_view str);
|
||||
void setUndefined ();
|
||||
void setArray ();
|
||||
bool equals (CScriptVar* v);
|
||||
|
||||
bool isInt () { return (flags & SCRIPTVAR_INTEGER) != 0; }
|
||||
bool isDouble () { return (flags & SCRIPTVAR_DOUBLE) != 0; }
|
||||
bool isString () { return (flags & SCRIPTVAR_STRING) != 0; }
|
||||
bool isNumeric () { return (flags & SCRIPTVAR_NUMERICMASK) != 0; }
|
||||
bool isFunction () { return (flags & SCRIPTVAR_FUNCTION) != 0; }
|
||||
bool isObject () { return (flags & SCRIPTVAR_OBJECT) != 0; }
|
||||
bool isArray () { return (flags & SCRIPTVAR_ARRAY) != 0; }
|
||||
bool isNative () { return (flags & SCRIPTVAR_NATIVE) != 0; }
|
||||
bool isUndefined () { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; }
|
||||
bool isNull () { return (flags & SCRIPTVAR_NULL) != 0; }
|
||||
/** Is this *not* an array/object/etc */
|
||||
bool isBasic () { return firstChild == 0; }
|
||||
|
||||
/** do a maths op with another script variable */
|
||||
CScriptVar* mathsOp (CScriptVar* b, int op);
|
||||
/** copy the value from the value given */
|
||||
void copyValue (CScriptVar* val);
|
||||
/** deep copy this node and return the result */
|
||||
CScriptVar* deepCopy ();
|
||||
|
||||
/** Dump out the contents of this using trace */
|
||||
void trace (string indentStr = "", const string& name = "");
|
||||
/** For debugging - just dump a string version of the flags */
|
||||
string getFlagsAsString ();
|
||||
/** Write out all the JS code needed to recreate this script variable to the stream (as JSON) */
|
||||
void getJSON (std::ostream& destination, const string linePrefix = "");
|
||||
/** Set the callback for native functions */
|
||||
void setCallback (const JSCallback& callback);
|
||||
/** Moves in the callback for native functions */
|
||||
void setCallback (JSCallback&& callback);
|
||||
|
||||
void callCallback (CScriptVar* var);
|
||||
|
||||
void setFunctionScript (std::string_view str);
|
||||
|
||||
/// For memory management/garbage collection
|
||||
/** Add reference to this variable */
|
||||
CScriptVar* addRef ();
|
||||
/** Remove a reference, and delete this variable if required */
|
||||
void release ();
|
||||
/** Get the number of references to this script variable */
|
||||
int getRefs ();
|
||||
|
||||
void setLifeTimeObserver (IScriptVarLifeTimeObserver* obs) { lifeTimeObserver = obs; }
|
||||
|
||||
CScriptVarLink* getFirstChild () const { return firstChild; }
|
||||
CScriptVarLink* getLastChild () const { return lastChild; }
|
||||
|
||||
void setCustomData (std::any&& cd) { customData = std::move (cd); }
|
||||
const std::any& getCustomData () const { return customData; }
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
protected:
|
||||
CScriptVarLink* firstChild {nullptr};
|
||||
CScriptVarLink* lastChild {nullptr};
|
||||
|
||||
/** The number of references held to this - used for garbage collection */
|
||||
int refs {0};
|
||||
/** the flags determine the type of the variable - int/double/string/etc */
|
||||
int flags {0};
|
||||
std::variant<string, int64_t, double, JSCallback> variant;
|
||||
std::any customData;
|
||||
string dataStr;
|
||||
|
||||
/** Copy the basic data and flags from the variable given, with no
|
||||
* children. Should be used internally only - by copyValue and deepCopy */
|
||||
void copySimpleData (CScriptVar* val);
|
||||
|
||||
private:
|
||||
IScriptVarLifeTimeObserver* lifeTimeObserver {nullptr};
|
||||
};
|
||||
|
||||
inline CScriptVar* owning (CScriptVar* v) { return v->addRef (); }
|
||||
|
||||
class CTinyJS
|
||||
{
|
||||
public:
|
||||
CTinyJS ();
|
||||
~CTinyJS ();
|
||||
|
||||
void execute (const string& code);
|
||||
/** Evaluate the given code and return a link to a javascript object,
|
||||
* useful for (dangerous) JSON parsing. If nothing to return, will return
|
||||
* 'undefined' variable type. CScriptVarLink is returned as this will
|
||||
* automatically release the result as it goes out of scope. If you want to
|
||||
* keep it, you must use addRef() and release() */
|
||||
CScriptVarLink evaluateComplex (std::string_view code);
|
||||
/** Evaluate the given code and return a string. If nothing to return, will return
|
||||
* 'undefined' */
|
||||
string evaluate (std::string_view code);
|
||||
|
||||
/** add a native function to be called from TinyJS
|
||||
example:
|
||||
\code
|
||||
void scRandInt(CScriptVar *c, void *userdata) { ... }
|
||||
tinyJS->addNative("function randInt(min, max)", scRandInt, 0);
|
||||
\endcode
|
||||
|
||||
or
|
||||
|
||||
\code
|
||||
void scSubstring(CScriptVar *c, void *userdata) { ... }
|
||||
tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0);
|
||||
\endcode
|
||||
*/
|
||||
void addNative (std::string_view funcDesc, const JSCallback& ptr);
|
||||
|
||||
/** Get the given variable specified by a path (var1.var2.etc), or return 0 */
|
||||
CScriptVar* getScriptVariable (const string& path) const;
|
||||
/** Get the value of the given variable, or return 0 */
|
||||
const string* getVariable (const string& path) const;
|
||||
/** set the value of the given variable, return trur if it exists and gets set */
|
||||
bool setVariable (const string& path, const string& varData);
|
||||
|
||||
/** Send all variables to stdout */
|
||||
void trace ();
|
||||
|
||||
CScriptVar* getRoot () const { return root; }
|
||||
|
||||
static void* operator new (std::size_t count);
|
||||
static void operator delete (void* ptr, std::size_t size);
|
||||
|
||||
private:
|
||||
/** root of symbol table */
|
||||
CScriptVar* root {nullptr};
|
||||
/** current lexer */
|
||||
CScriptLex* lexer {nullptr};
|
||||
/** stack of scopes when parsing */
|
||||
std::vector<CScriptVar*> scopes;
|
||||
#ifdef TINYJS_CALL_STACK
|
||||
/** Names of places called so we can show when erroring */
|
||||
std::vector<string> call_stack;
|
||||
#endif
|
||||
|
||||
/** Built in string class */
|
||||
CScriptVar* stringClass {nullptr};
|
||||
/** Built in object class */
|
||||
CScriptVar* objectClass {nullptr};
|
||||
/** Built in array class */
|
||||
CScriptVar* arrayClass {nullptr};
|
||||
|
||||
// parsing - in order of precedence
|
||||
CScriptVarLink* functionCall (bool& execute, CScriptVarLink* function, CScriptVar* parent);
|
||||
CScriptVarLink* factor (bool& execute);
|
||||
CScriptVarLink* unary (bool& execute);
|
||||
CScriptVarLink* term (bool& execute);
|
||||
CScriptVarLink* expression (bool& execute);
|
||||
CScriptVarLink* shift (bool& execute);
|
||||
CScriptVarLink* condition (bool& execute);
|
||||
CScriptVarLink* logic (bool& execute);
|
||||
CScriptVarLink* ternary (bool& execute);
|
||||
CScriptVarLink* base (bool& execute);
|
||||
void block (bool& execute);
|
||||
void statement (bool& execute);
|
||||
// parsing utility functions
|
||||
CScriptVarLink* parseFunctionDefinition ();
|
||||
void parseFunctionArguments (CScriptVar* funcVar) const;
|
||||
|
||||
/** Finds a child, looking recursively up the scopes */
|
||||
CScriptVarLink* findInScopes (const string& childName) const;
|
||||
/** Look up in any parent classes of the given object */
|
||||
CScriptVarLink* findInParentClasses (CScriptVar* object, const string& name) const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* - Useful language functions
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "TinyJS_Functions.h"
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
using namespace std;
|
||||
using namespace std::literals;
|
||||
// ----------------------------------------------- Actual Functions
|
||||
void scTrace (CScriptVar* c, void* userdata)
|
||||
{
|
||||
CTinyJS* js = (CTinyJS*)userdata;
|
||||
js->getRoot ()->trace ();
|
||||
}
|
||||
|
||||
void scObjectDump (CScriptVar* c) { c->getParameter ("this"sv)->trace ("> "); }
|
||||
|
||||
void scObjectClone (CScriptVar* c)
|
||||
{
|
||||
CScriptVar* obj = c->getParameter ("this"sv);
|
||||
c->getReturnVar ()->copyValue (obj);
|
||||
}
|
||||
|
||||
void scMathRand (CScriptVar* c) { c->getReturnVar ()->setDouble ((double)rand () / RAND_MAX); }
|
||||
|
||||
void scMathRandInt (CScriptVar* c)
|
||||
{
|
||||
auto min = c->getParameter ("min"sv)->getInt ();
|
||||
auto max = c->getParameter ("max"sv)->getInt ();
|
||||
auto val = min + (int64_t)(rand () % (1 + max - min));
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scCharToInt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("ch"sv)->getString ();
|
||||
;
|
||||
int val = 0;
|
||||
if (str.length () > 0)
|
||||
val = (int)str.c_str ()[0];
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scStringIndexOf (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
string search = c->getParameter ("search"sv)->getString ();
|
||||
size_t p = str.find (search);
|
||||
auto val = (p == string::npos) ? -1 : p;
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scStringSubstring (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
auto lo = c->getParameter ("lo"sv)->getInt ();
|
||||
auto hi = c->getParameter ("hi"sv)->getInt ();
|
||||
|
||||
auto l = hi - lo;
|
||||
if (l > 0 && lo >= 0 && lo + l <= static_cast<int64_t> (str.length ()))
|
||||
c->getReturnVar ()->setString (str.substr (lo, l));
|
||||
else
|
||||
c->getReturnVar ()->setString ("");
|
||||
}
|
||||
|
||||
void scStringCharAt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
auto p = c->getParameter ("pos"sv)->getInt ();
|
||||
if (p >= 0 && p < static_cast<int64_t> (str.length ()))
|
||||
c->getReturnVar ()->setString (str.substr (p, 1));
|
||||
else
|
||||
c->getReturnVar ()->setString ("");
|
||||
}
|
||||
|
||||
void scStringCharCodeAt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
auto p = c->getParameter ("pos"sv)->getInt ();
|
||||
if (p >= 0 && p < static_cast<int64_t> (str.length ()))
|
||||
c->getReturnVar ()->setInt (str.at (p));
|
||||
else
|
||||
c->getReturnVar ()->setInt (0);
|
||||
}
|
||||
|
||||
void scStringSplit (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("this"sv)->getString ();
|
||||
string sep = c->getParameter ("separator"sv)->getString ();
|
||||
CScriptVar* result = c->getReturnVar ();
|
||||
result->setArray ();
|
||||
int length = 0;
|
||||
|
||||
size_t pos = str.find (sep);
|
||||
while (pos != string::npos)
|
||||
{
|
||||
result->setArrayIndex (length++, new CScriptVar (str.substr (0, pos)));
|
||||
str = str.substr (pos + 1);
|
||||
pos = str.find (sep);
|
||||
}
|
||||
|
||||
if (str.size () > 0)
|
||||
result->setArrayIndex (length++, new CScriptVar (str));
|
||||
}
|
||||
|
||||
void scStringFromCharCode (CScriptVar* c)
|
||||
{
|
||||
char str[2];
|
||||
str[0] = static_cast<char> (c->getParameter ("char"sv)->getInt ());
|
||||
str[1] = 0;
|
||||
c->getReturnVar ()->setString (str);
|
||||
}
|
||||
|
||||
void scIntegerParseInt (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("str"sv)->getString ();
|
||||
auto val = stringToInteger (str);
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scIntegerValueOf (CScriptVar* c)
|
||||
{
|
||||
string str = c->getParameter ("str"sv)->getString ();
|
||||
|
||||
int val = 0;
|
||||
if (str.length () == 1)
|
||||
val = str[0];
|
||||
c->getReturnVar ()->setInt (val);
|
||||
}
|
||||
|
||||
void scJSONStringify (CScriptVar* c)
|
||||
{
|
||||
ostringstream result;
|
||||
c->getParameter ("obj"sv)->getJSON (result);
|
||||
c->getReturnVar ()->setString (result.str ());
|
||||
}
|
||||
|
||||
void scExec (CScriptVar* c, void* data)
|
||||
{
|
||||
CTinyJS* tinyJS = (CTinyJS*)data;
|
||||
string str = c->getParameter ("jsCode"sv)->getString ();
|
||||
tinyJS->execute (str);
|
||||
}
|
||||
|
||||
void scEval (CScriptVar* c, void* data)
|
||||
{
|
||||
CTinyJS* tinyJS = (CTinyJS*)data;
|
||||
string str = c->getParameter ("jsCode"sv)->getString ();
|
||||
c->setReturnVar (tinyJS->evaluateComplex (str).getVar ());
|
||||
}
|
||||
|
||||
void scArrayContains (CScriptVar* c)
|
||||
{
|
||||
CScriptVar* obj = c->getParameter ("obj"sv);
|
||||
CScriptVarLink* v = c->getParameter ("this"sv)->getFirstChild ();
|
||||
|
||||
bool contains = false;
|
||||
while (v)
|
||||
{
|
||||
if (v->getVar ()->equals (obj))
|
||||
{
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
v = v->getNextSibling ();
|
||||
}
|
||||
|
||||
c->getReturnVar ()->setInt (contains);
|
||||
}
|
||||
|
||||
void scArrayRemove (CScriptVar* c)
|
||||
{
|
||||
CScriptVar* obj = c->getParameter ("obj"sv);
|
||||
vector<int> removedIndices;
|
||||
CScriptVarLink* v;
|
||||
// remove
|
||||
v = c->getParameter ("this"sv)->getFirstChild ();
|
||||
while (v)
|
||||
{
|
||||
if (v->getVar ()->equals (obj))
|
||||
{
|
||||
removedIndices.push_back (v->getIntName ());
|
||||
}
|
||||
v = v->getNextSibling ();
|
||||
}
|
||||
// renumber
|
||||
v = c->getParameter ("this"sv)->getFirstChild ();
|
||||
while (v)
|
||||
{
|
||||
int n = v->getIntName ();
|
||||
int newn = n;
|
||||
for (size_t i = 0; i < removedIndices.size (); i++)
|
||||
if (n >= removedIndices[i])
|
||||
newn--;
|
||||
if (newn != n)
|
||||
v->setIntName (newn);
|
||||
v = v->getNextSibling ();
|
||||
}
|
||||
}
|
||||
|
||||
void scArrayJoin (CScriptVar* c)
|
||||
{
|
||||
string sep = c->getParameter ("separator"sv)->getString ();
|
||||
CScriptVar* arr = c->getParameter ("this"sv);
|
||||
|
||||
ostringstream sstr;
|
||||
int l = arr->getArrayLength ();
|
||||
for (int i = 0; i < l; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
sstr << sep;
|
||||
sstr << arr->getArrayIndex (i)->getString ();
|
||||
}
|
||||
|
||||
c->getReturnVar ()->setString (sstr.str ());
|
||||
}
|
||||
|
||||
// ----------------------------------------------- Register Functions
|
||||
void registerFunctions (CTinyJS* tinyJS)
|
||||
{
|
||||
|
||||
tinyJS->addNative ("function exec(jsCode)"sv, [=] (auto scriptVar) {
|
||||
scExec (scriptVar, tinyJS);
|
||||
}); // execute the given code
|
||||
tinyJS->addNative ("function eval(jsCode)"sv, [=] (auto scriptVar) {
|
||||
scEval (scriptVar, tinyJS);
|
||||
}); // execute the given string (an expression) and return the result
|
||||
tinyJS->addNative ("function trace()"sv, [=] (auto scriptVar) { scTrace (scriptVar, tinyJS); });
|
||||
tinyJS->addNative ("function Object.dump()"sv, scObjectDump);
|
||||
tinyJS->addNative ("function Object.clone()"sv, scObjectClone);
|
||||
tinyJS->addNative ("function Math.rand()"sv, scMathRand);
|
||||
tinyJS->addNative ("function Math.randInt(min, max)"sv, scMathRandInt);
|
||||
tinyJS->addNative ("function charToInt(ch)"sv,
|
||||
scCharToInt); // convert a character to an int - get its value
|
||||
tinyJS->addNative ("function String.indexOf(search)"sv,
|
||||
scStringIndexOf); // find the position of a string in a string, -1 if not
|
||||
tinyJS->addNative ("function String.substring(lo,hi)"sv, scStringSubstring);
|
||||
tinyJS->addNative ("function String.charAt(pos)"sv, scStringCharAt);
|
||||
tinyJS->addNative ("function String.charCodeAt(pos)"sv, scStringCharCodeAt);
|
||||
tinyJS->addNative ("function String.fromCharCode(char)"sv, scStringFromCharCode);
|
||||
tinyJS->addNative ("function String.split(separator)"sv, scStringSplit);
|
||||
tinyJS->addNative ("function Integer.parseInt(str)"sv, scIntegerParseInt); // string to int
|
||||
tinyJS->addNative ("function Integer.valueOf(str)"sv,
|
||||
scIntegerValueOf); // value of a single character
|
||||
tinyJS->addNative ("function JSON.stringify(obj, replacer)"sv,
|
||||
scJSONStringify); // convert to JSON. replacer is ignored at the moment
|
||||
// JSON.parse is left out as you can (unsafely!) use eval instead
|
||||
tinyJS->addNative ("function Array.contains(obj)"sv, scArrayContains);
|
||||
tinyJS->addNative ("function Array.remove(obj)"sv, scArrayRemove);
|
||||
tinyJS->addNative ("function Array.join(separator)"sv, scArrayJoin);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TinyJS.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
/// Register useful functions with the TinyJS interpreter
|
||||
void registerFunctions (CTinyJS* tinyJS);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
Vendored
+271
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* - Math and Trigonometry functions
|
||||
*
|
||||
* Authored By O.Z.L.B. <ozlbinfo@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2011 O.Z.L.B.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include "TinyJS_MathFunctions.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define k_E exp (1.0)
|
||||
#define k_PI 3.1415926535897932384626433832795
|
||||
|
||||
#define F_ABS(a) ((a) >= 0 ? (a) : (-(a)))
|
||||
#define F_MIN(a, b) ((a) > (b) ? (b) : (a))
|
||||
#define F_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define F_SGN(a) ((a) > 0 ? 1 : ((a) < 0 ? -1 : 0))
|
||||
#define F_RNG(a, min, max) ((a) < (min) ? min : ((a) > (max) ? max : a))
|
||||
#define F_ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : (int)((a)-0.5))
|
||||
|
||||
// CScriptVar shortcut macro
|
||||
#define scIsInt(a) (c->getParameter (a)->isInt ())
|
||||
#define scIsDouble(a) (c->getParameter (a)->isDouble ())
|
||||
#define scGetInt(a) (c->getParameter (a)->getInt ())
|
||||
#define scGetDouble(a) (c->getParameter (a)->getDouble ())
|
||||
#define scReturnInt(a) (c->getReturnVar ()->setInt (a))
|
||||
#define scReturnDouble(a) (c->getReturnVar ()->setDouble (a))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
namespace {
|
||||
double asinh (const double& value)
|
||||
{
|
||||
double returned;
|
||||
|
||||
if (value > 0)
|
||||
returned = log (value + sqrt (value * value + 1));
|
||||
else
|
||||
returned = -log (-value + sqrt (value * value + 1));
|
||||
|
||||
return (returned);
|
||||
}
|
||||
|
||||
double acosh (const double& value)
|
||||
{
|
||||
double returned;
|
||||
|
||||
if (value > 0)
|
||||
returned = log (value + sqrt (value * value - 1));
|
||||
else
|
||||
returned = -log (-value + sqrt (value * value - 1));
|
||||
|
||||
return (returned);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Math.abs(x) - returns absolute of given value
|
||||
void scMathAbs (CScriptVar* c)
|
||||
{
|
||||
if (scIsInt ("a"))
|
||||
{
|
||||
scReturnInt (F_ABS (scGetInt ("a")));
|
||||
}
|
||||
else if (scIsDouble ("a"))
|
||||
{
|
||||
scReturnDouble (F_ABS (scGetDouble ("a")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.round(a) - returns nearest round of given value
|
||||
void scMathRound (CScriptVar* c)
|
||||
{
|
||||
if (scIsInt ("a"))
|
||||
{
|
||||
scReturnInt (F_ROUND (scGetInt ("a")));
|
||||
}
|
||||
else if (scIsDouble ("a"))
|
||||
{
|
||||
scReturnDouble (F_ROUND (scGetDouble ("a")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.min(a,b) - returns minimum of two given values
|
||||
void scMathMin (CScriptVar* c)
|
||||
{
|
||||
if ((scIsInt ("a")) && (scIsInt ("b")))
|
||||
{
|
||||
scReturnInt (F_MIN (scGetInt ("a"), scGetInt ("b")));
|
||||
}
|
||||
else
|
||||
{
|
||||
scReturnDouble (F_MIN (scGetDouble ("a"), scGetDouble ("b")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.max(a,b) - returns maximum of two given values
|
||||
void scMathMax (CScriptVar* c)
|
||||
{
|
||||
if ((scIsInt ("a")) && (scIsInt ("b")))
|
||||
{
|
||||
scReturnInt (F_MAX (scGetInt ("a"), scGetInt ("b")));
|
||||
}
|
||||
else
|
||||
{
|
||||
scReturnDouble (F_MAX (scGetDouble ("a"), scGetDouble ("b")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.range(x,a,b) - returns value limited between two given values
|
||||
void scMathRange (CScriptVar* c)
|
||||
{
|
||||
if ((scIsInt ("x")))
|
||||
{
|
||||
scReturnInt (F_RNG (scGetInt ("x"), scGetInt ("a"), scGetInt ("b")));
|
||||
}
|
||||
else
|
||||
{
|
||||
scReturnDouble (F_RNG (scGetDouble ("x"), scGetDouble ("a"), scGetDouble ("b")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.sign(a) - returns sign of given value (-1==negative,0=zero,1=positive)
|
||||
void scMathSign (CScriptVar* c)
|
||||
{
|
||||
if (scIsInt ("a"))
|
||||
{
|
||||
scReturnInt (F_SGN (scGetInt ("a")));
|
||||
}
|
||||
else if (scIsDouble ("a"))
|
||||
{
|
||||
scReturnDouble (F_SGN (scGetDouble ("a")));
|
||||
}
|
||||
}
|
||||
|
||||
// Math.PI() - returns PI value
|
||||
void scMathPI (CScriptVar* c) { scReturnDouble (k_PI); }
|
||||
|
||||
// Math.toDegrees(a) - returns degree value of a given angle in radians
|
||||
void scMathToDegrees (CScriptVar* c) { scReturnDouble ((180.0 / k_PI) * (scGetDouble ("a"))); }
|
||||
|
||||
// Math.toRadians(a) - returns radians value of a given angle in degrees
|
||||
void scMathToRadians (CScriptVar* c) { scReturnDouble ((k_PI / 180.0) * (scGetDouble ("a"))); }
|
||||
|
||||
// Math.sin(a) - returns trig. sine of given angle in radians
|
||||
void scMathSin (CScriptVar* c) { scReturnDouble (sin (scGetDouble ("a"))); }
|
||||
|
||||
// Math.asin(a) - returns trig. arcsine of given angle in radians
|
||||
void scMathASin (CScriptVar* c) { scReturnDouble (asin (scGetDouble ("a"))); }
|
||||
|
||||
// Math.cos(a) - returns trig. cosine of given angle in radians
|
||||
void scMathCos (CScriptVar* c) { scReturnDouble (cos (scGetDouble ("a"))); }
|
||||
|
||||
// Math.acos(a) - returns trig. arccosine of given angle in radians
|
||||
void scMathACos (CScriptVar* c) { scReturnDouble (acos (scGetDouble ("a"))); }
|
||||
|
||||
// Math.tan(a) - returns trig. tangent of given angle in radians
|
||||
void scMathTan (CScriptVar* c) { scReturnDouble (tan (scGetDouble ("a"))); }
|
||||
|
||||
// Math.atan(a) - returns trig. arctangent of given angle in radians
|
||||
void scMathATan (CScriptVar* c) { scReturnDouble (atan (scGetDouble ("a"))); }
|
||||
|
||||
// Math.sinh(a) - returns trig. hyperbolic sine of given angle in radians
|
||||
void scMathSinh (CScriptVar* c) { scReturnDouble (sinh (scGetDouble ("a"))); }
|
||||
|
||||
// Math.asinh(a) - returns trig. hyperbolic arcsine of given angle in radians
|
||||
void scMathASinh (CScriptVar* c) { scReturnDouble (asinh ((long double)scGetDouble ("a"))); }
|
||||
|
||||
// Math.cosh(a) - returns trig. hyperbolic cosine of given angle in radians
|
||||
void scMathCosh (CScriptVar* c) { scReturnDouble (cosh (scGetDouble ("a"))); }
|
||||
|
||||
// Math.acosh(a) - returns trig. hyperbolic arccosine of given angle in radians
|
||||
void scMathACosh (CScriptVar* c) { scReturnDouble (acosh ((long double)scGetDouble ("a"))); }
|
||||
|
||||
// Math.tanh(a) - returns trig. hyperbolic tangent of given angle in radians
|
||||
void scMathTanh (CScriptVar* c) { scReturnDouble (tanh (scGetDouble ("a"))); }
|
||||
|
||||
// Math.atan(a) - returns trig. hyperbolic arctangent of given angle in radians
|
||||
void scMathATanh (CScriptVar* c) { scReturnDouble (atan (scGetDouble ("a"))); }
|
||||
|
||||
// Math.E() - returns E Neplero value
|
||||
void scMathE (CScriptVar* c) { scReturnDouble (k_E); }
|
||||
|
||||
// Math.log(a) - returns natural logaritm (base E) of given value
|
||||
void scMathLog (CScriptVar* c) { scReturnDouble (log (scGetDouble ("a"))); }
|
||||
|
||||
// Math.log10(a) - returns logaritm(base 10) of given value
|
||||
void scMathLog10 (CScriptVar* c) { scReturnDouble (log10 (scGetDouble ("a"))); }
|
||||
|
||||
// Math.exp(a) - returns e raised to the power of a given number
|
||||
void scMathExp (CScriptVar* c) { scReturnDouble (exp (scGetDouble ("a"))); }
|
||||
|
||||
// Math.pow(a,b) - returns the result of a number raised to a power (a)^(b)
|
||||
void scMathPow (CScriptVar* c) { scReturnDouble (pow (scGetDouble ("a"), scGetDouble ("b"))); }
|
||||
|
||||
// Math.sqr(a) - returns square of given value
|
||||
void scMathSqr (CScriptVar* c) { scReturnDouble ((scGetDouble ("a") * scGetDouble ("a"))); }
|
||||
|
||||
// Math.sqrt(a) - returns square root of given value
|
||||
void scMathSqrt (CScriptVar* c) { scReturnDouble (sqrt (scGetDouble ("a"))); }
|
||||
|
||||
// ----------------------------------------------- Register Functions
|
||||
void registerMathFunctions (CTinyJS* tinyJS)
|
||||
{
|
||||
using namespace std::literals;
|
||||
|
||||
// --- Math and Trigonometry functions ---
|
||||
tinyJS->addNative ("function Math.abs(a)"sv, scMathAbs);
|
||||
tinyJS->addNative ("function Math.round(a)"sv, scMathRound);
|
||||
tinyJS->addNative ("function Math.min(a,b)"sv, scMathMin);
|
||||
tinyJS->addNative ("function Math.max(a,b)"sv, scMathMax);
|
||||
tinyJS->addNative ("function Math.range(x,a,b)"sv, scMathRange);
|
||||
tinyJS->addNative ("function Math.sign(a)"sv, scMathSign);
|
||||
|
||||
tinyJS->addNative ("function Math.PI()"sv, scMathPI);
|
||||
tinyJS->addNative ("function Math.toDegrees(a)"sv, scMathToDegrees);
|
||||
tinyJS->addNative ("function Math.toRadians(a)"sv, scMathToRadians);
|
||||
tinyJS->addNative ("function Math.sin(a)"sv, scMathSin);
|
||||
tinyJS->addNative ("function Math.asin(a)"sv, scMathASin);
|
||||
tinyJS->addNative ("function Math.cos(a)"sv, scMathCos);
|
||||
tinyJS->addNative ("function Math.acos(a)"sv, scMathACos);
|
||||
tinyJS->addNative ("function Math.tan(a)"sv, scMathTan);
|
||||
tinyJS->addNative ("function Math.atan(a)"sv, scMathATan);
|
||||
tinyJS->addNative ("function Math.sinh(a)"sv, scMathSinh);
|
||||
tinyJS->addNative ("function Math.asinh(a)"sv, scMathASinh);
|
||||
tinyJS->addNative ("function Math.cosh(a)"sv, scMathCosh);
|
||||
tinyJS->addNative ("function Math.acosh(a)"sv, scMathACosh);
|
||||
tinyJS->addNative ("function Math.tanh(a)"sv, scMathTanh);
|
||||
tinyJS->addNative ("function Math.atanh(a)"sv, scMathATanh);
|
||||
|
||||
tinyJS->addNative ("function Math.E()"sv, scMathE);
|
||||
tinyJS->addNative ("function Math.log(a)"sv, scMathLog);
|
||||
tinyJS->addNative ("function Math.log10(a)"sv, scMathLog10);
|
||||
tinyJS->addNative ("function Math.exp(a)"sv, scMathExp);
|
||||
tinyJS->addNative ("function Math.pow(a,b)"sv, scMathPow);
|
||||
|
||||
tinyJS->addNative ("function Math.sqr(a)"sv, scMathSqr);
|
||||
tinyJS->addNative ("function Math.sqrt(a)"sv, scMathSqrt);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "TinyJS.h"
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace TJS {
|
||||
|
||||
/// Register useful math. functions with the TinyJS interpreter
|
||||
void registerMathFunctions (CTinyJS* tinyJS);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // TJS
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* TinyJS
|
||||
*
|
||||
* A single-file Javascript-alike engine
|
||||
*
|
||||
* Authored By Gordon Williams <gw@pur3.co.uk>
|
||||
*
|
||||
* Copyright (C) 2009 Pur3 Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a program to run all the tests in the tests folder...
|
||||
*/
|
||||
|
||||
#include "TinyJS.h"
|
||||
#include "TinyJS_Functions.h"
|
||||
#include "TinyJS_MathFunctions.h"
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
|
||||
#if __APPLE_CC__
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef MTRACE
|
||||
#include <mcheck.h>
|
||||
#endif
|
||||
|
||||
// #define INSANE_MEMORY_DEBUG
|
||||
|
||||
using namespace TJS;
|
||||
|
||||
#ifdef INSANE_MEMORY_DEBUG
|
||||
// needs -rdynamic when compiling/linking
|
||||
#include <execinfo.h>
|
||||
#include <malloc.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
|
||||
void** get_stackframe ()
|
||||
{
|
||||
void** trace = (void**)malloc (sizeof (void*) * 17);
|
||||
int trace_size = 0;
|
||||
|
||||
for (int i = 0; i < 17; i++)
|
||||
trace[i] = (void*)0;
|
||||
trace_size = backtrace (trace, 16);
|
||||
return trace;
|
||||
}
|
||||
|
||||
void print_stackframe (char* header, void** trace)
|
||||
{
|
||||
char** messages = (char**)NULL;
|
||||
int trace_size = 0;
|
||||
|
||||
trace_size = 0;
|
||||
while (trace[trace_size])
|
||||
trace_size++;
|
||||
messages = backtrace_symbols (trace, trace_size);
|
||||
|
||||
printf ("%s\n", header);
|
||||
for (int i = 0; i < trace_size; ++i)
|
||||
{
|
||||
printf ("%s\n", messages[i]);
|
||||
}
|
||||
// free(messages);
|
||||
}
|
||||
|
||||
/* Prototypes for our hooks. */
|
||||
static void* my_malloc_hook (size_t, const void*);
|
||||
static void my_free_hook (void*, const void*);
|
||||
static void* (*old_malloc_hook) (size_t, const void*);
|
||||
static void (*old_free_hook) (void*, const void*);
|
||||
|
||||
map<void*, void**> malloced;
|
||||
|
||||
static void* my_malloc_hook (size_t size, const void* caller)
|
||||
{
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
/* Call recursively */
|
||||
void* result = malloc (size);
|
||||
/* we call malloc here, so protect it too. */
|
||||
// printf ("malloc (%u) returns %p\n", (unsigned int) size, result);
|
||||
malloced[result] = get_stackframe ();
|
||||
|
||||
/* Restore our own hooks */
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void my_free_hook (void* ptr, const void* caller)
|
||||
{
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
/* Call recursively */
|
||||
free (ptr);
|
||||
/* we call malloc here, so protect it too. */
|
||||
// printf ("freed pointer %p\n", ptr);
|
||||
if (malloced.find (ptr) == malloced.end ())
|
||||
{
|
||||
/*fprintf(stderr, "INVALID FREE\n");
|
||||
void *trace[16];
|
||||
int trace_size = 0;
|
||||
trace_size = backtrace(trace, 16);
|
||||
backtrace_symbols_fd(trace, trace_size, STDERR_FILENO);*/
|
||||
}
|
||||
else
|
||||
malloced.erase (ptr);
|
||||
/* Restore our own hooks */
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
}
|
||||
|
||||
void memtracing_init ()
|
||||
{
|
||||
old_malloc_hook = __malloc_hook;
|
||||
old_free_hook = __free_hook;
|
||||
__malloc_hook = my_malloc_hook;
|
||||
__free_hook = my_free_hook;
|
||||
}
|
||||
|
||||
long gethash (void** trace)
|
||||
{
|
||||
unsigned long hash = 0;
|
||||
while (*trace)
|
||||
{
|
||||
hash = (hash << 1) ^ (hash >> 63) ^ (unsigned long)*trace;
|
||||
trace++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
void memtracing_kill ()
|
||||
{
|
||||
/* Restore all old hooks */
|
||||
__malloc_hook = old_malloc_hook;
|
||||
__free_hook = old_free_hook;
|
||||
|
||||
map<long, void**> hashToReal;
|
||||
map<long, int> counts;
|
||||
map<void*, void**>::iterator it = malloced.begin ();
|
||||
while (it != malloced.end ())
|
||||
{
|
||||
long hash = gethash (it->second);
|
||||
hashToReal[hash] = it->second;
|
||||
|
||||
if (counts.find (hash) == counts.end ())
|
||||
counts[hash] = 1;
|
||||
else
|
||||
counts[hash]++;
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
vector<pair<int, long>> sorting;
|
||||
map<long, int>::iterator countit = counts.begin ();
|
||||
while (countit != counts.end ())
|
||||
{
|
||||
sorting.push_back (pair<int, long> (countit->second, countit->first));
|
||||
countit++;
|
||||
}
|
||||
|
||||
// sort
|
||||
bool done = false;
|
||||
while (!done)
|
||||
{
|
||||
done = true;
|
||||
for (int i = 0; i < sorting.size () - 1; i++)
|
||||
{
|
||||
if (sorting[i].first < sorting[i + 1].first)
|
||||
{
|
||||
pair<int, long> t = sorting[i];
|
||||
sorting[i] = sorting[i + 1];
|
||||
sorting[i + 1] = t;
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sorting.size (); i++)
|
||||
{
|
||||
long hash = sorting[i].second;
|
||||
int count = sorting[i].first;
|
||||
char header[256];
|
||||
sprintf (header, "--------------------------- LEAKED %d", count);
|
||||
print_stackframe (header, hashToReal[hash]);
|
||||
}
|
||||
}
|
||||
#endif // INSANE_MEMORY_DEBUG
|
||||
|
||||
bool run_test (const char* filename)
|
||||
{
|
||||
printf ("TEST %s ", filename);
|
||||
struct stat results;
|
||||
if (!stat (filename, &results) == 0)
|
||||
{
|
||||
printf ("Cannot stat file! '%s'\n", filename);
|
||||
return false;
|
||||
}
|
||||
int size = results.st_size;
|
||||
FILE* file = fopen (filename, "rb");
|
||||
/* if we open as text, the number of bytes read may be > the size we read */
|
||||
if (!file)
|
||||
{
|
||||
printf ("Unable to open file! '%s'\n", filename);
|
||||
return false;
|
||||
}
|
||||
char* buffer = new char[size + 1];
|
||||
long actualRead = fread (buffer, 1, size, file);
|
||||
buffer[actualRead] = 0;
|
||||
buffer[size] = 0;
|
||||
fclose (file);
|
||||
|
||||
CTinyJS s;
|
||||
registerFunctions (&s);
|
||||
registerMathFunctions (&s);
|
||||
s.getRoot ()->addChild ("result", new CScriptVar ("0", SCRIPTVAR_INTEGER));
|
||||
try
|
||||
{
|
||||
s.execute (buffer);
|
||||
}
|
||||
catch (CScriptException& e)
|
||||
{
|
||||
printf ("ERROR: %s\n", e.text.c_str ());
|
||||
}
|
||||
bool pass = s.getRoot ()->getParameter ("result")->getBool ();
|
||||
|
||||
if (pass)
|
||||
printf ("PASS\n");
|
||||
else
|
||||
{
|
||||
char fn[PATH_MAX];
|
||||
sprintf (fn, "%s.fail.js", filename);
|
||||
FILE* f = fopen (fn, "wt");
|
||||
if (f)
|
||||
{
|
||||
std::ostringstream symbols;
|
||||
s.getRoot ()->getJSON (symbols);
|
||||
fprintf (f, "%s", symbols.str ().c_str ());
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
printf ("FAIL - symbols written to %s\n", fn);
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
#if __APPLE_CC__
|
||||
struct LeakDetector
|
||||
{
|
||||
~LeakDetector () noexcept
|
||||
{
|
||||
char* env = getenv ("MallocStackLogging");
|
||||
if (env && (!strcmp (env, "1") || !strcmp (env, "lite")))
|
||||
{
|
||||
char command[1024];
|
||||
pid_t pid = getpid ();
|
||||
snprintf (command, std::size (command), "leaks %d", pid);
|
||||
system (command);
|
||||
}
|
||||
}
|
||||
};
|
||||
static LeakDetector gLeakDetector;
|
||||
#endif
|
||||
|
||||
#ifdef MTRACE
|
||||
mtrace ();
|
||||
#endif
|
||||
#ifdef INSANE_MEMORY_DEBUG
|
||||
memtracing_init ();
|
||||
#endif
|
||||
printf ("TinyJS test runner\n");
|
||||
printf ("USAGE:\n");
|
||||
printf (" ./run_tests test.js : run just one test\n");
|
||||
printf (" ./run_tests : run all tests\n");
|
||||
if (argc == 2)
|
||||
{
|
||||
return !run_test (argv[1]);
|
||||
}
|
||||
|
||||
std::string basePath (__FILE__);
|
||||
auto pos = basePath.find_last_of ('/');
|
||||
basePath.erase (pos);
|
||||
|
||||
int test_num = 1;
|
||||
int count = 0;
|
||||
int passed = 0;
|
||||
|
||||
while (test_num < 1000)
|
||||
{
|
||||
auto path = basePath; // copy
|
||||
char fn[PATH_MAX];
|
||||
snprintf (fn, std::size (fn), "/tests/test%03d.js", test_num);
|
||||
// check if the file exists - if not, assume we're at the end of our tests
|
||||
path.append (fn);
|
||||
FILE* f = fopen (path.data (), "r");
|
||||
if (!f)
|
||||
break;
|
||||
fclose (f);
|
||||
|
||||
if (run_test (path.data ()))
|
||||
passed++;
|
||||
count++;
|
||||
test_num++;
|
||||
}
|
||||
|
||||
printf ("Done. %d tests, %d pass, %d fail\n", count, passed, count - passed);
|
||||
#ifdef INSANE_MEMORY_DEBUG
|
||||
memtracing_kill ();
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef _WIN32
|
||||
_CrtDumpMemoryLeaks ();
|
||||
#endif
|
||||
#endif
|
||||
#ifdef MTRACE
|
||||
muntrace ();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
// switch-case-tests
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 1: case with break;
|
||||
////////////////////////////////////////////////////
|
||||
var a1 = 5;
|
||||
var b1 = 6;
|
||||
var r1 = 0;
|
||||
|
||||
switch (a1 + 5)
|
||||
{
|
||||
case 6:
|
||||
r1 = 2;
|
||||
break;
|
||||
case b1 + 4:
|
||||
r1 = 42;
|
||||
break;
|
||||
case 7:
|
||||
r1 = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 2: case with out break;
|
||||
////////////////////////////////////////////////////
|
||||
var a2 = 5;
|
||||
var b2 = 6;
|
||||
var r2 = 0;
|
||||
|
||||
switch (a2 + 4)
|
||||
{
|
||||
case 6:
|
||||
r2 = 2;
|
||||
break;
|
||||
case b2 + 3:
|
||||
r2 = 40;
|
||||
// break;
|
||||
case 7:
|
||||
r2 += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 3: case with default;
|
||||
////////////////////////////////////////////////////
|
||||
var a3 = 5;
|
||||
var b3 = 6;
|
||||
var r3 = 0;
|
||||
|
||||
switch (a3 + 44)
|
||||
{
|
||||
case 6:
|
||||
r3 = 2;
|
||||
break;
|
||||
case b3 + 3:
|
||||
r3 = 1;
|
||||
break;
|
||||
default:
|
||||
r3 = 42;
|
||||
break;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// switch-test 4: case default before case;
|
||||
////////////////////////////////////////////////////
|
||||
var a4 = 5;
|
||||
var b4 = 6;
|
||||
var r4 = 0;
|
||||
|
||||
switch (a4 + 44)
|
||||
{
|
||||
default:
|
||||
r4 = 42;
|
||||
break;
|
||||
case 6:
|
||||
r4 = 2;
|
||||
break;
|
||||
case b4 + 3:
|
||||
r4 = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
result = r1 == 42 && r2 == 42 && r3 == 42 && r4 == 42;
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
// function-closure
|
||||
|
||||
var a = 40; // a global var
|
||||
|
||||
function closure ()
|
||||
{
|
||||
var a = 39; // a local var;
|
||||
return function () { return a; };
|
||||
}
|
||||
|
||||
var b = closure (); // the local var a is now hidden
|
||||
|
||||
result = b () + 3 == 42 && a + 2 == 42;
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
// with-test
|
||||
|
||||
var a;
|
||||
|
||||
with (Math) a = PI;
|
||||
|
||||
var b = {get_member: function () { return this.member; }, member: 41};
|
||||
|
||||
with (b)
|
||||
{
|
||||
let a = get_member (); //<--- a is local for this block
|
||||
var c = a + 1;
|
||||
}
|
||||
|
||||
result = a == Math.PI && c == 42;
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
// generator-test
|
||||
|
||||
function fibonacci ()
|
||||
{
|
||||
var fn1 = 1;
|
||||
var fn2 = 1;
|
||||
while (1)
|
||||
{
|
||||
var current = fn2;
|
||||
fn2 = fn1;
|
||||
fn1 = fn1 + current;
|
||||
var reset = yield current;
|
||||
if (reset)
|
||||
{
|
||||
fn1 = 1;
|
||||
fn2 = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var generator = fibonacci ();
|
||||
|
||||
generator.next(); // 1
|
||||
generator.next(); // 1
|
||||
generator.next(); // 2
|
||||
generator.next(); // 3
|
||||
generator.next(); // 5
|
||||
|
||||
result = generator.next() == 8 && generator.send(true) == 1;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// simply testing we can return the correct value
|
||||
result = 1;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// comparison
|
||||
var a = 42;
|
||||
result = a == 42;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// simple for loop
|
||||
var a = 0;
|
||||
var i;
|
||||
for (i = 1; i < 10; i++)
|
||||
a = a + i;
|
||||
result = a == 45;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// simple if
|
||||
var a = 42;
|
||||
if (a < 43)
|
||||
result = 1;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// simple for loop containing initialisation, using +=
|
||||
var a = 0;
|
||||
for (var i = 1; i < 10; i++)
|
||||
a += i;
|
||||
result = a == 45;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// simple function
|
||||
function add (x, y) { return x + y; }
|
||||
result = add (3, 6) == 9;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// simple function scoping test
|
||||
var a = 7;
|
||||
function add (x, y)
|
||||
{
|
||||
var a = x + y;
|
||||
return a;
|
||||
}
|
||||
result = add (3, 6) == 9 && a == 7;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// functions in variables
|
||||
var bob = {};
|
||||
bob.add = function (x, y) { return x + y; };
|
||||
|
||||
result = bob.add(3, 6) == 9;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// functions in variables using JSON-style initialisation
|
||||
var bob = {add: function (x, y) { return x + y; }};
|
||||
|
||||
result = bob.add(3, 6) == 9;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// double function calls
|
||||
function a (x) { return x + 2; }
|
||||
function b (x) { return a (x) + 1; }
|
||||
result = a (3) == 5 && b (3) == 6;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// recursion
|
||||
function a (x)
|
||||
{
|
||||
if (x > 1)
|
||||
return x * a (x - 1);
|
||||
return 1;
|
||||
}
|
||||
result = a (5) == 1 * 2 * 3 * 4 * 5;
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// if .. else
|
||||
var a = 42;
|
||||
if (a != 42)
|
||||
result = 0;
|
||||
else
|
||||
result = 1;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// if .. else with blocks
|
||||
var a = 42;
|
||||
if (a != 42)
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 1;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Variable creation and scope from http://en.wikipedia.org/wiki/JavaScript_syntax
|
||||
x = 0; // A global variable
|
||||
var y = 'Hello!'; // Another global variable
|
||||
z = 0; // yet another global variable
|
||||
|
||||
function f ()
|
||||
{
|
||||
var z = 'foxes'; // A local variable
|
||||
twenty = 20; // Global because keyword var is not used
|
||||
return x; // We can use x here because it is global
|
||||
}
|
||||
// The value of z is no longer available
|
||||
|
||||
// testing
|
||||
blah = f ();
|
||||
result = blah == 0 && z != 'foxes' && twenty == 20;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Number definition from http://en.wikipedia.org/wiki/JavaScript_syntax
|
||||
a = 345; // an "integer", although there is only one numeric type in JavaScript
|
||||
b = 34.5; // a floating-point number
|
||||
c = 3.45e2; // another floating-point, equivalent to 345
|
||||
d = 0377; // an octal integer equal to 255
|
||||
e = 0xFF; // a hexadecimal integer equal to 255, digits represented by the letters A-F may be upper
|
||||
// or lowercase
|
||||
|
||||
result = a == 345 && b * 10 == 345 && c == 345 && d == 255 && e == 255;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Undefined/null from http://en.wikipedia.org/wiki/JavaScript_syntax
|
||||
var testUndefined; // variable declared but not defined, set to value of undefined
|
||||
var testObj = {};
|
||||
|
||||
result = 1;
|
||||
if (("" + testUndefined) != "undefined")
|
||||
result = 0; // test variable exists but value not defined, displays undefined
|
||||
if (("" + testObj.myProp) != "undefined")
|
||||
result = 0; // testObj exists, property does not, displays undefined
|
||||
if (!(undefined == null))
|
||||
result = 0; // unenforced type during check, displays true
|
||||
if (undefined === null)
|
||||
result = 0; // enforce type during check, displays false
|
||||
|
||||
if (null != undefined)
|
||||
result = 0; // unenforced type during check, displays true
|
||||
if (null === undefined)
|
||||
result = 0; // enforce type during check, displays false
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// references for arrays
|
||||
|
||||
var a = [];
|
||||
a[0] = 10;
|
||||
a[1] = 22;
|
||||
|
||||
b = a;
|
||||
|
||||
b[0] = 5;
|
||||
|
||||
result = a[0] == 5 && a[1] == 22 && b[1] == 22;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// references with functions
|
||||
|
||||
var a = 42;
|
||||
var b = [];
|
||||
b[0] = 43;
|
||||
|
||||
function foo (myarray) { myarray[0]++; }
|
||||
|
||||
function bar (myvalue) { myvalue++; }
|
||||
|
||||
foo (b);
|
||||
bar (a);
|
||||
|
||||
result = a == 42 && b[0] == 44;
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// built-in functions
|
||||
|
||||
foo = "foo bar stuff";
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript this function is called Math.random()
|
||||
// r = Math.rand();
|
||||
r = Math.random();
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript parseInt is a methode in the global scope (root-scope)
|
||||
// parsed = Integer.parseInt("42");
|
||||
parsed = parseInt ("42");
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
aStr = "ABCD";
|
||||
aChar = aStr.charAt(0);
|
||||
|
||||
obj1 = new Object ();
|
||||
obj1.food = "cake";
|
||||
obj1.desert = "pie";
|
||||
|
||||
obj2 = obj1.clone();
|
||||
obj2.food = "kittens";
|
||||
|
||||
result = foo.length == 13 && foo.indexOf("bar") == 4 && foo.substring(8, 13) == "stuff" &&
|
||||
parsed == 42 &&
|
||||
// 42-tiny-js change begin --->
|
||||
// in 42tiny-js the Integer-Objecte will be removed
|
||||
// Integer.valueOf can be replaced by String.charCodeAt
|
||||
// Integer.valueOf(aChar)==65 && obj1.food=="cake" && obj2.desert=="pie";
|
||||
aChar.charCodeAt() == 65 && obj1.food == "cake" && obj2.desert == "pie";
|
||||
//<--- 42-tiny-js change end
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// built-in functions
|
||||
|
||||
foo = "foo bar stuff";
|
||||
r = Math.rand();
|
||||
|
||||
parsed = Integer.parseInt("42");
|
||||
parsedHex = Integer.parseInt("0xFF");
|
||||
parsedOct = Integer.parseInt("011");
|
||||
parsedBig = Integer.parseInt("4294967296");
|
||||
|
||||
aStr = "ABCD";
|
||||
aChar = aStr.charAt(0);
|
||||
|
||||
obj1 = new Object ();
|
||||
obj1.food = "cake";
|
||||
obj1.desert = "pie";
|
||||
|
||||
obj2 = obj1.clone();
|
||||
obj2.food = "kittens";
|
||||
|
||||
result = foo.length == 13 && foo.indexOf("bar") == 4 && foo.substring(8, 13) == "stuff" &&
|
||||
parsed == 42 && Integer.valueOf(aChar) == 65 && obj1.food == "cake" &&
|
||||
obj2.desert == "pie" && parsedHex == 255 && parsedOct == 9 && parsedBig == 4294967296;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Test reported by sterowang, Variable attribute defines conflict with function.
|
||||
/*
|
||||
What steps will reproduce the problem?
|
||||
1. function a (){};
|
||||
2. b = {};
|
||||
3. b.a = {};
|
||||
4. a();
|
||||
|
||||
What is the expected output? What do you see instead?
|
||||
Function "a" should be called. But the error message "Error Expecting 'a'
|
||||
to be a function at (line: 1, col: 1)" received.
|
||||
|
||||
What version of the product are you using? On what operating system?
|
||||
Version 1.6 is used on Cent OS 5.4
|
||||
|
||||
|
||||
Please provide any additional information below.
|
||||
When using dump() to show symbols, found the function "a" is reassigned to
|
||||
"{}" by "b.a = {};" call.
|
||||
*/
|
||||
|
||||
function a () {};
|
||||
b = {};
|
||||
b.a = {};
|
||||
a ();
|
||||
|
||||
result = 1;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/* Javascript eval */
|
||||
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript eval is not JSON.parse
|
||||
// use parentheses or JSON.parse instead
|
||||
// myfoo = eval("{ foo: 42 }");
|
||||
myfoo = eval ("(" +
|
||||
"{ foo: 42 }" +
|
||||
")");
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
result = eval ("4*10+2") == 42 && myfoo.foo == 42;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
/* Javascript eval */
|
||||
|
||||
myfoo = eval ("{ foo: 42 }");
|
||||
|
||||
result = eval ("4*10+2") == 42 && myfoo.foo == 42;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/* Javascript eval */
|
||||
|
||||
mystructure = {
|
||||
a: 39,
|
||||
b: 3,
|
||||
addStuff: function (c, d) { return c + d; }
|
||||
};
|
||||
|
||||
mystring = JSON.stringify(mystructure, undefined);
|
||||
|
||||
// 42-tiny-js change begin --->
|
||||
// in JavaScript eval is not JSON.parse
|
||||
// use parentheses or JSON.parse instead
|
||||
// mynewstructure = eval(mystring);
|
||||
mynewstructure = eval ("(" + mystring + ")");
|
||||
mynewstructure2 = JSON.parse(mystring);
|
||||
//<--- 42-tiny-js change end
|
||||
|
||||
result = mynewstructure.addStuff(mynewstructure.a, mynewstructure.b) == 42 &&
|
||||
mynewstructure2.addStuff(mynewstructure2.a, mynewstructure2.b) == 42;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/* Javascript eval */
|
||||
|
||||
mystructure = {
|
||||
a: 39,
|
||||
b: 3,
|
||||
addStuff: function (c, d) { return c + d; }
|
||||
};
|
||||
|
||||
mystring = JSON.stringify(mystructure, undefined);
|
||||
|
||||
mynewstructure = eval (mystring);
|
||||
|
||||
result = mynewstructure.addStuff(mynewstructure.a, mynewstructure.b);
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// mikael.kindborg@mobilesorcery.com - Function symbol is evaluated in bracket-less body of false
|
||||
// if-statement
|
||||
var foo; // a var is only created automated by assignment
|
||||
|
||||
if (foo !== undefined)
|
||||
foo ();
|
||||
|
||||
result = 1;
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/* Mandelbrot! */
|
||||
|
||||
X1 = -2.0;
|
||||
Y1 = -2.0;
|
||||
X2 = 2.0;
|
||||
Y2 = 2.0;
|
||||
PX = 32;
|
||||
PY = 32;
|
||||
|
||||
lines = [];
|
||||
for (y = 0; y < PY; y++)
|
||||
{
|
||||
line = "";
|
||||
for (x = 0; x < PX; x++)
|
||||
{
|
||||
Xr = 0;
|
||||
Xi = 0;
|
||||
Cr = X1 + ((X2 - X1) * x / PX);
|
||||
Ci = Y1 + ((Y2 - Y1) * y / PY);
|
||||
iterations = 0;
|
||||
while ((iterations < 32) && ((Xr * Xr + Xi * Xi) < 4))
|
||||
{
|
||||
t = Xr * Xr - Xi * Xi + Cr;
|
||||
Xi = 2 * Xr * Xi + Ci;
|
||||
Xr = t;
|
||||
iterations++;
|
||||
}
|
||||
if (iterations & 1)
|
||||
line += "*";
|
||||
else
|
||||
line += " ";
|
||||
}
|
||||
lines[y] = line;
|
||||
}
|
||||
|
||||
result = lines[0] == "********************************" &&
|
||||
lines[1] == "*********** **********" &&
|
||||
lines[2] == "********* ********" &&
|
||||
lines[3] == "******* ******" &&
|
||||
lines[4] == "****** *****" &&
|
||||
lines[5] == "***** ****" &&
|
||||
lines[6] == "**** ******* ***" &&
|
||||
lines[7] == "*** ******* ** ** **" &&
|
||||
lines[8] == "*** ****** * * * **" &&
|
||||
lines[9] == "** ******* ** ** ** *" &&
|
||||
lines[10] == "** ****** * * ** ** *" &&
|
||||
lines[11] == "* ***** *** ** ** " &&
|
||||
lines[12] == "****** *** ***** " &&
|
||||
lines[13] == "*** * * * ** ** " &&
|
||||
lines[14] == "* * * * * ** " &&
|
||||
lines[15] == "* *** ** ** " &&
|
||||
lines[16] == "* ** ** " &&
|
||||
lines[17] == "* *** ** ** " &&
|
||||
lines[18] == "* * * * * ** " &&
|
||||
lines[19] == "*** * * * ** ** " &&
|
||||
lines[20] == "****** *** ***** " &&
|
||||
lines[21] == "* ***** *** ** ** " &&
|
||||
lines[22] == "** ****** * * ** ** *" &&
|
||||
lines[23] == "** ******* ** ** ** *" &&
|
||||
lines[24] == "*** ****** * * * **" &&
|
||||
lines[25] == "*** ******* ** ** **" &&
|
||||
lines[26] == "**** ******* ***" &&
|
||||
lines[27] == "***** ****" &&
|
||||
lines[28] == "****** *****" &&
|
||||
lines[29] == "******* ******" &&
|
||||
lines[30] == "********* ********" &&
|
||||
lines[31] == "*********** **********";
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// Array length test
|
||||
|
||||
myArray = [1, 2, 3, 4, 5];
|
||||
myArray2 = [1, 2, 3, 4, 5];
|
||||
myArray2[8] = 42;
|
||||
|
||||
result = myArray.length == 5 && myArray2.length == 9;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// check for undefined-ness
|
||||
a = undefined;
|
||||
b = "foo";
|
||||
result = a == undefined && b != undefined;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// test for postincrement working as expected
|
||||
var foo = 5;
|
||||
result = (foo++) == 5;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// test for array contains
|
||||
var a = [1, 2, 4, 5, 7];
|
||||
var b = ["bread", "cheese", "sandwich"];
|
||||
|
||||
result = a.contains(1) && !a.contains(42) && b.contains("cheese") && !b.contains("eggs");
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// test for array remove
|
||||
var a = [1, 2, 4, 5, 7];
|
||||
|
||||
a.remove(2);
|
||||
a.remove(5);
|
||||
|
||||
result = a.length == 3 && a[0] == 1 && a[1] == 4 && a[2] == 7;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// test for array join
|
||||
var a = [1, 2, 4, 5, 7];
|
||||
|
||||
result = a.join(",") == "1,2,4,5,7";
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// test for string split
|
||||
var b = "1,4,7";
|
||||
var a = b.split(",");
|
||||
|
||||
result = a.length == 3 && a[0] == 1 && a[1] == 4 && a[2] == 7;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
function Foo () { this.__proto__ = Foo.prototype; }
|
||||
Foo.prototype = {
|
||||
value: function () { return this.x + this.y; }
|
||||
};
|
||||
|
||||
var a = {__proto__: Foo.prototype, x: 1, y: 2};
|
||||
var b = new Foo ();
|
||||
b.x = 2;
|
||||
b.y = 3;
|
||||
|
||||
var result1 = a.value();
|
||||
var result2 = b.value();
|
||||
result = result1 == 3 && result2 == 5;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
var Foo = {value: function () { return this.x + this.y; }};
|
||||
|
||||
var a = {prototype: Foo, x: 1, y: 2};
|
||||
var b = new Foo ();
|
||||
b.x = 2;
|
||||
b.y = 3;
|
||||
|
||||
var result1 = a.value();
|
||||
var result2 = b.value();
|
||||
result = result1 == 3 && result2 == 5;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// test for shift
|
||||
var a = (2 << 2);
|
||||
var b = (16 >> 3);
|
||||
var c = (-1 >>> 16);
|
||||
result = a == 8 && b == 2 && c == 0xFFFF;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// test for ternary
|
||||
|
||||
result = (true ? 3 : 4) == 3 && (false ? 5 : 6) == 6;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
function Person (name)
|
||||
{
|
||||
this.name = name;
|
||||
this.kill = function () { this.name += " is dead"; };
|
||||
}
|
||||
|
||||
var a = new Person ("Kenny");
|
||||
a.kill();
|
||||
result = a.name == "Kenny is dead";
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// the 'lf' in the printf caused issues writing doubles on some compilers
|
||||
var a = 5.0 / 10.0 * 100.0;
|
||||
var b = 5.0 * 110.0;
|
||||
var c = 50.0 / 10.0;
|
||||
a.dump();
|
||||
b.dump();
|
||||
c.dump();
|
||||
result = a == 50 && b == 550 && c == 5;
|
||||
Reference in New Issue
Block a user