[906] | 1 | /*
|
---|
| 2 | * TinyJS
|
---|
| 3 | *
|
---|
| 4 | * A single-file Javascript-alike engine
|
---|
| 5 | *
|
---|
| 6 | * Authored By Gordon Williams <gw@pur3.co.uk>
|
---|
| 7 | *
|
---|
| 8 | * Copyright (C) 2009 Pur3 Ltd
|
---|
| 9 | *
|
---|
| 10 |
|
---|
| 11 | * 42TinyJS
|
---|
| 12 | *
|
---|
| 13 | * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine
|
---|
| 14 | *
|
---|
| 15 | * Authored / Changed By Armin Diedering <armin@diedering.de>
|
---|
| 16 | *
|
---|
| 17 | * Copyright (C) 2010-2013 ardisoft
|
---|
| 18 | *
|
---|
| 19 | *
|
---|
| 20 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
|
---|
| 21 | * this software and associated documentation files (the "Software"), to deal in
|
---|
| 22 | * the Software without restriction, including without limitation the rights to
|
---|
| 23 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
---|
| 24 | * of the Software, and to permit persons to whom the Software is furnished to do
|
---|
| 25 | * so, subject to the following conditions:
|
---|
| 26 |
|
---|
| 27 | * The above copyright notice and this permission notice shall be included in all
|
---|
| 28 | * copies or substantial portions of the Software.
|
---|
| 29 |
|
---|
| 30 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
---|
| 31 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
---|
| 32 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
---|
| 33 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
---|
| 34 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
---|
| 35 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
---|
| 36 | * SOFTWARE.
|
---|
| 37 | */
|
---|
| 38 |
|
---|
| 39 | #ifdef _DEBUG
|
---|
| 40 | # ifndef _MSC_VER
|
---|
| 41 | # define DEBUG_MEMORY 1
|
---|
| 42 | # endif
|
---|
| 43 | #endif
|
---|
| 44 | #include <sstream>
|
---|
| 45 |
|
---|
| 46 | #include "TinyJS.h"
|
---|
| 47 |
|
---|
| 48 | #ifndef ASSERT
|
---|
| 49 | # define ASSERT(X) assert(X)
|
---|
| 50 | #endif
|
---|
| 51 |
|
---|
| 52 | #ifndef NO_REGEXP
|
---|
| 53 | # if defined HAVE_TR1_REGEX
|
---|
| 54 | # include <tr1/regex>
|
---|
| 55 | using namespace std::tr1;
|
---|
| 56 | # elif defined HAVE_BOOST_REGEX
|
---|
| 57 | # include <boost/regex.hpp>
|
---|
| 58 | using namespace boost;
|
---|
| 59 | # else
|
---|
| 60 | # include <regex>
|
---|
| 61 | # endif
|
---|
| 62 | #endif
|
---|
| 63 | #include <stdio.h> /* printf, scanf, NULL */
|
---|
| 64 | #include <stdlib.h>
|
---|
| 65 | #include <cstring>
|
---|
| 66 | #include <algorithm>
|
---|
| 67 | #include <cmath>
|
---|
| 68 | #include <memory>
|
---|
| 69 | #include <iterator>
|
---|
| 70 | #include "TinyJS_Functions.h"
|
---|
| 71 |
|
---|
| 72 | using namespace std;
|
---|
| 73 |
|
---|
| 74 | // -----------------------------------------------------------------------------------
|
---|
| 75 | //////////////////////////////////////////////////////////////////////////
|
---|
| 76 | /// Memory Debug
|
---|
| 77 | //////////////////////////////////////////////////////////////////////////
|
---|
| 78 |
|
---|
| 79 | //#define DEBUG_MEMORY 1
|
---|
| 80 |
|
---|
| 81 | #if DEBUG_MEMORY
|
---|
| 82 |
|
---|
| 83 | vector<CScriptVar*> allocatedVars;
|
---|
| 84 | vector<CScriptVarLink*> allocatedLinks;
|
---|
| 85 |
|
---|
| 86 | void mark_allocated(CScriptVar *v) {
|
---|
| 87 | allocatedVars.push_back(v);
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | void mark_deallocated(CScriptVar *v) {
|
---|
| 91 | for (size_t i=0;i<allocatedVars.size();i++) {
|
---|
| 92 | if (allocatedVars[i] == v) {
|
---|
| 93 | allocatedVars.erase(allocatedVars.begin()+i);
|
---|
| 94 | break;
|
---|
| 95 | }
|
---|
| 96 | }
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | void mark_allocated(CScriptVarLink *v) {
|
---|
| 100 | allocatedLinks.push_back(v);
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | void mark_deallocated(CScriptVarLink *v) {
|
---|
| 104 | for (size_t i=0;i<allocatedLinks.size();i++) {
|
---|
| 105 | if (allocatedLinks[i] == v) {
|
---|
| 106 | allocatedLinks.erase(allocatedLinks.begin()+i);
|
---|
| 107 | break;
|
---|
| 108 | }
|
---|
| 109 | }
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 | void show_allocated() {
|
---|
| 113 | for (size_t i=0;i<allocatedVars.size();i++) {
|
---|
| 114 | printf("ALLOCATED, %d refs\n", allocatedVars[i]->getRefs());
|
---|
| 115 | allocatedVars[i]->trace(" ");
|
---|
| 116 | }
|
---|
| 117 | for (size_t i=0;i<allocatedLinks.size();i++) {
|
---|
| 118 | printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->getName().c_str(), allocatedLinks[i]->getVarPtr()->getRefs());
|
---|
| 119 | allocatedLinks[i]->getVarPtr()->trace(" ");
|
---|
| 120 | }
|
---|
| 121 | allocatedVars.clear();
|
---|
| 122 | allocatedLinks.clear();
|
---|
| 123 | }
|
---|
| 124 | #endif
|
---|
| 125 |
|
---|
| 126 |
|
---|
| 127 | //////////////////////////////////////////////////////////////////////////
|
---|
| 128 | /// Utils
|
---|
| 129 | //////////////////////////////////////////////////////////////////////////
|
---|
| 130 |
|
---|
| 131 | inline bool isWhitespace(char ch) {
|
---|
| 132 | return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r');
|
---|
| 133 | }
|
---|
| 134 |
|
---|
| 135 | inline bool isNumeric(char ch) {
|
---|
| 136 | return (ch>='0') && (ch<='9');
|
---|
| 137 | }
|
---|
| 138 | uint32_t isArrayIndex(const string &str) {
|
---|
| 139 | if(str.size()==0 || !isNumeric(str[0]) || (str.size()>1 && str[0]=='0') ) return -1; // empty or (more as 1 digit and beginning with '0')
|
---|
| 140 | CNumber idx;
|
---|
| 141 | const char *endptr;
|
---|
| 142 | idx.parseInt(str.c_str(), 10, &endptr);
|
---|
| 143 | if(*endptr || idx>uint32_t(0xFFFFFFFFUL)) return -1;
|
---|
| 144 | return idx.toUInt32();
|
---|
| 145 | }
|
---|
| 146 | inline bool isHexadecimal(char ch) {
|
---|
| 147 | return ((ch>='0') && (ch<='9')) || ((ch>='a') && (ch<='f')) || ((ch>='A') && (ch<='F'));
|
---|
| 148 | }
|
---|
| 149 | inline bool isOctal(char ch) {
|
---|
| 150 | return ((ch>='0') && (ch<='7'));
|
---|
| 151 | }
|
---|
| 152 | inline bool isAlpha(char ch) {
|
---|
| 153 | return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_' || ch=='$';
|
---|
| 154 | }
|
---|
| 155 |
|
---|
| 156 | bool isIDString(const char *s) {
|
---|
| 157 | if (!isAlpha(*s))
|
---|
| 158 | return false;
|
---|
| 159 | while (*s) {
|
---|
| 160 | if (!(isAlpha(*s) || isNumeric(*s)))
|
---|
| 161 | return false;
|
---|
| 162 | s++;
|
---|
| 163 | }
|
---|
| 164 | return true;
|
---|
| 165 | }
|
---|
| 166 |
|
---|
| 167 | void replace(string &str, char textFrom, const char *textTo) {
|
---|
| 168 | int sLen = strlen(textTo);
|
---|
| 169 | size_t p = str.find(textFrom);
|
---|
| 170 | while (p != string::npos) {
|
---|
| 171 | str = str.substr(0, p) + textTo + str.substr(p+1);
|
---|
| 172 | p = str.find(textFrom, p+sLen);
|
---|
| 173 | }
|
---|
| 174 | }
|
---|
| 175 | string int2string(int32_t intData) {
|
---|
| 176 | ostringstream str;
|
---|
| 177 | str << intData;
|
---|
| 178 | return str.str();
|
---|
| 179 | }
|
---|
| 180 | string int2string(uint32_t intData) {
|
---|
| 181 | ostringstream str;
|
---|
| 182 | str << intData;
|
---|
| 183 | return str.str();
|
---|
| 184 | }
|
---|
| 185 | string float2string(const double &floatData) {
|
---|
| 186 | ostringstream str;
|
---|
| 187 | str.unsetf(ios::floatfield);
|
---|
| 188 | #if (defined(_MSC_VER) && _MSC_VER >= 1600) || __cplusplus >= 201103L
|
---|
| 189 | str.precision(numeric_limits<double>::max_digits10);
|
---|
| 190 | #else
|
---|
| 191 | str.precision(numeric_limits<double>::digits10+2);
|
---|
| 192 | #endif
|
---|
| 193 | str << floatData;
|
---|
| 194 | return str.str();
|
---|
| 195 | }
|
---|
| 196 | /// convert the given string into a quoted string suitable for javascript
|
---|
| 197 | string getJSString(const string &str) {
|
---|
| 198 | char buffer[5] = "\\x00";
|
---|
| 199 | string nStr; nStr.reserve(str.length());
|
---|
| 200 | nStr.push_back('\"');
|
---|
| 201 | for (string::const_iterator i=str.begin();i!=str.end();i++) {
|
---|
| 202 | const char *replaceWith = 0;
|
---|
| 203 | switch (*i) {
|
---|
| 204 | case '\\': replaceWith = "\\\\"; break;
|
---|
| 205 | case '\n': replaceWith = "\\n"; break;
|
---|
| 206 | case '\r': replaceWith = "\\r"; break;
|
---|
| 207 | case '\a': replaceWith = "\\a"; break;
|
---|
| 208 | case '\b': replaceWith = "\\b"; break;
|
---|
| 209 | case '\f': replaceWith = "\\f"; break;
|
---|
| 210 | case '\t': replaceWith = "\\t"; break;
|
---|
| 211 | case '\v': replaceWith = "\\v"; break;
|
---|
| 212 | case '"': replaceWith = "\\\""; break;
|
---|
| 213 | default: {
|
---|
| 214 | int nCh = ((unsigned char)*i) & 0xff;
|
---|
| 215 | if(nCh<32 || nCh>127) {
|
---|
| 216 | static char hex[] = "0123456789ABCDEF";
|
---|
| 217 | buffer[2] = hex[(nCh>>4)&0x0f];
|
---|
| 218 | buffer[3] = hex[nCh&0x0f];
|
---|
| 219 | replaceWith = buffer;
|
---|
| 220 | };
|
---|
| 221 | }
|
---|
| 222 | }
|
---|
| 223 | if (replaceWith)
|
---|
| 224 | nStr.append(replaceWith);
|
---|
| 225 | else
|
---|
| 226 | nStr.push_back(*i);
|
---|
| 227 | }
|
---|
| 228 | nStr.push_back('\"');
|
---|
| 229 | return nStr;
|
---|
| 230 | }
|
---|
| 231 |
|
---|
| 232 | static inline string getIDString(const string& str) {
|
---|
| 233 | if(isIDString(str.c_str()) && CScriptToken::isReservedWord(str)==LEX_ID)
|
---|
| 234 | return str;
|
---|
| 235 | return getJSString(str);
|
---|
| 236 | }
|
---|
| 237 |
|
---|
| 238 |
|
---|
| 239 | //////////////////////////////////////////////////////////////////////////
|
---|
| 240 | /// CScriptException
|
---|
| 241 | //////////////////////////////////////////////////////////////////////////
|
---|
| 242 |
|
---|
| 243 | string CScriptException::toString() {
|
---|
| 244 | ostringstream msg;
|
---|
| 245 | msg << ERROR_NAME[errorType] << ": " << message;
|
---|
| 246 | if(lineNumber >= 0) msg << " at Line:" << lineNumber+1;
|
---|
| 247 | if(column >=0) msg << " Column:" << column+1;
|
---|
| 248 | if(fileName.length()) msg << " in " << fileName;
|
---|
| 249 | return msg.str();
|
---|
| 250 | }
|
---|
| 251 |
|
---|
| 252 |
|
---|
| 253 | //////////////////////////////////////////////////////////////////////////
|
---|
| 254 | /// CScriptLex
|
---|
| 255 | //////////////////////////////////////////////////////////////////////////
|
---|
| 256 |
|
---|
| 257 | CScriptLex::CScriptLex(const char *Code, const string &File, int Line, int Column) : data(Code) {
|
---|
| 258 | currentFile = File;
|
---|
| 259 | pos.currentLineStart = pos.tokenStart = data;
|
---|
| 260 | pos.currentLine = Line;
|
---|
| 261 | reset(pos);
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | void CScriptLex::reset(const POS &toPos) { ///< Reset this lex so we can start again
|
---|
| 265 | dataPos = toPos.tokenStart;
|
---|
| 266 | tk = last_tk = 0;
|
---|
| 267 | tkStr = "";
|
---|
| 268 | pos = toPos;
|
---|
| 269 | lineBreakBeforeToken = false;
|
---|
| 270 | currCh = nextCh = 0;
|
---|
| 271 | getNextCh(); // currCh
|
---|
| 272 | getNextCh(); // nextCh
|
---|
| 273 | getNextToken();
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | void CScriptLex::check(int expected_tk, int alternate_tk/*=-1*/) {
|
---|
| 277 | if (expected_tk==';' && tk==LEX_EOF) return; // ignore last missing ';'
|
---|
| 278 | if (tk!=expected_tk && tk!=alternate_tk) {
|
---|
| 279 | ostringstream errorString;
|
---|
| 280 | if(expected_tk == LEX_EOF)
|
---|
| 281 | errorString << "Got unexpected " << CScriptToken::getTokenStr(tk);
|
---|
| 282 | else
|
---|
| 283 | errorString << "Got '" << CScriptToken::getTokenStr(tk) << "' expected '" << CScriptToken::getTokenStr(expected_tk) << "'";
|
---|
| 284 | if(alternate_tk!=-1) errorString << " or '" << CScriptToken::getTokenStr(alternate_tk) << "'";
|
---|
| 285 | throw new CScriptException(SyntaxError, errorString.str(), currentFile, pos.currentLine, currentColumn());
|
---|
| 286 | }
|
---|
| 287 | }
|
---|
| 288 | void CScriptLex::match(int expected_tk1, int alternate_tk/*=-1*/) {
|
---|
| 289 | check(expected_tk1, alternate_tk);
|
---|
| 290 | int line = pos.currentLine;
|
---|
| 291 | getNextToken();
|
---|
| 292 | lineBreakBeforeToken = line != pos.currentLine;
|
---|
| 293 | }
|
---|
| 294 |
|
---|
| 295 | void CScriptLex::getNextCh() {
|
---|
| 296 | if(currCh == '\n') { // Windows or Linux
|
---|
| 297 | pos.currentLine++;
|
---|
| 298 | pos.tokenStart = pos.currentLineStart = dataPos - (nextCh == LEX_EOF ? 0 : 1);
|
---|
| 299 | }
|
---|
| 300 | currCh = nextCh;
|
---|
| 301 | if ( (nextCh = *dataPos) != LEX_EOF ) dataPos++; // stay on EOF
|
---|
| 302 | if(currCh == '\r') { // Windows or Mac
|
---|
| 303 | if(nextCh == '\n')
|
---|
| 304 | getNextCh(); // Windows '\r\n\' --> skip '\r'
|
---|
| 305 | else
|
---|
| 306 | currCh = '\n'; // Mac (only '\r') --> convert '\r' to '\n'
|
---|
| 307 | }
|
---|
| 308 | }
|
---|
| 309 |
|
---|
| 310 | static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, ']', ')', '.', LEX_EOF};
|
---|
| 311 | void CScriptLex::getNextToken() {
|
---|
| 312 | while (currCh && isWhitespace(currCh)) getNextCh();
|
---|
| 313 | // newline comments
|
---|
| 314 | if (currCh=='/' && nextCh=='/') {
|
---|
| 315 | while (currCh && currCh!='\n') getNextCh();
|
---|
| 316 | getNextCh();
|
---|
| 317 | getNextToken();
|
---|
| 318 | return;
|
---|
| 319 | }
|
---|
| 320 | // block comments
|
---|
| 321 | if (currCh=='/' && nextCh=='*') {
|
---|
| 322 | while (currCh && (currCh!='*' || nextCh!='/')) getNextCh();
|
---|
| 323 | getNextCh();
|
---|
| 324 | getNextCh();
|
---|
| 325 | getNextToken();
|
---|
| 326 | return;
|
---|
| 327 | }
|
---|
| 328 | last_tk = tk;
|
---|
| 329 | tk = LEX_EOF;
|
---|
| 330 | tkStr.clear();
|
---|
| 331 | // record beginning of this token
|
---|
| 332 | pos.tokenStart = dataPos - (nextCh == LEX_EOF ? (currCh == LEX_EOF ? 0 : 1) : 2);
|
---|
| 333 | // tokens
|
---|
| 334 | if (isAlpha(currCh)) { // IDs
|
---|
| 335 | while (isAlpha(currCh) || isNumeric(currCh)) {
|
---|
| 336 | tkStr += currCh;
|
---|
| 337 | getNextCh();
|
---|
| 338 | }
|
---|
| 339 | tk = CScriptToken::isReservedWord(tkStr);
|
---|
| 340 | } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers
|
---|
| 341 | if(currCh=='.') tkStr+='0';
|
---|
| 342 | bool isHex = false, isOct=false;
|
---|
| 343 | if (currCh=='0') {
|
---|
| 344 | tkStr += currCh; getNextCh();
|
---|
| 345 | if(isOctal(currCh)) isOct = true;
|
---|
| 346 | }
|
---|
| 347 | if (currCh=='x' || currCh=='X') {
|
---|
| 348 | isHex = true;
|
---|
| 349 | tkStr += currCh; getNextCh();
|
---|
| 350 | }
|
---|
| 351 | tk = LEX_INT;
|
---|
| 352 | while (isOctal(currCh) || (!isOct && isNumeric(currCh)) || (isHex && isHexadecimal(currCh))) {
|
---|
| 353 | tkStr += currCh;
|
---|
| 354 | getNextCh();
|
---|
| 355 | }
|
---|
| 356 | if (!isHex && !isOct && currCh=='.') {
|
---|
| 357 | tk = LEX_FLOAT;
|
---|
| 358 | tkStr += '.';
|
---|
| 359 | getNextCh();
|
---|
| 360 | while (isNumeric(currCh)) {
|
---|
| 361 | tkStr += currCh;
|
---|
| 362 | getNextCh();
|
---|
| 363 | }
|
---|
| 364 | }
|
---|
| 365 | // do fancy e-style floating point
|
---|
| 366 | if (!isHex && !isOct && (currCh=='e' || currCh=='E')) {
|
---|
| 367 | tk = LEX_FLOAT;
|
---|
| 368 | tkStr += currCh; getNextCh();
|
---|
| 369 | if (currCh=='-') { tkStr += currCh; getNextCh(); }
|
---|
| 370 | while (isNumeric(currCh)) {
|
---|
| 371 | tkStr += currCh; getNextCh();
|
---|
| 372 | }
|
---|
| 373 | }
|
---|
| 374 | } else if (currCh=='"' || currCh=='\'') { // strings...
|
---|
| 375 | char endCh = currCh;
|
---|
| 376 | getNextCh();
|
---|
| 377 | while (currCh && currCh!=endCh && currCh!='\n') {
|
---|
| 378 | if (currCh == '\\') {
|
---|
| 379 | getNextCh();
|
---|
| 380 | switch (currCh) {
|
---|
| 381 | case '\n' : break; // ignore newline after '\'
|
---|
| 382 | case 'n': tkStr += '\n'; break;
|
---|
| 383 | case 'r': tkStr += '\r'; break;
|
---|
| 384 | case 'a': tkStr += '\a'; break;
|
---|
| 385 | case 'b': tkStr += '\b'; break;
|
---|
| 386 | case 'f': tkStr += '\f'; break;
|
---|
| 387 | case 't': tkStr += '\t'; break;
|
---|
| 388 | case 'v': tkStr += '\v'; break;
|
---|
| 389 | case 'x': { // hex digits
|
---|
| 390 | getNextCh();
|
---|
| 391 | if(isHexadecimal(currCh)) {
|
---|
| 392 | char buf[3]="\0\0";
|
---|
| 393 | buf[0] = currCh;
|
---|
| 394 | for(int i=0; i<2 && isHexadecimal(nextCh); i++) {
|
---|
| 395 | getNextCh(); buf[i] = currCh;
|
---|
| 396 | }
|
---|
| 397 | tkStr += (char)strtol(buf, 0, 16);
|
---|
| 398 | } else
|
---|
| 399 | throw new CScriptException(SyntaxError, "malformed hexadezimal character escape sequence", currentFile, pos.currentLine, currentColumn());
|
---|
| 400 | }
|
---|
| 401 | default: {
|
---|
| 402 | if(isOctal(currCh)) {
|
---|
| 403 | char buf[4]="\0\0\0";
|
---|
| 404 | buf[0] = currCh;
|
---|
| 405 | for(int i=1; i<3 && isOctal(nextCh); i++) {
|
---|
| 406 | getNextCh(); buf[i] = currCh;
|
---|
| 407 | }
|
---|
| 408 | tkStr += (char)strtol(buf, 0, 8);
|
---|
| 409 | }
|
---|
| 410 | else tkStr += currCh;
|
---|
| 411 | }
|
---|
| 412 | }
|
---|
| 413 | } else {
|
---|
| 414 | tkStr += currCh;
|
---|
| 415 | }
|
---|
| 416 | getNextCh();
|
---|
| 417 | }
|
---|
| 418 | if(currCh != endCh)
|
---|
| 419 | throw new CScriptException(SyntaxError, "unterminated string literal", currentFile, pos.currentLine, currentColumn());
|
---|
| 420 | getNextCh();
|
---|
| 421 | tk = LEX_STR;
|
---|
| 422 | } else {
|
---|
| 423 | // single chars
|
---|
| 424 | tk = currCh;
|
---|
| 425 | if (currCh) getNextCh();
|
---|
| 426 | if (tk=='=' && currCh=='=') { // ==
|
---|
| 427 | tk = LEX_EQUAL;
|
---|
| 428 | getNextCh();
|
---|
| 429 | if (currCh=='=') { // ===
|
---|
| 430 | tk = LEX_TYPEEQUAL;
|
---|
| 431 | getNextCh();
|
---|
| 432 | }
|
---|
| 433 | } else if (tk=='!' && currCh=='=') { // !=
|
---|
| 434 | tk = LEX_NEQUAL;
|
---|
| 435 | getNextCh();
|
---|
| 436 | if (currCh=='=') { // !==
|
---|
| 437 | tk = LEX_NTYPEEQUAL;
|
---|
| 438 | getNextCh();
|
---|
| 439 | }
|
---|
| 440 | } else if (tk=='<') {
|
---|
| 441 | if (currCh=='=') { // <=
|
---|
| 442 | tk = LEX_LEQUAL;
|
---|
| 443 | getNextCh();
|
---|
| 444 | } else if (currCh=='<') { // <<
|
---|
| 445 | tk = LEX_LSHIFT;
|
---|
| 446 | getNextCh();
|
---|
| 447 | if (currCh=='=') { // <<=
|
---|
| 448 | tk = LEX_LSHIFTEQUAL;
|
---|
| 449 | getNextCh();
|
---|
| 450 | }
|
---|
| 451 | }
|
---|
| 452 | } else if (tk=='>') {
|
---|
| 453 | if (currCh=='=') { // >=
|
---|
| 454 | tk = LEX_GEQUAL;
|
---|
| 455 | getNextCh();
|
---|
| 456 | } else if (currCh=='>') { // >>
|
---|
| 457 | tk = LEX_RSHIFT;
|
---|
| 458 | getNextCh();
|
---|
| 459 | if (currCh=='=') { // >>=
|
---|
| 460 | tk = LEX_RSHIFTEQUAL;
|
---|
| 461 | getNextCh();
|
---|
| 462 | } else if (currCh=='>') { // >>>
|
---|
| 463 | tk = LEX_RSHIFTU;
|
---|
| 464 | getNextCh();
|
---|
| 465 | if (currCh=='=') { // >>>=
|
---|
| 466 | tk = LEX_RSHIFTUEQUAL;
|
---|
| 467 | getNextCh();
|
---|
| 468 | }
|
---|
| 469 | }
|
---|
| 470 | }
|
---|
| 471 | } else if (tk=='+') {
|
---|
| 472 | if (currCh=='=') { // +=
|
---|
| 473 | tk = LEX_PLUSEQUAL;
|
---|
| 474 | getNextCh();
|
---|
| 475 | } else if (currCh=='+') { // ++
|
---|
| 476 | tk = LEX_PLUSPLUS;
|
---|
| 477 | getNextCh();
|
---|
| 478 | }
|
---|
| 479 | } else if (tk=='-') {
|
---|
| 480 | if (currCh=='=') { // -=
|
---|
| 481 | tk = LEX_MINUSEQUAL;
|
---|
| 482 | getNextCh();
|
---|
| 483 | } else if (currCh=='-') { // --
|
---|
| 484 | tk = LEX_MINUSMINUS;
|
---|
| 485 | getNextCh();
|
---|
| 486 | }
|
---|
| 487 | } else if (tk=='&') {
|
---|
| 488 | if (currCh=='=') { // &=
|
---|
| 489 | tk = LEX_ANDEQUAL;
|
---|
| 490 | getNextCh();
|
---|
| 491 | } else if (currCh=='&') { // &&
|
---|
| 492 | tk = LEX_ANDAND;
|
---|
| 493 | getNextCh();
|
---|
| 494 | }
|
---|
| 495 | } else if (tk=='|') {
|
---|
| 496 | if (currCh=='=') { // |=
|
---|
| 497 | tk = LEX_OREQUAL;
|
---|
| 498 | getNextCh();
|
---|
| 499 | } else if (currCh=='|') { // ||
|
---|
| 500 | tk = LEX_OROR;
|
---|
| 501 | getNextCh();
|
---|
| 502 | }
|
---|
| 503 | } else if (tk=='^' && currCh=='=') {
|
---|
| 504 | tk = LEX_XOREQUAL;
|
---|
| 505 | getNextCh();
|
---|
| 506 | } else if (tk=='*' && currCh=='=') {
|
---|
| 507 | tk = LEX_ASTERISKEQUAL;
|
---|
| 508 | getNextCh();
|
---|
| 509 | } else if (tk=='/') {
|
---|
| 510 | // check if it's a RegExp-Literal
|
---|
| 511 | tk = LEX_REGEXP;
|
---|
| 512 | for(uint16_t *p = not_allowed_tokens_befor_regexp; *p; p++) {
|
---|
| 513 | if(*p==last_tk) { tk = '/'; break; }
|
---|
| 514 | }
|
---|
| 515 | if(tk == LEX_REGEXP) {
|
---|
| 516 | #ifdef NO_REGEXP
|
---|
| 517 | throw new CScriptException(Error, "42TinyJS was built without support for regular expressions", currentFile, pos.currentLine, currentColumn());
|
---|
| 518 | #endif
|
---|
| 519 | tkStr = "/";
|
---|
| 520 | while (currCh && currCh!='/' && currCh!='\n') {
|
---|
| 521 | if (currCh == '\\' && nextCh == '/') {
|
---|
| 522 | tkStr.append(1, currCh);
|
---|
| 523 | getNextCh();
|
---|
| 524 | }
|
---|
| 525 | tkStr.append(1, currCh);
|
---|
| 526 | getNextCh();
|
---|
| 527 | }
|
---|
| 528 | if(currCh == '/') {
|
---|
| 529 | #ifndef NO_REGEXP
|
---|
| 530 | try { regex(tkStr.substr(1), regex_constants::ECMAScript); } catch(regex_error e) {
|
---|
| 531 | throw new CScriptException(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code()), currentFile, pos.currentLine, currentColumn());
|
---|
| 532 | }
|
---|
| 533 | #endif /* NO_REGEXP */
|
---|
| 534 | do {
|
---|
| 535 | tkStr.append(1, currCh);
|
---|
| 536 | getNextCh();
|
---|
| 537 | } while (currCh=='g' || currCh=='i' || currCh=='m' || currCh=='y');
|
---|
| 538 | } else
|
---|
| 539 | throw new CScriptException(SyntaxError, "unterminated regular expression literal", currentFile, pos.currentLine, currentColumn());
|
---|
| 540 | } else if(currCh=='=') {
|
---|
| 541 | tk = LEX_SLASHEQUAL;
|
---|
| 542 | getNextCh();
|
---|
| 543 | }
|
---|
| 544 | } else if (tk=='%' && currCh=='=') {
|
---|
| 545 | tk = LEX_PERCENTEQUAL;
|
---|
| 546 | getNextCh();
|
---|
| 547 | }
|
---|
| 548 | }
|
---|
| 549 | /* This isn't quite right yet */
|
---|
| 550 | }
|
---|
| 551 |
|
---|
| 552 |
|
---|
| 553 | //////////////////////////////////////////////////////////////////////////
|
---|
| 554 | // CScriptTokenDataForwards
|
---|
| 555 | //////////////////////////////////////////////////////////////////////////
|
---|
| 556 |
|
---|
| 557 | bool CScriptTokenDataForwards::compare_fnc_token_by_name::operator()(const CScriptToken& lhs, const CScriptToken& rhs) const {
|
---|
| 558 | return lhs.Fnc().name < rhs.Fnc().name;
|
---|
| 559 | }
|
---|
| 560 | bool CScriptTokenDataForwards::checkRedefinition(const string &Str, bool checkVarsInLetScope) {
|
---|
| 561 | STRING_SET_it it = varNames[LETS].find(Str);
|
---|
| 562 | if(it!=varNames[LETS].end()) return false;
|
---|
| 563 | else if(checkVarsInLetScope) {
|
---|
| 564 | STRING_SET_it it = vars_in_letscope.find(Str);
|
---|
| 565 | if(it!=vars_in_letscope.end()) return false;
|
---|
| 566 | }
|
---|
| 567 | return true;
|
---|
| 568 | }
|
---|
| 569 |
|
---|
| 570 | void CScriptTokenDataForwards::addVars( STRING_VECTOR_t &Vars ) {
|
---|
| 571 | varNames[VARS].insert(Vars.begin(), Vars.end());
|
---|
| 572 | }
|
---|
| 573 | void CScriptTokenDataForwards::addConsts( STRING_VECTOR_t &Vars ) {
|
---|
| 574 | varNames[CONSTS].insert(Vars.begin(), Vars.end());
|
---|
| 575 | }
|
---|
| 576 | std::string CScriptTokenDataForwards::addVarsInLetscope( STRING_VECTOR_t &Vars )
|
---|
| 577 | {
|
---|
| 578 | for(STRING_VECTOR_it it=Vars.begin(); it!=Vars.end(); ++it) {
|
---|
| 579 | if(!checkRedefinition(*it, false)) return *it;
|
---|
| 580 | vars_in_letscope.insert(*it);
|
---|
| 581 | }
|
---|
| 582 | return "";
|
---|
| 583 | }
|
---|
| 584 |
|
---|
| 585 | std::string CScriptTokenDataForwards::addLets( STRING_VECTOR_t &Lets )
|
---|
| 586 | {
|
---|
| 587 | for(STRING_VECTOR_it it=Lets.begin(); it!=Lets.end(); ++it) {
|
---|
| 588 | if(!checkRedefinition(*it, true)) return *it;
|
---|
| 589 | varNames[LETS].insert(*it);
|
---|
| 590 | }
|
---|
| 591 | return "";
|
---|
| 592 | }
|
---|
| 593 |
|
---|
| 594 |
|
---|
| 595 | //////////////////////////////////////////////////////////////////////////
|
---|
| 596 | // CScriptTokenDataLoop
|
---|
| 597 | //////////////////////////////////////////////////////////////////////////
|
---|
| 598 |
|
---|
| 599 | std::string CScriptTokenDataLoop::getParsableString(const string &IndentString/*=""*/, const string &Indent/*=""*/ ) {
|
---|
| 600 | static const char *heads[] = {"for each(", "for(", "for(", "for(", "while(", "do "};
|
---|
| 601 | static const char *ops[] = {" in ", " in ", " of ", "; "};
|
---|
| 602 | string out = heads[type];
|
---|
| 603 | if(init.size() && type==FOR)out.append(CScriptToken::getParsableString(init));
|
---|
| 604 | if(type<=WHILE) out.append(CScriptToken::getParsableString(condition.begin(), condition.end()-(type>=FOR ? 0 : 7)));
|
---|
| 605 | if(type<=FOR) out.append(ops[type]);
|
---|
| 606 | if(iter.size()) out.append(CScriptToken::getParsableString(iter));
|
---|
| 607 | out.append(")");
|
---|
| 608 | out.append(CScriptToken::getParsableString(body));
|
---|
| 609 | if(type==DO)out.append(" while(").append(CScriptToken::getParsableString(condition)).append(");");
|
---|
| 610 | return out;
|
---|
| 611 | }
|
---|
| 612 |
|
---|
| 613 |
|
---|
| 614 | //////////////////////////////////////////////////////////////////////////
|
---|
| 615 | // CScriptTokenDataTry
|
---|
| 616 | //////////////////////////////////////////////////////////////////////////
|
---|
| 617 |
|
---|
| 618 | std::string CScriptTokenDataTry::getParsableString( const string &IndentString/*=""*/, const string &Indent/*=""*/ ) {
|
---|
| 619 | string out = "try ";
|
---|
| 620 | string nl = Indent.size() ? "\n"+IndentString : " ";
|
---|
| 621 |
|
---|
| 622 | out.append(CScriptToken::getParsableString(tryBlock, IndentString, Indent));
|
---|
| 623 | for(CScriptTokenDataTry::CatchBlock_it catchBlock = catchBlocks.begin(); catchBlock!=catchBlocks.end(); catchBlock++) {
|
---|
| 624 | out.append(nl).append("catch(").append(catchBlock->indentifiers->getParsableString());
|
---|
| 625 | if(catchBlock->condition.size()>1) {
|
---|
| 626 | out.append(" if ").append(CScriptToken::getParsableString(catchBlock->condition.begin()+1, catchBlock->condition.end()));
|
---|
| 627 | }
|
---|
| 628 | out.append(") ").append(CScriptToken::getParsableString(catchBlock->block, IndentString, Indent));
|
---|
| 629 | }
|
---|
| 630 | if(finallyBlock.size())
|
---|
| 631 | out.append(nl).append("finally ").append(CScriptToken::getParsableString(finallyBlock, IndentString, Indent));
|
---|
| 632 | return out;
|
---|
| 633 | }
|
---|
| 634 |
|
---|
| 635 |
|
---|
| 636 | //////////////////////////////////////////////////////////////////////////
|
---|
| 637 | // CScriptTokenDataFnc
|
---|
| 638 | //////////////////////////////////////////////////////////////////////////
|
---|
| 639 |
|
---|
| 640 | string CScriptTokenDataFnc::getArgumentsString()
|
---|
| 641 | {
|
---|
| 642 | ostringstream destination;
|
---|
| 643 | destination << "(";
|
---|
| 644 | if(arguments.size()) {
|
---|
| 645 | const char *comma = "";
|
---|
| 646 | for(TOKEN_VECT_it argument=arguments.begin(); argument!=arguments.end(); ++argument, comma=", ") {
|
---|
| 647 | if(argument->token == LEX_ID)
|
---|
| 648 | destination << comma << argument->String();
|
---|
| 649 | else {
|
---|
| 650 | vector<bool> isObject(1, false);
|
---|
| 651 | for(DESTRUCTURING_VARS_it it=argument->DestructuringVar().vars.begin(); it!=argument->DestructuringVar().vars.end(); ++it) {
|
---|
| 652 | if(it->second == "}" || it->second == "]") {
|
---|
| 653 | destination << it->second;
|
---|
| 654 | isObject.pop_back();
|
---|
| 655 | } else {
|
---|
| 656 | destination << comma;
|
---|
| 657 | if(it->second == "[" || it->second == "{") {
|
---|
| 658 | comma = "";
|
---|
| 659 | if(isObject.back() && it->first.length())
|
---|
| 660 | destination << getIDString(it->first) << ":";
|
---|
| 661 | destination << it->second;
|
---|
| 662 | isObject.push_back(it->second == "{");
|
---|
| 663 | } else {
|
---|
| 664 | comma = ", ";
|
---|
| 665 | if(it->second.empty())
|
---|
| 666 | continue; // skip empty entries
|
---|
| 667 | if(isObject.back() && it->first!=it->second)
|
---|
| 668 | destination << getIDString(it->first) << ":";
|
---|
| 669 | destination << it->second;
|
---|
| 670 | }
|
---|
| 671 | }
|
---|
| 672 | }
|
---|
| 673 | }
|
---|
| 674 | }
|
---|
| 675 | }
|
---|
| 676 | destination << ") ";
|
---|
| 677 | return destination.str();
|
---|
| 678 | }
|
---|
| 679 |
|
---|
| 680 |
|
---|
| 681 | //////////////////////////////////////////////////////////////////////////
|
---|
| 682 | // CScriptTokenDataDestructuringVar
|
---|
| 683 | //////////////////////////////////////////////////////////////////////////
|
---|
| 684 |
|
---|
| 685 | void CScriptTokenDataDestructuringVar::getVarNames(STRING_VECTOR_t Names) {
|
---|
| 686 | for(DESTRUCTURING_VARS_it it = vars.begin(); it != vars.end(); ++it) {
|
---|
| 687 | if(it->second.size() && it->second.find_first_of("{[]}") == string::npos)
|
---|
| 688 | Names.push_back(it->second);
|
---|
| 689 | }
|
---|
| 690 | }
|
---|
| 691 |
|
---|
| 692 | std::string CScriptTokenDataDestructuringVar::getParsableString()
|
---|
| 693 | {
|
---|
| 694 | string out;
|
---|
| 695 | const char *comma = "";
|
---|
| 696 | vector<bool> isObject(1, false);
|
---|
| 697 | for(DESTRUCTURING_VARS_it it=vars.begin(); it!=vars.end(); ++it) {
|
---|
| 698 | if(it->second == "}" || it->second == "]") {
|
---|
| 699 | out.append(it->second);
|
---|
| 700 | isObject.pop_back();
|
---|
| 701 | } else {
|
---|
| 702 | out.append(comma);
|
---|
| 703 | if(it->second == "[" || it->second == "{") {
|
---|
| 704 | comma = "";
|
---|
| 705 | if(isObject.back() && it->first.length())
|
---|
| 706 | out.append(getIDString(it->first)).append(":");
|
---|
| 707 | out.append(it->second);
|
---|
| 708 | isObject.push_back(it->second == "{");
|
---|
| 709 | } else {
|
---|
| 710 | comma = ", ";
|
---|
| 711 | if(it->second.empty())
|
---|
| 712 | continue; // skip empty entries
|
---|
| 713 | if(isObject.back() && it->first!=it->second)
|
---|
| 714 | out.append(getIDString(it->first)).append(":");
|
---|
| 715 | out.append(it->second);
|
---|
| 716 | }
|
---|
| 717 | }
|
---|
| 718 | }
|
---|
| 719 | return out;
|
---|
| 720 | }
|
---|
| 721 |
|
---|
| 722 |
|
---|
| 723 | //////////////////////////////////////////////////////////////////////////
|
---|
| 724 | // CScriptTokenDataObjectLiteral
|
---|
| 725 | //////////////////////////////////////////////////////////////////////////
|
---|
| 726 |
|
---|
| 727 | void CScriptTokenDataObjectLiteral::setMode(bool Destructuring) {
|
---|
| 728 | structuring = !(destructuring = Destructuring);
|
---|
| 729 | for(vector<ELEMENT>::iterator it=elements.begin(); it!=elements.end(); ++it) {
|
---|
| 730 | if(it->value.size() && it->value.front().token == LEX_T_OBJECT_LITERAL) {
|
---|
| 731 | CScriptTokenDataObjectLiteral& e = it->value.front().Object();
|
---|
| 732 | if(e.destructuring && e.structuring)
|
---|
| 733 | e.setMode(Destructuring);
|
---|
| 734 | }
|
---|
| 735 | }
|
---|
| 736 | }
|
---|
| 737 |
|
---|
| 738 | string CScriptTokenDataObjectLiteral::getParsableString()
|
---|
| 739 | {
|
---|
| 740 | string out = type == OBJECT ? "{ " : "[ ";
|
---|
| 741 | const char *comma = "";
|
---|
| 742 | for(vector<ELEMENT>::iterator it=elements.begin(); it!=elements.end(); ++it) {
|
---|
| 743 | out.append(comma); comma=", ";
|
---|
| 744 | if(it->value.empty()) continue;
|
---|
| 745 | if(type == OBJECT)
|
---|
| 746 | out.append(getIDString(it->id)).append(" : ");
|
---|
| 747 | out.append(CScriptToken::getParsableString(it->value));
|
---|
| 748 | }
|
---|
| 749 | out.append(type == OBJECT ? " }" : " ]");
|
---|
| 750 | return out;
|
---|
| 751 | }
|
---|
| 752 |
|
---|
| 753 |
|
---|
| 754 | //////////////////////////////////////////////////////////////////////////
|
---|
| 755 | // CScriptToken
|
---|
| 756 | //////////////////////////////////////////////////////////////////////////
|
---|
| 757 |
|
---|
| 758 | typedef struct { int id; const char *str; bool need_space; } token2str_t;
|
---|
| 759 | static token2str_t reserved_words_begin[] ={
|
---|
| 760 | // reserved words
|
---|
| 761 | { LEX_R_IF, "if", true },
|
---|
| 762 | { LEX_R_ELSE, "else", true },
|
---|
| 763 | { LEX_R_DO, "do", true },
|
---|
| 764 | { LEX_R_WHILE, "while", true },
|
---|
| 765 | { LEX_R_FOR, "for", true },
|
---|
| 766 | { LEX_R_IN, "in", true },
|
---|
| 767 | { LEX_R_BREAK, "break", true },
|
---|
| 768 | { LEX_R_CONTINUE, "continue", true },
|
---|
| 769 | { LEX_R_FUNCTION, "function", true },
|
---|
| 770 | { LEX_R_RETURN, "return", true },
|
---|
| 771 | { LEX_R_VAR, "var", true },
|
---|
| 772 | { LEX_R_LET, "let", true },
|
---|
| 773 | { LEX_R_CONST, "const", true },
|
---|
| 774 | { LEX_R_WITH, "with", true },
|
---|
| 775 | { LEX_R_TRUE, "true", true },
|
---|
| 776 | { LEX_R_FALSE, "false", true },
|
---|
| 777 | { LEX_R_NULL, "null", true },
|
---|
| 778 | { LEX_R_NEW, "new", true },
|
---|
| 779 | { LEX_R_TRY, "try", true },
|
---|
| 780 | { LEX_R_CATCH, "catch", true },
|
---|
| 781 | { LEX_R_FINALLY, "finally", true },
|
---|
| 782 | { LEX_R_THROW, "throw", true },
|
---|
| 783 | { LEX_R_TYPEOF, "typeof", true },
|
---|
| 784 | { LEX_R_VOID, "void", true },
|
---|
| 785 | { LEX_R_DELETE, "delete", true },
|
---|
| 786 | { LEX_R_INSTANCEOF, "instanceof", true },
|
---|
| 787 | { LEX_R_SWITCH, "switch", true },
|
---|
| 788 | { LEX_R_CASE, "case", true },
|
---|
| 789 | { LEX_R_DEFAULT, "default", true },
|
---|
| 790 | };
|
---|
| 791 | #define ARRAY_LENGTH(array) (sizeof(array)/sizeof(array[0]))
|
---|
| 792 | #define ARRAY_END(array) (&array[ARRAY_LENGTH(array)])
|
---|
| 793 | static token2str_t *reserved_words_end = ARRAY_END(reserved_words_begin);//&reserved_words_begin[ARRAY_LENGTH(reserved_words_begin)];
|
---|
| 794 | static token2str_t *str2reserved_begin[sizeof(reserved_words_begin)/sizeof(reserved_words_begin[0])];
|
---|
| 795 | static token2str_t **str2reserved_end = &str2reserved_begin[sizeof(str2reserved_begin)/sizeof(str2reserved_begin[0])];
|
---|
| 796 | static token2str_t tokens2str_begin[] = {
|
---|
| 797 | { LEX_EOF, "EOF", false },
|
---|
| 798 | { LEX_ID, "ID", true },
|
---|
| 799 | { LEX_INT, "INT", true },
|
---|
| 800 | { LEX_FLOAT, "FLOAT", true },
|
---|
| 801 | { LEX_STR, "STRING", true },
|
---|
| 802 | { LEX_REGEXP, "REGEXP", true },
|
---|
| 803 | { LEX_EQUAL, "==", false },
|
---|
| 804 | { LEX_TYPEEQUAL, "===", false },
|
---|
| 805 | { LEX_NEQUAL, "!=", false },
|
---|
| 806 | { LEX_NTYPEEQUAL, "!==", false },
|
---|
| 807 | { LEX_LEQUAL, "<=", false },
|
---|
| 808 | { LEX_LSHIFT, "<<", false },
|
---|
| 809 | { LEX_LSHIFTEQUAL, "<<=", false },
|
---|
| 810 | { LEX_GEQUAL, ">=", false },
|
---|
| 811 | { LEX_RSHIFT, ">>", false },
|
---|
| 812 | { LEX_RSHIFTEQUAL, ">>=", false },
|
---|
| 813 | { LEX_RSHIFTU, ">>>", false },
|
---|
| 814 | { LEX_RSHIFTUEQUAL, ">>>=", false },
|
---|
| 815 | { LEX_PLUSEQUAL, "+=", false },
|
---|
| 816 | { LEX_MINUSEQUAL, "-=", false },
|
---|
| 817 | { LEX_PLUSPLUS, "++", false },
|
---|
| 818 | { LEX_MINUSMINUS, "--", false },
|
---|
| 819 | { LEX_ANDEQUAL, "&=", false },
|
---|
| 820 | { LEX_ANDAND, "&&", false },
|
---|
| 821 | { LEX_OREQUAL, "|=", false },
|
---|
| 822 | { LEX_OROR, "||", false },
|
---|
| 823 | { LEX_XOREQUAL, "^=", false },
|
---|
| 824 | { LEX_ASTERISKEQUAL, "*=", false },
|
---|
| 825 | { LEX_SLASHEQUAL, "/=", false },
|
---|
| 826 | { LEX_PERCENTEQUAL, "%=", false },
|
---|
| 827 | // special tokens
|
---|
| 828 | { LEX_T_OF, "of", true },
|
---|
| 829 | { LEX_T_FUNCTION_OPERATOR, "function", true },
|
---|
| 830 | { LEX_T_GET, "get", true },
|
---|
| 831 | { LEX_T_SET, "set", true },
|
---|
| 832 | { LEX_T_EXCEPTION_VAR, "LEX_T_EXCEPTION_VAR", false },
|
---|
| 833 | { LEX_T_SKIP, "LEX_SKIP", false },
|
---|
| 834 | { LEX_T_DUMMY_LABEL, "LABEL", true },
|
---|
| 835 | { LEX_T_LABEL, "LABEL", true },
|
---|
| 836 | { LEX_T_LOOP, "LEX_LOOP", true },
|
---|
| 837 | { LEX_T_FOR_IN, "LEX_FOR_IN", true },
|
---|
| 838 | { LEX_T_FORWARD, "LEX_T_FORWARD", false },
|
---|
| 839 | { LEX_T_OBJECT_LITERAL, "LEX_OBJECT_LITERAL", false },
|
---|
| 840 | { LEX_T_DESTRUCTURING_VAR, "Destructuring Var", false },
|
---|
| 841 | };
|
---|
| 842 | static token2str_t *tokens2str_end = &tokens2str_begin[sizeof(tokens2str_begin)/sizeof(tokens2str_begin[0])];
|
---|
| 843 | struct token2str_cmp_t {
|
---|
| 844 | bool operator()(const token2str_t &lhs, const token2str_t &rhs) {
|
---|
| 845 | return lhs.id < rhs.id;
|
---|
| 846 | }
|
---|
| 847 | bool operator()(const token2str_t &lhs, int rhs) {
|
---|
| 848 | return lhs.id < rhs;
|
---|
| 849 | }
|
---|
| 850 | bool operator()(const token2str_t *lhs, const token2str_t *rhs) {
|
---|
| 851 | return strcmp(lhs->str, rhs->str)<0;
|
---|
| 852 | }
|
---|
| 853 | bool operator()(const token2str_t *lhs, const char *rhs) {
|
---|
| 854 | return strcmp(lhs->str, rhs)<0;
|
---|
| 855 | }
|
---|
| 856 | };
|
---|
| 857 | static bool tokens2str_sort() {
|
---|
| 858 | // printf("tokens2str_sort called\n");
|
---|
| 859 | sort(tokens2str_begin, tokens2str_end, token2str_cmp_t());
|
---|
| 860 | sort(reserved_words_begin, reserved_words_end, token2str_cmp_t());
|
---|
| 861 | for(unsigned int i=0; i<ARRAY_LENGTH(str2reserved_begin); i++)
|
---|
| 862 | str2reserved_begin[i] = &reserved_words_begin[i];
|
---|
| 863 | sort(str2reserved_begin, str2reserved_end, token2str_cmp_t());
|
---|
| 864 | return true;
|
---|
| 865 | }
|
---|
| 866 | static bool tokens2str_sorted = tokens2str_sort();
|
---|
| 867 |
|
---|
| 868 | CScriptToken::CScriptToken(CScriptLex *l, int Match, int Alternate) : line(l->currentLine()), column(l->currentColumn()), token(l->tk), intData(0)
|
---|
| 869 | {
|
---|
| 870 | if(token == LEX_INT || LEX_TOKEN_DATA_FLOAT(token)) {
|
---|
| 871 | CNumber number(l->tkStr);
|
---|
| 872 | if(number.isInfinity())
|
---|
| 873 | token=LEX_ID, (tokenData=new CScriptTokenDataString("Infinity"))->ref();
|
---|
| 874 | else if(number.isInt32())
|
---|
| 875 | token=LEX_INT, intData=number.toInt32();
|
---|
| 876 | else
|
---|
| 877 | token=LEX_FLOAT, floatData=new double(number.toDouble());
|
---|
| 878 | } else if(LEX_TOKEN_DATA_STRING(token))
|
---|
| 879 | (tokenData = new CScriptTokenDataString(l->tkStr))->ref();
|
---|
| 880 | else if(LEX_TOKEN_DATA_FUNCTION(token))
|
---|
| 881 | (tokenData = new CScriptTokenDataFnc)->ref();
|
---|
| 882 | else if (LEX_TOKEN_DATA_LOOP(token))
|
---|
| 883 | (tokenData = new CScriptTokenDataLoop)->ref();
|
---|
| 884 | else if (LEX_TOKEN_DATA_TRY(token))
|
---|
| 885 | (tokenData = new CScriptTokenDataTry)->ref();
|
---|
| 886 | if(Match>=0)
|
---|
| 887 | l->match(Match, Alternate);
|
---|
| 888 | else
|
---|
| 889 | l->match(l->tk);
|
---|
| 890 | #ifdef _DEBUG
|
---|
| 891 | token_str = getTokenStr(token);
|
---|
| 892 | #endif
|
---|
| 893 | }
|
---|
| 894 | CScriptToken::CScriptToken(uint16_t Tk, int IntData) : line(0), column(0), token(Tk), intData(0) {
|
---|
| 895 | if (LEX_TOKEN_DATA_SIMPLE(token))
|
---|
| 896 | intData = IntData;
|
---|
| 897 | else if (LEX_TOKEN_DATA_FUNCTION(token))
|
---|
| 898 | (tokenData = new CScriptTokenDataFnc)->ref();
|
---|
| 899 | else if (LEX_TOKEN_DATA_DESTRUCTURING_VAR(token))
|
---|
| 900 | (tokenData = new CScriptTokenDataDestructuringVar)->ref();
|
---|
| 901 | else if (LEX_TOKEN_DATA_OBJECT_LITERAL(token))
|
---|
| 902 | (tokenData = new CScriptTokenDataObjectLiteral)->ref();
|
---|
| 903 | else if (LEX_TOKEN_DATA_LOOP(token))
|
---|
| 904 | (tokenData = new CScriptTokenDataLoop)->ref();
|
---|
| 905 | else if (LEX_TOKEN_DATA_TRY(token))
|
---|
| 906 | (tokenData = new CScriptTokenDataTry)->ref();
|
---|
| 907 | else if (LEX_TOKEN_DATA_FORWARDER(token))
|
---|
| 908 | (tokenData = new CScriptTokenDataForwards)->ref();
|
---|
| 909 | else
|
---|
| 910 | ASSERT(0);
|
---|
| 911 | #ifdef _DEBUG
|
---|
| 912 | token_str = getTokenStr(token);
|
---|
| 913 | #endif
|
---|
| 914 | }
|
---|
| 915 |
|
---|
| 916 | CScriptToken::CScriptToken(uint16_t Tk, const string &TkStr) : line(0), column(0), token(Tk), intData(0) {
|
---|
| 917 | ASSERT(LEX_TOKEN_DATA_STRING(token));
|
---|
| 918 | (tokenData = new CScriptTokenDataString(TkStr))->ref();
|
---|
| 919 | #ifdef _DEBUG
|
---|
| 920 | token_str = getTokenStr(token);
|
---|
| 921 | #endif
|
---|
| 922 | }
|
---|
| 923 |
|
---|
| 924 | CScriptToken &CScriptToken::operator =(const CScriptToken &Copy)
|
---|
| 925 | {
|
---|
| 926 | if(this == &Copy) return *this;
|
---|
| 927 | clear();
|
---|
| 928 | #ifdef _DEBUG
|
---|
| 929 | token_str = Copy.token_str;
|
---|
| 930 | #endif
|
---|
| 931 | line = Copy.line;
|
---|
| 932 | column = Copy.column;
|
---|
| 933 | token = Copy.token;
|
---|
| 934 | if(LEX_TOKEN_DATA_FLOAT(token))
|
---|
| 935 | floatData = new double(*Copy.floatData);
|
---|
| 936 | else if(!LEX_TOKEN_DATA_SIMPLE(token))
|
---|
| 937 | (tokenData = Copy.tokenData)->ref();
|
---|
| 938 | else
|
---|
| 939 | intData = Copy.intData;
|
---|
| 940 | return *this;
|
---|
| 941 | }
|
---|
| 942 | string CScriptToken::getParsableString(TOKEN_VECT &Tokens, const string &IndentString, const string &Indent) {
|
---|
| 943 | return getParsableString(Tokens.begin(), Tokens.end(), IndentString, Indent);
|
---|
| 944 | }
|
---|
| 945 | string CScriptToken::getParsableString(TOKEN_VECT_it Begin, TOKEN_VECT_it End, const string &IndentString, const string &Indent) {
|
---|
| 946 | ostringstream destination;
|
---|
| 947 | string nl = Indent.size() ? "\n" : " ";
|
---|
| 948 | string my_indentString = IndentString;
|
---|
| 949 | bool add_nl=false, block_start=false, need_space=false;
|
---|
| 950 | int skip_collon = 0;
|
---|
| 951 |
|
---|
| 952 | for(TOKEN_VECT_it it=Begin; it != End; ++it) {
|
---|
| 953 | string OutString;
|
---|
| 954 | if(add_nl) OutString.append(nl).append(my_indentString);
|
---|
| 955 | bool old_block_start = block_start;
|
---|
| 956 | bool old_need_space = need_space;
|
---|
| 957 | add_nl = block_start = need_space =false;
|
---|
| 958 | if(it->token == LEX_STR)
|
---|
| 959 | OutString.append(getJSString(it->String())), need_space=true;
|
---|
| 960 | else if(LEX_TOKEN_DATA_STRING(it->token))
|
---|
| 961 | OutString.append(it->String()), need_space=true;
|
---|
| 962 | else if(LEX_TOKEN_DATA_FLOAT(it->token))
|
---|
| 963 | OutString.append(CNumber(it->Float()).toString()), need_space=true;
|
---|
| 964 | else if(it->token == LEX_INT)
|
---|
| 965 | OutString.append(CNumber(it->Int()).toString()), need_space=true;
|
---|
| 966 | else if(LEX_TOKEN_DATA_FUNCTION(it->token)) {
|
---|
| 967 | OutString.append("function ");
|
---|
| 968 | if(it->Fnc().name.size() )
|
---|
| 969 | OutString.append(it->Fnc().name);
|
---|
| 970 | OutString.append(it->Fnc().getArgumentsString());
|
---|
| 971 | OutString.append(getParsableString(it->Fnc().body, my_indentString, Indent));
|
---|
| 972 | if(it->Fnc().body.front().token != '{') {
|
---|
| 973 | OutString.append(";");
|
---|
| 974 | }
|
---|
| 975 | } else if(LEX_TOKEN_DATA_LOOP(it->token)) {
|
---|
| 976 | OutString.append(it->Loop().getParsableString(my_indentString, Indent));
|
---|
| 977 | } else if(LEX_TOKEN_DATA_TRY(it->token)) {
|
---|
| 978 | OutString.append(it->Try().getParsableString(my_indentString, Indent));
|
---|
| 979 | } else if(LEX_TOKEN_DATA_DESTRUCTURING_VAR(it->token)) {
|
---|
| 980 | OutString.append(it->DestructuringVar().getParsableString());
|
---|
| 981 | } else if(LEX_TOKEN_DATA_OBJECT_LITERAL(it->token)) {
|
---|
| 982 | OutString.append(it->Object().getParsableString());
|
---|
| 983 | } else if(it->token == '{') {
|
---|
| 984 | OutString.append("{");
|
---|
| 985 | my_indentString.append(Indent);
|
---|
| 986 | add_nl = block_start = true;
|
---|
| 987 | } else if(it->token == '}') {
|
---|
| 988 | my_indentString.resize(my_indentString.size() - min(my_indentString.size(),Indent.size()));
|
---|
| 989 | if(old_block_start)
|
---|
| 990 | OutString = "}";
|
---|
| 991 | else
|
---|
| 992 | OutString = nl + my_indentString + "}";
|
---|
| 993 | add_nl = true;
|
---|
| 994 | } else if(it->token == LEX_T_SKIP) {
|
---|
| 995 | // ignore SKIP-Token
|
---|
| 996 | } else if(it->token == LEX_T_FORWARD) {
|
---|
| 997 | // ignore Forwarder-Token
|
---|
| 998 | } else if(it->token == LEX_R_FOR) {
|
---|
| 999 | OutString.append(CScriptToken::getTokenStr(it->token));
|
---|
| 1000 | skip_collon=2;
|
---|
| 1001 | } else {
|
---|
| 1002 | OutString.append(CScriptToken::getTokenStr(it->token,&need_space));
|
---|
| 1003 | if(it->token==';') {
|
---|
| 1004 | if(skip_collon) { --skip_collon; }
|
---|
| 1005 | else add_nl=true;
|
---|
| 1006 | }
|
---|
| 1007 | }
|
---|
| 1008 | if(need_space && old_need_space) destination << " ";
|
---|
| 1009 | destination << OutString;
|
---|
| 1010 | }
|
---|
| 1011 | return destination.str();
|
---|
| 1012 |
|
---|
| 1013 | }
|
---|
| 1014 |
|
---|
| 1015 | void CScriptToken::clear()
|
---|
| 1016 | {
|
---|
| 1017 | if(LEX_TOKEN_DATA_FLOAT(token))
|
---|
| 1018 | delete floatData;
|
---|
| 1019 | else if(!LEX_TOKEN_DATA_SIMPLE(token))
|
---|
| 1020 | tokenData->unref();
|
---|
| 1021 | token = 0;
|
---|
| 1022 | }
|
---|
| 1023 | string CScriptToken::getTokenStr( int token, bool *need_space/*=0*/ )
|
---|
| 1024 | {
|
---|
| 1025 | if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort();
|
---|
| 1026 | token2str_t *found = lower_bound(reserved_words_begin, reserved_words_end, token, token2str_cmp_t());
|
---|
| 1027 | if(found != reserved_words_end && found->id==token) {
|
---|
| 1028 | if(need_space) *need_space=found->need_space;
|
---|
| 1029 | return found->str;
|
---|
| 1030 | }
|
---|
| 1031 | found = lower_bound(tokens2str_begin, tokens2str_end, token, token2str_cmp_t());
|
---|
| 1032 | if(found != tokens2str_end && found->id==token) {
|
---|
| 1033 | if(need_space) *need_space=found->need_space;
|
---|
| 1034 | return found->str;
|
---|
| 1035 | }
|
---|
| 1036 | if(need_space) *need_space=false;
|
---|
| 1037 |
|
---|
| 1038 | if (token>32 && token<128) {
|
---|
| 1039 | char buf[2] = " ";
|
---|
| 1040 | buf[0] = (char)token;
|
---|
| 1041 | return buf;
|
---|
| 1042 | }
|
---|
| 1043 |
|
---|
| 1044 | ostringstream msg;
|
---|
| 1045 | msg << "?[" << token << "]";
|
---|
| 1046 | return msg.str();
|
---|
| 1047 | }
|
---|
| 1048 | const char *CScriptToken::isReservedWord(int Token) {
|
---|
| 1049 | if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort();
|
---|
| 1050 | token2str_t *found = lower_bound(reserved_words_begin, reserved_words_end, Token, token2str_cmp_t());
|
---|
| 1051 | if(found != reserved_words_end && found->id==Token) {
|
---|
| 1052 | return found->str;
|
---|
| 1053 | }
|
---|
| 1054 | return 0;
|
---|
| 1055 | }
|
---|
| 1056 | int CScriptToken::isReservedWord(const string &Str) {
|
---|
| 1057 | const char *str = Str.c_str();
|
---|
| 1058 | if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort();
|
---|
| 1059 | token2str_t **found = lower_bound(str2reserved_begin, str2reserved_end, str, token2str_cmp_t());
|
---|
| 1060 | if(found != str2reserved_end && strcmp((*found)->str, str)==0) {
|
---|
| 1061 | return (*found)->id;
|
---|
| 1062 | }
|
---|
| 1063 | return LEX_ID;
|
---|
| 1064 | }
|
---|
| 1065 |
|
---|
| 1066 |
|
---|
| 1067 | //////////////////////////////////////////////////////////////////////////
|
---|
| 1068 | /// CScriptTokenizer
|
---|
| 1069 | //////////////////////////////////////////////////////////////////////////
|
---|
| 1070 |
|
---|
| 1071 | CScriptTokenizer::CScriptTokenizer() : l(0), prevPos(&tokens) {
|
---|
| 1072 | }
|
---|
| 1073 | CScriptTokenizer::CScriptTokenizer(CScriptLex &Lexer) : l(0), prevPos(&tokens) {
|
---|
| 1074 | tokenizeCode(Lexer);
|
---|
| 1075 | }
|
---|
| 1076 | CScriptTokenizer::CScriptTokenizer(const char *Code, const string &File, int Line, int Column) : l(0), prevPos(&tokens) {
|
---|
| 1077 | CScriptLex lexer(Code, File, Line, Column);
|
---|
| 1078 | tokenizeCode(lexer);
|
---|
| 1079 | }
|
---|
| 1080 | void CScriptTokenizer::tokenizeCode(CScriptLex &Lexer) {
|
---|
| 1081 | try {
|
---|
| 1082 | l=&Lexer;
|
---|
| 1083 | tokens.clear();
|
---|
| 1084 | tokenScopeStack.clear();
|
---|
| 1085 | ScriptTokenState state;
|
---|
| 1086 | pushForwarder(state);
|
---|
| 1087 | if(l->tk == '§') { // special-Token at Start means the code begins not at Statement-Level
|
---|
| 1088 | l->match('§');
|
---|
| 1089 | tokenizeLiteral(state, 0);
|
---|
| 1090 | } else do {
|
---|
| 1091 | tokenizeStatement(state, 0);
|
---|
| 1092 | } while (l->tk!=LEX_EOF);
|
---|
| 1093 | pushToken(state.Tokens, LEX_EOF); // add LEX_EOF-Token
|
---|
| 1094 | removeEmptyForwarder(state);
|
---|
| 1095 | // TOKEN_VECT(tokens).swap(tokens);// tokens.shrink_to_fit();
|
---|
| 1096 | tokens.swap(state.Tokens);
|
---|
| 1097 | pushTokenScope(tokens);
|
---|
| 1098 | currentFile = l->currentFile;
|
---|
| 1099 | tk = getToken().token;
|
---|
| 1100 | } catch (...) {
|
---|
| 1101 | l=0;
|
---|
| 1102 | throw;
|
---|
| 1103 | }
|
---|
| 1104 | }
|
---|
| 1105 |
|
---|
| 1106 | void CScriptTokenizer::getNextToken() {
|
---|
| 1107 | prevPos = tokenScopeStack.back();
|
---|
| 1108 | if(getToken().token == LEX_EOF)
|
---|
| 1109 | return;
|
---|
| 1110 | ScriptTokenPosition &_TokenPos = tokenScopeStack.back();
|
---|
| 1111 | _TokenPos.pos++;
|
---|
| 1112 | if(_TokenPos.pos == _TokenPos.tokens->end())
|
---|
| 1113 | tokenScopeStack.pop_back();
|
---|
| 1114 | // ScriptTokenPosition &TokenPos = tokenScopeStack.back();
|
---|
| 1115 | tk = getToken().token;
|
---|
| 1116 | }
|
---|
| 1117 |
|
---|
| 1118 |
|
---|
| 1119 |
|
---|
| 1120 | void CScriptTokenizer::match(int ExpectedToken, int AlternateToken/*=-1*/) {
|
---|
| 1121 | if(check(ExpectedToken, AlternateToken))
|
---|
| 1122 | getNextToken();
|
---|
| 1123 | }
|
---|
| 1124 | bool CScriptTokenizer::check(int ExpectedToken, int AlternateToken/*=-1*/) {
|
---|
| 1125 | int currentToken = getToken().token;
|
---|
| 1126 | if (ExpectedToken==';' && (currentToken==LEX_EOF || currentToken=='}')) return false; // ignore last missing ';'
|
---|
| 1127 | if (currentToken!=ExpectedToken && currentToken!=AlternateToken) {
|
---|
| 1128 | ostringstream errorString;
|
---|
| 1129 | if(ExpectedToken == LEX_EOF)
|
---|
| 1130 | errorString << "Got unexpected " << CScriptToken::getTokenStr(currentToken);
|
---|
| 1131 | else {
|
---|
| 1132 | errorString << "Got '" << CScriptToken::getTokenStr(currentToken) << "' expected '" << CScriptToken::getTokenStr(ExpectedToken) << "'";
|
---|
| 1133 | if(AlternateToken!=-1) errorString << " or '" << CScriptToken::getTokenStr(AlternateToken) << "'";
|
---|
| 1134 | }
|
---|
| 1135 | throw new CScriptException(SyntaxError, errorString.str(), currentFile, currentLine(), currentColumn());
|
---|
| 1136 | }
|
---|
| 1137 | return true;
|
---|
| 1138 | }
|
---|
| 1139 | void CScriptTokenizer::pushTokenScope(TOKEN_VECT &Tokens) {
|
---|
| 1140 | tokenScopeStack.push_back(ScriptTokenPosition(&Tokens));
|
---|
| 1141 | tk = getToken().token;
|
---|
| 1142 | }
|
---|
| 1143 |
|
---|
| 1144 | void CScriptTokenizer::setPos(ScriptTokenPosition &TokenPos) {
|
---|
| 1145 | ASSERT( TokenPos.tokens == tokenScopeStack.back().tokens);
|
---|
| 1146 | tokenScopeStack.back().pos = TokenPos.pos;
|
---|
| 1147 | tk = getToken().token;
|
---|
| 1148 | }
|
---|
| 1149 | void CScriptTokenizer::skip(int Tokens) {
|
---|
| 1150 | ASSERT(tokenScopeStack.back().tokens->end()-tokenScopeStack.back().pos-Tokens>=0);
|
---|
| 1151 | tokenScopeStack.back().pos+=Tokens-1;
|
---|
| 1152 | getNextToken();
|
---|
| 1153 | }
|
---|
| 1154 |
|
---|
| 1155 | static inline void setTokenSkip(CScriptTokenizer::ScriptTokenState &State) {
|
---|
| 1156 | int tokenBeginIdx = State.Marks.back();
|
---|
| 1157 | State.Marks.pop_back();
|
---|
| 1158 | State.Tokens[tokenBeginIdx].Int() = State.Tokens.size()-tokenBeginIdx;
|
---|
| 1159 | }
|
---|
| 1160 |
|
---|
| 1161 | enum {
|
---|
| 1162 | TOKENIZE_FLAGS_canLabel = 1<<0,
|
---|
| 1163 | TOKENIZE_FLAGS_canBreak = 1<<1,
|
---|
| 1164 | TOKENIZE_FLAGS_canContinue = 1<<2,
|
---|
| 1165 | TOKENIZE_FLAGS_canReturn = 1<<3,
|
---|
| 1166 | TOKENIZE_FLAGS_asStatement = 1<<4,
|
---|
| 1167 | TOKENIZE_FLAGS_noIn = 1<<5,
|
---|
| 1168 | TOKENIZE_FLAGS_isAccessor = 1<<6,
|
---|
| 1169 | TOKENIZE_FLAGS_callForNew = 1<<7,
|
---|
| 1170 | TOKENIZE_FLAGS_noBlockStart = 1<<8,
|
---|
| 1171 | TOKENIZE_FLAGS_nestedObject = 1<<9,
|
---|
| 1172 | };
|
---|
| 1173 | void CScriptTokenizer::tokenizeTry(ScriptTokenState &State, int Flags) {
|
---|
| 1174 | l->match(LEX_R_TRY);
|
---|
| 1175 | CScriptToken TryToken(LEX_T_TRY);
|
---|
| 1176 | CScriptTokenDataTry &TryData = TryToken.Try();
|
---|
| 1177 | pushToken(State.Tokens, TryToken);
|
---|
| 1178 |
|
---|
| 1179 | TOKEN_VECT mainTokens;
|
---|
| 1180 | State.Tokens.swap(mainTokens);
|
---|
| 1181 |
|
---|
| 1182 | // try-block
|
---|
| 1183 | tokenizeBlock(State, Flags);
|
---|
| 1184 | State.Tokens.swap(TryData.tryBlock);
|
---|
| 1185 |
|
---|
| 1186 | // catch-blocks
|
---|
| 1187 | l->check(LEX_R_CATCH, LEX_R_FINALLY);
|
---|
| 1188 | bool unconditionalCatch = false;
|
---|
| 1189 | while(l->tk == LEX_R_CATCH) {
|
---|
| 1190 | if(unconditionalCatch) throw new CScriptException(SyntaxError, "catch after unconditional catch", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 1191 |
|
---|
| 1192 | // vars & condition
|
---|
| 1193 | l->match(LEX_R_CATCH);
|
---|
| 1194 | l->match('(');
|
---|
| 1195 | TryData.catchBlocks.resize(TryData.catchBlocks.size()+1);
|
---|
| 1196 | CScriptTokenDataTry::CatchBlock &catchBlock = TryData.catchBlocks.back();
|
---|
| 1197 | pushForwarder(State, true);
|
---|
| 1198 | STRING_VECTOR_t vars;
|
---|
| 1199 | catchBlock.indentifiers = tokenizeVarIdentifier(&vars).DestructuringVar();
|
---|
| 1200 | State.Forwarders.back()->addLets(vars);
|
---|
| 1201 | if(l->tk == LEX_R_IF) {
|
---|
| 1202 | l->match(LEX_R_IF);
|
---|
| 1203 | tokenizeExpression(State, Flags);
|
---|
| 1204 | } else
|
---|
| 1205 | unconditionalCatch = true;
|
---|
| 1206 | State.Tokens.swap(catchBlock.condition);
|
---|
| 1207 | l->match(')');
|
---|
| 1208 |
|
---|
| 1209 | // catch-block
|
---|
| 1210 | tokenizeBlock(State, Flags | TOKENIZE_FLAGS_noBlockStart);
|
---|
| 1211 | State.Tokens.swap(catchBlock.block);
|
---|
| 1212 | State.Forwarders.pop_back();
|
---|
| 1213 | }
|
---|
| 1214 | // finally-block
|
---|
| 1215 | if(l->tk == LEX_R_FINALLY) {
|
---|
| 1216 | l->match(LEX_R_FINALLY);
|
---|
| 1217 | tokenizeBlock(State, Flags);
|
---|
| 1218 | State.Tokens.swap(TryData.finallyBlock);
|
---|
| 1219 | }
|
---|
| 1220 | State.Tokens.swap(mainTokens);
|
---|
| 1221 | }
|
---|
| 1222 | void CScriptTokenizer::tokenizeSwitch(ScriptTokenState &State, int Flags) {
|
---|
| 1223 |
|
---|
| 1224 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
| 1225 | pushToken(State.Tokens, '(');
|
---|
| 1226 | tokenizeExpression(State, Flags);
|
---|
| 1227 | pushToken(State.Tokens, ')');
|
---|
| 1228 |
|
---|
| 1229 | State.Marks.push_back(pushToken(State.Tokens, '{')); // push Token & push blockBeginIdx
|
---|
| 1230 | pushForwarder(State);
|
---|
| 1231 |
|
---|
| 1232 |
|
---|
| 1233 | vector<int>::size_type MarksSize = State.Marks.size();
|
---|
| 1234 | Flags |= TOKENIZE_FLAGS_canBreak;
|
---|
| 1235 | for(bool hasDefault=false;;) {
|
---|
| 1236 | if( l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT) {
|
---|
| 1237 | if(l->tk == LEX_R_CASE) {
|
---|
| 1238 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push caseBeginIdx
|
---|
| 1239 | State.Marks.push_back(pushToken(State.Tokens,CScriptToken(LEX_T_SKIP))); // skipper to skip case-expression
|
---|
| 1240 | tokenizeExpression(State, Flags);
|
---|
| 1241 | setTokenSkip(State);
|
---|
| 1242 | } else { // default
|
---|
| 1243 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push caseBeginIdx
|
---|
| 1244 | if(hasDefault) throw new CScriptException(SyntaxError, "more than one switch default", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 1245 | hasDefault = true;
|
---|
| 1246 | }
|
---|
| 1247 |
|
---|
| 1248 | State.Marks.push_back(pushToken(State.Tokens, ':'));
|
---|
| 1249 | while(l->tk != '}' && l->tk != LEX_R_CASE && l->tk != LEX_R_DEFAULT && l->tk != LEX_EOF )
|
---|
| 1250 | tokenizeStatement(State, Flags);
|
---|
| 1251 | setTokenSkip(State);
|
---|
| 1252 | } else if(l->tk == '}')
|
---|
| 1253 | break;
|
---|
| 1254 | else
|
---|
| 1255 | throw new CScriptException(SyntaxError, "invalid switch statement", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 1256 | }
|
---|
| 1257 | while(MarksSize < State.Marks.size()) setTokenSkip(State);
|
---|
| 1258 | removeEmptyForwarder(State); // remove Forwarder if empty
|
---|
| 1259 | pushToken(State.Tokens, '}');
|
---|
| 1260 | setTokenSkip(State); // switch-block
|
---|
| 1261 | setTokenSkip(State); // switch-statement
|
---|
| 1262 | }
|
---|
| 1263 | void CScriptTokenizer::tokenizeWith(ScriptTokenState &State, int Flags) {
|
---|
| 1264 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
| 1265 |
|
---|
| 1266 | pushToken(State.Tokens, '(');
|
---|
| 1267 | tokenizeExpression(State, Flags);
|
---|
| 1268 | pushToken(State.Tokens, ')');
|
---|
| 1269 | tokenizeStatementNoLet(State, Flags);
|
---|
| 1270 |
|
---|
| 1271 | setTokenSkip(State);
|
---|
| 1272 | }
|
---|
| 1273 |
|
---|
| 1274 | static inline uint32_t GetLoopLabels(CScriptTokenizer::ScriptTokenState &State, CScriptTokenDataLoop &LoopData) {
|
---|
| 1275 | uint32_t label_count = 0;
|
---|
| 1276 | if(State.Tokens.size()>=2) {
|
---|
| 1277 | for(TOKEN_VECT::reverse_iterator it = State.Tokens.rbegin(); it!=State.Tokens.rend(); ++it) {
|
---|
| 1278 | if(it->token == ':' && (++it)->token == LEX_T_LABEL) {
|
---|
| 1279 | ++label_count;
|
---|
| 1280 | LoopData.labels.push_back(it->String());
|
---|
| 1281 | State.LoopLabels.push_back(it->String());
|
---|
| 1282 | it->token = LEX_T_DUMMY_LABEL;
|
---|
| 1283 | } else
|
---|
| 1284 | break;
|
---|
| 1285 | }
|
---|
| 1286 | }
|
---|
| 1287 | return label_count;
|
---|
| 1288 | }
|
---|
| 1289 | static inline void PopLoopLabels(uint32_t label_count, STRING_VECTOR_t &LoopLabels) {
|
---|
| 1290 | ASSERT(label_count <= LoopLabels.size());
|
---|
| 1291 | LoopLabels.resize(LoopLabels.size()-label_count);
|
---|
| 1292 | }
|
---|
| 1293 | void CScriptTokenizer::tokenizeWhileAndDo(ScriptTokenState &State, int Flags) {
|
---|
| 1294 |
|
---|
| 1295 | bool do_while = l->tk==LEX_R_DO;
|
---|
| 1296 |
|
---|
| 1297 | CScriptToken LoopToken(LEX_T_LOOP);
|
---|
| 1298 | CScriptTokenDataLoop &LoopData = LoopToken.Loop();
|
---|
| 1299 | LoopData.type = do_while ? CScriptTokenDataLoop::DO : CScriptTokenDataLoop::WHILE;
|
---|
| 1300 |
|
---|
| 1301 | // get loop-labels
|
---|
| 1302 | uint32_t label_count = GetLoopLabels(State, LoopData);
|
---|
| 1303 |
|
---|
| 1304 | l->match(l->tk); // match while or do
|
---|
| 1305 |
|
---|
| 1306 | pushToken(State.Tokens, LoopToken);
|
---|
| 1307 |
|
---|
| 1308 | TOKEN_VECT mainTokens;
|
---|
| 1309 | State.Tokens.swap(mainTokens);
|
---|
| 1310 |
|
---|
| 1311 | if(!do_while) {
|
---|
| 1312 | l->match('(');
|
---|
| 1313 | tokenizeExpression(State, Flags);
|
---|
| 1314 | State.Tokens.swap(LoopData.condition);
|
---|
| 1315 | l->match(')');
|
---|
| 1316 | }
|
---|
| 1317 | tokenizeStatementNoLet(State, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue);
|
---|
| 1318 | State.Tokens.swap(LoopData.body);
|
---|
| 1319 | if(do_while) {
|
---|
| 1320 | l->match(LEX_R_WHILE);
|
---|
| 1321 | l->match('(');
|
---|
| 1322 | tokenizeExpression(State, Flags);
|
---|
| 1323 | State.Tokens.swap(LoopData.condition);
|
---|
| 1324 | l->match(')');
|
---|
| 1325 | l->match(';');
|
---|
| 1326 | }
|
---|
| 1327 | State.Tokens.swap(mainTokens);
|
---|
| 1328 | PopLoopLabels(label_count, State.LoopLabels);
|
---|
| 1329 | }
|
---|
| 1330 |
|
---|
| 1331 | void CScriptTokenizer::tokenizeIf(ScriptTokenState &State, int Flags) {
|
---|
| 1332 |
|
---|
| 1333 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
| 1334 |
|
---|
| 1335 | pushToken(State.Tokens, '(');
|
---|
| 1336 | tokenizeExpression(State, Flags);
|
---|
| 1337 | pushToken(State.Tokens, ')');
|
---|
| 1338 | State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx
|
---|
| 1339 | tokenizeStatementNoLet(State, Flags);
|
---|
| 1340 |
|
---|
| 1341 | setTokenSkip(State);
|
---|
| 1342 |
|
---|
| 1343 | if(l->tk == LEX_R_ELSE) {
|
---|
| 1344 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
| 1345 | tokenizeStatementNoLet(State, Flags);
|
---|
| 1346 | setTokenSkip(State);
|
---|
| 1347 | }
|
---|
| 1348 |
|
---|
| 1349 | setTokenSkip(State);
|
---|
| 1350 | }
|
---|
| 1351 |
|
---|
| 1352 | void CScriptTokenizer::tokenizeFor(ScriptTokenState &State, int Flags) {
|
---|
| 1353 | bool for_in=false, for_of=false, for_each_in=false;
|
---|
| 1354 | CScriptToken LoopToken(LEX_T_LOOP);
|
---|
| 1355 | CScriptTokenDataLoop &LoopData = LoopToken.Loop();
|
---|
| 1356 |
|
---|
| 1357 | // get loop-labels
|
---|
| 1358 | uint32_t label_count = GetLoopLabels(State, LoopData);
|
---|
| 1359 |
|
---|
| 1360 | l->match(LEX_R_FOR);
|
---|
| 1361 | if((for_in = for_each_in = (l->tk == LEX_ID && l->tkStr == "each")))
|
---|
| 1362 | l->match(LEX_ID); // match "each"
|
---|
| 1363 |
|
---|
| 1364 | pushToken(State.Tokens, LoopToken);
|
---|
| 1365 |
|
---|
| 1366 | l->match('(');
|
---|
| 1367 | TOKEN_VECT mainTokens;
|
---|
| 1368 | State.Tokens.swap(mainTokens);
|
---|
| 1369 |
|
---|
| 1370 | bool haveLetScope = false;
|
---|
| 1371 |
|
---|
| 1372 | if(l->tk == LEX_R_VAR || l->tk == LEX_R_LET) {
|
---|
| 1373 | if(l->tk == LEX_R_VAR)
|
---|
| 1374 | tokenizeVarNoConst(State, Flags | TOKENIZE_FLAGS_noIn);
|
---|
| 1375 | else { //if(l->tk == LEX_R_LET)
|
---|
| 1376 | haveLetScope = true;
|
---|
| 1377 | pushForwarder(State, true); // no clean up empty tokenizer
|
---|
| 1378 | tokenizeLet(State, Flags | TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_asStatement);
|
---|
| 1379 | }
|
---|
| 1380 | } else if(l->tk!=';') {
|
---|
| 1381 | tokenizeExpression(State, Flags | TOKENIZE_FLAGS_noIn);
|
---|
| 1382 | }
|
---|
| 1383 | if((for_in=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))) {
|
---|
| 1384 | if(!State.LeftHand)
|
---|
| 1385 | throw new CScriptException(ReferenceError, "invalid for/in left-hand side", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 1386 | if(l->tk==LEX_ID && l->tkStr=="of") l->tk = LEX_T_OF; // fake token
|
---|
| 1387 | if((for_of = (!for_each_in && l->tk==LEX_T_OF))) {
|
---|
| 1388 | l->match(LEX_T_OF);
|
---|
| 1389 | LoopData.type = CScriptTokenDataLoop::FOR_OF;
|
---|
| 1390 | } else {
|
---|
| 1391 | l->match(LEX_R_IN);
|
---|
| 1392 | LoopData.type = for_each_in ? CScriptTokenDataLoop::FOR_EACH : CScriptTokenDataLoop::FOR_IN;
|
---|
| 1393 | }
|
---|
| 1394 | State.Tokens.swap(LoopData.condition);
|
---|
| 1395 |
|
---|
| 1396 | if(LoopData.condition.front().token == LEX_T_FORWARD) {
|
---|
| 1397 | LoopData.init.push_back(LoopData.condition.front());
|
---|
| 1398 | LoopData.condition.erase(LoopData.condition.begin());
|
---|
| 1399 | }
|
---|
| 1400 | mainTokens.back().token = LEX_T_FOR_IN;
|
---|
| 1401 | } else {
|
---|
| 1402 | l->check(';'); // no automatic ;-injection
|
---|
| 1403 | pushToken(State.Tokens, ';');
|
---|
| 1404 | State.Tokens.swap(LoopData.init);
|
---|
| 1405 | if(l->tk != ';') tokenizeExpression(State, Flags);
|
---|
| 1406 | l->check(';'); // no automatic ;-injection
|
---|
| 1407 | l->match(';'); // no automatic ;-injection
|
---|
| 1408 | State.Tokens.swap(LoopData.condition);
|
---|
| 1409 | }
|
---|
| 1410 |
|
---|
| 1411 | if(for_in || l->tk != ')') tokenizeExpression(State, Flags);
|
---|
| 1412 | l->match(')');
|
---|
| 1413 | State.Tokens.swap(LoopData.iter);
|
---|
| 1414 | Flags = TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue;
|
---|
| 1415 | if(haveLetScope) Flags |= TOKENIZE_FLAGS_noBlockStart;
|
---|
| 1416 | tokenizeStatementNoLet(State, Flags);
|
---|
| 1417 | if(haveLetScope) State.Forwarders.pop_back();
|
---|
| 1418 |
|
---|
| 1419 | State.Tokens.swap(LoopData.body);
|
---|
| 1420 | State.Tokens.swap(mainTokens);
|
---|
| 1421 | if(for_in) {
|
---|
| 1422 | LoopData.condition.push_back('=');
|
---|
| 1423 | LoopData.condition.push_back(LEX_T_EXCEPTION_VAR);
|
---|
| 1424 | LoopData.condition.push_back('.');
|
---|
| 1425 | LoopData.condition.push_back(CScriptToken(LEX_ID, "next"));
|
---|
| 1426 | LoopData.condition.push_back('(');
|
---|
| 1427 | LoopData.condition.push_back(')');
|
---|
| 1428 | LoopData.condition.push_back(';');
|
---|
| 1429 | }
|
---|
| 1430 | PopLoopLabels(label_count, State.LoopLabels);
|
---|
| 1431 | }
|
---|
| 1432 |
|
---|
| 1433 | static void tokenizeVarIdentifierDestructuring( CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t *VarNames );
|
---|
| 1434 | static void tokenizeVarIdentifierDestructuringObject(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t *VarNames) {
|
---|
| 1435 | Lexer->match('{');
|
---|
| 1436 | while(Lexer->tk != '}') {
|
---|
| 1437 | CScriptLex::POS prev_pos = Lexer->pos;
|
---|
| 1438 | string Path = Lexer->tkStr;
|
---|
| 1439 | Lexer->match(LEX_ID, LEX_STR);
|
---|
| 1440 | if(Lexer->tk == ':') {
|
---|
| 1441 | Lexer->match(':');
|
---|
| 1442 | tokenizeVarIdentifierDestructuring(Lexer, Vars, Path, VarNames);
|
---|
| 1443 | } else {
|
---|
| 1444 | Lexer->reset(prev_pos);
|
---|
| 1445 | if(VarNames) VarNames->push_back(Lexer->tkStr);
|
---|
| 1446 | Vars.push_back(DESTRUCTURING_VAR_t(Lexer->tkStr, Lexer->tkStr));
|
---|
| 1447 | Lexer->match(LEX_ID);
|
---|
| 1448 | }
|
---|
| 1449 | if (Lexer->tk!='}') Lexer->match(',', '}');
|
---|
| 1450 | }
|
---|
| 1451 | Lexer->match('}');
|
---|
| 1452 | }
|
---|
| 1453 | static void tokenizeVarIdentifierDestructuringArray(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t *VarNames) {
|
---|
| 1454 | int idx = 0;
|
---|
| 1455 | Lexer->match('[');
|
---|
| 1456 | while(Lexer->tk != ']') {
|
---|
| 1457 | if(Lexer->tk == ',')
|
---|
| 1458 | Vars.push_back(DESTRUCTURING_VAR_t("", "")); // empty
|
---|
| 1459 | else
|
---|
| 1460 | tokenizeVarIdentifierDestructuring(Lexer, Vars, int2string(idx), VarNames);
|
---|
| 1461 | ++idx;
|
---|
| 1462 | if (Lexer->tk!=']') Lexer->match(',',']');
|
---|
| 1463 | }
|
---|
| 1464 | Lexer->match(']');
|
---|
| 1465 | }
|
---|
| 1466 | static void tokenizeVarIdentifierDestructuring(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t *VarNames ) {
|
---|
| 1467 | if(Lexer->tk == '[') {
|
---|
| 1468 | Vars.push_back(DESTRUCTURING_VAR_t(Path, "[")); // marks array begin
|
---|
| 1469 | tokenizeVarIdentifierDestructuringArray(Lexer, Vars, VarNames);
|
---|
| 1470 | Vars.push_back(DESTRUCTURING_VAR_t("", "]")); // marks array end
|
---|
| 1471 | } else if(Lexer->tk == '{') {
|
---|
| 1472 | Vars.push_back(DESTRUCTURING_VAR_t(Path, "{")); // marks object begin
|
---|
| 1473 | tokenizeVarIdentifierDestructuringObject(Lexer, Vars, VarNames);
|
---|
| 1474 | Vars.push_back(DESTRUCTURING_VAR_t("", "}")); // marks object end
|
---|
| 1475 | } else {
|
---|
| 1476 | if(VarNames) VarNames->push_back(Lexer->tkStr);
|
---|
| 1477 | Vars.push_back(DESTRUCTURING_VAR_t(Path, Lexer->tkStr));
|
---|
| 1478 | Lexer->match(LEX_ID);
|
---|
| 1479 | }
|
---|
| 1480 | }
|
---|
| 1481 | CScriptToken CScriptTokenizer::tokenizeVarIdentifier( STRING_VECTOR_t *VarNames/*=0*/, bool *NeedAssignment/*=0*/ ) {
|
---|
| 1482 | CScriptToken token(LEX_T_DESTRUCTURING_VAR);
|
---|
| 1483 | if(NeedAssignment) *NeedAssignment=(l->tk == '[' || l->tk=='{');
|
---|
| 1484 | token.column = l->currentColumn();
|
---|
| 1485 | token.line = l->currentLine();
|
---|
| 1486 | tokenizeVarIdentifierDestructuring(l, token.DestructuringVar().vars, "", VarNames);
|
---|
| 1487 | return token;
|
---|
| 1488 | }
|
---|
| 1489 |
|
---|
| 1490 | void CScriptTokenizer::tokenizeFunction(ScriptTokenState &State, int Flags, bool noLetDef/*=false*/) {
|
---|
| 1491 | bool forward = false;
|
---|
| 1492 | bool Statement = (Flags & TOKENIZE_FLAGS_asStatement) != 0;
|
---|
| 1493 | bool Accessor = (Flags & TOKENIZE_FLAGS_isAccessor) != 0;
|
---|
| 1494 |
|
---|
| 1495 | int tk = l->tk;
|
---|
| 1496 | if(Accessor) {
|
---|
| 1497 | tk = State.Tokens.back().String()=="get"?LEX_T_GET:LEX_T_SET;
|
---|
| 1498 | State.Tokens.pop_back();
|
---|
| 1499 | } else {
|
---|
| 1500 | l->match(LEX_R_FUNCTION);
|
---|
| 1501 | if(!Statement) tk = LEX_T_FUNCTION_OPERATOR;
|
---|
| 1502 | }
|
---|
| 1503 | if(tk == LEX_R_FUNCTION) // only forward functions
|
---|
| 1504 | forward = !noLetDef && State.Forwarders.front() == State.Forwarders.back();
|
---|
| 1505 |
|
---|
| 1506 | CScriptToken FncToken(tk);
|
---|
| 1507 | CScriptTokenDataFnc &FncData = FncToken.Fnc();
|
---|
| 1508 |
|
---|
| 1509 | if(l->tk == LEX_ID || Accessor) {
|
---|
| 1510 | FncData.name = l->tkStr;
|
---|
| 1511 | l->match(LEX_ID, LEX_STR);
|
---|
| 1512 | } else if(Statement)
|
---|
| 1513 | throw new CScriptException(SyntaxError, "Function statement requires a name.", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 1514 | l->match('(');
|
---|
| 1515 | while(l->tk != ')') {
|
---|
| 1516 | FncData.arguments.push_back(tokenizeVarIdentifier());
|
---|
| 1517 | if (l->tk!=')') l->match(',',')');
|
---|
| 1518 | }
|
---|
| 1519 | // l->match(')');
|
---|
| 1520 | // to allow regexp at the beginning of a lambda-function fake last token
|
---|
| 1521 | l->tk = '{';
|
---|
| 1522 | l->match('{');
|
---|
| 1523 | FncData.file = l->currentFile;
|
---|
| 1524 | FncData.line = l->currentLine();
|
---|
| 1525 |
|
---|
| 1526 | ScriptTokenState functionState;
|
---|
| 1527 | if(l->tk == '{' || tk==LEX_T_GET || tk==LEX_T_SET)
|
---|
| 1528 | tokenizeBlock(functionState, TOKENIZE_FLAGS_canReturn);
|
---|
| 1529 | else {
|
---|
| 1530 | tokenizeExpression(functionState, 0);
|
---|
| 1531 | l->match(';');
|
---|
| 1532 | }
|
---|
| 1533 | functionState.Tokens.swap(FncData.body);
|
---|
| 1534 | if(forward) {
|
---|
| 1535 | State.Forwarders.front()->functions.insert(FncToken);
|
---|
| 1536 | FncToken.token = LEX_T_FUNCTION_PLACEHOLDER;
|
---|
| 1537 | }
|
---|
| 1538 | State.Tokens.push_back(FncToken);
|
---|
| 1539 | }
|
---|
| 1540 |
|
---|
| 1541 | void CScriptTokenizer::tokenizeLet(ScriptTokenState &State, int Flags, bool noLetDef/*=false*/) {
|
---|
| 1542 | bool Definition = (Flags & TOKENIZE_FLAGS_asStatement)!=0;
|
---|
| 1543 | bool noIN = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
| 1544 | bool Statement = Definition & !noIN;
|
---|
| 1545 | bool Expression = !Definition;
|
---|
| 1546 | Flags &= ~(TOKENIZE_FLAGS_asStatement);
|
---|
| 1547 | if(!Definition) noIN=false, Flags &= ~TOKENIZE_FLAGS_noIn;
|
---|
| 1548 |
|
---|
| 1549 | bool foundIN = false;
|
---|
| 1550 | bool leftHand = true;
|
---|
| 1551 | int currLine = l->currentLine(), currColumn = l->currentColumn();
|
---|
| 1552 |
|
---|
| 1553 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
| 1554 |
|
---|
| 1555 | if(l->tk == '(' || !Definition) { // no definition needs statement or expression
|
---|
| 1556 | leftHand = false;
|
---|
| 1557 | Expression = true;
|
---|
| 1558 | pushToken(State.Tokens, '(');
|
---|
| 1559 | pushForwarder(State);
|
---|
| 1560 | } else if(noLetDef)
|
---|
| 1561 | throw new CScriptException(SyntaxError, "let declaration not directly within block", l->currentFile, currLine, currColumn);
|
---|
| 1562 | STRING_VECTOR_t vars;
|
---|
| 1563 | for(;;) {
|
---|
| 1564 | bool needAssignment = false;
|
---|
| 1565 | State.Tokens.push_back(tokenizeVarIdentifier(&vars, &needAssignment));
|
---|
| 1566 | if(noIN && (foundIN=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of"))))
|
---|
| 1567 | break;
|
---|
| 1568 | if(needAssignment || l->tk=='=') {
|
---|
| 1569 | leftHand = false;
|
---|
| 1570 | pushToken(State.Tokens, '=');
|
---|
| 1571 | tokenizeAssignment(State, Flags);
|
---|
| 1572 | if(noIN && (foundIN=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of"))))
|
---|
| 1573 | break;
|
---|
| 1574 | }
|
---|
| 1575 | if(l->tk==',') {
|
---|
| 1576 | leftHand = false;
|
---|
| 1577 | pushToken(State.Tokens);
|
---|
| 1578 | }
|
---|
| 1579 | else
|
---|
| 1580 | break;
|
---|
| 1581 | }
|
---|
| 1582 | if(Expression) {
|
---|
| 1583 | string redeclared = State.Forwarders.back()->addLets(vars);
|
---|
| 1584 | if(redeclared.size())
|
---|
| 1585 | throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn);
|
---|
| 1586 | if(!foundIN) {
|
---|
| 1587 | pushToken(State.Tokens, ')');
|
---|
| 1588 | if(Statement) {
|
---|
| 1589 | if(l->tk == '{') // no extra BlockStart by expression
|
---|
| 1590 | tokenizeBlock(State, Flags|=TOKENIZE_FLAGS_noBlockStart);
|
---|
| 1591 | else
|
---|
| 1592 | tokenizeStatementNoLet(State, Flags);
|
---|
| 1593 | } else
|
---|
| 1594 | tokenizeAssignment(State, Flags);
|
---|
| 1595 | }
|
---|
| 1596 | // never remove Forwarder-token here -- popForwarder(Tokens, BlockStart, Marks);
|
---|
| 1597 | State.Forwarders.back()->vars_in_letscope.clear(); // only clear vars_in_letscope
|
---|
| 1598 | State.Marks.pop_back();
|
---|
| 1599 | } else {
|
---|
| 1600 | if(!noIN) pushToken(State.Tokens, ';');
|
---|
| 1601 |
|
---|
| 1602 | string redeclared;
|
---|
| 1603 | if(State.Forwarders.size()<=1) {
|
---|
| 1604 | // Currently it is allowed in javascript, to redeclare "let"-declared vars
|
---|
| 1605 | // in root- or function-scopes. In this case, "let" handled like "var"
|
---|
| 1606 | // To prevent redeclaration in root- or function-scopes define PREVENT_REDECLARATION_IN_FUNCTION_SCOPES
|
---|
| 1607 | #ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES
|
---|
| 1608 | redeclared = State.Forwarders.front()->addLets(vars);
|
---|
| 1609 | #else
|
---|
| 1610 | State.Forwarders.front()->addVars(vars);
|
---|
| 1611 | #endif
|
---|
| 1612 | } else
|
---|
| 1613 | redeclared = State.Forwarders.back()->addLets(vars);
|
---|
| 1614 | if(redeclared.size())
|
---|
| 1615 | throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn);
|
---|
| 1616 | }
|
---|
| 1617 | setTokenSkip(State);
|
---|
| 1618 | if(leftHand) State.LeftHand = true;
|
---|
| 1619 | }
|
---|
| 1620 |
|
---|
| 1621 | void CScriptTokenizer::tokenizeVarNoConst( ScriptTokenState &State, int Flags) {
|
---|
| 1622 | l->check(LEX_R_VAR);
|
---|
| 1623 | tokenizeVarAndConst(State, Flags);
|
---|
| 1624 | }
|
---|
| 1625 | void CScriptTokenizer::tokenizeVarAndConst( ScriptTokenState &State, int Flags) {
|
---|
| 1626 | bool noIN = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
| 1627 | int currLine = l->currentLine(), currColumn = l->currentColumn();
|
---|
| 1628 |
|
---|
| 1629 | bool leftHand = true;
|
---|
| 1630 | int tk = l->tk;
|
---|
| 1631 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
| 1632 |
|
---|
| 1633 | STRING_VECTOR_t vars;
|
---|
| 1634 | for(;;)
|
---|
| 1635 | {
|
---|
| 1636 | bool needAssignment = false;
|
---|
| 1637 | State.Tokens.push_back(tokenizeVarIdentifier(&vars, &needAssignment));
|
---|
| 1638 | if(noIN && (l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))
|
---|
| 1639 | break;
|
---|
| 1640 | if(needAssignment || l->tk=='=') {
|
---|
| 1641 | leftHand = false;
|
---|
| 1642 | pushToken(State.Tokens, '=');
|
---|
| 1643 | tokenizeAssignment(State, Flags);
|
---|
| 1644 | if(noIN && (l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))
|
---|
| 1645 | break;
|
---|
| 1646 | }
|
---|
| 1647 | if(l->tk==',') {
|
---|
| 1648 | leftHand = false;
|
---|
| 1649 | pushToken(State.Tokens);
|
---|
| 1650 | }
|
---|
| 1651 | else
|
---|
| 1652 | break;
|
---|
| 1653 | }
|
---|
| 1654 | if(!noIN) pushToken(State.Tokens, ';');
|
---|
| 1655 |
|
---|
| 1656 | setTokenSkip(State);
|
---|
| 1657 |
|
---|
| 1658 | if(tk==LEX_R_VAR)
|
---|
| 1659 | State.Forwarders.front()->addVars(vars);
|
---|
| 1660 | else
|
---|
| 1661 | State.Forwarders.front()->addConsts(vars);
|
---|
| 1662 | string redeclared;
|
---|
| 1663 | if(State.Forwarders.size()>1) // have let-scope
|
---|
| 1664 | redeclared = State.Forwarders.back()->addVarsInLetscope(vars);
|
---|
| 1665 | #ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES
|
---|
| 1666 | else
|
---|
| 1667 | redeclared = State.Forwarders.front()->addVarsInLetscope(vars);
|
---|
| 1668 | #endif
|
---|
| 1669 | if(redeclared.size())
|
---|
| 1670 | throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn);
|
---|
| 1671 | if(leftHand) State.LeftHand = true;
|
---|
| 1672 | }
|
---|
| 1673 |
|
---|
| 1674 | void CScriptTokenizer::_tokenizeLiteralObject(ScriptTokenState &State, int Flags) {
|
---|
| 1675 | bool forFor = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
| 1676 | bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0;
|
---|
| 1677 | Flags &= ~(TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_nestedObject);
|
---|
| 1678 | CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL);
|
---|
| 1679 | CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object();
|
---|
| 1680 |
|
---|
| 1681 | Objc.type = CScriptTokenDataObjectLiteral::OBJECT;
|
---|
| 1682 | Objc.destructuring = Objc.structuring = true;
|
---|
| 1683 |
|
---|
| 1684 | string msg, msgFile;
|
---|
| 1685 | int msgLine=0, msgColumn=0;
|
---|
| 1686 |
|
---|
| 1687 | l->match('{');
|
---|
| 1688 | while (l->tk != '}') {
|
---|
| 1689 | CScriptTokenDataObjectLiteral::ELEMENT element;
|
---|
| 1690 | bool assign = false;
|
---|
| 1691 | if(CScriptToken::isReservedWord(l->tk))
|
---|
| 1692 | l->tk = LEX_ID; // fake reserved-word as member.ID
|
---|
| 1693 | if(l->tk == LEX_ID) {
|
---|
| 1694 | element.id = l->tkStr;
|
---|
| 1695 | CScriptToken Token(l, LEX_ID);
|
---|
| 1696 | if((l->tk==LEX_ID || l->tk==LEX_STR ) && (element.id=="get" || element.id=="set")) {
|
---|
| 1697 | element.id = l->tkStr;
|
---|
| 1698 | element.value.push_back(Token);
|
---|
| 1699 | State.Tokens.swap(element.value);
|
---|
| 1700 | tokenizeFunction(State, Flags|TOKENIZE_FLAGS_isAccessor);
|
---|
| 1701 | State.Tokens.swap(element.value);
|
---|
| 1702 | Objc.destructuring = false;
|
---|
| 1703 | } else {
|
---|
| 1704 | if(Objc.destructuring && (l->tk == ',' || l->tk == '}')) {
|
---|
| 1705 | if(!msg.size()) {
|
---|
| 1706 | Objc.structuring = false;
|
---|
| 1707 | msg.append("Got '").append(CScriptToken::getTokenStr(l->tk)).append("' expected ':'");
|
---|
| 1708 | msgFile = l->currentFile;
|
---|
| 1709 | msgLine = l->currentLine();
|
---|
| 1710 | msgColumn = l->currentColumn();
|
---|
| 1711 | ;
|
---|
| 1712 | }
|
---|
| 1713 | element.value.push_back(Token);
|
---|
| 1714 | } else
|
---|
| 1715 | assign = true;
|
---|
| 1716 | }
|
---|
| 1717 | } else if(l->tk == LEX_INT) {
|
---|
| 1718 | element.id = int2string((int32_t)strtol(l->tkStr.c_str(),0,0));
|
---|
| 1719 | l->match(LEX_INT);
|
---|
| 1720 | assign = true;
|
---|
| 1721 | } else if(l->tk == LEX_FLOAT) {
|
---|
| 1722 | element.id = float2string(strtod(l->tkStr.c_str(),0));
|
---|
| 1723 | l->match(LEX_FLOAT);
|
---|
| 1724 | assign = true;
|
---|
| 1725 | } else if(LEX_TOKEN_DATA_STRING(l->tk) && l->tk != LEX_REGEXP) {
|
---|
| 1726 | element.id = l->tkStr;
|
---|
| 1727 | l->match(l->tk);
|
---|
| 1728 | assign = true;
|
---|
| 1729 | } else
|
---|
| 1730 | l->match(LEX_ID, LEX_STR);
|
---|
| 1731 | if(assign) {
|
---|
| 1732 | l->match(':');
|
---|
| 1733 | int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0;
|
---|
| 1734 | State.pushLeftHandState();
|
---|
| 1735 | State.Tokens.swap(element.value);
|
---|
| 1736 | tokenizeAssignment(State, dFlags);
|
---|
| 1737 | State.Tokens.swap(element.value);
|
---|
| 1738 | if(Objc.destructuring) Objc.destructuring = State.LeftHand;
|
---|
| 1739 | State.popLeftHandeState();
|
---|
| 1740 | }
|
---|
| 1741 |
|
---|
| 1742 | if(!Objc.destructuring && msg.size())
|
---|
| 1743 | throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn);
|
---|
| 1744 | Objc.elements.push_back(element);
|
---|
| 1745 | if (l->tk != '}') l->match(',', '}');
|
---|
| 1746 | }
|
---|
| 1747 | l->match('}');
|
---|
| 1748 | if(Objc.destructuring && Objc.structuring) {
|
---|
| 1749 | if(nestedObject) {
|
---|
| 1750 | if(l->tk!=',' && l->tk!='}' && l->tk!='=')
|
---|
| 1751 | Objc.destructuring = false;
|
---|
| 1752 | }
|
---|
| 1753 | else
|
---|
| 1754 | Objc.setMode(l->tk=='=' || (forFor && (l->tk==LEX_R_IN ||(l->tk==LEX_ID && l->tkStr=="of"))));
|
---|
| 1755 | } else {
|
---|
| 1756 | if(!Objc.destructuring && msg.size())
|
---|
| 1757 | throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn);
|
---|
| 1758 | if(!nestedObject) Objc.setMode(Objc.destructuring);
|
---|
| 1759 | }
|
---|
| 1760 |
|
---|
| 1761 | if(Objc.destructuring)
|
---|
| 1762 | State.LeftHand = true;
|
---|
| 1763 | State.Tokens.push_back(ObjectToken);
|
---|
| 1764 | }
|
---|
| 1765 | void CScriptTokenizer::_tokenizeLiteralArray(ScriptTokenState &State, int Flags) {
|
---|
| 1766 | bool forFor = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
| 1767 | bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0;
|
---|
| 1768 | Flags &= ~(TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_nestedObject);
|
---|
| 1769 | CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL);
|
---|
| 1770 | CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object();
|
---|
| 1771 |
|
---|
| 1772 | Objc.type = CScriptTokenDataObjectLiteral::ARRAY;
|
---|
| 1773 | Objc.destructuring = Objc.structuring = true;
|
---|
| 1774 | int idx = 0;
|
---|
| 1775 |
|
---|
| 1776 | l->match('[');
|
---|
| 1777 | while (l->tk != ']') {
|
---|
| 1778 | CScriptTokenDataObjectLiteral::ELEMENT element;
|
---|
| 1779 | element.id = int2string(idx++);
|
---|
| 1780 | if(l->tk != ',') {
|
---|
| 1781 | int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0;
|
---|
| 1782 | State.pushLeftHandState();
|
---|
| 1783 | State.Tokens.swap(element.value);
|
---|
| 1784 | tokenizeAssignment(State, dFlags);
|
---|
| 1785 | State.Tokens.swap(element.value);
|
---|
| 1786 | if(Objc.destructuring) Objc.destructuring = State.LeftHand;
|
---|
| 1787 | State.popLeftHandeState();
|
---|
| 1788 | }
|
---|
| 1789 | Objc.elements.push_back(element);
|
---|
| 1790 | if (l->tk != ']') l->match(',', ']');
|
---|
| 1791 | }
|
---|
| 1792 | l->match(']');
|
---|
| 1793 | if(Objc.destructuring && Objc.structuring) {
|
---|
| 1794 | if(nestedObject) {
|
---|
| 1795 | if(l->tk!=',' && l->tk!=']' && l->tk!='=')
|
---|
| 1796 | Objc.destructuring = false;
|
---|
| 1797 | }
|
---|
| 1798 | else
|
---|
| 1799 | Objc.setMode(l->tk=='=' || (forFor && (l->tk==LEX_R_IN ||(l->tk==LEX_ID && l->tkStr=="of"))));
|
---|
| 1800 | } else
|
---|
| 1801 | if(!nestedObject) Objc.setMode(Objc.destructuring);
|
---|
| 1802 | if(Objc.destructuring)
|
---|
| 1803 | State.LeftHand = true;
|
---|
| 1804 | State.Tokens.push_back(ObjectToken);
|
---|
| 1805 | }
|
---|
| 1806 |
|
---|
| 1807 | void CScriptTokenizer::tokenizeLiteral(ScriptTokenState &State, int Flags) {
|
---|
| 1808 | State.LeftHand = 0;
|
---|
| 1809 | bool canLabel = Flags & TOKENIZE_FLAGS_canLabel; Flags &= ~TOKENIZE_FLAGS_canLabel;
|
---|
| 1810 | int ObjectLiteralFlags = Flags;
|
---|
| 1811 | Flags &= ~TOKENIZE_FLAGS_noIn;
|
---|
| 1812 | switch(l->tk) {
|
---|
| 1813 | case LEX_ID:
|
---|
| 1814 | {
|
---|
| 1815 | string label = l->tkStr;
|
---|
| 1816 | pushToken(State.Tokens);
|
---|
| 1817 | if(l->tk==':' && canLabel) {
|
---|
| 1818 | if(find(State.Labels.begin(), State.Labels.end(), label) != State.Labels.end())
|
---|
| 1819 | throw new CScriptException(SyntaxError, "dublicate label '"+label+"'", l->currentFile, l->currentLine(), l->currentColumn()-label.size());
|
---|
| 1820 | State.Tokens[State.Tokens.size()-1].token = LEX_T_LABEL; // change LEX_ID to LEX_T_LABEL
|
---|
| 1821 | State.Labels.push_back(label);
|
---|
| 1822 | } else if(label=="this") {
|
---|
| 1823 | if( l->tk == '=' || (l->tk >= LEX_ASSIGNMENTS_BEGIN && l->tk <= LEX_ASSIGNMENTS_END) )
|
---|
| 1824 | throw new CScriptException(SyntaxError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn()-label.size());
|
---|
| 1825 | if( l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS )
|
---|
| 1826 | throw new CScriptException(SyntaxError, l->tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn()-label.size());
|
---|
| 1827 | } else
|
---|
| 1828 | State.LeftHand = true;
|
---|
| 1829 | }
|
---|
| 1830 | break;
|
---|
| 1831 | case LEX_INT:
|
---|
| 1832 | case LEX_FLOAT:
|
---|
| 1833 | case LEX_STR:
|
---|
| 1834 | case LEX_REGEXP:
|
---|
| 1835 | case LEX_R_TRUE:
|
---|
| 1836 | case LEX_R_FALSE:
|
---|
| 1837 | case LEX_R_NULL:
|
---|
| 1838 | pushToken(State.Tokens);
|
---|
| 1839 | break;
|
---|
| 1840 | case '{':
|
---|
| 1841 | _tokenizeLiteralObject(State, ObjectLiteralFlags);
|
---|
| 1842 | break;
|
---|
| 1843 | case '[':
|
---|
| 1844 | _tokenizeLiteralArray(State, ObjectLiteralFlags);
|
---|
| 1845 | break;
|
---|
| 1846 | case LEX_R_LET: // let as expression
|
---|
| 1847 | tokenizeLet(State, Flags);
|
---|
| 1848 | break;
|
---|
| 1849 | case LEX_R_FUNCTION:
|
---|
| 1850 | tokenizeFunction(State, Flags);
|
---|
| 1851 | break;
|
---|
| 1852 | case LEX_R_NEW:
|
---|
| 1853 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
| 1854 | {
|
---|
| 1855 | tokenizeFunctionCall(State, (Flags | TOKENIZE_FLAGS_callForNew) & ~TOKENIZE_FLAGS_noIn);
|
---|
| 1856 | State.LeftHand = 0;
|
---|
| 1857 | }
|
---|
| 1858 | setTokenSkip(State);
|
---|
| 1859 | break;
|
---|
| 1860 | case '(':
|
---|
| 1861 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
| 1862 | tokenizeExpression(State, Flags & ~TOKENIZE_FLAGS_noIn);
|
---|
| 1863 | State.LeftHand = 0;
|
---|
| 1864 | pushToken(State.Tokens, ')');
|
---|
| 1865 | setTokenSkip(State);
|
---|
| 1866 | break;
|
---|
| 1867 | default:
|
---|
| 1868 | l->check(LEX_EOF);
|
---|
| 1869 | }
|
---|
| 1870 | }
|
---|
| 1871 | void CScriptTokenizer::tokenizeMember(ScriptTokenState &State, int Flags) {
|
---|
| 1872 | while(l->tk == '.' || l->tk == '[') {
|
---|
| 1873 | if(l->tk == '.') {
|
---|
| 1874 | pushToken(State.Tokens);
|
---|
| 1875 | if(CScriptToken::isReservedWord(l->tk))
|
---|
| 1876 | l->tk = LEX_ID; // fake reserved-word as member.ID
|
---|
| 1877 | pushToken(State.Tokens , LEX_ID);
|
---|
| 1878 | } else {
|
---|
| 1879 | State.Marks.push_back(pushToken(State.Tokens));
|
---|
| 1880 | State.pushLeftHandState();
|
---|
| 1881 | tokenizeExpression(State, Flags & ~TOKENIZE_FLAGS_noIn);
|
---|
| 1882 | State.popLeftHandeState();
|
---|
| 1883 | pushToken(State.Tokens, ']');
|
---|
| 1884 | setTokenSkip(State);
|
---|
| 1885 | }
|
---|
| 1886 | State.LeftHand = true;
|
---|
| 1887 | }
|
---|
| 1888 | }
|
---|
| 1889 | void CScriptTokenizer::tokenizeFunctionCall(ScriptTokenState &State, int Flags) {
|
---|
| 1890 | bool for_new = (Flags & TOKENIZE_FLAGS_callForNew)!=0; Flags &= ~TOKENIZE_FLAGS_callForNew;
|
---|
| 1891 | tokenizeLiteral(State, Flags);
|
---|
| 1892 | tokenizeMember(State, Flags);
|
---|
| 1893 | while(l->tk == '(') {
|
---|
| 1894 | State.LeftHand = false;
|
---|
| 1895 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
| 1896 | State.pushLeftHandState();
|
---|
| 1897 | while(l->tk!=')') {
|
---|
| 1898 | tokenizeAssignment(State, Flags & ~TOKENIZE_FLAGS_noIn);
|
---|
| 1899 | if (l->tk!=')') pushToken(State.Tokens, ',', ')');
|
---|
| 1900 | }
|
---|
| 1901 | State.popLeftHandeState();
|
---|
| 1902 | pushToken(State.Tokens);
|
---|
| 1903 | setTokenSkip(State);
|
---|
| 1904 | if(for_new) break;
|
---|
| 1905 | tokenizeMember(State, Flags);
|
---|
| 1906 | }
|
---|
| 1907 | }
|
---|
| 1908 |
|
---|
| 1909 | void CScriptTokenizer::tokenizeSubExpression(ScriptTokenState &State, int Flags) {
|
---|
| 1910 | static int Left2Right_begin[] = {
|
---|
| 1911 | /* Precedence 5 */ '*', '/', '%',
|
---|
| 1912 | /* Precedence 6 */ '+', '-',
|
---|
| 1913 | /* Precedence 7 */ LEX_LSHIFT, LEX_RSHIFT, LEX_RSHIFTU,
|
---|
| 1914 | /* Precedence 8 */ LEX_EQUAL, LEX_NEQUAL, LEX_TYPEEQUAL, LEX_NTYPEEQUAL,
|
---|
| 1915 | /* Precedence 9 */ '<', LEX_LEQUAL, '>', LEX_GEQUAL, LEX_R_IN, LEX_R_INSTANCEOF,
|
---|
| 1916 | /* Precedence 10-12 */ '&', '^', '|',
|
---|
| 1917 | };
|
---|
| 1918 | static int *Left2Right_end = &Left2Right_begin[sizeof(Left2Right_begin)/sizeof(Left2Right_begin[0])];
|
---|
| 1919 | static bool Left2Right_sorted = false;
|
---|
| 1920 | if(!Left2Right_sorted) Left2Right_sorted = (sort(Left2Right_begin, Left2Right_end), true);
|
---|
| 1921 | bool noLeftHand = false;
|
---|
| 1922 | for(;;) {
|
---|
| 1923 | bool right2left_end = false;
|
---|
| 1924 | while(!right2left_end) {
|
---|
| 1925 | switch(l->tk) {
|
---|
| 1926 | case '-':
|
---|
| 1927 | case '+':
|
---|
| 1928 | case '!':
|
---|
| 1929 | case '~':
|
---|
| 1930 | case LEX_R_TYPEOF:
|
---|
| 1931 | case LEX_R_VOID:
|
---|
| 1932 | case LEX_R_DELETE:
|
---|
| 1933 | Flags &= ~TOKENIZE_FLAGS_canLabel;
|
---|
| 1934 | noLeftHand = true;
|
---|
| 1935 | pushToken(State.Tokens); // Precedence 3
|
---|
| 1936 | break;
|
---|
| 1937 | case LEX_PLUSPLUS: // pre-increment
|
---|
| 1938 | case LEX_MINUSMINUS: // pre-decrement
|
---|
| 1939 | {
|
---|
| 1940 | int tk = l->tk;
|
---|
| 1941 | Flags &= ~TOKENIZE_FLAGS_canLabel;
|
---|
| 1942 | noLeftHand = true;
|
---|
| 1943 | pushToken(State.Tokens); // Precedence 4
|
---|
| 1944 | if(l->tk == LEX_ID && l->tkStr == "this")
|
---|
| 1945 | throw new CScriptException(SyntaxError, tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 1946 | }
|
---|
| 1947 | default:
|
---|
| 1948 | right2left_end = true;
|
---|
| 1949 | }
|
---|
| 1950 | }
|
---|
| 1951 | tokenizeFunctionCall(State, Flags);
|
---|
| 1952 |
|
---|
| 1953 | if (!l->lineBreakBeforeToken && (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS)) { // post-in-/de-crement
|
---|
| 1954 | noLeftHand = true;;
|
---|
| 1955 | pushToken(State.Tokens); // Precedence 4
|
---|
| 1956 | }
|
---|
| 1957 | if(Flags&TOKENIZE_FLAGS_noIn && l->tk==LEX_R_IN)
|
---|
| 1958 | break;
|
---|
| 1959 | int *found = lower_bound(Left2Right_begin, Left2Right_end, l->tk);
|
---|
| 1960 | if(found != Left2Right_end && *found == l->tk) {
|
---|
| 1961 | noLeftHand = true;
|
---|
| 1962 | pushToken(State.Tokens); // Precedence 5-14
|
---|
| 1963 | }
|
---|
| 1964 | else
|
---|
| 1965 | break;
|
---|
| 1966 | }
|
---|
| 1967 | if(noLeftHand) State.LeftHand = false;
|
---|
| 1968 | }
|
---|
| 1969 |
|
---|
| 1970 | void CScriptTokenizer::tokenizeLogic(ScriptTokenState &State, int Flags, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) {
|
---|
| 1971 | op_n ? tokenizeLogic(State, Flags, op_n, 0) : tokenizeSubExpression(State, Flags);
|
---|
| 1972 | if(l->tk==op) {
|
---|
| 1973 | unsigned int marks_count = State.Marks.size();
|
---|
| 1974 | while(l->tk==op) {
|
---|
| 1975 | State.Marks.push_back(pushToken(State.Tokens));
|
---|
| 1976 | op_n ? tokenizeLogic(State, Flags, op_n, 0) : tokenizeSubExpression(State, Flags);
|
---|
| 1977 | }
|
---|
| 1978 | while(State.Marks.size()>marks_count) setTokenSkip(State);
|
---|
| 1979 | State.LeftHand = false;
|
---|
| 1980 | }
|
---|
| 1981 | }
|
---|
| 1982 |
|
---|
| 1983 | void CScriptTokenizer::tokenizeCondition(ScriptTokenState &State, int Flags) {
|
---|
| 1984 | tokenizeLogic(State, Flags);
|
---|
| 1985 | if(l->tk == '?') {
|
---|
| 1986 | Flags &= ~TOKENIZE_FLAGS_noIn;
|
---|
| 1987 | State.Marks.push_back(pushToken(State.Tokens));
|
---|
| 1988 | tokenizeCondition(State, Flags);
|
---|
| 1989 | setTokenSkip(State);
|
---|
| 1990 | State.Marks.push_back(pushToken(State.Tokens, ':'));
|
---|
| 1991 | tokenizeCondition(State, Flags);
|
---|
| 1992 | setTokenSkip(State);
|
---|
| 1993 | State.LeftHand = false;
|
---|
| 1994 | }
|
---|
| 1995 | }
|
---|
| 1996 | void CScriptTokenizer::tokenizeAssignment(ScriptTokenState &State, int Flags) {
|
---|
| 1997 | tokenizeCondition(State, Flags);
|
---|
| 1998 | if (l->tk=='=' || (l->tk>=LEX_ASSIGNMENTS_BEGIN && l->tk<=LEX_ASSIGNMENTS_END) ) {
|
---|
| 1999 | if(!State.LeftHand)
|
---|
| 2000 | throw new CScriptException(ReferenceError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 2001 | pushToken(State.Tokens);
|
---|
| 2002 | tokenizeAssignment(State, Flags);
|
---|
| 2003 | State.LeftHand = false;
|
---|
| 2004 | }
|
---|
| 2005 | }
|
---|
| 2006 | void CScriptTokenizer::tokenizeExpression(ScriptTokenState &State, int Flags) {
|
---|
| 2007 | tokenizeAssignment(State, Flags);
|
---|
| 2008 | while(l->tk == ',') {
|
---|
| 2009 | pushToken(State.Tokens);
|
---|
| 2010 | tokenizeAssignment(State, Flags);
|
---|
| 2011 | State.LeftHand = false;
|
---|
| 2012 | }
|
---|
| 2013 | }
|
---|
| 2014 | void CScriptTokenizer::tokenizeBlock(ScriptTokenState &State, int Flags) {
|
---|
| 2015 | bool addBlockStart = (Flags&TOKENIZE_FLAGS_noBlockStart)==0;
|
---|
| 2016 | Flags&=~(TOKENIZE_FLAGS_noBlockStart);
|
---|
| 2017 | State.Marks.push_back(pushToken(State.Tokens, '{')); // push Token & push BeginIdx
|
---|
| 2018 | if(addBlockStart) pushForwarder(State);
|
---|
| 2019 |
|
---|
| 2020 | while(l->tk != '}' && l->tk != LEX_EOF)
|
---|
| 2021 | tokenizeStatement(State, Flags);
|
---|
| 2022 | pushToken(State.Tokens, '}');
|
---|
| 2023 |
|
---|
| 2024 | if(addBlockStart) removeEmptyForwarder(State); // clean-up BlockStarts
|
---|
| 2025 |
|
---|
| 2026 | setTokenSkip(State);
|
---|
| 2027 | }
|
---|
| 2028 |
|
---|
| 2029 | void CScriptTokenizer::tokenizeStatementNoLet(ScriptTokenState &State, int Flags) {
|
---|
| 2030 | if(l->tk == LEX_R_LET)
|
---|
| 2031 | tokenizeLet(State, Flags | TOKENIZE_FLAGS_asStatement, true);
|
---|
| 2032 | else if(l->tk==LEX_R_FUNCTION)
|
---|
| 2033 | tokenizeFunction(State, Flags | TOKENIZE_FLAGS_asStatement, true);
|
---|
| 2034 | else
|
---|
| 2035 | tokenizeStatement(State, Flags);
|
---|
| 2036 | }
|
---|
| 2037 | void CScriptTokenizer::tokenizeStatement(ScriptTokenState &State, int Flags) {
|
---|
| 2038 | switch(l->tk)
|
---|
| 2039 | {
|
---|
| 2040 | case '{': tokenizeBlock(State, Flags); break;
|
---|
| 2041 | case ';': pushToken(State.Tokens); break;
|
---|
| 2042 | case LEX_R_CONST:
|
---|
| 2043 | case LEX_R_VAR: tokenizeVarAndConst(State, Flags); break;
|
---|
| 2044 | case LEX_R_LET: tokenizeLet(State, Flags | TOKENIZE_FLAGS_asStatement); break;
|
---|
| 2045 | case LEX_R_WITH: tokenizeWith(State, Flags); break;
|
---|
| 2046 | case LEX_R_IF: tokenizeIf(State, Flags); break;
|
---|
| 2047 | case LEX_R_SWITCH: tokenizeSwitch(State, Flags); break;
|
---|
| 2048 | case LEX_R_DO:
|
---|
| 2049 | case LEX_R_WHILE: tokenizeWhileAndDo(State, Flags); break;
|
---|
| 2050 | case LEX_R_FOR: tokenizeFor(State, Flags); break;
|
---|
| 2051 | case LEX_R_FUNCTION: tokenizeFunction(State, Flags | TOKENIZE_FLAGS_asStatement); break;
|
---|
| 2052 | case LEX_R_TRY: tokenizeTry(State, Flags); break;
|
---|
| 2053 | case LEX_R_RETURN:
|
---|
| 2054 | if( (Flags & TOKENIZE_FLAGS_canReturn)==0)
|
---|
| 2055 | throw new CScriptException(SyntaxError, "'return' statement, but not in a function.", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 2056 | case LEX_R_THROW:
|
---|
| 2057 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
| 2058 | if(l->tk != ';' && !l->lineBreakBeforeToken) {
|
---|
| 2059 | tokenizeExpression(State, Flags);
|
---|
| 2060 | }
|
---|
| 2061 | pushToken(State.Tokens, ';'); // push ';'
|
---|
| 2062 | setTokenSkip(State);
|
---|
| 2063 | break;
|
---|
| 2064 | case LEX_R_BREAK:
|
---|
| 2065 | case LEX_R_CONTINUE:
|
---|
| 2066 | {
|
---|
| 2067 | bool isBreak = l->tk == LEX_R_BREAK;
|
---|
| 2068 | State.Marks.push_back(pushToken(State.Tokens)); // push Token
|
---|
| 2069 |
|
---|
| 2070 | if(l->tk != ';' && !l->lineBreakBeforeToken) {
|
---|
| 2071 | l->check(LEX_ID);
|
---|
| 2072 | STRING_VECTOR_t &L = isBreak ? State.Labels : State.LoopLabels;
|
---|
| 2073 | if(find(L.begin(), L.end(), l->tkStr) == L.end())
|
---|
| 2074 | throw new CScriptException(SyntaxError, "label '"+l->tkStr+"' not found", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 2075 | pushToken(State.Tokens); // push 'Label'
|
---|
| 2076 | } else if((Flags & (isBreak ? TOKENIZE_FLAGS_canBreak : TOKENIZE_FLAGS_canContinue) )==0)
|
---|
| 2077 | throw new CScriptException(SyntaxError,
|
---|
| 2078 | isBreak ? "'break' must be inside loop, switch or labeled statement" : "'continue' must be inside loop",
|
---|
| 2079 | l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 2080 | pushToken(State.Tokens, ';'); // push ';'
|
---|
| 2081 | setTokenSkip(State);
|
---|
| 2082 | }
|
---|
| 2083 | break;
|
---|
| 2084 | case LEX_ID:
|
---|
| 2085 | {
|
---|
| 2086 | State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx
|
---|
| 2087 | STRING_VECTOR_t::size_type label_count = State.Labels.size();
|
---|
| 2088 | tokenizeExpression(State, Flags | TOKENIZE_FLAGS_canLabel);
|
---|
| 2089 | if(label_count < State.Labels.size() && l->tk == ':') {
|
---|
| 2090 | State.Tokens.erase(State.Tokens.begin()+State.Marks.back()); // remove skip
|
---|
| 2091 | State.Marks.pop_back();
|
---|
| 2092 | pushToken(State.Tokens); // push ':'
|
---|
| 2093 | tokenizeStatement(State, Flags);
|
---|
| 2094 | State.Labels.pop_back();
|
---|
| 2095 | } else {
|
---|
| 2096 | pushToken(State.Tokens, ';');
|
---|
| 2097 | setTokenSkip(State);
|
---|
| 2098 | }
|
---|
| 2099 | }
|
---|
| 2100 | break;
|
---|
| 2101 | default:
|
---|
| 2102 | State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx
|
---|
| 2103 | tokenizeExpression(State, Flags);
|
---|
| 2104 | pushToken(State.Tokens, ';');
|
---|
| 2105 | setTokenSkip(State);
|
---|
| 2106 | break;
|
---|
| 2107 | }
|
---|
| 2108 | }
|
---|
| 2109 |
|
---|
| 2110 | int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, int Match, int Alternate) {
|
---|
| 2111 | if(Match == ';' && l->tk != ';' && (l->lineBreakBeforeToken || l->tk=='}' || l->tk==LEX_EOF))
|
---|
| 2112 | Tokens.push_back(CScriptToken(';')); // inject ';'
|
---|
| 2113 | else
|
---|
| 2114 | Tokens.push_back(CScriptToken(l, Match, Alternate));
|
---|
| 2115 | return Tokens.size()-1;
|
---|
| 2116 | }
|
---|
| 2117 | int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token) {
|
---|
| 2118 | int ret = Tokens.size();
|
---|
| 2119 | Tokens.push_back(Token);
|
---|
| 2120 | return ret;
|
---|
| 2121 | }
|
---|
| 2122 | void CScriptTokenizer::pushForwarder(TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, vector<int> &Marks) {
|
---|
| 2123 | Marks.push_back(Tokens.size());
|
---|
| 2124 | CScriptToken token(LEX_T_FORWARD);
|
---|
| 2125 | Tokens.push_back(token);
|
---|
| 2126 | Forwarders.push_back(token.Forwarder());
|
---|
| 2127 | }
|
---|
| 2128 | void CScriptTokenizer::pushForwarder(ScriptTokenState &State, bool noMarks/*=false*/) {
|
---|
| 2129 | if(!noMarks) State.Marks.push_back(State.Tokens.size());
|
---|
| 2130 | CScriptToken token(LEX_T_FORWARD);
|
---|
| 2131 | State.Tokens.push_back(token);
|
---|
| 2132 | State.Forwarders.push_back(token.Forwarder());
|
---|
| 2133 | }
|
---|
| 2134 | void CScriptTokenizer::removeEmptyForwarder(ScriptTokenState &State)
|
---|
| 2135 | {
|
---|
| 2136 | CScriptTokenDataForwardsPtr &forwarder = State.Forwarders.back();
|
---|
| 2137 | forwarder->vars_in_letscope.clear();
|
---|
| 2138 | if(forwarder->empty())
|
---|
| 2139 | State.Tokens.erase(State.Tokens.begin()+State.Marks.back());
|
---|
| 2140 | State.Forwarders.pop_back();
|
---|
| 2141 | State.Marks.pop_back();
|
---|
| 2142 | }
|
---|
| 2143 |
|
---|
| 2144 | void CScriptTokenizer::removeEmptyForwarder( TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, vector<int> &Marks )
|
---|
| 2145 | {
|
---|
| 2146 | CScriptTokenDataForwardsPtr &forwarder = Forwarders.back();
|
---|
| 2147 | forwarder->vars_in_letscope.clear();
|
---|
| 2148 | if(forwarder->empty())
|
---|
| 2149 | Tokens.erase(Tokens.begin()+Marks.back());
|
---|
| 2150 | Forwarders.pop_back();
|
---|
| 2151 | Marks.pop_back();
|
---|
| 2152 | }
|
---|
| 2153 | void CScriptTokenizer::throwTokenNotExpected() {
|
---|
| 2154 | throw new CScriptException(SyntaxError, "'"+CScriptToken::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
| 2155 | }
|
---|
| 2156 |
|
---|
| 2157 |
|
---|
| 2158 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2159 | /// CScriptVar
|
---|
| 2160 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2161 |
|
---|
| 2162 | CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) {
|
---|
| 2163 | extensible = true;
|
---|
| 2164 | context = Context;
|
---|
| 2165 | temporaryID = 0;
|
---|
| 2166 | if(context->first) {
|
---|
| 2167 | next = context->first;
|
---|
| 2168 | next->prev = this;
|
---|
| 2169 | } else {
|
---|
| 2170 | next = 0;
|
---|
| 2171 | }
|
---|
| 2172 | context->first = this;
|
---|
| 2173 | prev = 0;
|
---|
| 2174 | refs = 0;
|
---|
| 2175 | if(Prototype)
|
---|
| 2176 | addChild(TINYJS___PROTO___VAR, Prototype, SCRIPTVARLINK_WRITABLE);
|
---|
| 2177 | #if DEBUG_MEMORY
|
---|
| 2178 | mark_allocated(this);
|
---|
| 2179 | #endif
|
---|
| 2180 | }
|
---|
| 2181 | CScriptVar::CScriptVar(const CScriptVar &Copy) {
|
---|
| 2182 | extensible = Copy.extensible;
|
---|
| 2183 | context = Copy.context;
|
---|
| 2184 | temporaryID = 0;
|
---|
| 2185 | if(context->first) {
|
---|
| 2186 | next = context->first;
|
---|
| 2187 | next->prev = this;
|
---|
| 2188 | } else {
|
---|
| 2189 | next = 0;
|
---|
| 2190 | }
|
---|
| 2191 | context->first = this;
|
---|
| 2192 | prev = 0;
|
---|
| 2193 | refs = 0;
|
---|
| 2194 | SCRIPTVAR_CHILDS_cit it;
|
---|
| 2195 | for(it = Copy.Childs.begin(); it!= Copy.Childs.end(); ++it) {
|
---|
| 2196 | addChild((*it)->getName(), (*it)->getVarPtr(), (*it)->getFlags());
|
---|
| 2197 | }
|
---|
| 2198 |
|
---|
| 2199 | #if DEBUG_MEMORY
|
---|
| 2200 | mark_allocated(this);
|
---|
| 2201 | #endif
|
---|
| 2202 | }
|
---|
| 2203 | CScriptVar::~CScriptVar(void) {
|
---|
| 2204 | #if DEBUG_MEMORY
|
---|
| 2205 | mark_deallocated(this);
|
---|
| 2206 | #endif
|
---|
| 2207 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
|
---|
| 2208 | (*it)->setOwner(0);
|
---|
| 2209 | removeAllChildren();
|
---|
| 2210 | if(prev)
|
---|
| 2211 | prev->next = next;
|
---|
| 2212 | else
|
---|
| 2213 | context->first = next;
|
---|
| 2214 | if(next)
|
---|
| 2215 | next->prev = prev;
|
---|
| 2216 | }
|
---|
| 2217 |
|
---|
| 2218 | /// Type
|
---|
| 2219 |
|
---|
| 2220 | bool CScriptVar::isObject() {return false;}
|
---|
| 2221 | bool CScriptVar::isError() {return false;}
|
---|
| 2222 | bool CScriptVar::isArray() {return false;}
|
---|
| 2223 | bool CScriptVar::isRegExp() {return false;}
|
---|
| 2224 | bool CScriptVar::isAccessor() {return false;}
|
---|
| 2225 | bool CScriptVar::isNull() {return false;}
|
---|
| 2226 | bool CScriptVar::isUndefined() {return false;}
|
---|
| 2227 | bool CScriptVar::isNaN() {return false;}
|
---|
| 2228 | bool CScriptVar::isString() {return false;}
|
---|
| 2229 | bool CScriptVar::isInt() {return false;}
|
---|
| 2230 | bool CScriptVar::isBool() {return false;}
|
---|
| 2231 | int CScriptVar::isInfinity() { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar
|
---|
| 2232 | bool CScriptVar::isDouble() {return false;}
|
---|
| 2233 | bool CScriptVar::isRealNumber() {return false;}
|
---|
| 2234 | bool CScriptVar::isNumber() {return false;}
|
---|
| 2235 | bool CScriptVar::isPrimitive() {return false;}
|
---|
| 2236 | bool CScriptVar::isFunction() {return false;}
|
---|
| 2237 | bool CScriptVar::isNative() {return false;}
|
---|
| 2238 | bool CScriptVar::isBounded() {return false;}
|
---|
| 2239 | bool CScriptVar::isIterator() {return false;}
|
---|
| 2240 |
|
---|
| 2241 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2242 | /// Value
|
---|
| 2243 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2244 |
|
---|
| 2245 | CScriptVarPrimitivePtr CScriptVar::getRawPrimitive() {
|
---|
| 2246 | return CScriptVarPrimitivePtr(); // default NULL-Ptr
|
---|
| 2247 | }
|
---|
| 2248 | CScriptVarPrimitivePtr CScriptVar::toPrimitive() {
|
---|
| 2249 | return toPrimitive_hintNumber();
|
---|
| 2250 | }
|
---|
| 2251 |
|
---|
| 2252 | CScriptVarPrimitivePtr CScriptVar::toPrimitive(CScriptResult &execute) {
|
---|
| 2253 | return toPrimitive_hintNumber(execute);
|
---|
| 2254 | }
|
---|
| 2255 |
|
---|
| 2256 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(int32_t radix) {
|
---|
| 2257 | CScriptResult execute;
|
---|
| 2258 | CScriptVarPrimitivePtr var = toPrimitive_hintString(execute, radix);
|
---|
| 2259 | execute.cThrow();
|
---|
| 2260 | return var;
|
---|
| 2261 | }
|
---|
| 2262 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(CScriptResult &execute, int32_t radix) {
|
---|
| 2263 | if(execute) {
|
---|
| 2264 | if(!isPrimitive()) {
|
---|
| 2265 | CScriptVarPtr ret = callJS_toString(execute, radix);
|
---|
| 2266 | if(execute && !ret->isPrimitive()) {
|
---|
| 2267 | ret = callJS_valueOf(execute);
|
---|
| 2268 | if(execute && !ret->isPrimitive())
|
---|
| 2269 | context->throwError(execute, TypeError, "can't convert b to primitive type");
|
---|
| 2270 | }
|
---|
| 2271 | return ret;
|
---|
| 2272 | }
|
---|
| 2273 | return this;
|
---|
| 2274 | }
|
---|
| 2275 | return constScriptVar(Undefined);
|
---|
| 2276 | }
|
---|
| 2277 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber() {
|
---|
| 2278 | CScriptResult execute;
|
---|
| 2279 | CScriptVarPrimitivePtr var = toPrimitive_hintNumber(execute);
|
---|
| 2280 | execute.cThrow();
|
---|
| 2281 | return var;
|
---|
| 2282 | }
|
---|
| 2283 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber(CScriptResult &execute) {
|
---|
| 2284 | if(execute) {
|
---|
| 2285 | if(!isPrimitive()) {
|
---|
| 2286 | CScriptVarPtr ret = callJS_valueOf(execute);
|
---|
| 2287 | if(execute && !ret->isPrimitive()) {
|
---|
| 2288 | ret = callJS_toString(execute);
|
---|
| 2289 | if(execute && !ret->isPrimitive())
|
---|
| 2290 | context->throwError(execute, TypeError, "can't convert to primitive type");
|
---|
| 2291 | }
|
---|
| 2292 | return ret;
|
---|
| 2293 | }
|
---|
| 2294 | return this;
|
---|
| 2295 | }
|
---|
| 2296 | return constScriptVar(Undefined);
|
---|
| 2297 | }
|
---|
| 2298 |
|
---|
| 2299 | CScriptVarPtr CScriptVar::callJS_valueOf(CScriptResult &execute) {
|
---|
| 2300 | if(execute) {
|
---|
| 2301 | CScriptVarPtr FncValueOf = findChildWithPrototypeChain("valueOf").getter(execute);
|
---|
| 2302 | if(FncValueOf != context->objectPrototype_valueOf) { // custom valueOf in JavaScript
|
---|
| 2303 | if(FncValueOf->isFunction()) { // no Error if toString not callable
|
---|
| 2304 | vector<CScriptVarPtr> Params;
|
---|
| 2305 | return context->callFunction(execute, FncValueOf, Params, this);
|
---|
| 2306 | }
|
---|
| 2307 | } else
|
---|
| 2308 | return valueOf_CallBack();
|
---|
| 2309 | }
|
---|
| 2310 | return this;
|
---|
| 2311 | }
|
---|
| 2312 | CScriptVarPtr CScriptVar::valueOf_CallBack() {
|
---|
| 2313 | return this;
|
---|
| 2314 | }
|
---|
| 2315 |
|
---|
| 2316 | CScriptVarPtr CScriptVar::callJS_toString(CScriptResult &execute, int radix/*=0*/) {
|
---|
| 2317 | if(execute) {
|
---|
| 2318 | CScriptVarPtr FncToString = findChildWithPrototypeChain("toString").getter(execute);
|
---|
| 2319 | if(FncToString != context->objectPrototype_toString) { // custom valueOf in JavaScript
|
---|
| 2320 | if(FncToString->isFunction()) { // no Error if toString not callable
|
---|
| 2321 | vector<CScriptVarPtr> Params;
|
---|
| 2322 | Params.push_back(newScriptVar(radix));
|
---|
| 2323 | return context->callFunction(execute, FncToString, Params, this);
|
---|
| 2324 | }
|
---|
| 2325 | } else
|
---|
| 2326 | return toString_CallBack(execute, radix);
|
---|
| 2327 | }
|
---|
| 2328 | return this;
|
---|
| 2329 | }
|
---|
| 2330 | CScriptVarPtr CScriptVar::toString_CallBack(CScriptResult &execute, int radix/*=0*/) {
|
---|
| 2331 | return this;
|
---|
| 2332 | }
|
---|
| 2333 |
|
---|
| 2334 | CNumber CScriptVar::toNumber() { return toPrimitive_hintNumber()->toNumber_Callback(); };
|
---|
| 2335 | CNumber CScriptVar::toNumber(CScriptResult &execute) { return toPrimitive_hintNumber(execute)->toNumber_Callback(); };
|
---|
| 2336 | bool CScriptVar::toBoolean() { return true; }
|
---|
| 2337 | string CScriptVar::toString(int32_t radix) { return toPrimitive_hintString(radix)->toCString(radix); }
|
---|
| 2338 | string CScriptVar::toString(CScriptResult &execute, int32_t radix) { return toPrimitive_hintString(execute, radix)->toCString(radix); }
|
---|
| 2339 |
|
---|
| 2340 | int CScriptVar::getInt() { return toNumber().toInt32(); }
|
---|
| 2341 | double CScriptVar::getDouble() { return toNumber().toDouble(); }
|
---|
| 2342 | bool CScriptVar::getBool() { return toBoolean(); }
|
---|
| 2343 | string CScriptVar::getString() { return toPrimitive_hintString()->toCString(); }
|
---|
| 2344 |
|
---|
| 2345 | CScriptTokenDataFnc *CScriptVar::getFunctionData() { return 0; }
|
---|
| 2346 |
|
---|
| 2347 | CScriptVarPtr CScriptVar::toIterator(int Mode/*=3*/) {
|
---|
| 2348 | CScriptResult execute;
|
---|
| 2349 | CScriptVarPtr var = toIterator(execute, Mode);
|
---|
| 2350 | execute.cThrow();
|
---|
| 2351 | return var;
|
---|
| 2352 | }
|
---|
| 2353 | CScriptVarPtr CScriptVar::toIterator(CScriptResult &execute, int Mode/*=3*/) {
|
---|
| 2354 | if(!execute) return constScriptVar(Undefined);
|
---|
| 2355 | if(isIterator()) return this;
|
---|
| 2356 | CScriptVarFunctionPtr Generator(findChildWithPrototypeChain("__iterator__").getter(execute));
|
---|
| 2357 | vector<CScriptVarPtr> args;
|
---|
| 2358 | if(Generator) return context->callFunction(execute, Generator, args, this);
|
---|
| 2359 | return newScriptVarDefaultIterator(context, this, Mode);
|
---|
| 2360 | }
|
---|
| 2361 |
|
---|
| 2362 | string CScriptVar::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
| 2363 | getParsableStringRecursionsCheck();
|
---|
| 2364 | return toString();
|
---|
| 2365 | }
|
---|
| 2366 |
|
---|
| 2367 | CScriptVarPtr CScriptVar::getNumericVar() { return newScriptVar(toNumber()); }
|
---|
| 2368 |
|
---|
| 2369 | ////// Flags
|
---|
| 2370 |
|
---|
| 2371 | void CScriptVar::seal() {
|
---|
| 2372 | preventExtensions();
|
---|
| 2373 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
|
---|
| 2374 | (*it)->setConfigurable(false);
|
---|
| 2375 | }
|
---|
| 2376 | bool CScriptVar::isSealed() const {
|
---|
| 2377 | if(isExtensible()) return false;
|
---|
| 2378 | for(SCRIPTVAR_CHILDS_cit it = Childs.begin(); it != Childs.end(); ++it)
|
---|
| 2379 | if((*it)->isConfigurable()) return false;
|
---|
| 2380 | return true;
|
---|
| 2381 | }
|
---|
| 2382 | void CScriptVar::freeze() {
|
---|
| 2383 | preventExtensions();
|
---|
| 2384 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
|
---|
| 2385 | (*it)->setConfigurable(false), (*it)->setWritable(false);
|
---|
| 2386 | }
|
---|
| 2387 | bool CScriptVar::isFrozen() const {
|
---|
| 2388 | if(isExtensible()) return false;
|
---|
| 2389 | for(SCRIPTVAR_CHILDS_cit it = Childs.begin(); it != Childs.end(); ++it)
|
---|
| 2390 | if((*it)->isConfigurable() || (*it)->isWritable()) return false;
|
---|
| 2391 | return true;
|
---|
| 2392 | }
|
---|
| 2393 |
|
---|
| 2394 | ////// Childs
|
---|
| 2395 |
|
---|
| 2396 | CScriptVarPtr CScriptVar::getOwnPropertyDescriptor(const string &Name) {
|
---|
| 2397 | CScriptVarLinkPtr child = findChild(Name);
|
---|
| 2398 | if(!child) {
|
---|
| 2399 | CScriptVarStringPtr strVar = getRawPrimitive();
|
---|
| 2400 | uint32_t Idx;
|
---|
| 2401 | if (strVar && (Idx=isArrayIndex(Name))!=uint32_t(-1) && Idx<strVar->stringLength()) {
|
---|
| 2402 | int Char = strVar->getChar(Idx);
|
---|
| 2403 | CScriptVarPtr ret = newScriptVar(Object);
|
---|
| 2404 | ret->addChild("value", newScriptVar(string(1, (char)Char)));
|
---|
| 2405 | ret->addChild("writable", constScriptVar(false));
|
---|
| 2406 | ret->addChild("enumerable", constScriptVar(true));
|
---|
| 2407 | ret->addChild("configurable", constScriptVar(false));
|
---|
| 2408 | return ret;
|
---|
| 2409 | }
|
---|
| 2410 | }
|
---|
| 2411 |
|
---|
| 2412 | if(!child || child->getVarPtr()->isUndefined()) return constScriptVar(Undefined);
|
---|
| 2413 | CScriptVarPtr ret = newScriptVar(Object);
|
---|
| 2414 | if(child->getVarPtr()->isAccessor()) {
|
---|
| 2415 | CScriptVarLinkPtr value = child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR);
|
---|
| 2416 | ret->addChild("get", value ? value->getVarPtr() : constScriptVar(Undefined));
|
---|
| 2417 | value = child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR);
|
---|
| 2418 | ret->addChild("set", value ? value->getVarPtr() : constScriptVar(Undefined));
|
---|
| 2419 | } else {
|
---|
| 2420 | ret->addChild("value", child->getVarPtr()->valueOf_CallBack());
|
---|
| 2421 | ret->addChild("writable", constScriptVar(child->isWritable()));
|
---|
| 2422 | }
|
---|
| 2423 | ret->addChild("enumerable", constScriptVar(child->isEnumerable()));
|
---|
| 2424 | ret->addChild("configurable", constScriptVar(child->isConfigurable()));
|
---|
| 2425 | return ret;
|
---|
| 2426 | }
|
---|
| 2427 | const char *CScriptVar::defineProperty(const string &Name, CScriptVarPtr Attributes) {
|
---|
| 2428 | CScriptVarPtr attr;
|
---|
| 2429 | CScriptVarLinkPtr child = findChildWithStringChars(Name);
|
---|
| 2430 |
|
---|
| 2431 | CScriptVarPtr attr_value = Attributes->findChild("value");
|
---|
| 2432 | CScriptVarPtr attr_writable = Attributes->findChild("writable");
|
---|
| 2433 | CScriptVarPtr attr_get = Attributes->findChild("get");
|
---|
| 2434 | CScriptVarPtr attr_set = Attributes->findChild("set");
|
---|
| 2435 | CScriptVarPtr attr_enumerable = Attributes->findChild("enumerable");
|
---|
| 2436 | CScriptVarPtr attr_configurable = Attributes->findChild("configurable");
|
---|
| 2437 | bool attr_isDataDescriptor = !attr_get && !attr_set;
|
---|
| 2438 | if(!attr_isDataDescriptor && (attr_value || attr_writable)) return "property descriptors must not specify a value or be writable when a getter or setter has been specified";
|
---|
| 2439 | if(attr_isDataDescriptor) {
|
---|
| 2440 | if(attr_get && (!attr_get->isUndefined() || !attr_get->isFunction())) return "property descriptor's getter field is neither undefined nor a function";
|
---|
| 2441 | if(attr_set && (!attr_set->isUndefined() || !attr_set->isFunction())) return "property descriptor's setter field is neither undefined nor a function";
|
---|
| 2442 | }
|
---|
| 2443 | if(!child) {
|
---|
| 2444 | if(!isExtensible()) return "is not extensible";
|
---|
| 2445 | if(attr_isDataDescriptor) {
|
---|
| 2446 | child = addChild(Name, attr_value?attr_value:constScriptVar(Undefined), 0);
|
---|
| 2447 | if(attr_writable) child->setWritable(attr_writable->toBoolean());
|
---|
| 2448 | } else {
|
---|
| 2449 | child = addChild(Name, newScriptVarAccessor(context, attr_get, attr_set), SCRIPTVARLINK_WRITABLE);
|
---|
| 2450 | }
|
---|
| 2451 | } else {
|
---|
| 2452 | if(!child->isConfigurable()) {
|
---|
| 2453 | if(attr_configurable && attr_configurable->toBoolean()) goto cant_redefine;
|
---|
| 2454 | if(attr_enumerable && attr_enumerable->toBoolean() != child->isEnumerable()) goto cant_redefine;
|
---|
| 2455 | if(child->getVarPtr()->isAccessor()) {
|
---|
| 2456 | if(attr_isDataDescriptor) goto cant_redefine;
|
---|
| 2457 | if(attr_get && attr_get != child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR)) goto cant_redefine;
|
---|
| 2458 | if(attr_set && attr_set != child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR)) goto cant_redefine;
|
---|
| 2459 | } else if(!attr_isDataDescriptor) goto cant_redefine;
|
---|
| 2460 | else if(!child->isWritable()) {
|
---|
| 2461 | if(attr_writable && attr_writable->toBoolean()) goto cant_redefine;
|
---|
| 2462 | if(attr_value && !attr_value->mathsOp(child, LEX_EQUAL)->toBoolean()) goto cant_redefine;
|
---|
| 2463 | }
|
---|
| 2464 | }
|
---|
| 2465 | if(attr_isDataDescriptor) {
|
---|
| 2466 | if(child->getVarPtr()->isAccessor()) child->setWritable(false);
|
---|
| 2467 | child->setVarPtr(attr_value?attr_value:constScriptVar(Undefined));
|
---|
| 2468 | if(attr_writable) child->setWritable(attr_writable->toBoolean());
|
---|
| 2469 | } else {
|
---|
| 2470 | if(child->getVarPtr()->isAccessor()) {
|
---|
| 2471 | if(!attr_get) attr_get = child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR);
|
---|
| 2472 | if(!attr_set) attr_set = child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR);
|
---|
| 2473 | }
|
---|
| 2474 | child->setVarPtr(newScriptVarAccessor(context, attr_get, attr_set));
|
---|
| 2475 | child->setWritable(true);
|
---|
| 2476 | }
|
---|
| 2477 | }
|
---|
| 2478 | if(attr_enumerable) child->setEnumerable(attr_enumerable->toBoolean());
|
---|
| 2479 | if(attr_configurable) child->setConfigurable(attr_configurable->toBoolean());
|
---|
| 2480 | return 0;
|
---|
| 2481 | cant_redefine:
|
---|
| 2482 | return "can't redefine non-configurable property";
|
---|
| 2483 | }
|
---|
| 2484 |
|
---|
| 2485 | CScriptVarLinkPtr CScriptVar::findChild(const string &childName) {
|
---|
| 2486 | if(Childs.empty()) return 0;
|
---|
| 2487 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName);
|
---|
| 2488 | if(it != Childs.end() && (*it)->getName() == childName)
|
---|
| 2489 | return *it;
|
---|
| 2490 | return 0;
|
---|
| 2491 | }
|
---|
| 2492 |
|
---|
| 2493 | CScriptVarLinkWorkPtr CScriptVar::findChildWithStringChars(const string &childName) {
|
---|
| 2494 | CScriptVarLinkWorkPtr child = findChild(childName);
|
---|
| 2495 | if(child) return child;
|
---|
| 2496 | CScriptVarStringPtr strVar = getRawPrimitive();
|
---|
| 2497 | uint32_t Idx;
|
---|
| 2498 | if (strVar && (Idx=isArrayIndex(childName))!=uint32_t(-1) && Idx<strVar->stringLength()) {
|
---|
| 2499 | int Char = strVar->getChar(Idx);
|
---|
| 2500 | child(newScriptVar(string(1, (char)Char)), childName, SCRIPTVARLINK_ENUMERABLE);
|
---|
| 2501 | child.setReferencedOwner(this); // fake referenced Owner
|
---|
| 2502 | return child;
|
---|
| 2503 | }
|
---|
| 2504 | return 0;
|
---|
| 2505 | }
|
---|
| 2506 |
|
---|
| 2507 | CScriptVarLinkPtr CScriptVar::findChildInPrototypeChain(const string &childName) {
|
---|
| 2508 | unsigned int uniqueID = context->getUniqueID();
|
---|
| 2509 | // Look for links to actual parent classes
|
---|
| 2510 | CScriptVarPtr object = this;
|
---|
| 2511 | CScriptVarLinkPtr __proto__;
|
---|
| 2512 | while( object->getTempraryID() != uniqueID && (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) {
|
---|
| 2513 | CScriptVarLinkPtr implementation = __proto__->getVarPtr()->findChild(childName);
|
---|
| 2514 | if (implementation) return implementation;
|
---|
| 2515 | object->setTemporaryID(uniqueID); // prevents recursions
|
---|
| 2516 | object = __proto__;
|
---|
| 2517 | }
|
---|
| 2518 | return 0;
|
---|
| 2519 | }
|
---|
| 2520 |
|
---|
| 2521 | CScriptVarLinkWorkPtr CScriptVar::findChildWithPrototypeChain(const string &childName) {
|
---|
| 2522 | CScriptVarLinkWorkPtr child = findChildWithStringChars(childName);
|
---|
| 2523 | if(child) return child;
|
---|
| 2524 | child = findChildInPrototypeChain(childName);
|
---|
| 2525 | if(child) {
|
---|
| 2526 | child(child->getVarPtr(), child->getName(), child->getFlags()); // recreate implementation
|
---|
| 2527 | child.setReferencedOwner(this); // fake referenced Owner
|
---|
| 2528 | }
|
---|
| 2529 | return child;
|
---|
| 2530 | }
|
---|
| 2531 | CScriptVarLinkPtr CScriptVar::findChildByPath(const string &path) {
|
---|
| 2532 | string::size_type p = path.find('.');
|
---|
| 2533 | CScriptVarLinkPtr child;
|
---|
| 2534 | if (p == string::npos)
|
---|
| 2535 | return findChild(path);
|
---|
| 2536 | if( (child = findChild(path.substr(0,p))) )
|
---|
| 2537 | return child->getVarPtr()->findChildByPath(path.substr(p+1));
|
---|
| 2538 | return 0;
|
---|
| 2539 | }
|
---|
| 2540 |
|
---|
| 2541 | CScriptVarLinkPtr CScriptVar::findChildOrCreate(const string &childName/*, int varFlags*/) {
|
---|
| 2542 | CScriptVarLinkPtr l = findChild(childName);
|
---|
| 2543 | if (l) return l;
|
---|
| 2544 | return addChild(childName, constScriptVar(Undefined));
|
---|
| 2545 | // return addChild(childName, new CScriptVar(context, TINYJS_BLANK_DATA, varFlags));
|
---|
| 2546 | }
|
---|
| 2547 |
|
---|
| 2548 | CScriptVarLinkPtr CScriptVar::findChildOrCreateByPath(const string &path) {
|
---|
| 2549 | string::size_type p = path.find('.');
|
---|
| 2550 | if (p == string::npos)
|
---|
| 2551 | return findChildOrCreate(path);
|
---|
| 2552 | string childName(path, 0, p);
|
---|
| 2553 | CScriptVarLinkPtr l = findChild(childName);
|
---|
| 2554 | if (!l) l = addChild(childName, newScriptVar(Object));
|
---|
| 2555 | return l->getVarPtr()->findChildOrCreateByPath(path.substr(p+1));
|
---|
| 2556 | }
|
---|
| 2557 |
|
---|
| 2558 | void CScriptVar::keys(set<string> &Keys, bool OnlyEnumerable/*=true*/, uint32_t ID/*=0*/)
|
---|
| 2559 | {
|
---|
| 2560 | setTemporaryID(ID);
|
---|
| 2561 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
| 2562 | if(!OnlyEnumerable || (*it)->isEnumerable())
|
---|
| 2563 | Keys.insert((*it)->getName());
|
---|
| 2564 | }
|
---|
| 2565 | CScriptVarStringPtr isStringObj = this->getRawPrimitive();
|
---|
| 2566 | if(isStringObj) {
|
---|
| 2567 | uint32_t length = isStringObj->stringLength();
|
---|
| 2568 | for(uint32_t i=0; i<length; ++i)
|
---|
| 2569 | Keys.insert(int2string(i));
|
---|
| 2570 | }
|
---|
| 2571 | CScriptVarLinkPtr __proto__;
|
---|
| 2572 | if( ID && (__proto__ = findChild(TINYJS___PROTO___VAR)) && __proto__->getVarPtr()->getTempraryID() != ID )
|
---|
| 2573 | __proto__->getVarPtr()->keys(Keys, OnlyEnumerable, ID);
|
---|
| 2574 | }
|
---|
| 2575 |
|
---|
| 2576 | /// add & remove
|
---|
| 2577 | CScriptVarLinkPtr CScriptVar::addChild(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) {
|
---|
| 2578 | CScriptVarLinkPtr link;
|
---|
| 2579 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName);
|
---|
| 2580 | if(it == Childs.end() || (*it)->getName() != childName) {
|
---|
| 2581 | link = CScriptVarLinkPtr(child?child:constScriptVar(Undefined), childName, linkFlags);
|
---|
| 2582 | link->setOwner(this);
|
---|
| 2583 |
|
---|
| 2584 | Childs.insert(it, 1, link);
|
---|
| 2585 | #ifdef _DEBUG
|
---|
| 2586 | } else {
|
---|
| 2587 | ASSERT(0); // addChild - the child exists
|
---|
| 2588 | #endif
|
---|
| 2589 | }
|
---|
| 2590 | return link;
|
---|
| 2591 | }
|
---|
| 2592 | CScriptVarLinkPtr CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) {
|
---|
| 2593 | return addChildOrReplace(childName, child, linkFlags);
|
---|
| 2594 | }
|
---|
| 2595 | CScriptVarLinkPtr CScriptVar::addChildOrReplace(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) {
|
---|
| 2596 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName);
|
---|
| 2597 | if(it == Childs.end() || (*it)->getName() != childName) {
|
---|
| 2598 | CScriptVarLinkPtr link(child, childName, linkFlags);
|
---|
| 2599 | link->setOwner(this);
|
---|
| 2600 | Childs.insert(it, 1, link);
|
---|
| 2601 | return link;
|
---|
| 2602 | } else {
|
---|
| 2603 | (*it)->setVarPtr(child);
|
---|
| 2604 | return (*it);
|
---|
| 2605 | }
|
---|
| 2606 | }
|
---|
| 2607 |
|
---|
| 2608 | bool CScriptVar::removeLink(CScriptVarLinkPtr &link) {
|
---|
| 2609 | if (!link) return false;
|
---|
| 2610 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->getName());
|
---|
| 2611 | if(it != Childs.end() && (*it) == link) {
|
---|
| 2612 | Childs.erase(it);
|
---|
| 2613 | #ifdef _DEBUG
|
---|
| 2614 | } else {
|
---|
| 2615 | ASSERT(0); // removeLink - the link is not atached to this var
|
---|
| 2616 | #endif
|
---|
| 2617 | }
|
---|
| 2618 | link.clear();
|
---|
| 2619 | return true;
|
---|
| 2620 | }
|
---|
| 2621 | void CScriptVar::removeAllChildren() {
|
---|
| 2622 | Childs.clear();
|
---|
| 2623 | }
|
---|
| 2624 |
|
---|
| 2625 | CScriptVarPtr CScriptVar::getArrayIndex(uint32_t idx) {
|
---|
| 2626 | CScriptVarLinkPtr link = findChild(int2string(idx));
|
---|
| 2627 | if (link) return link;
|
---|
| 2628 | else return constScriptVar(Undefined); // undefined
|
---|
| 2629 | }
|
---|
| 2630 |
|
---|
| 2631 | void CScriptVar::setArrayIndex(uint32_t idx, const CScriptVarPtr &value) {
|
---|
| 2632 | string sIdx = int2string(idx);
|
---|
| 2633 | CScriptVarLinkPtr link = findChild(sIdx);
|
---|
| 2634 |
|
---|
| 2635 | if (link) {
|
---|
| 2636 | link->setVarPtr(value);
|
---|
| 2637 | } else {
|
---|
| 2638 | addChild(sIdx, value);
|
---|
| 2639 | }
|
---|
| 2640 | }
|
---|
| 2641 |
|
---|
| 2642 | uint32_t CScriptVar::getArrayLength() {
|
---|
| 2643 | if (!isArray() || Childs.size()==0) return 0;
|
---|
| 2644 | return isArrayIndex(Childs.back()->getName())+1;
|
---|
| 2645 | }
|
---|
| 2646 |
|
---|
| 2647 | CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) {
|
---|
| 2648 | CScriptResult execute;
|
---|
| 2649 | return context->mathsOp(execute, this, b, op);
|
---|
| 2650 | }
|
---|
| 2651 |
|
---|
| 2652 | void CScriptVar::trace(const string &name) {
|
---|
| 2653 | string indentStr;
|
---|
| 2654 | uint32_t uniqueID = context->getUniqueID();
|
---|
| 2655 | trace(indentStr, uniqueID, name);
|
---|
| 2656 | }
|
---|
| 2657 | void CScriptVar::trace(string &indentStr, uint32_t uniqueID, const string &name) {
|
---|
| 2658 | string indent = " ";
|
---|
| 2659 | const char *extra="";
|
---|
| 2660 | if(temporaryID == uniqueID)
|
---|
| 2661 | extra = " recursion detected";
|
---|
| 2662 | TRACE("%s'%s' = '%s' %s%s\n",
|
---|
| 2663 | indentStr.c_str(),
|
---|
| 2664 | name.c_str(),
|
---|
| 2665 | toString().c_str(),
|
---|
| 2666 | getFlagsAsString().c_str(),
|
---|
| 2667 | extra);
|
---|
| 2668 | if(temporaryID != uniqueID) {
|
---|
| 2669 | temporaryID = uniqueID;
|
---|
| 2670 | indentStr+=indent;
|
---|
| 2671 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
| 2672 | if((*it)->isEnumerable())
|
---|
| 2673 | (*it)->getVarPtr()->trace(indentStr, uniqueID, (*it)->getName());
|
---|
| 2674 | }
|
---|
| 2675 | indentStr = indentStr.substr(0, indentStr.length()-2);
|
---|
| 2676 | }
|
---|
| 2677 | }
|
---|
| 2678 |
|
---|
| 2679 | string CScriptVar::getFlagsAsString() {
|
---|
| 2680 | string flagstr = "";
|
---|
| 2681 | if (isFunction()) flagstr = flagstr + "FUNCTION ";
|
---|
| 2682 | if (isObject()) flagstr = flagstr + "OBJECT ";
|
---|
| 2683 | if (isArray()) flagstr = flagstr + "ARRAY ";
|
---|
| 2684 | if (isNative()) flagstr = flagstr + "NATIVE ";
|
---|
| 2685 | if (isDouble()) flagstr = flagstr + "DOUBLE ";
|
---|
| 2686 | if (isInt()) flagstr = flagstr + "INTEGER ";
|
---|
| 2687 | if (isBool()) flagstr = flagstr + "BOOLEAN ";
|
---|
| 2688 | if (isString()) flagstr = flagstr + "STRING ";
|
---|
| 2689 | if (isRegExp()) flagstr = flagstr + "REGEXP ";
|
---|
| 2690 | if (isNaN()) flagstr = flagstr + "NaN ";
|
---|
| 2691 | if (isInfinity()) flagstr = flagstr + "INFINITY ";
|
---|
| 2692 | return flagstr;
|
---|
| 2693 | }
|
---|
| 2694 |
|
---|
| 2695 | CScriptVar *CScriptVar::ref() {
|
---|
| 2696 | refs++;
|
---|
| 2697 | return this;
|
---|
| 2698 | }
|
---|
| 2699 | void CScriptVar::unref() {
|
---|
| 2700 | refs--;
|
---|
| 2701 | ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n");
|
---|
| 2702 | if (refs==0)
|
---|
| 2703 | delete this;
|
---|
| 2704 | }
|
---|
| 2705 |
|
---|
| 2706 | int CScriptVar::getRefs() {
|
---|
| 2707 | return refs;
|
---|
| 2708 | }
|
---|
| 2709 |
|
---|
| 2710 | void CScriptVar::setTemporaryID_recursive(uint32_t ID) {
|
---|
| 2711 | if(temporaryID != ID) {
|
---|
| 2712 | temporaryID = ID;
|
---|
| 2713 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
| 2714 | (*it)->getVarPtr()->setTemporaryID_recursive(ID);
|
---|
| 2715 | }
|
---|
| 2716 | }
|
---|
| 2717 | }
|
---|
| 2718 |
|
---|
| 2719 |
|
---|
| 2720 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2721 | /// CScriptVarLink
|
---|
| 2722 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2723 |
|
---|
| 2724 | CScriptVarLink::CScriptVarLink(const CScriptVarPtr &Var, const string &Name /*=TINYJS_TEMP_NAME*/, int Flags /*=SCRIPTVARLINK_DEFAULT*/)
|
---|
| 2725 | : name(Name), owner(0), flags(Flags), refs(0) {
|
---|
| 2726 | #if DEBUG_MEMORY
|
---|
| 2727 | mark_allocated(this);
|
---|
| 2728 | #endif
|
---|
| 2729 | var = Var;
|
---|
| 2730 | }
|
---|
| 2731 |
|
---|
| 2732 | CScriptVarLink::~CScriptVarLink() {
|
---|
| 2733 | #if DEBUG_MEMORY
|
---|
| 2734 | mark_deallocated(this);
|
---|
| 2735 | #endif
|
---|
| 2736 | }
|
---|
| 2737 |
|
---|
| 2738 | CScriptVarLink *CScriptVarLink::ref() {
|
---|
| 2739 | refs++;
|
---|
| 2740 | return this;
|
---|
| 2741 | }
|
---|
| 2742 | void CScriptVarLink::unref() {
|
---|
| 2743 | refs--;
|
---|
| 2744 | ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n");
|
---|
| 2745 | if (refs==0)
|
---|
| 2746 | delete this;
|
---|
| 2747 | }
|
---|
| 2748 |
|
---|
| 2749 |
|
---|
| 2750 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2751 | /// CScriptVarLinkPtr
|
---|
| 2752 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2753 |
|
---|
| 2754 | CScriptVarLinkPtr & CScriptVarLinkPtr::operator()( const CScriptVarPtr &var, const std::string &name /*= TINYJS_TEMP_NAME*/, int flags /*= SCRIPTVARLINK_DEFAULT*/ ) {
|
---|
| 2755 | if(link && link->refs == 1) { // the link is only refered by this
|
---|
| 2756 | link->name = name;
|
---|
| 2757 | link->owner = 0;
|
---|
| 2758 | link->flags = flags;
|
---|
| 2759 | link->var = var;
|
---|
| 2760 | } else {
|
---|
| 2761 | if(link) link->unref();
|
---|
| 2762 | link = (new CScriptVarLink(var, name, flags))->ref();
|
---|
| 2763 | }
|
---|
| 2764 | return *this;
|
---|
| 2765 | }
|
---|
| 2766 |
|
---|
| 2767 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter() {
|
---|
| 2768 | return CScriptVarLinkWorkPtr(*this).getter();
|
---|
| 2769 | }
|
---|
| 2770 |
|
---|
| 2771 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter( CScriptResult &execute ) {
|
---|
| 2772 | return CScriptVarLinkWorkPtr(*this).getter(execute);
|
---|
| 2773 | }
|
---|
| 2774 |
|
---|
| 2775 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( const CScriptVarPtr &Var ) {
|
---|
| 2776 | return CScriptVarLinkWorkPtr(*this).setter(Var);
|
---|
| 2777 | }
|
---|
| 2778 |
|
---|
| 2779 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) {
|
---|
| 2780 | return CScriptVarLinkWorkPtr(*this).setter(execute, Var);
|
---|
| 2781 | }
|
---|
| 2782 |
|
---|
| 2783 | bool CScriptVarLinkPtr::operator <(const string &rhs) const {
|
---|
| 2784 | uint32_t lhs_int = isArrayIndex(link->getName());
|
---|
| 2785 | uint32_t rhs_int = isArrayIndex(rhs);
|
---|
| 2786 | if(lhs_int==uint32_t(-1)) {
|
---|
| 2787 | if(rhs_int==uint32_t(-1))
|
---|
| 2788 | return link->getName() < rhs;
|
---|
| 2789 | else
|
---|
| 2790 | return true;
|
---|
| 2791 | } else if(rhs_int==uint32_t(-1))
|
---|
| 2792 | return false;
|
---|
| 2793 | return lhs_int < rhs_int;
|
---|
| 2794 | }
|
---|
| 2795 |
|
---|
| 2796 |
|
---|
| 2797 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2798 | /// CScriptVarLinkWorkPtr
|
---|
| 2799 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2800 |
|
---|
| 2801 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter() {
|
---|
| 2802 | if(link && link->getVarPtr()) {
|
---|
| 2803 | CScriptResult execute;
|
---|
| 2804 | CScriptVarPtr ret = getter(execute);
|
---|
| 2805 | execute.cThrow();
|
---|
| 2806 | return ret;
|
---|
| 2807 | }
|
---|
| 2808 | return *this;
|
---|
| 2809 | }
|
---|
| 2810 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter(CScriptResult &execute) {
|
---|
| 2811 | if(execute && link && link->getVarPtr() && link->getVarPtr()->isAccessor()) {
|
---|
| 2812 | const CScriptVarPtr &var = link->getVarPtr();
|
---|
| 2813 | CScriptVarLinkPtr getter = var->findChild(TINYJS_ACCESSOR_GET_VAR);
|
---|
| 2814 | if(getter) {
|
---|
| 2815 | vector<CScriptVarPtr> Params;
|
---|
| 2816 | ASSERT(getReferencedOwner());
|
---|
| 2817 | return getter->getVarPtr()->getContext()->callFunction(execute, getter->getVarPtr(), Params, getReferencedOwner());
|
---|
| 2818 | } else
|
---|
| 2819 | return var->constScriptVar(Undefined);
|
---|
| 2820 | } else
|
---|
| 2821 | return *this;
|
---|
| 2822 | }
|
---|
| 2823 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( const CScriptVarPtr &Var ) {
|
---|
| 2824 | if(link && link->getVarPtr()) {
|
---|
| 2825 | CScriptResult execute;
|
---|
| 2826 | CScriptVarPtr ret = setter(execute, Var);
|
---|
| 2827 | execute.cThrow();
|
---|
| 2828 | return ret;
|
---|
| 2829 | }
|
---|
| 2830 | return *this;
|
---|
| 2831 | }
|
---|
| 2832 |
|
---|
| 2833 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) {
|
---|
| 2834 | if(execute) {
|
---|
| 2835 | if(link) {
|
---|
| 2836 | if(link->getVarPtr() && link->getVarPtr()->isAccessor()) {
|
---|
| 2837 | const CScriptVarPtr &var = link->getVarPtr();
|
---|
| 2838 | CScriptVarLinkPtr setter = var->findChild(TINYJS_ACCESSOR_SET_VAR);
|
---|
| 2839 | if(setter) {
|
---|
| 2840 | vector<CScriptVarPtr> Params;
|
---|
| 2841 | Params.push_back(Var);
|
---|
| 2842 | ASSERT(getReferencedOwner());
|
---|
| 2843 | setter->getVarPtr()->getContext()->callFunction(execute, setter->getVarPtr(), Params, getReferencedOwner());
|
---|
| 2844 | }
|
---|
| 2845 | } else
|
---|
| 2846 | link->setVarPtr(Var);
|
---|
| 2847 | }
|
---|
| 2848 | }
|
---|
| 2849 | return *this;
|
---|
| 2850 | }
|
---|
| 2851 |
|
---|
| 2852 |
|
---|
| 2853 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2854 | /// CScriptVarPrimitive
|
---|
| 2855 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2856 |
|
---|
| 2857 | CScriptVarPrimitive::~CScriptVarPrimitive(){}
|
---|
| 2858 |
|
---|
| 2859 | bool CScriptVarPrimitive::isPrimitive() { return true; }
|
---|
| 2860 | CScriptVarPrimitivePtr CScriptVarPrimitive::getRawPrimitive() { return this; }
|
---|
| 2861 | bool CScriptVarPrimitive::toBoolean() { return false; }
|
---|
| 2862 | CScriptVarPtr CScriptVarPrimitive::toObject() { return this; }
|
---|
| 2863 | CScriptVarPtr CScriptVarPrimitive::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
|
---|
| 2864 | return newScriptVar(toCString(radix));
|
---|
| 2865 | }
|
---|
| 2866 |
|
---|
| 2867 |
|
---|
| 2868 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2869 | // CScriptVarUndefined
|
---|
| 2870 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2871 |
|
---|
| 2872 | declare_dummy_t(Undefined);
|
---|
| 2873 | CScriptVarUndefined::CScriptVarUndefined(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { }
|
---|
| 2874 | CScriptVarUndefined::~CScriptVarUndefined() {}
|
---|
| 2875 | CScriptVarPtr CScriptVarUndefined::clone() { return new CScriptVarUndefined(*this); }
|
---|
| 2876 | bool CScriptVarUndefined::isUndefined() { return true; }
|
---|
| 2877 |
|
---|
| 2878 | CNumber CScriptVarUndefined::toNumber_Callback() { return NaN; }
|
---|
| 2879 | string CScriptVarUndefined::toCString(int radix/*=0*/) { return "undefined"; }
|
---|
| 2880 | string CScriptVarUndefined::getVarType() { return "undefined"; }
|
---|
| 2881 |
|
---|
| 2882 |
|
---|
| 2883 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2884 | // CScriptVarNull
|
---|
| 2885 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2886 |
|
---|
| 2887 | declare_dummy_t(Null);
|
---|
| 2888 | CScriptVarNull::CScriptVarNull(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { }
|
---|
| 2889 | CScriptVarNull::~CScriptVarNull() {}
|
---|
| 2890 | CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); }
|
---|
| 2891 | bool CScriptVarNull::isNull() { return true; }
|
---|
| 2892 |
|
---|
| 2893 | CNumber CScriptVarNull::toNumber_Callback() { return 0; }
|
---|
| 2894 | string CScriptVarNull::toCString(int radix/*=0*/) { return "null"; }
|
---|
| 2895 | string CScriptVarNull::getVarType() { return "null"; }
|
---|
| 2896 |
|
---|
| 2897 |
|
---|
| 2898 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2899 | /// CScriptVarString
|
---|
| 2900 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2901 |
|
---|
| 2902 | CScriptVarString::CScriptVarString(CTinyJS *Context, const string &Data) : CScriptVarPrimitive(Context, Context->stringPrototype), data(Data) {
|
---|
| 2903 | addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT);
|
---|
| 2904 | /*
|
---|
| 2905 | CScriptVarLinkPtr acc = addChild("length", newScriptVar(Accessor), 0);
|
---|
| 2906 | CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarString::native_Length, 0));
|
---|
| 2907 | getter->setFunctionData(new CScriptTokenDataFnc);
|
---|
| 2908 | acc->getVarPtr()->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0);
|
---|
| 2909 | */
|
---|
| 2910 | }
|
---|
| 2911 | CScriptVarString::~CScriptVarString() {}
|
---|
| 2912 | CScriptVarPtr CScriptVarString::clone() { return new CScriptVarString(*this); }
|
---|
| 2913 | bool CScriptVarString::isString() { return true; }
|
---|
| 2914 |
|
---|
| 2915 | bool CScriptVarString::toBoolean() { return data.length()!=0; }
|
---|
| 2916 | CNumber CScriptVarString::toNumber_Callback() { return data.c_str(); }
|
---|
| 2917 | string CScriptVarString::toCString(int radix/*=0*/) { return data; }
|
---|
| 2918 |
|
---|
| 2919 | string CScriptVarString::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getJSString(data); }
|
---|
| 2920 | string CScriptVarString::getVarType() { return "string"; }
|
---|
| 2921 |
|
---|
| 2922 | CScriptVarPtr CScriptVarString::toObject() {
|
---|
| 2923 | CScriptVarPtr ret = newScriptVar(CScriptVarPrimitivePtr(this), context->stringPrototype);
|
---|
| 2924 | ret->addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT);
|
---|
| 2925 | return ret;
|
---|
| 2926 | }
|
---|
| 2927 |
|
---|
| 2928 | CScriptVarPtr CScriptVarString::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
|
---|
| 2929 | return this;
|
---|
| 2930 | }
|
---|
| 2931 |
|
---|
| 2932 | int CScriptVarString::getChar(uint32_t Idx) {
|
---|
| 2933 | if((string::size_type)Idx >= data.length())
|
---|
| 2934 | return -1;
|
---|
| 2935 | else
|
---|
| 2936 | return (unsigned char)data[Idx];
|
---|
| 2937 | }
|
---|
| 2938 |
|
---|
| 2939 |
|
---|
| 2940 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2941 | /// CNumber
|
---|
| 2942 | //////////////////////////////////////////////////////////////////////////
|
---|
| 2943 |
|
---|
| 2944 | NegativeZero_t NegativeZero;
|
---|
| 2945 | declare_dummy_t(NaN);
|
---|
| 2946 | Infinity InfinityPositive(1);
|
---|
| 2947 | Infinity InfinityNegative(-1);
|
---|
| 2948 | #if 1
|
---|
| 2949 | static inline bool _isNaN(volatile double *Value1, volatile double *Value2) {
|
---|
| 2950 | return !(*Value1==*Value2);
|
---|
| 2951 | }
|
---|
| 2952 |
|
---|
| 2953 | static inline bool isNaN(double Value) {
|
---|
| 2954 | return _isNaN(&Value, &Value);
|
---|
| 2955 | }
|
---|
| 2956 | inline bool isNegZero(double d) {
|
---|
| 2957 | double x=-0.0;
|
---|
| 2958 | return memcmp(&d, &x, sizeof(double))==0;
|
---|
| 2959 | }
|
---|
| 2960 |
|
---|
| 2961 | CNumber &CNumber::operator=(double Value) {
|
---|
| 2962 | double integral;
|
---|
| 2963 | if(isNegZero(Value))
|
---|
| 2964 | type=tnNULL, Int32=0;
|
---|
| 2965 | else if(numeric_limits<double>::has_infinity && Value == numeric_limits<double>::infinity())
|
---|
| 2966 | type=tInfinity, Int32=1;
|
---|
| 2967 | else if(numeric_limits<double>::has_infinity && Value == -numeric_limits<double>::infinity())
|
---|
| 2968 | type=tInfinity, Int32=-1;
|
---|
| 2969 | else if(::isNaN(Value) || Value == numeric_limits<double>::quiet_NaN() || Value == std::numeric_limits<double>::signaling_NaN())
|
---|
| 2970 | type=tNaN, Int32=0;
|
---|
| 2971 | else if(modf(Value, &integral)==0.0 && numeric_limits<int32_t>::min()<=integral && integral<=numeric_limits<int32_t>::max())
|
---|
| 2972 | type=tInt32, Int32=int32_t(integral);
|
---|
| 2973 | else
|
---|
| 2974 | type=tDouble, Double=Value;
|
---|
| 2975 | return *this;
|
---|
| 2976 | }
|
---|
| 2977 | CNumber &CNumber::operator=(const char *str) {
|
---|
| 2978 |
|
---|
| 2979 | while(isWhitespace(*str)) str++;
|
---|
| 2980 | const char *start = str, *endptr;
|
---|
| 2981 | if(*str == '-' || *str == '+') str++;
|
---|
| 2982 | if(*str == '0' && ( str[1]=='x' || str[1]=='X'))
|
---|
| 2983 | parseInt(start, 16, &endptr);
|
---|
| 2984 | else if(*str == '0' && str[1]>='0' && str[1]<='7')
|
---|
| 2985 | parseInt(start, 8, &endptr);
|
---|
| 2986 | else
|
---|
| 2987 | parseFloat(start, &endptr);
|
---|
| 2988 | while(isWhitespace(*endptr)) endptr++;
|
---|
| 2989 | if(*endptr != '\0')
|
---|
| 2990 | type=tNaN, Int32=0;
|
---|
| 2991 | return *this;
|
---|
| 2992 | }
|
---|
| 2993 | int32_t CNumber::parseInt(const char * str, int32_t radix/*=0*/, const char **endptr/*=0*/) {
|
---|
| 2994 | type=tInt32, Int32=0;
|
---|
| 2995 | if(endptr) *endptr = str;
|
---|
| 2996 | bool stripPrefix = false; //< is true if radix==0 or radix==16
|
---|
| 2997 | if(radix == 0) {
|
---|
| 2998 | radix=10;
|
---|
| 2999 | stripPrefix = true;
|
---|
| 3000 | } else if(radix < 2 || radix > 36) {
|
---|
| 3001 | type = tNaN;
|
---|
| 3002 | return 0;
|
---|
| 3003 | } else
|
---|
| 3004 | stripPrefix = radix == 16;
|
---|
| 3005 | while(isWhitespace(*str)) str++;
|
---|
| 3006 | int sign=1;
|
---|
| 3007 | if(*str=='-') sign=-1,str++;
|
---|
| 3008 | else if(*str=='+') str++;
|
---|
| 3009 | if(stripPrefix && *str=='0' && (str[1]=='x' || str[1]=='X')) str+=2, radix=16;
|
---|
| 3010 | else if(stripPrefix && *str=='0' && str[1]>='0' && str[1]<='7') str+=1, radix=8;
|
---|
| 3011 | int32_t max = 0x7fffffff/radix;
|
---|
| 3012 | const char *start = str;
|
---|
| 3013 | for( ; *str; str++) {
|
---|
| 3014 | if(*str >= '0' && *str <= '0'-1+radix) Int32 = Int32*radix+*str-'0';
|
---|
| 3015 | else if(*str>='a' && *str<='a'-11+radix) Int32 = Int32*radix+*str-'a'+10;
|
---|
| 3016 | else if(*str>='A' && *str<='A'-11+radix) Int32 = Int32*radix+*str-'A'+10;
|
---|
| 3017 | else break;
|
---|
| 3018 | if(Int32 >= max) {
|
---|
| 3019 | type=tDouble, Double=double(Int32);
|
---|
| 3020 | for(str++ ; *str; str++) {
|
---|
| 3021 | if(*str >= '0' && *str <= '0'-1+radix) Double = Double *radix+*str-'0';
|
---|
| 3022 | else if(*str>='a' && *str<='a'-11+radix) Double = Double *radix+*str-'a'+10;
|
---|
| 3023 | else if(*str>='A' && *str<='A'-11+radix) Double = Double *radix+*str-'A'+10;
|
---|
| 3024 | else break;
|
---|
| 3025 | }
|
---|
| 3026 | break;
|
---|
| 3027 | }
|
---|
| 3028 | }
|
---|
| 3029 | if(str == start) {
|
---|
| 3030 | type= tNaN;
|
---|
| 3031 | return 0;
|
---|
| 3032 | }
|
---|
| 3033 | if(sign<0 && ((type==tInt32 && Int32==0) || (type==tDouble && Double==0.0))) { type=tnNULL,Int32=0; return radix; }
|
---|
| 3034 | if(type==tInt32) operator=(sign<0 ? -Int32 : Int32);
|
---|
| 3035 | else operator=(sign<0 ? -Double : Double);
|
---|
| 3036 | if(endptr) *endptr = (char*)str;
|
---|
| 3037 | return radix;
|
---|
| 3038 | }
|
---|
| 3039 |
|
---|
| 3040 | void CNumber::parseFloat(const char * str, const char **endptr/*=0*/) {
|
---|
| 3041 | type=tInt32, Int32=0;
|
---|
| 3042 | if(endptr) *endptr = str;
|
---|
| 3043 | while(isWhitespace(*str)) str++;
|
---|
| 3044 | int sign=1;
|
---|
| 3045 | if(*str=='-') sign=-1,str++;
|
---|
| 3046 | else if(*str=='+') str++;
|
---|
| 3047 | if(strncmp(str, "Infinity", 8) == 0) { type=tInfinity, Int32=sign; return; }
|
---|
| 3048 | double d = strtod(str, (char**)endptr);
|
---|
| 3049 | operator=(sign>0 ? d : -d);
|
---|
| 3050 | return;
|
---|
| 3051 | }
|
---|
| 3052 |
|
---|
| 3053 | CNumber CNumber::add(const CNumber &Value) const {
|
---|
| 3054 | if(type==tNaN || Value.type==tNaN)
|
---|
| 3055 | return CNumber(tNaN);
|
---|
| 3056 | else if(type==tInfinity || Value.type==tInfinity) {
|
---|
| 3057 | if(type!=tInfinity)
|
---|
| 3058 | return Value;
|
---|
| 3059 | else if(Value.type!=tInfinity || sign()==Value.sign())
|
---|
| 3060 | return *this;
|
---|
| 3061 | else
|
---|
| 3062 | return CNumber(tNaN);
|
---|
| 3063 | } else if(type==tnNULL)
|
---|
| 3064 | return Value;
|
---|
| 3065 | else if(Value.type==tnNULL)
|
---|
| 3066 | return *this;
|
---|
| 3067 | else if(type==tDouble || Value.type==tDouble)
|
---|
| 3068 | return CNumber(toDouble()+Value.toDouble());
|
---|
| 3069 | else {
|
---|
| 3070 | int32_t range_max = numeric_limits<int32_t>::max();
|
---|
| 3071 | int32_t range_min = numeric_limits<int32_t>::min();
|
---|
| 3072 | if(Int32>0) range_max-=Int32;
|
---|
| 3073 | else if(Int32<0) range_min-=Int32;
|
---|
| 3074 | if(range_min<=Value.Int32 && Value.Int32<=range_max)
|
---|
| 3075 | return CNumber(Int32+Value.Int32);
|
---|
| 3076 | else
|
---|
| 3077 | return CNumber(double(Int32)+double(Value.Int32));
|
---|
| 3078 | }
|
---|
| 3079 | }
|
---|
| 3080 |
|
---|
| 3081 | CNumber CNumber::operator-() const {
|
---|
| 3082 | switch(type) {
|
---|
| 3083 | case tInt32:
|
---|
| 3084 | if(Int32==0)
|
---|
| 3085 | return CNumber(NegativeZero);
|
---|
| 3086 | case tnNULL:
|
---|
| 3087 | return CNumber(-Int32);
|
---|
| 3088 | case tDouble:
|
---|
| 3089 | return CNumber(-Double);
|
---|
| 3090 | case tInfinity:
|
---|
| 3091 | return CNumber(tInfinity, -Int32);
|
---|
| 3092 | default:
|
---|
| 3093 | return CNumber(tNaN);
|
---|
| 3094 | }
|
---|
| 3095 | }
|
---|
| 3096 |
|
---|
| 3097 | static inline int bits(uint32_t Value) {
|
---|
| 3098 | uint32_t b=0, mask=0xFFFF0000UL;
|
---|
| 3099 | for(int shift=16; shift>0 && Value!=0; shift>>=1, mask>>=shift) {
|
---|
| 3100 | if(Value & mask) {
|
---|
| 3101 | b += shift;
|
---|
| 3102 | Value>>=shift;
|
---|
| 3103 | }
|
---|
| 3104 | }
|
---|
| 3105 | return b;
|
---|
| 3106 | }
|
---|
| 3107 | static inline int bits(int32_t Value) {
|
---|
| 3108 | return bits(uint32_t(Value<0?-Value:Value));
|
---|
| 3109 | }
|
---|
| 3110 |
|
---|
| 3111 | CNumber CNumber::multi(const CNumber &Value) const {
|
---|
| 3112 | if(type==tNaN || Value.type==tNaN)
|
---|
| 3113 | return CNumber(tNaN);
|
---|
| 3114 | else if(type==tInfinity || Value.type==tInfinity) {
|
---|
| 3115 | if(isZero() || Value.isZero())
|
---|
| 3116 | return CNumber(tNaN);
|
---|
| 3117 | else
|
---|
| 3118 | return CNumber(tInfinity, sign()==Value.sign()?1:-1);
|
---|
| 3119 | } else if(isZero() || Value.isZero()) {
|
---|
| 3120 | if(sign()==Value.sign())
|
---|
| 3121 | return CNumber(0);
|
---|
| 3122 | else
|
---|
| 3123 | return CNumber(NegativeZero);
|
---|
| 3124 | } else if(type==tDouble || Value.type==tDouble)
|
---|
| 3125 | return CNumber(toDouble()*Value.toDouble());
|
---|
| 3126 | else {
|
---|
| 3127 | // Int32*Int32
|
---|
| 3128 | if(bits(Int32)+bits(Value.Int32) <= 29)
|
---|
| 3129 | return CNumber(Int32*Value.Int32);
|
---|
| 3130 | else
|
---|
| 3131 | return CNumber(double(Int32)*double(Value.Int32));
|
---|
| 3132 | }
|
---|
| 3133 | }
|
---|
| 3134 |
|
---|
| 3135 |
|
---|
| 3136 | CNumber CNumber::div( const CNumber &Value ) const {
|
---|
| 3137 | if(type==tNaN || Value.type==tNaN) return CNumber(tNaN);
|
---|
| 3138 | int Sign = sign()*Value.sign();
|
---|
| 3139 | if(type==tInfinity) {
|
---|
| 3140 | if(Value.type==tInfinity) return CNumber(tNaN);
|
---|
| 3141 | else return CNumber(tInfinity, Sign);
|
---|
| 3142 | }
|
---|
| 3143 | if(Value.type==tInfinity) {
|
---|
| 3144 | if(Sign<0) return CNumber(NegativeZero);
|
---|
| 3145 | else return CNumber(0);
|
---|
| 3146 | } else if(Value.isZero()) {
|
---|
| 3147 | if(isZero()) return CNumber(tNaN);
|
---|
| 3148 | else return CNumber(tInfinity, Sign);
|
---|
| 3149 | } else
|
---|
| 3150 | return CNumber(toDouble() / Value.toDouble());
|
---|
| 3151 | }
|
---|
| 3152 |
|
---|
| 3153 | CNumber CNumber::modulo( const CNumber &Value ) const {
|
---|
| 3154 | if(type==tNaN || type==tInfinity || Value.type==tNaN || Value.isZero()) return CNumber(tNaN);
|
---|
| 3155 | if(Value.type==tInfinity) return CNumber(*this);
|
---|
| 3156 | if(isZero()) return CNumber(0);
|
---|
| 3157 | if(type==tDouble || Value.type==tDouble) {
|
---|
| 3158 | double n = toDouble(), d = Value.toDouble(), q;
|
---|
| 3159 | modf(n/d, &q);
|
---|
| 3160 | return CNumber(n - (d * q));
|
---|
| 3161 | } else
|
---|
| 3162 | return CNumber(Int32 % Value.Int32);
|
---|
| 3163 | }
|
---|
| 3164 |
|
---|
| 3165 | CNumber CNumber::round() const {
|
---|
| 3166 | if(type != tDouble) return CNumber(*this);
|
---|
| 3167 | if(Double < 0.0 && Double >= -0.5)
|
---|
| 3168 | return CNumber(NegativeZero);
|
---|
| 3169 | return CNumber(::floor(Double+0.5));
|
---|
| 3170 | }
|
---|
| 3171 |
|
---|
| 3172 | CNumber CNumber::floor() const {
|
---|
| 3173 | if(type != tDouble) return CNumber(*this);
|
---|
| 3174 | return CNumber(::floor(Double));
|
---|
| 3175 | }
|
---|
| 3176 |
|
---|
| 3177 | CNumber CNumber::ceil() const {
|
---|
| 3178 | if(type != tDouble) return CNumber(*this);
|
---|
| 3179 | return CNumber(::ceil(Double));
|
---|
| 3180 | }
|
---|
| 3181 |
|
---|
| 3182 | CNumber CNumber::abs() const {
|
---|
| 3183 | if(sign()<0) return -CNumber(*this);
|
---|
| 3184 | else return CNumber(*this);
|
---|
| 3185 | }
|
---|
| 3186 |
|
---|
| 3187 | CNumber CNumber::shift(const CNumber &Value, bool Right) const {
|
---|
| 3188 | int32_t lhs = toInt32();
|
---|
| 3189 | uint32_t rhs = Value.toUInt32() & 0x1F;
|
---|
| 3190 | return CNumber(Right ? lhs>>rhs : lhs<<rhs);
|
---|
| 3191 | }
|
---|
| 3192 |
|
---|
| 3193 | CNumber CNumber::ushift(const CNumber &Value, bool Right) const {
|
---|
| 3194 | uint32_t lhs = toUInt32();
|
---|
| 3195 | uint32_t rhs = Value.toUInt32() & 0x1F;
|
---|
| 3196 | return CNumber(Right ? lhs>>rhs : lhs<<rhs);
|
---|
| 3197 | }
|
---|
| 3198 |
|
---|
| 3199 | CNumber CNumber::binary(const CNumber &Value, char Mode) const {
|
---|
| 3200 | int32_t lhs = toInt32();
|
---|
| 3201 | int32_t rhs = Value.toInt32();
|
---|
| 3202 |
|
---|
| 3203 | switch(Mode) {
|
---|
| 3204 | case '&': lhs&=rhs; break;
|
---|
| 3205 | case '|': lhs|=rhs; break;
|
---|
| 3206 | case '^': lhs^=rhs; break;
|
---|
| 3207 | }
|
---|
| 3208 | return CNumber(lhs);
|
---|
| 3209 | }
|
---|
| 3210 |
|
---|
| 3211 |
|
---|
| 3212 | int CNumber::less( const CNumber &Value ) const {
|
---|
| 3213 | if(type==tNaN || Value.type==tNaN) return 0;
|
---|
| 3214 | else if(type==tInfinity) {
|
---|
| 3215 | if(Value.type==tInfinity) return Int32<Value.Int32 ? 1 : -1;
|
---|
| 3216 | return -Int32;
|
---|
| 3217 | } else if(Value.type==tInfinity)
|
---|
| 3218 | return Value.Int32;
|
---|
| 3219 | else if(isZero() && Value.isZero()) return -1;
|
---|
| 3220 | else if(type==tDouble || Value.type==tDouble) return toDouble() < Value.toDouble() ? 1 : -1;
|
---|
| 3221 | return toInt32() < Value.toInt32() ? 1 : -1;
|
---|
| 3222 | }
|
---|
| 3223 |
|
---|
| 3224 | bool CNumber::equal( const CNumber &Value ) const {
|
---|
| 3225 | if(type==tNaN || Value.type==tNaN) return false;
|
---|
| 3226 | else if(type==tInfinity) {
|
---|
| 3227 | if(Value.type==tInfinity) return Int32==Value.Int32;
|
---|
| 3228 | return false;
|
---|
| 3229 | } else if(Value.type==tInfinity)
|
---|
| 3230 | return false;
|
---|
| 3231 | else if(isZero() && Value.isZero()) return true;
|
---|
| 3232 | else if(type==tDouble || Value.type==tDouble) return toDouble() == Value.toDouble();
|
---|
| 3233 | return toInt32() == Value.toInt32();
|
---|
| 3234 | }
|
---|
| 3235 |
|
---|
| 3236 | bool CNumber::isZero() const
|
---|
| 3237 | {
|
---|
| 3238 | switch(type) {
|
---|
| 3239 | case tInt32:
|
---|
| 3240 | return Int32==0;
|
---|
| 3241 | case tnNULL:
|
---|
| 3242 | return true;
|
---|
| 3243 | case tDouble:
|
---|
| 3244 | return Double==0.0;
|
---|
| 3245 | default:
|
---|
| 3246 | return false;
|
---|
| 3247 | }
|
---|
| 3248 | }
|
---|
| 3249 |
|
---|
| 3250 | bool CNumber::isInteger() const
|
---|
| 3251 | {
|
---|
| 3252 | double integral;
|
---|
| 3253 | switch(type) {
|
---|
| 3254 | case tInt32:
|
---|
| 3255 | case tnNULL:
|
---|
| 3256 | return true;
|
---|
| 3257 | case tDouble:
|
---|
| 3258 | return modf(Double, &integral)==0.0;
|
---|
| 3259 | default:
|
---|
| 3260 | return false;
|
---|
| 3261 | }
|
---|
| 3262 | }
|
---|
| 3263 |
|
---|
| 3264 | int CNumber::sign() const {
|
---|
| 3265 | switch(type) {
|
---|
| 3266 | case tInt32:
|
---|
| 3267 | case tInfinity:
|
---|
| 3268 | return Int32<0?-1:1;
|
---|
| 3269 | case tnNULL:
|
---|
| 3270 | return -1;
|
---|
| 3271 | case tDouble:
|
---|
| 3272 | return Double<0.0?-1:1;
|
---|
| 3273 | default:
|
---|
| 3274 | return 1;
|
---|
| 3275 | }
|
---|
| 3276 | }
|
---|
| 3277 | char *tiny_ltoa(int32_t val, unsigned radix) {
|
---|
| 3278 | char *buf, *buf_end, *p, *firstdig, temp;
|
---|
| 3279 | unsigned digval;
|
---|
| 3280 |
|
---|
| 3281 | buf = (char*)malloc(64);
|
---|
| 3282 | if(!buf) return 0;
|
---|
| 3283 | buf_end = buf+64-1; // -1 for '\0'
|
---|
| 3284 |
|
---|
| 3285 | p = buf;
|
---|
| 3286 | if (val < 0) {
|
---|
| 3287 | *p++ = '-';
|
---|
| 3288 | val = -val;
|
---|
| 3289 | }
|
---|
| 3290 |
|
---|
| 3291 | do {
|
---|
| 3292 | digval = (unsigned) (val % radix);
|
---|
| 3293 | val /= radix;
|
---|
| 3294 | *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0'));
|
---|
| 3295 | if(p==buf_end) {
|
---|
| 3296 | char *new_buf = (char *)realloc(buf, buf_end-buf+16+1); // for '\0'
|
---|
| 3297 | if(!new_buf) { free(buf); return 0; }
|
---|
| 3298 | p = new_buf + (buf_end - buf);
|
---|
| 3299 | buf_end = p + 16;
|
---|
| 3300 | buf = new_buf;
|
---|
| 3301 | }
|
---|
| 3302 | } while (val > 0);
|
---|
| 3303 |
|
---|
| 3304 | // We now have the digit of the number in the buffer, but in reverse
|
---|
| 3305 | // order. Thus we reverse them now.
|
---|
| 3306 | *p-- = '\0';
|
---|
| 3307 | firstdig = buf;
|
---|
| 3308 | if(*firstdig=='-') firstdig++;
|
---|
| 3309 | do {
|
---|
| 3310 | temp = *p;
|
---|
| 3311 | *p = *firstdig;
|
---|
| 3312 | *firstdig = temp;
|
---|
| 3313 | p--;
|
---|
| 3314 | firstdig++;
|
---|
| 3315 | } while (firstdig < p);
|
---|
| 3316 | return buf;
|
---|
| 3317 | }
|
---|
| 3318 |
|
---|
| 3319 | static char *tiny_dtoa(double val, unsigned radix) {
|
---|
| 3320 | char *buf, *buf_end, *p, temp;
|
---|
| 3321 | unsigned digval;
|
---|
| 3322 |
|
---|
| 3323 | buf = (char*)malloc(64);
|
---|
| 3324 | if(!buf) return 0;
|
---|
| 3325 | buf_end = buf+64-2; // -1 for '.' , -1 for '\0'
|
---|
| 3326 |
|
---|
| 3327 | p = buf;
|
---|
| 3328 | if (val < 0.0) {
|
---|
| 3329 | *p++ = '-';
|
---|
| 3330 | val = -val;
|
---|
| 3331 | }
|
---|
| 3332 |
|
---|
| 3333 | double val_1 = floor(val);
|
---|
| 3334 | double val_2 = val - val_1;
|
---|
| 3335 |
|
---|
| 3336 |
|
---|
| 3337 | do {
|
---|
| 3338 | double tmp = val_1 / radix;
|
---|
| 3339 | val_1 = floor(tmp);
|
---|
| 3340 | digval = (unsigned)((tmp - val_1) * radix);
|
---|
| 3341 |
|
---|
| 3342 | *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0'));
|
---|
| 3343 | if(p==buf_end) {
|
---|
| 3344 | char *new_buf = (char *)realloc(buf, buf_end-buf+16+2); // +2 for '.' + '\0'
|
---|
| 3345 | if(!new_buf) { free(buf); return 0; }
|
---|
| 3346 | p = new_buf + (buf_end - buf);
|
---|
| 3347 | buf_end = p + 16;
|
---|
| 3348 | buf = new_buf;
|
---|
| 3349 | }
|
---|
| 3350 | } while (val_1 > 0.0);
|
---|
| 3351 |
|
---|
| 3352 | // We now have the digit of the number in the buffer, but in reverse
|
---|
| 3353 | // order. Thus we reverse them now.
|
---|
| 3354 | char *p1 = buf;
|
---|
| 3355 | char *p2 = p-1;
|
---|
| 3356 | do {
|
---|
| 3357 | temp = *p2;
|
---|
| 3358 | *p2-- = *p1;
|
---|
| 3359 | *p1++ = temp;
|
---|
| 3360 | } while (p1 < p2);
|
---|
| 3361 |
|
---|
| 3362 | if(val_2) {
|
---|
| 3363 | *p++ = '.';
|
---|
| 3364 | do {
|
---|
| 3365 | val_2 *= radix;
|
---|
| 3366 | digval = (unsigned)(val_2);
|
---|
| 3367 | val_2 -= digval;
|
---|
| 3368 |
|
---|
| 3369 | *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0'));
|
---|
| 3370 | if(p==buf_end) {
|
---|
| 3371 | char *new_buf = (char *)realloc(buf, buf_end-buf+16);
|
---|
| 3372 | if(!new_buf) { free(buf); return 0; }
|
---|
| 3373 | p = new_buf + (buf_end - buf);
|
---|
| 3374 | buf_end = p + 16;
|
---|
| 3375 | buf = new_buf;
|
---|
| 3376 | }
|
---|
| 3377 | } while (val_2 > 0.0);
|
---|
| 3378 |
|
---|
| 3379 | }
|
---|
| 3380 | *p = '\0';
|
---|
| 3381 | return buf;
|
---|
| 3382 | }
|
---|
| 3383 | std::string CNumber::toString( uint32_t Radix/*=10*/ ) const {
|
---|
| 3384 | char *str;
|
---|
| 3385 | if(2 > Radix || Radix > 36)
|
---|
| 3386 | Radix = 10; // todo error;
|
---|
| 3387 | switch(type) {
|
---|
| 3388 | case tInt32:
|
---|
| 3389 | if( (str = tiny_ltoa(Int32, Radix)) ) {
|
---|
| 3390 | string ret(str); free(str);
|
---|
| 3391 | return ret;
|
---|
| 3392 | }
|
---|
| 3393 | break;
|
---|
| 3394 | case tnNULL:
|
---|
| 3395 | return "0";
|
---|
| 3396 | case tDouble:
|
---|
| 3397 | if(Radix==10) {
|
---|
| 3398 | ostringstream str;
|
---|
| 3399 | str.unsetf(ios::floatfield);
|
---|
| 3400 | #if (defined(_MSC_VER) && _MSC_VER >= 1600) || __cplusplus >= 201103L
|
---|
| 3401 | str.precision(numeric_limits<double>::max_digits10);
|
---|
| 3402 | #else
|
---|
| 3403 | str.precision(numeric_limits<double>::digits10+2);
|
---|
| 3404 | #endif
|
---|
| 3405 | str << Double;
|
---|
| 3406 | return str.str();
|
---|
| 3407 | } else if( (str = tiny_dtoa(Double, Radix)) ) {
|
---|
| 3408 | string ret(str); free(str);
|
---|
| 3409 | return ret;
|
---|
| 3410 | }
|
---|
| 3411 | break;
|
---|
| 3412 | case tInfinity:
|
---|
| 3413 | return Int32<0?"-Infinity":"Infinity";
|
---|
| 3414 | case tNaN:
|
---|
| 3415 | return "NaN";
|
---|
| 3416 | }
|
---|
| 3417 | return "";
|
---|
| 3418 | }
|
---|
| 3419 |
|
---|
| 3420 | double CNumber::toDouble() const
|
---|
| 3421 | {
|
---|
| 3422 | switch(type) {
|
---|
| 3423 | case tnNULL:
|
---|
| 3424 | return -0.0;
|
---|
| 3425 | case tInt32:
|
---|
| 3426 | return double(Int32);
|
---|
| 3427 | case tDouble:
|
---|
| 3428 | return Double;
|
---|
| 3429 | case tNaN:
|
---|
| 3430 | return std::numeric_limits<double>::quiet_NaN();
|
---|
| 3431 | case tInfinity:
|
---|
| 3432 | return Int32<0 ? -std::numeric_limits<double>::infinity():std::numeric_limits<double>::infinity();
|
---|
| 3433 | }
|
---|
| 3434 | return 0.0;
|
---|
| 3435 | }
|
---|
| 3436 |
|
---|
| 3437 | #endif
|
---|
| 3438 |
|
---|
| 3439 |
|
---|
| 3440 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3441 | /// CScriptVarNumber
|
---|
| 3442 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3443 |
|
---|
| 3444 | CScriptVarNumber::CScriptVarNumber(CTinyJS *Context, const CNumber &Data) : CScriptVarPrimitive(Context, Context->numberPrototype), data(Data) {}
|
---|
| 3445 | CScriptVarNumber::~CScriptVarNumber() {}
|
---|
| 3446 | CScriptVarPtr CScriptVarNumber::clone() { return new CScriptVarNumber(*this); }
|
---|
| 3447 | bool CScriptVarNumber::isNumber() { return true; }
|
---|
| 3448 | bool CScriptVarNumber::isInt() { return data.isInt32(); }
|
---|
| 3449 | bool CScriptVarNumber::isDouble() { return data.isDouble(); }
|
---|
| 3450 | bool CScriptVarNumber::isRealNumber() { return isInt() || isDouble(); }
|
---|
| 3451 | bool CScriptVarNumber::isNaN() { return data.isNaN(); }
|
---|
| 3452 | int CScriptVarNumber::isInfinity() { return data.isInfinity(); }
|
---|
| 3453 |
|
---|
| 3454 | bool CScriptVarNumber::toBoolean() { return data.toBoolean(); }
|
---|
| 3455 | CNumber CScriptVarNumber::toNumber_Callback() { return data; }
|
---|
| 3456 | string CScriptVarNumber::toCString(int radix/*=0*/) { return data.toString(radix); }
|
---|
| 3457 |
|
---|
| 3458 | string CScriptVarNumber::getVarType() { return "number"; }
|
---|
| 3459 |
|
---|
| 3460 | CScriptVarPtr CScriptVarNumber::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->numberPrototype); }
|
---|
| 3461 | inline define_newScriptVar_Fnc(Number, CTinyJS *Context, const CNumber &Obj) {
|
---|
| 3462 | if(!Obj.isInt32() && !Obj.isDouble()) {
|
---|
| 3463 | if(Obj.isNaN()) return Context->constScriptVar(NaN);
|
---|
| 3464 | if(Obj.isInfinity()) return Context->constScriptVar(Infinity(Obj.sign()));
|
---|
| 3465 | if(Obj.isNegativeZero()) return Context->constScriptVar(NegativeZero);
|
---|
| 3466 | }
|
---|
| 3467 | return new CScriptVarNumber(Context, Obj);
|
---|
| 3468 | }
|
---|
| 3469 |
|
---|
| 3470 |
|
---|
| 3471 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3472 | // CScriptVarBool
|
---|
| 3473 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3474 |
|
---|
| 3475 | CScriptVarBool::CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarPrimitive(Context, Context->booleanPrototype), data(Data) {}
|
---|
| 3476 | CScriptVarBool::~CScriptVarBool() {}
|
---|
| 3477 | CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); }
|
---|
| 3478 | bool CScriptVarBool::isBool() { return true; }
|
---|
| 3479 |
|
---|
| 3480 | bool CScriptVarBool::toBoolean() { return data; }
|
---|
| 3481 | CNumber CScriptVarBool::toNumber_Callback() { return data?1:0; }
|
---|
| 3482 | string CScriptVarBool::toCString(int radix/*=0*/) { return data ? "true" : "false"; }
|
---|
| 3483 |
|
---|
| 3484 | string CScriptVarBool::getVarType() { return "boolean"; }
|
---|
| 3485 |
|
---|
| 3486 | CScriptVarPtr CScriptVarBool::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->booleanPrototype); }
|
---|
| 3487 |
|
---|
| 3488 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3489 | /// CScriptVarObject
|
---|
| 3490 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3491 |
|
---|
| 3492 | declare_dummy_t(Object);
|
---|
| 3493 | declare_dummy_t(StopIteration);
|
---|
| 3494 | CScriptVarObject::CScriptVarObject(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { }
|
---|
| 3495 | CScriptVarObject::~CScriptVarObject() {}
|
---|
| 3496 | CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); }
|
---|
| 3497 | CScriptVarPrimitivePtr CScriptVarObject::getRawPrimitive() { return value; }
|
---|
| 3498 | bool CScriptVarObject::isObject() { return true; }
|
---|
| 3499 |
|
---|
| 3500 | string CScriptVarObject::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
| 3501 | getParsableStringRecursionsCheck();
|
---|
| 3502 | string destination;
|
---|
| 3503 | const char *nl = indent.size() ? "\n" : " ";
|
---|
| 3504 | const char *comma = "";
|
---|
| 3505 | destination.append("{");
|
---|
| 3506 | if(Childs.size()) {
|
---|
| 3507 | string new_indentString = indentString + indent;
|
---|
| 3508 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
| 3509 | if((*it)->isEnumerable()) {
|
---|
| 3510 | destination.append(comma); comma=",";
|
---|
| 3511 | destination.append(nl).append(new_indentString).append(getIDString((*it)->getName()));
|
---|
| 3512 | destination.append(" : ");
|
---|
| 3513 | destination.append((*it)->getVarPtr()->getParsableString(new_indentString, indent, uniqueID, hasRecursion));
|
---|
| 3514 | }
|
---|
| 3515 | }
|
---|
| 3516 | destination.append(nl).append(indentString);
|
---|
| 3517 | }
|
---|
| 3518 | destination.append("}");
|
---|
| 3519 | return destination;
|
---|
| 3520 | }
|
---|
| 3521 | string CScriptVarObject::getVarType() { return "object"; }
|
---|
| 3522 |
|
---|
| 3523 | CScriptVarPtr CScriptVarObject::toObject() { return this; }
|
---|
| 3524 |
|
---|
| 3525 | CScriptVarPtr CScriptVarObject::valueOf_CallBack() {
|
---|
| 3526 | if(value)
|
---|
| 3527 | return value->valueOf_CallBack();
|
---|
| 3528 | return CScriptVar::valueOf_CallBack();
|
---|
| 3529 | }
|
---|
| 3530 | CScriptVarPtr CScriptVarObject::toString_CallBack(CScriptResult &execute, int radix) {
|
---|
| 3531 | if(value)
|
---|
| 3532 | return value->toString_CallBack(execute, radix);
|
---|
| 3533 | return newScriptVar("[object Object]");
|
---|
| 3534 | };
|
---|
| 3535 |
|
---|
| 3536 | void CScriptVarObject::setTemporaryID_recursive( uint32_t ID ) {
|
---|
| 3537 | CScriptVar::setTemporaryID_recursive(ID);
|
---|
| 3538 | if(value) value->setTemporaryID_recursive(ID);
|
---|
| 3539 | }
|
---|
| 3540 |
|
---|
| 3541 |
|
---|
| 3542 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3543 | /// CScriptVarError
|
---|
| 3544 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3545 |
|
---|
| 3546 | const char *ERROR_NAME[] = {"Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError"};
|
---|
| 3547 |
|
---|
| 3548 | CScriptVarError::CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column) : CScriptVarObject(Context, Context->getErrorPrototype(type)) {
|
---|
| 3549 | if(message && *message) addChild("message", newScriptVar(message));
|
---|
| 3550 | if(file && *file) addChild("fileName", newScriptVar(file));
|
---|
| 3551 | if(line>=0) addChild("lineNumber", newScriptVar(line+1));
|
---|
| 3552 | if(column>=0) addChild("column", newScriptVar(column+1));
|
---|
| 3553 | }
|
---|
| 3554 |
|
---|
| 3555 | CScriptVarError::~CScriptVarError() {}
|
---|
| 3556 | CScriptVarPtr CScriptVarError::clone() { return new CScriptVarError(*this); }
|
---|
| 3557 | bool CScriptVarError::isError() { return true; }
|
---|
| 3558 |
|
---|
| 3559 | CScriptVarPtr CScriptVarError::toString_CallBack(CScriptResult &execute, int radix) {
|
---|
| 3560 | CScriptVarLinkPtr link;
|
---|
| 3561 | string name = ERROR_NAME[Error];
|
---|
| 3562 | link = findChildWithPrototypeChain("name"); if(link) name = link->toString(execute);
|
---|
| 3563 | string message; link = findChildWithPrototypeChain("message"); if(link) message = link->toString(execute);
|
---|
| 3564 | string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = link->toString(execute);
|
---|
| 3565 | int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = link->toNumber().toInt32();
|
---|
| 3566 | int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = link->toNumber().toInt32();
|
---|
| 3567 | ostringstream msg;
|
---|
| 3568 | msg << name << ": " << message;
|
---|
| 3569 | if(lineNumber >= 0) msg << " at Line:" << lineNumber+1;
|
---|
| 3570 | if(column >=0) msg << " Column:" << column+1;
|
---|
| 3571 | if(fileName.length()) msg << " in " << fileName;
|
---|
| 3572 | return newScriptVar(msg.str());
|
---|
| 3573 | }
|
---|
| 3574 |
|
---|
| 3575 | CScriptException *CScriptVarError::toCScriptException()
|
---|
| 3576 | {
|
---|
| 3577 | CScriptVarLinkPtr link;
|
---|
| 3578 | string name = ERROR_NAME[Error];
|
---|
| 3579 | link = findChildWithPrototypeChain("name"); if(link) name = link->toString();
|
---|
| 3580 | int ErrorCode;
|
---|
| 3581 | for(ErrorCode=(sizeof(ERROR_NAME)/sizeof(ERROR_NAME[0]))-1; ErrorCode>0; ErrorCode--) {
|
---|
| 3582 | if(name == ERROR_NAME[ErrorCode]) break;
|
---|
| 3583 | }
|
---|
| 3584 | string message; link = findChildWithPrototypeChain("message"); if(link) message = link->toString();
|
---|
| 3585 | string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = link->toString();
|
---|
| 3586 | int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = link->toNumber().toInt32();
|
---|
| 3587 | int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = link->toNumber().toInt32();
|
---|
| 3588 | return new CScriptException((enum ERROR_TYPES)ErrorCode, message, fileName, lineNumber, column);
|
---|
| 3589 | }
|
---|
| 3590 |
|
---|
| 3591 |
|
---|
| 3592 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3593 | // CScriptVarArray
|
---|
| 3594 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3595 |
|
---|
| 3596 | declare_dummy_t(Array);
|
---|
| 3597 | CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVarObject(Context, Context->arrayPrototype) {
|
---|
| 3598 | CScriptVarLinkPtr acc = addChild("length", newScriptVar(Accessor), 0);
|
---|
| 3599 | CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarArray::native_Length, 0));
|
---|
| 3600 | getter->setFunctionData(new CScriptTokenDataFnc);
|
---|
| 3601 | acc->getVarPtr()->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0);
|
---|
| 3602 | }
|
---|
| 3603 |
|
---|
| 3604 | CScriptVarArray::~CScriptVarArray() {}
|
---|
| 3605 | CScriptVarPtr CScriptVarArray::clone() { return new CScriptVarArray(*this); }
|
---|
| 3606 | bool CScriptVarArray::isArray() { return true; }
|
---|
| 3607 | string CScriptVarArray::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
| 3608 | getParsableStringRecursionsCheck();
|
---|
| 3609 | string destination;
|
---|
| 3610 | const char *nl = indent.size() ? "\n" : " ";
|
---|
| 3611 | const char *comma = "";
|
---|
| 3612 | destination.append("[");
|
---|
| 3613 | int len = getArrayLength();
|
---|
| 3614 | if(len) {
|
---|
| 3615 | string new_indentString = indentString + indent;
|
---|
| 3616 | for (int i=0;i<len;i++) {
|
---|
| 3617 | destination.append(comma); comma = ",";
|
---|
| 3618 | destination.append(nl).append(new_indentString).append(getArrayIndex(i)->getParsableString(new_indentString, indent, uniqueID, hasRecursion));
|
---|
| 3619 | }
|
---|
| 3620 | destination.append(nl).append(indentString);
|
---|
| 3621 | }
|
---|
| 3622 | destination.append("]");
|
---|
| 3623 | return destination;
|
---|
| 3624 | }
|
---|
| 3625 | CScriptVarPtr CScriptVarArray::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
|
---|
| 3626 | ostringstream destination;
|
---|
| 3627 | int len = getArrayLength();
|
---|
| 3628 | for (int i=0;i<len;i++) {
|
---|
| 3629 | destination << getArrayIndex(i)->toString(execute);
|
---|
| 3630 | if (i<len-1) destination << ", ";
|
---|
| 3631 | }
|
---|
| 3632 | return newScriptVar(destination.str());
|
---|
| 3633 |
|
---|
| 3634 | }
|
---|
| 3635 |
|
---|
| 3636 | void CScriptVarArray::native_Length(const CFunctionsScopePtr &c, void *data) {
|
---|
| 3637 | c->setReturnVar(newScriptVar(c->getArgument("this")->getArrayLength()));
|
---|
| 3638 | }
|
---|
| 3639 |
|
---|
| 3640 |
|
---|
| 3641 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3642 | /// CScriptVarRegExp
|
---|
| 3643 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3644 |
|
---|
| 3645 | #ifndef NO_REGEXP
|
---|
| 3646 |
|
---|
| 3647 | CScriptVarRegExp::CScriptVarRegExp(CTinyJS *Context, const string &Regexp, const string &Flags) : CScriptVarObject(Context, Context->regexpPrototype), regexp(Regexp), flags(Flags) {
|
---|
| 3648 | addChild("global", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Global, 0, 0, 0), 0);
|
---|
| 3649 | addChild("ignoreCase", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_IgnoreCase, 0, 0, 0), 0);
|
---|
| 3650 | addChild("multiline", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Multiline, 0, 0, 0), 0);
|
---|
| 3651 | addChild("sticky", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Sticky, 0, 0, 0), 0);
|
---|
| 3652 | addChild("regexp", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Source, 0, 0, 0), 0);
|
---|
| 3653 | addChild("lastIndex", newScriptVar(0));
|
---|
| 3654 | }
|
---|
| 3655 | CScriptVarRegExp::~CScriptVarRegExp() {}
|
---|
| 3656 | CScriptVarPtr CScriptVarRegExp::clone() { return new CScriptVarRegExp(*this); }
|
---|
| 3657 | bool CScriptVarRegExp::isRegExp() { return true; }
|
---|
| 3658 | //int CScriptVarRegExp::getInt() {return strtol(regexp.c_str(),0,0); }
|
---|
| 3659 | //bool CScriptVarRegExp::getBool() {return regexp.length()!=0;}
|
---|
| 3660 | //double CScriptVarRegExp::getDouble() {return strtod(regexp.c_str(),0);}
|
---|
| 3661 | //string CScriptVarRegExp::getString() { return "/"+regexp+"/"+flags; }
|
---|
| 3662 | //string CScriptVarRegExp::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getString(); }
|
---|
| 3663 | CScriptVarPtr CScriptVarRegExp::toString_CallBack(CScriptResult &execute, int radix) {
|
---|
| 3664 | return newScriptVar("/"+regexp+"/"+flags);
|
---|
| 3665 | }
|
---|
| 3666 | void CScriptVarRegExp::native_Global(const CFunctionsScopePtr &c, void *data) {
|
---|
| 3667 | c->setReturnVar(constScriptVar(Global()));
|
---|
| 3668 | }
|
---|
| 3669 | void CScriptVarRegExp::native_IgnoreCase(const CFunctionsScopePtr &c, void *data) {
|
---|
| 3670 | c->setReturnVar(constScriptVar(IgnoreCase()));
|
---|
| 3671 | }
|
---|
| 3672 | void CScriptVarRegExp::native_Multiline(const CFunctionsScopePtr &c, void *data) {
|
---|
| 3673 | c->setReturnVar(constScriptVar(Multiline()));
|
---|
| 3674 | }
|
---|
| 3675 | void CScriptVarRegExp::native_Sticky(const CFunctionsScopePtr &c, void *data) {
|
---|
| 3676 | c->setReturnVar(constScriptVar(Sticky()));
|
---|
| 3677 | }
|
---|
| 3678 | void CScriptVarRegExp::native_Source(const CFunctionsScopePtr &c, void *data) {
|
---|
| 3679 | c->setReturnVar(newScriptVar(regexp));
|
---|
| 3680 | }
|
---|
| 3681 | unsigned int CScriptVarRegExp::LastIndex() {
|
---|
| 3682 | CScriptVarPtr lastIndex = findChild("lastIndex");
|
---|
| 3683 | if(lastIndex) return lastIndex->toNumber().toInt32();
|
---|
| 3684 | return 0;
|
---|
| 3685 | }
|
---|
| 3686 | void CScriptVarRegExp::LastIndex(unsigned int Idx) {
|
---|
| 3687 | addChildOrReplace("lastIndex", newScriptVar((int)Idx));
|
---|
| 3688 | }
|
---|
| 3689 |
|
---|
| 3690 | CScriptVarPtr CScriptVarRegExp::exec( const string &Input, bool Test /*= false*/ )
|
---|
| 3691 | {
|
---|
| 3692 | regex::flag_type flags = regex_constants::ECMAScript;
|
---|
| 3693 | if(IgnoreCase()) flags |= regex_constants::icase;
|
---|
| 3694 | bool global = Global(), sticky = Sticky();
|
---|
| 3695 | unsigned int lastIndex = LastIndex();
|
---|
| 3696 | int offset = 0;
|
---|
| 3697 | if(global || sticky) {
|
---|
| 3698 | if(lastIndex > Input.length()) goto failed;
|
---|
| 3699 | offset=lastIndex;
|
---|
| 3700 | }
|
---|
| 3701 | {
|
---|
| 3702 | regex_constants::match_flag_type mflag = sticky?regex_constants::match_continuous:regex_constants::match_default;
|
---|
| 3703 | if(offset) mflag |= regex_constants::match_prev_avail;
|
---|
| 3704 | smatch match;
|
---|
| 3705 | if(regex_search(Input.begin()+offset, Input.end(), match, regex(regexp, flags), mflag) ) {
|
---|
| 3706 | LastIndex(offset+match.position()+match.str().length());
|
---|
| 3707 | if(Test) return constScriptVar(true);
|
---|
| 3708 |
|
---|
| 3709 | CScriptVarArrayPtr retVar = newScriptVar(Array);
|
---|
| 3710 | retVar->addChild("input", newScriptVar(Input));
|
---|
| 3711 | retVar->addChild("index", newScriptVar(match.position()));
|
---|
| 3712 | for(smatch::size_type idx=0; idx<match.size(); idx++)
|
---|
| 3713 | retVar->addChild(int2string(idx), newScriptVar(match[idx].str()));
|
---|
| 3714 | return retVar;
|
---|
| 3715 | }
|
---|
| 3716 | }
|
---|
| 3717 | failed:
|
---|
| 3718 | if(global || sticky)
|
---|
| 3719 | LastIndex(0);
|
---|
| 3720 | if(Test) return constScriptVar(false);
|
---|
| 3721 | return constScriptVar(Null);
|
---|
| 3722 | }
|
---|
| 3723 |
|
---|
| 3724 | const char * CScriptVarRegExp::ErrorStr( int Error )
|
---|
| 3725 | {
|
---|
| 3726 | switch(Error) {
|
---|
| 3727 | case regex_constants::error_badbrace: return "the expression contained an invalid count in a { } expression";
|
---|
| 3728 | case regex_constants::error_badrepeat: return "a repeat expression (one of '*', '?', '+', '{' in most contexts) was not preceded by an expression";
|
---|
| 3729 | case regex_constants::error_brace: return "the expression contained an unmatched '{' or '}'";
|
---|
| 3730 | case regex_constants::error_brack: return "the expression contained an unmatched '[' or ']'";
|
---|
| 3731 | case regex_constants::error_collate: return "the expression contained an invalid collating element name";
|
---|
| 3732 | case regex_constants::error_complexity: return "an attempted match failed because it was too complex";
|
---|
| 3733 | case regex_constants::error_ctype: return "the expression contained an invalid character class name";
|
---|
| 3734 | case regex_constants::error_escape: return "the expression contained an invalid escape sequence";
|
---|
| 3735 | case regex_constants::error_paren: return "the expression contained an unmatched '(' or ')'";
|
---|
| 3736 | case regex_constants::error_range: return "the expression contained an invalid character range specifier";
|
---|
| 3737 | case regex_constants::error_space: return "parsing a regular expression failed because there were not enough resources available";
|
---|
| 3738 | case regex_constants::error_stack: return "an attempted match failed because there was not enough memory available";
|
---|
| 3739 | case regex_constants::error_backref: return "the expression contained an invalid back reference";
|
---|
| 3740 | default: return "";
|
---|
| 3741 | }
|
---|
| 3742 | }
|
---|
| 3743 |
|
---|
| 3744 | #endif /* NO_REGEXP */
|
---|
| 3745 |
|
---|
| 3746 |
|
---|
| 3747 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3748 | /// CScriptVarDefaultIterator
|
---|
| 3749 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3750 |
|
---|
| 3751 | //declare_dummy_t(DefaultIterator);
|
---|
| 3752 | CScriptVarDefaultIterator::CScriptVarDefaultIterator(CTinyJS *Context, const CScriptVarPtr &Object, int Mode)
|
---|
| 3753 | : CScriptVarObject(Context, Context->iteratorPrototype), mode(Mode), object(Object) {
|
---|
| 3754 | object->keys(keys, true);
|
---|
| 3755 | pos = keys.begin();
|
---|
| 3756 | addChild("next", ::newScriptVar(context, this, &CScriptVarDefaultIterator::native_next, 0));
|
---|
| 3757 | }
|
---|
| 3758 | CScriptVarDefaultIterator::~CScriptVarDefaultIterator() {}
|
---|
| 3759 | CScriptVarPtr CScriptVarDefaultIterator::clone() { return new CScriptVarDefaultIterator(*this); }
|
---|
| 3760 | bool CScriptVarDefaultIterator::isIterator() {return true;}
|
---|
| 3761 | void CScriptVarDefaultIterator::native_next(const CFunctionsScopePtr &c, void *data) {
|
---|
| 3762 | if(pos==keys.end()) throw constScriptVar(StopIteration);
|
---|
| 3763 | CScriptVarPtr ret, ret0, ret1;
|
---|
| 3764 | if(mode&1) ret0 = newScriptVar(*pos);
|
---|
| 3765 | if(mode&2) ret1 = object->findChildWithStringChars(*pos);
|
---|
| 3766 | pos++;
|
---|
| 3767 | if(mode==3) {
|
---|
| 3768 | ret = newScriptVar(Array);
|
---|
| 3769 | ret->setArrayIndex(0, ret0);
|
---|
| 3770 | ret->setArrayIndex(1, ret1);
|
---|
| 3771 | } else if(mode==1)
|
---|
| 3772 | ret = ret0;
|
---|
| 3773 | else
|
---|
| 3774 | ret = ret1;
|
---|
| 3775 | c->setReturnVar(ret);
|
---|
| 3776 | }
|
---|
| 3777 |
|
---|
| 3778 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3779 | // CScriptVarFunction
|
---|
| 3780 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3781 |
|
---|
| 3782 | CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVarObject(Context, Context->functionPrototype), data(0) {
|
---|
| 3783 | setFunctionData(Data);
|
---|
| 3784 | }
|
---|
| 3785 | CScriptVarFunction::~CScriptVarFunction() { setFunctionData(0); }
|
---|
| 3786 | CScriptVarPtr CScriptVarFunction::clone() { return new CScriptVarFunction(*this); }
|
---|
| 3787 | bool CScriptVarFunction::isObject() { return true; }
|
---|
| 3788 | bool CScriptVarFunction::isFunction() { return true; }
|
---|
| 3789 | bool CScriptVarFunction::isPrimitive() { return false; }
|
---|
| 3790 |
|
---|
| 3791 | //string CScriptVarFunction::getString() {return "[ Function ]";}
|
---|
| 3792 | string CScriptVarFunction::getVarType() { return "function"; }
|
---|
| 3793 | string CScriptVarFunction::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
| 3794 | getParsableStringRecursionsCheck();
|
---|
| 3795 | string destination;
|
---|
| 3796 | destination.append("function ").append(data->name);
|
---|
| 3797 | // get list of arguments
|
---|
| 3798 | destination.append(data->getArgumentsString());
|
---|
| 3799 |
|
---|
| 3800 | if(isNative() || isBounded())
|
---|
| 3801 | destination.append("{ [native code] }");
|
---|
| 3802 | else {
|
---|
| 3803 | destination.append(CScriptToken::getParsableString(data->body, indentString, indent));
|
---|
| 3804 | if(!data->body.size() || data->body.front().token != '{')
|
---|
| 3805 | destination.append(";");
|
---|
| 3806 | }
|
---|
| 3807 | return destination;
|
---|
| 3808 | }
|
---|
| 3809 |
|
---|
| 3810 | CScriptVarPtr CScriptVarFunction::toString_CallBack(CScriptResult &execute, int radix){
|
---|
| 3811 | bool hasRecursion;
|
---|
| 3812 | return newScriptVar(getParsableString("", " ", 0, hasRecursion));
|
---|
| 3813 | }
|
---|
| 3814 |
|
---|
| 3815 | CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; }
|
---|
| 3816 |
|
---|
| 3817 | void CScriptVarFunction::setFunctionData(CScriptTokenDataFnc *Data) {
|
---|
| 3818 | if(data) { data->unref(); data = 0; }
|
---|
| 3819 | if(Data) {
|
---|
| 3820 | data = Data; data->ref();
|
---|
| 3821 | addChildOrReplace("length", newScriptVar((int)data->arguments.size()), 0);
|
---|
| 3822 | // can not add "name" here because name is a StingVar with length as getter
|
---|
| 3823 | // length-getter is a function with a function name -> endless recursion
|
---|
| 3824 | //addChildNoDup("name", newScriptVar(data->name), 0);
|
---|
| 3825 | }
|
---|
| 3826 | }
|
---|
| 3827 |
|
---|
| 3828 |
|
---|
| 3829 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3830 | /// CScriptVarFunctionBounded
|
---|
| 3831 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3832 |
|
---|
| 3833 | CScriptVarFunctionBounded::CScriptVarFunctionBounded(CScriptVarFunctionPtr BoundedFunction, CScriptVarPtr BoundedThis, const std::vector<CScriptVarPtr> &BoundedArguments)
|
---|
| 3834 | : CScriptVarFunction(BoundedFunction->getContext(), new CScriptTokenDataFnc) ,
|
---|
| 3835 | boundedFunction(BoundedFunction),
|
---|
| 3836 | boundedThis(BoundedThis),
|
---|
| 3837 | boundedArguments(BoundedArguments) {
|
---|
| 3838 | getFunctionData()->name = BoundedFunction->getFunctionData()->name;
|
---|
| 3839 | }
|
---|
| 3840 | CScriptVarFunctionBounded::~CScriptVarFunctionBounded(){}
|
---|
| 3841 | CScriptVarPtr CScriptVarFunctionBounded::clone() { return new CScriptVarFunctionBounded(*this); }
|
---|
| 3842 | bool CScriptVarFunctionBounded::isBounded() { return true; }
|
---|
| 3843 | void CScriptVarFunctionBounded::setTemporaryID_recursive( uint32_t ID ) {
|
---|
| 3844 | CScriptVarFunction::setTemporaryID_recursive(ID);
|
---|
| 3845 | boundedThis->setTemporaryID_recursive(ID);
|
---|
| 3846 | for(vector<CScriptVarPtr>::iterator it=boundedArguments.begin(); it!=boundedArguments.end(); ++it)
|
---|
| 3847 | (*it)->setTemporaryID_recursive(ID);
|
---|
| 3848 | }
|
---|
| 3849 |
|
---|
| 3850 | CScriptVarPtr CScriptVarFunctionBounded::callFunction( CScriptResult &execute, vector<CScriptVarPtr> &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis/*=0*/ )
|
---|
| 3851 | {
|
---|
| 3852 | vector<CScriptVarPtr> newArgs=boundedArguments;
|
---|
| 3853 | newArgs.insert(newArgs.end(), Arguments.begin(), Arguments.end());
|
---|
| 3854 | return context->callFunction(execute, boundedFunction, newArgs, newThis ? This : boundedThis, newThis);
|
---|
| 3855 | }
|
---|
| 3856 |
|
---|
| 3857 |
|
---|
| 3858 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3859 | /// CScriptVarFunctionNative
|
---|
| 3860 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3861 |
|
---|
| 3862 | CScriptVarFunctionNative::~CScriptVarFunctionNative() {}
|
---|
| 3863 | bool CScriptVarFunctionNative::isNative() { return true; }
|
---|
| 3864 |
|
---|
| 3865 |
|
---|
| 3866 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3867 | /// CScriptVarFunctionNativeCallback
|
---|
| 3868 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3869 |
|
---|
| 3870 | CScriptVarFunctionNativeCallback::~CScriptVarFunctionNativeCallback() {}
|
---|
| 3871 | CScriptVarPtr CScriptVarFunctionNativeCallback::clone() { return new CScriptVarFunctionNativeCallback(*this); }
|
---|
| 3872 | void CScriptVarFunctionNativeCallback::callFunction(const CFunctionsScopePtr &c) { jsCallback(c, jsUserData); }
|
---|
| 3873 |
|
---|
| 3874 |
|
---|
| 3875 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3876 | /// CScriptVarAccessor
|
---|
| 3877 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3878 |
|
---|
| 3879 | declare_dummy_t(Accessor);
|
---|
| 3880 | CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context) : CScriptVarObject(Context, Context->objectPrototype) { }
|
---|
| 3881 | CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context, JSCallback getterFnc, void *getterData, JSCallback setterFnc, void *setterData)
|
---|
| 3882 | : CScriptVarObject(Context)
|
---|
| 3883 | {
|
---|
| 3884 | if(getterFnc)
|
---|
| 3885 | addChild(TINYJS_ACCESSOR_GET_VAR, ::newScriptVar(Context, getterFnc, getterData), 0);
|
---|
| 3886 | if(setterFnc)
|
---|
| 3887 | addChild(TINYJS_ACCESSOR_SET_VAR, ::newScriptVar(Context, setterFnc, setterData), 0);
|
---|
| 3888 | }
|
---|
| 3889 |
|
---|
| 3890 | CScriptVarAccessor::CScriptVarAccessor( CTinyJS *Context, const CScriptVarFunctionPtr &getter, const CScriptVarFunctionPtr &setter) : CScriptVarObject(Context, Context->objectPrototype) {
|
---|
| 3891 | if(getter)
|
---|
| 3892 | addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0);
|
---|
| 3893 | if(setter)
|
---|
| 3894 | addChild(TINYJS_ACCESSOR_SET_VAR, setter, 0);
|
---|
| 3895 | }
|
---|
| 3896 |
|
---|
| 3897 | CScriptVarAccessor::~CScriptVarAccessor() {}
|
---|
| 3898 | CScriptVarPtr CScriptVarAccessor::clone() { return new CScriptVarAccessor(*this); }
|
---|
| 3899 | bool CScriptVarAccessor::isAccessor() { return true; }
|
---|
| 3900 | bool CScriptVarAccessor::isPrimitive() { return false; }
|
---|
| 3901 | string CScriptVarAccessor::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
| 3902 | return "";
|
---|
| 3903 | }
|
---|
| 3904 | string CScriptVarAccessor::getVarType() { return "accessor"; }
|
---|
| 3905 |
|
---|
| 3906 |
|
---|
| 3907 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3908 | /// CScriptVarScope
|
---|
| 3909 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3910 |
|
---|
| 3911 | declare_dummy_t(Scope);
|
---|
| 3912 | CScriptVarScope::~CScriptVarScope() {}
|
---|
| 3913 | CScriptVarPtr CScriptVarScope::clone() { return CScriptVarPtr(); }
|
---|
| 3914 | bool CScriptVarScope::isObject() { return false; }
|
---|
| 3915 | CScriptVarPtr CScriptVarScope::scopeVar() { return this; } ///< to create var like: var a = ...
|
---|
| 3916 | CScriptVarPtr CScriptVarScope::scopeLet() { return this; } ///< to create var like: let a = ...
|
---|
| 3917 | CScriptVarLinkWorkPtr CScriptVarScope::findInScopes(const string &childName) {
|
---|
| 3918 | return CScriptVar::findChild(childName);
|
---|
| 3919 | }
|
---|
| 3920 | CScriptVarScopePtr CScriptVarScope::getParent() { return CScriptVarScopePtr(); } ///< no Parent
|
---|
| 3921 |
|
---|
| 3922 |
|
---|
| 3923 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3924 | /// CScriptVarScopeFnc
|
---|
| 3925 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3926 |
|
---|
| 3927 | declare_dummy_t(ScopeFnc);
|
---|
| 3928 | CScriptVarScopeFnc::~CScriptVarScopeFnc() {}
|
---|
| 3929 | CScriptVarLinkWorkPtr CScriptVarScopeFnc::findInScopes(const string &childName) {
|
---|
| 3930 | CScriptVarLinkWorkPtr ret = findChild(childName);
|
---|
| 3931 | if( !ret ) {
|
---|
| 3932 | if(closure) ret = CScriptVarScopePtr(closure)->findInScopes(childName);
|
---|
| 3933 | else ret = context->getRoot()->findChild(childName);
|
---|
| 3934 | }
|
---|
| 3935 | return ret;
|
---|
| 3936 | }
|
---|
| 3937 |
|
---|
| 3938 | void CScriptVarScopeFnc::setReturnVar(const CScriptVarPtr &var) {
|
---|
| 3939 | addChildOrReplace(TINYJS_RETURN_VAR, var);
|
---|
| 3940 | }
|
---|
| 3941 |
|
---|
| 3942 | CScriptVarPtr CScriptVarScopeFnc::getParameter(const string &name) {
|
---|
| 3943 | return getArgument(name);
|
---|
| 3944 | }
|
---|
| 3945 |
|
---|
| 3946 | CScriptVarPtr CScriptVarScopeFnc::getParameter(int Idx) {
|
---|
| 3947 | return getArgument(Idx);
|
---|
| 3948 | }
|
---|
| 3949 | CScriptVarPtr CScriptVarScopeFnc::getArgument(const string &name) {
|
---|
| 3950 | return findChildOrCreate(name);
|
---|
| 3951 | }
|
---|
| 3952 | CScriptVarPtr CScriptVarScopeFnc::getArgument(int Idx) {
|
---|
| 3953 | CScriptVarLinkPtr arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR);
|
---|
| 3954 | if(arguments) arguments = arguments->getVarPtr()->findChild(int2string(Idx));
|
---|
| 3955 | return arguments ? arguments->getVarPtr() : constScriptVar(Undefined);
|
---|
| 3956 | }
|
---|
| 3957 | int CScriptVarScopeFnc::getParameterLength() {
|
---|
| 3958 | return getArgumentsLength();
|
---|
| 3959 | }
|
---|
| 3960 | int CScriptVarScopeFnc::getArgumentsLength() {
|
---|
| 3961 | CScriptVarLinkPtr arguments = findChild(TINYJS_ARGUMENTS_VAR);
|
---|
| 3962 | if(arguments) arguments = arguments->getVarPtr()->findChild("length");
|
---|
| 3963 | return arguments ? arguments.getter()->toNumber().toInt32() : 0;
|
---|
| 3964 | }
|
---|
| 3965 |
|
---|
| 3966 | void CScriptVarScopeFnc::throwError( ERROR_TYPES ErrorType, const string &message ) {
|
---|
| 3967 | throw newScriptVarError(context, ErrorType, message.c_str());
|
---|
| 3968 | }
|
---|
| 3969 |
|
---|
| 3970 |
|
---|
| 3971 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3972 | /// CScriptVarScopeLet
|
---|
| 3973 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3974 |
|
---|
| 3975 | declare_dummy_t(ScopeLet);
|
---|
| 3976 | CScriptVarScopeLet::CScriptVarScopeLet(const CScriptVarScopePtr &Parent) // constructor for LetScope
|
---|
| 3977 | : CScriptVarScope(Parent->getContext()), parent(addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0))
|
---|
| 3978 | , letExpressionInitMode(false) {}
|
---|
| 3979 |
|
---|
| 3980 | CScriptVarScopeLet::~CScriptVarScopeLet() {}
|
---|
| 3981 | CScriptVarPtr CScriptVarScopeLet::scopeVar() { // to create var like: var a = ...
|
---|
| 3982 | return getParent()->scopeVar();
|
---|
| 3983 | }
|
---|
| 3984 | CScriptVarScopePtr CScriptVarScopeLet::getParent() { return (CScriptVarPtr)parent; }
|
---|
| 3985 | CScriptVarLinkWorkPtr CScriptVarScopeLet::findInScopes(const string &childName) {
|
---|
| 3986 | CScriptVarLinkWorkPtr ret;
|
---|
| 3987 | if(letExpressionInitMode) {
|
---|
| 3988 | return getParent()->findInScopes(childName);
|
---|
| 3989 | } else {
|
---|
| 3990 | ret = findChild(childName);
|
---|
| 3991 | if( !ret ) ret = getParent()->findInScopes(childName);
|
---|
| 3992 | }
|
---|
| 3993 | return ret;
|
---|
| 3994 | }
|
---|
| 3995 |
|
---|
| 3996 |
|
---|
| 3997 | //////////////////////////////////////////////////////////////////////////
|
---|
| 3998 | /// CScriptVarScopeWith
|
---|
| 3999 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4000 |
|
---|
| 4001 | declare_dummy_t(ScopeWith);
|
---|
| 4002 | CScriptVarScopeWith::~CScriptVarScopeWith() {}
|
---|
| 4003 | CScriptVarPtr CScriptVarScopeWith::scopeLet() { // to create var like: let a = ...
|
---|
| 4004 | return getParent()->scopeLet();
|
---|
| 4005 | }
|
---|
| 4006 | CScriptVarLinkWorkPtr CScriptVarScopeWith::findInScopes(const string &childName) {
|
---|
| 4007 | if(childName == "this") return with;
|
---|
| 4008 | CScriptVarLinkWorkPtr ret = with->getVarPtr()->findChild(childName);
|
---|
| 4009 | if( !ret ) {
|
---|
| 4010 | ret = with->getVarPtr()->findChildInPrototypeChain(childName);
|
---|
| 4011 | if(ret) {
|
---|
| 4012 | ret(ret->getVarPtr(), ret->getName()); // recreate ret
|
---|
| 4013 | ret.setReferencedOwner(with->getVarPtr()); // fake referenced Owner
|
---|
| 4014 | }
|
---|
| 4015 | }
|
---|
| 4016 | if( !ret ) ret = getParent()->findInScopes(childName);
|
---|
| 4017 | return ret;
|
---|
| 4018 | }
|
---|
| 4019 |
|
---|
| 4020 |
|
---|
| 4021 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4022 | /// CTinyJS
|
---|
| 4023 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4024 |
|
---|
| 4025 | extern "C" void _registerFunctions(CTinyJS *tinyJS);
|
---|
| 4026 | extern "C" void _registerStringFunctions(CTinyJS *tinyJS);
|
---|
| 4027 | extern "C" void _registerMathFunctions(CTinyJS *tinyJS);
|
---|
| 4028 |
|
---|
| 4029 | CTinyJS::CTinyJS() {
|
---|
| 4030 | CScriptVarPtr var;
|
---|
| 4031 | t = 0;
|
---|
| 4032 | haveTry = false;
|
---|
| 4033 | first = 0;
|
---|
| 4034 | uniqueID = 0;
|
---|
| 4035 |
|
---|
| 4036 |
|
---|
| 4037 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4038 | // Object-Prototype
|
---|
| 4039 | // must be created as first object because this prototype is the base of all objects
|
---|
| 4040 | objectPrototype = newScriptVar(Object);
|
---|
| 4041 |
|
---|
| 4042 | // all objects have a prototype. Also the prototype of prototypes
|
---|
| 4043 | objectPrototype->addChild(TINYJS___PROTO___VAR, objectPrototype, 0);
|
---|
| 4044 |
|
---|
| 4045 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4046 | // Function-Prototype
|
---|
| 4047 | // must be created as second object because this is the base of all functions (also constructors)
|
---|
| 4048 | functionPrototype = newScriptVar(Object);
|
---|
| 4049 |
|
---|
| 4050 |
|
---|
| 4051 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4052 | // Scopes
|
---|
| 4053 | root = ::newScriptVar(this, Scope);
|
---|
| 4054 | scopes.push_back(root);
|
---|
| 4055 |
|
---|
| 4056 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4057 | // Add built-in classes
|
---|
| 4058 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4059 | // Object
|
---|
| 4060 | var = addNative("function Object()", this, &CTinyJS::native_Object, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4061 | objectPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4062 | addNative("function Object.getPrototypeOf(obj)", this, &CTinyJS::native_Object_getPrototypeOf);
|
---|
| 4063 | addNative("function Object.preventExtensions(obj)", this, &CTinyJS::native_Object_setObjectSecure);
|
---|
| 4064 | addNative("function Object.isExtensible(obj)", this, &CTinyJS::native_Object_isSecureObject);
|
---|
| 4065 | addNative("function Object.seel(obj)", this, &CTinyJS::native_Object_setObjectSecure, (void*)1);
|
---|
| 4066 | addNative("function Object.isSealed(obj)", this, &CTinyJS::native_Object_isSecureObject, (void*)1);
|
---|
| 4067 | addNative("function Object.freeze(obj)", this, &CTinyJS::native_Object_setObjectSecure, (void*)2);
|
---|
| 4068 | addNative("function Object.isFrozen(obj)", this, &CTinyJS::native_Object_isSecureObject, (void*)2);
|
---|
| 4069 | addNative("function Object.keys(obj)", this, &CTinyJS::native_Object_keys);
|
---|
| 4070 | addNative("function Object.getOwnPropertyNames(obj)", this, &CTinyJS::native_Object_keys, (void*)1);
|
---|
| 4071 | addNative("function Object.getOwnPropertyDescriptor(obj,name)", this, &CTinyJS::native_Object_getOwnPropertyDescriptor);
|
---|
| 4072 | addNative("function Object.defineProperty(obj,name,attributes)", this, &CTinyJS::native_Object_defineProperty);
|
---|
| 4073 | addNative("function Object.defineProperties(obj,properties)", this, &CTinyJS::native_Object_defineProperties);
|
---|
| 4074 | addNative("function Object.create(obj,properties)", this, &CTinyJS::native_Object_defineProperties, (void*)1);
|
---|
| 4075 |
|
---|
| 4076 | addNative("function Object.prototype.hasOwnProperty(prop)", this, &CTinyJS::native_Object_prototype_hasOwnProperty);
|
---|
| 4077 | objectPrototype_valueOf = addNative("function Object.prototype.valueOf()", this, &CTinyJS::native_Object_prototype_valueOf);
|
---|
| 4078 | objectPrototype_toString = addNative("function Object.prototype.toString(radix)", this, &CTinyJS::native_Object_prototype_toString);
|
---|
| 4079 | pseudo_refered.push_back(&objectPrototype);
|
---|
| 4080 | pseudo_refered.push_back(&objectPrototype_valueOf);
|
---|
| 4081 | pseudo_refered.push_back(&objectPrototype_toString);
|
---|
| 4082 |
|
---|
| 4083 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4084 | // Array
|
---|
| 4085 | var = addNative("function Array()", this, &CTinyJS::native_Array, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4086 | arrayPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4087 | arrayPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
| 4088 | arrayPrototype->addChild("toString", objectPrototype_toString);
|
---|
| 4089 | pseudo_refered.push_back(&arrayPrototype);
|
---|
| 4090 | var = addNative("function Array.__constructor__()", this, &CTinyJS::native_Array, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
| 4091 | var->getFunctionData()->name = "Array";
|
---|
| 4092 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4093 | // String
|
---|
| 4094 | var = addNative("function String()", this, &CTinyJS::native_String, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4095 | stringPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4096 | stringPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
| 4097 | stringPrototype->addChild("toString", objectPrototype_toString);
|
---|
| 4098 | pseudo_refered.push_back(&stringPrototype);
|
---|
| 4099 | var = addNative("function String.__constructor__()", this, &CTinyJS::native_String, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
| 4100 | var->getFunctionData()->name = "String";
|
---|
| 4101 |
|
---|
| 4102 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4103 | // RegExp
|
---|
| 4104 | #ifndef NO_REGEXP
|
---|
| 4105 | var = addNative("function RegExp()", this, &CTinyJS::native_RegExp, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4106 | regexpPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4107 | regexpPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
| 4108 | regexpPrototype->addChild("toString", objectPrototype_toString);
|
---|
| 4109 | pseudo_refered.push_back(®expPrototype);
|
---|
| 4110 | #endif /* NO_REGEXP */
|
---|
| 4111 |
|
---|
| 4112 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4113 | // Number
|
---|
| 4114 | var = addNative("function Number()", this, &CTinyJS::native_Number, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4115 | var->addChild("NaN", constNaN = newScriptVarNumber(this, NaN), SCRIPTVARLINK_CONSTANT);
|
---|
| 4116 | var->addChild("MAX_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits<double>::max()), SCRIPTVARLINK_CONSTANT);
|
---|
| 4117 | var->addChild("MIN_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits<double>::min()), SCRIPTVARLINK_CONSTANT);
|
---|
| 4118 | var->addChild("POSITIVE_INFINITY", constInfinityPositive = newScriptVarNumber(this, InfinityPositive), SCRIPTVARLINK_CONSTANT);
|
---|
| 4119 | var->addChild("NEGATIVE_INFINITY", constInfinityNegative = newScriptVarNumber(this, InfinityNegative), SCRIPTVARLINK_CONSTANT);
|
---|
| 4120 | numberPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4121 | numberPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
| 4122 | numberPrototype->addChild("toString", objectPrototype_toString);
|
---|
| 4123 | pseudo_refered.push_back(&numberPrototype);
|
---|
| 4124 | pseudo_refered.push_back(&constNaN);
|
---|
| 4125 | pseudo_refered.push_back(&constInfinityPositive);
|
---|
| 4126 | pseudo_refered.push_back(&constInfinityNegative);
|
---|
| 4127 | var = addNative("function Number.__constructor__()", this, &CTinyJS::native_Number, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
| 4128 | var->getFunctionData()->name = "Number";
|
---|
| 4129 |
|
---|
| 4130 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4131 | // Boolean
|
---|
| 4132 | var = addNative("function Boolean()", this, &CTinyJS::native_Boolean, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4133 | booleanPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4134 | booleanPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
| 4135 | booleanPrototype->addChild("toString", objectPrototype_toString);
|
---|
| 4136 | pseudo_refered.push_back(&booleanPrototype);
|
---|
| 4137 | var = addNative("function Boolean.__constructor__()", this, &CTinyJS::native_Boolean, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
| 4138 | var->getFunctionData()->name = "Boolean";
|
---|
| 4139 |
|
---|
| 4140 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4141 | // Iterator
|
---|
| 4142 | var = addNative("function Iterator(obj,mode)", this, &CTinyJS::native_Iterator, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4143 | iteratorPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4144 | pseudo_refered.push_back(&iteratorPrototype);
|
---|
| 4145 |
|
---|
| 4146 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4147 | // Function
|
---|
| 4148 | var = addNative("function Function(params, body)", this, &CTinyJS::native_Function, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4149 | var->addChildOrReplace(TINYJS_PROTOTYPE_CLASS, functionPrototype);
|
---|
| 4150 | addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_prototype_call);
|
---|
| 4151 | addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_prototype_apply);
|
---|
| 4152 | addNative("function Function.prototype.bind(objc, args)", this, &CTinyJS::native_Function_prototype_bind);
|
---|
| 4153 | functionPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
| 4154 | functionPrototype->addChild("toString", objectPrototype_toString);
|
---|
| 4155 | pseudo_refered.push_back(&functionPrototype);
|
---|
| 4156 |
|
---|
| 4157 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4158 | // Error
|
---|
| 4159 | var = addNative("function Error(message, fileName, lineNumber, column)", this, &CTinyJS::native_Error, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4160 | errorPrototypes[Error] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4161 | errorPrototypes[Error]->addChild("message", newScriptVar(""));
|
---|
| 4162 | errorPrototypes[Error]->addChild("name", newScriptVar("Error"));
|
---|
| 4163 | errorPrototypes[Error]->addChild("fileName", newScriptVar(""));
|
---|
| 4164 | errorPrototypes[Error]->addChild("lineNumber", newScriptVar(-1)); // -1 means not viable
|
---|
| 4165 | errorPrototypes[Error]->addChild("column", newScriptVar(-1)); // -1 means not viable
|
---|
| 4166 |
|
---|
| 4167 | var = addNative("function EvalError(message, fileName, lineNumber, column)", this, &CTinyJS::native_EvalError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4168 | errorPrototypes[EvalError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4169 | errorPrototypes[EvalError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
| 4170 | errorPrototypes[EvalError]->addChild("name", newScriptVar("EvalError"));
|
---|
| 4171 |
|
---|
| 4172 | var = addNative("function RangeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_RangeError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4173 | errorPrototypes[RangeError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4174 | errorPrototypes[RangeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
| 4175 | errorPrototypes[RangeError]->addChild("name", newScriptVar("RangeError"));
|
---|
| 4176 |
|
---|
| 4177 | var = addNative("function ReferenceError(message, fileName, lineNumber, column)", this, &CTinyJS::native_ReferenceError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4178 | errorPrototypes[ReferenceError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4179 | errorPrototypes[ReferenceError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
| 4180 | errorPrototypes[ReferenceError]->addChild("name", newScriptVar("ReferenceError"));
|
---|
| 4181 |
|
---|
| 4182 | var = addNative("function SyntaxError(message, fileName, lineNumber, column)", this, &CTinyJS::native_SyntaxError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4183 | errorPrototypes[SyntaxError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4184 | errorPrototypes[SyntaxError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
| 4185 | errorPrototypes[SyntaxError]->addChild("name", newScriptVar("SyntaxError"));
|
---|
| 4186 |
|
---|
| 4187 | var = addNative("function TypeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_TypeError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
| 4188 | errorPrototypes[TypeError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4189 | errorPrototypes[TypeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
| 4190 | errorPrototypes[TypeError]->addChild("name", newScriptVar("TypeError"));
|
---|
| 4191 |
|
---|
| 4192 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4193 | // add global built-in vars & constants
|
---|
| 4194 | root->addChild("undefined", constUndefined = newScriptVarUndefined(this), SCRIPTVARLINK_CONSTANT);
|
---|
| 4195 | pseudo_refered.push_back(&constUndefined);
|
---|
| 4196 | constNull = newScriptVarNull(this); pseudo_refered.push_back(&constNull);
|
---|
| 4197 | root->addChild("NaN", constNaN, SCRIPTVARLINK_CONSTANT);
|
---|
| 4198 | root->addChild("Infinity", constInfinityPositive, SCRIPTVARLINK_CONSTANT);
|
---|
| 4199 | root->addChild("StopIteration", constStopIteration=newScriptVar(Object, var=newScriptVar(Object)), SCRIPTVARLINK_CONSTANT);
|
---|
| 4200 | constStopIteration->addChild(TINYJS_PROTOTYPE_CLASS, var, SCRIPTVARLINK_CONSTANT); pseudo_refered.push_back(&constStopIteration);
|
---|
| 4201 | constNegativZero = newScriptVarNumber(this, NegativeZero); pseudo_refered.push_back(&constNegativZero);
|
---|
| 4202 | constFalse = newScriptVarBool(this, false); pseudo_refered.push_back(&constFalse);
|
---|
| 4203 | constTrue = newScriptVarBool(this, true); pseudo_refered.push_back(&constTrue);
|
---|
| 4204 |
|
---|
| 4205 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4206 | // add global functions
|
---|
| 4207 | addNative("function eval(jsCode)", this, &CTinyJS::native_eval);
|
---|
| 4208 | addNative("function isNaN(objc)", this, &CTinyJS::native_isNAN);
|
---|
| 4209 | addNative("function isFinite(objc)", this, &CTinyJS::native_isFinite);
|
---|
| 4210 | addNative("function parseInt(string, radix)", this, &CTinyJS::native_parseInt);
|
---|
| 4211 | addNative("function parseFloat(string)", this, &CTinyJS::native_parseFloat);
|
---|
| 4212 |
|
---|
| 4213 |
|
---|
| 4214 | addNative("function JSON.parse(text, reviver)", this, &CTinyJS::native_JSON_parse);
|
---|
| 4215 |
|
---|
| 4216 | _registerFunctions(this);
|
---|
| 4217 | _registerStringFunctions(this);
|
---|
| 4218 | _registerMathFunctions(this);
|
---|
| 4219 | }
|
---|
| 4220 |
|
---|
| 4221 | CTinyJS::~CTinyJS() {
|
---|
| 4222 | ASSERT(!t);
|
---|
| 4223 | for(vector<CScriptVarPtr*>::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it)
|
---|
| 4224 | **it = CScriptVarPtr();
|
---|
| 4225 | for(int i=Error; i<ERROR_COUNT; i++)
|
---|
| 4226 | errorPrototypes[i] = CScriptVarPtr();
|
---|
| 4227 | root->removeAllChildren();
|
---|
| 4228 | scopes.clear();
|
---|
| 4229 | ClearUnreferedVars();
|
---|
| 4230 | root = CScriptVarPtr();
|
---|
| 4231 | #ifdef _DEBUG
|
---|
| 4232 | for(CScriptVar *p = first; p; p=p->next)
|
---|
| 4233 | printf("%p\n", p);
|
---|
| 4234 | #endif
|
---|
| 4235 | #if DEBUG_MEMORY
|
---|
| 4236 | show_allocated();
|
---|
| 4237 | #endif
|
---|
| 4238 | }
|
---|
| 4239 |
|
---|
| 4240 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4241 | /// throws an Error & Exception
|
---|
| 4242 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4243 |
|
---|
| 4244 | void CTinyJS::throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const string &message ) {
|
---|
| 4245 | if(execute && haveTry) {
|
---|
| 4246 | execute.set(CScriptResult::Throw, newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), t->currentLine(), t->currentColumn()));
|
---|
| 4247 | return;
|
---|
| 4248 | }
|
---|
| 4249 | throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn());
|
---|
| 4250 | }
|
---|
| 4251 | void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message ) {
|
---|
| 4252 | throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn());
|
---|
| 4253 | }
|
---|
| 4254 |
|
---|
| 4255 | void CTinyJS::throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){
|
---|
| 4256 | if(execute && haveTry) {
|
---|
| 4257 | execute.set(CScriptResult::Throw, newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), Pos.currentLine(), Pos.currentColumn()));
|
---|
| 4258 | return;
|
---|
| 4259 | }
|
---|
| 4260 | throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn());
|
---|
| 4261 | }
|
---|
| 4262 | void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){
|
---|
| 4263 | throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn());
|
---|
| 4264 | }
|
---|
| 4265 |
|
---|
| 4266 | //////////////////////////////////////////////////////////////////////////
|
---|
| 4267 |
|
---|
| 4268 | void CTinyJS::trace() {
|
---|
| 4269 | root->trace();
|
---|
| 4270 | }
|
---|
| 4271 |
|
---|
| 4272 | void CTinyJS::execute(CScriptTokenizer &Tokenizer) {
|
---|
| 4273 | evaluateComplex(Tokenizer);
|
---|
| 4274 | }
|
---|
| 4275 |
|
---|
| 4276 | void CTinyJS::execute(const char *Code, const string &File, int Line, int Column) {
|
---|
| 4277 | evaluateComplex(Code, File, Line, Column);
|
---|
| 4278 | }
|
---|
| 4279 |
|
---|
| 4280 | void CTinyJS::execute(const string &Code, const string &File, int Line, int Column) {
|
---|
| 4281 | evaluateComplex(Code, File, Line, Column);
|
---|
| 4282 | }
|
---|
| 4283 |
|
---|
| 4284 | CScriptVarLinkPtr CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) {
|
---|
| 4285 | t = &Tokenizer;
|
---|
| 4286 | CScriptResult execute;
|
---|
| 4287 | try {
|
---|
| 4288 | do {
|
---|
| 4289 | execute_statement(execute);
|
---|
| 4290 | while (t->tk==';') t->match(';'); // skip empty statements
|
---|
| 4291 | } while (t->tk!=LEX_EOF);
|
---|
| 4292 | } catch (...) {
|
---|
| 4293 | haveTry = false;
|
---|
| 4294 | t=0; // clean up Tokenizer
|
---|
| 4295 | throw; //
|
---|
| 4296 | }
|
---|
| 4297 | t=0;
|
---|
| 4298 | ClearUnreferedVars(execute.value);
|
---|
| 4299 |
|
---|
| 4300 | uint32_t UniqueID = getUniqueID();
|
---|
| 4301 | setTemporaryID_recursive(UniqueID);
|
---|
| 4302 | if(execute.value) execute.value->setTemporaryID_recursive(UniqueID);
|
---|
| 4303 | for(CScriptVar *p = first; p; p=p->next)
|
---|
| 4304 | {
|
---|
| 4305 | if(p->temporaryID != UniqueID)
|
---|
| 4306 | printf("%p\n", p);
|
---|
| 4307 | }
|
---|
| 4308 |
|
---|
| 4309 | if (execute.value)
|
---|
| 4310 | return CScriptVarLinkPtr(execute.value);
|
---|
| 4311 | // return undefined...
|
---|
| 4312 | return CScriptVarLinkPtr(constScriptVar(Undefined));
|
---|
| 4313 | }
|
---|
| 4314 | CScriptVarLinkPtr CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) {
|
---|
| 4315 | CScriptTokenizer Tokenizer(Code, File, Line, Column);
|
---|
| 4316 | return evaluateComplex(Tokenizer);
|
---|
| 4317 | }
|
---|
| 4318 | CScriptVarLinkPtr CTinyJS::evaluateComplex(const string &Code, const string &File, int Line, int Column) {
|
---|
| 4319 | CScriptTokenizer Tokenizer(Code.c_str(), File, Line, Column);
|
---|
| 4320 | return evaluateComplex(Tokenizer);
|
---|
| 4321 | }
|
---|
| 4322 |
|
---|
| 4323 | string CTinyJS::evaluate(CScriptTokenizer &Tokenizer) {
|
---|
| 4324 | return evaluateComplex(Tokenizer)->toString();
|
---|
| 4325 | }
|
---|
| 4326 | string CTinyJS::evaluate(const char *Code, const string &File, int Line, int Column) {
|
---|
| 4327 | return evaluateComplex(Code, File, Line, Column)->toString();
|
---|
| 4328 | }
|
---|
| 4329 | string CTinyJS::evaluate(const string &Code, const string &File, int Line, int Column) {
|
---|
| 4330 | return evaluate(Code.c_str(), File, Line, Column);
|
---|
| 4331 | }
|
---|
| 4332 |
|
---|
| 4333 | CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata, int LinkFlags) {
|
---|
| 4334 | return addNative(funcDesc, ::newScriptVar(this, ptr, userdata), LinkFlags);
|
---|
| 4335 | }
|
---|
| 4336 |
|
---|
| 4337 | CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVarFunctionNativePtr Var, int LinkFlags) {
|
---|
| 4338 | CScriptLex lex(funcDesc.c_str());
|
---|
| 4339 | CScriptVarPtr base = root;
|
---|
| 4340 |
|
---|
| 4341 | lex.match(LEX_R_FUNCTION);
|
---|
| 4342 | string funcName = lex.tkStr;
|
---|
| 4343 | lex.match(LEX_ID);
|
---|
| 4344 | /* Check for dots, we might want to do something like function String.substring ... */
|
---|
| 4345 | while (lex.tk == '.') {
|
---|
| 4346 | lex.match('.');
|
---|
| 4347 | CScriptVarLinkPtr link = base->findChild(funcName);
|
---|
| 4348 | // if it doesn't exist, make an object class
|
---|
| 4349 | if (!link) link = base->addChild(funcName, newScriptVar(Object));
|
---|
| 4350 | base = link->getVarPtr();
|
---|
| 4351 | funcName = lex.tkStr;
|
---|
| 4352 | lex.match(LEX_ID);
|
---|
| 4353 | }
|
---|
| 4354 |
|
---|
| 4355 | auto_ptr<CScriptTokenDataFnc> pFunctionData(new CScriptTokenDataFnc);
|
---|
| 4356 | pFunctionData->name = funcName;
|
---|
| 4357 | lex.match('(');
|
---|
| 4358 | while (lex.tk!=')') {
|
---|
| 4359 | pFunctionData->arguments.push_back(CScriptToken(LEX_ID, lex.tkStr));
|
---|
| 4360 | lex.match(LEX_ID);
|
---|
| 4361 | if (lex.tk!=')') lex.match(',',')');
|
---|
| 4362 | }
|
---|
| 4363 | lex.match(')');
|
---|
| 4364 | Var->setFunctionData(pFunctionData.release());
|
---|
| 4365 | Var->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE);
|
---|
| 4366 |
|
---|
| 4367 | base->addChild(funcName, Var, LinkFlags);
|
---|
| 4368 | return Var;
|
---|
| 4369 |
|
---|
| 4370 | }
|
---|
| 4371 |
|
---|
| 4372 | CScriptVarLinkWorkPtr CTinyJS::parseFunctionDefinition(const CScriptToken &FncToken) {
|
---|
| 4373 | const CScriptTokenDataFnc &Fnc = FncToken.Fnc();
|
---|
| 4374 | // string fncName = (FncToken.token == LEX_T_FUNCTION_OPERATOR) ? TINYJS_TEMP_NAME : Fnc.name;
|
---|
| 4375 | CScriptVarLinkWorkPtr funcVar(newScriptVar((CScriptTokenDataFnc*)&Fnc), Fnc.name);
|
---|
| 4376 | if(scope() != root)
|
---|
| 4377 | funcVar->getVarPtr()->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0);
|
---|
| 4378 | funcVar->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE);
|
---|
| 4379 | return funcVar;
|
---|
| 4380 | }
|
---|
| 4381 |
|
---|
| 4382 | CScriptVarLinkWorkPtr CTinyJS::parseFunctionsBodyFromString(const string &ArgumentList, const string &FncBody) {
|
---|
| 4383 | string Fnc = "function ("+ArgumentList+"){"+FncBody+"}";
|
---|
| 4384 | CScriptTokenizer tokenizer(Fnc.c_str());
|
---|
| 4385 | return parseFunctionDefinition(tokenizer.getToken());
|
---|
| 4386 | }
|
---|
| 4387 | CScriptVarPtr CTinyJS::callFunction(const CScriptVarFunctionPtr &Function, vector<CScriptVarPtr> &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) {
|
---|
| 4388 | CScriptResult execute;
|
---|
| 4389 | CScriptVarPtr retVar = callFunction(execute, Function, Arguments, This, newThis);
|
---|
| 4390 | execute.cThrow();
|
---|
| 4391 | return retVar;
|
---|
| 4392 | }
|
---|
| 4393 |
|
---|
| 4394 | CScriptVarPtr CTinyJS::callFunction(CScriptResult &execute, const CScriptVarFunctionPtr &Function, vector<CScriptVarPtr> &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) {
|
---|
| 4395 | ASSERT(Function && Function->isFunction());
|
---|
| 4396 |
|
---|
| 4397 | if(Function->isBounded()) return CScriptVarFunctionBoundedPtr(Function)->callFunction(execute, Arguments, This, newThis);
|
---|
| 4398 |
|
---|
| 4399 | CScriptTokenDataFnc *Fnc = Function->getFunctionData();
|
---|
| 4400 | CScriptVarScopeFncPtr functionRoot(::newScriptVar(this, ScopeFnc, CScriptVarPtr(Function->findChild(TINYJS_FUNCTION_CLOSURE_VAR))));
|
---|
| 4401 | if(Fnc->name.size()) functionRoot->addChild(Fnc->name, Function);
|
---|
| 4402 | functionRoot->addChild("this", This);
|
---|
| 4403 | CScriptVarPtr arguments = functionRoot->addChild(TINYJS_ARGUMENTS_VAR, newScriptVar(Object));
|
---|
| 4404 |
|
---|
| 4405 | CScriptResult function_execute;
|
---|
| 4406 | int length_proto = Fnc->arguments.size();
|
---|
| 4407 | int length_arguments = Arguments.size();
|
---|
| 4408 | int length = max(length_proto, length_arguments);
|
---|
| 4409 | for(int arguments_idx = 0; arguments_idx<length; ++arguments_idx) {
|
---|
| 4410 | string arguments_idx_str = int2string(arguments_idx);
|
---|
| 4411 | CScriptVarLinkWorkPtr value;
|
---|
| 4412 | if(arguments_idx < length_arguments) {
|
---|
| 4413 | value = arguments->addChild(arguments_idx_str, Arguments[arguments_idx]);
|
---|
| 4414 | } else {
|
---|
| 4415 | value = constScriptVar(Undefined);
|
---|
| 4416 | }
|
---|
| 4417 | if(arguments_idx < length_proto) {
|
---|
| 4418 | CScriptToken &FncArguments = Fnc->arguments[arguments_idx];
|
---|
| 4419 | if(FncArguments.token == LEX_ID)
|
---|
| 4420 | functionRoot->addChildOrReplace(FncArguments.String(), value);
|
---|
| 4421 | else
|
---|
| 4422 | assign_destructuring_var(functionRoot, FncArguments.DestructuringVar(), value, function_execute);
|
---|
| 4423 | }
|
---|
| 4424 | }
|
---|
| 4425 | arguments->addChild("length", newScriptVar(length_arguments));
|
---|
| 4426 |
|
---|
| 4427 | // execute function!
|
---|
| 4428 | // add the function's execute space to the symbol table so we can recurse
|
---|
| 4429 | CScopeControl ScopeControl(this);
|
---|
| 4430 | ScopeControl.addFncScope(functionRoot);
|
---|
| 4431 | if (Function->isNative()) {
|
---|
| 4432 | try {
|
---|
| 4433 | CScriptVarFunctionNativePtr(Function)->callFunction(functionRoot);
|
---|
| 4434 | CScriptVarLinkPtr ret = functionRoot->findChild(TINYJS_RETURN_VAR);
|
---|
| 4435 | function_execute.set(CScriptResult::Return, ret ? CScriptVarPtr(ret) : constUndefined);
|
---|
| 4436 | } catch (CScriptVarPtr v) {
|
---|
| 4437 | if(haveTry) {
|
---|
| 4438 | function_execute.set(CScriptResult::Throw, v);
|
---|
| 4439 | } else if(v->isError()) {
|
---|
| 4440 | CScriptException *err = CScriptVarErrorPtr(v)->toCScriptException();
|
---|
| 4441 | if(err->fileName.empty()) err->fileName = "native function '"+Function->getFunctionData()->name+"'";
|
---|
| 4442 | throw err;
|
---|
| 4443 | }
|
---|
| 4444 | else
|
---|
| 4445 | throw new CScriptException(Error, v->toString(function_execute)+"' in: native function '"+Function->getFunctionData()->name+"'");
|
---|
| 4446 | }
|
---|
| 4447 | } else {
|
---|
| 4448 | /* we just want to execute the block, but something could
|
---|
| 4449 | * have messed up and left us with the wrong ScriptLex, so
|
---|
| 4450 | * we want to be careful here... */
|
---|
| 4451 | string oldFile = t->currentFile;
|
---|
| 4452 | t->currentFile = Fnc->file;
|
---|
| 4453 | t->pushTokenScope(Fnc->body);
|
---|
| 4454 | if(Fnc->body.front().token == '{')
|
---|
| 4455 | execute_block(function_execute);
|
---|
| 4456 | else {
|
---|
| 4457 | CScriptVarPtr ret = execute_base(function_execute);
|
---|
| 4458 | if(function_execute) function_execute.set(CScriptResult::Return, ret);
|
---|
| 4459 | }
|
---|
| 4460 | t->currentFile = oldFile;
|
---|
| 4461 |
|
---|
| 4462 | // because return will probably have called this, and set execute to false
|
---|
| 4463 | }
|
---|
| 4464 | if(function_execute.isReturnNormal()) {
|
---|
| 4465 | if(newThis) *newThis = functionRoot->findChild("this");
|
---|
| 4466 | if(function_execute.isReturn()) {
|
---|
| 4467 | CScriptVarPtr ret = function_execute.value;
|
---|
| 4468 | return ret;
|
---|
| 4469 | }
|
---|
| 4470 | } else
|
---|
| 4471 | execute = function_execute;
|
---|
| 4472 | return constScriptVar(Undefined);
|
---|
| 4473 | }
|
---|
| 4474 |
|
---|
| 4475 | CScriptVarPtr CTinyJS::mathsOp(CScriptResult &execute, const CScriptVarPtr &A, const CScriptVarPtr &B, int op) {
|
---|
| 4476 | if(!execute) return constUndefined;
|
---|
| 4477 | if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
|
---|
| 4478 | // check type first
|
---|
| 4479 | if( (A->getVarType() == B->getVarType()) ^ (op == LEX_TYPEEQUAL)) return constFalse;
|
---|
| 4480 | // check value second
|
---|
| 4481 | return mathsOp(execute, A, B, op == LEX_TYPEEQUAL ? LEX_EQUAL : LEX_NEQUAL);
|
---|
| 4482 | }
|
---|
| 4483 | if (!A->isPrimitive() && !B->isPrimitive()) { // Objects both
|
---|
| 4484 | // check pointers
|
---|
| 4485 | switch (op) {
|
---|
| 4486 | case LEX_EQUAL: return constScriptVar(A==B);
|
---|
| 4487 | case LEX_NEQUAL: return constScriptVar(A!=B);
|
---|
| 4488 | }
|
---|
| 4489 | }
|
---|
| 4490 |
|
---|
| 4491 | CScriptVarPtr a = A->toPrimitive_hintNumber(execute);
|
---|
| 4492 | CScriptVarPtr b = B->toPrimitive_hintNumber(execute);
|
---|
| 4493 | if(!execute) return constUndefined;
|
---|
| 4494 | // do maths...
|
---|
| 4495 | bool a_isString = a->isString();
|
---|
| 4496 | bool b_isString = b->isString();
|
---|
| 4497 | // both a String or one a String and op='+'
|
---|
| 4498 | if( (a_isString && b_isString) || ((a_isString || b_isString) && op == '+')) {
|
---|
| 4499 | string da = a->isNull() ? "" : a->toString(execute);
|
---|
| 4500 | string db = b->isNull() ? "" : b->toString(execute);
|
---|
| 4501 | switch (op) {
|
---|
| 4502 | case '+': return newScriptVar(da+db);
|
---|
| 4503 | case LEX_EQUAL: return constScriptVar(da==db);
|
---|
| 4504 | case LEX_NEQUAL: return constScriptVar(da!=db);
|
---|
| 4505 | case '<': return constScriptVar(da<db);
|
---|
| 4506 | case LEX_LEQUAL: return constScriptVar(da<=db);
|
---|
| 4507 | case '>': return constScriptVar(da>db);
|
---|
| 4508 | case LEX_GEQUAL: return constScriptVar(da>=db);
|
---|
| 4509 | }
|
---|
| 4510 | }
|
---|
| 4511 | // special for undefined and null --> every true: undefined==undefined, undefined==null, null==undefined and null=null
|
---|
| 4512 | else if( (a->isUndefined() || a->isNull()) && (b->isUndefined() || b->isNull()) ) {
|
---|
| 4513 | switch (op) {
|
---|
| 4514 | case LEX_EQUAL: return constScriptVar(true);
|
---|
| 4515 | case LEX_NEQUAL:
|
---|
| 4516 | case LEX_GEQUAL:
|
---|
| 4517 | case LEX_LEQUAL:
|
---|
| 4518 | case '<':
|
---|
| 4519 | case '>': return constScriptVar(false);
|
---|
| 4520 | }
|
---|
| 4521 | }
|
---|
| 4522 | CNumber da = a->toNumber();
|
---|
| 4523 | CNumber db = b->toNumber();
|
---|
| 4524 | switch (op) {
|
---|
| 4525 | case '+': return a->newScriptVar(da+db);
|
---|
| 4526 | case '-': return a->newScriptVar(da-db);
|
---|
| 4527 | case '*': return a->newScriptVar(da*db);
|
---|
| 4528 | case '/': return a->newScriptVar(da/db);
|
---|
| 4529 | case '%': return a->newScriptVar(da%db);
|
---|
| 4530 | case '&': return a->newScriptVar(da.toInt32()&db.toInt32());
|
---|
| 4531 | case '|': return a->newScriptVar(da.toInt32()|db.toInt32());
|
---|
| 4532 | case '^': return a->newScriptVar(da.toInt32()^db.toInt32());
|
---|
| 4533 | case '~': return a->newScriptVar(~da);
|
---|
| 4534 | case LEX_LSHIFT: return a->newScriptVar(da<<db);
|
---|
| 4535 | case LEX_RSHIFT: return a->newScriptVar(da>>db);
|
---|
| 4536 | case LEX_RSHIFTU: return a->newScriptVar(da.ushift(db));
|
---|
| 4537 | case LEX_EQUAL: return a->constScriptVar(da==db);
|
---|
| 4538 | case LEX_NEQUAL: return a->constScriptVar(da!=db);
|
---|
| 4539 | case '<': return a->constScriptVar(da<db);
|
---|
| 4540 | case LEX_LEQUAL: return a->constScriptVar(da<=db);
|
---|
| 4541 | case '>': return a->constScriptVar(da>db);
|
---|
| 4542 | case LEX_GEQUAL: return a->constScriptVar(da>=db);
|
---|
| 4543 | default: throw new CScriptException("This operation not supported on the int datatype");
|
---|
| 4544 | }
|
---|
| 4545 | }
|
---|
| 4546 |
|
---|
| 4547 | void CTinyJS::assign_destructuring_var(const CScriptVarPtr &Scope, const CScriptTokenDataDestructuringVar &Objc, const CScriptVarPtr &Val, CScriptResult &execute) {
|
---|
| 4548 | if(!execute) return;
|
---|
| 4549 | if(Objc.vars.size() == 1) {
|
---|
| 4550 | if(Scope)
|
---|
| 4551 | Scope->addChildOrReplace(Objc.vars.front().second, Val);
|
---|
| 4552 | else {
|
---|
| 4553 | CScriptVarLinkWorkPtr v(findInScopes(Objc.vars.front().second));
|
---|
| 4554 | ASSERT(v==true);
|
---|
| 4555 | if(v) v->setVarPtr(Val);
|
---|
| 4556 | }
|
---|
| 4557 | } else {
|
---|
| 4558 | vector<CScriptVarPtr> Path(1, Val);
|
---|
| 4559 | for(DESTRUCTURING_VARS_cit it=Objc.vars.begin()+1; it!=Objc.vars.end(); ++it) {
|
---|
| 4560 | if(it->second == "}" || it->second == "]")
|
---|
| 4561 | Path.pop_back();
|
---|
| 4562 | else {
|
---|
| 4563 | if(it->second.empty()) continue; // skip empty entries
|
---|
| 4564 | CScriptVarLinkWorkPtr var = Path.back()->findChildWithStringChars(it->first);
|
---|
| 4565 | if(var) var = var.getter(execute); else var = constUndefined;
|
---|
| 4566 | if(!execute) return;
|
---|
| 4567 | if(it->second == "{" || it->second == "[") {
|
---|
| 4568 | Path.push_back(var);
|
---|
| 4569 | } else if(Scope)
|
---|
| 4570 | Scope->addChildOrReplace(it->second, var);
|
---|
| 4571 | else {
|
---|
| 4572 | CScriptVarLinkWorkPtr v(findInScopes(it->second));
|
---|
| 4573 | ASSERT(v==true);
|
---|
| 4574 | if(v) v->setVarPtr(var);
|
---|
| 4575 | }
|
---|
| 4576 | }
|
---|
| 4577 | }
|
---|
| 4578 | }
|
---|
| 4579 | }
|
---|
| 4580 |
|
---|
| 4581 | void CTinyJS::execute_var_init( bool hideLetScope, CScriptResult &execute )
|
---|
| 4582 | {
|
---|
| 4583 | for(;;) {
|
---|
| 4584 | t->check(LEX_T_DESTRUCTURING_VAR);
|
---|
| 4585 | CScriptTokenDataDestructuringVar &Objc = t->getToken().DestructuringVar();
|
---|
| 4586 | t->match(LEX_T_DESTRUCTURING_VAR);
|
---|
| 4587 | if(t->tk == '=') {
|
---|
| 4588 | t->match('=');
|
---|
| 4589 | if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(true);
|
---|
| 4590 | CScriptVarPtr Val = execute_assignment(execute);
|
---|
| 4591 | if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(false);
|
---|
| 4592 | assign_destructuring_var(0, Objc, Val, execute);
|
---|
| 4593 | }
|
---|
| 4594 | if (t->tk == ',')
|
---|
| 4595 | t->match(',');
|
---|
| 4596 | else
|
---|
| 4597 | break;
|
---|
| 4598 | }
|
---|
| 4599 | }
|
---|
| 4600 | void CTinyJS::execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, CScriptResult &execute) {
|
---|
| 4601 | for(vector<CScriptTokenDataObjectLiteral::ELEMENT>::iterator it=Objc.elements.begin(); execute && it!=Objc.elements.end(); ++it) {
|
---|
| 4602 | if(it->value.empty()) continue;
|
---|
| 4603 | CScriptVarPtr rhs = Val->findChildWithStringChars(it->id).getter(execute);
|
---|
| 4604 | if(!rhs) rhs=constUndefined;
|
---|
| 4605 | if(it->value.front().token == LEX_T_OBJECT_LITERAL && it->value.front().Object().destructuring) {
|
---|
| 4606 | execute_destructuring(it->value.front().Object(), rhs, execute);
|
---|
| 4607 | } else {
|
---|
| 4608 | t->pushTokenScope(it->value);
|
---|
| 4609 | CScriptVarLinkWorkPtr lhs = execute_condition(execute);
|
---|
| 4610 | if(lhs->isWritable()) {
|
---|
| 4611 | if (!lhs->isOwned()) {
|
---|
| 4612 | CScriptVarPtr fakedOwner = lhs.getReferencedOwner();
|
---|
| 4613 | if(fakedOwner) {
|
---|
| 4614 | if(!fakedOwner->isExtensible())
|
---|
| 4615 | continue;
|
---|
| 4616 | lhs = fakedOwner->addChildOrReplace(lhs->getName(), lhs);
|
---|
| 4617 | } else
|
---|
| 4618 | lhs = root->addChildOrReplace(lhs->getName(), lhs);
|
---|
| 4619 | }
|
---|
| 4620 | lhs.setter(execute, rhs);
|
---|
| 4621 | }
|
---|
| 4622 | }
|
---|
| 4623 | }
|
---|
| 4624 | }
|
---|
| 4625 |
|
---|
| 4626 | CScriptVarLinkWorkPtr CTinyJS::execute_literals(CScriptResult &execute) {
|
---|
| 4627 | switch(t->tk) {
|
---|
| 4628 | case LEX_ID:
|
---|
| 4629 | if(execute) {
|
---|
| 4630 | CScriptVarLinkWorkPtr a(findInScopes(t->tkStr()));
|
---|
| 4631 | if (!a) {
|
---|
| 4632 | /* Variable doesn't exist! JavaScript says we should create it
|
---|
| 4633 | * (we won't add it here. This is done in the assignment operator)*/
|
---|
| 4634 | if(t->tkStr() == "this")
|
---|
| 4635 | a = root; // fake this
|
---|
| 4636 | else
|
---|
| 4637 | a = CScriptVarLinkPtr(constScriptVar(Undefined), t->tkStr());
|
---|
| 4638 | }
|
---|
| 4639 | /*
|
---|
| 4640 | prvention for assignment to this is now done by the tokenizer
|
---|
| 4641 | else if(t->tkStr() == "this")
|
---|
| 4642 | a(a->getVarPtr()); // prevent assign to this
|
---|
| 4643 | */
|
---|
| 4644 | t->match(LEX_ID);
|
---|
| 4645 | return a;
|
---|
| 4646 | }
|
---|
| 4647 | t->match(LEX_ID);
|
---|
| 4648 | break;
|
---|
| 4649 | case LEX_INT:
|
---|
| 4650 | {
|
---|
| 4651 | CScriptVarPtr a = newScriptVar(t->getToken().Int());
|
---|
| 4652 | a->setExtensible(false);
|
---|
| 4653 | t->match(LEX_INT);
|
---|
| 4654 | return a;
|
---|
| 4655 | }
|
---|
| 4656 | break;
|
---|
| 4657 | case LEX_FLOAT:
|
---|
| 4658 | {
|
---|
| 4659 | CScriptVarPtr a = newScriptVar(t->getToken().Float());
|
---|
| 4660 | t->match(LEX_FLOAT);
|
---|
| 4661 | return a;
|
---|
| 4662 | }
|
---|
| 4663 | break;
|
---|
| 4664 | case LEX_STR:
|
---|
| 4665 | {
|
---|
| 4666 | CScriptVarPtr a = newScriptVar(t->getToken().String());
|
---|
| 4667 | t->match(LEX_STR);
|
---|
| 4668 | return a;
|
---|
| 4669 | }
|
---|
| 4670 | break;
|
---|
| 4671 | #ifndef NO_REGEXP
|
---|
| 4672 | case LEX_REGEXP:
|
---|
| 4673 | {
|
---|
| 4674 | string::size_type pos = t->getToken().String().find_last_of('/');
|
---|
| 4675 | string source = t->getToken().String().substr(1, pos-1);
|
---|
| 4676 | string flags = t->getToken().String().substr(pos+1);
|
---|
| 4677 | CScriptVarPtr a = newScriptVar(source, flags);
|
---|
| 4678 | t->match(LEX_REGEXP);
|
---|
| 4679 | return a;
|
---|
| 4680 | }
|
---|
| 4681 | break;
|
---|
| 4682 | #endif /* NO_REGEXP */
|
---|
| 4683 | case LEX_T_OBJECT_LITERAL:
|
---|
| 4684 | if(execute) {
|
---|
| 4685 | CScriptTokenDataObjectLiteral &Objc = t->getToken().Object();
|
---|
| 4686 | t->match(LEX_T_OBJECT_LITERAL);
|
---|
| 4687 | if(Objc.destructuring) {
|
---|
| 4688 | t->match('=');
|
---|
| 4689 | CScriptVarPtr a = execute_assignment(execute);
|
---|
| 4690 | if(execute) execute_destructuring(Objc, a, execute);
|
---|
| 4691 | return a;
|
---|
| 4692 | } else {
|
---|
| 4693 | CScriptVarPtr a = Objc.type==CScriptTokenDataObjectLiteral::OBJECT ? newScriptVar(Object) : newScriptVar(Array);
|
---|
| 4694 | for(vector<CScriptTokenDataObjectLiteral::ELEMENT>::iterator it=Objc.elements.begin(); execute && it!=Objc.elements.end(); ++it) {
|
---|
| 4695 | if(it->value.empty()) continue;
|
---|
| 4696 | CScriptToken &tk = it->value.front();
|
---|
| 4697 | if(tk.token==LEX_T_GET || tk.token==LEX_T_SET) {
|
---|
| 4698 | CScriptTokenDataFnc &Fnc = tk.Fnc();
|
---|
| 4699 | if((tk.token == LEX_T_GET && Fnc.arguments.size()==0) || (tk.token == LEX_T_SET && Fnc.arguments.size()==1)) {
|
---|
| 4700 | CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(tk);
|
---|
| 4701 | CScriptVarLinkWorkPtr child = a->findChild(Fnc.name);
|
---|
| 4702 | if(child && !child->getVarPtr()->isAccessor()) child.clear();
|
---|
| 4703 | if(!child) child = a->addChildOrReplace(Fnc.name, newScriptVar(Accessor));
|
---|
| 4704 | child->getVarPtr()->addChildOrReplace((tk.token==LEX_T_GET?TINYJS_ACCESSOR_GET_VAR:TINYJS_ACCESSOR_SET_VAR), funcVar->getVarPtr());
|
---|
| 4705 | }
|
---|
| 4706 | } else {
|
---|
| 4707 | t->pushTokenScope(it->value);
|
---|
| 4708 | a->addChildOrReplace(it->id, execute_assignment(execute));
|
---|
| 4709 | while(0);
|
---|
| 4710 | }
|
---|
| 4711 | }
|
---|
| 4712 | return a;
|
---|
| 4713 | }
|
---|
| 4714 | } else
|
---|
| 4715 | t->match(LEX_T_OBJECT_LITERAL);
|
---|
| 4716 | break;
|
---|
| 4717 | case LEX_R_LET: // let as expression
|
---|
| 4718 | if(execute) {
|
---|
| 4719 | CScopeControl ScopeControl(this);
|
---|
| 4720 | t->match(LEX_R_LET);
|
---|
| 4721 | t->match('(');
|
---|
| 4722 | t->check(LEX_T_FORWARD);
|
---|
| 4723 | ScopeControl.addLetScope();
|
---|
| 4724 | execute_statement(execute); // execute forwarder
|
---|
| 4725 | execute_var_init(true, execute);
|
---|
| 4726 | t->match(')');
|
---|
| 4727 | return execute_assignment(execute);
|
---|
| 4728 | } else {
|
---|
| 4729 | t->skip(t->getToken().Int());
|
---|
| 4730 | }
|
---|
| 4731 | break;
|
---|
| 4732 | case LEX_T_FUNCTION_OPERATOR:
|
---|
| 4733 | if(execute) {
|
---|
| 4734 | CScriptVarLinkWorkPtr a = parseFunctionDefinition(t->getToken());
|
---|
| 4735 | t->match(LEX_T_FUNCTION_OPERATOR);
|
---|
| 4736 | return a;
|
---|
| 4737 | }
|
---|
| 4738 | t->match(LEX_T_FUNCTION_OPERATOR);
|
---|
| 4739 | break;
|
---|
| 4740 | case LEX_R_NEW: // new -> create a new object
|
---|
| 4741 | if (execute) {
|
---|
| 4742 | t->match(LEX_R_NEW);
|
---|
| 4743 | CScriptVarLinkWorkPtr parent = execute_literals(execute);
|
---|
| 4744 | CScriptVarLinkWorkPtr objClass = execute_member(parent, execute).getter(execute);
|
---|
| 4745 | if (execute) {
|
---|
| 4746 | if(objClass->getVarPtr()->isFunction()) {
|
---|
| 4747 | CScriptVarPtr obj(newScriptVar(Object));
|
---|
| 4748 | CScriptVarLinkPtr prototype = objClass->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 4749 | if(!prototype || prototype->getVarPtr()->isUndefined() || prototype->getVarPtr()->isNull()) {
|
---|
| 4750 | prototype = objClass->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE);
|
---|
| 4751 | obj->addChildOrReplace(TINYJS___PROTO___VAR, prototype, SCRIPTVARLINK_WRITABLE);
|
---|
| 4752 | }
|
---|
| 4753 | CScriptVarLinkPtr constructor = objClass->getVarPtr()->findChild("__constructor__");
|
---|
| 4754 | if(constructor && constructor->getVarPtr()->isFunction())
|
---|
| 4755 | objClass = constructor;
|
---|
| 4756 | vector<CScriptVarPtr> arguments;
|
---|
| 4757 | if (t->tk == '(') {
|
---|
| 4758 | t->match('(');
|
---|
| 4759 | while(t->tk!=')') {
|
---|
| 4760 | CScriptVarPtr value = execute_assignment(execute).getter(execute);
|
---|
| 4761 | if (execute)
|
---|
| 4762 | arguments.push_back(value);
|
---|
| 4763 | if (t->tk!=')') t->match(',', ')');
|
---|
| 4764 | }
|
---|
| 4765 | t->match(')');
|
---|
| 4766 | }
|
---|
| 4767 | if(execute) {
|
---|
| 4768 | CScriptVarPtr returnVar = callFunction(execute, objClass->getVarPtr(), arguments, obj, &obj);
|
---|
| 4769 | if(returnVar->isObject())
|
---|
| 4770 | return CScriptVarLinkWorkPtr(returnVar);
|
---|
| 4771 | return CScriptVarLinkWorkPtr(obj);
|
---|
| 4772 | }
|
---|
| 4773 | } else
|
---|
| 4774 | throwError(execute, TypeError, objClass->getName() + " is not a constructor");
|
---|
| 4775 | } else
|
---|
| 4776 | if (t->tk == '(') t->skip(t->getToken().Int());
|
---|
| 4777 | } else
|
---|
| 4778 | t->skip(t->getToken().Int());
|
---|
| 4779 | break;
|
---|
| 4780 | case LEX_R_TRUE:
|
---|
| 4781 | t->match(LEX_R_TRUE);
|
---|
| 4782 | return constScriptVar(true);
|
---|
| 4783 | case LEX_R_FALSE:
|
---|
| 4784 | t->match(LEX_R_FALSE);
|
---|
| 4785 | return constScriptVar(false);
|
---|
| 4786 | case LEX_R_NULL:
|
---|
| 4787 | t->match(LEX_R_NULL);
|
---|
| 4788 | return constScriptVar(Null);
|
---|
| 4789 | case '(':
|
---|
| 4790 | if(execute) {
|
---|
| 4791 | t->match('(');
|
---|
| 4792 | CScriptVarLinkWorkPtr a = execute_base(execute).getter(execute);
|
---|
| 4793 | t->match(')');
|
---|
| 4794 | return a;
|
---|
| 4795 | } else
|
---|
| 4796 | t->skip(t->getToken().Int());
|
---|
| 4797 | break;
|
---|
| 4798 | case LEX_T_EXCEPTION_VAR:
|
---|
| 4799 | t->match(LEX_T_EXCEPTION_VAR);
|
---|
| 4800 | if(execute.value) return execute.value;
|
---|
| 4801 | break;
|
---|
| 4802 | default:
|
---|
| 4803 | t->match(LEX_EOF);
|
---|
| 4804 | break;
|
---|
| 4805 | }
|
---|
| 4806 | return constScriptVar(Undefined);
|
---|
| 4807 |
|
---|
| 4808 | }
|
---|
| 4809 | CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, CScriptResult &execute) {
|
---|
| 4810 | CScriptVarLinkWorkPtr a;
|
---|
| 4811 | parent.swap(a);
|
---|
| 4812 | if(t->tk == '.' || t->tk == '[') {
|
---|
| 4813 | while(t->tk == '.' || t->tk == '[') {
|
---|
| 4814 | parent.swap(a);
|
---|
| 4815 | a = parent.getter(execute); // a is now the "getted" var
|
---|
| 4816 | if(execute && (a->getVarPtr()->isUndefined() || a->getVarPtr()->isNull())) {
|
---|
| 4817 | throwError(execute, ReferenceError, a->getName() + " is " + a->toString(execute));
|
---|
| 4818 | }
|
---|
| 4819 | string name;
|
---|
| 4820 | if(t->tk == '.') {
|
---|
| 4821 | t->match('.');
|
---|
| 4822 | name = t->tkStr();
|
---|
| 4823 | t->match(LEX_ID);
|
---|
| 4824 | } else {
|
---|
| 4825 | if(execute) {
|
---|
| 4826 | t->match('[');
|
---|
| 4827 | name = execute_expression(execute)->toString(execute);
|
---|
| 4828 | t->match(']');
|
---|
| 4829 | } else
|
---|
| 4830 | t->skip(t->getToken().Int());
|
---|
| 4831 | }
|
---|
| 4832 | if (execute) {
|
---|
| 4833 | CScriptVarPtr aVar = a;
|
---|
| 4834 | a = aVar->findChildWithPrototypeChain(name);
|
---|
| 4835 | if(!a) {
|
---|
| 4836 | a(constScriptVar(Undefined), name);
|
---|
| 4837 | a.setReferencedOwner(aVar);
|
---|
| 4838 | }
|
---|
| 4839 | }
|
---|
| 4840 | }
|
---|
| 4841 | }
|
---|
| 4842 | return a;
|
---|
| 4843 | }
|
---|
| 4844 |
|
---|
| 4845 | CScriptVarLinkWorkPtr CTinyJS::execute_function_call(CScriptResult &execute) {
|
---|
| 4846 | CScriptVarLinkWorkPtr parent = execute_literals(execute);
|
---|
| 4847 | CScriptVarLinkWorkPtr a = execute_member(parent, execute);
|
---|
| 4848 | while (t->tk == '(') {
|
---|
| 4849 | if (execute) {
|
---|
| 4850 | if(a->getVarPtr()->isUndefined() || a->getVarPtr()->isNull())
|
---|
| 4851 | throwError(execute, ReferenceError, a->getName() + " is " + a->toString(execute));
|
---|
| 4852 | CScriptVarLinkWorkPtr fnc = a.getter(execute);
|
---|
| 4853 | if (!fnc->getVarPtr()->isFunction())
|
---|
| 4854 | throwError(execute, TypeError, a->getName() + " is not a function");
|
---|
| 4855 | t->match('('); // path += '(';
|
---|
| 4856 |
|
---|
| 4857 | // grab in all parameters
|
---|
| 4858 | vector<CScriptVarPtr> arguments;
|
---|
| 4859 | while(t->tk!=')') {
|
---|
| 4860 | CScriptVarLinkWorkPtr value = execute_assignment(execute).getter(execute);
|
---|
| 4861 | // path += (*value)->getString();
|
---|
| 4862 | if (execute) {
|
---|
| 4863 | arguments.push_back(value);
|
---|
| 4864 | }
|
---|
| 4865 | if (t->tk!=')') { t->match(','); /*path+=',';*/ }
|
---|
| 4866 | }
|
---|
| 4867 | t->match(')'); //path+=')';
|
---|
| 4868 | // setup a return variable
|
---|
| 4869 | CScriptVarLinkWorkPtr returnVar;
|
---|
| 4870 | if(execute) {
|
---|
| 4871 | if (!parent)
|
---|
| 4872 | parent = findInScopes("this");
|
---|
| 4873 | // if no parent use the root-scope
|
---|
| 4874 | CScriptVarPtr This(parent ? parent->getVarPtr() : (CScriptVarPtr )root);
|
---|
| 4875 | a = callFunction(execute, a->getVarPtr(), arguments, This);
|
---|
| 4876 | }
|
---|
| 4877 | } else {
|
---|
| 4878 | // function, but not executing - just parse args and be done
|
---|
| 4879 | t->match('(');
|
---|
| 4880 | while (t->tk != ')') {
|
---|
| 4881 | CScriptVarLinkWorkPtr value = execute_base(execute);
|
---|
| 4882 | // if (t->tk!=')') t->match(',');
|
---|
| 4883 | }
|
---|
| 4884 | t->match(')');
|
---|
| 4885 | }
|
---|
| 4886 | a = execute_member(parent = a, execute);
|
---|
| 4887 | }
|
---|
| 4888 | return a;
|
---|
| 4889 | }
|
---|
| 4890 | // R->L: Precedence 3 (in-/decrement) ++ --
|
---|
| 4891 | // R<-L: Precedence 4 (unary) ! ~ + - typeof void delete
|
---|
| 4892 | bool CTinyJS::execute_unary_rhs(CScriptResult &execute, CScriptVarLinkWorkPtr& a) {
|
---|
| 4893 | t->match(t->tk);
|
---|
| 4894 | a = execute_unary(execute).getter(execute);
|
---|
| 4895 | if(execute) CheckRightHandVar(execute, a);
|
---|
| 4896 | return execute;
|
---|
| 4897 | };
|
---|
| 4898 | CScriptVarLinkWorkPtr CTinyJS::execute_unary(CScriptResult &execute) {
|
---|
| 4899 | CScriptVarLinkWorkPtr a;
|
---|
| 4900 | switch(t->tk) {
|
---|
| 4901 | case '-':
|
---|
| 4902 | if(execute_unary_rhs(execute, a))
|
---|
| 4903 | a(newScriptVar(-a->getVarPtr()->toNumber(execute)));
|
---|
| 4904 | break;
|
---|
| 4905 | case '+':
|
---|
| 4906 | if(execute_unary_rhs(execute, a))
|
---|
| 4907 | a = newScriptVar(a->getVarPtr()->toNumber(execute));
|
---|
| 4908 | break;
|
---|
| 4909 | case '!':
|
---|
| 4910 | if(execute_unary_rhs(execute, a))
|
---|
| 4911 | a = constScriptVar(!a->getVarPtr()->toBoolean());
|
---|
| 4912 | break;
|
---|
| 4913 | case '~':
|
---|
| 4914 | if(execute_unary_rhs(execute, a))
|
---|
| 4915 | a = newScriptVar(~a->getVarPtr()->toNumber(execute));
|
---|
| 4916 | break;
|
---|
| 4917 | case LEX_R_TYPEOF:
|
---|
| 4918 | if(execute_unary_rhs(execute, a))
|
---|
| 4919 | a = newScriptVar(a->getVarPtr()->getVarType());
|
---|
| 4920 | break;
|
---|
| 4921 | case LEX_R_VOID:
|
---|
| 4922 | if(execute_unary_rhs(execute, a))
|
---|
| 4923 | a = constScriptVar(Undefined);
|
---|
| 4924 | break;
|
---|
| 4925 | case LEX_R_DELETE:
|
---|
| 4926 | t->match(LEX_R_DELETE); // delete
|
---|
| 4927 | a = execute_unary(execute); // no getter - delete can remove the accessor
|
---|
| 4928 | if (execute) {
|
---|
| 4929 | // !!! no right-hand-check by delete
|
---|
| 4930 | if(a->isOwned() && a->isConfigurable()) {
|
---|
| 4931 | a->getOwner()->removeLink(a); // removes the link from owner
|
---|
| 4932 | a = constScriptVar(true);
|
---|
| 4933 | }
|
---|
| 4934 | else
|
---|
| 4935 | a = constScriptVar(false);
|
---|
| 4936 | }
|
---|
| 4937 | break;
|
---|
| 4938 | case LEX_PLUSPLUS:
|
---|
| 4939 | case LEX_MINUSMINUS:
|
---|
| 4940 | {
|
---|
| 4941 | int op = t->tk;
|
---|
| 4942 | t->match(op); // pre increment/decrement
|
---|
| 4943 | CScriptTokenizer::ScriptTokenPosition ErrorPos = t->getPos();
|
---|
| 4944 | a = execute_function_call(execute);
|
---|
| 4945 | if (execute) {
|
---|
| 4946 | if(a->getName().empty())
|
---|
| 4947 | throwError(execute, SyntaxError, string("invalid ")+(op==LEX_PLUSPLUS ? "increment" : "decrement")+" operand", ErrorPos);
|
---|
| 4948 | else if(!a->isOwned() && !a.hasReferencedOwner() && !a->getName().empty())
|
---|
| 4949 | throwError(execute, ReferenceError, a->getName() + " is not defined", ErrorPos);
|
---|
| 4950 | CScriptVarPtr res = newScriptVar(a.getter(execute)->getVarPtr()->toNumber(execute).add(op==LEX_PLUSPLUS ? 1 : -1));
|
---|
| 4951 | if(a->isWritable()) {
|
---|
| 4952 | if(!a->isOwned() && a.hasReferencedOwner() && a.getReferencedOwner()->isExtensible())
|
---|
| 4953 | a.getReferencedOwner()->addChildOrReplace(a->getName(), res);
|
---|
| 4954 | else
|
---|
| 4955 | a.setter(execute, res);
|
---|
| 4956 | }
|
---|
| 4957 | a = res;
|
---|
| 4958 | }
|
---|
| 4959 | }
|
---|
| 4960 | break;
|
---|
| 4961 | default:
|
---|
| 4962 | a = execute_function_call(execute);
|
---|
| 4963 | break;
|
---|
| 4964 | }
|
---|
| 4965 | // post increment/decrement
|
---|
| 4966 | if (t->tk==LEX_PLUSPLUS || t->tk==LEX_MINUSMINUS) {
|
---|
| 4967 | int op = t->tk;
|
---|
| 4968 | t->match(op);
|
---|
| 4969 | if (execute) {
|
---|
| 4970 | if(a->getName().empty())
|
---|
| 4971 | throwError(execute, SyntaxError, string("invalid ")+(op==LEX_PLUSPLUS ? "increment" : "decrement")+" operand", t->getPrevPos());
|
---|
| 4972 | else if(!a->isOwned() && !a.hasReferencedOwner() && !a->getName().empty())
|
---|
| 4973 | throwError(execute, ReferenceError, a->getName() + " is not defined", t->getPrevPos());
|
---|
| 4974 | CNumber num = a.getter(execute)->getVarPtr()->toNumber(execute);
|
---|
| 4975 | CScriptVarPtr res = newScriptVar(num.add(op==LEX_PLUSPLUS ? 1 : -1));
|
---|
| 4976 | if(a->isWritable()) {
|
---|
| 4977 | if(!a->isOwned() && a.hasReferencedOwner() && a.getReferencedOwner()->isExtensible())
|
---|
| 4978 | a.getReferencedOwner()->addChildOrReplace(a->getName(), res);
|
---|
| 4979 | else
|
---|
| 4980 | a.setter(execute, res);
|
---|
| 4981 | }
|
---|
| 4982 | a = newScriptVar(num);
|
---|
| 4983 | }
|
---|
| 4984 | }
|
---|
| 4985 | return a;
|
---|
| 4986 | }
|
---|
| 4987 |
|
---|
| 4988 | // L->R: Precedence 5 (term) * / %
|
---|
| 4989 | CScriptVarLinkWorkPtr CTinyJS::execute_term(CScriptResult &execute) {
|
---|
| 4990 | CScriptVarLinkWorkPtr a = execute_unary(execute);
|
---|
| 4991 | if (t->tk=='*' || t->tk=='/' || t->tk=='%') {
|
---|
| 4992 | CheckRightHandVar(execute, a);
|
---|
| 4993 | while (t->tk=='*' || t->tk=='/' || t->tk=='%') {
|
---|
| 4994 | int op = t->tk;
|
---|
| 4995 | t->match(t->tk);
|
---|
| 4996 | CScriptVarLinkWorkPtr b = execute_unary(execute); // L->R
|
---|
| 4997 | if (execute) {
|
---|
| 4998 | CheckRightHandVar(execute, b);
|
---|
| 4999 | a = mathsOp(execute, a.getter(execute), b.getter(execute), op);
|
---|
| 5000 | }
|
---|
| 5001 | }
|
---|
| 5002 | }
|
---|
| 5003 | return a;
|
---|
| 5004 | }
|
---|
| 5005 |
|
---|
| 5006 | // L->R: Precedence 6 (addition/subtraction) + -
|
---|
| 5007 | CScriptVarLinkWorkPtr CTinyJS::execute_expression(CScriptResult &execute) {
|
---|
| 5008 | CScriptVarLinkWorkPtr a = execute_term(execute);
|
---|
| 5009 | if (t->tk=='+' || t->tk=='-') {
|
---|
| 5010 | CheckRightHandVar(execute, a);
|
---|
| 5011 | while (t->tk=='+' || t->tk=='-') {
|
---|
| 5012 | int op = t->tk;
|
---|
| 5013 | t->match(t->tk);
|
---|
| 5014 | CScriptVarLinkWorkPtr b = execute_term(execute); // L->R
|
---|
| 5015 | if (execute) {
|
---|
| 5016 | CheckRightHandVar(execute, b);
|
---|
| 5017 | a = mathsOp(execute, a.getter(execute), b.getter(execute), op);
|
---|
| 5018 | }
|
---|
| 5019 | }
|
---|
| 5020 | }
|
---|
| 5021 | return a;
|
---|
| 5022 | }
|
---|
| 5023 |
|
---|
| 5024 | // L->R: Precedence 7 (bitwise shift) << >> >>>
|
---|
| 5025 | CScriptVarLinkWorkPtr CTinyJS::execute_binary_shift(CScriptResult &execute) {
|
---|
| 5026 | CScriptVarLinkWorkPtr a = execute_expression(execute);
|
---|
| 5027 | if (t->tk==LEX_LSHIFT || t->tk==LEX_RSHIFT || t->tk==LEX_RSHIFTU) {
|
---|
| 5028 | CheckRightHandVar(execute, a);
|
---|
| 5029 | while (t->tk>=LEX_SHIFTS_BEGIN && t->tk<=LEX_SHIFTS_END) {
|
---|
| 5030 | int op = t->tk;
|
---|
| 5031 | t->match(t->tk);
|
---|
| 5032 |
|
---|
| 5033 | CScriptVarLinkWorkPtr b = execute_expression(execute); // L->R
|
---|
| 5034 | if (execute) {
|
---|
| 5035 | CheckRightHandVar(execute, a);
|
---|
| 5036 | // not in-place, so just replace
|
---|
| 5037 | a = mathsOp(execute, a.getter(execute), b.getter(execute), op);
|
---|
| 5038 | }
|
---|
| 5039 | }
|
---|
| 5040 | }
|
---|
| 5041 | return a;
|
---|
| 5042 | }
|
---|
| 5043 | // L->R: Precedence 8 (relational) < <= > <= in instanceof
|
---|
| 5044 | // L->R: Precedence 9 (equality) == != === !===
|
---|
| 5045 | CScriptVarLinkWorkPtr CTinyJS::execute_relation(CScriptResult &execute, int set, int set_n) {
|
---|
| 5046 | CScriptVarLinkWorkPtr a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute);
|
---|
| 5047 | if ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END)
|
---|
| 5048 | || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) {
|
---|
| 5049 | CheckRightHandVar(execute, a);
|
---|
| 5050 | a = a.getter(execute);
|
---|
| 5051 | while ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END)
|
---|
| 5052 | || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) {
|
---|
| 5053 | int op = t->tk;
|
---|
| 5054 | t->match(t->tk);
|
---|
| 5055 | CScriptVarLinkWorkPtr b = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); // L->R
|
---|
| 5056 | if (execute) {
|
---|
| 5057 | CheckRightHandVar(execute, b);
|
---|
| 5058 | string nameOf_b = b->getName();
|
---|
| 5059 | b = b.getter(execute);
|
---|
| 5060 | if(op == LEX_R_IN) {
|
---|
| 5061 | if(!b->getVarPtr()->isObject())
|
---|
| 5062 | throwError(execute, TypeError, "invalid 'in' operand "+nameOf_b);
|
---|
| 5063 | a(constScriptVar( (bool)b->getVarPtr()->findChildWithPrototypeChain(a->toString(execute))));
|
---|
| 5064 | } else if(op == LEX_R_INSTANCEOF) {
|
---|
| 5065 | CScriptVarLinkPtr prototype = b->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
| 5066 | if(!prototype)
|
---|
| 5067 | throwError(execute, TypeError, "invalid 'instanceof' operand "+nameOf_b);
|
---|
| 5068 | else {
|
---|
| 5069 | unsigned int uniqueID = getUniqueID();
|
---|
| 5070 | CScriptVarPtr object = a->getVarPtr()->findChild(TINYJS___PROTO___VAR);
|
---|
| 5071 | while( object && object!=prototype->getVarPtr() && object->getTempraryID() != uniqueID) {
|
---|
| 5072 | object->setTemporaryID(uniqueID); // prevents recursions
|
---|
| 5073 | object = object->findChild(TINYJS___PROTO___VAR);
|
---|
| 5074 | }
|
---|
| 5075 | a(constScriptVar(object && object==prototype->getVarPtr()));
|
---|
| 5076 | }
|
---|
| 5077 | } else
|
---|
| 5078 | a = mathsOp(execute, a, b, op);
|
---|
| 5079 | }
|
---|
| 5080 | }
|
---|
| 5081 | }
|
---|
| 5082 | return a;
|
---|
| 5083 | }
|
---|
| 5084 |
|
---|
| 5085 | // L->R: Precedence 10 (bitwise-and) &
|
---|
| 5086 | // L->R: Precedence 11 (bitwise-xor) ^
|
---|
| 5087 | // L->R: Precedence 12 (bitwise-or) |
|
---|
| 5088 | CScriptVarLinkWorkPtr CTinyJS::execute_binary_logic(CScriptResult &execute, int op, int op_n1, int op_n2) {
|
---|
| 5089 | CScriptVarLinkWorkPtr a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute);
|
---|
| 5090 | if (t->tk==op) {
|
---|
| 5091 | CheckRightHandVar(execute, a);
|
---|
| 5092 | a = a.getter(execute);
|
---|
| 5093 | while (t->tk==op) {
|
---|
| 5094 | t->match(t->tk);
|
---|
| 5095 | CScriptVarLinkWorkPtr b = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); // L->R
|
---|
| 5096 | if (execute) {
|
---|
| 5097 | CheckRightHandVar(execute, b);
|
---|
| 5098 | a = mathsOp(execute, a, b.getter(execute), op);
|
---|
| 5099 | }
|
---|
| 5100 | }
|
---|
| 5101 | }
|
---|
| 5102 | return a;
|
---|
| 5103 | }
|
---|
| 5104 | // L->R: Precedence 13 ==> (logical-and) &&
|
---|
| 5105 | // L->R: Precedence 14 ==> (logical-or) ||
|
---|
| 5106 | CScriptVarLinkWorkPtr CTinyJS::execute_logic(CScriptResult &execute, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) {
|
---|
| 5107 | CScriptVarLinkWorkPtr a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute);
|
---|
| 5108 | if (t->tk==op) {
|
---|
| 5109 | if(execute) {
|
---|
| 5110 | CScriptVarLinkWorkPtr b;
|
---|
| 5111 | CheckRightHandVar(execute, a);
|
---|
| 5112 | a(a.getter(execute)); // rebuild a
|
---|
| 5113 | do {
|
---|
| 5114 | if(execute && (op==LEX_ANDAND ? a->toBoolean() : !a->toBoolean())) {
|
---|
| 5115 | t->match(t->tk);
|
---|
| 5116 | b = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute);
|
---|
| 5117 | CheckRightHandVar(execute, b); a(b.getter(execute)); // rebuild a
|
---|
| 5118 | } else
|
---|
| 5119 | t->skip(t->getToken().Int());
|
---|
| 5120 | } while(t->tk==op);
|
---|
| 5121 | } else
|
---|
| 5122 | t->skip(t->getToken().Int());
|
---|
| 5123 | }
|
---|
| 5124 | return a;
|
---|
| 5125 | }
|
---|
| 5126 |
|
---|
| 5127 | // L<-R: Precedence 15 (condition) ?:
|
---|
| 5128 | CScriptVarLinkWorkPtr CTinyJS::execute_condition(CScriptResult &execute) {
|
---|
| 5129 | CScriptVarLinkWorkPtr a = execute_logic(execute);
|
---|
| 5130 | if (t->tk=='?') {
|
---|
| 5131 | CheckRightHandVar(execute, a);
|
---|
| 5132 | bool cond = execute && a.getter(execute)->toBoolean();
|
---|
| 5133 | if(execute) {
|
---|
| 5134 | if(cond) {
|
---|
| 5135 | t->match('?');
|
---|
| 5136 | a = execute_condition(execute);
|
---|
| 5137 | t->check(':');
|
---|
| 5138 | t->skip(t->getToken().Int());
|
---|
| 5139 | } else {
|
---|
| 5140 | CScriptVarLinkWorkPtr b;
|
---|
| 5141 | t->skip(t->getToken().Int());
|
---|
| 5142 | t->match(':');
|
---|
| 5143 | return execute_condition(execute);
|
---|
| 5144 | }
|
---|
| 5145 | } else {
|
---|
| 5146 | t->skip(t->getToken().Int());
|
---|
| 5147 | t->skip(t->getToken().Int());
|
---|
| 5148 | }
|
---|
| 5149 | }
|
---|
| 5150 | return a;
|
---|
| 5151 | }
|
---|
| 5152 |
|
---|
| 5153 | // L<-R: Precedence 16 (assignment) = += -= *= /= %= <<= >>= >>>= &= |= ^=
|
---|
| 5154 | // now we can return CScriptVarLinkPtr execute_assignment returns always no setters/getters
|
---|
| 5155 | // force life of the Owner is no more needed
|
---|
| 5156 | CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptResult &execute) {
|
---|
| 5157 | return execute_assignment(execute_condition(execute), execute);
|
---|
| 5158 | }
|
---|
| 5159 | CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptVarLinkWorkPtr lhs, CScriptResult &execute) {
|
---|
| 5160 | if (t->tk=='=' || (t->tk>=LEX_ASSIGNMENTS_BEGIN && t->tk<=LEX_ASSIGNMENTS_END) ) {
|
---|
| 5161 | int op = t->tk;
|
---|
| 5162 | CScriptTokenizer::ScriptTokenPosition leftHandPos = t->getPos();
|
---|
| 5163 | t->match(t->tk);
|
---|
| 5164 | CScriptVarLinkWorkPtr rhs = execute_assignment(execute).getter(execute); // L<-R
|
---|
| 5165 | if (execute) {
|
---|
| 5166 | if (!lhs->isOwned() && !lhs.hasReferencedOwner() && lhs->getName().empty()) {
|
---|
| 5167 | throw new CScriptException(ReferenceError, "invalid assignment left-hand side (at runtime)", t->currentFile, leftHandPos.currentLine(), leftHandPos.currentColumn());
|
---|
| 5168 | } else if (op != '=' && !lhs->isOwned()) {
|
---|
| 5169 | throwError(execute, ReferenceError, lhs->getName() + " is not defined");
|
---|
| 5170 | }
|
---|
| 5171 | else if(lhs->isWritable()) {
|
---|
| 5172 | if (op=='=') {
|
---|
| 5173 | if (!lhs->isOwned()) {
|
---|
| 5174 | CScriptVarPtr fakedOwner = lhs.getReferencedOwner();
|
---|
| 5175 | if(fakedOwner) {
|
---|
| 5176 | if(!fakedOwner->isExtensible())
|
---|
| 5177 | return rhs->getVarPtr();
|
---|
| 5178 | lhs = fakedOwner->addChildOrReplace(lhs->getName(), lhs);
|
---|
| 5179 | } else
|
---|
| 5180 | lhs = root->addChildOrReplace(lhs->getName(), lhs);
|
---|
| 5181 | }
|
---|
| 5182 | lhs.setter(execute, rhs);
|
---|
| 5183 | return rhs->getVarPtr();
|
---|
| 5184 | } else {
|
---|
| 5185 | CScriptVarPtr result;
|
---|
| 5186 | static int assignments[] = {'+', '-', '*', '/', '%', LEX_LSHIFT, LEX_RSHIFT, LEX_RSHIFTU, '&', '|', '^'};
|
---|
| 5187 | result = mathsOp(execute, lhs, rhs, assignments[op-LEX_PLUSEQUAL]);
|
---|
| 5188 | lhs.setter(execute, result);
|
---|
| 5189 | return result;
|
---|
| 5190 | }
|
---|
| 5191 | } else {
|
---|
| 5192 | // lhs is not writable we ignore lhs & use rhs
|
---|
| 5193 | return rhs->getVarPtr();
|
---|
| 5194 | }
|
---|
| 5195 | }
|
---|
| 5196 | }
|
---|
| 5197 | else
|
---|
| 5198 | CheckRightHandVar(execute, lhs);
|
---|
| 5199 | return lhs.getter(execute);
|
---|
| 5200 | }
|
---|
| 5201 | // L->R: Precedence 17 (comma) ,
|
---|
| 5202 | CScriptVarLinkPtr CTinyJS::execute_base(CScriptResult &execute) {
|
---|
| 5203 | CScriptVarLinkPtr a;
|
---|
| 5204 | for(;;)
|
---|
| 5205 | {
|
---|
| 5206 | a = execute_assignment(execute); // L->R
|
---|
| 5207 | if (t->tk == ',') {
|
---|
| 5208 | t->match(',');
|
---|
| 5209 | } else
|
---|
| 5210 | break;
|
---|
| 5211 | }
|
---|
| 5212 | return a;
|
---|
| 5213 | }
|
---|
| 5214 | void CTinyJS::execute_block(CScriptResult &execute) {
|
---|
| 5215 | if(execute) {
|
---|
| 5216 | t->match('{');
|
---|
| 5217 | CScopeControl ScopeControl(this);
|
---|
| 5218 | if(t->tk==LEX_T_FORWARD) // add a LetScope only if needed
|
---|
| 5219 | ScopeControl.addLetScope();
|
---|
| 5220 | while (t->tk && t->tk!='}')
|
---|
| 5221 | execute_statement(execute);
|
---|
| 5222 | t->match('}');
|
---|
| 5223 | // scopes.pop_back();
|
---|
| 5224 | }
|
---|
| 5225 | else
|
---|
| 5226 | t->skip(t->getToken().Int());
|
---|
| 5227 | }
|
---|
| 5228 | void CTinyJS::execute_statement(CScriptResult &execute) {
|
---|
| 5229 | switch(t->tk) {
|
---|
| 5230 | case '{': /* A block of code */
|
---|
| 5231 | execute_block(execute);
|
---|
| 5232 | break;
|
---|
| 5233 | case ';': /* Empty statement - to allow things like ;;; */
|
---|
| 5234 | t->match(';');
|
---|
| 5235 | break;
|
---|
| 5236 | case LEX_T_FORWARD:
|
---|
| 5237 | {
|
---|
| 5238 | CScriptVarPtr in_scope = scope()->scopeLet();
|
---|
| 5239 | STRING_SET_t *varNames = t->getToken().Forwarder().varNames;
|
---|
| 5240 | for(int i=0; i<CScriptTokenDataForwards::END; ++i) {
|
---|
| 5241 | for(STRING_SET_it it=varNames[i].begin(); it!=varNames[i].end(); ++it) {
|
---|
| 5242 | CScriptVarLinkPtr a = in_scope->findChild(*it);
|
---|
| 5243 | if(!a) in_scope->addChild(*it, constScriptVar(Undefined), i==CScriptTokenDataForwards::CONSTS ? SCRIPTVARLINK_CONSTDEFAULT : SCRIPTVARLINK_VARDEFAULT);
|
---|
| 5244 | }
|
---|
| 5245 | in_scope = scope()->scopeVar();
|
---|
| 5246 | }
|
---|
| 5247 | CScriptTokenDataForwards::FNC_SET_t &functions = t->getToken().Forwarder().functions;
|
---|
| 5248 | for(CScriptTokenDataForwards::FNC_SET_it it=functions.begin(); it!=functions.end(); ++it) {
|
---|
| 5249 | CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(*it);
|
---|
| 5250 | in_scope->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT);
|
---|
| 5251 | }
|
---|
| 5252 | t->match(LEX_T_FORWARD);
|
---|
| 5253 | }
|
---|
| 5254 | break;
|
---|
| 5255 | case LEX_R_VAR:
|
---|
| 5256 | case LEX_R_LET:
|
---|
| 5257 | case LEX_R_CONST:
|
---|
| 5258 | if(execute)
|
---|
| 5259 | {
|
---|
| 5260 | CScopeControl ScopeControl(this);
|
---|
| 5261 | bool isLet = t->tk==LEX_R_LET, let_ext=false;
|
---|
| 5262 | t->match(t->tk);
|
---|
| 5263 | if(isLet && t->tk=='(') {
|
---|
| 5264 | let_ext = true;
|
---|
| 5265 | t->match('(');
|
---|
| 5266 | t->check(LEX_T_FORWARD);
|
---|
| 5267 | ScopeControl.addLetScope();
|
---|
| 5268 | execute_statement(execute); // forwarder
|
---|
| 5269 | }
|
---|
| 5270 | execute_var_init(let_ext, execute);
|
---|
| 5271 | if(let_ext) {
|
---|
| 5272 | t->match(')');
|
---|
| 5273 | execute_statement(execute);
|
---|
| 5274 | } else
|
---|
| 5275 | t->match(';');
|
---|
| 5276 | } else
|
---|
| 5277 | t->skip(t->getToken().Int());
|
---|
| 5278 | break;
|
---|
| 5279 | case LEX_R_WITH:
|
---|
| 5280 | if(execute) {
|
---|
| 5281 | t->match(LEX_R_WITH);
|
---|
| 5282 | t->match('(');
|
---|
| 5283 | CScriptVarLinkPtr var = execute_base(execute);
|
---|
| 5284 | t->match(')');
|
---|
| 5285 | CScopeControl ScopeControl(this);
|
---|
| 5286 | ScopeControl.addWithScope(var);
|
---|
| 5287 | execute_statement(execute);
|
---|
| 5288 | } else
|
---|
| 5289 | t->skip(t->getToken().Int());
|
---|
| 5290 | break;
|
---|
| 5291 | case LEX_R_IF:
|
---|
| 5292 | if(execute) {
|
---|
| 5293 | t->match(LEX_R_IF);
|
---|
| 5294 | t->match('(');
|
---|
| 5295 | bool cond = execute_base(execute)->toBoolean();
|
---|
| 5296 | t->match(')');
|
---|
| 5297 | if(cond && execute) {
|
---|
| 5298 | t->match(LEX_T_SKIP);
|
---|
| 5299 | execute_statement(execute);
|
---|
| 5300 | } else {
|
---|
| 5301 | t->check(LEX_T_SKIP);
|
---|
| 5302 | t->skip(t->getToken().Int());
|
---|
| 5303 | }
|
---|
| 5304 | if (t->tk==LEX_R_ELSE) {
|
---|
| 5305 | if(!cond && execute) {
|
---|
| 5306 | t->match(LEX_R_ELSE);
|
---|
| 5307 | execute_statement(execute);
|
---|
| 5308 | }
|
---|
| 5309 | else
|
---|
| 5310 | t->skip(t->getToken().Int());
|
---|
| 5311 | }
|
---|
| 5312 | } else
|
---|
| 5313 | t->skip(t->getToken().Int());
|
---|
| 5314 | break;
|
---|
| 5315 | case LEX_T_FOR_IN:
|
---|
| 5316 | {
|
---|
| 5317 | CScriptTokenDataLoop &LoopData = t->getToken().Loop();
|
---|
| 5318 | t->match(LEX_T_FOR_IN);
|
---|
| 5319 | if(!execute) break;
|
---|
| 5320 |
|
---|
| 5321 | CScopeControl ScopeControl(this);
|
---|
| 5322 | if(LoopData.init.size()) {
|
---|
| 5323 | t->pushTokenScope(LoopData.init);
|
---|
| 5324 | ScopeControl.addLetScope();
|
---|
| 5325 | execute_statement(execute); // forwarder
|
---|
| 5326 | }
|
---|
| 5327 | if(!execute) break;
|
---|
| 5328 |
|
---|
| 5329 | t->pushTokenScope(LoopData.iter);
|
---|
| 5330 | CScriptVarPtr for_in_var = execute_base(execute);
|
---|
| 5331 |
|
---|
| 5332 | if(!execute) break;
|
---|
| 5333 |
|
---|
| 5334 | CScriptVarPtr Iterator(for_in_var->toIterator(execute, LoopData.type!=CScriptTokenDataLoop::FOR_IN ? 2:1));
|
---|
| 5335 | CScriptVarFunctionPtr Iterator_next(Iterator->findChildWithPrototypeChain("next").getter(execute));
|
---|
| 5336 | if(execute && !Iterator_next) throwError(execute, TypeError, "'" + for_in_var->toString(execute) + "' is not iterable", t->getPrevPos());
|
---|
| 5337 | if(!execute) break;
|
---|
| 5338 | CScriptResult tmp_execute;
|
---|
| 5339 | for(;;) {
|
---|
| 5340 | bool old_haveTry = haveTry;
|
---|
| 5341 | haveTry = true;
|
---|
| 5342 | tmp_execute.set(CScriptResult::Normal, Iterator);
|
---|
| 5343 | t->pushTokenScope(LoopData.condition);
|
---|
| 5344 | execute_statement(tmp_execute);
|
---|
| 5345 | haveTry = old_haveTry;
|
---|
| 5346 | if(tmp_execute.isThrow()){
|
---|
| 5347 | if(tmp_execute.value != constStopIteration) {
|
---|
| 5348 | if(!haveTry)
|
---|
| 5349 | throw new CScriptException("uncaught exception: ", t->currentFile, t->currentLine(), t->currentColumn());
|
---|
| 5350 | else
|
---|
| 5351 | execute = tmp_execute;
|
---|
| 5352 | }
|
---|
| 5353 | break;
|
---|
| 5354 | }
|
---|
| 5355 | t->pushTokenScope(LoopData.body);
|
---|
| 5356 | execute_statement(execute);
|
---|
| 5357 | if(!execute) {
|
---|
| 5358 | bool Continue = false;
|
---|
| 5359 | if(execute.isBreakContinue()
|
---|
| 5360 | &&
|
---|
| 5361 | (execute.target.empty() || find(LoopData.labels.begin(), LoopData.labels.end(), execute.target) != LoopData.labels.end())) {
|
---|
| 5362 | Continue = execute.isContinue();
|
---|
| 5363 | execute.set(CScriptResult::Normal, false);
|
---|
| 5364 | }
|
---|
| 5365 | if(!Continue) break;
|
---|
| 5366 | }
|
---|
| 5367 | }
|
---|
| 5368 | }
|
---|
| 5369 | break;
|
---|
| 5370 | case LEX_T_LOOP:
|
---|
| 5371 | {
|
---|
| 5372 | CScriptTokenDataLoop &LoopData = t->getToken().Loop();
|
---|
| 5373 | t->match(LEX_T_LOOP);
|
---|
| 5374 | if(!execute) break;
|
---|
| 5375 |
|
---|
| 5376 | CScopeControl ScopeControl(this);
|
---|
| 5377 | if(LoopData.type == CScriptTokenDataLoop::FOR) {
|
---|
| 5378 | CScriptResult tmp_execute;
|
---|
| 5379 | t->pushTokenScope(LoopData.init);
|
---|
| 5380 | if(t->tk == LEX_T_FORWARD) {
|
---|
| 5381 | ScopeControl.addLetScope();
|
---|
| 5382 | execute_statement(tmp_execute); // forwarder
|
---|
| 5383 | }
|
---|
| 5384 | if(t->tk==LEX_R_VAR || t->tk==LEX_R_LET)
|
---|
| 5385 | execute_statement(tmp_execute); // initialisation
|
---|
| 5386 | else
|
---|
| 5387 | execute_base(tmp_execute); // initialisation
|
---|
| 5388 | if(!execute(tmp_execute)) break;
|
---|
| 5389 | }
|
---|
| 5390 |
|
---|
| 5391 | bool loopCond = true; // Empty Condition -->always true
|
---|
| 5392 | if(LoopData.type != CScriptTokenDataLoop::DO && LoopData.condition.size()) {
|
---|
| 5393 | t->pushTokenScope(LoopData.condition);
|
---|
| 5394 | loopCond = execute_base(execute)->toBoolean();
|
---|
| 5395 | if(!execute) break;
|
---|
| 5396 | }
|
---|
| 5397 | while (loopCond && execute) {
|
---|
| 5398 | t->pushTokenScope(LoopData.body);
|
---|
| 5399 | execute_statement(execute);
|
---|
| 5400 | if(!execute) {
|
---|
| 5401 | bool Continue = false;
|
---|
| 5402 | if(execute.isBreakContinue()
|
---|
| 5403 | &&
|
---|
| 5404 | (execute.target.empty() || find(LoopData.labels.begin(), LoopData.labels.end(), execute.target) != LoopData.labels.end())) {
|
---|
| 5405 | Continue = execute.isContinue();
|
---|
| 5406 | execute.set(CScriptResult::Normal, false);
|
---|
| 5407 | }
|
---|
| 5408 | if(!Continue) break;
|
---|
| 5409 | }
|
---|
| 5410 | if(LoopData.type == CScriptTokenDataLoop::FOR && execute && LoopData.iter.size()) {
|
---|
| 5411 | t->pushTokenScope(LoopData.iter);
|
---|
| 5412 | execute_base(execute);
|
---|
| 5413 | }
|
---|
| 5414 | if(execute && LoopData.condition.size()) {
|
---|
| 5415 | t->pushTokenScope(LoopData.condition);
|
---|
| 5416 | loopCond = execute_base(execute)->toBoolean();
|
---|
| 5417 | }
|
---|
| 5418 | }
|
---|
| 5419 | }
|
---|
| 5420 | break;
|
---|
| 5421 | case LEX_R_BREAK:
|
---|
| 5422 | case LEX_R_CONTINUE:
|
---|
| 5423 | if (execute)
|
---|
| 5424 | {
|
---|
| 5425 | CScriptResult::TYPE type = t->tk==LEX_R_BREAK ? CScriptResult::Break : CScriptResult::Continue;
|
---|
| 5426 | string label;
|
---|
| 5427 | t->match(t->tk);
|
---|
| 5428 | if(t->tk == LEX_ID) {
|
---|
| 5429 | label = t->tkStr();
|
---|
| 5430 | t->match(LEX_ID);
|
---|
| 5431 | }
|
---|
| 5432 | t->match(';');
|
---|
| 5433 | execute.set(type, label);
|
---|
| 5434 | } else
|
---|
| 5435 | t->skip(t->getToken().Int());
|
---|
| 5436 | break;
|
---|
| 5437 | case LEX_R_RETURN:
|
---|
| 5438 | if (execute) {
|
---|
| 5439 | t->match(LEX_R_RETURN);
|
---|
| 5440 | CScriptVarPtr result;
|
---|
| 5441 | if (t->tk != ';')
|
---|
| 5442 | result = execute_base(execute);
|
---|
| 5443 | t->match(';');
|
---|
| 5444 | execute.set(CScriptResult::Return, result);
|
---|
| 5445 | } else
|
---|
| 5446 | t->skip(t->getToken().Int());
|
---|
| 5447 | break;
|
---|
| 5448 | case LEX_R_FUNCTION:
|
---|
| 5449 | if(execute) {
|
---|
| 5450 | CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(t->getToken());
|
---|
| 5451 | scope()->scopeVar()->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT);
|
---|
| 5452 | }
|
---|
| 5453 | case LEX_T_FUNCTION_PLACEHOLDER:
|
---|
| 5454 | t->match(t->tk);
|
---|
| 5455 | break;
|
---|
| 5456 | case LEX_T_TRY:
|
---|
| 5457 | if(execute) {
|
---|
| 5458 | CScriptTokenDataTry &TryData = t->getToken().Try();
|
---|
| 5459 |
|
---|
| 5460 | bool old_haveTry = haveTry;
|
---|
| 5461 | haveTry = true;
|
---|
| 5462 |
|
---|
| 5463 | // execute try-block
|
---|
| 5464 | t->pushTokenScope(TryData.tryBlock);
|
---|
| 5465 | execute_block(execute);
|
---|
| 5466 |
|
---|
| 5467 | bool isThrow = execute.isThrow();
|
---|
| 5468 |
|
---|
| 5469 | if(isThrow) {
|
---|
| 5470 | // execute catch-blocks
|
---|
| 5471 | for(CScriptTokenDataTry::CatchBlock_it catchBlock = TryData.catchBlocks.begin(); catchBlock!=TryData.catchBlocks.end(); catchBlock++) {
|
---|
| 5472 | CScriptResult catch_execute;
|
---|
| 5473 | CScopeControl ScopeControl(this);
|
---|
| 5474 | ScopeControl.addLetScope();
|
---|
| 5475 | t->pushTokenScope(catchBlock->condition); // condition;
|
---|
| 5476 | execute_statement(catch_execute); // forwarder
|
---|
| 5477 | assign_destructuring_var(0, *catchBlock->indentifiers, execute.value, catch_execute);
|
---|
| 5478 | bool condition = true;
|
---|
| 5479 | if(catchBlock->condition.size()>1)
|
---|
| 5480 | condition = execute_base(catch_execute)->toBoolean();
|
---|
| 5481 | if(!catch_execute) {
|
---|
| 5482 | execute = catch_execute;
|
---|
| 5483 | break;
|
---|
| 5484 | } else if(condition) {
|
---|
| 5485 | t->pushTokenScope(catchBlock->block); // condition;
|
---|
| 5486 | execute_block(catch_execute);
|
---|
| 5487 | execute = catch_execute;
|
---|
| 5488 | break;
|
---|
| 5489 | }
|
---|
| 5490 | }
|
---|
| 5491 | }
|
---|
| 5492 | if(TryData.finallyBlock.size()) {
|
---|
| 5493 | CScriptResult finally_execute; // alway execute finally-block
|
---|
| 5494 | t->pushTokenScope(TryData.finallyBlock); // finally;
|
---|
| 5495 | execute_block(finally_execute);
|
---|
| 5496 | execute(finally_execute);
|
---|
| 5497 | }
|
---|
| 5498 | // restore haveTry
|
---|
| 5499 | haveTry = old_haveTry;
|
---|
| 5500 | if(execute.isThrow() && !haveTry) { // (exception in catch or finally or no catch-clause found) and no parent try-block
|
---|
| 5501 | if(execute.value->isError())
|
---|
| 5502 | throw CScriptVarErrorPtr(execute.value)->toCScriptException();
|
---|
| 5503 | throw new CScriptException("uncaught exception: '"+execute.value->toString()+"'", t->currentFile, t->currentLine(), t->currentColumn());
|
---|
| 5504 | }
|
---|
| 5505 |
|
---|
| 5506 | }
|
---|
| 5507 | t->match(LEX_T_TRY);
|
---|
| 5508 | break;
|
---|
| 5509 | case LEX_R_THROW:
|
---|
| 5510 | if(execute) {
|
---|
| 5511 | CScriptTokenizer::ScriptTokenPosition tokenPos = t->getPos();
|
---|
| 5512 | // int tokenStart = t->getToken().pos;
|
---|
| 5513 | t->match(LEX_R_THROW);
|
---|
| 5514 | CScriptVarPtr a = execute_base(execute);
|
---|
| 5515 | if(execute) {
|
---|
| 5516 | if(haveTry)
|
---|
| 5517 | execute.set(CScriptResult::Throw, a);
|
---|
| 5518 | else
|
---|
| 5519 | throw new CScriptException("uncaught exception: '"+a->toString(execute)+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn());
|
---|
| 5520 | }
|
---|
| 5521 | } else
|
---|
| 5522 | t->skip(t->getToken().Int());
|
---|
| 5523 | break;
|
---|
| 5524 | case LEX_R_SWITCH:
|
---|
| 5525 | if(execute) {
|
---|
| 5526 | t->match(LEX_R_SWITCH);
|
---|
| 5527 | t->match('(');
|
---|
| 5528 | CScriptVarPtr SwitchValue = execute_base(execute);
|
---|
| 5529 | t->match(')');
|
---|
| 5530 | if(execute) {
|
---|
| 5531 | t->match('{');
|
---|
| 5532 | CScopeControl ScopeControl(this);
|
---|
| 5533 | if(t->tk == LEX_T_FORWARD) {
|
---|
| 5534 | ScopeControl.addLetScope(); // add let-scope only if needed
|
---|
| 5535 | execute_statement(execute); // execute forwarder
|
---|
| 5536 | }
|
---|
| 5537 | CScriptTokenizer::ScriptTokenPosition defaultStart = t->getPos();
|
---|
| 5538 | bool hasDefault = false, found = false;
|
---|
| 5539 | while (t->tk) {
|
---|
| 5540 | switch(t->tk) {
|
---|
| 5541 | case LEX_R_CASE:
|
---|
| 5542 | if(!execute)
|
---|
| 5543 | t->skip(t->getToken().Int()); // skip up to'}'
|
---|
| 5544 | else if(found) { // execute && found
|
---|
| 5545 | t->match(LEX_R_CASE);
|
---|
| 5546 | t->skip(t->getToken().Int()); // skip up to ':'
|
---|
| 5547 | t->match(':'); // skip ':' and execute all after ':'
|
---|
| 5548 | } else { // execute && !found
|
---|
| 5549 | t->match(LEX_R_CASE);
|
---|
| 5550 | t->match(LEX_T_SKIP); // skip 'L_T_SKIP'
|
---|
| 5551 | CScriptVarLinkPtr CaseValue = execute_base(execute);
|
---|
| 5552 | CaseValue = mathsOp(execute, CaseValue, SwitchValue, LEX_TYPEEQUAL);
|
---|
| 5553 | if(execute) {
|
---|
| 5554 | found = CaseValue->toBoolean();
|
---|
| 5555 | if(found) t->match(':'); // skip ':' and execute all after ':'
|
---|
| 5556 | else t->skip(t->getToken().Int()); // skip up to next 'case'/'default' or '}'
|
---|
| 5557 | } else
|
---|
| 5558 | t->skip(t->getToken().Int()); // skip up to next 'case'/'default' or '}'
|
---|
| 5559 | }
|
---|
| 5560 | break;
|
---|
| 5561 | case LEX_R_DEFAULT:
|
---|
| 5562 | if(!execute)
|
---|
| 5563 | t->skip(t->getToken().Int()); // skip up to'}' NOTE: no extra 'L_T_SKIP' for skipping tp ':'
|
---|
| 5564 | else {
|
---|
| 5565 | t->match(LEX_R_DEFAULT);
|
---|
| 5566 | if(found)
|
---|
| 5567 | t->match(':'); // skip ':' and execute all after ':'
|
---|
| 5568 | else {
|
---|
| 5569 | hasDefault = true; // in fist pass: skip default-area
|
---|
| 5570 | defaultStart = t->getPos(); // remember pos of default
|
---|
| 5571 | t->skip(t->getToken().Int()); // skip up to next 'case' or '}'
|
---|
| 5572 | }
|
---|
| 5573 | }
|
---|
| 5574 | break;
|
---|
| 5575 | case '}':
|
---|
| 5576 | if(execute && !found && hasDefault) { // if not found & have default -> execute default
|
---|
| 5577 | found = true;
|
---|
| 5578 | t->setPos(defaultStart);
|
---|
| 5579 | t->match(':');
|
---|
| 5580 | } else
|
---|
| 5581 | goto end_while; // goto isn't fine but C supports no "break lable;"
|
---|
| 5582 | break;
|
---|
| 5583 | default:
|
---|
| 5584 | ASSERT(found);
|
---|
| 5585 | execute_statement(execute);
|
---|
| 5586 | break;
|
---|
| 5587 | }
|
---|
| 5588 | }
|
---|
| 5589 | end_while:
|
---|
| 5590 | t->match('}');
|
---|
| 5591 | if(execute.isBreak() && execute.target.empty()) {
|
---|
| 5592 | execute.set(CScriptResult::Normal);
|
---|
| 5593 | }
|
---|
| 5594 | } else
|
---|
| 5595 | t->skip(t->getToken().Int());
|
---|
| 5596 | } else
|
---|
| 5597 | t->skip(t->getToken().Int());
|
---|
| 5598 | break;
|
---|
| 5599 | case LEX_T_DUMMY_LABEL:
|
---|
| 5600 | t->match(LEX_T_DUMMY_LABEL);
|
---|
| 5601 | t->match(':');
|
---|
| 5602 | break;
|
---|
| 5603 | case LEX_T_LABEL:
|
---|
| 5604 | {
|
---|
| 5605 | STRING_VECTOR_t Labels;
|
---|
| 5606 | while(t->tk == LEX_T_LABEL) {
|
---|
| 5607 | Labels.push_back(t->tkStr());
|
---|
| 5608 | t->match(LEX_T_LABEL);
|
---|
| 5609 | t->match(':');
|
---|
| 5610 | }
|
---|
| 5611 | if(execute) {
|
---|
| 5612 | execute_statement(execute);
|
---|
| 5613 | if(execute.isBreak() && find(Labels.begin(), Labels.end(), execute.target) != Labels.end()) { // break this label
|
---|
| 5614 | execute.set(CScriptResult::Normal, false);
|
---|
| 5615 | }
|
---|
| 5616 | }
|
---|
| 5617 | else
|
---|
| 5618 | execute_statement(execute);
|
---|
| 5619 | }
|
---|
| 5620 | break;
|
---|
| 5621 | case LEX_EOF:
|
---|
| 5622 | t->match(LEX_EOF);
|
---|
| 5623 | break;
|
---|
| 5624 | default:
|
---|
| 5625 | if(execute) {
|
---|
| 5626 | t->match(LEX_T_SKIP);
|
---|
| 5627 | /* Execute a simple statement that only contains basic arithmetic... */
|
---|
| 5628 | CScriptVarPtr ret = execute_base(execute);
|
---|
| 5629 | if(execute) execute.set(CScriptResult::Normal, CScriptVarPtr(ret));
|
---|
| 5630 | t->match(';');
|
---|
| 5631 | } else
|
---|
| 5632 | t->skip(t->getToken().Int());
|
---|
| 5633 | break;
|
---|
| 5634 | }
|
---|
| 5635 | }
|
---|
| 5636 |
|
---|
| 5637 |
|
---|
| 5638 | /// Finds a child, looking recursively up the scopes
|
---|
| 5639 | CScriptVarLinkPtr CTinyJS::findInScopes(const string &childName) {
|
---|
| 5640 | return scope()->findInScopes(childName);
|
---|
| 5641 | }
|
---|
| 5642 |
|
---|
| 5643 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5644 | /// Object
|
---|
| 5645 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5646 |
|
---|
| 5647 | void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5648 | c->setReturnVar(c->getArgument(0)->toObject());
|
---|
| 5649 | }
|
---|
| 5650 | void CTinyJS::native_Object_getPrototypeOf(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5651 | if(c->getArgumentsLength()>=1) {
|
---|
| 5652 | CScriptVarPtr obj = c->getArgument(0);
|
---|
| 5653 | if(obj->isObject()) {
|
---|
| 5654 | c->setReturnVar(obj->findChild(TINYJS___PROTO___VAR));
|
---|
| 5655 | return;
|
---|
| 5656 | }
|
---|
| 5657 | }
|
---|
| 5658 | c->throwError(TypeError, "argument is not an object");
|
---|
| 5659 | }
|
---|
| 5660 |
|
---|
| 5661 | void CTinyJS::native_Object_setObjectSecure(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5662 | CScriptVarPtr obj = c->getArgument(0);
|
---|
| 5663 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
| 5664 | if(data==(void*)2)
|
---|
| 5665 | obj->freeze();
|
---|
| 5666 | else if(data==(void*)1)
|
---|
| 5667 | obj->seal();
|
---|
| 5668 | else
|
---|
| 5669 | obj->preventExtensions();
|
---|
| 5670 | c->setReturnVar(obj);
|
---|
| 5671 | }
|
---|
| 5672 |
|
---|
| 5673 | void CTinyJS::native_Object_isSecureObject(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5674 | CScriptVarPtr obj = c->getArgument(0);
|
---|
| 5675 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
| 5676 | bool ret;
|
---|
| 5677 | if(data==(void*)2)
|
---|
| 5678 | ret = obj->isFrozen();
|
---|
| 5679 | else if(data==(void*)1)
|
---|
| 5680 | ret = obj->isSealed();
|
---|
| 5681 | else
|
---|
| 5682 | ret = obj->isExtensible();
|
---|
| 5683 | c->setReturnVar(constScriptVar(ret));
|
---|
| 5684 | }
|
---|
| 5685 |
|
---|
| 5686 | void CTinyJS::native_Object_keys(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5687 | CScriptVarPtr obj = c->getArgument(0);
|
---|
| 5688 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
| 5689 | CScriptVarPtr returnVar = c->newScriptVar(Array);
|
---|
| 5690 | c->setReturnVar(returnVar);
|
---|
| 5691 |
|
---|
| 5692 | STRING_SET_t keys;
|
---|
| 5693 | obj->keys(keys, data==0);
|
---|
| 5694 |
|
---|
| 5695 | uint32_t idx=0;
|
---|
| 5696 | for(STRING_SET_it it=keys.begin(); it!=keys.end(); ++it)
|
---|
| 5697 | returnVar->setArrayIndex(idx++, newScriptVar(*it));
|
---|
| 5698 | }
|
---|
| 5699 |
|
---|
| 5700 | void CTinyJS::native_Object_getOwnPropertyDescriptor(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5701 | CScriptVarPtr obj = c->getArgument(0);
|
---|
| 5702 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
| 5703 | c->setReturnVar(obj->getOwnPropertyDescriptor(c->getArgument(1)->toString()));
|
---|
| 5704 | }
|
---|
| 5705 |
|
---|
| 5706 | void CTinyJS::native_Object_defineProperty(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5707 | CScriptVarPtr obj = c->getArgument(0);
|
---|
| 5708 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
| 5709 | string name = c->getArgument(1)->toString();
|
---|
| 5710 | CScriptVarPtr attributes = c->getArgument(2);
|
---|
| 5711 | if(!attributes->isObject()) c->throwError(TypeError, "attributes is not an object");
|
---|
| 5712 | const char *err = obj->defineProperty(name, attributes);
|
---|
| 5713 | if(err) c->throwError(TypeError, err);
|
---|
| 5714 | c->setReturnVar(obj);
|
---|
| 5715 | }
|
---|
| 5716 |
|
---|
| 5717 | void CTinyJS::native_Object_defineProperties(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5718 | bool ObjectCreate = data!=0;
|
---|
| 5719 | CScriptVarPtr obj = c->getArgument(0);
|
---|
| 5720 | if(ObjectCreate) {
|
---|
| 5721 | if(!obj->isObject() && !obj->isNull()) c->throwError(TypeError, "argument is not an object or null");
|
---|
| 5722 | obj = newScriptVar(Object, obj);
|
---|
| 5723 | } else
|
---|
| 5724 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
| 5725 | c->setReturnVar(obj);
|
---|
| 5726 | if(c->getArrayLength()<2) {
|
---|
| 5727 | if(ObjectCreate) return;
|
---|
| 5728 | c->throwError(TypeError, "Object.defineProperties requires 2 arguments");
|
---|
| 5729 | }
|
---|
| 5730 |
|
---|
| 5731 | CScriptVarPtr properties = c->getArgument(1);
|
---|
| 5732 |
|
---|
| 5733 | STRING_SET_t names;
|
---|
| 5734 | properties->keys(names, true);
|
---|
| 5735 |
|
---|
| 5736 | for(STRING_SET_it it=names.begin(); it!=names.end(); ++it) {
|
---|
| 5737 | CScriptVarPtr attributes = properties->findChildWithStringChars(*it).getter();
|
---|
| 5738 | if(!attributes->isObject()) c->throwError(TypeError, "descriptor for "+*it+" is not an object");
|
---|
| 5739 | const char *err = obj->defineProperty(*it, attributes);
|
---|
| 5740 | if(err) c->throwError(TypeError, err);
|
---|
| 5741 | }
|
---|
| 5742 | }
|
---|
| 5743 |
|
---|
| 5744 |
|
---|
| 5745 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5746 | /// Object.prototype
|
---|
| 5747 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5748 |
|
---|
| 5749 | void CTinyJS::native_Object_prototype_hasOwnProperty(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5750 | CScriptVarPtr This = c->getArgument("this");
|
---|
| 5751 | string PropStr = c->getArgument("prop")->toString();
|
---|
| 5752 | CScriptVarLinkPtr Prop = This->findChild(PropStr);
|
---|
| 5753 | bool res = Prop && !Prop->getVarPtr()->isUndefined();
|
---|
| 5754 | if(!res) {
|
---|
| 5755 | CScriptVarStringPtr This_asString = This->getRawPrimitive();
|
---|
| 5756 | if(This_asString) {
|
---|
| 5757 | uint32_t Idx = isArrayIndex(PropStr);
|
---|
| 5758 | res = Idx!=uint32_t(-1) && Idx<This_asString->stringLength();
|
---|
| 5759 | }
|
---|
| 5760 | }
|
---|
| 5761 | c->setReturnVar(c->constScriptVar(res));
|
---|
| 5762 | }
|
---|
| 5763 | void CTinyJS::native_Object_prototype_valueOf(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5764 | c->setReturnVar(c->getArgument("this")->valueOf_CallBack());
|
---|
| 5765 | }
|
---|
| 5766 | void CTinyJS::native_Object_prototype_toString(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5767 | CScriptResult execute;
|
---|
| 5768 | int radix = 10;
|
---|
| 5769 | if(c->getArgumentsLength()>=1) radix = c->getArgument("radix")->toNumber().toInt32();
|
---|
| 5770 | c->setReturnVar(c->getArgument("this")->toString_CallBack(execute, radix));
|
---|
| 5771 | if(!execute) {
|
---|
| 5772 | // TODO
|
---|
| 5773 | }
|
---|
| 5774 | }
|
---|
| 5775 |
|
---|
| 5776 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5777 | /// Array
|
---|
| 5778 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5779 |
|
---|
| 5780 | void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5781 | CScriptVarPtr returnVar = c->newScriptVar(Array);
|
---|
| 5782 | c->setReturnVar(returnVar);
|
---|
| 5783 | int length = c->getArgumentsLength();
|
---|
| 5784 | CScriptVarPtr Argument_0_Var = c->getArgument(0);
|
---|
| 5785 | if(data!=0 && length == 1 && Argument_0_Var->isNumber()) {
|
---|
| 5786 | CNumber Argument_0 = Argument_0_Var->toNumber();
|
---|
| 5787 | uint32_t new_size = Argument_0.toUInt32();
|
---|
| 5788 | if(Argument_0.isFinite() && Argument_0 == new_size)
|
---|
| 5789 | returnVar->setArrayIndex(new_size-1, constScriptVar(Undefined));
|
---|
| 5790 | else
|
---|
| 5791 | c->throwError(RangeError, "invalid array length");
|
---|
| 5792 | } else for(int i=0; i<length; i++)
|
---|
| 5793 | returnVar->setArrayIndex(i, c->getArgument(i));
|
---|
| 5794 | }
|
---|
| 5795 |
|
---|
| 5796 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5797 | /// String
|
---|
| 5798 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5799 |
|
---|
| 5800 | void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5801 | CScriptVarPtr arg;
|
---|
| 5802 | if(c->getArgumentsLength()==0)
|
---|
| 5803 | arg = newScriptVar("");
|
---|
| 5804 | else
|
---|
| 5805 | arg = newScriptVar(c->getArgument(0)->toString());
|
---|
| 5806 | if(data)
|
---|
| 5807 | c->setReturnVar(arg->toObject());
|
---|
| 5808 | else
|
---|
| 5809 | c->setReturnVar(arg);
|
---|
| 5810 | }
|
---|
| 5811 |
|
---|
| 5812 |
|
---|
| 5813 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5814 | /// RegExp
|
---|
| 5815 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5816 | #ifndef NO_REGEXP
|
---|
| 5817 |
|
---|
| 5818 | void CTinyJS::native_RegExp(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5819 | int arglen = c->getArgumentsLength();
|
---|
| 5820 | string RegExp, Flags;
|
---|
| 5821 | if(arglen>=1) {
|
---|
| 5822 | RegExp = c->getArgument(0)->toString();
|
---|
| 5823 | try { regex(RegExp, regex_constants::ECMAScript); } catch(regex_error e) {
|
---|
| 5824 | c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code()));
|
---|
| 5825 | }
|
---|
| 5826 | if(arglen>=2) {
|
---|
| 5827 | Flags = c->getArgument(1)->toString();
|
---|
| 5828 | string::size_type pos = Flags.find_first_not_of("gimy");
|
---|
| 5829 | if(pos != string::npos) {
|
---|
| 5830 | c->throwError(SyntaxError, string("invalid regular expression flag ")+Flags[pos]);
|
---|
| 5831 | }
|
---|
| 5832 | }
|
---|
| 5833 | }
|
---|
| 5834 | c->setReturnVar(newScriptVar(RegExp, Flags));
|
---|
| 5835 | }
|
---|
| 5836 | #endif /* NO_REGEXP */
|
---|
| 5837 |
|
---|
| 5838 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5839 | /// Number
|
---|
| 5840 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5841 |
|
---|
| 5842 | void CTinyJS::native_Number(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5843 | CScriptVarPtr arg;
|
---|
| 5844 | if(c->getArgumentsLength()==0)
|
---|
| 5845 | arg = newScriptVar(0);
|
---|
| 5846 | else
|
---|
| 5847 | arg = newScriptVar(c->getArgument(0)->toNumber());
|
---|
| 5848 | if(data)
|
---|
| 5849 | c->setReturnVar(arg->toObject());
|
---|
| 5850 | else
|
---|
| 5851 | c->setReturnVar(arg);
|
---|
| 5852 | }
|
---|
| 5853 |
|
---|
| 5854 |
|
---|
| 5855 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5856 | /// Boolean
|
---|
| 5857 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5858 |
|
---|
| 5859 | void CTinyJS::native_Boolean(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5860 | CScriptVarPtr arg;
|
---|
| 5861 | if(c->getArgumentsLength()==0)
|
---|
| 5862 | arg = constScriptVar(false);
|
---|
| 5863 | else
|
---|
| 5864 | arg = constScriptVar(c->getArgument(0)->toBoolean());
|
---|
| 5865 | if(data)
|
---|
| 5866 | c->setReturnVar(arg->toObject());
|
---|
| 5867 | else
|
---|
| 5868 | c->setReturnVar(arg);
|
---|
| 5869 | }
|
---|
| 5870 |
|
---|
| 5871 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5872 | /// Iterator
|
---|
| 5873 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5874 |
|
---|
| 5875 | void CTinyJS::native_Iterator(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5876 | if(c->getArgumentsLength()<1) c->throwError(TypeError, "missing argument 0 when calling function Iterator");
|
---|
| 5877 | c->setReturnVar(c->getArgument(0)->toIterator(c->getArgument(1)->toBoolean()?1:3));
|
---|
| 5878 | }
|
---|
| 5879 |
|
---|
| 5880 |
|
---|
| 5881 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5882 | /// Function
|
---|
| 5883 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5884 |
|
---|
| 5885 | void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5886 | int length = c->getArgumentsLength();
|
---|
| 5887 | string params, body;
|
---|
| 5888 | if(length>=1)
|
---|
| 5889 | body = c->getArgument(length-1)->toString();
|
---|
| 5890 | if(length>=2) {
|
---|
| 5891 | params = c->getArgument(0)->toString();
|
---|
| 5892 | for(int i=1; i<length-1; i++)
|
---|
| 5893 | {
|
---|
| 5894 | params.append(",");
|
---|
| 5895 | params.append(c->getArgument(i)->toString());
|
---|
| 5896 | }
|
---|
| 5897 | }
|
---|
| 5898 | c->setReturnVar(parseFunctionsBodyFromString(params,body));
|
---|
| 5899 | }
|
---|
| 5900 |
|
---|
| 5901 | void CTinyJS::native_Function_prototype_call(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5902 | int length = c->getArgumentsLength();
|
---|
| 5903 | CScriptVarPtr Fnc = c->getArgument("this");
|
---|
| 5904 | if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.call called on incompatible Object");
|
---|
| 5905 | CScriptVarPtr This = c->getArgument(0);
|
---|
| 5906 | vector<CScriptVarPtr> Args;
|
---|
| 5907 | for(int i=1; i<length; i++)
|
---|
| 5908 | Args.push_back(c->getArgument(i));
|
---|
| 5909 | c->setReturnVar(callFunction(Fnc, Args, This));
|
---|
| 5910 | }
|
---|
| 5911 | void CTinyJS::native_Function_prototype_apply(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5912 | int length=0;
|
---|
| 5913 | CScriptVarPtr Fnc = c->getArgument("this");
|
---|
| 5914 | if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.apply called on incompatible Object");
|
---|
| 5915 | // Argument_0
|
---|
| 5916 | CScriptVarPtr This = c->getArgument(0)->toObject();
|
---|
| 5917 | if(This->isNull() || This->isUndefined()) This=root;
|
---|
| 5918 | // Argument_1
|
---|
| 5919 | CScriptVarPtr Array = c->getArgument(1);
|
---|
| 5920 | if(!Array->isNull() && !Array->isUndefined()) {
|
---|
| 5921 | CScriptVarLinkWorkPtr Length = Array->findChild("length");
|
---|
| 5922 | if(!Length) c->throwError(TypeError, "second argument to Function.prototype.apply must be an array or an array like object");
|
---|
| 5923 | length = Length.getter()->toNumber().toInt32();
|
---|
| 5924 | }
|
---|
| 5925 | vector<CScriptVarPtr> Args;
|
---|
| 5926 | for(int i=0; i<length; i++) {
|
---|
| 5927 | CScriptVarLinkPtr value = Array->findChild(int2string(i));
|
---|
| 5928 | if(value) Args.push_back(value);
|
---|
| 5929 | else Args.push_back(constScriptVar(Undefined));
|
---|
| 5930 | }
|
---|
| 5931 | c->setReturnVar(callFunction(Fnc, Args, This));
|
---|
| 5932 | }
|
---|
| 5933 | void CTinyJS::native_Function_prototype_bind(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5934 | int length = c->getArgumentsLength();
|
---|
| 5935 | CScriptVarPtr Fnc = c->getArgument("this");
|
---|
| 5936 | if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.bind called on incompatible Object");
|
---|
| 5937 | CScriptVarPtr This = c->getArgument(0);
|
---|
| 5938 | if(This->isUndefined() || This->isNull()) This = root;
|
---|
| 5939 | vector<CScriptVarPtr> Args;
|
---|
| 5940 | for(int i=1; i<length; i++) Args.push_back(c->getArgument(i));
|
---|
| 5941 | c->setReturnVar(newScriptVarFunctionBounded(Fnc, This, Args));
|
---|
| 5942 | }
|
---|
| 5943 |
|
---|
| 5944 |
|
---|
| 5945 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5946 | /// Error
|
---|
| 5947 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5948 |
|
---|
| 5949 | static CScriptVarPtr _newError(CTinyJS *context, ERROR_TYPES type, const CFunctionsScopePtr &c) {
|
---|
| 5950 | int i = c->getArgumentsLength();
|
---|
| 5951 | string message, fileName;
|
---|
| 5952 | int line=-1, column=-1;
|
---|
| 5953 | if(i>0) message = c->getArgument(0)->toString();
|
---|
| 5954 | if(i>1) fileName = c->getArgument(1)->toString();
|
---|
| 5955 | if(i>2) line = c->getArgument(2)->toNumber().toInt32();
|
---|
| 5956 | if(i>3) column = c->getArgument(3)->toNumber().toInt32();
|
---|
| 5957 | return ::newScriptVarError(context, type, message.c_str(), fileName.c_str(), line, column);
|
---|
| 5958 | }
|
---|
| 5959 | void CTinyJS::native_Error(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, Error,c)); }
|
---|
| 5960 | void CTinyJS::native_EvalError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, EvalError,c)); }
|
---|
| 5961 | void CTinyJS::native_RangeError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, RangeError,c)); }
|
---|
| 5962 | void CTinyJS::native_ReferenceError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, ReferenceError,c)); }
|
---|
| 5963 | void CTinyJS::native_SyntaxError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, SyntaxError,c)); }
|
---|
| 5964 | void CTinyJS::native_TypeError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, TypeError,c)); }
|
---|
| 5965 |
|
---|
| 5966 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5967 | /// global functions
|
---|
| 5968 | //////////////////////////////////////////////////////////////////////////
|
---|
| 5969 |
|
---|
| 5970 | void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) {
|
---|
| 5971 | string Code = c->getArgument("jsCode")->toString();
|
---|
| 5972 | CScriptVarScopePtr scEvalScope = scopes.back(); // save scope
|
---|
| 5973 | scopes.pop_back(); // go back to the callers scope
|
---|
| 5974 | CScriptResult execute;
|
---|
| 5975 | CScriptTokenizer *oldTokenizer = t; t=0;
|
---|
| 5976 | try {
|
---|
| 5977 | CScriptTokenizer Tokenizer(Code.c_str(), "eval");
|
---|
| 5978 | t = &Tokenizer;
|
---|
| 5979 | do {
|
---|
| 5980 | execute_statement(execute);
|
---|
| 5981 | while (t->tk==';') t->match(';'); // skip empty statements
|
---|
| 5982 | } while (t->tk!=LEX_EOF);
|
---|
| 5983 | } catch (CScriptException *e) { // script exceptions
|
---|
| 5984 | t = oldTokenizer; // restore tokenizer
|
---|
| 5985 | scopes.push_back(scEvalScope); // restore Scopes;
|
---|
| 5986 | if(haveTry) { // an Error in eval is always catchable
|
---|
| 5987 | CScriptVarPtr E = newScriptVarError(this, e->errorType, e->message.c_str(), e->fileName.c_str(), e->lineNumber, e->column);
|
---|
| 5988 | delete e;
|
---|
| 5989 | throw E;
|
---|
| 5990 | } else
|
---|
| 5991 | throw e;
|
---|
| 5992 | } catch (...) { // all other exceptions
|
---|
| 5993 | t = oldTokenizer; // restore tokenizer
|
---|
| 5994 | scopes.push_back(scEvalScope); // restore Scopes;
|
---|
| 5995 | throw; // re-throw
|
---|
| 5996 | }
|
---|
| 5997 | t = oldTokenizer; // restore tokenizer
|
---|
| 5998 | scopes.push_back(scEvalScope); // restore Scopes;
|
---|
| 5999 | if(execute.value)
|
---|
| 6000 | c->setReturnVar(execute.value);
|
---|
| 6001 | }
|
---|
| 6002 |
|
---|
| 6003 | void CTinyJS::native_isNAN(const CFunctionsScopePtr &c, void *data) {
|
---|
| 6004 | c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isNaN()));
|
---|
| 6005 | }
|
---|
| 6006 |
|
---|
| 6007 | void CTinyJS::native_isFinite(const CFunctionsScopePtr &c, void *data) {
|
---|
| 6008 | c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isFinite()));
|
---|
| 6009 | }
|
---|
| 6010 |
|
---|
| 6011 | void CTinyJS::native_parseInt(const CFunctionsScopePtr &c, void *) {
|
---|
| 6012 | CNumber result;
|
---|
| 6013 | result.parseInt(c->getArgument("string")->toString(), c->getArgument("radix")->toNumber().toInt32());
|
---|
| 6014 | c->setReturnVar(c->newScriptVar(result));
|
---|
| 6015 | }
|
---|
| 6016 |
|
---|
| 6017 | void CTinyJS::native_parseFloat(const CFunctionsScopePtr &c, void *) {
|
---|
| 6018 | CNumber result;
|
---|
| 6019 | result.parseFloat(c->getArgument("string")->toString());
|
---|
| 6020 | c->setReturnVar(c->newScriptVar(result));
|
---|
| 6021 | }
|
---|
| 6022 |
|
---|
| 6023 |
|
---|
| 6024 |
|
---|
| 6025 | void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) {
|
---|
| 6026 | string Code = "§" + c->getArgument("text")->toString();
|
---|
| 6027 | // "§" is a spezal-token - it's for the tokenizer and means the code begins not in Statement-level
|
---|
| 6028 | CScriptVarLinkWorkPtr returnVar;
|
---|
| 6029 | CScriptTokenizer *oldTokenizer = t; t=0;
|
---|
| 6030 | try {
|
---|
| 6031 | CScriptTokenizer Tokenizer(Code.c_str(), "JSON.parse", 0, -1);
|
---|
| 6032 | t = &Tokenizer;
|
---|
| 6033 | CScriptResult execute;
|
---|
| 6034 | returnVar = execute_literals(execute);
|
---|
| 6035 | t->match(LEX_EOF);
|
---|
| 6036 | } catch (CScriptException *e) {
|
---|
| 6037 | t = oldTokenizer;
|
---|
| 6038 | throw e;
|
---|
| 6039 | }
|
---|
| 6040 | t = oldTokenizer;
|
---|
| 6041 |
|
---|
| 6042 | if(returnVar)
|
---|
| 6043 | c->setReturnVar(returnVar);
|
---|
| 6044 | }
|
---|
| 6045 |
|
---|
| 6046 | void CTinyJS::setTemporaryID_recursive(uint32_t ID) {
|
---|
| 6047 | for(vector<CScriptVarPtr*>::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it)
|
---|
| 6048 | if(**it) (**it)->setTemporaryID_recursive(ID);
|
---|
| 6049 | for(int i=Error; i<ERROR_COUNT; i++)
|
---|
| 6050 | if(errorPrototypes[i]) errorPrototypes[i]->setTemporaryID_recursive(ID);
|
---|
| 6051 | root->setTemporaryID_recursive(ID);
|
---|
| 6052 | }
|
---|
| 6053 |
|
---|
| 6054 | void CTinyJS::ClearUnreferedVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) {
|
---|
| 6055 | uint32_t UniqueID = getUniqueID();
|
---|
| 6056 | setTemporaryID_recursive(UniqueID);
|
---|
| 6057 | if(extra) extra->setTemporaryID_recursive(UniqueID);
|
---|
| 6058 | CScriptVar *p = first;
|
---|
| 6059 | while(p)
|
---|
| 6060 | {
|
---|
| 6061 | if(p->temporaryID != UniqueID)
|
---|
| 6062 | {
|
---|
| 6063 | CScriptVarPtr var = p;
|
---|
| 6064 | var->removeAllChildren();
|
---|
| 6065 | p = var->next;
|
---|
| 6066 | }
|
---|
| 6067 | else
|
---|
| 6068 | p = p->next;
|
---|
| 6069 | }
|
---|
| 6070 | }
|
---|
| 6071 |
|
---|