Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,3 @@
.vscode/
build/
build.*/
@@ -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.
@@ -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)
@@ -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).
@@ -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;
}
File diff suppressed because it is too large Load Diff
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,12 @@
#pragma once
#include "TinyJS.h"
//------------------------------------------------------------------------
namespace TJS {
/// Register useful math. functions with the TinyJS interpreter
void registerMathFunctions (CTinyJS* tinyJS);
//------------------------------------------------------------------------
} // TJS
@@ -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;
}
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -0,0 +1,2 @@
// simply testing we can return the correct value
result = 1;
@@ -0,0 +1,3 @@
// comparison
var a = 42;
result = a == 42;
@@ -0,0 +1,6 @@
// simple for loop
var a = 0;
var i;
for (i = 1; i < 10; i++)
a = a + i;
result = a == 45;
@@ -0,0 +1,4 @@
// simple if
var a = 42;
if (a < 43)
result = 1;
@@ -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;
@@ -0,0 +1,3 @@
// simple function
function add (x, y) { return x + y; }
result = add (3, 6) == 9;
@@ -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;
@@ -0,0 +1,5 @@
// functions in variables
var bob = {};
bob.add = function (x, y) { return x + y; };
result = bob.add(3, 6) == 9;
@@ -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;
@@ -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;
@@ -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;
@@ -0,0 +1,6 @@
// if .. else
var a = 42;
if (a != 42)
result = 0;
else
result = 1;
@@ -0,0 +1,10 @@
// if .. else with blocks
var a = 42;
if (a != 42)
{
result = 0;
}
else
{
result = 1;
}
@@ -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;
@@ -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;
@@ -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
@@ -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;
@@ -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;
@@ -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
@@ -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;
@@ -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;
@@ -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;
@@ -0,0 +1,5 @@
/* Javascript eval */
myfoo = eval ("{ foo: 42 }");
result = eval ("4*10+2") == 42 && myfoo.foo == 42;
@@ -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;
@@ -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);
@@ -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;
@@ -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] == "*********** **********";
@@ -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;
@@ -0,0 +1,4 @@
// check for undefined-ness
a = undefined;
b = "foo";
result = a == undefined && b != undefined;
@@ -0,0 +1,3 @@
// test for postincrement working as expected
var foo = 5;
result = (foo++) == 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");
@@ -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;
@@ -0,0 +1,4 @@
// test for array join
var a = [1, 2, 4, 5, 7];
result = a.join(",") == "1,2,4,5,7";
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -0,0 +1,3 @@
// test for ternary
result = (true ? 3 : 4) == 3 && (false ? 5 : 6) == 6;
@@ -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";
@@ -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;