source: XmlTools2/trunk/libs/TinyJS.cpp@ 914

Last change on this file since 914 was 906, checked in by s10k, 11 years ago
File size: 217.5 KB
RevLine 
[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
72using namespace std;
73
74// -----------------------------------------------------------------------------------
75//////////////////////////////////////////////////////////////////////////
76/// Memory Debug
77//////////////////////////////////////////////////////////////////////////
78
79//#define DEBUG_MEMORY 1
80
81#if DEBUG_MEMORY
82
83vector<CScriptVar*> allocatedVars;
84vector<CScriptVarLink*> allocatedLinks;
85
86void mark_allocated(CScriptVar *v) {
87 allocatedVars.push_back(v);
88}
89
90void 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
99void mark_allocated(CScriptVarLink *v) {
100 allocatedLinks.push_back(v);
101}
102
103void 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
112void 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
131inline bool isWhitespace(char ch) {
132 return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r');
133}
134
135inline bool isNumeric(char ch) {
136 return (ch>='0') && (ch<='9');
137}
138uint32_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}
146inline bool isHexadecimal(char ch) {
147 return ((ch>='0') && (ch<='9')) || ((ch>='a') && (ch<='f')) || ((ch>='A') && (ch<='F'));
148}
149inline bool isOctal(char ch) {
150 return ((ch>='0') && (ch<='7'));
151}
152inline bool isAlpha(char ch) {
153 return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_' || ch=='$';
154}
155
156bool 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
167void 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}
175string int2string(int32_t intData) {
176 ostringstream str;
177 str << intData;
178 return str.str();
179}
180string int2string(uint32_t intData) {
181 ostringstream str;
182 str << intData;
183 return str.str();
184}
185string 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
197string 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
232static 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
243string 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
257CScriptLex::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
264void 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
276void 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}
288void 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
295void 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
310static 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};
311void 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
557bool CScriptTokenDataForwards::compare_fnc_token_by_name::operator()(const CScriptToken& lhs, const CScriptToken& rhs) const {
558 return lhs.Fnc().name < rhs.Fnc().name;
559}
560bool 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
570void CScriptTokenDataForwards::addVars( STRING_VECTOR_t &Vars ) {
571 varNames[VARS].insert(Vars.begin(), Vars.end());
572}
573void CScriptTokenDataForwards::addConsts( STRING_VECTOR_t &Vars ) {
574 varNames[CONSTS].insert(Vars.begin(), Vars.end());
575}
576std::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
585std::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
599std::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
618std::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
640string 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
685void 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
692std::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
727void 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
738string 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
758typedef struct { int id; const char *str; bool need_space; } token2str_t;
759static 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)])
793static token2str_t *reserved_words_end = ARRAY_END(reserved_words_begin);//&reserved_words_begin[ARRAY_LENGTH(reserved_words_begin)];
794static token2str_t *str2reserved_begin[sizeof(reserved_words_begin)/sizeof(reserved_words_begin[0])];
795static token2str_t **str2reserved_end = &str2reserved_begin[sizeof(str2reserved_begin)/sizeof(str2reserved_begin[0])];
796static 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};
842static token2str_t *tokens2str_end = &tokens2str_begin[sizeof(tokens2str_begin)/sizeof(tokens2str_begin[0])];
843struct 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};
857static 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}
866static bool tokens2str_sorted = tokens2str_sort();
867
868CScriptToken::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}
894CScriptToken::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
916CScriptToken::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
924CScriptToken &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}
942string CScriptToken::getParsableString(TOKEN_VECT &Tokens, const string &IndentString, const string &Indent) {
943 return getParsableString(Tokens.begin(), Tokens.end(), IndentString, Indent);
944}
945string 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
1015void 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}
1023string 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}
1048const 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}
1056int 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
1071CScriptTokenizer::CScriptTokenizer() : l(0), prevPos(&tokens) {
1072}
1073CScriptTokenizer::CScriptTokenizer(CScriptLex &Lexer) : l(0), prevPos(&tokens) {
1074 tokenizeCode(Lexer);
1075}
1076CScriptTokenizer::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}
1080void 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
1106void 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
1120void CScriptTokenizer::match(int ExpectedToken, int AlternateToken/*=-1*/) {
1121 if(check(ExpectedToken, AlternateToken))
1122 getNextToken();
1123}
1124bool 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}
1139void CScriptTokenizer::pushTokenScope(TOKEN_VECT &Tokens) {
1140 tokenScopeStack.push_back(ScriptTokenPosition(&Tokens));
1141 tk = getToken().token;
1142}
1143
1144void CScriptTokenizer::setPos(ScriptTokenPosition &TokenPos) {
1145 ASSERT( TokenPos.tokens == tokenScopeStack.back().tokens);
1146 tokenScopeStack.back().pos = TokenPos.pos;
1147 tk = getToken().token;
1148}
1149void 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
1155static 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
1161enum {
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};
1173void 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}
1222void 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}
1263void 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
1274static 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}
1289static 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}
1293void 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
1331void 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
1352void 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
1433static void tokenizeVarIdentifierDestructuring( CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t *VarNames );
1434static 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}
1453static 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}
1466static 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}
1481CScriptToken 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
1490void 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
1541void 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
1621void CScriptTokenizer::tokenizeVarNoConst( ScriptTokenState &State, int Flags) {
1622 l->check(LEX_R_VAR);
1623 tokenizeVarAndConst(State, Flags);
1624}
1625void 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
1674void 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}
1765void 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
1807void 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}
1871void 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}
1889void 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
1909void 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
1970void 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
1983void 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}
1996void 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}
2006void 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}
2014void 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
2029void 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}
2037void 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
2110int 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}
2117int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token) {
2118 int ret = Tokens.size();
2119 Tokens.push_back(Token);
2120 return ret;
2121}
2122void 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}
2128void 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}
2134void 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
2144void 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}
2153void 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
2162CScriptVar::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}
2181CScriptVar::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}
2203CScriptVar::~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
2220bool CScriptVar::isObject() {return false;}
2221bool CScriptVar::isError() {return false;}
2222bool CScriptVar::isArray() {return false;}
2223bool CScriptVar::isRegExp() {return false;}
2224bool CScriptVar::isAccessor() {return false;}
2225bool CScriptVar::isNull() {return false;}
2226bool CScriptVar::isUndefined() {return false;}
2227bool CScriptVar::isNaN() {return false;}
2228bool CScriptVar::isString() {return false;}
2229bool CScriptVar::isInt() {return false;}
2230bool CScriptVar::isBool() {return false;}
2231int CScriptVar::isInfinity() { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar
2232bool CScriptVar::isDouble() {return false;}
2233bool CScriptVar::isRealNumber() {return false;}
2234bool CScriptVar::isNumber() {return false;}
2235bool CScriptVar::isPrimitive() {return false;}
2236bool CScriptVar::isFunction() {return false;}
2237bool CScriptVar::isNative() {return false;}
2238bool CScriptVar::isBounded() {return false;}
2239bool CScriptVar::isIterator() {return false;}
2240
2241//////////////////////////////////////////////////////////////////////////
2242/// Value
2243//////////////////////////////////////////////////////////////////////////
2244
2245CScriptVarPrimitivePtr CScriptVar::getRawPrimitive() {
2246 return CScriptVarPrimitivePtr(); // default NULL-Ptr
2247}
2248CScriptVarPrimitivePtr CScriptVar::toPrimitive() {
2249 return toPrimitive_hintNumber();
2250}
2251
2252CScriptVarPrimitivePtr CScriptVar::toPrimitive(CScriptResult &execute) {
2253 return toPrimitive_hintNumber(execute);
2254}
2255
2256CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(int32_t radix) {
2257 CScriptResult execute;
2258 CScriptVarPrimitivePtr var = toPrimitive_hintString(execute, radix);
2259 execute.cThrow();
2260 return var;
2261}
2262CScriptVarPrimitivePtr 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}
2277CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber() {
2278 CScriptResult execute;
2279 CScriptVarPrimitivePtr var = toPrimitive_hintNumber(execute);
2280 execute.cThrow();
2281 return var;
2282}
2283CScriptVarPrimitivePtr 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
2299CScriptVarPtr 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}
2312CScriptVarPtr CScriptVar::valueOf_CallBack() {
2313 return this;
2314}
2315
2316CScriptVarPtr 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}
2330CScriptVarPtr CScriptVar::toString_CallBack(CScriptResult &execute, int radix/*=0*/) {
2331 return this;
2332}
2333
2334CNumber CScriptVar::toNumber() { return toPrimitive_hintNumber()->toNumber_Callback(); };
2335CNumber CScriptVar::toNumber(CScriptResult &execute) { return toPrimitive_hintNumber(execute)->toNumber_Callback(); };
2336bool CScriptVar::toBoolean() { return true; }
2337string CScriptVar::toString(int32_t radix) { return toPrimitive_hintString(radix)->toCString(radix); }
2338string CScriptVar::toString(CScriptResult &execute, int32_t radix) { return toPrimitive_hintString(execute, radix)->toCString(radix); }
2339
2340int CScriptVar::getInt() { return toNumber().toInt32(); }
2341double CScriptVar::getDouble() { return toNumber().toDouble(); }
2342bool CScriptVar::getBool() { return toBoolean(); }
2343string CScriptVar::getString() { return toPrimitive_hintString()->toCString(); }
2344
2345CScriptTokenDataFnc *CScriptVar::getFunctionData() { return 0; }
2346
2347CScriptVarPtr CScriptVar::toIterator(int Mode/*=3*/) {
2348 CScriptResult execute;
2349 CScriptVarPtr var = toIterator(execute, Mode);
2350 execute.cThrow();
2351 return var;
2352}
2353CScriptVarPtr 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
2362string CScriptVar::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
2363 getParsableStringRecursionsCheck();
2364 return toString();
2365}
2366
2367CScriptVarPtr CScriptVar::getNumericVar() { return newScriptVar(toNumber()); }
2368
2369////// Flags
2370
2371void CScriptVar::seal() {
2372 preventExtensions();
2373 for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
2374 (*it)->setConfigurable(false);
2375}
2376bool 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}
2382void CScriptVar::freeze() {
2383 preventExtensions();
2384 for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
2385 (*it)->setConfigurable(false), (*it)->setWritable(false);
2386}
2387bool 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
2396CScriptVarPtr 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}
2427const 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;
2481cant_redefine:
2482 return "can't redefine non-configurable property";
2483}
2484
2485CScriptVarLinkPtr 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
2493CScriptVarLinkWorkPtr 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
2507CScriptVarLinkPtr 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
2521CScriptVarLinkWorkPtr 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}
2531CScriptVarLinkPtr 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
2541CScriptVarLinkPtr 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
2548CScriptVarLinkPtr 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
2558void 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
2577CScriptVarLinkPtr 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}
2592CScriptVarLinkPtr CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) {
2593 return addChildOrReplace(childName, child, linkFlags);
2594}
2595CScriptVarLinkPtr 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
2608bool 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}
2621void CScriptVar::removeAllChildren() {
2622 Childs.clear();
2623}
2624
2625CScriptVarPtr CScriptVar::getArrayIndex(uint32_t idx) {
2626 CScriptVarLinkPtr link = findChild(int2string(idx));
2627 if (link) return link;
2628 else return constScriptVar(Undefined); // undefined
2629}
2630
2631void 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
2642uint32_t CScriptVar::getArrayLength() {
2643 if (!isArray() || Childs.size()==0) return 0;
2644 return isArrayIndex(Childs.back()->getName())+1;
2645}
2646
2647CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) {
2648 CScriptResult execute;
2649 return context->mathsOp(execute, this, b, op);
2650}
2651
2652void CScriptVar::trace(const string &name) {
2653 string indentStr;
2654 uint32_t uniqueID = context->getUniqueID();
2655 trace(indentStr, uniqueID, name);
2656}
2657void 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
2679string 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
2695CScriptVar *CScriptVar::ref() {
2696 refs++;
2697 return this;
2698}
2699void 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
2706int CScriptVar::getRefs() {
2707 return refs;
2708}
2709
2710void 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
2724CScriptVarLink::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
2732CScriptVarLink::~CScriptVarLink() {
2733#if DEBUG_MEMORY
2734 mark_deallocated(this);
2735#endif
2736}
2737
2738CScriptVarLink *CScriptVarLink::ref() {
2739 refs++;
2740 return this;
2741}
2742void 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
2754CScriptVarLinkPtr & 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
2767CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter() {
2768 return CScriptVarLinkWorkPtr(*this).getter();
2769}
2770
2771CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter( CScriptResult &execute ) {
2772 return CScriptVarLinkWorkPtr(*this).getter(execute);
2773}
2774
2775CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( const CScriptVarPtr &Var ) {
2776 return CScriptVarLinkWorkPtr(*this).setter(Var);
2777}
2778
2779CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) {
2780 return CScriptVarLinkWorkPtr(*this).setter(execute, Var);
2781}
2782
2783bool 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
2801CScriptVarLinkWorkPtr 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}
2810CScriptVarLinkWorkPtr 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}
2823CScriptVarLinkWorkPtr 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
2833CScriptVarLinkWorkPtr 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
2857CScriptVarPrimitive::~CScriptVarPrimitive(){}
2858
2859bool CScriptVarPrimitive::isPrimitive() { return true; }
2860CScriptVarPrimitivePtr CScriptVarPrimitive::getRawPrimitive() { return this; }
2861bool CScriptVarPrimitive::toBoolean() { return false; }
2862CScriptVarPtr CScriptVarPrimitive::toObject() { return this; }
2863CScriptVarPtr CScriptVarPrimitive::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
2864 return newScriptVar(toCString(radix));
2865}
2866
2867
2868//////////////////////////////////////////////////////////////////////////
2869// CScriptVarUndefined
2870//////////////////////////////////////////////////////////////////////////
2871
2872declare_dummy_t(Undefined);
2873CScriptVarUndefined::CScriptVarUndefined(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { }
2874CScriptVarUndefined::~CScriptVarUndefined() {}
2875CScriptVarPtr CScriptVarUndefined::clone() { return new CScriptVarUndefined(*this); }
2876bool CScriptVarUndefined::isUndefined() { return true; }
2877
2878CNumber CScriptVarUndefined::toNumber_Callback() { return NaN; }
2879string CScriptVarUndefined::toCString(int radix/*=0*/) { return "undefined"; }
2880string CScriptVarUndefined::getVarType() { return "undefined"; }
2881
2882
2883//////////////////////////////////////////////////////////////////////////
2884// CScriptVarNull
2885//////////////////////////////////////////////////////////////////////////
2886
2887declare_dummy_t(Null);
2888CScriptVarNull::CScriptVarNull(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { }
2889CScriptVarNull::~CScriptVarNull() {}
2890CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); }
2891bool CScriptVarNull::isNull() { return true; }
2892
2893CNumber CScriptVarNull::toNumber_Callback() { return 0; }
2894string CScriptVarNull::toCString(int radix/*=0*/) { return "null"; }
2895string CScriptVarNull::getVarType() { return "null"; }
2896
2897
2898//////////////////////////////////////////////////////////////////////////
2899/// CScriptVarString
2900//////////////////////////////////////////////////////////////////////////
2901
2902CScriptVarString::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}
2911CScriptVarString::~CScriptVarString() {}
2912CScriptVarPtr CScriptVarString::clone() { return new CScriptVarString(*this); }
2913bool CScriptVarString::isString() { return true; }
2914
2915bool CScriptVarString::toBoolean() { return data.length()!=0; }
2916CNumber CScriptVarString::toNumber_Callback() { return data.c_str(); }
2917string CScriptVarString::toCString(int radix/*=0*/) { return data; }
2918
2919string CScriptVarString::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getJSString(data); }
2920string CScriptVarString::getVarType() { return "string"; }
2921
2922CScriptVarPtr CScriptVarString::toObject() {
2923 CScriptVarPtr ret = newScriptVar(CScriptVarPrimitivePtr(this), context->stringPrototype);
2924 ret->addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT);
2925 return ret;
2926}
2927
2928CScriptVarPtr CScriptVarString::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
2929 return this;
2930}
2931
2932int 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
2944NegativeZero_t NegativeZero;
2945declare_dummy_t(NaN);
2946Infinity InfinityPositive(1);
2947Infinity InfinityNegative(-1);
2948#if 1
2949static inline bool _isNaN(volatile double *Value1, volatile double *Value2) {
2950 return !(*Value1==*Value2);
2951}
2952
2953static inline bool isNaN(double Value) {
2954 return _isNaN(&Value, &Value);
2955}
2956inline bool isNegZero(double d) {
2957 double x=-0.0;
2958 return memcmp(&d, &x, sizeof(double))==0;
2959}
2960
2961CNumber &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}
2977CNumber &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}
2993int32_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
3040void 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
3053CNumber 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
3081CNumber 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
3097static 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}
3107static inline int bits(int32_t Value) {
3108 return bits(uint32_t(Value<0?-Value:Value));
3109}
3110
3111CNumber 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
3136CNumber 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
3153CNumber 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
3165CNumber 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
3172CNumber CNumber::floor() const {
3173 if(type != tDouble) return CNumber(*this);
3174 return CNumber(::floor(Double));
3175}
3176
3177CNumber CNumber::ceil() const {
3178 if(type != tDouble) return CNumber(*this);
3179 return CNumber(::ceil(Double));
3180}
3181
3182CNumber CNumber::abs() const {
3183 if(sign()<0) return -CNumber(*this);
3184 else return CNumber(*this);
3185}
3186
3187CNumber 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
3193CNumber 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
3199CNumber 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
3212int 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
3224bool 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
3236bool 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
3250bool 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
3264int 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}
3277char *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
3319static 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}
3383std::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
3420double 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
3444CScriptVarNumber::CScriptVarNumber(CTinyJS *Context, const CNumber &Data) : CScriptVarPrimitive(Context, Context->numberPrototype), data(Data) {}
3445CScriptVarNumber::~CScriptVarNumber() {}
3446CScriptVarPtr CScriptVarNumber::clone() { return new CScriptVarNumber(*this); }
3447bool CScriptVarNumber::isNumber() { return true; }
3448bool CScriptVarNumber::isInt() { return data.isInt32(); }
3449bool CScriptVarNumber::isDouble() { return data.isDouble(); }
3450bool CScriptVarNumber::isRealNumber() { return isInt() || isDouble(); }
3451bool CScriptVarNumber::isNaN() { return data.isNaN(); }
3452int CScriptVarNumber::isInfinity() { return data.isInfinity(); }
3453
3454bool CScriptVarNumber::toBoolean() { return data.toBoolean(); }
3455CNumber CScriptVarNumber::toNumber_Callback() { return data; }
3456string CScriptVarNumber::toCString(int radix/*=0*/) { return data.toString(radix); }
3457
3458string CScriptVarNumber::getVarType() { return "number"; }
3459
3460CScriptVarPtr CScriptVarNumber::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->numberPrototype); }
3461inline 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
3475CScriptVarBool::CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarPrimitive(Context, Context->booleanPrototype), data(Data) {}
3476CScriptVarBool::~CScriptVarBool() {}
3477CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); }
3478bool CScriptVarBool::isBool() { return true; }
3479
3480bool CScriptVarBool::toBoolean() { return data; }
3481CNumber CScriptVarBool::toNumber_Callback() { return data?1:0; }
3482string CScriptVarBool::toCString(int radix/*=0*/) { return data ? "true" : "false"; }
3483
3484string CScriptVarBool::getVarType() { return "boolean"; }
3485
3486CScriptVarPtr CScriptVarBool::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->booleanPrototype); }
3487
3488//////////////////////////////////////////////////////////////////////////
3489/// CScriptVarObject
3490//////////////////////////////////////////////////////////////////////////
3491
3492declare_dummy_t(Object);
3493declare_dummy_t(StopIteration);
3494CScriptVarObject::CScriptVarObject(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { }
3495CScriptVarObject::~CScriptVarObject() {}
3496CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); }
3497CScriptVarPrimitivePtr CScriptVarObject::getRawPrimitive() { return value; }
3498bool CScriptVarObject::isObject() { return true; }
3499
3500string 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}
3521string CScriptVarObject::getVarType() { return "object"; }
3522
3523CScriptVarPtr CScriptVarObject::toObject() { return this; }
3524
3525CScriptVarPtr CScriptVarObject::valueOf_CallBack() {
3526 if(value)
3527 return value->valueOf_CallBack();
3528 return CScriptVar::valueOf_CallBack();
3529}
3530CScriptVarPtr CScriptVarObject::toString_CallBack(CScriptResult &execute, int radix) {
3531 if(value)
3532 return value->toString_CallBack(execute, radix);
3533 return newScriptVar("[object Object]");
3534};
3535
3536void 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
3546const char *ERROR_NAME[] = {"Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError"};
3547
3548CScriptVarError::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
3555CScriptVarError::~CScriptVarError() {}
3556CScriptVarPtr CScriptVarError::clone() { return new CScriptVarError(*this); }
3557bool CScriptVarError::isError() { return true; }
3558
3559CScriptVarPtr 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
3575CScriptException *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
3596declare_dummy_t(Array);
3597CScriptVarArray::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
3604CScriptVarArray::~CScriptVarArray() {}
3605CScriptVarPtr CScriptVarArray::clone() { return new CScriptVarArray(*this); }
3606bool CScriptVarArray::isArray() { return true; }
3607string 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}
3625CScriptVarPtr 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
3636void 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
3647CScriptVarRegExp::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}
3655CScriptVarRegExp::~CScriptVarRegExp() {}
3656CScriptVarPtr CScriptVarRegExp::clone() { return new CScriptVarRegExp(*this); }
3657bool 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(); }
3663CScriptVarPtr CScriptVarRegExp::toString_CallBack(CScriptResult &execute, int radix) {
3664 return newScriptVar("/"+regexp+"/"+flags);
3665}
3666void CScriptVarRegExp::native_Global(const CFunctionsScopePtr &c, void *data) {
3667 c->setReturnVar(constScriptVar(Global()));
3668}
3669void CScriptVarRegExp::native_IgnoreCase(const CFunctionsScopePtr &c, void *data) {
3670 c->setReturnVar(constScriptVar(IgnoreCase()));
3671}
3672void CScriptVarRegExp::native_Multiline(const CFunctionsScopePtr &c, void *data) {
3673 c->setReturnVar(constScriptVar(Multiline()));
3674}
3675void CScriptVarRegExp::native_Sticky(const CFunctionsScopePtr &c, void *data) {
3676 c->setReturnVar(constScriptVar(Sticky()));
3677}
3678void CScriptVarRegExp::native_Source(const CFunctionsScopePtr &c, void *data) {
3679 c->setReturnVar(newScriptVar(regexp));
3680}
3681unsigned int CScriptVarRegExp::LastIndex() {
3682 CScriptVarPtr lastIndex = findChild("lastIndex");
3683 if(lastIndex) return lastIndex->toNumber().toInt32();
3684 return 0;
3685}
3686void CScriptVarRegExp::LastIndex(unsigned int Idx) {
3687 addChildOrReplace("lastIndex", newScriptVar((int)Idx));
3688}
3689
3690CScriptVarPtr 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 }
3717failed:
3718 if(global || sticky)
3719 LastIndex(0);
3720 if(Test) return constScriptVar(false);
3721 return constScriptVar(Null);
3722}
3723
3724const 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);
3752CScriptVarDefaultIterator::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}
3758CScriptVarDefaultIterator::~CScriptVarDefaultIterator() {}
3759CScriptVarPtr CScriptVarDefaultIterator::clone() { return new CScriptVarDefaultIterator(*this); }
3760bool CScriptVarDefaultIterator::isIterator() {return true;}
3761void 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
3782CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVarObject(Context, Context->functionPrototype), data(0) {
3783 setFunctionData(Data);
3784}
3785CScriptVarFunction::~CScriptVarFunction() { setFunctionData(0); }
3786CScriptVarPtr CScriptVarFunction::clone() { return new CScriptVarFunction(*this); }
3787bool CScriptVarFunction::isObject() { return true; }
3788bool CScriptVarFunction::isFunction() { return true; }
3789bool CScriptVarFunction::isPrimitive() { return false; }
3790
3791//string CScriptVarFunction::getString() {return "[ Function ]";}
3792string CScriptVarFunction::getVarType() { return "function"; }
3793string 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
3810CScriptVarPtr CScriptVarFunction::toString_CallBack(CScriptResult &execute, int radix){
3811 bool hasRecursion;
3812 return newScriptVar(getParsableString("", " ", 0, hasRecursion));
3813}
3814
3815CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; }
3816
3817void 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
3833CScriptVarFunctionBounded::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}
3840CScriptVarFunctionBounded::~CScriptVarFunctionBounded(){}
3841CScriptVarPtr CScriptVarFunctionBounded::clone() { return new CScriptVarFunctionBounded(*this); }
3842bool CScriptVarFunctionBounded::isBounded() { return true; }
3843void 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
3850CScriptVarPtr 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
3862CScriptVarFunctionNative::~CScriptVarFunctionNative() {}
3863bool CScriptVarFunctionNative::isNative() { return true; }
3864
3865
3866//////////////////////////////////////////////////////////////////////////
3867/// CScriptVarFunctionNativeCallback
3868//////////////////////////////////////////////////////////////////////////
3869
3870CScriptVarFunctionNativeCallback::~CScriptVarFunctionNativeCallback() {}
3871CScriptVarPtr CScriptVarFunctionNativeCallback::clone() { return new CScriptVarFunctionNativeCallback(*this); }
3872void CScriptVarFunctionNativeCallback::callFunction(const CFunctionsScopePtr &c) { jsCallback(c, jsUserData); }
3873
3874
3875//////////////////////////////////////////////////////////////////////////
3876/// CScriptVarAccessor
3877//////////////////////////////////////////////////////////////////////////
3878
3879declare_dummy_t(Accessor);
3880CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context) : CScriptVarObject(Context, Context->objectPrototype) { }
3881CScriptVarAccessor::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
3890CScriptVarAccessor::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
3897CScriptVarAccessor::~CScriptVarAccessor() {}
3898CScriptVarPtr CScriptVarAccessor::clone() { return new CScriptVarAccessor(*this); }
3899bool CScriptVarAccessor::isAccessor() { return true; }
3900bool CScriptVarAccessor::isPrimitive() { return false; }
3901string CScriptVarAccessor::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
3902 return "";
3903}
3904string CScriptVarAccessor::getVarType() { return "accessor"; }
3905
3906
3907//////////////////////////////////////////////////////////////////////////
3908/// CScriptVarScope
3909//////////////////////////////////////////////////////////////////////////
3910
3911declare_dummy_t(Scope);
3912CScriptVarScope::~CScriptVarScope() {}
3913CScriptVarPtr CScriptVarScope::clone() { return CScriptVarPtr(); }
3914bool CScriptVarScope::isObject() { return false; }
3915CScriptVarPtr CScriptVarScope::scopeVar() { return this; } ///< to create var like: var a = ...
3916CScriptVarPtr CScriptVarScope::scopeLet() { return this; } ///< to create var like: let a = ...
3917CScriptVarLinkWorkPtr CScriptVarScope::findInScopes(const string &childName) {
3918 return CScriptVar::findChild(childName);
3919}
3920CScriptVarScopePtr CScriptVarScope::getParent() { return CScriptVarScopePtr(); } ///< no Parent
3921
3922
3923//////////////////////////////////////////////////////////////////////////
3924/// CScriptVarScopeFnc
3925//////////////////////////////////////////////////////////////////////////
3926
3927declare_dummy_t(ScopeFnc);
3928CScriptVarScopeFnc::~CScriptVarScopeFnc() {}
3929CScriptVarLinkWorkPtr 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
3938void CScriptVarScopeFnc::setReturnVar(const CScriptVarPtr &var) {
3939 addChildOrReplace(TINYJS_RETURN_VAR, var);
3940}
3941
3942CScriptVarPtr CScriptVarScopeFnc::getParameter(const string &name) {
3943 return getArgument(name);
3944}
3945
3946CScriptVarPtr CScriptVarScopeFnc::getParameter(int Idx) {
3947 return getArgument(Idx);
3948}
3949CScriptVarPtr CScriptVarScopeFnc::getArgument(const string &name) {
3950 return findChildOrCreate(name);
3951}
3952CScriptVarPtr 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}
3957int CScriptVarScopeFnc::getParameterLength() {
3958 return getArgumentsLength();
3959}
3960int 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
3966void CScriptVarScopeFnc::throwError( ERROR_TYPES ErrorType, const string &message ) {
3967 throw newScriptVarError(context, ErrorType, message.c_str());
3968}
3969
3970
3971//////////////////////////////////////////////////////////////////////////
3972/// CScriptVarScopeLet
3973//////////////////////////////////////////////////////////////////////////
3974
3975declare_dummy_t(ScopeLet);
3976CScriptVarScopeLet::CScriptVarScopeLet(const CScriptVarScopePtr &Parent) // constructor for LetScope
3977 : CScriptVarScope(Parent->getContext()), parent(addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0))
3978 , letExpressionInitMode(false) {}
3979
3980CScriptVarScopeLet::~CScriptVarScopeLet() {}
3981CScriptVarPtr CScriptVarScopeLet::scopeVar() { // to create var like: var a = ...
3982 return getParent()->scopeVar();
3983}
3984CScriptVarScopePtr CScriptVarScopeLet::getParent() { return (CScriptVarPtr)parent; }
3985CScriptVarLinkWorkPtr 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
4001declare_dummy_t(ScopeWith);
4002CScriptVarScopeWith::~CScriptVarScopeWith() {}
4003CScriptVarPtr CScriptVarScopeWith::scopeLet() { // to create var like: let a = ...
4004 return getParent()->scopeLet();
4005}
4006CScriptVarLinkWorkPtr 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
4025extern "C" void _registerFunctions(CTinyJS *tinyJS);
4026extern "C" void _registerStringFunctions(CTinyJS *tinyJS);
4027extern "C" void _registerMathFunctions(CTinyJS *tinyJS);
4028
4029CTinyJS::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(&regexpPrototype);
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
4221CTinyJS::~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
4244void 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}
4251void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message ) {
4252 throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn());
4253}
4254
4255void 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}
4262void 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
4268void CTinyJS::trace() {
4269 root->trace();
4270}
4271
4272void CTinyJS::execute(CScriptTokenizer &Tokenizer) {
4273 evaluateComplex(Tokenizer);
4274}
4275
4276void CTinyJS::execute(const char *Code, const string &File, int Line, int Column) {
4277 evaluateComplex(Code, File, Line, Column);
4278}
4279
4280void CTinyJS::execute(const string &Code, const string &File, int Line, int Column) {
4281 evaluateComplex(Code, File, Line, Column);
4282}
4283
4284CScriptVarLinkPtr 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}
4314CScriptVarLinkPtr CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) {
4315 CScriptTokenizer Tokenizer(Code, File, Line, Column);
4316 return evaluateComplex(Tokenizer);
4317}
4318CScriptVarLinkPtr 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
4323string CTinyJS::evaluate(CScriptTokenizer &Tokenizer) {
4324 return evaluateComplex(Tokenizer)->toString();
4325}
4326string CTinyJS::evaluate(const char *Code, const string &File, int Line, int Column) {
4327 return evaluateComplex(Code, File, Line, Column)->toString();
4328}
4329string CTinyJS::evaluate(const string &Code, const string &File, int Line, int Column) {
4330 return evaluate(Code.c_str(), File, Line, Column);
4331}
4332
4333CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata, int LinkFlags) {
4334 return addNative(funcDesc, ::newScriptVar(this, ptr, userdata), LinkFlags);
4335}
4336
4337CScriptVarFunctionNativePtr 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
4372CScriptVarLinkWorkPtr 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
4382CScriptVarLinkWorkPtr 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}
4387CScriptVarPtr 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
4394CScriptVarPtr 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
4475CScriptVarPtr 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
4547void 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
4581void 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}
4600void 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
4626CScriptVarLinkWorkPtr 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}
4809CScriptVarLinkWorkPtr 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
4845CScriptVarLinkWorkPtr 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
4892bool 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};
4898CScriptVarLinkWorkPtr 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) * / %
4989CScriptVarLinkWorkPtr 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) + -
5007CScriptVarLinkWorkPtr 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) << >> >>>
5025CScriptVarLinkWorkPtr 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) == != === !===
5045CScriptVarLinkWorkPtr 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) |
5088CScriptVarLinkWorkPtr 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) ||
5106CScriptVarLinkWorkPtr 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) ?:
5128CScriptVarLinkWorkPtr 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
5156CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptResult &execute) {
5157 return execute_assignment(execute_condition(execute), execute);
5158}
5159CScriptVarLinkPtr 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) ,
5202CScriptVarLinkPtr 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}
5214void 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}
5228void 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 }
5589end_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
5639CScriptVarLinkPtr CTinyJS::findInScopes(const string &childName) {
5640 return scope()->findInScopes(childName);
5641}
5642
5643//////////////////////////////////////////////////////////////////////////
5644/// Object
5645//////////////////////////////////////////////////////////////////////////
5646
5647void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) {
5648 c->setReturnVar(c->getArgument(0)->toObject());
5649}
5650void 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
5661void 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
5673void 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
5686void 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
5700void 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
5706void 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
5717void 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
5749void 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}
5763void CTinyJS::native_Object_prototype_valueOf(const CFunctionsScopePtr &c, void *data) {
5764 c->setReturnVar(c->getArgument("this")->valueOf_CallBack());
5765}
5766void 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
5780void 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
5800void 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
5818void 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
5842void 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
5859void 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
5875void 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
5885void 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
5901void 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}
5911void 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}
5933void 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
5949static 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}
5959void CTinyJS::native_Error(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, Error,c)); }
5960void CTinyJS::native_EvalError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, EvalError,c)); }
5961void CTinyJS::native_RangeError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, RangeError,c)); }
5962void CTinyJS::native_ReferenceError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, ReferenceError,c)); }
5963void CTinyJS::native_SyntaxError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, SyntaxError,c)); }
5964void CTinyJS::native_TypeError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, TypeError,c)); }
5965
5966//////////////////////////////////////////////////////////////////////////
5967/// global functions
5968//////////////////////////////////////////////////////////////////////////
5969
5970void 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
6003void CTinyJS::native_isNAN(const CFunctionsScopePtr &c, void *data) {
6004 c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isNaN()));
6005}
6006
6007void CTinyJS::native_isFinite(const CFunctionsScopePtr &c, void *data) {
6008 c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isFinite()));
6009}
6010
6011void 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
6017void 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
6025void 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
6046void 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
6054void 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
Note: See TracBrowser for help on using the repository browser.