1 | /*
|
---|
2 | * TinyJS
|
---|
3 | *
|
---|
4 | * A single-file Javascript-alike engine
|
---|
5 | *
|
---|
6 | * Authored By Gordon Williams <gw@pur3.co.uk>
|
---|
7 | *
|
---|
8 | * Copyright (C) 2009 Pur3 Ltd
|
---|
9 | *
|
---|
10 |
|
---|
11 | * 42TinyJS
|
---|
12 | *
|
---|
13 | * A fork of TinyJS with the goal to makes a more JavaScript/ECMA compliant engine
|
---|
14 | *
|
---|
15 | * Authored / Changed By Armin Diedering <armin@diedering.de>
|
---|
16 | *
|
---|
17 | * Copyright (C) 2010-2013 ardisoft
|
---|
18 | *
|
---|
19 | *
|
---|
20 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
|
---|
21 | * this software and associated documentation files (the "Software"), to deal in
|
---|
22 | * the Software without restriction, including without limitation the rights to
|
---|
23 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
---|
24 | * of the Software, and to permit persons to whom the Software is furnished to do
|
---|
25 | * so, subject to the following conditions:
|
---|
26 |
|
---|
27 | * The above copyright notice and this permission notice shall be included in all
|
---|
28 | * copies or substantial portions of the Software.
|
---|
29 |
|
---|
30 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
---|
31 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
---|
32 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
---|
33 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
---|
34 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
---|
35 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
---|
36 | * SOFTWARE.
|
---|
37 | */
|
---|
38 |
|
---|
39 | #ifdef _DEBUG
|
---|
40 | # ifndef _MSC_VER
|
---|
41 | # define DEBUG_MEMORY 1
|
---|
42 | # endif
|
---|
43 | #endif
|
---|
44 | #include <sstream>
|
---|
45 |
|
---|
46 | #include "TinyJS.h"
|
---|
47 |
|
---|
48 | #ifndef ASSERT
|
---|
49 | # define ASSERT(X) assert(X)
|
---|
50 | #endif
|
---|
51 |
|
---|
52 | #ifndef NO_REGEXP
|
---|
53 | # if defined HAVE_TR1_REGEX
|
---|
54 | # include <tr1/regex>
|
---|
55 | using namespace std::tr1;
|
---|
56 | # elif defined HAVE_BOOST_REGEX
|
---|
57 | # include <boost/regex.hpp>
|
---|
58 | using namespace boost;
|
---|
59 | # else
|
---|
60 | # include <regex>
|
---|
61 | # endif
|
---|
62 | #endif
|
---|
63 | #include <stdio.h> /* printf, scanf, NULL */
|
---|
64 | #include <stdlib.h>
|
---|
65 | #include <cstring>
|
---|
66 | #include <algorithm>
|
---|
67 | #include <cmath>
|
---|
68 | #include <memory>
|
---|
69 | #include <iterator>
|
---|
70 | #include "TinyJS_Functions.h"
|
---|
71 |
|
---|
72 | using namespace std;
|
---|
73 |
|
---|
74 | // -----------------------------------------------------------------------------------
|
---|
75 | //////////////////////////////////////////////////////////////////////////
|
---|
76 | /// Memory Debug
|
---|
77 | //////////////////////////////////////////////////////////////////////////
|
---|
78 |
|
---|
79 | //#define DEBUG_MEMORY 1
|
---|
80 |
|
---|
81 | #if DEBUG_MEMORY
|
---|
82 |
|
---|
83 | vector<CScriptVar*> allocatedVars;
|
---|
84 | vector<CScriptVarLink*> allocatedLinks;
|
---|
85 |
|
---|
86 | void mark_allocated(CScriptVar *v) {
|
---|
87 | allocatedVars.push_back(v);
|
---|
88 | }
|
---|
89 |
|
---|
90 | void mark_deallocated(CScriptVar *v) {
|
---|
91 | for (size_t i=0;i<allocatedVars.size();i++) {
|
---|
92 | if (allocatedVars[i] == v) {
|
---|
93 | allocatedVars.erase(allocatedVars.begin()+i);
|
---|
94 | break;
|
---|
95 | }
|
---|
96 | }
|
---|
97 | }
|
---|
98 |
|
---|
99 | void mark_allocated(CScriptVarLink *v) {
|
---|
100 | allocatedLinks.push_back(v);
|
---|
101 | }
|
---|
102 |
|
---|
103 | void mark_deallocated(CScriptVarLink *v) {
|
---|
104 | for (size_t i=0;i<allocatedLinks.size();i++) {
|
---|
105 | if (allocatedLinks[i] == v) {
|
---|
106 | allocatedLinks.erase(allocatedLinks.begin()+i);
|
---|
107 | break;
|
---|
108 | }
|
---|
109 | }
|
---|
110 | }
|
---|
111 |
|
---|
112 | void show_allocated() {
|
---|
113 | for (size_t i=0;i<allocatedVars.size();i++) {
|
---|
114 | printf("ALLOCATED, %d refs\n", allocatedVars[i]->getRefs());
|
---|
115 | allocatedVars[i]->trace(" ");
|
---|
116 | }
|
---|
117 | for (size_t i=0;i<allocatedLinks.size();i++) {
|
---|
118 | printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->getName().c_str(), allocatedLinks[i]->getVarPtr()->getRefs());
|
---|
119 | allocatedLinks[i]->getVarPtr()->trace(" ");
|
---|
120 | }
|
---|
121 | allocatedVars.clear();
|
---|
122 | allocatedLinks.clear();
|
---|
123 | }
|
---|
124 | #endif
|
---|
125 |
|
---|
126 |
|
---|
127 | //////////////////////////////////////////////////////////////////////////
|
---|
128 | /// Utils
|
---|
129 | //////////////////////////////////////////////////////////////////////////
|
---|
130 |
|
---|
131 | inline bool isWhitespace(char ch) {
|
---|
132 | return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r');
|
---|
133 | }
|
---|
134 |
|
---|
135 | inline bool isNumeric(char ch) {
|
---|
136 | return (ch>='0') && (ch<='9');
|
---|
137 | }
|
---|
138 | uint32_t isArrayIndex(const string &str) {
|
---|
139 | if(str.size()==0 || !isNumeric(str[0]) || (str.size()>1 && str[0]=='0') ) return -1; // empty or (more as 1 digit and beginning with '0')
|
---|
140 | CNumber idx;
|
---|
141 | const char *endptr;
|
---|
142 | idx.parseInt(str.c_str(), 10, &endptr);
|
---|
143 | if(*endptr || idx>uint32_t(0xFFFFFFFFUL)) return -1;
|
---|
144 | return idx.toUInt32();
|
---|
145 | }
|
---|
146 | inline bool isHexadecimal(char ch) {
|
---|
147 | return ((ch>='0') && (ch<='9')) || ((ch>='a') && (ch<='f')) || ((ch>='A') && (ch<='F'));
|
---|
148 | }
|
---|
149 | inline bool isOctal(char ch) {
|
---|
150 | return ((ch>='0') && (ch<='7'));
|
---|
151 | }
|
---|
152 | inline bool isAlpha(char ch) {
|
---|
153 | return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_' || ch=='$';
|
---|
154 | }
|
---|
155 |
|
---|
156 | bool isIDString(const char *s) {
|
---|
157 | if (!isAlpha(*s))
|
---|
158 | return false;
|
---|
159 | while (*s) {
|
---|
160 | if (!(isAlpha(*s) || isNumeric(*s)))
|
---|
161 | return false;
|
---|
162 | s++;
|
---|
163 | }
|
---|
164 | return true;
|
---|
165 | }
|
---|
166 |
|
---|
167 | void replace(string &str, char textFrom, const char *textTo) {
|
---|
168 | int sLen = strlen(textTo);
|
---|
169 | size_t p = str.find(textFrom);
|
---|
170 | while (p != string::npos) {
|
---|
171 | str = str.substr(0, p) + textTo + str.substr(p+1);
|
---|
172 | p = str.find(textFrom, p+sLen);
|
---|
173 | }
|
---|
174 | }
|
---|
175 | string int2string(int32_t intData) {
|
---|
176 | ostringstream str;
|
---|
177 | str << intData;
|
---|
178 | return str.str();
|
---|
179 | }
|
---|
180 | string int2string(uint32_t intData) {
|
---|
181 | ostringstream str;
|
---|
182 | str << intData;
|
---|
183 | return str.str();
|
---|
184 | }
|
---|
185 | string float2string(const double &floatData) {
|
---|
186 | ostringstream str;
|
---|
187 | str.unsetf(ios::floatfield);
|
---|
188 | #if (defined(_MSC_VER) && _MSC_VER >= 1600) || __cplusplus >= 201103L
|
---|
189 | str.precision(numeric_limits<double>::max_digits10);
|
---|
190 | #else
|
---|
191 | str.precision(numeric_limits<double>::digits10+2);
|
---|
192 | #endif
|
---|
193 | str << floatData;
|
---|
194 | return str.str();
|
---|
195 | }
|
---|
196 | /// convert the given string into a quoted string suitable for javascript
|
---|
197 | string getJSString(const string &str) {
|
---|
198 | char buffer[5] = "\\x00";
|
---|
199 | string nStr; nStr.reserve(str.length());
|
---|
200 | nStr.push_back('\"');
|
---|
201 | for (string::const_iterator i=str.begin();i!=str.end();i++) {
|
---|
202 | const char *replaceWith = 0;
|
---|
203 | switch (*i) {
|
---|
204 | case '\\': replaceWith = "\\\\"; break;
|
---|
205 | case '\n': replaceWith = "\\n"; break;
|
---|
206 | case '\r': replaceWith = "\\r"; break;
|
---|
207 | case '\a': replaceWith = "\\a"; break;
|
---|
208 | case '\b': replaceWith = "\\b"; break;
|
---|
209 | case '\f': replaceWith = "\\f"; break;
|
---|
210 | case '\t': replaceWith = "\\t"; break;
|
---|
211 | case '\v': replaceWith = "\\v"; break;
|
---|
212 | case '"': replaceWith = "\\\""; break;
|
---|
213 | default: {
|
---|
214 | int nCh = ((unsigned char)*i) & 0xff;
|
---|
215 | if(nCh<32 || nCh>127) {
|
---|
216 | static char hex[] = "0123456789ABCDEF";
|
---|
217 | buffer[2] = hex[(nCh>>4)&0x0f];
|
---|
218 | buffer[3] = hex[nCh&0x0f];
|
---|
219 | replaceWith = buffer;
|
---|
220 | };
|
---|
221 | }
|
---|
222 | }
|
---|
223 | if (replaceWith)
|
---|
224 | nStr.append(replaceWith);
|
---|
225 | else
|
---|
226 | nStr.push_back(*i);
|
---|
227 | }
|
---|
228 | nStr.push_back('\"');
|
---|
229 | return nStr;
|
---|
230 | }
|
---|
231 |
|
---|
232 | static inline string getIDString(const string& str) {
|
---|
233 | if(isIDString(str.c_str()) && CScriptToken::isReservedWord(str)==LEX_ID)
|
---|
234 | return str;
|
---|
235 | return getJSString(str);
|
---|
236 | }
|
---|
237 |
|
---|
238 |
|
---|
239 | //////////////////////////////////////////////////////////////////////////
|
---|
240 | /// CScriptException
|
---|
241 | //////////////////////////////////////////////////////////////////////////
|
---|
242 |
|
---|
243 | string CScriptException::toString() {
|
---|
244 | ostringstream msg;
|
---|
245 | msg << ERROR_NAME[errorType] << ": " << message;
|
---|
246 | if(lineNumber >= 0) msg << " at Line:" << lineNumber+1;
|
---|
247 | if(column >=0) msg << " Column:" << column+1;
|
---|
248 | if(fileName.length()) msg << " in " << fileName;
|
---|
249 | return msg.str();
|
---|
250 | }
|
---|
251 |
|
---|
252 |
|
---|
253 | //////////////////////////////////////////////////////////////////////////
|
---|
254 | /// CScriptLex
|
---|
255 | //////////////////////////////////////////////////////////////////////////
|
---|
256 |
|
---|
257 | CScriptLex::CScriptLex(const char *Code, const string &File, int Line, int Column) : data(Code) {
|
---|
258 | currentFile = File;
|
---|
259 | pos.currentLineStart = pos.tokenStart = data;
|
---|
260 | pos.currentLine = Line;
|
---|
261 | reset(pos);
|
---|
262 | }
|
---|
263 |
|
---|
264 | void CScriptLex::reset(const POS &toPos) { ///< Reset this lex so we can start again
|
---|
265 | dataPos = toPos.tokenStart;
|
---|
266 | tk = last_tk = 0;
|
---|
267 | tkStr = "";
|
---|
268 | pos = toPos;
|
---|
269 | lineBreakBeforeToken = false;
|
---|
270 | currCh = nextCh = 0;
|
---|
271 | getNextCh(); // currCh
|
---|
272 | getNextCh(); // nextCh
|
---|
273 | getNextToken();
|
---|
274 | }
|
---|
275 |
|
---|
276 | void CScriptLex::check(int expected_tk, int alternate_tk/*=-1*/) {
|
---|
277 | if (expected_tk==';' && tk==LEX_EOF) return; // ignore last missing ';'
|
---|
278 | if (tk!=expected_tk && tk!=alternate_tk) {
|
---|
279 | ostringstream errorString;
|
---|
280 | if(expected_tk == LEX_EOF)
|
---|
281 | errorString << "Got unexpected " << CScriptToken::getTokenStr(tk);
|
---|
282 | else
|
---|
283 | errorString << "Got '" << CScriptToken::getTokenStr(tk) << "' expected '" << CScriptToken::getTokenStr(expected_tk) << "'";
|
---|
284 | if(alternate_tk!=-1) errorString << " or '" << CScriptToken::getTokenStr(alternate_tk) << "'";
|
---|
285 | throw new CScriptException(SyntaxError, errorString.str(), currentFile, pos.currentLine, currentColumn());
|
---|
286 | }
|
---|
287 | }
|
---|
288 | void CScriptLex::match(int expected_tk1, int alternate_tk/*=-1*/) {
|
---|
289 | check(expected_tk1, alternate_tk);
|
---|
290 | int line = pos.currentLine;
|
---|
291 | getNextToken();
|
---|
292 | lineBreakBeforeToken = line != pos.currentLine;
|
---|
293 | }
|
---|
294 |
|
---|
295 | void CScriptLex::getNextCh() {
|
---|
296 | if(currCh == '\n') { // Windows or Linux
|
---|
297 | pos.currentLine++;
|
---|
298 | pos.tokenStart = pos.currentLineStart = dataPos - (nextCh == LEX_EOF ? 0 : 1);
|
---|
299 | }
|
---|
300 | currCh = nextCh;
|
---|
301 | if ( (nextCh = *dataPos) != LEX_EOF ) dataPos++; // stay on EOF
|
---|
302 | if(currCh == '\r') { // Windows or Mac
|
---|
303 | if(nextCh == '\n')
|
---|
304 | getNextCh(); // Windows '\r\n\' --> skip '\r'
|
---|
305 | else
|
---|
306 | currCh = '\n'; // Mac (only '\r') --> convert '\r' to '\n'
|
---|
307 | }
|
---|
308 | }
|
---|
309 |
|
---|
310 | static uint16_t not_allowed_tokens_befor_regexp[] = {LEX_ID, LEX_INT, LEX_FLOAT, LEX_STR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, ']', ')', '.', LEX_EOF};
|
---|
311 | void CScriptLex::getNextToken() {
|
---|
312 | while (currCh && isWhitespace(currCh)) getNextCh();
|
---|
313 | // newline comments
|
---|
314 | if (currCh=='/' && nextCh=='/') {
|
---|
315 | while (currCh && currCh!='\n') getNextCh();
|
---|
316 | getNextCh();
|
---|
317 | getNextToken();
|
---|
318 | return;
|
---|
319 | }
|
---|
320 | // block comments
|
---|
321 | if (currCh=='/' && nextCh=='*') {
|
---|
322 | while (currCh && (currCh!='*' || nextCh!='/')) getNextCh();
|
---|
323 | getNextCh();
|
---|
324 | getNextCh();
|
---|
325 | getNextToken();
|
---|
326 | return;
|
---|
327 | }
|
---|
328 | last_tk = tk;
|
---|
329 | tk = LEX_EOF;
|
---|
330 | tkStr.clear();
|
---|
331 | // record beginning of this token
|
---|
332 | pos.tokenStart = dataPos - (nextCh == LEX_EOF ? (currCh == LEX_EOF ? 0 : 1) : 2);
|
---|
333 | // tokens
|
---|
334 | if (isAlpha(currCh)) { // IDs
|
---|
335 | while (isAlpha(currCh) || isNumeric(currCh)) {
|
---|
336 | tkStr += currCh;
|
---|
337 | getNextCh();
|
---|
338 | }
|
---|
339 | tk = CScriptToken::isReservedWord(tkStr);
|
---|
340 | } else if (isNumeric(currCh) || (currCh=='.' && isNumeric(nextCh))) { // Numbers
|
---|
341 | if(currCh=='.') tkStr+='0';
|
---|
342 | bool isHex = false, isOct=false;
|
---|
343 | if (currCh=='0') {
|
---|
344 | tkStr += currCh; getNextCh();
|
---|
345 | if(isOctal(currCh)) isOct = true;
|
---|
346 | }
|
---|
347 | if (currCh=='x' || currCh=='X') {
|
---|
348 | isHex = true;
|
---|
349 | tkStr += currCh; getNextCh();
|
---|
350 | }
|
---|
351 | tk = LEX_INT;
|
---|
352 | while (isOctal(currCh) || (!isOct && isNumeric(currCh)) || (isHex && isHexadecimal(currCh))) {
|
---|
353 | tkStr += currCh;
|
---|
354 | getNextCh();
|
---|
355 | }
|
---|
356 | if (!isHex && !isOct && currCh=='.') {
|
---|
357 | tk = LEX_FLOAT;
|
---|
358 | tkStr += '.';
|
---|
359 | getNextCh();
|
---|
360 | while (isNumeric(currCh)) {
|
---|
361 | tkStr += currCh;
|
---|
362 | getNextCh();
|
---|
363 | }
|
---|
364 | }
|
---|
365 | // do fancy e-style floating point
|
---|
366 | if (!isHex && !isOct && (currCh=='e' || currCh=='E')) {
|
---|
367 | tk = LEX_FLOAT;
|
---|
368 | tkStr += currCh; getNextCh();
|
---|
369 | if (currCh=='-') { tkStr += currCh; getNextCh(); }
|
---|
370 | while (isNumeric(currCh)) {
|
---|
371 | tkStr += currCh; getNextCh();
|
---|
372 | }
|
---|
373 | }
|
---|
374 | } else if (currCh=='"' || currCh=='\'') { // strings...
|
---|
375 | char endCh = currCh;
|
---|
376 | getNextCh();
|
---|
377 | while (currCh && currCh!=endCh && currCh!='\n') {
|
---|
378 | if (currCh == '\\') {
|
---|
379 | getNextCh();
|
---|
380 | switch (currCh) {
|
---|
381 | case '\n' : break; // ignore newline after '\'
|
---|
382 | case 'n': tkStr += '\n'; break;
|
---|
383 | case 'r': tkStr += '\r'; break;
|
---|
384 | case 'a': tkStr += '\a'; break;
|
---|
385 | case 'b': tkStr += '\b'; break;
|
---|
386 | case 'f': tkStr += '\f'; break;
|
---|
387 | case 't': tkStr += '\t'; break;
|
---|
388 | case 'v': tkStr += '\v'; break;
|
---|
389 | case 'x': { // hex digits
|
---|
390 | getNextCh();
|
---|
391 | if(isHexadecimal(currCh)) {
|
---|
392 | char buf[3]="\0\0";
|
---|
393 | buf[0] = currCh;
|
---|
394 | for(int i=0; i<2 && isHexadecimal(nextCh); i++) {
|
---|
395 | getNextCh(); buf[i] = currCh;
|
---|
396 | }
|
---|
397 | tkStr += (char)strtol(buf, 0, 16);
|
---|
398 | } else
|
---|
399 | throw new CScriptException(SyntaxError, "malformed hexadezimal character escape sequence", currentFile, pos.currentLine, currentColumn());
|
---|
400 | }
|
---|
401 | default: {
|
---|
402 | if(isOctal(currCh)) {
|
---|
403 | char buf[4]="\0\0\0";
|
---|
404 | buf[0] = currCh;
|
---|
405 | for(int i=1; i<3 && isOctal(nextCh); i++) {
|
---|
406 | getNextCh(); buf[i] = currCh;
|
---|
407 | }
|
---|
408 | tkStr += (char)strtol(buf, 0, 8);
|
---|
409 | }
|
---|
410 | else tkStr += currCh;
|
---|
411 | }
|
---|
412 | }
|
---|
413 | } else {
|
---|
414 | tkStr += currCh;
|
---|
415 | }
|
---|
416 | getNextCh();
|
---|
417 | }
|
---|
418 | if(currCh != endCh)
|
---|
419 | throw new CScriptException(SyntaxError, "unterminated string literal", currentFile, pos.currentLine, currentColumn());
|
---|
420 | getNextCh();
|
---|
421 | tk = LEX_STR;
|
---|
422 | } else {
|
---|
423 | // single chars
|
---|
424 | tk = currCh;
|
---|
425 | if (currCh) getNextCh();
|
---|
426 | if (tk=='=' && currCh=='=') { // ==
|
---|
427 | tk = LEX_EQUAL;
|
---|
428 | getNextCh();
|
---|
429 | if (currCh=='=') { // ===
|
---|
430 | tk = LEX_TYPEEQUAL;
|
---|
431 | getNextCh();
|
---|
432 | }
|
---|
433 | } else if (tk=='!' && currCh=='=') { // !=
|
---|
434 | tk = LEX_NEQUAL;
|
---|
435 | getNextCh();
|
---|
436 | if (currCh=='=') { // !==
|
---|
437 | tk = LEX_NTYPEEQUAL;
|
---|
438 | getNextCh();
|
---|
439 | }
|
---|
440 | } else if (tk=='<') {
|
---|
441 | if (currCh=='=') { // <=
|
---|
442 | tk = LEX_LEQUAL;
|
---|
443 | getNextCh();
|
---|
444 | } else if (currCh=='<') { // <<
|
---|
445 | tk = LEX_LSHIFT;
|
---|
446 | getNextCh();
|
---|
447 | if (currCh=='=') { // <<=
|
---|
448 | tk = LEX_LSHIFTEQUAL;
|
---|
449 | getNextCh();
|
---|
450 | }
|
---|
451 | }
|
---|
452 | } else if (tk=='>') {
|
---|
453 | if (currCh=='=') { // >=
|
---|
454 | tk = LEX_GEQUAL;
|
---|
455 | getNextCh();
|
---|
456 | } else if (currCh=='>') { // >>
|
---|
457 | tk = LEX_RSHIFT;
|
---|
458 | getNextCh();
|
---|
459 | if (currCh=='=') { // >>=
|
---|
460 | tk = LEX_RSHIFTEQUAL;
|
---|
461 | getNextCh();
|
---|
462 | } else if (currCh=='>') { // >>>
|
---|
463 | tk = LEX_RSHIFTU;
|
---|
464 | getNextCh();
|
---|
465 | if (currCh=='=') { // >>>=
|
---|
466 | tk = LEX_RSHIFTUEQUAL;
|
---|
467 | getNextCh();
|
---|
468 | }
|
---|
469 | }
|
---|
470 | }
|
---|
471 | } else if (tk=='+') {
|
---|
472 | if (currCh=='=') { // +=
|
---|
473 | tk = LEX_PLUSEQUAL;
|
---|
474 | getNextCh();
|
---|
475 | } else if (currCh=='+') { // ++
|
---|
476 | tk = LEX_PLUSPLUS;
|
---|
477 | getNextCh();
|
---|
478 | }
|
---|
479 | } else if (tk=='-') {
|
---|
480 | if (currCh=='=') { // -=
|
---|
481 | tk = LEX_MINUSEQUAL;
|
---|
482 | getNextCh();
|
---|
483 | } else if (currCh=='-') { // --
|
---|
484 | tk = LEX_MINUSMINUS;
|
---|
485 | getNextCh();
|
---|
486 | }
|
---|
487 | } else if (tk=='&') {
|
---|
488 | if (currCh=='=') { // &=
|
---|
489 | tk = LEX_ANDEQUAL;
|
---|
490 | getNextCh();
|
---|
491 | } else if (currCh=='&') { // &&
|
---|
492 | tk = LEX_ANDAND;
|
---|
493 | getNextCh();
|
---|
494 | }
|
---|
495 | } else if (tk=='|') {
|
---|
496 | if (currCh=='=') { // |=
|
---|
497 | tk = LEX_OREQUAL;
|
---|
498 | getNextCh();
|
---|
499 | } else if (currCh=='|') { // ||
|
---|
500 | tk = LEX_OROR;
|
---|
501 | getNextCh();
|
---|
502 | }
|
---|
503 | } else if (tk=='^' && currCh=='=') {
|
---|
504 | tk = LEX_XOREQUAL;
|
---|
505 | getNextCh();
|
---|
506 | } else if (tk=='*' && currCh=='=') {
|
---|
507 | tk = LEX_ASTERISKEQUAL;
|
---|
508 | getNextCh();
|
---|
509 | } else if (tk=='/') {
|
---|
510 | // check if it's a RegExp-Literal
|
---|
511 | tk = LEX_REGEXP;
|
---|
512 | for(uint16_t *p = not_allowed_tokens_befor_regexp; *p; p++) {
|
---|
513 | if(*p==last_tk) { tk = '/'; break; }
|
---|
514 | }
|
---|
515 | if(tk == LEX_REGEXP) {
|
---|
516 | #ifdef NO_REGEXP
|
---|
517 | throw new CScriptException(Error, "42TinyJS was built without support for regular expressions", currentFile, pos.currentLine, currentColumn());
|
---|
518 | #endif
|
---|
519 | tkStr = "/";
|
---|
520 | while (currCh && currCh!='/' && currCh!='\n') {
|
---|
521 | if (currCh == '\\' && nextCh == '/') {
|
---|
522 | tkStr.append(1, currCh);
|
---|
523 | getNextCh();
|
---|
524 | }
|
---|
525 | tkStr.append(1, currCh);
|
---|
526 | getNextCh();
|
---|
527 | }
|
---|
528 | if(currCh == '/') {
|
---|
529 | #ifndef NO_REGEXP
|
---|
530 | try { regex(tkStr.substr(1), regex_constants::ECMAScript); } catch(regex_error e) {
|
---|
531 | throw new CScriptException(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code()), currentFile, pos.currentLine, currentColumn());
|
---|
532 | }
|
---|
533 | #endif /* NO_REGEXP */
|
---|
534 | do {
|
---|
535 | tkStr.append(1, currCh);
|
---|
536 | getNextCh();
|
---|
537 | } while (currCh=='g' || currCh=='i' || currCh=='m' || currCh=='y');
|
---|
538 | } else
|
---|
539 | throw new CScriptException(SyntaxError, "unterminated regular expression literal", currentFile, pos.currentLine, currentColumn());
|
---|
540 | } else if(currCh=='=') {
|
---|
541 | tk = LEX_SLASHEQUAL;
|
---|
542 | getNextCh();
|
---|
543 | }
|
---|
544 | } else if (tk=='%' && currCh=='=') {
|
---|
545 | tk = LEX_PERCENTEQUAL;
|
---|
546 | getNextCh();
|
---|
547 | }
|
---|
548 | }
|
---|
549 | /* This isn't quite right yet */
|
---|
550 | }
|
---|
551 |
|
---|
552 |
|
---|
553 | //////////////////////////////////////////////////////////////////////////
|
---|
554 | // CScriptTokenDataForwards
|
---|
555 | //////////////////////////////////////////////////////////////////////////
|
---|
556 |
|
---|
557 | bool CScriptTokenDataForwards::compare_fnc_token_by_name::operator()(const CScriptToken& lhs, const CScriptToken& rhs) const {
|
---|
558 | return lhs.Fnc().name < rhs.Fnc().name;
|
---|
559 | }
|
---|
560 | bool CScriptTokenDataForwards::checkRedefinition(const string &Str, bool checkVarsInLetScope) {
|
---|
561 | STRING_SET_it it = varNames[LETS].find(Str);
|
---|
562 | if(it!=varNames[LETS].end()) return false;
|
---|
563 | else if(checkVarsInLetScope) {
|
---|
564 | STRING_SET_it it = vars_in_letscope.find(Str);
|
---|
565 | if(it!=vars_in_letscope.end()) return false;
|
---|
566 | }
|
---|
567 | return true;
|
---|
568 | }
|
---|
569 |
|
---|
570 | void CScriptTokenDataForwards::addVars( STRING_VECTOR_t &Vars ) {
|
---|
571 | varNames[VARS].insert(Vars.begin(), Vars.end());
|
---|
572 | }
|
---|
573 | void CScriptTokenDataForwards::addConsts( STRING_VECTOR_t &Vars ) {
|
---|
574 | varNames[CONSTS].insert(Vars.begin(), Vars.end());
|
---|
575 | }
|
---|
576 | std::string CScriptTokenDataForwards::addVarsInLetscope( STRING_VECTOR_t &Vars )
|
---|
577 | {
|
---|
578 | for(STRING_VECTOR_it it=Vars.begin(); it!=Vars.end(); ++it) {
|
---|
579 | if(!checkRedefinition(*it, false)) return *it;
|
---|
580 | vars_in_letscope.insert(*it);
|
---|
581 | }
|
---|
582 | return "";
|
---|
583 | }
|
---|
584 |
|
---|
585 | std::string CScriptTokenDataForwards::addLets( STRING_VECTOR_t &Lets )
|
---|
586 | {
|
---|
587 | for(STRING_VECTOR_it it=Lets.begin(); it!=Lets.end(); ++it) {
|
---|
588 | if(!checkRedefinition(*it, true)) return *it;
|
---|
589 | varNames[LETS].insert(*it);
|
---|
590 | }
|
---|
591 | return "";
|
---|
592 | }
|
---|
593 |
|
---|
594 |
|
---|
595 | //////////////////////////////////////////////////////////////////////////
|
---|
596 | // CScriptTokenDataLoop
|
---|
597 | //////////////////////////////////////////////////////////////////////////
|
---|
598 |
|
---|
599 | std::string CScriptTokenDataLoop::getParsableString(const string &IndentString/*=""*/, const string &Indent/*=""*/ ) {
|
---|
600 | static const char *heads[] = {"for each(", "for(", "for(", "for(", "while(", "do "};
|
---|
601 | static const char *ops[] = {" in ", " in ", " of ", "; "};
|
---|
602 | string out = heads[type];
|
---|
603 | if(init.size() && type==FOR)out.append(CScriptToken::getParsableString(init));
|
---|
604 | if(type<=WHILE) out.append(CScriptToken::getParsableString(condition.begin(), condition.end()-(type>=FOR ? 0 : 7)));
|
---|
605 | if(type<=FOR) out.append(ops[type]);
|
---|
606 | if(iter.size()) out.append(CScriptToken::getParsableString(iter));
|
---|
607 | out.append(")");
|
---|
608 | out.append(CScriptToken::getParsableString(body));
|
---|
609 | if(type==DO)out.append(" while(").append(CScriptToken::getParsableString(condition)).append(");");
|
---|
610 | return out;
|
---|
611 | }
|
---|
612 |
|
---|
613 |
|
---|
614 | //////////////////////////////////////////////////////////////////////////
|
---|
615 | // CScriptTokenDataTry
|
---|
616 | //////////////////////////////////////////////////////////////////////////
|
---|
617 |
|
---|
618 | std::string CScriptTokenDataTry::getParsableString( const string &IndentString/*=""*/, const string &Indent/*=""*/ ) {
|
---|
619 | string out = "try ";
|
---|
620 | string nl = Indent.size() ? "\n"+IndentString : " ";
|
---|
621 |
|
---|
622 | out.append(CScriptToken::getParsableString(tryBlock, IndentString, Indent));
|
---|
623 | for(CScriptTokenDataTry::CatchBlock_it catchBlock = catchBlocks.begin(); catchBlock!=catchBlocks.end(); catchBlock++) {
|
---|
624 | out.append(nl).append("catch(").append(catchBlock->indentifiers->getParsableString());
|
---|
625 | if(catchBlock->condition.size()>1) {
|
---|
626 | out.append(" if ").append(CScriptToken::getParsableString(catchBlock->condition.begin()+1, catchBlock->condition.end()));
|
---|
627 | }
|
---|
628 | out.append(") ").append(CScriptToken::getParsableString(catchBlock->block, IndentString, Indent));
|
---|
629 | }
|
---|
630 | if(finallyBlock.size())
|
---|
631 | out.append(nl).append("finally ").append(CScriptToken::getParsableString(finallyBlock, IndentString, Indent));
|
---|
632 | return out;
|
---|
633 | }
|
---|
634 |
|
---|
635 |
|
---|
636 | //////////////////////////////////////////////////////////////////////////
|
---|
637 | // CScriptTokenDataFnc
|
---|
638 | //////////////////////////////////////////////////////////////////////////
|
---|
639 |
|
---|
640 | string CScriptTokenDataFnc::getArgumentsString()
|
---|
641 | {
|
---|
642 | ostringstream destination;
|
---|
643 | destination << "(";
|
---|
644 | if(arguments.size()) {
|
---|
645 | const char *comma = "";
|
---|
646 | for(TOKEN_VECT_it argument=arguments.begin(); argument!=arguments.end(); ++argument, comma=", ") {
|
---|
647 | if(argument->token == LEX_ID)
|
---|
648 | destination << comma << argument->String();
|
---|
649 | else {
|
---|
650 | vector<bool> isObject(1, false);
|
---|
651 | for(DESTRUCTURING_VARS_it it=argument->DestructuringVar().vars.begin(); it!=argument->DestructuringVar().vars.end(); ++it) {
|
---|
652 | if(it->second == "}" || it->second == "]") {
|
---|
653 | destination << it->second;
|
---|
654 | isObject.pop_back();
|
---|
655 | } else {
|
---|
656 | destination << comma;
|
---|
657 | if(it->second == "[" || it->second == "{") {
|
---|
658 | comma = "";
|
---|
659 | if(isObject.back() && it->first.length())
|
---|
660 | destination << getIDString(it->first) << ":";
|
---|
661 | destination << it->second;
|
---|
662 | isObject.push_back(it->second == "{");
|
---|
663 | } else {
|
---|
664 | comma = ", ";
|
---|
665 | if(it->second.empty())
|
---|
666 | continue; // skip empty entries
|
---|
667 | if(isObject.back() && it->first!=it->second)
|
---|
668 | destination << getIDString(it->first) << ":";
|
---|
669 | destination << it->second;
|
---|
670 | }
|
---|
671 | }
|
---|
672 | }
|
---|
673 | }
|
---|
674 | }
|
---|
675 | }
|
---|
676 | destination << ") ";
|
---|
677 | return destination.str();
|
---|
678 | }
|
---|
679 |
|
---|
680 |
|
---|
681 | //////////////////////////////////////////////////////////////////////////
|
---|
682 | // CScriptTokenDataDestructuringVar
|
---|
683 | //////////////////////////////////////////////////////////////////////////
|
---|
684 |
|
---|
685 | void CScriptTokenDataDestructuringVar::getVarNames(STRING_VECTOR_t Names) {
|
---|
686 | for(DESTRUCTURING_VARS_it it = vars.begin(); it != vars.end(); ++it) {
|
---|
687 | if(it->second.size() && it->second.find_first_of("{[]}") == string::npos)
|
---|
688 | Names.push_back(it->second);
|
---|
689 | }
|
---|
690 | }
|
---|
691 |
|
---|
692 | std::string CScriptTokenDataDestructuringVar::getParsableString()
|
---|
693 | {
|
---|
694 | string out;
|
---|
695 | const char *comma = "";
|
---|
696 | vector<bool> isObject(1, false);
|
---|
697 | for(DESTRUCTURING_VARS_it it=vars.begin(); it!=vars.end(); ++it) {
|
---|
698 | if(it->second == "}" || it->second == "]") {
|
---|
699 | out.append(it->second);
|
---|
700 | isObject.pop_back();
|
---|
701 | } else {
|
---|
702 | out.append(comma);
|
---|
703 | if(it->second == "[" || it->second == "{") {
|
---|
704 | comma = "";
|
---|
705 | if(isObject.back() && it->first.length())
|
---|
706 | out.append(getIDString(it->first)).append(":");
|
---|
707 | out.append(it->second);
|
---|
708 | isObject.push_back(it->second == "{");
|
---|
709 | } else {
|
---|
710 | comma = ", ";
|
---|
711 | if(it->second.empty())
|
---|
712 | continue; // skip empty entries
|
---|
713 | if(isObject.back() && it->first!=it->second)
|
---|
714 | out.append(getIDString(it->first)).append(":");
|
---|
715 | out.append(it->second);
|
---|
716 | }
|
---|
717 | }
|
---|
718 | }
|
---|
719 | return out;
|
---|
720 | }
|
---|
721 |
|
---|
722 |
|
---|
723 | //////////////////////////////////////////////////////////////////////////
|
---|
724 | // CScriptTokenDataObjectLiteral
|
---|
725 | //////////////////////////////////////////////////////////////////////////
|
---|
726 |
|
---|
727 | void CScriptTokenDataObjectLiteral::setMode(bool Destructuring) {
|
---|
728 | structuring = !(destructuring = Destructuring);
|
---|
729 | for(vector<ELEMENT>::iterator it=elements.begin(); it!=elements.end(); ++it) {
|
---|
730 | if(it->value.size() && it->value.front().token == LEX_T_OBJECT_LITERAL) {
|
---|
731 | CScriptTokenDataObjectLiteral& e = it->value.front().Object();
|
---|
732 | if(e.destructuring && e.structuring)
|
---|
733 | e.setMode(Destructuring);
|
---|
734 | }
|
---|
735 | }
|
---|
736 | }
|
---|
737 |
|
---|
738 | string CScriptTokenDataObjectLiteral::getParsableString()
|
---|
739 | {
|
---|
740 | string out = type == OBJECT ? "{ " : "[ ";
|
---|
741 | const char *comma = "";
|
---|
742 | for(vector<ELEMENT>::iterator it=elements.begin(); it!=elements.end(); ++it) {
|
---|
743 | out.append(comma); comma=", ";
|
---|
744 | if(it->value.empty()) continue;
|
---|
745 | if(type == OBJECT)
|
---|
746 | out.append(getIDString(it->id)).append(" : ");
|
---|
747 | out.append(CScriptToken::getParsableString(it->value));
|
---|
748 | }
|
---|
749 | out.append(type == OBJECT ? " }" : " ]");
|
---|
750 | return out;
|
---|
751 | }
|
---|
752 |
|
---|
753 |
|
---|
754 | //////////////////////////////////////////////////////////////////////////
|
---|
755 | // CScriptToken
|
---|
756 | //////////////////////////////////////////////////////////////////////////
|
---|
757 |
|
---|
758 | typedef struct { int id; const char *str; bool need_space; } token2str_t;
|
---|
759 | static token2str_t reserved_words_begin[] ={
|
---|
760 | // reserved words
|
---|
761 | { LEX_R_IF, "if", true },
|
---|
762 | { LEX_R_ELSE, "else", true },
|
---|
763 | { LEX_R_DO, "do", true },
|
---|
764 | { LEX_R_WHILE, "while", true },
|
---|
765 | { LEX_R_FOR, "for", true },
|
---|
766 | { LEX_R_IN, "in", true },
|
---|
767 | { LEX_R_BREAK, "break", true },
|
---|
768 | { LEX_R_CONTINUE, "continue", true },
|
---|
769 | { LEX_R_FUNCTION, "function", true },
|
---|
770 | { LEX_R_RETURN, "return", true },
|
---|
771 | { LEX_R_VAR, "var", true },
|
---|
772 | { LEX_R_LET, "let", true },
|
---|
773 | { LEX_R_CONST, "const", true },
|
---|
774 | { LEX_R_WITH, "with", true },
|
---|
775 | { LEX_R_TRUE, "true", true },
|
---|
776 | { LEX_R_FALSE, "false", true },
|
---|
777 | { LEX_R_NULL, "null", true },
|
---|
778 | { LEX_R_NEW, "new", true },
|
---|
779 | { LEX_R_TRY, "try", true },
|
---|
780 | { LEX_R_CATCH, "catch", true },
|
---|
781 | { LEX_R_FINALLY, "finally", true },
|
---|
782 | { LEX_R_THROW, "throw", true },
|
---|
783 | { LEX_R_TYPEOF, "typeof", true },
|
---|
784 | { LEX_R_VOID, "void", true },
|
---|
785 | { LEX_R_DELETE, "delete", true },
|
---|
786 | { LEX_R_INSTANCEOF, "instanceof", true },
|
---|
787 | { LEX_R_SWITCH, "switch", true },
|
---|
788 | { LEX_R_CASE, "case", true },
|
---|
789 | { LEX_R_DEFAULT, "default", true },
|
---|
790 | };
|
---|
791 | #define ARRAY_LENGTH(array) (sizeof(array)/sizeof(array[0]))
|
---|
792 | #define ARRAY_END(array) (&array[ARRAY_LENGTH(array)])
|
---|
793 | static token2str_t *reserved_words_end = ARRAY_END(reserved_words_begin);//&reserved_words_begin[ARRAY_LENGTH(reserved_words_begin)];
|
---|
794 | static token2str_t *str2reserved_begin[sizeof(reserved_words_begin)/sizeof(reserved_words_begin[0])];
|
---|
795 | static token2str_t **str2reserved_end = &str2reserved_begin[sizeof(str2reserved_begin)/sizeof(str2reserved_begin[0])];
|
---|
796 | static token2str_t tokens2str_begin[] = {
|
---|
797 | { LEX_EOF, "EOF", false },
|
---|
798 | { LEX_ID, "ID", true },
|
---|
799 | { LEX_INT, "INT", true },
|
---|
800 | { LEX_FLOAT, "FLOAT", true },
|
---|
801 | { LEX_STR, "STRING", true },
|
---|
802 | { LEX_REGEXP, "REGEXP", true },
|
---|
803 | { LEX_EQUAL, "==", false },
|
---|
804 | { LEX_TYPEEQUAL, "===", false },
|
---|
805 | { LEX_NEQUAL, "!=", false },
|
---|
806 | { LEX_NTYPEEQUAL, "!==", false },
|
---|
807 | { LEX_LEQUAL, "<=", false },
|
---|
808 | { LEX_LSHIFT, "<<", false },
|
---|
809 | { LEX_LSHIFTEQUAL, "<<=", false },
|
---|
810 | { LEX_GEQUAL, ">=", false },
|
---|
811 | { LEX_RSHIFT, ">>", false },
|
---|
812 | { LEX_RSHIFTEQUAL, ">>=", false },
|
---|
813 | { LEX_RSHIFTU, ">>>", false },
|
---|
814 | { LEX_RSHIFTUEQUAL, ">>>=", false },
|
---|
815 | { LEX_PLUSEQUAL, "+=", false },
|
---|
816 | { LEX_MINUSEQUAL, "-=", false },
|
---|
817 | { LEX_PLUSPLUS, "++", false },
|
---|
818 | { LEX_MINUSMINUS, "--", false },
|
---|
819 | { LEX_ANDEQUAL, "&=", false },
|
---|
820 | { LEX_ANDAND, "&&", false },
|
---|
821 | { LEX_OREQUAL, "|=", false },
|
---|
822 | { LEX_OROR, "||", false },
|
---|
823 | { LEX_XOREQUAL, "^=", false },
|
---|
824 | { LEX_ASTERISKEQUAL, "*=", false },
|
---|
825 | { LEX_SLASHEQUAL, "/=", false },
|
---|
826 | { LEX_PERCENTEQUAL, "%=", false },
|
---|
827 | // special tokens
|
---|
828 | { LEX_T_OF, "of", true },
|
---|
829 | { LEX_T_FUNCTION_OPERATOR, "function", true },
|
---|
830 | { LEX_T_GET, "get", true },
|
---|
831 | { LEX_T_SET, "set", true },
|
---|
832 | { LEX_T_EXCEPTION_VAR, "LEX_T_EXCEPTION_VAR", false },
|
---|
833 | { LEX_T_SKIP, "LEX_SKIP", false },
|
---|
834 | { LEX_T_DUMMY_LABEL, "LABEL", true },
|
---|
835 | { LEX_T_LABEL, "LABEL", true },
|
---|
836 | { LEX_T_LOOP, "LEX_LOOP", true },
|
---|
837 | { LEX_T_FOR_IN, "LEX_FOR_IN", true },
|
---|
838 | { LEX_T_FORWARD, "LEX_T_FORWARD", false },
|
---|
839 | { LEX_T_OBJECT_LITERAL, "LEX_OBJECT_LITERAL", false },
|
---|
840 | { LEX_T_DESTRUCTURING_VAR, "Destructuring Var", false },
|
---|
841 | };
|
---|
842 | static token2str_t *tokens2str_end = &tokens2str_begin[sizeof(tokens2str_begin)/sizeof(tokens2str_begin[0])];
|
---|
843 | struct token2str_cmp_t {
|
---|
844 | bool operator()(const token2str_t &lhs, const token2str_t &rhs) {
|
---|
845 | return lhs.id < rhs.id;
|
---|
846 | }
|
---|
847 | bool operator()(const token2str_t &lhs, int rhs) {
|
---|
848 | return lhs.id < rhs;
|
---|
849 | }
|
---|
850 | bool operator()(const token2str_t *lhs, const token2str_t *rhs) {
|
---|
851 | return strcmp(lhs->str, rhs->str)<0;
|
---|
852 | }
|
---|
853 | bool operator()(const token2str_t *lhs, const char *rhs) {
|
---|
854 | return strcmp(lhs->str, rhs)<0;
|
---|
855 | }
|
---|
856 | };
|
---|
857 | static bool tokens2str_sort() {
|
---|
858 | // printf("tokens2str_sort called\n");
|
---|
859 | sort(tokens2str_begin, tokens2str_end, token2str_cmp_t());
|
---|
860 | sort(reserved_words_begin, reserved_words_end, token2str_cmp_t());
|
---|
861 | for(unsigned int i=0; i<ARRAY_LENGTH(str2reserved_begin); i++)
|
---|
862 | str2reserved_begin[i] = &reserved_words_begin[i];
|
---|
863 | sort(str2reserved_begin, str2reserved_end, token2str_cmp_t());
|
---|
864 | return true;
|
---|
865 | }
|
---|
866 | static bool tokens2str_sorted = tokens2str_sort();
|
---|
867 |
|
---|
868 | CScriptToken::CScriptToken(CScriptLex *l, int Match, int Alternate) : line(l->currentLine()), column(l->currentColumn()), token(l->tk), intData(0)
|
---|
869 | {
|
---|
870 | if(token == LEX_INT || LEX_TOKEN_DATA_FLOAT(token)) {
|
---|
871 | CNumber number(l->tkStr);
|
---|
872 | if(number.isInfinity())
|
---|
873 | token=LEX_ID, (tokenData=new CScriptTokenDataString("Infinity"))->ref();
|
---|
874 | else if(number.isInt32())
|
---|
875 | token=LEX_INT, intData=number.toInt32();
|
---|
876 | else
|
---|
877 | token=LEX_FLOAT, floatData=new double(number.toDouble());
|
---|
878 | } else if(LEX_TOKEN_DATA_STRING(token))
|
---|
879 | (tokenData = new CScriptTokenDataString(l->tkStr))->ref();
|
---|
880 | else if(LEX_TOKEN_DATA_FUNCTION(token))
|
---|
881 | (tokenData = new CScriptTokenDataFnc)->ref();
|
---|
882 | else if (LEX_TOKEN_DATA_LOOP(token))
|
---|
883 | (tokenData = new CScriptTokenDataLoop)->ref();
|
---|
884 | else if (LEX_TOKEN_DATA_TRY(token))
|
---|
885 | (tokenData = new CScriptTokenDataTry)->ref();
|
---|
886 | if(Match>=0)
|
---|
887 | l->match(Match, Alternate);
|
---|
888 | else
|
---|
889 | l->match(l->tk);
|
---|
890 | #ifdef _DEBUG
|
---|
891 | token_str = getTokenStr(token);
|
---|
892 | #endif
|
---|
893 | }
|
---|
894 | CScriptToken::CScriptToken(uint16_t Tk, int IntData) : line(0), column(0), token(Tk), intData(0) {
|
---|
895 | if (LEX_TOKEN_DATA_SIMPLE(token))
|
---|
896 | intData = IntData;
|
---|
897 | else if (LEX_TOKEN_DATA_FUNCTION(token))
|
---|
898 | (tokenData = new CScriptTokenDataFnc)->ref();
|
---|
899 | else if (LEX_TOKEN_DATA_DESTRUCTURING_VAR(token))
|
---|
900 | (tokenData = new CScriptTokenDataDestructuringVar)->ref();
|
---|
901 | else if (LEX_TOKEN_DATA_OBJECT_LITERAL(token))
|
---|
902 | (tokenData = new CScriptTokenDataObjectLiteral)->ref();
|
---|
903 | else if (LEX_TOKEN_DATA_LOOP(token))
|
---|
904 | (tokenData = new CScriptTokenDataLoop)->ref();
|
---|
905 | else if (LEX_TOKEN_DATA_TRY(token))
|
---|
906 | (tokenData = new CScriptTokenDataTry)->ref();
|
---|
907 | else if (LEX_TOKEN_DATA_FORWARDER(token))
|
---|
908 | (tokenData = new CScriptTokenDataForwards)->ref();
|
---|
909 | else
|
---|
910 | ASSERT(0);
|
---|
911 | #ifdef _DEBUG
|
---|
912 | token_str = getTokenStr(token);
|
---|
913 | #endif
|
---|
914 | }
|
---|
915 |
|
---|
916 | CScriptToken::CScriptToken(uint16_t Tk, const string &TkStr) : line(0), column(0), token(Tk), intData(0) {
|
---|
917 | ASSERT(LEX_TOKEN_DATA_STRING(token));
|
---|
918 | (tokenData = new CScriptTokenDataString(TkStr))->ref();
|
---|
919 | #ifdef _DEBUG
|
---|
920 | token_str = getTokenStr(token);
|
---|
921 | #endif
|
---|
922 | }
|
---|
923 |
|
---|
924 | CScriptToken &CScriptToken::operator =(const CScriptToken &Copy)
|
---|
925 | {
|
---|
926 | if(this == &Copy) return *this;
|
---|
927 | clear();
|
---|
928 | #ifdef _DEBUG
|
---|
929 | token_str = Copy.token_str;
|
---|
930 | #endif
|
---|
931 | line = Copy.line;
|
---|
932 | column = Copy.column;
|
---|
933 | token = Copy.token;
|
---|
934 | if(LEX_TOKEN_DATA_FLOAT(token))
|
---|
935 | floatData = new double(*Copy.floatData);
|
---|
936 | else if(!LEX_TOKEN_DATA_SIMPLE(token))
|
---|
937 | (tokenData = Copy.tokenData)->ref();
|
---|
938 | else
|
---|
939 | intData = Copy.intData;
|
---|
940 | return *this;
|
---|
941 | }
|
---|
942 | string CScriptToken::getParsableString(TOKEN_VECT &Tokens, const string &IndentString, const string &Indent) {
|
---|
943 | return getParsableString(Tokens.begin(), Tokens.end(), IndentString, Indent);
|
---|
944 | }
|
---|
945 | string CScriptToken::getParsableString(TOKEN_VECT_it Begin, TOKEN_VECT_it End, const string &IndentString, const string &Indent) {
|
---|
946 | ostringstream destination;
|
---|
947 | string nl = Indent.size() ? "\n" : " ";
|
---|
948 | string my_indentString = IndentString;
|
---|
949 | bool add_nl=false, block_start=false, need_space=false;
|
---|
950 | int skip_collon = 0;
|
---|
951 |
|
---|
952 | for(TOKEN_VECT_it it=Begin; it != End; ++it) {
|
---|
953 | string OutString;
|
---|
954 | if(add_nl) OutString.append(nl).append(my_indentString);
|
---|
955 | bool old_block_start = block_start;
|
---|
956 | bool old_need_space = need_space;
|
---|
957 | add_nl = block_start = need_space =false;
|
---|
958 | if(it->token == LEX_STR)
|
---|
959 | OutString.append(getJSString(it->String())), need_space=true;
|
---|
960 | else if(LEX_TOKEN_DATA_STRING(it->token))
|
---|
961 | OutString.append(it->String()), need_space=true;
|
---|
962 | else if(LEX_TOKEN_DATA_FLOAT(it->token))
|
---|
963 | OutString.append(CNumber(it->Float()).toString()), need_space=true;
|
---|
964 | else if(it->token == LEX_INT)
|
---|
965 | OutString.append(CNumber(it->Int()).toString()), need_space=true;
|
---|
966 | else if(LEX_TOKEN_DATA_FUNCTION(it->token)) {
|
---|
967 | OutString.append("function ");
|
---|
968 | if(it->Fnc().name.size() )
|
---|
969 | OutString.append(it->Fnc().name);
|
---|
970 | OutString.append(it->Fnc().getArgumentsString());
|
---|
971 | OutString.append(getParsableString(it->Fnc().body, my_indentString, Indent));
|
---|
972 | if(it->Fnc().body.front().token != '{') {
|
---|
973 | OutString.append(";");
|
---|
974 | }
|
---|
975 | } else if(LEX_TOKEN_DATA_LOOP(it->token)) {
|
---|
976 | OutString.append(it->Loop().getParsableString(my_indentString, Indent));
|
---|
977 | } else if(LEX_TOKEN_DATA_TRY(it->token)) {
|
---|
978 | OutString.append(it->Try().getParsableString(my_indentString, Indent));
|
---|
979 | } else if(LEX_TOKEN_DATA_DESTRUCTURING_VAR(it->token)) {
|
---|
980 | OutString.append(it->DestructuringVar().getParsableString());
|
---|
981 | } else if(LEX_TOKEN_DATA_OBJECT_LITERAL(it->token)) {
|
---|
982 | OutString.append(it->Object().getParsableString());
|
---|
983 | } else if(it->token == '{') {
|
---|
984 | OutString.append("{");
|
---|
985 | my_indentString.append(Indent);
|
---|
986 | add_nl = block_start = true;
|
---|
987 | } else if(it->token == '}') {
|
---|
988 | my_indentString.resize(my_indentString.size() - min(my_indentString.size(),Indent.size()));
|
---|
989 | if(old_block_start)
|
---|
990 | OutString = "}";
|
---|
991 | else
|
---|
992 | OutString = nl + my_indentString + "}";
|
---|
993 | add_nl = true;
|
---|
994 | } else if(it->token == LEX_T_SKIP) {
|
---|
995 | // ignore SKIP-Token
|
---|
996 | } else if(it->token == LEX_T_FORWARD) {
|
---|
997 | // ignore Forwarder-Token
|
---|
998 | } else if(it->token == LEX_R_FOR) {
|
---|
999 | OutString.append(CScriptToken::getTokenStr(it->token));
|
---|
1000 | skip_collon=2;
|
---|
1001 | } else {
|
---|
1002 | OutString.append(CScriptToken::getTokenStr(it->token,&need_space));
|
---|
1003 | if(it->token==';') {
|
---|
1004 | if(skip_collon) { --skip_collon; }
|
---|
1005 | else add_nl=true;
|
---|
1006 | }
|
---|
1007 | }
|
---|
1008 | if(need_space && old_need_space) destination << " ";
|
---|
1009 | destination << OutString;
|
---|
1010 | }
|
---|
1011 | return destination.str();
|
---|
1012 |
|
---|
1013 | }
|
---|
1014 |
|
---|
1015 | void CScriptToken::clear()
|
---|
1016 | {
|
---|
1017 | if(LEX_TOKEN_DATA_FLOAT(token))
|
---|
1018 | delete floatData;
|
---|
1019 | else if(!LEX_TOKEN_DATA_SIMPLE(token))
|
---|
1020 | tokenData->unref();
|
---|
1021 | token = 0;
|
---|
1022 | }
|
---|
1023 | string CScriptToken::getTokenStr( int token, bool *need_space/*=0*/ )
|
---|
1024 | {
|
---|
1025 | if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort();
|
---|
1026 | token2str_t *found = lower_bound(reserved_words_begin, reserved_words_end, token, token2str_cmp_t());
|
---|
1027 | if(found != reserved_words_end && found->id==token) {
|
---|
1028 | if(need_space) *need_space=found->need_space;
|
---|
1029 | return found->str;
|
---|
1030 | }
|
---|
1031 | found = lower_bound(tokens2str_begin, tokens2str_end, token, token2str_cmp_t());
|
---|
1032 | if(found != tokens2str_end && found->id==token) {
|
---|
1033 | if(need_space) *need_space=found->need_space;
|
---|
1034 | return found->str;
|
---|
1035 | }
|
---|
1036 | if(need_space) *need_space=false;
|
---|
1037 |
|
---|
1038 | if (token>32 && token<128) {
|
---|
1039 | char buf[2] = " ";
|
---|
1040 | buf[0] = (char)token;
|
---|
1041 | return buf;
|
---|
1042 | }
|
---|
1043 |
|
---|
1044 | ostringstream msg;
|
---|
1045 | msg << "?[" << token << "]";
|
---|
1046 | return msg.str();
|
---|
1047 | }
|
---|
1048 | const char *CScriptToken::isReservedWord(int Token) {
|
---|
1049 | if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort();
|
---|
1050 | token2str_t *found = lower_bound(reserved_words_begin, reserved_words_end, Token, token2str_cmp_t());
|
---|
1051 | if(found != reserved_words_end && found->id==Token) {
|
---|
1052 | return found->str;
|
---|
1053 | }
|
---|
1054 | return 0;
|
---|
1055 | }
|
---|
1056 | int CScriptToken::isReservedWord(const string &Str) {
|
---|
1057 | const char *str = Str.c_str();
|
---|
1058 | if(!tokens2str_sorted) tokens2str_sorted=tokens2str_sort();
|
---|
1059 | token2str_t **found = lower_bound(str2reserved_begin, str2reserved_end, str, token2str_cmp_t());
|
---|
1060 | if(found != str2reserved_end && strcmp((*found)->str, str)==0) {
|
---|
1061 | return (*found)->id;
|
---|
1062 | }
|
---|
1063 | return LEX_ID;
|
---|
1064 | }
|
---|
1065 |
|
---|
1066 |
|
---|
1067 | //////////////////////////////////////////////////////////////////////////
|
---|
1068 | /// CScriptTokenizer
|
---|
1069 | //////////////////////////////////////////////////////////////////////////
|
---|
1070 |
|
---|
1071 | CScriptTokenizer::CScriptTokenizer() : l(0), prevPos(&tokens) {
|
---|
1072 | }
|
---|
1073 | CScriptTokenizer::CScriptTokenizer(CScriptLex &Lexer) : l(0), prevPos(&tokens) {
|
---|
1074 | tokenizeCode(Lexer);
|
---|
1075 | }
|
---|
1076 | CScriptTokenizer::CScriptTokenizer(const char *Code, const string &File, int Line, int Column) : l(0), prevPos(&tokens) {
|
---|
1077 | CScriptLex lexer(Code, File, Line, Column);
|
---|
1078 | tokenizeCode(lexer);
|
---|
1079 | }
|
---|
1080 | void CScriptTokenizer::tokenizeCode(CScriptLex &Lexer) {
|
---|
1081 | try {
|
---|
1082 | l=&Lexer;
|
---|
1083 | tokens.clear();
|
---|
1084 | tokenScopeStack.clear();
|
---|
1085 | ScriptTokenState state;
|
---|
1086 | pushForwarder(state);
|
---|
1087 | if(l->tk == '§') { // special-Token at Start means the code begins not at Statement-Level
|
---|
1088 | l->match('§');
|
---|
1089 | tokenizeLiteral(state, 0);
|
---|
1090 | } else do {
|
---|
1091 | tokenizeStatement(state, 0);
|
---|
1092 | } while (l->tk!=LEX_EOF);
|
---|
1093 | pushToken(state.Tokens, LEX_EOF); // add LEX_EOF-Token
|
---|
1094 | removeEmptyForwarder(state);
|
---|
1095 | // TOKEN_VECT(tokens).swap(tokens);// tokens.shrink_to_fit();
|
---|
1096 | tokens.swap(state.Tokens);
|
---|
1097 | pushTokenScope(tokens);
|
---|
1098 | currentFile = l->currentFile;
|
---|
1099 | tk = getToken().token;
|
---|
1100 | } catch (...) {
|
---|
1101 | l=0;
|
---|
1102 | throw;
|
---|
1103 | }
|
---|
1104 | }
|
---|
1105 |
|
---|
1106 | void CScriptTokenizer::getNextToken() {
|
---|
1107 | prevPos = tokenScopeStack.back();
|
---|
1108 | if(getToken().token == LEX_EOF)
|
---|
1109 | return;
|
---|
1110 | ScriptTokenPosition &_TokenPos = tokenScopeStack.back();
|
---|
1111 | _TokenPos.pos++;
|
---|
1112 | if(_TokenPos.pos == _TokenPos.tokens->end())
|
---|
1113 | tokenScopeStack.pop_back();
|
---|
1114 | // ScriptTokenPosition &TokenPos = tokenScopeStack.back();
|
---|
1115 | tk = getToken().token;
|
---|
1116 | }
|
---|
1117 |
|
---|
1118 |
|
---|
1119 |
|
---|
1120 | void CScriptTokenizer::match(int ExpectedToken, int AlternateToken/*=-1*/) {
|
---|
1121 | if(check(ExpectedToken, AlternateToken))
|
---|
1122 | getNextToken();
|
---|
1123 | }
|
---|
1124 | bool CScriptTokenizer::check(int ExpectedToken, int AlternateToken/*=-1*/) {
|
---|
1125 | int currentToken = getToken().token;
|
---|
1126 | if (ExpectedToken==';' && (currentToken==LEX_EOF || currentToken=='}')) return false; // ignore last missing ';'
|
---|
1127 | if (currentToken!=ExpectedToken && currentToken!=AlternateToken) {
|
---|
1128 | ostringstream errorString;
|
---|
1129 | if(ExpectedToken == LEX_EOF)
|
---|
1130 | errorString << "Got unexpected " << CScriptToken::getTokenStr(currentToken);
|
---|
1131 | else {
|
---|
1132 | errorString << "Got '" << CScriptToken::getTokenStr(currentToken) << "' expected '" << CScriptToken::getTokenStr(ExpectedToken) << "'";
|
---|
1133 | if(AlternateToken!=-1) errorString << " or '" << CScriptToken::getTokenStr(AlternateToken) << "'";
|
---|
1134 | }
|
---|
1135 | throw new CScriptException(SyntaxError, errorString.str(), currentFile, currentLine(), currentColumn());
|
---|
1136 | }
|
---|
1137 | return true;
|
---|
1138 | }
|
---|
1139 | void CScriptTokenizer::pushTokenScope(TOKEN_VECT &Tokens) {
|
---|
1140 | tokenScopeStack.push_back(ScriptTokenPosition(&Tokens));
|
---|
1141 | tk = getToken().token;
|
---|
1142 | }
|
---|
1143 |
|
---|
1144 | void CScriptTokenizer::setPos(ScriptTokenPosition &TokenPos) {
|
---|
1145 | ASSERT( TokenPos.tokens == tokenScopeStack.back().tokens);
|
---|
1146 | tokenScopeStack.back().pos = TokenPos.pos;
|
---|
1147 | tk = getToken().token;
|
---|
1148 | }
|
---|
1149 | void CScriptTokenizer::skip(int Tokens) {
|
---|
1150 | ASSERT(tokenScopeStack.back().tokens->end()-tokenScopeStack.back().pos-Tokens>=0);
|
---|
1151 | tokenScopeStack.back().pos+=Tokens-1;
|
---|
1152 | getNextToken();
|
---|
1153 | }
|
---|
1154 |
|
---|
1155 | static inline void setTokenSkip(CScriptTokenizer::ScriptTokenState &State) {
|
---|
1156 | int tokenBeginIdx = State.Marks.back();
|
---|
1157 | State.Marks.pop_back();
|
---|
1158 | State.Tokens[tokenBeginIdx].Int() = State.Tokens.size()-tokenBeginIdx;
|
---|
1159 | }
|
---|
1160 |
|
---|
1161 | enum {
|
---|
1162 | TOKENIZE_FLAGS_canLabel = 1<<0,
|
---|
1163 | TOKENIZE_FLAGS_canBreak = 1<<1,
|
---|
1164 | TOKENIZE_FLAGS_canContinue = 1<<2,
|
---|
1165 | TOKENIZE_FLAGS_canReturn = 1<<3,
|
---|
1166 | TOKENIZE_FLAGS_asStatement = 1<<4,
|
---|
1167 | TOKENIZE_FLAGS_noIn = 1<<5,
|
---|
1168 | TOKENIZE_FLAGS_isAccessor = 1<<6,
|
---|
1169 | TOKENIZE_FLAGS_callForNew = 1<<7,
|
---|
1170 | TOKENIZE_FLAGS_noBlockStart = 1<<8,
|
---|
1171 | TOKENIZE_FLAGS_nestedObject = 1<<9,
|
---|
1172 | };
|
---|
1173 | void CScriptTokenizer::tokenizeTry(ScriptTokenState &State, int Flags) {
|
---|
1174 | l->match(LEX_R_TRY);
|
---|
1175 | CScriptToken TryToken(LEX_T_TRY);
|
---|
1176 | CScriptTokenDataTry &TryData = TryToken.Try();
|
---|
1177 | pushToken(State.Tokens, TryToken);
|
---|
1178 |
|
---|
1179 | TOKEN_VECT mainTokens;
|
---|
1180 | State.Tokens.swap(mainTokens);
|
---|
1181 |
|
---|
1182 | // try-block
|
---|
1183 | tokenizeBlock(State, Flags);
|
---|
1184 | State.Tokens.swap(TryData.tryBlock);
|
---|
1185 |
|
---|
1186 | // catch-blocks
|
---|
1187 | l->check(LEX_R_CATCH, LEX_R_FINALLY);
|
---|
1188 | bool unconditionalCatch = false;
|
---|
1189 | while(l->tk == LEX_R_CATCH) {
|
---|
1190 | if(unconditionalCatch) throw new CScriptException(SyntaxError, "catch after unconditional catch", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
1191 |
|
---|
1192 | // vars & condition
|
---|
1193 | l->match(LEX_R_CATCH);
|
---|
1194 | l->match('(');
|
---|
1195 | TryData.catchBlocks.resize(TryData.catchBlocks.size()+1);
|
---|
1196 | CScriptTokenDataTry::CatchBlock &catchBlock = TryData.catchBlocks.back();
|
---|
1197 | pushForwarder(State, true);
|
---|
1198 | STRING_VECTOR_t vars;
|
---|
1199 | catchBlock.indentifiers = tokenizeVarIdentifier(&vars).DestructuringVar();
|
---|
1200 | State.Forwarders.back()->addLets(vars);
|
---|
1201 | if(l->tk == LEX_R_IF) {
|
---|
1202 | l->match(LEX_R_IF);
|
---|
1203 | tokenizeExpression(State, Flags);
|
---|
1204 | } else
|
---|
1205 | unconditionalCatch = true;
|
---|
1206 | State.Tokens.swap(catchBlock.condition);
|
---|
1207 | l->match(')');
|
---|
1208 |
|
---|
1209 | // catch-block
|
---|
1210 | tokenizeBlock(State, Flags | TOKENIZE_FLAGS_noBlockStart);
|
---|
1211 | State.Tokens.swap(catchBlock.block);
|
---|
1212 | State.Forwarders.pop_back();
|
---|
1213 | }
|
---|
1214 | // finally-block
|
---|
1215 | if(l->tk == LEX_R_FINALLY) {
|
---|
1216 | l->match(LEX_R_FINALLY);
|
---|
1217 | tokenizeBlock(State, Flags);
|
---|
1218 | State.Tokens.swap(TryData.finallyBlock);
|
---|
1219 | }
|
---|
1220 | State.Tokens.swap(mainTokens);
|
---|
1221 | }
|
---|
1222 | void CScriptTokenizer::tokenizeSwitch(ScriptTokenState &State, int Flags) {
|
---|
1223 |
|
---|
1224 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
1225 | pushToken(State.Tokens, '(');
|
---|
1226 | tokenizeExpression(State, Flags);
|
---|
1227 | pushToken(State.Tokens, ')');
|
---|
1228 |
|
---|
1229 | State.Marks.push_back(pushToken(State.Tokens, '{')); // push Token & push blockBeginIdx
|
---|
1230 | pushForwarder(State);
|
---|
1231 |
|
---|
1232 |
|
---|
1233 | vector<int>::size_type MarksSize = State.Marks.size();
|
---|
1234 | Flags |= TOKENIZE_FLAGS_canBreak;
|
---|
1235 | for(bool hasDefault=false;;) {
|
---|
1236 | if( l->tk == LEX_R_CASE || l->tk == LEX_R_DEFAULT) {
|
---|
1237 | if(l->tk == LEX_R_CASE) {
|
---|
1238 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push caseBeginIdx
|
---|
1239 | State.Marks.push_back(pushToken(State.Tokens,CScriptToken(LEX_T_SKIP))); // skipper to skip case-expression
|
---|
1240 | tokenizeExpression(State, Flags);
|
---|
1241 | setTokenSkip(State);
|
---|
1242 | } else { // default
|
---|
1243 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push caseBeginIdx
|
---|
1244 | if(hasDefault) throw new CScriptException(SyntaxError, "more than one switch default", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
1245 | hasDefault = true;
|
---|
1246 | }
|
---|
1247 |
|
---|
1248 | State.Marks.push_back(pushToken(State.Tokens, ':'));
|
---|
1249 | while(l->tk != '}' && l->tk != LEX_R_CASE && l->tk != LEX_R_DEFAULT && l->tk != LEX_EOF )
|
---|
1250 | tokenizeStatement(State, Flags);
|
---|
1251 | setTokenSkip(State);
|
---|
1252 | } else if(l->tk == '}')
|
---|
1253 | break;
|
---|
1254 | else
|
---|
1255 | throw new CScriptException(SyntaxError, "invalid switch statement", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
1256 | }
|
---|
1257 | while(MarksSize < State.Marks.size()) setTokenSkip(State);
|
---|
1258 | removeEmptyForwarder(State); // remove Forwarder if empty
|
---|
1259 | pushToken(State.Tokens, '}');
|
---|
1260 | setTokenSkip(State); // switch-block
|
---|
1261 | setTokenSkip(State); // switch-statement
|
---|
1262 | }
|
---|
1263 | void CScriptTokenizer::tokenizeWith(ScriptTokenState &State, int Flags) {
|
---|
1264 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
1265 |
|
---|
1266 | pushToken(State.Tokens, '(');
|
---|
1267 | tokenizeExpression(State, Flags);
|
---|
1268 | pushToken(State.Tokens, ')');
|
---|
1269 | tokenizeStatementNoLet(State, Flags);
|
---|
1270 |
|
---|
1271 | setTokenSkip(State);
|
---|
1272 | }
|
---|
1273 |
|
---|
1274 | static inline uint32_t GetLoopLabels(CScriptTokenizer::ScriptTokenState &State, CScriptTokenDataLoop &LoopData) {
|
---|
1275 | uint32_t label_count = 0;
|
---|
1276 | if(State.Tokens.size()>=2) {
|
---|
1277 | for(TOKEN_VECT::reverse_iterator it = State.Tokens.rbegin(); it!=State.Tokens.rend(); ++it) {
|
---|
1278 | if(it->token == ':' && (++it)->token == LEX_T_LABEL) {
|
---|
1279 | ++label_count;
|
---|
1280 | LoopData.labels.push_back(it->String());
|
---|
1281 | State.LoopLabels.push_back(it->String());
|
---|
1282 | it->token = LEX_T_DUMMY_LABEL;
|
---|
1283 | } else
|
---|
1284 | break;
|
---|
1285 | }
|
---|
1286 | }
|
---|
1287 | return label_count;
|
---|
1288 | }
|
---|
1289 | static inline void PopLoopLabels(uint32_t label_count, STRING_VECTOR_t &LoopLabels) {
|
---|
1290 | ASSERT(label_count <= LoopLabels.size());
|
---|
1291 | LoopLabels.resize(LoopLabels.size()-label_count);
|
---|
1292 | }
|
---|
1293 | void CScriptTokenizer::tokenizeWhileAndDo(ScriptTokenState &State, int Flags) {
|
---|
1294 |
|
---|
1295 | bool do_while = l->tk==LEX_R_DO;
|
---|
1296 |
|
---|
1297 | CScriptToken LoopToken(LEX_T_LOOP);
|
---|
1298 | CScriptTokenDataLoop &LoopData = LoopToken.Loop();
|
---|
1299 | LoopData.type = do_while ? CScriptTokenDataLoop::DO : CScriptTokenDataLoop::WHILE;
|
---|
1300 |
|
---|
1301 | // get loop-labels
|
---|
1302 | uint32_t label_count = GetLoopLabels(State, LoopData);
|
---|
1303 |
|
---|
1304 | l->match(l->tk); // match while or do
|
---|
1305 |
|
---|
1306 | pushToken(State.Tokens, LoopToken);
|
---|
1307 |
|
---|
1308 | TOKEN_VECT mainTokens;
|
---|
1309 | State.Tokens.swap(mainTokens);
|
---|
1310 |
|
---|
1311 | if(!do_while) {
|
---|
1312 | l->match('(');
|
---|
1313 | tokenizeExpression(State, Flags);
|
---|
1314 | State.Tokens.swap(LoopData.condition);
|
---|
1315 | l->match(')');
|
---|
1316 | }
|
---|
1317 | tokenizeStatementNoLet(State, Flags | TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue);
|
---|
1318 | State.Tokens.swap(LoopData.body);
|
---|
1319 | if(do_while) {
|
---|
1320 | l->match(LEX_R_WHILE);
|
---|
1321 | l->match('(');
|
---|
1322 | tokenizeExpression(State, Flags);
|
---|
1323 | State.Tokens.swap(LoopData.condition);
|
---|
1324 | l->match(')');
|
---|
1325 | l->match(';');
|
---|
1326 | }
|
---|
1327 | State.Tokens.swap(mainTokens);
|
---|
1328 | PopLoopLabels(label_count, State.LoopLabels);
|
---|
1329 | }
|
---|
1330 |
|
---|
1331 | void CScriptTokenizer::tokenizeIf(ScriptTokenState &State, int Flags) {
|
---|
1332 |
|
---|
1333 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
1334 |
|
---|
1335 | pushToken(State.Tokens, '(');
|
---|
1336 | tokenizeExpression(State, Flags);
|
---|
1337 | pushToken(State.Tokens, ')');
|
---|
1338 | State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx
|
---|
1339 | tokenizeStatementNoLet(State, Flags);
|
---|
1340 |
|
---|
1341 | setTokenSkip(State);
|
---|
1342 |
|
---|
1343 | if(l->tk == LEX_R_ELSE) {
|
---|
1344 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push tokenBeginIdx
|
---|
1345 | tokenizeStatementNoLet(State, Flags);
|
---|
1346 | setTokenSkip(State);
|
---|
1347 | }
|
---|
1348 |
|
---|
1349 | setTokenSkip(State);
|
---|
1350 | }
|
---|
1351 |
|
---|
1352 | void CScriptTokenizer::tokenizeFor(ScriptTokenState &State, int Flags) {
|
---|
1353 | bool for_in=false, for_of=false, for_each_in=false;
|
---|
1354 | CScriptToken LoopToken(LEX_T_LOOP);
|
---|
1355 | CScriptTokenDataLoop &LoopData = LoopToken.Loop();
|
---|
1356 |
|
---|
1357 | // get loop-labels
|
---|
1358 | uint32_t label_count = GetLoopLabels(State, LoopData);
|
---|
1359 |
|
---|
1360 | l->match(LEX_R_FOR);
|
---|
1361 | if((for_in = for_each_in = (l->tk == LEX_ID && l->tkStr == "each")))
|
---|
1362 | l->match(LEX_ID); // match "each"
|
---|
1363 |
|
---|
1364 | pushToken(State.Tokens, LoopToken);
|
---|
1365 |
|
---|
1366 | l->match('(');
|
---|
1367 | TOKEN_VECT mainTokens;
|
---|
1368 | State.Tokens.swap(mainTokens);
|
---|
1369 |
|
---|
1370 | bool haveLetScope = false;
|
---|
1371 |
|
---|
1372 | if(l->tk == LEX_R_VAR || l->tk == LEX_R_LET) {
|
---|
1373 | if(l->tk == LEX_R_VAR)
|
---|
1374 | tokenizeVarNoConst(State, Flags | TOKENIZE_FLAGS_noIn);
|
---|
1375 | else { //if(l->tk == LEX_R_LET)
|
---|
1376 | haveLetScope = true;
|
---|
1377 | pushForwarder(State, true); // no clean up empty tokenizer
|
---|
1378 | tokenizeLet(State, Flags | TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_asStatement);
|
---|
1379 | }
|
---|
1380 | } else if(l->tk!=';') {
|
---|
1381 | tokenizeExpression(State, Flags | TOKENIZE_FLAGS_noIn);
|
---|
1382 | }
|
---|
1383 | if((for_in=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))) {
|
---|
1384 | if(!State.LeftHand)
|
---|
1385 | throw new CScriptException(ReferenceError, "invalid for/in left-hand side", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
1386 | if(l->tk==LEX_ID && l->tkStr=="of") l->tk = LEX_T_OF; // fake token
|
---|
1387 | if((for_of = (!for_each_in && l->tk==LEX_T_OF))) {
|
---|
1388 | l->match(LEX_T_OF);
|
---|
1389 | LoopData.type = CScriptTokenDataLoop::FOR_OF;
|
---|
1390 | } else {
|
---|
1391 | l->match(LEX_R_IN);
|
---|
1392 | LoopData.type = for_each_in ? CScriptTokenDataLoop::FOR_EACH : CScriptTokenDataLoop::FOR_IN;
|
---|
1393 | }
|
---|
1394 | State.Tokens.swap(LoopData.condition);
|
---|
1395 |
|
---|
1396 | if(LoopData.condition.front().token == LEX_T_FORWARD) {
|
---|
1397 | LoopData.init.push_back(LoopData.condition.front());
|
---|
1398 | LoopData.condition.erase(LoopData.condition.begin());
|
---|
1399 | }
|
---|
1400 | mainTokens.back().token = LEX_T_FOR_IN;
|
---|
1401 | } else {
|
---|
1402 | l->check(';'); // no automatic ;-injection
|
---|
1403 | pushToken(State.Tokens, ';');
|
---|
1404 | State.Tokens.swap(LoopData.init);
|
---|
1405 | if(l->tk != ';') tokenizeExpression(State, Flags);
|
---|
1406 | l->check(';'); // no automatic ;-injection
|
---|
1407 | l->match(';'); // no automatic ;-injection
|
---|
1408 | State.Tokens.swap(LoopData.condition);
|
---|
1409 | }
|
---|
1410 |
|
---|
1411 | if(for_in || l->tk != ')') tokenizeExpression(State, Flags);
|
---|
1412 | l->match(')');
|
---|
1413 | State.Tokens.swap(LoopData.iter);
|
---|
1414 | Flags = TOKENIZE_FLAGS_canBreak | TOKENIZE_FLAGS_canContinue;
|
---|
1415 | if(haveLetScope) Flags |= TOKENIZE_FLAGS_noBlockStart;
|
---|
1416 | tokenizeStatementNoLet(State, Flags);
|
---|
1417 | if(haveLetScope) State.Forwarders.pop_back();
|
---|
1418 |
|
---|
1419 | State.Tokens.swap(LoopData.body);
|
---|
1420 | State.Tokens.swap(mainTokens);
|
---|
1421 | if(for_in) {
|
---|
1422 | LoopData.condition.push_back('=');
|
---|
1423 | LoopData.condition.push_back(LEX_T_EXCEPTION_VAR);
|
---|
1424 | LoopData.condition.push_back('.');
|
---|
1425 | LoopData.condition.push_back(CScriptToken(LEX_ID, "next"));
|
---|
1426 | LoopData.condition.push_back('(');
|
---|
1427 | LoopData.condition.push_back(')');
|
---|
1428 | LoopData.condition.push_back(';');
|
---|
1429 | }
|
---|
1430 | PopLoopLabels(label_count, State.LoopLabels);
|
---|
1431 | }
|
---|
1432 |
|
---|
1433 | static void tokenizeVarIdentifierDestructuring( CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t *VarNames );
|
---|
1434 | static void tokenizeVarIdentifierDestructuringObject(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t *VarNames) {
|
---|
1435 | Lexer->match('{');
|
---|
1436 | while(Lexer->tk != '}') {
|
---|
1437 | CScriptLex::POS prev_pos = Lexer->pos;
|
---|
1438 | string Path = Lexer->tkStr;
|
---|
1439 | Lexer->match(LEX_ID, LEX_STR);
|
---|
1440 | if(Lexer->tk == ':') {
|
---|
1441 | Lexer->match(':');
|
---|
1442 | tokenizeVarIdentifierDestructuring(Lexer, Vars, Path, VarNames);
|
---|
1443 | } else {
|
---|
1444 | Lexer->reset(prev_pos);
|
---|
1445 | if(VarNames) VarNames->push_back(Lexer->tkStr);
|
---|
1446 | Vars.push_back(DESTRUCTURING_VAR_t(Lexer->tkStr, Lexer->tkStr));
|
---|
1447 | Lexer->match(LEX_ID);
|
---|
1448 | }
|
---|
1449 | if (Lexer->tk!='}') Lexer->match(',', '}');
|
---|
1450 | }
|
---|
1451 | Lexer->match('}');
|
---|
1452 | }
|
---|
1453 | static void tokenizeVarIdentifierDestructuringArray(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, STRING_VECTOR_t *VarNames) {
|
---|
1454 | int idx = 0;
|
---|
1455 | Lexer->match('[');
|
---|
1456 | while(Lexer->tk != ']') {
|
---|
1457 | if(Lexer->tk == ',')
|
---|
1458 | Vars.push_back(DESTRUCTURING_VAR_t("", "")); // empty
|
---|
1459 | else
|
---|
1460 | tokenizeVarIdentifierDestructuring(Lexer, Vars, int2string(idx), VarNames);
|
---|
1461 | ++idx;
|
---|
1462 | if (Lexer->tk!=']') Lexer->match(',',']');
|
---|
1463 | }
|
---|
1464 | Lexer->match(']');
|
---|
1465 | }
|
---|
1466 | static void tokenizeVarIdentifierDestructuring(CScriptLex *Lexer, DESTRUCTURING_VARS_t &Vars, const std::string &Path, STRING_VECTOR_t *VarNames ) {
|
---|
1467 | if(Lexer->tk == '[') {
|
---|
1468 | Vars.push_back(DESTRUCTURING_VAR_t(Path, "[")); // marks array begin
|
---|
1469 | tokenizeVarIdentifierDestructuringArray(Lexer, Vars, VarNames);
|
---|
1470 | Vars.push_back(DESTRUCTURING_VAR_t("", "]")); // marks array end
|
---|
1471 | } else if(Lexer->tk == '{') {
|
---|
1472 | Vars.push_back(DESTRUCTURING_VAR_t(Path, "{")); // marks object begin
|
---|
1473 | tokenizeVarIdentifierDestructuringObject(Lexer, Vars, VarNames);
|
---|
1474 | Vars.push_back(DESTRUCTURING_VAR_t("", "}")); // marks object end
|
---|
1475 | } else {
|
---|
1476 | if(VarNames) VarNames->push_back(Lexer->tkStr);
|
---|
1477 | Vars.push_back(DESTRUCTURING_VAR_t(Path, Lexer->tkStr));
|
---|
1478 | Lexer->match(LEX_ID);
|
---|
1479 | }
|
---|
1480 | }
|
---|
1481 | CScriptToken CScriptTokenizer::tokenizeVarIdentifier( STRING_VECTOR_t *VarNames/*=0*/, bool *NeedAssignment/*=0*/ ) {
|
---|
1482 | CScriptToken token(LEX_T_DESTRUCTURING_VAR);
|
---|
1483 | if(NeedAssignment) *NeedAssignment=(l->tk == '[' || l->tk=='{');
|
---|
1484 | token.column = l->currentColumn();
|
---|
1485 | token.line = l->currentLine();
|
---|
1486 | tokenizeVarIdentifierDestructuring(l, token.DestructuringVar().vars, "", VarNames);
|
---|
1487 | return token;
|
---|
1488 | }
|
---|
1489 |
|
---|
1490 | void CScriptTokenizer::tokenizeFunction(ScriptTokenState &State, int Flags, bool noLetDef/*=false*/) {
|
---|
1491 | bool forward = false;
|
---|
1492 | bool Statement = (Flags & TOKENIZE_FLAGS_asStatement) != 0;
|
---|
1493 | bool Accessor = (Flags & TOKENIZE_FLAGS_isAccessor) != 0;
|
---|
1494 |
|
---|
1495 | int tk = l->tk;
|
---|
1496 | if(Accessor) {
|
---|
1497 | tk = State.Tokens.back().String()=="get"?LEX_T_GET:LEX_T_SET;
|
---|
1498 | State.Tokens.pop_back();
|
---|
1499 | } else {
|
---|
1500 | l->match(LEX_R_FUNCTION);
|
---|
1501 | if(!Statement) tk = LEX_T_FUNCTION_OPERATOR;
|
---|
1502 | }
|
---|
1503 | if(tk == LEX_R_FUNCTION) // only forward functions
|
---|
1504 | forward = !noLetDef && State.Forwarders.front() == State.Forwarders.back();
|
---|
1505 |
|
---|
1506 | CScriptToken FncToken(tk);
|
---|
1507 | CScriptTokenDataFnc &FncData = FncToken.Fnc();
|
---|
1508 |
|
---|
1509 | if(l->tk == LEX_ID || Accessor) {
|
---|
1510 | FncData.name = l->tkStr;
|
---|
1511 | l->match(LEX_ID, LEX_STR);
|
---|
1512 | } else if(Statement)
|
---|
1513 | throw new CScriptException(SyntaxError, "Function statement requires a name.", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
1514 | l->match('(');
|
---|
1515 | while(l->tk != ')') {
|
---|
1516 | FncData.arguments.push_back(tokenizeVarIdentifier());
|
---|
1517 | if (l->tk!=')') l->match(',',')');
|
---|
1518 | }
|
---|
1519 | // l->match(')');
|
---|
1520 | // to allow regexp at the beginning of a lambda-function fake last token
|
---|
1521 | l->tk = '{';
|
---|
1522 | l->match('{');
|
---|
1523 | FncData.file = l->currentFile;
|
---|
1524 | FncData.line = l->currentLine();
|
---|
1525 |
|
---|
1526 | ScriptTokenState functionState;
|
---|
1527 | if(l->tk == '{' || tk==LEX_T_GET || tk==LEX_T_SET)
|
---|
1528 | tokenizeBlock(functionState, TOKENIZE_FLAGS_canReturn);
|
---|
1529 | else {
|
---|
1530 | tokenizeExpression(functionState, 0);
|
---|
1531 | l->match(';');
|
---|
1532 | }
|
---|
1533 | functionState.Tokens.swap(FncData.body);
|
---|
1534 | if(forward) {
|
---|
1535 | State.Forwarders.front()->functions.insert(FncToken);
|
---|
1536 | FncToken.token = LEX_T_FUNCTION_PLACEHOLDER;
|
---|
1537 | }
|
---|
1538 | State.Tokens.push_back(FncToken);
|
---|
1539 | }
|
---|
1540 |
|
---|
1541 | void CScriptTokenizer::tokenizeLet(ScriptTokenState &State, int Flags, bool noLetDef/*=false*/) {
|
---|
1542 | bool Definition = (Flags & TOKENIZE_FLAGS_asStatement)!=0;
|
---|
1543 | bool noIN = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
1544 | bool Statement = Definition & !noIN;
|
---|
1545 | bool Expression = !Definition;
|
---|
1546 | Flags &= ~(TOKENIZE_FLAGS_asStatement);
|
---|
1547 | if(!Definition) noIN=false, Flags &= ~TOKENIZE_FLAGS_noIn;
|
---|
1548 |
|
---|
1549 | bool foundIN = false;
|
---|
1550 | bool leftHand = true;
|
---|
1551 | int currLine = l->currentLine(), currColumn = l->currentColumn();
|
---|
1552 |
|
---|
1553 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
1554 |
|
---|
1555 | if(l->tk == '(' || !Definition) { // no definition needs statement or expression
|
---|
1556 | leftHand = false;
|
---|
1557 | Expression = true;
|
---|
1558 | pushToken(State.Tokens, '(');
|
---|
1559 | pushForwarder(State);
|
---|
1560 | } else if(noLetDef)
|
---|
1561 | throw new CScriptException(SyntaxError, "let declaration not directly within block", l->currentFile, currLine, currColumn);
|
---|
1562 | STRING_VECTOR_t vars;
|
---|
1563 | for(;;) {
|
---|
1564 | bool needAssignment = false;
|
---|
1565 | State.Tokens.push_back(tokenizeVarIdentifier(&vars, &needAssignment));
|
---|
1566 | if(noIN && (foundIN=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of"))))
|
---|
1567 | break;
|
---|
1568 | if(needAssignment || l->tk=='=') {
|
---|
1569 | leftHand = false;
|
---|
1570 | pushToken(State.Tokens, '=');
|
---|
1571 | tokenizeAssignment(State, Flags);
|
---|
1572 | if(noIN && (foundIN=(l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of"))))
|
---|
1573 | break;
|
---|
1574 | }
|
---|
1575 | if(l->tk==',') {
|
---|
1576 | leftHand = false;
|
---|
1577 | pushToken(State.Tokens);
|
---|
1578 | }
|
---|
1579 | else
|
---|
1580 | break;
|
---|
1581 | }
|
---|
1582 | if(Expression) {
|
---|
1583 | string redeclared = State.Forwarders.back()->addLets(vars);
|
---|
1584 | if(redeclared.size())
|
---|
1585 | throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn);
|
---|
1586 | if(!foundIN) {
|
---|
1587 | pushToken(State.Tokens, ')');
|
---|
1588 | if(Statement) {
|
---|
1589 | if(l->tk == '{') // no extra BlockStart by expression
|
---|
1590 | tokenizeBlock(State, Flags|=TOKENIZE_FLAGS_noBlockStart);
|
---|
1591 | else
|
---|
1592 | tokenizeStatementNoLet(State, Flags);
|
---|
1593 | } else
|
---|
1594 | tokenizeAssignment(State, Flags);
|
---|
1595 | }
|
---|
1596 | // never remove Forwarder-token here -- popForwarder(Tokens, BlockStart, Marks);
|
---|
1597 | State.Forwarders.back()->vars_in_letscope.clear(); // only clear vars_in_letscope
|
---|
1598 | State.Marks.pop_back();
|
---|
1599 | } else {
|
---|
1600 | if(!noIN) pushToken(State.Tokens, ';');
|
---|
1601 |
|
---|
1602 | string redeclared;
|
---|
1603 | if(State.Forwarders.size()<=1) {
|
---|
1604 | // Currently it is allowed in javascript, to redeclare "let"-declared vars
|
---|
1605 | // in root- or function-scopes. In this case, "let" handled like "var"
|
---|
1606 | // To prevent redeclaration in root- or function-scopes define PREVENT_REDECLARATION_IN_FUNCTION_SCOPES
|
---|
1607 | #ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES
|
---|
1608 | redeclared = State.Forwarders.front()->addLets(vars);
|
---|
1609 | #else
|
---|
1610 | State.Forwarders.front()->addVars(vars);
|
---|
1611 | #endif
|
---|
1612 | } else
|
---|
1613 | redeclared = State.Forwarders.back()->addLets(vars);
|
---|
1614 | if(redeclared.size())
|
---|
1615 | throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn);
|
---|
1616 | }
|
---|
1617 | setTokenSkip(State);
|
---|
1618 | if(leftHand) State.LeftHand = true;
|
---|
1619 | }
|
---|
1620 |
|
---|
1621 | void CScriptTokenizer::tokenizeVarNoConst( ScriptTokenState &State, int Flags) {
|
---|
1622 | l->check(LEX_R_VAR);
|
---|
1623 | tokenizeVarAndConst(State, Flags);
|
---|
1624 | }
|
---|
1625 | void CScriptTokenizer::tokenizeVarAndConst( ScriptTokenState &State, int Flags) {
|
---|
1626 | bool noIN = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
1627 | int currLine = l->currentLine(), currColumn = l->currentColumn();
|
---|
1628 |
|
---|
1629 | bool leftHand = true;
|
---|
1630 | int tk = l->tk;
|
---|
1631 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
1632 |
|
---|
1633 | STRING_VECTOR_t vars;
|
---|
1634 | for(;;)
|
---|
1635 | {
|
---|
1636 | bool needAssignment = false;
|
---|
1637 | State.Tokens.push_back(tokenizeVarIdentifier(&vars, &needAssignment));
|
---|
1638 | if(noIN && (l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))
|
---|
1639 | break;
|
---|
1640 | if(needAssignment || l->tk=='=') {
|
---|
1641 | leftHand = false;
|
---|
1642 | pushToken(State.Tokens, '=');
|
---|
1643 | tokenizeAssignment(State, Flags);
|
---|
1644 | if(noIN && (l->tk==LEX_R_IN || (l->tk==LEX_ID && l->tkStr=="of")))
|
---|
1645 | break;
|
---|
1646 | }
|
---|
1647 | if(l->tk==',') {
|
---|
1648 | leftHand = false;
|
---|
1649 | pushToken(State.Tokens);
|
---|
1650 | }
|
---|
1651 | else
|
---|
1652 | break;
|
---|
1653 | }
|
---|
1654 | if(!noIN) pushToken(State.Tokens, ';');
|
---|
1655 |
|
---|
1656 | setTokenSkip(State);
|
---|
1657 |
|
---|
1658 | if(tk==LEX_R_VAR)
|
---|
1659 | State.Forwarders.front()->addVars(vars);
|
---|
1660 | else
|
---|
1661 | State.Forwarders.front()->addConsts(vars);
|
---|
1662 | string redeclared;
|
---|
1663 | if(State.Forwarders.size()>1) // have let-scope
|
---|
1664 | redeclared = State.Forwarders.back()->addVarsInLetscope(vars);
|
---|
1665 | #ifdef PREVENT_REDECLARATION_IN_FUNCTION_SCOPES
|
---|
1666 | else
|
---|
1667 | redeclared = State.Forwarders.front()->addVarsInLetscope(vars);
|
---|
1668 | #endif
|
---|
1669 | if(redeclared.size())
|
---|
1670 | throw new CScriptException(TypeError, "redeclaration of variable '"+redeclared+"'", l->currentFile, currLine, currColumn);
|
---|
1671 | if(leftHand) State.LeftHand = true;
|
---|
1672 | }
|
---|
1673 |
|
---|
1674 | void CScriptTokenizer::_tokenizeLiteralObject(ScriptTokenState &State, int Flags) {
|
---|
1675 | bool forFor = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
1676 | bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0;
|
---|
1677 | Flags &= ~(TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_nestedObject);
|
---|
1678 | CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL);
|
---|
1679 | CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object();
|
---|
1680 |
|
---|
1681 | Objc.type = CScriptTokenDataObjectLiteral::OBJECT;
|
---|
1682 | Objc.destructuring = Objc.structuring = true;
|
---|
1683 |
|
---|
1684 | string msg, msgFile;
|
---|
1685 | int msgLine=0, msgColumn=0;
|
---|
1686 |
|
---|
1687 | l->match('{');
|
---|
1688 | while (l->tk != '}') {
|
---|
1689 | CScriptTokenDataObjectLiteral::ELEMENT element;
|
---|
1690 | bool assign = false;
|
---|
1691 | if(CScriptToken::isReservedWord(l->tk))
|
---|
1692 | l->tk = LEX_ID; // fake reserved-word as member.ID
|
---|
1693 | if(l->tk == LEX_ID) {
|
---|
1694 | element.id = l->tkStr;
|
---|
1695 | CScriptToken Token(l, LEX_ID);
|
---|
1696 | if((l->tk==LEX_ID || l->tk==LEX_STR ) && (element.id=="get" || element.id=="set")) {
|
---|
1697 | element.id = l->tkStr;
|
---|
1698 | element.value.push_back(Token);
|
---|
1699 | State.Tokens.swap(element.value);
|
---|
1700 | tokenizeFunction(State, Flags|TOKENIZE_FLAGS_isAccessor);
|
---|
1701 | State.Tokens.swap(element.value);
|
---|
1702 | Objc.destructuring = false;
|
---|
1703 | } else {
|
---|
1704 | if(Objc.destructuring && (l->tk == ',' || l->tk == '}')) {
|
---|
1705 | if(!msg.size()) {
|
---|
1706 | Objc.structuring = false;
|
---|
1707 | msg.append("Got '").append(CScriptToken::getTokenStr(l->tk)).append("' expected ':'");
|
---|
1708 | msgFile = l->currentFile;
|
---|
1709 | msgLine = l->currentLine();
|
---|
1710 | msgColumn = l->currentColumn();
|
---|
1711 | ;
|
---|
1712 | }
|
---|
1713 | element.value.push_back(Token);
|
---|
1714 | } else
|
---|
1715 | assign = true;
|
---|
1716 | }
|
---|
1717 | } else if(l->tk == LEX_INT) {
|
---|
1718 | element.id = int2string((int32_t)strtol(l->tkStr.c_str(),0,0));
|
---|
1719 | l->match(LEX_INT);
|
---|
1720 | assign = true;
|
---|
1721 | } else if(l->tk == LEX_FLOAT) {
|
---|
1722 | element.id = float2string(strtod(l->tkStr.c_str(),0));
|
---|
1723 | l->match(LEX_FLOAT);
|
---|
1724 | assign = true;
|
---|
1725 | } else if(LEX_TOKEN_DATA_STRING(l->tk) && l->tk != LEX_REGEXP) {
|
---|
1726 | element.id = l->tkStr;
|
---|
1727 | l->match(l->tk);
|
---|
1728 | assign = true;
|
---|
1729 | } else
|
---|
1730 | l->match(LEX_ID, LEX_STR);
|
---|
1731 | if(assign) {
|
---|
1732 | l->match(':');
|
---|
1733 | int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0;
|
---|
1734 | State.pushLeftHandState();
|
---|
1735 | State.Tokens.swap(element.value);
|
---|
1736 | tokenizeAssignment(State, dFlags);
|
---|
1737 | State.Tokens.swap(element.value);
|
---|
1738 | if(Objc.destructuring) Objc.destructuring = State.LeftHand;
|
---|
1739 | State.popLeftHandeState();
|
---|
1740 | }
|
---|
1741 |
|
---|
1742 | if(!Objc.destructuring && msg.size())
|
---|
1743 | throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn);
|
---|
1744 | Objc.elements.push_back(element);
|
---|
1745 | if (l->tk != '}') l->match(',', '}');
|
---|
1746 | }
|
---|
1747 | l->match('}');
|
---|
1748 | if(Objc.destructuring && Objc.structuring) {
|
---|
1749 | if(nestedObject) {
|
---|
1750 | if(l->tk!=',' && l->tk!='}' && l->tk!='=')
|
---|
1751 | Objc.destructuring = false;
|
---|
1752 | }
|
---|
1753 | else
|
---|
1754 | Objc.setMode(l->tk=='=' || (forFor && (l->tk==LEX_R_IN ||(l->tk==LEX_ID && l->tkStr=="of"))));
|
---|
1755 | } else {
|
---|
1756 | if(!Objc.destructuring && msg.size())
|
---|
1757 | throw new CScriptException(SyntaxError, msg, msgFile, msgLine, msgColumn);
|
---|
1758 | if(!nestedObject) Objc.setMode(Objc.destructuring);
|
---|
1759 | }
|
---|
1760 |
|
---|
1761 | if(Objc.destructuring)
|
---|
1762 | State.LeftHand = true;
|
---|
1763 | State.Tokens.push_back(ObjectToken);
|
---|
1764 | }
|
---|
1765 | void CScriptTokenizer::_tokenizeLiteralArray(ScriptTokenState &State, int Flags) {
|
---|
1766 | bool forFor = (Flags & TOKENIZE_FLAGS_noIn)!=0;
|
---|
1767 | bool nestedObject = (Flags & TOKENIZE_FLAGS_nestedObject) != 0;
|
---|
1768 | Flags &= ~(TOKENIZE_FLAGS_noIn | TOKENIZE_FLAGS_nestedObject);
|
---|
1769 | CScriptToken ObjectToken(LEX_T_OBJECT_LITERAL);
|
---|
1770 | CScriptTokenDataObjectLiteral &Objc = ObjectToken.Object();
|
---|
1771 |
|
---|
1772 | Objc.type = CScriptTokenDataObjectLiteral::ARRAY;
|
---|
1773 | Objc.destructuring = Objc.structuring = true;
|
---|
1774 | int idx = 0;
|
---|
1775 |
|
---|
1776 | l->match('[');
|
---|
1777 | while (l->tk != ']') {
|
---|
1778 | CScriptTokenDataObjectLiteral::ELEMENT element;
|
---|
1779 | element.id = int2string(idx++);
|
---|
1780 | if(l->tk != ',') {
|
---|
1781 | int dFlags = Flags | (l->tk == '{' || l->tk == '[') ? TOKENIZE_FLAGS_nestedObject : 0;
|
---|
1782 | State.pushLeftHandState();
|
---|
1783 | State.Tokens.swap(element.value);
|
---|
1784 | tokenizeAssignment(State, dFlags);
|
---|
1785 | State.Tokens.swap(element.value);
|
---|
1786 | if(Objc.destructuring) Objc.destructuring = State.LeftHand;
|
---|
1787 | State.popLeftHandeState();
|
---|
1788 | }
|
---|
1789 | Objc.elements.push_back(element);
|
---|
1790 | if (l->tk != ']') l->match(',', ']');
|
---|
1791 | }
|
---|
1792 | l->match(']');
|
---|
1793 | if(Objc.destructuring && Objc.structuring) {
|
---|
1794 | if(nestedObject) {
|
---|
1795 | if(l->tk!=',' && l->tk!=']' && l->tk!='=')
|
---|
1796 | Objc.destructuring = false;
|
---|
1797 | }
|
---|
1798 | else
|
---|
1799 | Objc.setMode(l->tk=='=' || (forFor && (l->tk==LEX_R_IN ||(l->tk==LEX_ID && l->tkStr=="of"))));
|
---|
1800 | } else
|
---|
1801 | if(!nestedObject) Objc.setMode(Objc.destructuring);
|
---|
1802 | if(Objc.destructuring)
|
---|
1803 | State.LeftHand = true;
|
---|
1804 | State.Tokens.push_back(ObjectToken);
|
---|
1805 | }
|
---|
1806 |
|
---|
1807 | void CScriptTokenizer::tokenizeLiteral(ScriptTokenState &State, int Flags) {
|
---|
1808 | State.LeftHand = 0;
|
---|
1809 | bool canLabel = Flags & TOKENIZE_FLAGS_canLabel; Flags &= ~TOKENIZE_FLAGS_canLabel;
|
---|
1810 | int ObjectLiteralFlags = Flags;
|
---|
1811 | Flags &= ~TOKENIZE_FLAGS_noIn;
|
---|
1812 | switch(l->tk) {
|
---|
1813 | case LEX_ID:
|
---|
1814 | {
|
---|
1815 | string label = l->tkStr;
|
---|
1816 | pushToken(State.Tokens);
|
---|
1817 | if(l->tk==':' && canLabel) {
|
---|
1818 | if(find(State.Labels.begin(), State.Labels.end(), label) != State.Labels.end())
|
---|
1819 | throw new CScriptException(SyntaxError, "dublicate label '"+label+"'", l->currentFile, l->currentLine(), l->currentColumn()-label.size());
|
---|
1820 | State.Tokens[State.Tokens.size()-1].token = LEX_T_LABEL; // change LEX_ID to LEX_T_LABEL
|
---|
1821 | State.Labels.push_back(label);
|
---|
1822 | } else if(label=="this") {
|
---|
1823 | if( l->tk == '=' || (l->tk >= LEX_ASSIGNMENTS_BEGIN && l->tk <= LEX_ASSIGNMENTS_END) )
|
---|
1824 | throw new CScriptException(SyntaxError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn()-label.size());
|
---|
1825 | if( l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS )
|
---|
1826 | throw new CScriptException(SyntaxError, l->tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn()-label.size());
|
---|
1827 | } else
|
---|
1828 | State.LeftHand = true;
|
---|
1829 | }
|
---|
1830 | break;
|
---|
1831 | case LEX_INT:
|
---|
1832 | case LEX_FLOAT:
|
---|
1833 | case LEX_STR:
|
---|
1834 | case LEX_REGEXP:
|
---|
1835 | case LEX_R_TRUE:
|
---|
1836 | case LEX_R_FALSE:
|
---|
1837 | case LEX_R_NULL:
|
---|
1838 | pushToken(State.Tokens);
|
---|
1839 | break;
|
---|
1840 | case '{':
|
---|
1841 | _tokenizeLiteralObject(State, ObjectLiteralFlags);
|
---|
1842 | break;
|
---|
1843 | case '[':
|
---|
1844 | _tokenizeLiteralArray(State, ObjectLiteralFlags);
|
---|
1845 | break;
|
---|
1846 | case LEX_R_LET: // let as expression
|
---|
1847 | tokenizeLet(State, Flags);
|
---|
1848 | break;
|
---|
1849 | case LEX_R_FUNCTION:
|
---|
1850 | tokenizeFunction(State, Flags);
|
---|
1851 | break;
|
---|
1852 | case LEX_R_NEW:
|
---|
1853 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
1854 | {
|
---|
1855 | tokenizeFunctionCall(State, (Flags | TOKENIZE_FLAGS_callForNew) & ~TOKENIZE_FLAGS_noIn);
|
---|
1856 | State.LeftHand = 0;
|
---|
1857 | }
|
---|
1858 | setTokenSkip(State);
|
---|
1859 | break;
|
---|
1860 | case '(':
|
---|
1861 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
1862 | tokenizeExpression(State, Flags & ~TOKENIZE_FLAGS_noIn);
|
---|
1863 | State.LeftHand = 0;
|
---|
1864 | pushToken(State.Tokens, ')');
|
---|
1865 | setTokenSkip(State);
|
---|
1866 | break;
|
---|
1867 | default:
|
---|
1868 | l->check(LEX_EOF);
|
---|
1869 | }
|
---|
1870 | }
|
---|
1871 | void CScriptTokenizer::tokenizeMember(ScriptTokenState &State, int Flags) {
|
---|
1872 | while(l->tk == '.' || l->tk == '[') {
|
---|
1873 | if(l->tk == '.') {
|
---|
1874 | pushToken(State.Tokens);
|
---|
1875 | if(CScriptToken::isReservedWord(l->tk))
|
---|
1876 | l->tk = LEX_ID; // fake reserved-word as member.ID
|
---|
1877 | pushToken(State.Tokens , LEX_ID);
|
---|
1878 | } else {
|
---|
1879 | State.Marks.push_back(pushToken(State.Tokens));
|
---|
1880 | State.pushLeftHandState();
|
---|
1881 | tokenizeExpression(State, Flags & ~TOKENIZE_FLAGS_noIn);
|
---|
1882 | State.popLeftHandeState();
|
---|
1883 | pushToken(State.Tokens, ']');
|
---|
1884 | setTokenSkip(State);
|
---|
1885 | }
|
---|
1886 | State.LeftHand = true;
|
---|
1887 | }
|
---|
1888 | }
|
---|
1889 | void CScriptTokenizer::tokenizeFunctionCall(ScriptTokenState &State, int Flags) {
|
---|
1890 | bool for_new = (Flags & TOKENIZE_FLAGS_callForNew)!=0; Flags &= ~TOKENIZE_FLAGS_callForNew;
|
---|
1891 | tokenizeLiteral(State, Flags);
|
---|
1892 | tokenizeMember(State, Flags);
|
---|
1893 | while(l->tk == '(') {
|
---|
1894 | State.LeftHand = false;
|
---|
1895 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
1896 | State.pushLeftHandState();
|
---|
1897 | while(l->tk!=')') {
|
---|
1898 | tokenizeAssignment(State, Flags & ~TOKENIZE_FLAGS_noIn);
|
---|
1899 | if (l->tk!=')') pushToken(State.Tokens, ',', ')');
|
---|
1900 | }
|
---|
1901 | State.popLeftHandeState();
|
---|
1902 | pushToken(State.Tokens);
|
---|
1903 | setTokenSkip(State);
|
---|
1904 | if(for_new) break;
|
---|
1905 | tokenizeMember(State, Flags);
|
---|
1906 | }
|
---|
1907 | }
|
---|
1908 |
|
---|
1909 | void CScriptTokenizer::tokenizeSubExpression(ScriptTokenState &State, int Flags) {
|
---|
1910 | static int Left2Right_begin[] = {
|
---|
1911 | /* Precedence 5 */ '*', '/', '%',
|
---|
1912 | /* Precedence 6 */ '+', '-',
|
---|
1913 | /* Precedence 7 */ LEX_LSHIFT, LEX_RSHIFT, LEX_RSHIFTU,
|
---|
1914 | /* Precedence 8 */ LEX_EQUAL, LEX_NEQUAL, LEX_TYPEEQUAL, LEX_NTYPEEQUAL,
|
---|
1915 | /* Precedence 9 */ '<', LEX_LEQUAL, '>', LEX_GEQUAL, LEX_R_IN, LEX_R_INSTANCEOF,
|
---|
1916 | /* Precedence 10-12 */ '&', '^', '|',
|
---|
1917 | };
|
---|
1918 | static int *Left2Right_end = &Left2Right_begin[sizeof(Left2Right_begin)/sizeof(Left2Right_begin[0])];
|
---|
1919 | static bool Left2Right_sorted = false;
|
---|
1920 | if(!Left2Right_sorted) Left2Right_sorted = (sort(Left2Right_begin, Left2Right_end), true);
|
---|
1921 | bool noLeftHand = false;
|
---|
1922 | for(;;) {
|
---|
1923 | bool right2left_end = false;
|
---|
1924 | while(!right2left_end) {
|
---|
1925 | switch(l->tk) {
|
---|
1926 | case '-':
|
---|
1927 | case '+':
|
---|
1928 | case '!':
|
---|
1929 | case '~':
|
---|
1930 | case LEX_R_TYPEOF:
|
---|
1931 | case LEX_R_VOID:
|
---|
1932 | case LEX_R_DELETE:
|
---|
1933 | Flags &= ~TOKENIZE_FLAGS_canLabel;
|
---|
1934 | noLeftHand = true;
|
---|
1935 | pushToken(State.Tokens); // Precedence 3
|
---|
1936 | break;
|
---|
1937 | case LEX_PLUSPLUS: // pre-increment
|
---|
1938 | case LEX_MINUSMINUS: // pre-decrement
|
---|
1939 | {
|
---|
1940 | int tk = l->tk;
|
---|
1941 | Flags &= ~TOKENIZE_FLAGS_canLabel;
|
---|
1942 | noLeftHand = true;
|
---|
1943 | pushToken(State.Tokens); // Precedence 4
|
---|
1944 | if(l->tk == LEX_ID && l->tkStr == "this")
|
---|
1945 | throw new CScriptException(SyntaxError, tk==LEX_PLUSPLUS?"invalid increment operand":"invalid decrement operand", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
1946 | }
|
---|
1947 | default:
|
---|
1948 | right2left_end = true;
|
---|
1949 | }
|
---|
1950 | }
|
---|
1951 | tokenizeFunctionCall(State, Flags);
|
---|
1952 |
|
---|
1953 | if (!l->lineBreakBeforeToken && (l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS)) { // post-in-/de-crement
|
---|
1954 | noLeftHand = true;;
|
---|
1955 | pushToken(State.Tokens); // Precedence 4
|
---|
1956 | }
|
---|
1957 | if(Flags&TOKENIZE_FLAGS_noIn && l->tk==LEX_R_IN)
|
---|
1958 | break;
|
---|
1959 | int *found = lower_bound(Left2Right_begin, Left2Right_end, l->tk);
|
---|
1960 | if(found != Left2Right_end && *found == l->tk) {
|
---|
1961 | noLeftHand = true;
|
---|
1962 | pushToken(State.Tokens); // Precedence 5-14
|
---|
1963 | }
|
---|
1964 | else
|
---|
1965 | break;
|
---|
1966 | }
|
---|
1967 | if(noLeftHand) State.LeftHand = false;
|
---|
1968 | }
|
---|
1969 |
|
---|
1970 | void CScriptTokenizer::tokenizeLogic(ScriptTokenState &State, int Flags, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) {
|
---|
1971 | op_n ? tokenizeLogic(State, Flags, op_n, 0) : tokenizeSubExpression(State, Flags);
|
---|
1972 | if(l->tk==op) {
|
---|
1973 | unsigned int marks_count = State.Marks.size();
|
---|
1974 | while(l->tk==op) {
|
---|
1975 | State.Marks.push_back(pushToken(State.Tokens));
|
---|
1976 | op_n ? tokenizeLogic(State, Flags, op_n, 0) : tokenizeSubExpression(State, Flags);
|
---|
1977 | }
|
---|
1978 | while(State.Marks.size()>marks_count) setTokenSkip(State);
|
---|
1979 | State.LeftHand = false;
|
---|
1980 | }
|
---|
1981 | }
|
---|
1982 |
|
---|
1983 | void CScriptTokenizer::tokenizeCondition(ScriptTokenState &State, int Flags) {
|
---|
1984 | tokenizeLogic(State, Flags);
|
---|
1985 | if(l->tk == '?') {
|
---|
1986 | Flags &= ~TOKENIZE_FLAGS_noIn;
|
---|
1987 | State.Marks.push_back(pushToken(State.Tokens));
|
---|
1988 | tokenizeCondition(State, Flags);
|
---|
1989 | setTokenSkip(State);
|
---|
1990 | State.Marks.push_back(pushToken(State.Tokens, ':'));
|
---|
1991 | tokenizeCondition(State, Flags);
|
---|
1992 | setTokenSkip(State);
|
---|
1993 | State.LeftHand = false;
|
---|
1994 | }
|
---|
1995 | }
|
---|
1996 | void CScriptTokenizer::tokenizeAssignment(ScriptTokenState &State, int Flags) {
|
---|
1997 | tokenizeCondition(State, Flags);
|
---|
1998 | if (l->tk=='=' || (l->tk>=LEX_ASSIGNMENTS_BEGIN && l->tk<=LEX_ASSIGNMENTS_END) ) {
|
---|
1999 | if(!State.LeftHand)
|
---|
2000 | throw new CScriptException(ReferenceError, "invalid assignment left-hand side", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
2001 | pushToken(State.Tokens);
|
---|
2002 | tokenizeAssignment(State, Flags);
|
---|
2003 | State.LeftHand = false;
|
---|
2004 | }
|
---|
2005 | }
|
---|
2006 | void CScriptTokenizer::tokenizeExpression(ScriptTokenState &State, int Flags) {
|
---|
2007 | tokenizeAssignment(State, Flags);
|
---|
2008 | while(l->tk == ',') {
|
---|
2009 | pushToken(State.Tokens);
|
---|
2010 | tokenizeAssignment(State, Flags);
|
---|
2011 | State.LeftHand = false;
|
---|
2012 | }
|
---|
2013 | }
|
---|
2014 | void CScriptTokenizer::tokenizeBlock(ScriptTokenState &State, int Flags) {
|
---|
2015 | bool addBlockStart = (Flags&TOKENIZE_FLAGS_noBlockStart)==0;
|
---|
2016 | Flags&=~(TOKENIZE_FLAGS_noBlockStart);
|
---|
2017 | State.Marks.push_back(pushToken(State.Tokens, '{')); // push Token & push BeginIdx
|
---|
2018 | if(addBlockStart) pushForwarder(State);
|
---|
2019 |
|
---|
2020 | while(l->tk != '}' && l->tk != LEX_EOF)
|
---|
2021 | tokenizeStatement(State, Flags);
|
---|
2022 | pushToken(State.Tokens, '}');
|
---|
2023 |
|
---|
2024 | if(addBlockStart) removeEmptyForwarder(State); // clean-up BlockStarts
|
---|
2025 |
|
---|
2026 | setTokenSkip(State);
|
---|
2027 | }
|
---|
2028 |
|
---|
2029 | void CScriptTokenizer::tokenizeStatementNoLet(ScriptTokenState &State, int Flags) {
|
---|
2030 | if(l->tk == LEX_R_LET)
|
---|
2031 | tokenizeLet(State, Flags | TOKENIZE_FLAGS_asStatement, true);
|
---|
2032 | else if(l->tk==LEX_R_FUNCTION)
|
---|
2033 | tokenizeFunction(State, Flags | TOKENIZE_FLAGS_asStatement, true);
|
---|
2034 | else
|
---|
2035 | tokenizeStatement(State, Flags);
|
---|
2036 | }
|
---|
2037 | void CScriptTokenizer::tokenizeStatement(ScriptTokenState &State, int Flags) {
|
---|
2038 | switch(l->tk)
|
---|
2039 | {
|
---|
2040 | case '{': tokenizeBlock(State, Flags); break;
|
---|
2041 | case ';': pushToken(State.Tokens); break;
|
---|
2042 | case LEX_R_CONST:
|
---|
2043 | case LEX_R_VAR: tokenizeVarAndConst(State, Flags); break;
|
---|
2044 | case LEX_R_LET: tokenizeLet(State, Flags | TOKENIZE_FLAGS_asStatement); break;
|
---|
2045 | case LEX_R_WITH: tokenizeWith(State, Flags); break;
|
---|
2046 | case LEX_R_IF: tokenizeIf(State, Flags); break;
|
---|
2047 | case LEX_R_SWITCH: tokenizeSwitch(State, Flags); break;
|
---|
2048 | case LEX_R_DO:
|
---|
2049 | case LEX_R_WHILE: tokenizeWhileAndDo(State, Flags); break;
|
---|
2050 | case LEX_R_FOR: tokenizeFor(State, Flags); break;
|
---|
2051 | case LEX_R_FUNCTION: tokenizeFunction(State, Flags | TOKENIZE_FLAGS_asStatement); break;
|
---|
2052 | case LEX_R_TRY: tokenizeTry(State, Flags); break;
|
---|
2053 | case LEX_R_RETURN:
|
---|
2054 | if( (Flags & TOKENIZE_FLAGS_canReturn)==0)
|
---|
2055 | throw new CScriptException(SyntaxError, "'return' statement, but not in a function.", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
2056 | case LEX_R_THROW:
|
---|
2057 | State.Marks.push_back(pushToken(State.Tokens)); // push Token & push BeginIdx
|
---|
2058 | if(l->tk != ';' && !l->lineBreakBeforeToken) {
|
---|
2059 | tokenizeExpression(State, Flags);
|
---|
2060 | }
|
---|
2061 | pushToken(State.Tokens, ';'); // push ';'
|
---|
2062 | setTokenSkip(State);
|
---|
2063 | break;
|
---|
2064 | case LEX_R_BREAK:
|
---|
2065 | case LEX_R_CONTINUE:
|
---|
2066 | {
|
---|
2067 | bool isBreak = l->tk == LEX_R_BREAK;
|
---|
2068 | State.Marks.push_back(pushToken(State.Tokens)); // push Token
|
---|
2069 |
|
---|
2070 | if(l->tk != ';' && !l->lineBreakBeforeToken) {
|
---|
2071 | l->check(LEX_ID);
|
---|
2072 | STRING_VECTOR_t &L = isBreak ? State.Labels : State.LoopLabels;
|
---|
2073 | if(find(L.begin(), L.end(), l->tkStr) == L.end())
|
---|
2074 | throw new CScriptException(SyntaxError, "label '"+l->tkStr+"' not found", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
2075 | pushToken(State.Tokens); // push 'Label'
|
---|
2076 | } else if((Flags & (isBreak ? TOKENIZE_FLAGS_canBreak : TOKENIZE_FLAGS_canContinue) )==0)
|
---|
2077 | throw new CScriptException(SyntaxError,
|
---|
2078 | isBreak ? "'break' must be inside loop, switch or labeled statement" : "'continue' must be inside loop",
|
---|
2079 | l->currentFile, l->currentLine(), l->currentColumn());
|
---|
2080 | pushToken(State.Tokens, ';'); // push ';'
|
---|
2081 | setTokenSkip(State);
|
---|
2082 | }
|
---|
2083 | break;
|
---|
2084 | case LEX_ID:
|
---|
2085 | {
|
---|
2086 | State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx
|
---|
2087 | STRING_VECTOR_t::size_type label_count = State.Labels.size();
|
---|
2088 | tokenizeExpression(State, Flags | TOKENIZE_FLAGS_canLabel);
|
---|
2089 | if(label_count < State.Labels.size() && l->tk == ':') {
|
---|
2090 | State.Tokens.erase(State.Tokens.begin()+State.Marks.back()); // remove skip
|
---|
2091 | State.Marks.pop_back();
|
---|
2092 | pushToken(State.Tokens); // push ':'
|
---|
2093 | tokenizeStatement(State, Flags);
|
---|
2094 | State.Labels.pop_back();
|
---|
2095 | } else {
|
---|
2096 | pushToken(State.Tokens, ';');
|
---|
2097 | setTokenSkip(State);
|
---|
2098 | }
|
---|
2099 | }
|
---|
2100 | break;
|
---|
2101 | default:
|
---|
2102 | State.Marks.push_back(pushToken(State.Tokens, CScriptToken(LEX_T_SKIP))); // push skip & skiperBeginIdx
|
---|
2103 | tokenizeExpression(State, Flags);
|
---|
2104 | pushToken(State.Tokens, ';');
|
---|
2105 | setTokenSkip(State);
|
---|
2106 | break;
|
---|
2107 | }
|
---|
2108 | }
|
---|
2109 |
|
---|
2110 | int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, int Match, int Alternate) {
|
---|
2111 | if(Match == ';' && l->tk != ';' && (l->lineBreakBeforeToken || l->tk=='}' || l->tk==LEX_EOF))
|
---|
2112 | Tokens.push_back(CScriptToken(';')); // inject ';'
|
---|
2113 | else
|
---|
2114 | Tokens.push_back(CScriptToken(l, Match, Alternate));
|
---|
2115 | return Tokens.size()-1;
|
---|
2116 | }
|
---|
2117 | int CScriptTokenizer::pushToken(TOKEN_VECT &Tokens, const CScriptToken &Token) {
|
---|
2118 | int ret = Tokens.size();
|
---|
2119 | Tokens.push_back(Token);
|
---|
2120 | return ret;
|
---|
2121 | }
|
---|
2122 | void CScriptTokenizer::pushForwarder(TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, vector<int> &Marks) {
|
---|
2123 | Marks.push_back(Tokens.size());
|
---|
2124 | CScriptToken token(LEX_T_FORWARD);
|
---|
2125 | Tokens.push_back(token);
|
---|
2126 | Forwarders.push_back(token.Forwarder());
|
---|
2127 | }
|
---|
2128 | void CScriptTokenizer::pushForwarder(ScriptTokenState &State, bool noMarks/*=false*/) {
|
---|
2129 | if(!noMarks) State.Marks.push_back(State.Tokens.size());
|
---|
2130 | CScriptToken token(LEX_T_FORWARD);
|
---|
2131 | State.Tokens.push_back(token);
|
---|
2132 | State.Forwarders.push_back(token.Forwarder());
|
---|
2133 | }
|
---|
2134 | void CScriptTokenizer::removeEmptyForwarder(ScriptTokenState &State)
|
---|
2135 | {
|
---|
2136 | CScriptTokenDataForwardsPtr &forwarder = State.Forwarders.back();
|
---|
2137 | forwarder->vars_in_letscope.clear();
|
---|
2138 | if(forwarder->empty())
|
---|
2139 | State.Tokens.erase(State.Tokens.begin()+State.Marks.back());
|
---|
2140 | State.Forwarders.pop_back();
|
---|
2141 | State.Marks.pop_back();
|
---|
2142 | }
|
---|
2143 |
|
---|
2144 | void CScriptTokenizer::removeEmptyForwarder( TOKEN_VECT &Tokens, FORWARDER_VECTOR_t &Forwarders, vector<int> &Marks )
|
---|
2145 | {
|
---|
2146 | CScriptTokenDataForwardsPtr &forwarder = Forwarders.back();
|
---|
2147 | forwarder->vars_in_letscope.clear();
|
---|
2148 | if(forwarder->empty())
|
---|
2149 | Tokens.erase(Tokens.begin()+Marks.back());
|
---|
2150 | Forwarders.pop_back();
|
---|
2151 | Marks.pop_back();
|
---|
2152 | }
|
---|
2153 | void CScriptTokenizer::throwTokenNotExpected() {
|
---|
2154 | throw new CScriptException(SyntaxError, "'"+CScriptToken::getTokenStr(l->tk)+"' was not expected", l->currentFile, l->currentLine(), l->currentColumn());
|
---|
2155 | }
|
---|
2156 |
|
---|
2157 |
|
---|
2158 | //////////////////////////////////////////////////////////////////////////
|
---|
2159 | /// CScriptVar
|
---|
2160 | //////////////////////////////////////////////////////////////////////////
|
---|
2161 |
|
---|
2162 | CScriptVar::CScriptVar(CTinyJS *Context, const CScriptVarPtr &Prototype) {
|
---|
2163 | extensible = true;
|
---|
2164 | context = Context;
|
---|
2165 | temporaryID = 0;
|
---|
2166 | if(context->first) {
|
---|
2167 | next = context->first;
|
---|
2168 | next->prev = this;
|
---|
2169 | } else {
|
---|
2170 | next = 0;
|
---|
2171 | }
|
---|
2172 | context->first = this;
|
---|
2173 | prev = 0;
|
---|
2174 | refs = 0;
|
---|
2175 | if(Prototype)
|
---|
2176 | addChild(TINYJS___PROTO___VAR, Prototype, SCRIPTVARLINK_WRITABLE);
|
---|
2177 | #if DEBUG_MEMORY
|
---|
2178 | mark_allocated(this);
|
---|
2179 | #endif
|
---|
2180 | }
|
---|
2181 | CScriptVar::CScriptVar(const CScriptVar &Copy) {
|
---|
2182 | extensible = Copy.extensible;
|
---|
2183 | context = Copy.context;
|
---|
2184 | temporaryID = 0;
|
---|
2185 | if(context->first) {
|
---|
2186 | next = context->first;
|
---|
2187 | next->prev = this;
|
---|
2188 | } else {
|
---|
2189 | next = 0;
|
---|
2190 | }
|
---|
2191 | context->first = this;
|
---|
2192 | prev = 0;
|
---|
2193 | refs = 0;
|
---|
2194 | SCRIPTVAR_CHILDS_cit it;
|
---|
2195 | for(it = Copy.Childs.begin(); it!= Copy.Childs.end(); ++it) {
|
---|
2196 | addChild((*it)->getName(), (*it)->getVarPtr(), (*it)->getFlags());
|
---|
2197 | }
|
---|
2198 |
|
---|
2199 | #if DEBUG_MEMORY
|
---|
2200 | mark_allocated(this);
|
---|
2201 | #endif
|
---|
2202 | }
|
---|
2203 | CScriptVar::~CScriptVar(void) {
|
---|
2204 | #if DEBUG_MEMORY
|
---|
2205 | mark_deallocated(this);
|
---|
2206 | #endif
|
---|
2207 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
|
---|
2208 | (*it)->setOwner(0);
|
---|
2209 | removeAllChildren();
|
---|
2210 | if(prev)
|
---|
2211 | prev->next = next;
|
---|
2212 | else
|
---|
2213 | context->first = next;
|
---|
2214 | if(next)
|
---|
2215 | next->prev = prev;
|
---|
2216 | }
|
---|
2217 |
|
---|
2218 | /// Type
|
---|
2219 |
|
---|
2220 | bool CScriptVar::isObject() {return false;}
|
---|
2221 | bool CScriptVar::isError() {return false;}
|
---|
2222 | bool CScriptVar::isArray() {return false;}
|
---|
2223 | bool CScriptVar::isRegExp() {return false;}
|
---|
2224 | bool CScriptVar::isAccessor() {return false;}
|
---|
2225 | bool CScriptVar::isNull() {return false;}
|
---|
2226 | bool CScriptVar::isUndefined() {return false;}
|
---|
2227 | bool CScriptVar::isNaN() {return false;}
|
---|
2228 | bool CScriptVar::isString() {return false;}
|
---|
2229 | bool CScriptVar::isInt() {return false;}
|
---|
2230 | bool CScriptVar::isBool() {return false;}
|
---|
2231 | int CScriptVar::isInfinity() { return 0; } ///< +1==POSITIVE_INFINITY, -1==NEGATIVE_INFINITY, 0==is not an InfinityVar
|
---|
2232 | bool CScriptVar::isDouble() {return false;}
|
---|
2233 | bool CScriptVar::isRealNumber() {return false;}
|
---|
2234 | bool CScriptVar::isNumber() {return false;}
|
---|
2235 | bool CScriptVar::isPrimitive() {return false;}
|
---|
2236 | bool CScriptVar::isFunction() {return false;}
|
---|
2237 | bool CScriptVar::isNative() {return false;}
|
---|
2238 | bool CScriptVar::isBounded() {return false;}
|
---|
2239 | bool CScriptVar::isIterator() {return false;}
|
---|
2240 |
|
---|
2241 | //////////////////////////////////////////////////////////////////////////
|
---|
2242 | /// Value
|
---|
2243 | //////////////////////////////////////////////////////////////////////////
|
---|
2244 |
|
---|
2245 | CScriptVarPrimitivePtr CScriptVar::getRawPrimitive() {
|
---|
2246 | return CScriptVarPrimitivePtr(); // default NULL-Ptr
|
---|
2247 | }
|
---|
2248 | CScriptVarPrimitivePtr CScriptVar::toPrimitive() {
|
---|
2249 | return toPrimitive_hintNumber();
|
---|
2250 | }
|
---|
2251 |
|
---|
2252 | CScriptVarPrimitivePtr CScriptVar::toPrimitive(CScriptResult &execute) {
|
---|
2253 | return toPrimitive_hintNumber(execute);
|
---|
2254 | }
|
---|
2255 |
|
---|
2256 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(int32_t radix) {
|
---|
2257 | CScriptResult execute;
|
---|
2258 | CScriptVarPrimitivePtr var = toPrimitive_hintString(execute, radix);
|
---|
2259 | execute.cThrow();
|
---|
2260 | return var;
|
---|
2261 | }
|
---|
2262 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintString(CScriptResult &execute, int32_t radix) {
|
---|
2263 | if(execute) {
|
---|
2264 | if(!isPrimitive()) {
|
---|
2265 | CScriptVarPtr ret = callJS_toString(execute, radix);
|
---|
2266 | if(execute && !ret->isPrimitive()) {
|
---|
2267 | ret = callJS_valueOf(execute);
|
---|
2268 | if(execute && !ret->isPrimitive())
|
---|
2269 | context->throwError(execute, TypeError, "can't convert b to primitive type");
|
---|
2270 | }
|
---|
2271 | return ret;
|
---|
2272 | }
|
---|
2273 | return this;
|
---|
2274 | }
|
---|
2275 | return constScriptVar(Undefined);
|
---|
2276 | }
|
---|
2277 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber() {
|
---|
2278 | CScriptResult execute;
|
---|
2279 | CScriptVarPrimitivePtr var = toPrimitive_hintNumber(execute);
|
---|
2280 | execute.cThrow();
|
---|
2281 | return var;
|
---|
2282 | }
|
---|
2283 | CScriptVarPrimitivePtr CScriptVar::toPrimitive_hintNumber(CScriptResult &execute) {
|
---|
2284 | if(execute) {
|
---|
2285 | if(!isPrimitive()) {
|
---|
2286 | CScriptVarPtr ret = callJS_valueOf(execute);
|
---|
2287 | if(execute && !ret->isPrimitive()) {
|
---|
2288 | ret = callJS_toString(execute);
|
---|
2289 | if(execute && !ret->isPrimitive())
|
---|
2290 | context->throwError(execute, TypeError, "can't convert to primitive type");
|
---|
2291 | }
|
---|
2292 | return ret;
|
---|
2293 | }
|
---|
2294 | return this;
|
---|
2295 | }
|
---|
2296 | return constScriptVar(Undefined);
|
---|
2297 | }
|
---|
2298 |
|
---|
2299 | CScriptVarPtr CScriptVar::callJS_valueOf(CScriptResult &execute) {
|
---|
2300 | if(execute) {
|
---|
2301 | CScriptVarPtr FncValueOf = findChildWithPrototypeChain("valueOf").getter(execute);
|
---|
2302 | if(FncValueOf != context->objectPrototype_valueOf) { // custom valueOf in JavaScript
|
---|
2303 | if(FncValueOf->isFunction()) { // no Error if toString not callable
|
---|
2304 | vector<CScriptVarPtr> Params;
|
---|
2305 | return context->callFunction(execute, FncValueOf, Params, this);
|
---|
2306 | }
|
---|
2307 | } else
|
---|
2308 | return valueOf_CallBack();
|
---|
2309 | }
|
---|
2310 | return this;
|
---|
2311 | }
|
---|
2312 | CScriptVarPtr CScriptVar::valueOf_CallBack() {
|
---|
2313 | return this;
|
---|
2314 | }
|
---|
2315 |
|
---|
2316 | CScriptVarPtr CScriptVar::callJS_toString(CScriptResult &execute, int radix/*=0*/) {
|
---|
2317 | if(execute) {
|
---|
2318 | CScriptVarPtr FncToString = findChildWithPrototypeChain("toString").getter(execute);
|
---|
2319 | if(FncToString != context->objectPrototype_toString) { // custom valueOf in JavaScript
|
---|
2320 | if(FncToString->isFunction()) { // no Error if toString not callable
|
---|
2321 | vector<CScriptVarPtr> Params;
|
---|
2322 | Params.push_back(newScriptVar(radix));
|
---|
2323 | return context->callFunction(execute, FncToString, Params, this);
|
---|
2324 | }
|
---|
2325 | } else
|
---|
2326 | return toString_CallBack(execute, radix);
|
---|
2327 | }
|
---|
2328 | return this;
|
---|
2329 | }
|
---|
2330 | CScriptVarPtr CScriptVar::toString_CallBack(CScriptResult &execute, int radix/*=0*/) {
|
---|
2331 | return this;
|
---|
2332 | }
|
---|
2333 |
|
---|
2334 | CNumber CScriptVar::toNumber() { return toPrimitive_hintNumber()->toNumber_Callback(); };
|
---|
2335 | CNumber CScriptVar::toNumber(CScriptResult &execute) { return toPrimitive_hintNumber(execute)->toNumber_Callback(); };
|
---|
2336 | bool CScriptVar::toBoolean() { return true; }
|
---|
2337 | string CScriptVar::toString(int32_t radix) { return toPrimitive_hintString(radix)->toCString(radix); }
|
---|
2338 | string CScriptVar::toString(CScriptResult &execute, int32_t radix) { return toPrimitive_hintString(execute, radix)->toCString(radix); }
|
---|
2339 |
|
---|
2340 | int CScriptVar::getInt() { return toNumber().toInt32(); }
|
---|
2341 | double CScriptVar::getDouble() { return toNumber().toDouble(); }
|
---|
2342 | bool CScriptVar::getBool() { return toBoolean(); }
|
---|
2343 | string CScriptVar::getString() { return toPrimitive_hintString()->toCString(); }
|
---|
2344 |
|
---|
2345 | CScriptTokenDataFnc *CScriptVar::getFunctionData() { return 0; }
|
---|
2346 |
|
---|
2347 | CScriptVarPtr CScriptVar::toIterator(int Mode/*=3*/) {
|
---|
2348 | CScriptResult execute;
|
---|
2349 | CScriptVarPtr var = toIterator(execute, Mode);
|
---|
2350 | execute.cThrow();
|
---|
2351 | return var;
|
---|
2352 | }
|
---|
2353 | CScriptVarPtr CScriptVar::toIterator(CScriptResult &execute, int Mode/*=3*/) {
|
---|
2354 | if(!execute) return constScriptVar(Undefined);
|
---|
2355 | if(isIterator()) return this;
|
---|
2356 | CScriptVarFunctionPtr Generator(findChildWithPrototypeChain("__iterator__").getter(execute));
|
---|
2357 | vector<CScriptVarPtr> args;
|
---|
2358 | if(Generator) return context->callFunction(execute, Generator, args, this);
|
---|
2359 | return newScriptVarDefaultIterator(context, this, Mode);
|
---|
2360 | }
|
---|
2361 |
|
---|
2362 | string CScriptVar::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
2363 | getParsableStringRecursionsCheck();
|
---|
2364 | return toString();
|
---|
2365 | }
|
---|
2366 |
|
---|
2367 | CScriptVarPtr CScriptVar::getNumericVar() { return newScriptVar(toNumber()); }
|
---|
2368 |
|
---|
2369 | ////// Flags
|
---|
2370 |
|
---|
2371 | void CScriptVar::seal() {
|
---|
2372 | preventExtensions();
|
---|
2373 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
|
---|
2374 | (*it)->setConfigurable(false);
|
---|
2375 | }
|
---|
2376 | bool CScriptVar::isSealed() const {
|
---|
2377 | if(isExtensible()) return false;
|
---|
2378 | for(SCRIPTVAR_CHILDS_cit it = Childs.begin(); it != Childs.end(); ++it)
|
---|
2379 | if((*it)->isConfigurable()) return false;
|
---|
2380 | return true;
|
---|
2381 | }
|
---|
2382 | void CScriptVar::freeze() {
|
---|
2383 | preventExtensions();
|
---|
2384 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it)
|
---|
2385 | (*it)->setConfigurable(false), (*it)->setWritable(false);
|
---|
2386 | }
|
---|
2387 | bool CScriptVar::isFrozen() const {
|
---|
2388 | if(isExtensible()) return false;
|
---|
2389 | for(SCRIPTVAR_CHILDS_cit it = Childs.begin(); it != Childs.end(); ++it)
|
---|
2390 | if((*it)->isConfigurable() || (*it)->isWritable()) return false;
|
---|
2391 | return true;
|
---|
2392 | }
|
---|
2393 |
|
---|
2394 | ////// Childs
|
---|
2395 |
|
---|
2396 | CScriptVarPtr CScriptVar::getOwnPropertyDescriptor(const string &Name) {
|
---|
2397 | CScriptVarLinkPtr child = findChild(Name);
|
---|
2398 | if(!child) {
|
---|
2399 | CScriptVarStringPtr strVar = getRawPrimitive();
|
---|
2400 | uint32_t Idx;
|
---|
2401 | if (strVar && (Idx=isArrayIndex(Name))!=uint32_t(-1) && Idx<strVar->stringLength()) {
|
---|
2402 | int Char = strVar->getChar(Idx);
|
---|
2403 | CScriptVarPtr ret = newScriptVar(Object);
|
---|
2404 | ret->addChild("value", newScriptVar(string(1, (char)Char)));
|
---|
2405 | ret->addChild("writable", constScriptVar(false));
|
---|
2406 | ret->addChild("enumerable", constScriptVar(true));
|
---|
2407 | ret->addChild("configurable", constScriptVar(false));
|
---|
2408 | return ret;
|
---|
2409 | }
|
---|
2410 | }
|
---|
2411 |
|
---|
2412 | if(!child || child->getVarPtr()->isUndefined()) return constScriptVar(Undefined);
|
---|
2413 | CScriptVarPtr ret = newScriptVar(Object);
|
---|
2414 | if(child->getVarPtr()->isAccessor()) {
|
---|
2415 | CScriptVarLinkPtr value = child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR);
|
---|
2416 | ret->addChild("get", value ? value->getVarPtr() : constScriptVar(Undefined));
|
---|
2417 | value = child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR);
|
---|
2418 | ret->addChild("set", value ? value->getVarPtr() : constScriptVar(Undefined));
|
---|
2419 | } else {
|
---|
2420 | ret->addChild("value", child->getVarPtr()->valueOf_CallBack());
|
---|
2421 | ret->addChild("writable", constScriptVar(child->isWritable()));
|
---|
2422 | }
|
---|
2423 | ret->addChild("enumerable", constScriptVar(child->isEnumerable()));
|
---|
2424 | ret->addChild("configurable", constScriptVar(child->isConfigurable()));
|
---|
2425 | return ret;
|
---|
2426 | }
|
---|
2427 | const char *CScriptVar::defineProperty(const string &Name, CScriptVarPtr Attributes) {
|
---|
2428 | CScriptVarPtr attr;
|
---|
2429 | CScriptVarLinkPtr child = findChildWithStringChars(Name);
|
---|
2430 |
|
---|
2431 | CScriptVarPtr attr_value = Attributes->findChild("value");
|
---|
2432 | CScriptVarPtr attr_writable = Attributes->findChild("writable");
|
---|
2433 | CScriptVarPtr attr_get = Attributes->findChild("get");
|
---|
2434 | CScriptVarPtr attr_set = Attributes->findChild("set");
|
---|
2435 | CScriptVarPtr attr_enumerable = Attributes->findChild("enumerable");
|
---|
2436 | CScriptVarPtr attr_configurable = Attributes->findChild("configurable");
|
---|
2437 | bool attr_isDataDescriptor = !attr_get && !attr_set;
|
---|
2438 | if(!attr_isDataDescriptor && (attr_value || attr_writable)) return "property descriptors must not specify a value or be writable when a getter or setter has been specified";
|
---|
2439 | if(attr_isDataDescriptor) {
|
---|
2440 | if(attr_get && (!attr_get->isUndefined() || !attr_get->isFunction())) return "property descriptor's getter field is neither undefined nor a function";
|
---|
2441 | if(attr_set && (!attr_set->isUndefined() || !attr_set->isFunction())) return "property descriptor's setter field is neither undefined nor a function";
|
---|
2442 | }
|
---|
2443 | if(!child) {
|
---|
2444 | if(!isExtensible()) return "is not extensible";
|
---|
2445 | if(attr_isDataDescriptor) {
|
---|
2446 | child = addChild(Name, attr_value?attr_value:constScriptVar(Undefined), 0);
|
---|
2447 | if(attr_writable) child->setWritable(attr_writable->toBoolean());
|
---|
2448 | } else {
|
---|
2449 | child = addChild(Name, newScriptVarAccessor(context, attr_get, attr_set), SCRIPTVARLINK_WRITABLE);
|
---|
2450 | }
|
---|
2451 | } else {
|
---|
2452 | if(!child->isConfigurable()) {
|
---|
2453 | if(attr_configurable && attr_configurable->toBoolean()) goto cant_redefine;
|
---|
2454 | if(attr_enumerable && attr_enumerable->toBoolean() != child->isEnumerable()) goto cant_redefine;
|
---|
2455 | if(child->getVarPtr()->isAccessor()) {
|
---|
2456 | if(attr_isDataDescriptor) goto cant_redefine;
|
---|
2457 | if(attr_get && attr_get != child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR)) goto cant_redefine;
|
---|
2458 | if(attr_set && attr_set != child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR)) goto cant_redefine;
|
---|
2459 | } else if(!attr_isDataDescriptor) goto cant_redefine;
|
---|
2460 | else if(!child->isWritable()) {
|
---|
2461 | if(attr_writable && attr_writable->toBoolean()) goto cant_redefine;
|
---|
2462 | if(attr_value && !attr_value->mathsOp(child, LEX_EQUAL)->toBoolean()) goto cant_redefine;
|
---|
2463 | }
|
---|
2464 | }
|
---|
2465 | if(attr_isDataDescriptor) {
|
---|
2466 | if(child->getVarPtr()->isAccessor()) child->setWritable(false);
|
---|
2467 | child->setVarPtr(attr_value?attr_value:constScriptVar(Undefined));
|
---|
2468 | if(attr_writable) child->setWritable(attr_writable->toBoolean());
|
---|
2469 | } else {
|
---|
2470 | if(child->getVarPtr()->isAccessor()) {
|
---|
2471 | if(!attr_get) attr_get = child->getVarPtr()->findChild(TINYJS_ACCESSOR_GET_VAR);
|
---|
2472 | if(!attr_set) attr_set = child->getVarPtr()->findChild(TINYJS_ACCESSOR_SET_VAR);
|
---|
2473 | }
|
---|
2474 | child->setVarPtr(newScriptVarAccessor(context, attr_get, attr_set));
|
---|
2475 | child->setWritable(true);
|
---|
2476 | }
|
---|
2477 | }
|
---|
2478 | if(attr_enumerable) child->setEnumerable(attr_enumerable->toBoolean());
|
---|
2479 | if(attr_configurable) child->setConfigurable(attr_configurable->toBoolean());
|
---|
2480 | return 0;
|
---|
2481 | cant_redefine:
|
---|
2482 | return "can't redefine non-configurable property";
|
---|
2483 | }
|
---|
2484 |
|
---|
2485 | CScriptVarLinkPtr CScriptVar::findChild(const string &childName) {
|
---|
2486 | if(Childs.empty()) return 0;
|
---|
2487 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName);
|
---|
2488 | if(it != Childs.end() && (*it)->getName() == childName)
|
---|
2489 | return *it;
|
---|
2490 | return 0;
|
---|
2491 | }
|
---|
2492 |
|
---|
2493 | CScriptVarLinkWorkPtr CScriptVar::findChildWithStringChars(const string &childName) {
|
---|
2494 | CScriptVarLinkWorkPtr child = findChild(childName);
|
---|
2495 | if(child) return child;
|
---|
2496 | CScriptVarStringPtr strVar = getRawPrimitive();
|
---|
2497 | uint32_t Idx;
|
---|
2498 | if (strVar && (Idx=isArrayIndex(childName))!=uint32_t(-1) && Idx<strVar->stringLength()) {
|
---|
2499 | int Char = strVar->getChar(Idx);
|
---|
2500 | child(newScriptVar(string(1, (char)Char)), childName, SCRIPTVARLINK_ENUMERABLE);
|
---|
2501 | child.setReferencedOwner(this); // fake referenced Owner
|
---|
2502 | return child;
|
---|
2503 | }
|
---|
2504 | return 0;
|
---|
2505 | }
|
---|
2506 |
|
---|
2507 | CScriptVarLinkPtr CScriptVar::findChildInPrototypeChain(const string &childName) {
|
---|
2508 | unsigned int uniqueID = context->getUniqueID();
|
---|
2509 | // Look for links to actual parent classes
|
---|
2510 | CScriptVarPtr object = this;
|
---|
2511 | CScriptVarLinkPtr __proto__;
|
---|
2512 | while( object->getTempraryID() != uniqueID && (__proto__ = object->findChild(TINYJS___PROTO___VAR)) ) {
|
---|
2513 | CScriptVarLinkPtr implementation = __proto__->getVarPtr()->findChild(childName);
|
---|
2514 | if (implementation) return implementation;
|
---|
2515 | object->setTemporaryID(uniqueID); // prevents recursions
|
---|
2516 | object = __proto__;
|
---|
2517 | }
|
---|
2518 | return 0;
|
---|
2519 | }
|
---|
2520 |
|
---|
2521 | CScriptVarLinkWorkPtr CScriptVar::findChildWithPrototypeChain(const string &childName) {
|
---|
2522 | CScriptVarLinkWorkPtr child = findChildWithStringChars(childName);
|
---|
2523 | if(child) return child;
|
---|
2524 | child = findChildInPrototypeChain(childName);
|
---|
2525 | if(child) {
|
---|
2526 | child(child->getVarPtr(), child->getName(), child->getFlags()); // recreate implementation
|
---|
2527 | child.setReferencedOwner(this); // fake referenced Owner
|
---|
2528 | }
|
---|
2529 | return child;
|
---|
2530 | }
|
---|
2531 | CScriptVarLinkPtr CScriptVar::findChildByPath(const string &path) {
|
---|
2532 | string::size_type p = path.find('.');
|
---|
2533 | CScriptVarLinkPtr child;
|
---|
2534 | if (p == string::npos)
|
---|
2535 | return findChild(path);
|
---|
2536 | if( (child = findChild(path.substr(0,p))) )
|
---|
2537 | return child->getVarPtr()->findChildByPath(path.substr(p+1));
|
---|
2538 | return 0;
|
---|
2539 | }
|
---|
2540 |
|
---|
2541 | CScriptVarLinkPtr CScriptVar::findChildOrCreate(const string &childName/*, int varFlags*/) {
|
---|
2542 | CScriptVarLinkPtr l = findChild(childName);
|
---|
2543 | if (l) return l;
|
---|
2544 | return addChild(childName, constScriptVar(Undefined));
|
---|
2545 | // return addChild(childName, new CScriptVar(context, TINYJS_BLANK_DATA, varFlags));
|
---|
2546 | }
|
---|
2547 |
|
---|
2548 | CScriptVarLinkPtr CScriptVar::findChildOrCreateByPath(const string &path) {
|
---|
2549 | string::size_type p = path.find('.');
|
---|
2550 | if (p == string::npos)
|
---|
2551 | return findChildOrCreate(path);
|
---|
2552 | string childName(path, 0, p);
|
---|
2553 | CScriptVarLinkPtr l = findChild(childName);
|
---|
2554 | if (!l) l = addChild(childName, newScriptVar(Object));
|
---|
2555 | return l->getVarPtr()->findChildOrCreateByPath(path.substr(p+1));
|
---|
2556 | }
|
---|
2557 |
|
---|
2558 | void CScriptVar::keys(set<string> &Keys, bool OnlyEnumerable/*=true*/, uint32_t ID/*=0*/)
|
---|
2559 | {
|
---|
2560 | setTemporaryID(ID);
|
---|
2561 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
2562 | if(!OnlyEnumerable || (*it)->isEnumerable())
|
---|
2563 | Keys.insert((*it)->getName());
|
---|
2564 | }
|
---|
2565 | CScriptVarStringPtr isStringObj = this->getRawPrimitive();
|
---|
2566 | if(isStringObj) {
|
---|
2567 | uint32_t length = isStringObj->stringLength();
|
---|
2568 | for(uint32_t i=0; i<length; ++i)
|
---|
2569 | Keys.insert(int2string(i));
|
---|
2570 | }
|
---|
2571 | CScriptVarLinkPtr __proto__;
|
---|
2572 | if( ID && (__proto__ = findChild(TINYJS___PROTO___VAR)) && __proto__->getVarPtr()->getTempraryID() != ID )
|
---|
2573 | __proto__->getVarPtr()->keys(Keys, OnlyEnumerable, ID);
|
---|
2574 | }
|
---|
2575 |
|
---|
2576 | /// add & remove
|
---|
2577 | CScriptVarLinkPtr CScriptVar::addChild(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) {
|
---|
2578 | CScriptVarLinkPtr link;
|
---|
2579 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName);
|
---|
2580 | if(it == Childs.end() || (*it)->getName() != childName) {
|
---|
2581 | link = CScriptVarLinkPtr(child?child:constScriptVar(Undefined), childName, linkFlags);
|
---|
2582 | link->setOwner(this);
|
---|
2583 |
|
---|
2584 | Childs.insert(it, 1, link);
|
---|
2585 | #ifdef _DEBUG
|
---|
2586 | } else {
|
---|
2587 | ASSERT(0); // addChild - the child exists
|
---|
2588 | #endif
|
---|
2589 | }
|
---|
2590 | return link;
|
---|
2591 | }
|
---|
2592 | CScriptVarLinkPtr CScriptVar::addChildNoDup(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) {
|
---|
2593 | return addChildOrReplace(childName, child, linkFlags);
|
---|
2594 | }
|
---|
2595 | CScriptVarLinkPtr CScriptVar::addChildOrReplace(const string &childName, const CScriptVarPtr &child, int linkFlags /*= SCRIPTVARLINK_DEFAULT*/) {
|
---|
2596 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), childName);
|
---|
2597 | if(it == Childs.end() || (*it)->getName() != childName) {
|
---|
2598 | CScriptVarLinkPtr link(child, childName, linkFlags);
|
---|
2599 | link->setOwner(this);
|
---|
2600 | Childs.insert(it, 1, link);
|
---|
2601 | return link;
|
---|
2602 | } else {
|
---|
2603 | (*it)->setVarPtr(child);
|
---|
2604 | return (*it);
|
---|
2605 | }
|
---|
2606 | }
|
---|
2607 |
|
---|
2608 | bool CScriptVar::removeLink(CScriptVarLinkPtr &link) {
|
---|
2609 | if (!link) return false;
|
---|
2610 | SCRIPTVAR_CHILDS_it it = lower_bound(Childs.begin(), Childs.end(), link->getName());
|
---|
2611 | if(it != Childs.end() && (*it) == link) {
|
---|
2612 | Childs.erase(it);
|
---|
2613 | #ifdef _DEBUG
|
---|
2614 | } else {
|
---|
2615 | ASSERT(0); // removeLink - the link is not atached to this var
|
---|
2616 | #endif
|
---|
2617 | }
|
---|
2618 | link.clear();
|
---|
2619 | return true;
|
---|
2620 | }
|
---|
2621 | void CScriptVar::removeAllChildren() {
|
---|
2622 | Childs.clear();
|
---|
2623 | }
|
---|
2624 |
|
---|
2625 | CScriptVarPtr CScriptVar::getArrayIndex(uint32_t idx) {
|
---|
2626 | CScriptVarLinkPtr link = findChild(int2string(idx));
|
---|
2627 | if (link) return link;
|
---|
2628 | else return constScriptVar(Undefined); // undefined
|
---|
2629 | }
|
---|
2630 |
|
---|
2631 | void CScriptVar::setArrayIndex(uint32_t idx, const CScriptVarPtr &value) {
|
---|
2632 | string sIdx = int2string(idx);
|
---|
2633 | CScriptVarLinkPtr link = findChild(sIdx);
|
---|
2634 |
|
---|
2635 | if (link) {
|
---|
2636 | link->setVarPtr(value);
|
---|
2637 | } else {
|
---|
2638 | addChild(sIdx, value);
|
---|
2639 | }
|
---|
2640 | }
|
---|
2641 |
|
---|
2642 | uint32_t CScriptVar::getArrayLength() {
|
---|
2643 | if (!isArray() || Childs.size()==0) return 0;
|
---|
2644 | return isArrayIndex(Childs.back()->getName())+1;
|
---|
2645 | }
|
---|
2646 |
|
---|
2647 | CScriptVarPtr CScriptVar::mathsOp(const CScriptVarPtr &b, int op) {
|
---|
2648 | CScriptResult execute;
|
---|
2649 | return context->mathsOp(execute, this, b, op);
|
---|
2650 | }
|
---|
2651 |
|
---|
2652 | void CScriptVar::trace(const string &name) {
|
---|
2653 | string indentStr;
|
---|
2654 | uint32_t uniqueID = context->getUniqueID();
|
---|
2655 | trace(indentStr, uniqueID, name);
|
---|
2656 | }
|
---|
2657 | void CScriptVar::trace(string &indentStr, uint32_t uniqueID, const string &name) {
|
---|
2658 | string indent = " ";
|
---|
2659 | const char *extra="";
|
---|
2660 | if(temporaryID == uniqueID)
|
---|
2661 | extra = " recursion detected";
|
---|
2662 | TRACE("%s'%s' = '%s' %s%s\n",
|
---|
2663 | indentStr.c_str(),
|
---|
2664 | name.c_str(),
|
---|
2665 | toString().c_str(),
|
---|
2666 | getFlagsAsString().c_str(),
|
---|
2667 | extra);
|
---|
2668 | if(temporaryID != uniqueID) {
|
---|
2669 | temporaryID = uniqueID;
|
---|
2670 | indentStr+=indent;
|
---|
2671 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
2672 | if((*it)->isEnumerable())
|
---|
2673 | (*it)->getVarPtr()->trace(indentStr, uniqueID, (*it)->getName());
|
---|
2674 | }
|
---|
2675 | indentStr = indentStr.substr(0, indentStr.length()-2);
|
---|
2676 | }
|
---|
2677 | }
|
---|
2678 |
|
---|
2679 | string CScriptVar::getFlagsAsString() {
|
---|
2680 | string flagstr = "";
|
---|
2681 | if (isFunction()) flagstr = flagstr + "FUNCTION ";
|
---|
2682 | if (isObject()) flagstr = flagstr + "OBJECT ";
|
---|
2683 | if (isArray()) flagstr = flagstr + "ARRAY ";
|
---|
2684 | if (isNative()) flagstr = flagstr + "NATIVE ";
|
---|
2685 | if (isDouble()) flagstr = flagstr + "DOUBLE ";
|
---|
2686 | if (isInt()) flagstr = flagstr + "INTEGER ";
|
---|
2687 | if (isBool()) flagstr = flagstr + "BOOLEAN ";
|
---|
2688 | if (isString()) flagstr = flagstr + "STRING ";
|
---|
2689 | if (isRegExp()) flagstr = flagstr + "REGEXP ";
|
---|
2690 | if (isNaN()) flagstr = flagstr + "NaN ";
|
---|
2691 | if (isInfinity()) flagstr = flagstr + "INFINITY ";
|
---|
2692 | return flagstr;
|
---|
2693 | }
|
---|
2694 |
|
---|
2695 | CScriptVar *CScriptVar::ref() {
|
---|
2696 | refs++;
|
---|
2697 | return this;
|
---|
2698 | }
|
---|
2699 | void CScriptVar::unref() {
|
---|
2700 | refs--;
|
---|
2701 | ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n");
|
---|
2702 | if (refs==0)
|
---|
2703 | delete this;
|
---|
2704 | }
|
---|
2705 |
|
---|
2706 | int CScriptVar::getRefs() {
|
---|
2707 | return refs;
|
---|
2708 | }
|
---|
2709 |
|
---|
2710 | void CScriptVar::setTemporaryID_recursive(uint32_t ID) {
|
---|
2711 | if(temporaryID != ID) {
|
---|
2712 | temporaryID = ID;
|
---|
2713 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
2714 | (*it)->getVarPtr()->setTemporaryID_recursive(ID);
|
---|
2715 | }
|
---|
2716 | }
|
---|
2717 | }
|
---|
2718 |
|
---|
2719 |
|
---|
2720 | //////////////////////////////////////////////////////////////////////////
|
---|
2721 | /// CScriptVarLink
|
---|
2722 | //////////////////////////////////////////////////////////////////////////
|
---|
2723 |
|
---|
2724 | CScriptVarLink::CScriptVarLink(const CScriptVarPtr &Var, const string &Name /*=TINYJS_TEMP_NAME*/, int Flags /*=SCRIPTVARLINK_DEFAULT*/)
|
---|
2725 | : name(Name), owner(0), flags(Flags), refs(0) {
|
---|
2726 | #if DEBUG_MEMORY
|
---|
2727 | mark_allocated(this);
|
---|
2728 | #endif
|
---|
2729 | var = Var;
|
---|
2730 | }
|
---|
2731 |
|
---|
2732 | CScriptVarLink::~CScriptVarLink() {
|
---|
2733 | #if DEBUG_MEMORY
|
---|
2734 | mark_deallocated(this);
|
---|
2735 | #endif
|
---|
2736 | }
|
---|
2737 |
|
---|
2738 | CScriptVarLink *CScriptVarLink::ref() {
|
---|
2739 | refs++;
|
---|
2740 | return this;
|
---|
2741 | }
|
---|
2742 | void CScriptVarLink::unref() {
|
---|
2743 | refs--;
|
---|
2744 | ASSERT(refs>=0); // printf("OMFG, we have unreffed too far!\n");
|
---|
2745 | if (refs==0)
|
---|
2746 | delete this;
|
---|
2747 | }
|
---|
2748 |
|
---|
2749 |
|
---|
2750 | //////////////////////////////////////////////////////////////////////////
|
---|
2751 | /// CScriptVarLinkPtr
|
---|
2752 | //////////////////////////////////////////////////////////////////////////
|
---|
2753 |
|
---|
2754 | CScriptVarLinkPtr & CScriptVarLinkPtr::operator()( const CScriptVarPtr &var, const std::string &name /*= TINYJS_TEMP_NAME*/, int flags /*= SCRIPTVARLINK_DEFAULT*/ ) {
|
---|
2755 | if(link && link->refs == 1) { // the link is only refered by this
|
---|
2756 | link->name = name;
|
---|
2757 | link->owner = 0;
|
---|
2758 | link->flags = flags;
|
---|
2759 | link->var = var;
|
---|
2760 | } else {
|
---|
2761 | if(link) link->unref();
|
---|
2762 | link = (new CScriptVarLink(var, name, flags))->ref();
|
---|
2763 | }
|
---|
2764 | return *this;
|
---|
2765 | }
|
---|
2766 |
|
---|
2767 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter() {
|
---|
2768 | return CScriptVarLinkWorkPtr(*this).getter();
|
---|
2769 | }
|
---|
2770 |
|
---|
2771 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::getter( CScriptResult &execute ) {
|
---|
2772 | return CScriptVarLinkWorkPtr(*this).getter(execute);
|
---|
2773 | }
|
---|
2774 |
|
---|
2775 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( const CScriptVarPtr &Var ) {
|
---|
2776 | return CScriptVarLinkWorkPtr(*this).setter(Var);
|
---|
2777 | }
|
---|
2778 |
|
---|
2779 | CScriptVarLinkWorkPtr CScriptVarLinkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) {
|
---|
2780 | return CScriptVarLinkWorkPtr(*this).setter(execute, Var);
|
---|
2781 | }
|
---|
2782 |
|
---|
2783 | bool CScriptVarLinkPtr::operator <(const string &rhs) const {
|
---|
2784 | uint32_t lhs_int = isArrayIndex(link->getName());
|
---|
2785 | uint32_t rhs_int = isArrayIndex(rhs);
|
---|
2786 | if(lhs_int==uint32_t(-1)) {
|
---|
2787 | if(rhs_int==uint32_t(-1))
|
---|
2788 | return link->getName() < rhs;
|
---|
2789 | else
|
---|
2790 | return true;
|
---|
2791 | } else if(rhs_int==uint32_t(-1))
|
---|
2792 | return false;
|
---|
2793 | return lhs_int < rhs_int;
|
---|
2794 | }
|
---|
2795 |
|
---|
2796 |
|
---|
2797 | //////////////////////////////////////////////////////////////////////////
|
---|
2798 | /// CScriptVarLinkWorkPtr
|
---|
2799 | //////////////////////////////////////////////////////////////////////////
|
---|
2800 |
|
---|
2801 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter() {
|
---|
2802 | if(link && link->getVarPtr()) {
|
---|
2803 | CScriptResult execute;
|
---|
2804 | CScriptVarPtr ret = getter(execute);
|
---|
2805 | execute.cThrow();
|
---|
2806 | return ret;
|
---|
2807 | }
|
---|
2808 | return *this;
|
---|
2809 | }
|
---|
2810 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::getter(CScriptResult &execute) {
|
---|
2811 | if(execute && link && link->getVarPtr() && link->getVarPtr()->isAccessor()) {
|
---|
2812 | const CScriptVarPtr &var = link->getVarPtr();
|
---|
2813 | CScriptVarLinkPtr getter = var->findChild(TINYJS_ACCESSOR_GET_VAR);
|
---|
2814 | if(getter) {
|
---|
2815 | vector<CScriptVarPtr> Params;
|
---|
2816 | ASSERT(getReferencedOwner());
|
---|
2817 | return getter->getVarPtr()->getContext()->callFunction(execute, getter->getVarPtr(), Params, getReferencedOwner());
|
---|
2818 | } else
|
---|
2819 | return var->constScriptVar(Undefined);
|
---|
2820 | } else
|
---|
2821 | return *this;
|
---|
2822 | }
|
---|
2823 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( const CScriptVarPtr &Var ) {
|
---|
2824 | if(link && link->getVarPtr()) {
|
---|
2825 | CScriptResult execute;
|
---|
2826 | CScriptVarPtr ret = setter(execute, Var);
|
---|
2827 | execute.cThrow();
|
---|
2828 | return ret;
|
---|
2829 | }
|
---|
2830 | return *this;
|
---|
2831 | }
|
---|
2832 |
|
---|
2833 | CScriptVarLinkWorkPtr CScriptVarLinkWorkPtr::setter( CScriptResult &execute, const CScriptVarPtr &Var ) {
|
---|
2834 | if(execute) {
|
---|
2835 | if(link) {
|
---|
2836 | if(link->getVarPtr() && link->getVarPtr()->isAccessor()) {
|
---|
2837 | const CScriptVarPtr &var = link->getVarPtr();
|
---|
2838 | CScriptVarLinkPtr setter = var->findChild(TINYJS_ACCESSOR_SET_VAR);
|
---|
2839 | if(setter) {
|
---|
2840 | vector<CScriptVarPtr> Params;
|
---|
2841 | Params.push_back(Var);
|
---|
2842 | ASSERT(getReferencedOwner());
|
---|
2843 | setter->getVarPtr()->getContext()->callFunction(execute, setter->getVarPtr(), Params, getReferencedOwner());
|
---|
2844 | }
|
---|
2845 | } else
|
---|
2846 | link->setVarPtr(Var);
|
---|
2847 | }
|
---|
2848 | }
|
---|
2849 | return *this;
|
---|
2850 | }
|
---|
2851 |
|
---|
2852 |
|
---|
2853 | //////////////////////////////////////////////////////////////////////////
|
---|
2854 | /// CScriptVarPrimitive
|
---|
2855 | //////////////////////////////////////////////////////////////////////////
|
---|
2856 |
|
---|
2857 | CScriptVarPrimitive::~CScriptVarPrimitive(){}
|
---|
2858 |
|
---|
2859 | bool CScriptVarPrimitive::isPrimitive() { return true; }
|
---|
2860 | CScriptVarPrimitivePtr CScriptVarPrimitive::getRawPrimitive() { return this; }
|
---|
2861 | bool CScriptVarPrimitive::toBoolean() { return false; }
|
---|
2862 | CScriptVarPtr CScriptVarPrimitive::toObject() { return this; }
|
---|
2863 | CScriptVarPtr CScriptVarPrimitive::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
|
---|
2864 | return newScriptVar(toCString(radix));
|
---|
2865 | }
|
---|
2866 |
|
---|
2867 |
|
---|
2868 | //////////////////////////////////////////////////////////////////////////
|
---|
2869 | // CScriptVarUndefined
|
---|
2870 | //////////////////////////////////////////////////////////////////////////
|
---|
2871 |
|
---|
2872 | declare_dummy_t(Undefined);
|
---|
2873 | CScriptVarUndefined::CScriptVarUndefined(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { }
|
---|
2874 | CScriptVarUndefined::~CScriptVarUndefined() {}
|
---|
2875 | CScriptVarPtr CScriptVarUndefined::clone() { return new CScriptVarUndefined(*this); }
|
---|
2876 | bool CScriptVarUndefined::isUndefined() { return true; }
|
---|
2877 |
|
---|
2878 | CNumber CScriptVarUndefined::toNumber_Callback() { return NaN; }
|
---|
2879 | string CScriptVarUndefined::toCString(int radix/*=0*/) { return "undefined"; }
|
---|
2880 | string CScriptVarUndefined::getVarType() { return "undefined"; }
|
---|
2881 |
|
---|
2882 |
|
---|
2883 | //////////////////////////////////////////////////////////////////////////
|
---|
2884 | // CScriptVarNull
|
---|
2885 | //////////////////////////////////////////////////////////////////////////
|
---|
2886 |
|
---|
2887 | declare_dummy_t(Null);
|
---|
2888 | CScriptVarNull::CScriptVarNull(CTinyJS *Context) : CScriptVarPrimitive(Context, Context->objectPrototype) { }
|
---|
2889 | CScriptVarNull::~CScriptVarNull() {}
|
---|
2890 | CScriptVarPtr CScriptVarNull::clone() { return new CScriptVarNull(*this); }
|
---|
2891 | bool CScriptVarNull::isNull() { return true; }
|
---|
2892 |
|
---|
2893 | CNumber CScriptVarNull::toNumber_Callback() { return 0; }
|
---|
2894 | string CScriptVarNull::toCString(int radix/*=0*/) { return "null"; }
|
---|
2895 | string CScriptVarNull::getVarType() { return "null"; }
|
---|
2896 |
|
---|
2897 |
|
---|
2898 | //////////////////////////////////////////////////////////////////////////
|
---|
2899 | /// CScriptVarString
|
---|
2900 | //////////////////////////////////////////////////////////////////////////
|
---|
2901 |
|
---|
2902 | CScriptVarString::CScriptVarString(CTinyJS *Context, const string &Data) : CScriptVarPrimitive(Context, Context->stringPrototype), data(Data) {
|
---|
2903 | addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT);
|
---|
2904 | /*
|
---|
2905 | CScriptVarLinkPtr acc = addChild("length", newScriptVar(Accessor), 0);
|
---|
2906 | CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarString::native_Length, 0));
|
---|
2907 | getter->setFunctionData(new CScriptTokenDataFnc);
|
---|
2908 | acc->getVarPtr()->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0);
|
---|
2909 | */
|
---|
2910 | }
|
---|
2911 | CScriptVarString::~CScriptVarString() {}
|
---|
2912 | CScriptVarPtr CScriptVarString::clone() { return new CScriptVarString(*this); }
|
---|
2913 | bool CScriptVarString::isString() { return true; }
|
---|
2914 |
|
---|
2915 | bool CScriptVarString::toBoolean() { return data.length()!=0; }
|
---|
2916 | CNumber CScriptVarString::toNumber_Callback() { return data.c_str(); }
|
---|
2917 | string CScriptVarString::toCString(int radix/*=0*/) { return data; }
|
---|
2918 |
|
---|
2919 | string CScriptVarString::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getJSString(data); }
|
---|
2920 | string CScriptVarString::getVarType() { return "string"; }
|
---|
2921 |
|
---|
2922 | CScriptVarPtr CScriptVarString::toObject() {
|
---|
2923 | CScriptVarPtr ret = newScriptVar(CScriptVarPrimitivePtr(this), context->stringPrototype);
|
---|
2924 | ret->addChild("length", newScriptVar(data.size()), SCRIPTVARLINK_CONSTANT);
|
---|
2925 | return ret;
|
---|
2926 | }
|
---|
2927 |
|
---|
2928 | CScriptVarPtr CScriptVarString::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
|
---|
2929 | return this;
|
---|
2930 | }
|
---|
2931 |
|
---|
2932 | int CScriptVarString::getChar(uint32_t Idx) {
|
---|
2933 | if((string::size_type)Idx >= data.length())
|
---|
2934 | return -1;
|
---|
2935 | else
|
---|
2936 | return (unsigned char)data[Idx];
|
---|
2937 | }
|
---|
2938 |
|
---|
2939 |
|
---|
2940 | //////////////////////////////////////////////////////////////////////////
|
---|
2941 | /// CNumber
|
---|
2942 | //////////////////////////////////////////////////////////////////////////
|
---|
2943 |
|
---|
2944 | NegativeZero_t NegativeZero;
|
---|
2945 | declare_dummy_t(NaN);
|
---|
2946 | Infinity InfinityPositive(1);
|
---|
2947 | Infinity InfinityNegative(-1);
|
---|
2948 | #if 1
|
---|
2949 | static inline bool _isNaN(volatile double *Value1, volatile double *Value2) {
|
---|
2950 | return !(*Value1==*Value2);
|
---|
2951 | }
|
---|
2952 |
|
---|
2953 | static inline bool isNaN(double Value) {
|
---|
2954 | return _isNaN(&Value, &Value);
|
---|
2955 | }
|
---|
2956 | inline bool isNegZero(double d) {
|
---|
2957 | double x=-0.0;
|
---|
2958 | return memcmp(&d, &x, sizeof(double))==0;
|
---|
2959 | }
|
---|
2960 |
|
---|
2961 | CNumber &CNumber::operator=(double Value) {
|
---|
2962 | double integral;
|
---|
2963 | if(isNegZero(Value))
|
---|
2964 | type=tnNULL, Int32=0;
|
---|
2965 | else if(numeric_limits<double>::has_infinity && Value == numeric_limits<double>::infinity())
|
---|
2966 | type=tInfinity, Int32=1;
|
---|
2967 | else if(numeric_limits<double>::has_infinity && Value == -numeric_limits<double>::infinity())
|
---|
2968 | type=tInfinity, Int32=-1;
|
---|
2969 | else if(::isNaN(Value) || Value == numeric_limits<double>::quiet_NaN() || Value == std::numeric_limits<double>::signaling_NaN())
|
---|
2970 | type=tNaN, Int32=0;
|
---|
2971 | else if(modf(Value, &integral)==0.0 && numeric_limits<int32_t>::min()<=integral && integral<=numeric_limits<int32_t>::max())
|
---|
2972 | type=tInt32, Int32=int32_t(integral);
|
---|
2973 | else
|
---|
2974 | type=tDouble, Double=Value;
|
---|
2975 | return *this;
|
---|
2976 | }
|
---|
2977 | CNumber &CNumber::operator=(const char *str) {
|
---|
2978 |
|
---|
2979 | while(isWhitespace(*str)) str++;
|
---|
2980 | const char *start = str, *endptr;
|
---|
2981 | if(*str == '-' || *str == '+') str++;
|
---|
2982 | if(*str == '0' && ( str[1]=='x' || str[1]=='X'))
|
---|
2983 | parseInt(start, 16, &endptr);
|
---|
2984 | else if(*str == '0' && str[1]>='0' && str[1]<='7')
|
---|
2985 | parseInt(start, 8, &endptr);
|
---|
2986 | else
|
---|
2987 | parseFloat(start, &endptr);
|
---|
2988 | while(isWhitespace(*endptr)) endptr++;
|
---|
2989 | if(*endptr != '\0')
|
---|
2990 | type=tNaN, Int32=0;
|
---|
2991 | return *this;
|
---|
2992 | }
|
---|
2993 | int32_t CNumber::parseInt(const char * str, int32_t radix/*=0*/, const char **endptr/*=0*/) {
|
---|
2994 | type=tInt32, Int32=0;
|
---|
2995 | if(endptr) *endptr = str;
|
---|
2996 | bool stripPrefix = false; //< is true if radix==0 or radix==16
|
---|
2997 | if(radix == 0) {
|
---|
2998 | radix=10;
|
---|
2999 | stripPrefix = true;
|
---|
3000 | } else if(radix < 2 || radix > 36) {
|
---|
3001 | type = tNaN;
|
---|
3002 | return 0;
|
---|
3003 | } else
|
---|
3004 | stripPrefix = radix == 16;
|
---|
3005 | while(isWhitespace(*str)) str++;
|
---|
3006 | int sign=1;
|
---|
3007 | if(*str=='-') sign=-1,str++;
|
---|
3008 | else if(*str=='+') str++;
|
---|
3009 | if(stripPrefix && *str=='0' && (str[1]=='x' || str[1]=='X')) str+=2, radix=16;
|
---|
3010 | else if(stripPrefix && *str=='0' && str[1]>='0' && str[1]<='7') str+=1, radix=8;
|
---|
3011 | int32_t max = 0x7fffffff/radix;
|
---|
3012 | const char *start = str;
|
---|
3013 | for( ; *str; str++) {
|
---|
3014 | if(*str >= '0' && *str <= '0'-1+radix) Int32 = Int32*radix+*str-'0';
|
---|
3015 | else if(*str>='a' && *str<='a'-11+radix) Int32 = Int32*radix+*str-'a'+10;
|
---|
3016 | else if(*str>='A' && *str<='A'-11+radix) Int32 = Int32*radix+*str-'A'+10;
|
---|
3017 | else break;
|
---|
3018 | if(Int32 >= max) {
|
---|
3019 | type=tDouble, Double=double(Int32);
|
---|
3020 | for(str++ ; *str; str++) {
|
---|
3021 | if(*str >= '0' && *str <= '0'-1+radix) Double = Double *radix+*str-'0';
|
---|
3022 | else if(*str>='a' && *str<='a'-11+radix) Double = Double *radix+*str-'a'+10;
|
---|
3023 | else if(*str>='A' && *str<='A'-11+radix) Double = Double *radix+*str-'A'+10;
|
---|
3024 | else break;
|
---|
3025 | }
|
---|
3026 | break;
|
---|
3027 | }
|
---|
3028 | }
|
---|
3029 | if(str == start) {
|
---|
3030 | type= tNaN;
|
---|
3031 | return 0;
|
---|
3032 | }
|
---|
3033 | if(sign<0 && ((type==tInt32 && Int32==0) || (type==tDouble && Double==0.0))) { type=tnNULL,Int32=0; return radix; }
|
---|
3034 | if(type==tInt32) operator=(sign<0 ? -Int32 : Int32);
|
---|
3035 | else operator=(sign<0 ? -Double : Double);
|
---|
3036 | if(endptr) *endptr = (char*)str;
|
---|
3037 | return radix;
|
---|
3038 | }
|
---|
3039 |
|
---|
3040 | void CNumber::parseFloat(const char * str, const char **endptr/*=0*/) {
|
---|
3041 | type=tInt32, Int32=0;
|
---|
3042 | if(endptr) *endptr = str;
|
---|
3043 | while(isWhitespace(*str)) str++;
|
---|
3044 | int sign=1;
|
---|
3045 | if(*str=='-') sign=-1,str++;
|
---|
3046 | else if(*str=='+') str++;
|
---|
3047 | if(strncmp(str, "Infinity", 8) == 0) { type=tInfinity, Int32=sign; return; }
|
---|
3048 | double d = strtod(str, (char**)endptr);
|
---|
3049 | operator=(sign>0 ? d : -d);
|
---|
3050 | return;
|
---|
3051 | }
|
---|
3052 |
|
---|
3053 | CNumber CNumber::add(const CNumber &Value) const {
|
---|
3054 | if(type==tNaN || Value.type==tNaN)
|
---|
3055 | return CNumber(tNaN);
|
---|
3056 | else if(type==tInfinity || Value.type==tInfinity) {
|
---|
3057 | if(type!=tInfinity)
|
---|
3058 | return Value;
|
---|
3059 | else if(Value.type!=tInfinity || sign()==Value.sign())
|
---|
3060 | return *this;
|
---|
3061 | else
|
---|
3062 | return CNumber(tNaN);
|
---|
3063 | } else if(type==tnNULL)
|
---|
3064 | return Value;
|
---|
3065 | else if(Value.type==tnNULL)
|
---|
3066 | return *this;
|
---|
3067 | else if(type==tDouble || Value.type==tDouble)
|
---|
3068 | return CNumber(toDouble()+Value.toDouble());
|
---|
3069 | else {
|
---|
3070 | int32_t range_max = numeric_limits<int32_t>::max();
|
---|
3071 | int32_t range_min = numeric_limits<int32_t>::min();
|
---|
3072 | if(Int32>0) range_max-=Int32;
|
---|
3073 | else if(Int32<0) range_min-=Int32;
|
---|
3074 | if(range_min<=Value.Int32 && Value.Int32<=range_max)
|
---|
3075 | return CNumber(Int32+Value.Int32);
|
---|
3076 | else
|
---|
3077 | return CNumber(double(Int32)+double(Value.Int32));
|
---|
3078 | }
|
---|
3079 | }
|
---|
3080 |
|
---|
3081 | CNumber CNumber::operator-() const {
|
---|
3082 | switch(type) {
|
---|
3083 | case tInt32:
|
---|
3084 | if(Int32==0)
|
---|
3085 | return CNumber(NegativeZero);
|
---|
3086 | case tnNULL:
|
---|
3087 | return CNumber(-Int32);
|
---|
3088 | case tDouble:
|
---|
3089 | return CNumber(-Double);
|
---|
3090 | case tInfinity:
|
---|
3091 | return CNumber(tInfinity, -Int32);
|
---|
3092 | default:
|
---|
3093 | return CNumber(tNaN);
|
---|
3094 | }
|
---|
3095 | }
|
---|
3096 |
|
---|
3097 | static inline int bits(uint32_t Value) {
|
---|
3098 | uint32_t b=0, mask=0xFFFF0000UL;
|
---|
3099 | for(int shift=16; shift>0 && Value!=0; shift>>=1, mask>>=shift) {
|
---|
3100 | if(Value & mask) {
|
---|
3101 | b += shift;
|
---|
3102 | Value>>=shift;
|
---|
3103 | }
|
---|
3104 | }
|
---|
3105 | return b;
|
---|
3106 | }
|
---|
3107 | static inline int bits(int32_t Value) {
|
---|
3108 | return bits(uint32_t(Value<0?-Value:Value));
|
---|
3109 | }
|
---|
3110 |
|
---|
3111 | CNumber CNumber::multi(const CNumber &Value) const {
|
---|
3112 | if(type==tNaN || Value.type==tNaN)
|
---|
3113 | return CNumber(tNaN);
|
---|
3114 | else if(type==tInfinity || Value.type==tInfinity) {
|
---|
3115 | if(isZero() || Value.isZero())
|
---|
3116 | return CNumber(tNaN);
|
---|
3117 | else
|
---|
3118 | return CNumber(tInfinity, sign()==Value.sign()?1:-1);
|
---|
3119 | } else if(isZero() || Value.isZero()) {
|
---|
3120 | if(sign()==Value.sign())
|
---|
3121 | return CNumber(0);
|
---|
3122 | else
|
---|
3123 | return CNumber(NegativeZero);
|
---|
3124 | } else if(type==tDouble || Value.type==tDouble)
|
---|
3125 | return CNumber(toDouble()*Value.toDouble());
|
---|
3126 | else {
|
---|
3127 | // Int32*Int32
|
---|
3128 | if(bits(Int32)+bits(Value.Int32) <= 29)
|
---|
3129 | return CNumber(Int32*Value.Int32);
|
---|
3130 | else
|
---|
3131 | return CNumber(double(Int32)*double(Value.Int32));
|
---|
3132 | }
|
---|
3133 | }
|
---|
3134 |
|
---|
3135 |
|
---|
3136 | CNumber CNumber::div( const CNumber &Value ) const {
|
---|
3137 | if(type==tNaN || Value.type==tNaN) return CNumber(tNaN);
|
---|
3138 | int Sign = sign()*Value.sign();
|
---|
3139 | if(type==tInfinity) {
|
---|
3140 | if(Value.type==tInfinity) return CNumber(tNaN);
|
---|
3141 | else return CNumber(tInfinity, Sign);
|
---|
3142 | }
|
---|
3143 | if(Value.type==tInfinity) {
|
---|
3144 | if(Sign<0) return CNumber(NegativeZero);
|
---|
3145 | else return CNumber(0);
|
---|
3146 | } else if(Value.isZero()) {
|
---|
3147 | if(isZero()) return CNumber(tNaN);
|
---|
3148 | else return CNumber(tInfinity, Sign);
|
---|
3149 | } else
|
---|
3150 | return CNumber(toDouble() / Value.toDouble());
|
---|
3151 | }
|
---|
3152 |
|
---|
3153 | CNumber CNumber::modulo( const CNumber &Value ) const {
|
---|
3154 | if(type==tNaN || type==tInfinity || Value.type==tNaN || Value.isZero()) return CNumber(tNaN);
|
---|
3155 | if(Value.type==tInfinity) return CNumber(*this);
|
---|
3156 | if(isZero()) return CNumber(0);
|
---|
3157 | if(type==tDouble || Value.type==tDouble) {
|
---|
3158 | double n = toDouble(), d = Value.toDouble(), q;
|
---|
3159 | modf(n/d, &q);
|
---|
3160 | return CNumber(n - (d * q));
|
---|
3161 | } else
|
---|
3162 | return CNumber(Int32 % Value.Int32);
|
---|
3163 | }
|
---|
3164 |
|
---|
3165 | CNumber CNumber::round() const {
|
---|
3166 | if(type != tDouble) return CNumber(*this);
|
---|
3167 | if(Double < 0.0 && Double >= -0.5)
|
---|
3168 | return CNumber(NegativeZero);
|
---|
3169 | return CNumber(::floor(Double+0.5));
|
---|
3170 | }
|
---|
3171 |
|
---|
3172 | CNumber CNumber::floor() const {
|
---|
3173 | if(type != tDouble) return CNumber(*this);
|
---|
3174 | return CNumber(::floor(Double));
|
---|
3175 | }
|
---|
3176 |
|
---|
3177 | CNumber CNumber::ceil() const {
|
---|
3178 | if(type != tDouble) return CNumber(*this);
|
---|
3179 | return CNumber(::ceil(Double));
|
---|
3180 | }
|
---|
3181 |
|
---|
3182 | CNumber CNumber::abs() const {
|
---|
3183 | if(sign()<0) return -CNumber(*this);
|
---|
3184 | else return CNumber(*this);
|
---|
3185 | }
|
---|
3186 |
|
---|
3187 | CNumber CNumber::shift(const CNumber &Value, bool Right) const {
|
---|
3188 | int32_t lhs = toInt32();
|
---|
3189 | uint32_t rhs = Value.toUInt32() & 0x1F;
|
---|
3190 | return CNumber(Right ? lhs>>rhs : lhs<<rhs);
|
---|
3191 | }
|
---|
3192 |
|
---|
3193 | CNumber CNumber::ushift(const CNumber &Value, bool Right) const {
|
---|
3194 | uint32_t lhs = toUInt32();
|
---|
3195 | uint32_t rhs = Value.toUInt32() & 0x1F;
|
---|
3196 | return CNumber(Right ? lhs>>rhs : lhs<<rhs);
|
---|
3197 | }
|
---|
3198 |
|
---|
3199 | CNumber CNumber::binary(const CNumber &Value, char Mode) const {
|
---|
3200 | int32_t lhs = toInt32();
|
---|
3201 | int32_t rhs = Value.toInt32();
|
---|
3202 |
|
---|
3203 | switch(Mode) {
|
---|
3204 | case '&': lhs&=rhs; break;
|
---|
3205 | case '|': lhs|=rhs; break;
|
---|
3206 | case '^': lhs^=rhs; break;
|
---|
3207 | }
|
---|
3208 | return CNumber(lhs);
|
---|
3209 | }
|
---|
3210 |
|
---|
3211 |
|
---|
3212 | int CNumber::less( const CNumber &Value ) const {
|
---|
3213 | if(type==tNaN || Value.type==tNaN) return 0;
|
---|
3214 | else if(type==tInfinity) {
|
---|
3215 | if(Value.type==tInfinity) return Int32<Value.Int32 ? 1 : -1;
|
---|
3216 | return -Int32;
|
---|
3217 | } else if(Value.type==tInfinity)
|
---|
3218 | return Value.Int32;
|
---|
3219 | else if(isZero() && Value.isZero()) return -1;
|
---|
3220 | else if(type==tDouble || Value.type==tDouble) return toDouble() < Value.toDouble() ? 1 : -1;
|
---|
3221 | return toInt32() < Value.toInt32() ? 1 : -1;
|
---|
3222 | }
|
---|
3223 |
|
---|
3224 | bool CNumber::equal( const CNumber &Value ) const {
|
---|
3225 | if(type==tNaN || Value.type==tNaN) return false;
|
---|
3226 | else if(type==tInfinity) {
|
---|
3227 | if(Value.type==tInfinity) return Int32==Value.Int32;
|
---|
3228 | return false;
|
---|
3229 | } else if(Value.type==tInfinity)
|
---|
3230 | return false;
|
---|
3231 | else if(isZero() && Value.isZero()) return true;
|
---|
3232 | else if(type==tDouble || Value.type==tDouble) return toDouble() == Value.toDouble();
|
---|
3233 | return toInt32() == Value.toInt32();
|
---|
3234 | }
|
---|
3235 |
|
---|
3236 | bool CNumber::isZero() const
|
---|
3237 | {
|
---|
3238 | switch(type) {
|
---|
3239 | case tInt32:
|
---|
3240 | return Int32==0;
|
---|
3241 | case tnNULL:
|
---|
3242 | return true;
|
---|
3243 | case tDouble:
|
---|
3244 | return Double==0.0;
|
---|
3245 | default:
|
---|
3246 | return false;
|
---|
3247 | }
|
---|
3248 | }
|
---|
3249 |
|
---|
3250 | bool CNumber::isInteger() const
|
---|
3251 | {
|
---|
3252 | double integral;
|
---|
3253 | switch(type) {
|
---|
3254 | case tInt32:
|
---|
3255 | case tnNULL:
|
---|
3256 | return true;
|
---|
3257 | case tDouble:
|
---|
3258 | return modf(Double, &integral)==0.0;
|
---|
3259 | default:
|
---|
3260 | return false;
|
---|
3261 | }
|
---|
3262 | }
|
---|
3263 |
|
---|
3264 | int CNumber::sign() const {
|
---|
3265 | switch(type) {
|
---|
3266 | case tInt32:
|
---|
3267 | case tInfinity:
|
---|
3268 | return Int32<0?-1:1;
|
---|
3269 | case tnNULL:
|
---|
3270 | return -1;
|
---|
3271 | case tDouble:
|
---|
3272 | return Double<0.0?-1:1;
|
---|
3273 | default:
|
---|
3274 | return 1;
|
---|
3275 | }
|
---|
3276 | }
|
---|
3277 | char *tiny_ltoa(int32_t val, unsigned radix) {
|
---|
3278 | char *buf, *buf_end, *p, *firstdig, temp;
|
---|
3279 | unsigned digval;
|
---|
3280 |
|
---|
3281 | buf = (char*)malloc(64);
|
---|
3282 | if(!buf) return 0;
|
---|
3283 | buf_end = buf+64-1; // -1 for '\0'
|
---|
3284 |
|
---|
3285 | p = buf;
|
---|
3286 | if (val < 0) {
|
---|
3287 | *p++ = '-';
|
---|
3288 | val = -val;
|
---|
3289 | }
|
---|
3290 |
|
---|
3291 | do {
|
---|
3292 | digval = (unsigned) (val % radix);
|
---|
3293 | val /= radix;
|
---|
3294 | *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0'));
|
---|
3295 | if(p==buf_end) {
|
---|
3296 | char *new_buf = (char *)realloc(buf, buf_end-buf+16+1); // for '\0'
|
---|
3297 | if(!new_buf) { free(buf); return 0; }
|
---|
3298 | p = new_buf + (buf_end - buf);
|
---|
3299 | buf_end = p + 16;
|
---|
3300 | buf = new_buf;
|
---|
3301 | }
|
---|
3302 | } while (val > 0);
|
---|
3303 |
|
---|
3304 | // We now have the digit of the number in the buffer, but in reverse
|
---|
3305 | // order. Thus we reverse them now.
|
---|
3306 | *p-- = '\0';
|
---|
3307 | firstdig = buf;
|
---|
3308 | if(*firstdig=='-') firstdig++;
|
---|
3309 | do {
|
---|
3310 | temp = *p;
|
---|
3311 | *p = *firstdig;
|
---|
3312 | *firstdig = temp;
|
---|
3313 | p--;
|
---|
3314 | firstdig++;
|
---|
3315 | } while (firstdig < p);
|
---|
3316 | return buf;
|
---|
3317 | }
|
---|
3318 |
|
---|
3319 | static char *tiny_dtoa(double val, unsigned radix) {
|
---|
3320 | char *buf, *buf_end, *p, temp;
|
---|
3321 | unsigned digval;
|
---|
3322 |
|
---|
3323 | buf = (char*)malloc(64);
|
---|
3324 | if(!buf) return 0;
|
---|
3325 | buf_end = buf+64-2; // -1 for '.' , -1 for '\0'
|
---|
3326 |
|
---|
3327 | p = buf;
|
---|
3328 | if (val < 0.0) {
|
---|
3329 | *p++ = '-';
|
---|
3330 | val = -val;
|
---|
3331 | }
|
---|
3332 |
|
---|
3333 | double val_1 = floor(val);
|
---|
3334 | double val_2 = val - val_1;
|
---|
3335 |
|
---|
3336 |
|
---|
3337 | do {
|
---|
3338 | double tmp = val_1 / radix;
|
---|
3339 | val_1 = floor(tmp);
|
---|
3340 | digval = (unsigned)((tmp - val_1) * radix);
|
---|
3341 |
|
---|
3342 | *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0'));
|
---|
3343 | if(p==buf_end) {
|
---|
3344 | char *new_buf = (char *)realloc(buf, buf_end-buf+16+2); // +2 for '.' + '\0'
|
---|
3345 | if(!new_buf) { free(buf); return 0; }
|
---|
3346 | p = new_buf + (buf_end - buf);
|
---|
3347 | buf_end = p + 16;
|
---|
3348 | buf = new_buf;
|
---|
3349 | }
|
---|
3350 | } while (val_1 > 0.0);
|
---|
3351 |
|
---|
3352 | // We now have the digit of the number in the buffer, but in reverse
|
---|
3353 | // order. Thus we reverse them now.
|
---|
3354 | char *p1 = buf;
|
---|
3355 | char *p2 = p-1;
|
---|
3356 | do {
|
---|
3357 | temp = *p2;
|
---|
3358 | *p2-- = *p1;
|
---|
3359 | *p1++ = temp;
|
---|
3360 | } while (p1 < p2);
|
---|
3361 |
|
---|
3362 | if(val_2) {
|
---|
3363 | *p++ = '.';
|
---|
3364 | do {
|
---|
3365 | val_2 *= radix;
|
---|
3366 | digval = (unsigned)(val_2);
|
---|
3367 | val_2 -= digval;
|
---|
3368 |
|
---|
3369 | *p++ = (char) (digval + (digval > 9 ? ('a'-10) : '0'));
|
---|
3370 | if(p==buf_end) {
|
---|
3371 | char *new_buf = (char *)realloc(buf, buf_end-buf+16);
|
---|
3372 | if(!new_buf) { free(buf); return 0; }
|
---|
3373 | p = new_buf + (buf_end - buf);
|
---|
3374 | buf_end = p + 16;
|
---|
3375 | buf = new_buf;
|
---|
3376 | }
|
---|
3377 | } while (val_2 > 0.0);
|
---|
3378 |
|
---|
3379 | }
|
---|
3380 | *p = '\0';
|
---|
3381 | return buf;
|
---|
3382 | }
|
---|
3383 | std::string CNumber::toString( uint32_t Radix/*=10*/ ) const {
|
---|
3384 | char *str;
|
---|
3385 | if(2 > Radix || Radix > 36)
|
---|
3386 | Radix = 10; // todo error;
|
---|
3387 | switch(type) {
|
---|
3388 | case tInt32:
|
---|
3389 | if( (str = tiny_ltoa(Int32, Radix)) ) {
|
---|
3390 | string ret(str); free(str);
|
---|
3391 | return ret;
|
---|
3392 | }
|
---|
3393 | break;
|
---|
3394 | case tnNULL:
|
---|
3395 | return "0";
|
---|
3396 | case tDouble:
|
---|
3397 | if(Radix==10) {
|
---|
3398 | ostringstream str;
|
---|
3399 | str.unsetf(ios::floatfield);
|
---|
3400 | #if (defined(_MSC_VER) && _MSC_VER >= 1600) || __cplusplus >= 201103L
|
---|
3401 | str.precision(numeric_limits<double>::max_digits10);
|
---|
3402 | #else
|
---|
3403 | str.precision(numeric_limits<double>::digits10+2);
|
---|
3404 | #endif
|
---|
3405 | str << Double;
|
---|
3406 | return str.str();
|
---|
3407 | } else if( (str = tiny_dtoa(Double, Radix)) ) {
|
---|
3408 | string ret(str); free(str);
|
---|
3409 | return ret;
|
---|
3410 | }
|
---|
3411 | break;
|
---|
3412 | case tInfinity:
|
---|
3413 | return Int32<0?"-Infinity":"Infinity";
|
---|
3414 | case tNaN:
|
---|
3415 | return "NaN";
|
---|
3416 | }
|
---|
3417 | return "";
|
---|
3418 | }
|
---|
3419 |
|
---|
3420 | double CNumber::toDouble() const
|
---|
3421 | {
|
---|
3422 | switch(type) {
|
---|
3423 | case tnNULL:
|
---|
3424 | return -0.0;
|
---|
3425 | case tInt32:
|
---|
3426 | return double(Int32);
|
---|
3427 | case tDouble:
|
---|
3428 | return Double;
|
---|
3429 | case tNaN:
|
---|
3430 | return std::numeric_limits<double>::quiet_NaN();
|
---|
3431 | case tInfinity:
|
---|
3432 | return Int32<0 ? -std::numeric_limits<double>::infinity():std::numeric_limits<double>::infinity();
|
---|
3433 | }
|
---|
3434 | return 0.0;
|
---|
3435 | }
|
---|
3436 |
|
---|
3437 | #endif
|
---|
3438 |
|
---|
3439 |
|
---|
3440 | //////////////////////////////////////////////////////////////////////////
|
---|
3441 | /// CScriptVarNumber
|
---|
3442 | //////////////////////////////////////////////////////////////////////////
|
---|
3443 |
|
---|
3444 | CScriptVarNumber::CScriptVarNumber(CTinyJS *Context, const CNumber &Data) : CScriptVarPrimitive(Context, Context->numberPrototype), data(Data) {}
|
---|
3445 | CScriptVarNumber::~CScriptVarNumber() {}
|
---|
3446 | CScriptVarPtr CScriptVarNumber::clone() { return new CScriptVarNumber(*this); }
|
---|
3447 | bool CScriptVarNumber::isNumber() { return true; }
|
---|
3448 | bool CScriptVarNumber::isInt() { return data.isInt32(); }
|
---|
3449 | bool CScriptVarNumber::isDouble() { return data.isDouble(); }
|
---|
3450 | bool CScriptVarNumber::isRealNumber() { return isInt() || isDouble(); }
|
---|
3451 | bool CScriptVarNumber::isNaN() { return data.isNaN(); }
|
---|
3452 | int CScriptVarNumber::isInfinity() { return data.isInfinity(); }
|
---|
3453 |
|
---|
3454 | bool CScriptVarNumber::toBoolean() { return data.toBoolean(); }
|
---|
3455 | CNumber CScriptVarNumber::toNumber_Callback() { return data; }
|
---|
3456 | string CScriptVarNumber::toCString(int radix/*=0*/) { return data.toString(radix); }
|
---|
3457 |
|
---|
3458 | string CScriptVarNumber::getVarType() { return "number"; }
|
---|
3459 |
|
---|
3460 | CScriptVarPtr CScriptVarNumber::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->numberPrototype); }
|
---|
3461 | inline define_newScriptVar_Fnc(Number, CTinyJS *Context, const CNumber &Obj) {
|
---|
3462 | if(!Obj.isInt32() && !Obj.isDouble()) {
|
---|
3463 | if(Obj.isNaN()) return Context->constScriptVar(NaN);
|
---|
3464 | if(Obj.isInfinity()) return Context->constScriptVar(Infinity(Obj.sign()));
|
---|
3465 | if(Obj.isNegativeZero()) return Context->constScriptVar(NegativeZero);
|
---|
3466 | }
|
---|
3467 | return new CScriptVarNumber(Context, Obj);
|
---|
3468 | }
|
---|
3469 |
|
---|
3470 |
|
---|
3471 | //////////////////////////////////////////////////////////////////////////
|
---|
3472 | // CScriptVarBool
|
---|
3473 | //////////////////////////////////////////////////////////////////////////
|
---|
3474 |
|
---|
3475 | CScriptVarBool::CScriptVarBool(CTinyJS *Context, bool Data) : CScriptVarPrimitive(Context, Context->booleanPrototype), data(Data) {}
|
---|
3476 | CScriptVarBool::~CScriptVarBool() {}
|
---|
3477 | CScriptVarPtr CScriptVarBool::clone() { return new CScriptVarBool(*this); }
|
---|
3478 | bool CScriptVarBool::isBool() { return true; }
|
---|
3479 |
|
---|
3480 | bool CScriptVarBool::toBoolean() { return data; }
|
---|
3481 | CNumber CScriptVarBool::toNumber_Callback() { return data?1:0; }
|
---|
3482 | string CScriptVarBool::toCString(int radix/*=0*/) { return data ? "true" : "false"; }
|
---|
3483 |
|
---|
3484 | string CScriptVarBool::getVarType() { return "boolean"; }
|
---|
3485 |
|
---|
3486 | CScriptVarPtr CScriptVarBool::toObject() { return newScriptVar(CScriptVarPrimitivePtr(this), context->booleanPrototype); }
|
---|
3487 |
|
---|
3488 | //////////////////////////////////////////////////////////////////////////
|
---|
3489 | /// CScriptVarObject
|
---|
3490 | //////////////////////////////////////////////////////////////////////////
|
---|
3491 |
|
---|
3492 | declare_dummy_t(Object);
|
---|
3493 | declare_dummy_t(StopIteration);
|
---|
3494 | CScriptVarObject::CScriptVarObject(CTinyJS *Context) : CScriptVar(Context, Context->objectPrototype) { }
|
---|
3495 | CScriptVarObject::~CScriptVarObject() {}
|
---|
3496 | CScriptVarPtr CScriptVarObject::clone() { return new CScriptVarObject(*this); }
|
---|
3497 | CScriptVarPrimitivePtr CScriptVarObject::getRawPrimitive() { return value; }
|
---|
3498 | bool CScriptVarObject::isObject() { return true; }
|
---|
3499 |
|
---|
3500 | string CScriptVarObject::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
3501 | getParsableStringRecursionsCheck();
|
---|
3502 | string destination;
|
---|
3503 | const char *nl = indent.size() ? "\n" : " ";
|
---|
3504 | const char *comma = "";
|
---|
3505 | destination.append("{");
|
---|
3506 | if(Childs.size()) {
|
---|
3507 | string new_indentString = indentString + indent;
|
---|
3508 | for(SCRIPTVAR_CHILDS_it it = Childs.begin(); it != Childs.end(); ++it) {
|
---|
3509 | if((*it)->isEnumerable()) {
|
---|
3510 | destination.append(comma); comma=",";
|
---|
3511 | destination.append(nl).append(new_indentString).append(getIDString((*it)->getName()));
|
---|
3512 | destination.append(" : ");
|
---|
3513 | destination.append((*it)->getVarPtr()->getParsableString(new_indentString, indent, uniqueID, hasRecursion));
|
---|
3514 | }
|
---|
3515 | }
|
---|
3516 | destination.append(nl).append(indentString);
|
---|
3517 | }
|
---|
3518 | destination.append("}");
|
---|
3519 | return destination;
|
---|
3520 | }
|
---|
3521 | string CScriptVarObject::getVarType() { return "object"; }
|
---|
3522 |
|
---|
3523 | CScriptVarPtr CScriptVarObject::toObject() { return this; }
|
---|
3524 |
|
---|
3525 | CScriptVarPtr CScriptVarObject::valueOf_CallBack() {
|
---|
3526 | if(value)
|
---|
3527 | return value->valueOf_CallBack();
|
---|
3528 | return CScriptVar::valueOf_CallBack();
|
---|
3529 | }
|
---|
3530 | CScriptVarPtr CScriptVarObject::toString_CallBack(CScriptResult &execute, int radix) {
|
---|
3531 | if(value)
|
---|
3532 | return value->toString_CallBack(execute, radix);
|
---|
3533 | return newScriptVar("[object Object]");
|
---|
3534 | };
|
---|
3535 |
|
---|
3536 | void CScriptVarObject::setTemporaryID_recursive( uint32_t ID ) {
|
---|
3537 | CScriptVar::setTemporaryID_recursive(ID);
|
---|
3538 | if(value) value->setTemporaryID_recursive(ID);
|
---|
3539 | }
|
---|
3540 |
|
---|
3541 |
|
---|
3542 | //////////////////////////////////////////////////////////////////////////
|
---|
3543 | /// CScriptVarError
|
---|
3544 | //////////////////////////////////////////////////////////////////////////
|
---|
3545 |
|
---|
3546 | const char *ERROR_NAME[] = {"Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError"};
|
---|
3547 |
|
---|
3548 | CScriptVarError::CScriptVarError(CTinyJS *Context, ERROR_TYPES type, const char *message, const char *file, int line, int column) : CScriptVarObject(Context, Context->getErrorPrototype(type)) {
|
---|
3549 | if(message && *message) addChild("message", newScriptVar(message));
|
---|
3550 | if(file && *file) addChild("fileName", newScriptVar(file));
|
---|
3551 | if(line>=0) addChild("lineNumber", newScriptVar(line+1));
|
---|
3552 | if(column>=0) addChild("column", newScriptVar(column+1));
|
---|
3553 | }
|
---|
3554 |
|
---|
3555 | CScriptVarError::~CScriptVarError() {}
|
---|
3556 | CScriptVarPtr CScriptVarError::clone() { return new CScriptVarError(*this); }
|
---|
3557 | bool CScriptVarError::isError() { return true; }
|
---|
3558 |
|
---|
3559 | CScriptVarPtr CScriptVarError::toString_CallBack(CScriptResult &execute, int radix) {
|
---|
3560 | CScriptVarLinkPtr link;
|
---|
3561 | string name = ERROR_NAME[Error];
|
---|
3562 | link = findChildWithPrototypeChain("name"); if(link) name = link->toString(execute);
|
---|
3563 | string message; link = findChildWithPrototypeChain("message"); if(link) message = link->toString(execute);
|
---|
3564 | string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = link->toString(execute);
|
---|
3565 | int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = link->toNumber().toInt32();
|
---|
3566 | int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = link->toNumber().toInt32();
|
---|
3567 | ostringstream msg;
|
---|
3568 | msg << name << ": " << message;
|
---|
3569 | if(lineNumber >= 0) msg << " at Line:" << lineNumber+1;
|
---|
3570 | if(column >=0) msg << " Column:" << column+1;
|
---|
3571 | if(fileName.length()) msg << " in " << fileName;
|
---|
3572 | return newScriptVar(msg.str());
|
---|
3573 | }
|
---|
3574 |
|
---|
3575 | CScriptException *CScriptVarError::toCScriptException()
|
---|
3576 | {
|
---|
3577 | CScriptVarLinkPtr link;
|
---|
3578 | string name = ERROR_NAME[Error];
|
---|
3579 | link = findChildWithPrototypeChain("name"); if(link) name = link->toString();
|
---|
3580 | int ErrorCode;
|
---|
3581 | for(ErrorCode=(sizeof(ERROR_NAME)/sizeof(ERROR_NAME[0]))-1; ErrorCode>0; ErrorCode--) {
|
---|
3582 | if(name == ERROR_NAME[ErrorCode]) break;
|
---|
3583 | }
|
---|
3584 | string message; link = findChildWithPrototypeChain("message"); if(link) message = link->toString();
|
---|
3585 | string fileName; link = findChildWithPrototypeChain("fileName"); if(link) fileName = link->toString();
|
---|
3586 | int lineNumber=-1; link = findChildWithPrototypeChain("lineNumber"); if(link) lineNumber = link->toNumber().toInt32();
|
---|
3587 | int column=-1; link = findChildWithPrototypeChain("column"); if(link) column = link->toNumber().toInt32();
|
---|
3588 | return new CScriptException((enum ERROR_TYPES)ErrorCode, message, fileName, lineNumber, column);
|
---|
3589 | }
|
---|
3590 |
|
---|
3591 |
|
---|
3592 | //////////////////////////////////////////////////////////////////////////
|
---|
3593 | // CScriptVarArray
|
---|
3594 | //////////////////////////////////////////////////////////////////////////
|
---|
3595 |
|
---|
3596 | declare_dummy_t(Array);
|
---|
3597 | CScriptVarArray::CScriptVarArray(CTinyJS *Context) : CScriptVarObject(Context, Context->arrayPrototype) {
|
---|
3598 | CScriptVarLinkPtr acc = addChild("length", newScriptVar(Accessor), 0);
|
---|
3599 | CScriptVarFunctionPtr getter(::newScriptVar(Context, this, &CScriptVarArray::native_Length, 0));
|
---|
3600 | getter->setFunctionData(new CScriptTokenDataFnc);
|
---|
3601 | acc->getVarPtr()->addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0);
|
---|
3602 | }
|
---|
3603 |
|
---|
3604 | CScriptVarArray::~CScriptVarArray() {}
|
---|
3605 | CScriptVarPtr CScriptVarArray::clone() { return new CScriptVarArray(*this); }
|
---|
3606 | bool CScriptVarArray::isArray() { return true; }
|
---|
3607 | string CScriptVarArray::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
3608 | getParsableStringRecursionsCheck();
|
---|
3609 | string destination;
|
---|
3610 | const char *nl = indent.size() ? "\n" : " ";
|
---|
3611 | const char *comma = "";
|
---|
3612 | destination.append("[");
|
---|
3613 | int len = getArrayLength();
|
---|
3614 | if(len) {
|
---|
3615 | string new_indentString = indentString + indent;
|
---|
3616 | for (int i=0;i<len;i++) {
|
---|
3617 | destination.append(comma); comma = ",";
|
---|
3618 | destination.append(nl).append(new_indentString).append(getArrayIndex(i)->getParsableString(new_indentString, indent, uniqueID, hasRecursion));
|
---|
3619 | }
|
---|
3620 | destination.append(nl).append(indentString);
|
---|
3621 | }
|
---|
3622 | destination.append("]");
|
---|
3623 | return destination;
|
---|
3624 | }
|
---|
3625 | CScriptVarPtr CScriptVarArray::toString_CallBack( CScriptResult &execute, int radix/*=0*/ ) {
|
---|
3626 | ostringstream destination;
|
---|
3627 | int len = getArrayLength();
|
---|
3628 | for (int i=0;i<len;i++) {
|
---|
3629 | destination << getArrayIndex(i)->toString(execute);
|
---|
3630 | if (i<len-1) destination << ", ";
|
---|
3631 | }
|
---|
3632 | return newScriptVar(destination.str());
|
---|
3633 |
|
---|
3634 | }
|
---|
3635 |
|
---|
3636 | void CScriptVarArray::native_Length(const CFunctionsScopePtr &c, void *data) {
|
---|
3637 | c->setReturnVar(newScriptVar(c->getArgument("this")->getArrayLength()));
|
---|
3638 | }
|
---|
3639 |
|
---|
3640 |
|
---|
3641 | //////////////////////////////////////////////////////////////////////////
|
---|
3642 | /// CScriptVarRegExp
|
---|
3643 | //////////////////////////////////////////////////////////////////////////
|
---|
3644 |
|
---|
3645 | #ifndef NO_REGEXP
|
---|
3646 |
|
---|
3647 | CScriptVarRegExp::CScriptVarRegExp(CTinyJS *Context, const string &Regexp, const string &Flags) : CScriptVarObject(Context, Context->regexpPrototype), regexp(Regexp), flags(Flags) {
|
---|
3648 | addChild("global", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Global, 0, 0, 0), 0);
|
---|
3649 | addChild("ignoreCase", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_IgnoreCase, 0, 0, 0), 0);
|
---|
3650 | addChild("multiline", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Multiline, 0, 0, 0), 0);
|
---|
3651 | addChild("sticky", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Sticky, 0, 0, 0), 0);
|
---|
3652 | addChild("regexp", ::newScriptVarAccessor<CScriptVarRegExp>(Context, this, &CScriptVarRegExp::native_Source, 0, 0, 0), 0);
|
---|
3653 | addChild("lastIndex", newScriptVar(0));
|
---|
3654 | }
|
---|
3655 | CScriptVarRegExp::~CScriptVarRegExp() {}
|
---|
3656 | CScriptVarPtr CScriptVarRegExp::clone() { return new CScriptVarRegExp(*this); }
|
---|
3657 | bool CScriptVarRegExp::isRegExp() { return true; }
|
---|
3658 | //int CScriptVarRegExp::getInt() {return strtol(regexp.c_str(),0,0); }
|
---|
3659 | //bool CScriptVarRegExp::getBool() {return regexp.length()!=0;}
|
---|
3660 | //double CScriptVarRegExp::getDouble() {return strtod(regexp.c_str(),0);}
|
---|
3661 | //string CScriptVarRegExp::getString() { return "/"+regexp+"/"+flags; }
|
---|
3662 | //string CScriptVarRegExp::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) { return getString(); }
|
---|
3663 | CScriptVarPtr CScriptVarRegExp::toString_CallBack(CScriptResult &execute, int radix) {
|
---|
3664 | return newScriptVar("/"+regexp+"/"+flags);
|
---|
3665 | }
|
---|
3666 | void CScriptVarRegExp::native_Global(const CFunctionsScopePtr &c, void *data) {
|
---|
3667 | c->setReturnVar(constScriptVar(Global()));
|
---|
3668 | }
|
---|
3669 | void CScriptVarRegExp::native_IgnoreCase(const CFunctionsScopePtr &c, void *data) {
|
---|
3670 | c->setReturnVar(constScriptVar(IgnoreCase()));
|
---|
3671 | }
|
---|
3672 | void CScriptVarRegExp::native_Multiline(const CFunctionsScopePtr &c, void *data) {
|
---|
3673 | c->setReturnVar(constScriptVar(Multiline()));
|
---|
3674 | }
|
---|
3675 | void CScriptVarRegExp::native_Sticky(const CFunctionsScopePtr &c, void *data) {
|
---|
3676 | c->setReturnVar(constScriptVar(Sticky()));
|
---|
3677 | }
|
---|
3678 | void CScriptVarRegExp::native_Source(const CFunctionsScopePtr &c, void *data) {
|
---|
3679 | c->setReturnVar(newScriptVar(regexp));
|
---|
3680 | }
|
---|
3681 | unsigned int CScriptVarRegExp::LastIndex() {
|
---|
3682 | CScriptVarPtr lastIndex = findChild("lastIndex");
|
---|
3683 | if(lastIndex) return lastIndex->toNumber().toInt32();
|
---|
3684 | return 0;
|
---|
3685 | }
|
---|
3686 | void CScriptVarRegExp::LastIndex(unsigned int Idx) {
|
---|
3687 | addChildOrReplace("lastIndex", newScriptVar((int)Idx));
|
---|
3688 | }
|
---|
3689 |
|
---|
3690 | CScriptVarPtr CScriptVarRegExp::exec( const string &Input, bool Test /*= false*/ )
|
---|
3691 | {
|
---|
3692 | regex::flag_type flags = regex_constants::ECMAScript;
|
---|
3693 | if(IgnoreCase()) flags |= regex_constants::icase;
|
---|
3694 | bool global = Global(), sticky = Sticky();
|
---|
3695 | unsigned int lastIndex = LastIndex();
|
---|
3696 | int offset = 0;
|
---|
3697 | if(global || sticky) {
|
---|
3698 | if(lastIndex > Input.length()) goto failed;
|
---|
3699 | offset=lastIndex;
|
---|
3700 | }
|
---|
3701 | {
|
---|
3702 | regex_constants::match_flag_type mflag = sticky?regex_constants::match_continuous:regex_constants::match_default;
|
---|
3703 | if(offset) mflag |= regex_constants::match_prev_avail;
|
---|
3704 | smatch match;
|
---|
3705 | if(regex_search(Input.begin()+offset, Input.end(), match, regex(regexp, flags), mflag) ) {
|
---|
3706 | LastIndex(offset+match.position()+match.str().length());
|
---|
3707 | if(Test) return constScriptVar(true);
|
---|
3708 |
|
---|
3709 | CScriptVarArrayPtr retVar = newScriptVar(Array);
|
---|
3710 | retVar->addChild("input", newScriptVar(Input));
|
---|
3711 | retVar->addChild("index", newScriptVar(match.position()));
|
---|
3712 | for(smatch::size_type idx=0; idx<match.size(); idx++)
|
---|
3713 | retVar->addChild(int2string(idx), newScriptVar(match[idx].str()));
|
---|
3714 | return retVar;
|
---|
3715 | }
|
---|
3716 | }
|
---|
3717 | failed:
|
---|
3718 | if(global || sticky)
|
---|
3719 | LastIndex(0);
|
---|
3720 | if(Test) return constScriptVar(false);
|
---|
3721 | return constScriptVar(Null);
|
---|
3722 | }
|
---|
3723 |
|
---|
3724 | const char * CScriptVarRegExp::ErrorStr( int Error )
|
---|
3725 | {
|
---|
3726 | switch(Error) {
|
---|
3727 | case regex_constants::error_badbrace: return "the expression contained an invalid count in a { } expression";
|
---|
3728 | case regex_constants::error_badrepeat: return "a repeat expression (one of '*', '?', '+', '{' in most contexts) was not preceded by an expression";
|
---|
3729 | case regex_constants::error_brace: return "the expression contained an unmatched '{' or '}'";
|
---|
3730 | case regex_constants::error_brack: return "the expression contained an unmatched '[' or ']'";
|
---|
3731 | case regex_constants::error_collate: return "the expression contained an invalid collating element name";
|
---|
3732 | case regex_constants::error_complexity: return "an attempted match failed because it was too complex";
|
---|
3733 | case regex_constants::error_ctype: return "the expression contained an invalid character class name";
|
---|
3734 | case regex_constants::error_escape: return "the expression contained an invalid escape sequence";
|
---|
3735 | case regex_constants::error_paren: return "the expression contained an unmatched '(' or ')'";
|
---|
3736 | case regex_constants::error_range: return "the expression contained an invalid character range specifier";
|
---|
3737 | case regex_constants::error_space: return "parsing a regular expression failed because there were not enough resources available";
|
---|
3738 | case regex_constants::error_stack: return "an attempted match failed because there was not enough memory available";
|
---|
3739 | case regex_constants::error_backref: return "the expression contained an invalid back reference";
|
---|
3740 | default: return "";
|
---|
3741 | }
|
---|
3742 | }
|
---|
3743 |
|
---|
3744 | #endif /* NO_REGEXP */
|
---|
3745 |
|
---|
3746 |
|
---|
3747 | //////////////////////////////////////////////////////////////////////////
|
---|
3748 | /// CScriptVarDefaultIterator
|
---|
3749 | //////////////////////////////////////////////////////////////////////////
|
---|
3750 |
|
---|
3751 | //declare_dummy_t(DefaultIterator);
|
---|
3752 | CScriptVarDefaultIterator::CScriptVarDefaultIterator(CTinyJS *Context, const CScriptVarPtr &Object, int Mode)
|
---|
3753 | : CScriptVarObject(Context, Context->iteratorPrototype), mode(Mode), object(Object) {
|
---|
3754 | object->keys(keys, true);
|
---|
3755 | pos = keys.begin();
|
---|
3756 | addChild("next", ::newScriptVar(context, this, &CScriptVarDefaultIterator::native_next, 0));
|
---|
3757 | }
|
---|
3758 | CScriptVarDefaultIterator::~CScriptVarDefaultIterator() {}
|
---|
3759 | CScriptVarPtr CScriptVarDefaultIterator::clone() { return new CScriptVarDefaultIterator(*this); }
|
---|
3760 | bool CScriptVarDefaultIterator::isIterator() {return true;}
|
---|
3761 | void CScriptVarDefaultIterator::native_next(const CFunctionsScopePtr &c, void *data) {
|
---|
3762 | if(pos==keys.end()) throw constScriptVar(StopIteration);
|
---|
3763 | CScriptVarPtr ret, ret0, ret1;
|
---|
3764 | if(mode&1) ret0 = newScriptVar(*pos);
|
---|
3765 | if(mode&2) ret1 = object->findChildWithStringChars(*pos);
|
---|
3766 | pos++;
|
---|
3767 | if(mode==3) {
|
---|
3768 | ret = newScriptVar(Array);
|
---|
3769 | ret->setArrayIndex(0, ret0);
|
---|
3770 | ret->setArrayIndex(1, ret1);
|
---|
3771 | } else if(mode==1)
|
---|
3772 | ret = ret0;
|
---|
3773 | else
|
---|
3774 | ret = ret1;
|
---|
3775 | c->setReturnVar(ret);
|
---|
3776 | }
|
---|
3777 |
|
---|
3778 | //////////////////////////////////////////////////////////////////////////
|
---|
3779 | // CScriptVarFunction
|
---|
3780 | //////////////////////////////////////////////////////////////////////////
|
---|
3781 |
|
---|
3782 | CScriptVarFunction::CScriptVarFunction(CTinyJS *Context, CScriptTokenDataFnc *Data) : CScriptVarObject(Context, Context->functionPrototype), data(0) {
|
---|
3783 | setFunctionData(Data);
|
---|
3784 | }
|
---|
3785 | CScriptVarFunction::~CScriptVarFunction() { setFunctionData(0); }
|
---|
3786 | CScriptVarPtr CScriptVarFunction::clone() { return new CScriptVarFunction(*this); }
|
---|
3787 | bool CScriptVarFunction::isObject() { return true; }
|
---|
3788 | bool CScriptVarFunction::isFunction() { return true; }
|
---|
3789 | bool CScriptVarFunction::isPrimitive() { return false; }
|
---|
3790 |
|
---|
3791 | //string CScriptVarFunction::getString() {return "[ Function ]";}
|
---|
3792 | string CScriptVarFunction::getVarType() { return "function"; }
|
---|
3793 | string CScriptVarFunction::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
3794 | getParsableStringRecursionsCheck();
|
---|
3795 | string destination;
|
---|
3796 | destination.append("function ").append(data->name);
|
---|
3797 | // get list of arguments
|
---|
3798 | destination.append(data->getArgumentsString());
|
---|
3799 |
|
---|
3800 | if(isNative() || isBounded())
|
---|
3801 | destination.append("{ [native code] }");
|
---|
3802 | else {
|
---|
3803 | destination.append(CScriptToken::getParsableString(data->body, indentString, indent));
|
---|
3804 | if(!data->body.size() || data->body.front().token != '{')
|
---|
3805 | destination.append(";");
|
---|
3806 | }
|
---|
3807 | return destination;
|
---|
3808 | }
|
---|
3809 |
|
---|
3810 | CScriptVarPtr CScriptVarFunction::toString_CallBack(CScriptResult &execute, int radix){
|
---|
3811 | bool hasRecursion;
|
---|
3812 | return newScriptVar(getParsableString("", " ", 0, hasRecursion));
|
---|
3813 | }
|
---|
3814 |
|
---|
3815 | CScriptTokenDataFnc *CScriptVarFunction::getFunctionData() { return data; }
|
---|
3816 |
|
---|
3817 | void CScriptVarFunction::setFunctionData(CScriptTokenDataFnc *Data) {
|
---|
3818 | if(data) { data->unref(); data = 0; }
|
---|
3819 | if(Data) {
|
---|
3820 | data = Data; data->ref();
|
---|
3821 | addChildOrReplace("length", newScriptVar((int)data->arguments.size()), 0);
|
---|
3822 | // can not add "name" here because name is a StingVar with length as getter
|
---|
3823 | // length-getter is a function with a function name -> endless recursion
|
---|
3824 | //addChildNoDup("name", newScriptVar(data->name), 0);
|
---|
3825 | }
|
---|
3826 | }
|
---|
3827 |
|
---|
3828 |
|
---|
3829 | //////////////////////////////////////////////////////////////////////////
|
---|
3830 | /// CScriptVarFunctionBounded
|
---|
3831 | //////////////////////////////////////////////////////////////////////////
|
---|
3832 |
|
---|
3833 | CScriptVarFunctionBounded::CScriptVarFunctionBounded(CScriptVarFunctionPtr BoundedFunction, CScriptVarPtr BoundedThis, const std::vector<CScriptVarPtr> &BoundedArguments)
|
---|
3834 | : CScriptVarFunction(BoundedFunction->getContext(), new CScriptTokenDataFnc) ,
|
---|
3835 | boundedFunction(BoundedFunction),
|
---|
3836 | boundedThis(BoundedThis),
|
---|
3837 | boundedArguments(BoundedArguments) {
|
---|
3838 | getFunctionData()->name = BoundedFunction->getFunctionData()->name;
|
---|
3839 | }
|
---|
3840 | CScriptVarFunctionBounded::~CScriptVarFunctionBounded(){}
|
---|
3841 | CScriptVarPtr CScriptVarFunctionBounded::clone() { return new CScriptVarFunctionBounded(*this); }
|
---|
3842 | bool CScriptVarFunctionBounded::isBounded() { return true; }
|
---|
3843 | void CScriptVarFunctionBounded::setTemporaryID_recursive( uint32_t ID ) {
|
---|
3844 | CScriptVarFunction::setTemporaryID_recursive(ID);
|
---|
3845 | boundedThis->setTemporaryID_recursive(ID);
|
---|
3846 | for(vector<CScriptVarPtr>::iterator it=boundedArguments.begin(); it!=boundedArguments.end(); ++it)
|
---|
3847 | (*it)->setTemporaryID_recursive(ID);
|
---|
3848 | }
|
---|
3849 |
|
---|
3850 | CScriptVarPtr CScriptVarFunctionBounded::callFunction( CScriptResult &execute, vector<CScriptVarPtr> &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis/*=0*/ )
|
---|
3851 | {
|
---|
3852 | vector<CScriptVarPtr> newArgs=boundedArguments;
|
---|
3853 | newArgs.insert(newArgs.end(), Arguments.begin(), Arguments.end());
|
---|
3854 | return context->callFunction(execute, boundedFunction, newArgs, newThis ? This : boundedThis, newThis);
|
---|
3855 | }
|
---|
3856 |
|
---|
3857 |
|
---|
3858 | //////////////////////////////////////////////////////////////////////////
|
---|
3859 | /// CScriptVarFunctionNative
|
---|
3860 | //////////////////////////////////////////////////////////////////////////
|
---|
3861 |
|
---|
3862 | CScriptVarFunctionNative::~CScriptVarFunctionNative() {}
|
---|
3863 | bool CScriptVarFunctionNative::isNative() { return true; }
|
---|
3864 |
|
---|
3865 |
|
---|
3866 | //////////////////////////////////////////////////////////////////////////
|
---|
3867 | /// CScriptVarFunctionNativeCallback
|
---|
3868 | //////////////////////////////////////////////////////////////////////////
|
---|
3869 |
|
---|
3870 | CScriptVarFunctionNativeCallback::~CScriptVarFunctionNativeCallback() {}
|
---|
3871 | CScriptVarPtr CScriptVarFunctionNativeCallback::clone() { return new CScriptVarFunctionNativeCallback(*this); }
|
---|
3872 | void CScriptVarFunctionNativeCallback::callFunction(const CFunctionsScopePtr &c) { jsCallback(c, jsUserData); }
|
---|
3873 |
|
---|
3874 |
|
---|
3875 | //////////////////////////////////////////////////////////////////////////
|
---|
3876 | /// CScriptVarAccessor
|
---|
3877 | //////////////////////////////////////////////////////////////////////////
|
---|
3878 |
|
---|
3879 | declare_dummy_t(Accessor);
|
---|
3880 | CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context) : CScriptVarObject(Context, Context->objectPrototype) { }
|
---|
3881 | CScriptVarAccessor::CScriptVarAccessor(CTinyJS *Context, JSCallback getterFnc, void *getterData, JSCallback setterFnc, void *setterData)
|
---|
3882 | : CScriptVarObject(Context)
|
---|
3883 | {
|
---|
3884 | if(getterFnc)
|
---|
3885 | addChild(TINYJS_ACCESSOR_GET_VAR, ::newScriptVar(Context, getterFnc, getterData), 0);
|
---|
3886 | if(setterFnc)
|
---|
3887 | addChild(TINYJS_ACCESSOR_SET_VAR, ::newScriptVar(Context, setterFnc, setterData), 0);
|
---|
3888 | }
|
---|
3889 |
|
---|
3890 | CScriptVarAccessor::CScriptVarAccessor( CTinyJS *Context, const CScriptVarFunctionPtr &getter, const CScriptVarFunctionPtr &setter) : CScriptVarObject(Context, Context->objectPrototype) {
|
---|
3891 | if(getter)
|
---|
3892 | addChild(TINYJS_ACCESSOR_GET_VAR, getter, 0);
|
---|
3893 | if(setter)
|
---|
3894 | addChild(TINYJS_ACCESSOR_SET_VAR, setter, 0);
|
---|
3895 | }
|
---|
3896 |
|
---|
3897 | CScriptVarAccessor::~CScriptVarAccessor() {}
|
---|
3898 | CScriptVarPtr CScriptVarAccessor::clone() { return new CScriptVarAccessor(*this); }
|
---|
3899 | bool CScriptVarAccessor::isAccessor() { return true; }
|
---|
3900 | bool CScriptVarAccessor::isPrimitive() { return false; }
|
---|
3901 | string CScriptVarAccessor::getParsableString(const string &indentString, const string &indent, uint32_t uniqueID, bool &hasRecursion) {
|
---|
3902 | return "";
|
---|
3903 | }
|
---|
3904 | string CScriptVarAccessor::getVarType() { return "accessor"; }
|
---|
3905 |
|
---|
3906 |
|
---|
3907 | //////////////////////////////////////////////////////////////////////////
|
---|
3908 | /// CScriptVarScope
|
---|
3909 | //////////////////////////////////////////////////////////////////////////
|
---|
3910 |
|
---|
3911 | declare_dummy_t(Scope);
|
---|
3912 | CScriptVarScope::~CScriptVarScope() {}
|
---|
3913 | CScriptVarPtr CScriptVarScope::clone() { return CScriptVarPtr(); }
|
---|
3914 | bool CScriptVarScope::isObject() { return false; }
|
---|
3915 | CScriptVarPtr CScriptVarScope::scopeVar() { return this; } ///< to create var like: var a = ...
|
---|
3916 | CScriptVarPtr CScriptVarScope::scopeLet() { return this; } ///< to create var like: let a = ...
|
---|
3917 | CScriptVarLinkWorkPtr CScriptVarScope::findInScopes(const string &childName) {
|
---|
3918 | return CScriptVar::findChild(childName);
|
---|
3919 | }
|
---|
3920 | CScriptVarScopePtr CScriptVarScope::getParent() { return CScriptVarScopePtr(); } ///< no Parent
|
---|
3921 |
|
---|
3922 |
|
---|
3923 | //////////////////////////////////////////////////////////////////////////
|
---|
3924 | /// CScriptVarScopeFnc
|
---|
3925 | //////////////////////////////////////////////////////////////////////////
|
---|
3926 |
|
---|
3927 | declare_dummy_t(ScopeFnc);
|
---|
3928 | CScriptVarScopeFnc::~CScriptVarScopeFnc() {}
|
---|
3929 | CScriptVarLinkWorkPtr CScriptVarScopeFnc::findInScopes(const string &childName) {
|
---|
3930 | CScriptVarLinkWorkPtr ret = findChild(childName);
|
---|
3931 | if( !ret ) {
|
---|
3932 | if(closure) ret = CScriptVarScopePtr(closure)->findInScopes(childName);
|
---|
3933 | else ret = context->getRoot()->findChild(childName);
|
---|
3934 | }
|
---|
3935 | return ret;
|
---|
3936 | }
|
---|
3937 |
|
---|
3938 | void CScriptVarScopeFnc::setReturnVar(const CScriptVarPtr &var) {
|
---|
3939 | addChildOrReplace(TINYJS_RETURN_VAR, var);
|
---|
3940 | }
|
---|
3941 |
|
---|
3942 | CScriptVarPtr CScriptVarScopeFnc::getParameter(const string &name) {
|
---|
3943 | return getArgument(name);
|
---|
3944 | }
|
---|
3945 |
|
---|
3946 | CScriptVarPtr CScriptVarScopeFnc::getParameter(int Idx) {
|
---|
3947 | return getArgument(Idx);
|
---|
3948 | }
|
---|
3949 | CScriptVarPtr CScriptVarScopeFnc::getArgument(const string &name) {
|
---|
3950 | return findChildOrCreate(name);
|
---|
3951 | }
|
---|
3952 | CScriptVarPtr CScriptVarScopeFnc::getArgument(int Idx) {
|
---|
3953 | CScriptVarLinkPtr arguments = findChildOrCreate(TINYJS_ARGUMENTS_VAR);
|
---|
3954 | if(arguments) arguments = arguments->getVarPtr()->findChild(int2string(Idx));
|
---|
3955 | return arguments ? arguments->getVarPtr() : constScriptVar(Undefined);
|
---|
3956 | }
|
---|
3957 | int CScriptVarScopeFnc::getParameterLength() {
|
---|
3958 | return getArgumentsLength();
|
---|
3959 | }
|
---|
3960 | int CScriptVarScopeFnc::getArgumentsLength() {
|
---|
3961 | CScriptVarLinkPtr arguments = findChild(TINYJS_ARGUMENTS_VAR);
|
---|
3962 | if(arguments) arguments = arguments->getVarPtr()->findChild("length");
|
---|
3963 | return arguments ? arguments.getter()->toNumber().toInt32() : 0;
|
---|
3964 | }
|
---|
3965 |
|
---|
3966 | void CScriptVarScopeFnc::throwError( ERROR_TYPES ErrorType, const string &message ) {
|
---|
3967 | throw newScriptVarError(context, ErrorType, message.c_str());
|
---|
3968 | }
|
---|
3969 |
|
---|
3970 |
|
---|
3971 | //////////////////////////////////////////////////////////////////////////
|
---|
3972 | /// CScriptVarScopeLet
|
---|
3973 | //////////////////////////////////////////////////////////////////////////
|
---|
3974 |
|
---|
3975 | declare_dummy_t(ScopeLet);
|
---|
3976 | CScriptVarScopeLet::CScriptVarScopeLet(const CScriptVarScopePtr &Parent) // constructor for LetScope
|
---|
3977 | : CScriptVarScope(Parent->getContext()), parent(addChild(TINYJS_SCOPE_PARENT_VAR, Parent, 0))
|
---|
3978 | , letExpressionInitMode(false) {}
|
---|
3979 |
|
---|
3980 | CScriptVarScopeLet::~CScriptVarScopeLet() {}
|
---|
3981 | CScriptVarPtr CScriptVarScopeLet::scopeVar() { // to create var like: var a = ...
|
---|
3982 | return getParent()->scopeVar();
|
---|
3983 | }
|
---|
3984 | CScriptVarScopePtr CScriptVarScopeLet::getParent() { return (CScriptVarPtr)parent; }
|
---|
3985 | CScriptVarLinkWorkPtr CScriptVarScopeLet::findInScopes(const string &childName) {
|
---|
3986 | CScriptVarLinkWorkPtr ret;
|
---|
3987 | if(letExpressionInitMode) {
|
---|
3988 | return getParent()->findInScopes(childName);
|
---|
3989 | } else {
|
---|
3990 | ret = findChild(childName);
|
---|
3991 | if( !ret ) ret = getParent()->findInScopes(childName);
|
---|
3992 | }
|
---|
3993 | return ret;
|
---|
3994 | }
|
---|
3995 |
|
---|
3996 |
|
---|
3997 | //////////////////////////////////////////////////////////////////////////
|
---|
3998 | /// CScriptVarScopeWith
|
---|
3999 | //////////////////////////////////////////////////////////////////////////
|
---|
4000 |
|
---|
4001 | declare_dummy_t(ScopeWith);
|
---|
4002 | CScriptVarScopeWith::~CScriptVarScopeWith() {}
|
---|
4003 | CScriptVarPtr CScriptVarScopeWith::scopeLet() { // to create var like: let a = ...
|
---|
4004 | return getParent()->scopeLet();
|
---|
4005 | }
|
---|
4006 | CScriptVarLinkWorkPtr CScriptVarScopeWith::findInScopes(const string &childName) {
|
---|
4007 | if(childName == "this") return with;
|
---|
4008 | CScriptVarLinkWorkPtr ret = with->getVarPtr()->findChild(childName);
|
---|
4009 | if( !ret ) {
|
---|
4010 | ret = with->getVarPtr()->findChildInPrototypeChain(childName);
|
---|
4011 | if(ret) {
|
---|
4012 | ret(ret->getVarPtr(), ret->getName()); // recreate ret
|
---|
4013 | ret.setReferencedOwner(with->getVarPtr()); // fake referenced Owner
|
---|
4014 | }
|
---|
4015 | }
|
---|
4016 | if( !ret ) ret = getParent()->findInScopes(childName);
|
---|
4017 | return ret;
|
---|
4018 | }
|
---|
4019 |
|
---|
4020 |
|
---|
4021 | //////////////////////////////////////////////////////////////////////////
|
---|
4022 | /// CTinyJS
|
---|
4023 | //////////////////////////////////////////////////////////////////////////
|
---|
4024 |
|
---|
4025 | extern "C" void _registerFunctions(CTinyJS *tinyJS);
|
---|
4026 | extern "C" void _registerStringFunctions(CTinyJS *tinyJS);
|
---|
4027 | extern "C" void _registerMathFunctions(CTinyJS *tinyJS);
|
---|
4028 |
|
---|
4029 | CTinyJS::CTinyJS() {
|
---|
4030 | CScriptVarPtr var;
|
---|
4031 | t = 0;
|
---|
4032 | haveTry = false;
|
---|
4033 | first = 0;
|
---|
4034 | uniqueID = 0;
|
---|
4035 |
|
---|
4036 |
|
---|
4037 | //////////////////////////////////////////////////////////////////////////
|
---|
4038 | // Object-Prototype
|
---|
4039 | // must be created as first object because this prototype is the base of all objects
|
---|
4040 | objectPrototype = newScriptVar(Object);
|
---|
4041 |
|
---|
4042 | // all objects have a prototype. Also the prototype of prototypes
|
---|
4043 | objectPrototype->addChild(TINYJS___PROTO___VAR, objectPrototype, 0);
|
---|
4044 |
|
---|
4045 | //////////////////////////////////////////////////////////////////////////
|
---|
4046 | // Function-Prototype
|
---|
4047 | // must be created as second object because this is the base of all functions (also constructors)
|
---|
4048 | functionPrototype = newScriptVar(Object);
|
---|
4049 |
|
---|
4050 |
|
---|
4051 | //////////////////////////////////////////////////////////////////////////
|
---|
4052 | // Scopes
|
---|
4053 | root = ::newScriptVar(this, Scope);
|
---|
4054 | scopes.push_back(root);
|
---|
4055 |
|
---|
4056 | //////////////////////////////////////////////////////////////////////////
|
---|
4057 | // Add built-in classes
|
---|
4058 | //////////////////////////////////////////////////////////////////////////
|
---|
4059 | // Object
|
---|
4060 | var = addNative("function Object()", this, &CTinyJS::native_Object, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4061 | objectPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4062 | addNative("function Object.getPrototypeOf(obj)", this, &CTinyJS::native_Object_getPrototypeOf);
|
---|
4063 | addNative("function Object.preventExtensions(obj)", this, &CTinyJS::native_Object_setObjectSecure);
|
---|
4064 | addNative("function Object.isExtensible(obj)", this, &CTinyJS::native_Object_isSecureObject);
|
---|
4065 | addNative("function Object.seel(obj)", this, &CTinyJS::native_Object_setObjectSecure, (void*)1);
|
---|
4066 | addNative("function Object.isSealed(obj)", this, &CTinyJS::native_Object_isSecureObject, (void*)1);
|
---|
4067 | addNative("function Object.freeze(obj)", this, &CTinyJS::native_Object_setObjectSecure, (void*)2);
|
---|
4068 | addNative("function Object.isFrozen(obj)", this, &CTinyJS::native_Object_isSecureObject, (void*)2);
|
---|
4069 | addNative("function Object.keys(obj)", this, &CTinyJS::native_Object_keys);
|
---|
4070 | addNative("function Object.getOwnPropertyNames(obj)", this, &CTinyJS::native_Object_keys, (void*)1);
|
---|
4071 | addNative("function Object.getOwnPropertyDescriptor(obj,name)", this, &CTinyJS::native_Object_getOwnPropertyDescriptor);
|
---|
4072 | addNative("function Object.defineProperty(obj,name,attributes)", this, &CTinyJS::native_Object_defineProperty);
|
---|
4073 | addNative("function Object.defineProperties(obj,properties)", this, &CTinyJS::native_Object_defineProperties);
|
---|
4074 | addNative("function Object.create(obj,properties)", this, &CTinyJS::native_Object_defineProperties, (void*)1);
|
---|
4075 |
|
---|
4076 | addNative("function Object.prototype.hasOwnProperty(prop)", this, &CTinyJS::native_Object_prototype_hasOwnProperty);
|
---|
4077 | objectPrototype_valueOf = addNative("function Object.prototype.valueOf()", this, &CTinyJS::native_Object_prototype_valueOf);
|
---|
4078 | objectPrototype_toString = addNative("function Object.prototype.toString(radix)", this, &CTinyJS::native_Object_prototype_toString);
|
---|
4079 | pseudo_refered.push_back(&objectPrototype);
|
---|
4080 | pseudo_refered.push_back(&objectPrototype_valueOf);
|
---|
4081 | pseudo_refered.push_back(&objectPrototype_toString);
|
---|
4082 |
|
---|
4083 | //////////////////////////////////////////////////////////////////////////
|
---|
4084 | // Array
|
---|
4085 | var = addNative("function Array()", this, &CTinyJS::native_Array, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4086 | arrayPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4087 | arrayPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
4088 | arrayPrototype->addChild("toString", objectPrototype_toString);
|
---|
4089 | pseudo_refered.push_back(&arrayPrototype);
|
---|
4090 | var = addNative("function Array.__constructor__()", this, &CTinyJS::native_Array, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
4091 | var->getFunctionData()->name = "Array";
|
---|
4092 | //////////////////////////////////////////////////////////////////////////
|
---|
4093 | // String
|
---|
4094 | var = addNative("function String()", this, &CTinyJS::native_String, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4095 | stringPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4096 | stringPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
4097 | stringPrototype->addChild("toString", objectPrototype_toString);
|
---|
4098 | pseudo_refered.push_back(&stringPrototype);
|
---|
4099 | var = addNative("function String.__constructor__()", this, &CTinyJS::native_String, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
4100 | var->getFunctionData()->name = "String";
|
---|
4101 |
|
---|
4102 | //////////////////////////////////////////////////////////////////////////
|
---|
4103 | // RegExp
|
---|
4104 | #ifndef NO_REGEXP
|
---|
4105 | var = addNative("function RegExp()", this, &CTinyJS::native_RegExp, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4106 | regexpPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4107 | regexpPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
4108 | regexpPrototype->addChild("toString", objectPrototype_toString);
|
---|
4109 | pseudo_refered.push_back(®expPrototype);
|
---|
4110 | #endif /* NO_REGEXP */
|
---|
4111 |
|
---|
4112 | //////////////////////////////////////////////////////////////////////////
|
---|
4113 | // Number
|
---|
4114 | var = addNative("function Number()", this, &CTinyJS::native_Number, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4115 | var->addChild("NaN", constNaN = newScriptVarNumber(this, NaN), SCRIPTVARLINK_CONSTANT);
|
---|
4116 | var->addChild("MAX_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits<double>::max()), SCRIPTVARLINK_CONSTANT);
|
---|
4117 | var->addChild("MIN_VALUE", constInfinityPositive = newScriptVarNumber(this, numeric_limits<double>::min()), SCRIPTVARLINK_CONSTANT);
|
---|
4118 | var->addChild("POSITIVE_INFINITY", constInfinityPositive = newScriptVarNumber(this, InfinityPositive), SCRIPTVARLINK_CONSTANT);
|
---|
4119 | var->addChild("NEGATIVE_INFINITY", constInfinityNegative = newScriptVarNumber(this, InfinityNegative), SCRIPTVARLINK_CONSTANT);
|
---|
4120 | numberPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4121 | numberPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
4122 | numberPrototype->addChild("toString", objectPrototype_toString);
|
---|
4123 | pseudo_refered.push_back(&numberPrototype);
|
---|
4124 | pseudo_refered.push_back(&constNaN);
|
---|
4125 | pseudo_refered.push_back(&constInfinityPositive);
|
---|
4126 | pseudo_refered.push_back(&constInfinityNegative);
|
---|
4127 | var = addNative("function Number.__constructor__()", this, &CTinyJS::native_Number, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
4128 | var->getFunctionData()->name = "Number";
|
---|
4129 |
|
---|
4130 | //////////////////////////////////////////////////////////////////////////
|
---|
4131 | // Boolean
|
---|
4132 | var = addNative("function Boolean()", this, &CTinyJS::native_Boolean, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4133 | booleanPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4134 | booleanPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
4135 | booleanPrototype->addChild("toString", objectPrototype_toString);
|
---|
4136 | pseudo_refered.push_back(&booleanPrototype);
|
---|
4137 | var = addNative("function Boolean.__constructor__()", this, &CTinyJS::native_Boolean, (void*)1, SCRIPTVARLINK_CONSTANT);
|
---|
4138 | var->getFunctionData()->name = "Boolean";
|
---|
4139 |
|
---|
4140 | //////////////////////////////////////////////////////////////////////////
|
---|
4141 | // Iterator
|
---|
4142 | var = addNative("function Iterator(obj,mode)", this, &CTinyJS::native_Iterator, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4143 | iteratorPrototype = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4144 | pseudo_refered.push_back(&iteratorPrototype);
|
---|
4145 |
|
---|
4146 | //////////////////////////////////////////////////////////////////////////
|
---|
4147 | // Function
|
---|
4148 | var = addNative("function Function(params, body)", this, &CTinyJS::native_Function, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4149 | var->addChildOrReplace(TINYJS_PROTOTYPE_CLASS, functionPrototype);
|
---|
4150 | addNative("function Function.prototype.call(objc)", this, &CTinyJS::native_Function_prototype_call);
|
---|
4151 | addNative("function Function.prototype.apply(objc, args)", this, &CTinyJS::native_Function_prototype_apply);
|
---|
4152 | addNative("function Function.prototype.bind(objc, args)", this, &CTinyJS::native_Function_prototype_bind);
|
---|
4153 | functionPrototype->addChild("valueOf", objectPrototype_valueOf);
|
---|
4154 | functionPrototype->addChild("toString", objectPrototype_toString);
|
---|
4155 | pseudo_refered.push_back(&functionPrototype);
|
---|
4156 |
|
---|
4157 | //////////////////////////////////////////////////////////////////////////
|
---|
4158 | // Error
|
---|
4159 | var = addNative("function Error(message, fileName, lineNumber, column)", this, &CTinyJS::native_Error, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4160 | errorPrototypes[Error] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4161 | errorPrototypes[Error]->addChild("message", newScriptVar(""));
|
---|
4162 | errorPrototypes[Error]->addChild("name", newScriptVar("Error"));
|
---|
4163 | errorPrototypes[Error]->addChild("fileName", newScriptVar(""));
|
---|
4164 | errorPrototypes[Error]->addChild("lineNumber", newScriptVar(-1)); // -1 means not viable
|
---|
4165 | errorPrototypes[Error]->addChild("column", newScriptVar(-1)); // -1 means not viable
|
---|
4166 |
|
---|
4167 | var = addNative("function EvalError(message, fileName, lineNumber, column)", this, &CTinyJS::native_EvalError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4168 | errorPrototypes[EvalError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4169 | errorPrototypes[EvalError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
4170 | errorPrototypes[EvalError]->addChild("name", newScriptVar("EvalError"));
|
---|
4171 |
|
---|
4172 | var = addNative("function RangeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_RangeError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4173 | errorPrototypes[RangeError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4174 | errorPrototypes[RangeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
4175 | errorPrototypes[RangeError]->addChild("name", newScriptVar("RangeError"));
|
---|
4176 |
|
---|
4177 | var = addNative("function ReferenceError(message, fileName, lineNumber, column)", this, &CTinyJS::native_ReferenceError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4178 | errorPrototypes[ReferenceError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4179 | errorPrototypes[ReferenceError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
4180 | errorPrototypes[ReferenceError]->addChild("name", newScriptVar("ReferenceError"));
|
---|
4181 |
|
---|
4182 | var = addNative("function SyntaxError(message, fileName, lineNumber, column)", this, &CTinyJS::native_SyntaxError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4183 | errorPrototypes[SyntaxError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4184 | errorPrototypes[SyntaxError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
4185 | errorPrototypes[SyntaxError]->addChild("name", newScriptVar("SyntaxError"));
|
---|
4186 |
|
---|
4187 | var = addNative("function TypeError(message, fileName, lineNumber, column)", this, &CTinyJS::native_TypeError, 0, SCRIPTVARLINK_CONSTANT);
|
---|
4188 | errorPrototypes[TypeError] = var->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4189 | errorPrototypes[TypeError]->addChildOrReplace(TINYJS___PROTO___VAR, errorPrototypes[Error], SCRIPTVARLINK_WRITABLE);
|
---|
4190 | errorPrototypes[TypeError]->addChild("name", newScriptVar("TypeError"));
|
---|
4191 |
|
---|
4192 | //////////////////////////////////////////////////////////////////////////
|
---|
4193 | // add global built-in vars & constants
|
---|
4194 | root->addChild("undefined", constUndefined = newScriptVarUndefined(this), SCRIPTVARLINK_CONSTANT);
|
---|
4195 | pseudo_refered.push_back(&constUndefined);
|
---|
4196 | constNull = newScriptVarNull(this); pseudo_refered.push_back(&constNull);
|
---|
4197 | root->addChild("NaN", constNaN, SCRIPTVARLINK_CONSTANT);
|
---|
4198 | root->addChild("Infinity", constInfinityPositive, SCRIPTVARLINK_CONSTANT);
|
---|
4199 | root->addChild("StopIteration", constStopIteration=newScriptVar(Object, var=newScriptVar(Object)), SCRIPTVARLINK_CONSTANT);
|
---|
4200 | constStopIteration->addChild(TINYJS_PROTOTYPE_CLASS, var, SCRIPTVARLINK_CONSTANT); pseudo_refered.push_back(&constStopIteration);
|
---|
4201 | constNegativZero = newScriptVarNumber(this, NegativeZero); pseudo_refered.push_back(&constNegativZero);
|
---|
4202 | constFalse = newScriptVarBool(this, false); pseudo_refered.push_back(&constFalse);
|
---|
4203 | constTrue = newScriptVarBool(this, true); pseudo_refered.push_back(&constTrue);
|
---|
4204 |
|
---|
4205 | //////////////////////////////////////////////////////////////////////////
|
---|
4206 | // add global functions
|
---|
4207 | addNative("function eval(jsCode)", this, &CTinyJS::native_eval);
|
---|
4208 | addNative("function isNaN(objc)", this, &CTinyJS::native_isNAN);
|
---|
4209 | addNative("function isFinite(objc)", this, &CTinyJS::native_isFinite);
|
---|
4210 | addNative("function parseInt(string, radix)", this, &CTinyJS::native_parseInt);
|
---|
4211 | addNative("function parseFloat(string)", this, &CTinyJS::native_parseFloat);
|
---|
4212 |
|
---|
4213 |
|
---|
4214 | addNative("function JSON.parse(text, reviver)", this, &CTinyJS::native_JSON_parse);
|
---|
4215 |
|
---|
4216 | _registerFunctions(this);
|
---|
4217 | _registerStringFunctions(this);
|
---|
4218 | _registerMathFunctions(this);
|
---|
4219 | }
|
---|
4220 |
|
---|
4221 | CTinyJS::~CTinyJS() {
|
---|
4222 | ASSERT(!t);
|
---|
4223 | for(vector<CScriptVarPtr*>::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it)
|
---|
4224 | **it = CScriptVarPtr();
|
---|
4225 | for(int i=Error; i<ERROR_COUNT; i++)
|
---|
4226 | errorPrototypes[i] = CScriptVarPtr();
|
---|
4227 | root->removeAllChildren();
|
---|
4228 | scopes.clear();
|
---|
4229 | ClearUnreferedVars();
|
---|
4230 | root = CScriptVarPtr();
|
---|
4231 | #ifdef _DEBUG
|
---|
4232 | for(CScriptVar *p = first; p; p=p->next)
|
---|
4233 | printf("%p\n", p);
|
---|
4234 | #endif
|
---|
4235 | #if DEBUG_MEMORY
|
---|
4236 | show_allocated();
|
---|
4237 | #endif
|
---|
4238 | }
|
---|
4239 |
|
---|
4240 | //////////////////////////////////////////////////////////////////////////
|
---|
4241 | /// throws an Error & Exception
|
---|
4242 | //////////////////////////////////////////////////////////////////////////
|
---|
4243 |
|
---|
4244 | void CTinyJS::throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const string &message ) {
|
---|
4245 | if(execute && haveTry) {
|
---|
4246 | execute.set(CScriptResult::Throw, newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), t->currentLine(), t->currentColumn()));
|
---|
4247 | return;
|
---|
4248 | }
|
---|
4249 | throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn());
|
---|
4250 | }
|
---|
4251 | void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message ) {
|
---|
4252 | throw new CScriptException(ErrorType, message, t->currentFile, t->currentLine(), t->currentColumn());
|
---|
4253 | }
|
---|
4254 |
|
---|
4255 | void CTinyJS::throwError(CScriptResult &execute, ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){
|
---|
4256 | if(execute && haveTry) {
|
---|
4257 | execute.set(CScriptResult::Throw, newScriptVarError(this, ErrorType, message.c_str(), t->currentFile.c_str(), Pos.currentLine(), Pos.currentColumn()));
|
---|
4258 | return;
|
---|
4259 | }
|
---|
4260 | throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn());
|
---|
4261 | }
|
---|
4262 | void CTinyJS::throwException(ERROR_TYPES ErrorType, const string &message, CScriptTokenizer::ScriptTokenPosition &Pos ){
|
---|
4263 | throw new CScriptException(ErrorType, message, t->currentFile, Pos.currentLine(), Pos.currentColumn());
|
---|
4264 | }
|
---|
4265 |
|
---|
4266 | //////////////////////////////////////////////////////////////////////////
|
---|
4267 |
|
---|
4268 | void CTinyJS::trace() {
|
---|
4269 | root->trace();
|
---|
4270 | }
|
---|
4271 |
|
---|
4272 | void CTinyJS::execute(CScriptTokenizer &Tokenizer) {
|
---|
4273 | evaluateComplex(Tokenizer);
|
---|
4274 | }
|
---|
4275 |
|
---|
4276 | void CTinyJS::execute(const char *Code, const string &File, int Line, int Column) {
|
---|
4277 | evaluateComplex(Code, File, Line, Column);
|
---|
4278 | }
|
---|
4279 |
|
---|
4280 | void CTinyJS::execute(const string &Code, const string &File, int Line, int Column) {
|
---|
4281 | evaluateComplex(Code, File, Line, Column);
|
---|
4282 | }
|
---|
4283 |
|
---|
4284 | CScriptVarLinkPtr CTinyJS::evaluateComplex(CScriptTokenizer &Tokenizer) {
|
---|
4285 | t = &Tokenizer;
|
---|
4286 | CScriptResult execute;
|
---|
4287 | try {
|
---|
4288 | do {
|
---|
4289 | execute_statement(execute);
|
---|
4290 | while (t->tk==';') t->match(';'); // skip empty statements
|
---|
4291 | } while (t->tk!=LEX_EOF);
|
---|
4292 | } catch (...) {
|
---|
4293 | haveTry = false;
|
---|
4294 | t=0; // clean up Tokenizer
|
---|
4295 | throw; //
|
---|
4296 | }
|
---|
4297 | t=0;
|
---|
4298 | ClearUnreferedVars(execute.value);
|
---|
4299 |
|
---|
4300 | uint32_t UniqueID = getUniqueID();
|
---|
4301 | setTemporaryID_recursive(UniqueID);
|
---|
4302 | if(execute.value) execute.value->setTemporaryID_recursive(UniqueID);
|
---|
4303 | for(CScriptVar *p = first; p; p=p->next)
|
---|
4304 | {
|
---|
4305 | if(p->temporaryID != UniqueID)
|
---|
4306 | printf("%p\n", p);
|
---|
4307 | }
|
---|
4308 |
|
---|
4309 | if (execute.value)
|
---|
4310 | return CScriptVarLinkPtr(execute.value);
|
---|
4311 | // return undefined...
|
---|
4312 | return CScriptVarLinkPtr(constScriptVar(Undefined));
|
---|
4313 | }
|
---|
4314 | CScriptVarLinkPtr CTinyJS::evaluateComplex(const char *Code, const string &File, int Line, int Column) {
|
---|
4315 | CScriptTokenizer Tokenizer(Code, File, Line, Column);
|
---|
4316 | return evaluateComplex(Tokenizer);
|
---|
4317 | }
|
---|
4318 | CScriptVarLinkPtr CTinyJS::evaluateComplex(const string &Code, const string &File, int Line, int Column) {
|
---|
4319 | CScriptTokenizer Tokenizer(Code.c_str(), File, Line, Column);
|
---|
4320 | return evaluateComplex(Tokenizer);
|
---|
4321 | }
|
---|
4322 |
|
---|
4323 | string CTinyJS::evaluate(CScriptTokenizer &Tokenizer) {
|
---|
4324 | return evaluateComplex(Tokenizer)->toString();
|
---|
4325 | }
|
---|
4326 | string CTinyJS::evaluate(const char *Code, const string &File, int Line, int Column) {
|
---|
4327 | return evaluateComplex(Code, File, Line, Column)->toString();
|
---|
4328 | }
|
---|
4329 | string CTinyJS::evaluate(const string &Code, const string &File, int Line, int Column) {
|
---|
4330 | return evaluate(Code.c_str(), File, Line, Column);
|
---|
4331 | }
|
---|
4332 |
|
---|
4333 | CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata, int LinkFlags) {
|
---|
4334 | return addNative(funcDesc, ::newScriptVar(this, ptr, userdata), LinkFlags);
|
---|
4335 | }
|
---|
4336 |
|
---|
4337 | CScriptVarFunctionNativePtr CTinyJS::addNative(const string &funcDesc, CScriptVarFunctionNativePtr Var, int LinkFlags) {
|
---|
4338 | CScriptLex lex(funcDesc.c_str());
|
---|
4339 | CScriptVarPtr base = root;
|
---|
4340 |
|
---|
4341 | lex.match(LEX_R_FUNCTION);
|
---|
4342 | string funcName = lex.tkStr;
|
---|
4343 | lex.match(LEX_ID);
|
---|
4344 | /* Check for dots, we might want to do something like function String.substring ... */
|
---|
4345 | while (lex.tk == '.') {
|
---|
4346 | lex.match('.');
|
---|
4347 | CScriptVarLinkPtr link = base->findChild(funcName);
|
---|
4348 | // if it doesn't exist, make an object class
|
---|
4349 | if (!link) link = base->addChild(funcName, newScriptVar(Object));
|
---|
4350 | base = link->getVarPtr();
|
---|
4351 | funcName = lex.tkStr;
|
---|
4352 | lex.match(LEX_ID);
|
---|
4353 | }
|
---|
4354 |
|
---|
4355 | auto_ptr<CScriptTokenDataFnc> pFunctionData(new CScriptTokenDataFnc);
|
---|
4356 | pFunctionData->name = funcName;
|
---|
4357 | lex.match('(');
|
---|
4358 | while (lex.tk!=')') {
|
---|
4359 | pFunctionData->arguments.push_back(CScriptToken(LEX_ID, lex.tkStr));
|
---|
4360 | lex.match(LEX_ID);
|
---|
4361 | if (lex.tk!=')') lex.match(',',')');
|
---|
4362 | }
|
---|
4363 | lex.match(')');
|
---|
4364 | Var->setFunctionData(pFunctionData.release());
|
---|
4365 | Var->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE);
|
---|
4366 |
|
---|
4367 | base->addChild(funcName, Var, LinkFlags);
|
---|
4368 | return Var;
|
---|
4369 |
|
---|
4370 | }
|
---|
4371 |
|
---|
4372 | CScriptVarLinkWorkPtr CTinyJS::parseFunctionDefinition(const CScriptToken &FncToken) {
|
---|
4373 | const CScriptTokenDataFnc &Fnc = FncToken.Fnc();
|
---|
4374 | // string fncName = (FncToken.token == LEX_T_FUNCTION_OPERATOR) ? TINYJS_TEMP_NAME : Fnc.name;
|
---|
4375 | CScriptVarLinkWorkPtr funcVar(newScriptVar((CScriptTokenDataFnc*)&Fnc), Fnc.name);
|
---|
4376 | if(scope() != root)
|
---|
4377 | funcVar->getVarPtr()->addChild(TINYJS_FUNCTION_CLOSURE_VAR, scope(), 0);
|
---|
4378 | funcVar->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE);
|
---|
4379 | return funcVar;
|
---|
4380 | }
|
---|
4381 |
|
---|
4382 | CScriptVarLinkWorkPtr CTinyJS::parseFunctionsBodyFromString(const string &ArgumentList, const string &FncBody) {
|
---|
4383 | string Fnc = "function ("+ArgumentList+"){"+FncBody+"}";
|
---|
4384 | CScriptTokenizer tokenizer(Fnc.c_str());
|
---|
4385 | return parseFunctionDefinition(tokenizer.getToken());
|
---|
4386 | }
|
---|
4387 | CScriptVarPtr CTinyJS::callFunction(const CScriptVarFunctionPtr &Function, vector<CScriptVarPtr> &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) {
|
---|
4388 | CScriptResult execute;
|
---|
4389 | CScriptVarPtr retVar = callFunction(execute, Function, Arguments, This, newThis);
|
---|
4390 | execute.cThrow();
|
---|
4391 | return retVar;
|
---|
4392 | }
|
---|
4393 |
|
---|
4394 | CScriptVarPtr CTinyJS::callFunction(CScriptResult &execute, const CScriptVarFunctionPtr &Function, vector<CScriptVarPtr> &Arguments, const CScriptVarPtr &This, CScriptVarPtr *newThis) {
|
---|
4395 | ASSERT(Function && Function->isFunction());
|
---|
4396 |
|
---|
4397 | if(Function->isBounded()) return CScriptVarFunctionBoundedPtr(Function)->callFunction(execute, Arguments, This, newThis);
|
---|
4398 |
|
---|
4399 | CScriptTokenDataFnc *Fnc = Function->getFunctionData();
|
---|
4400 | CScriptVarScopeFncPtr functionRoot(::newScriptVar(this, ScopeFnc, CScriptVarPtr(Function->findChild(TINYJS_FUNCTION_CLOSURE_VAR))));
|
---|
4401 | if(Fnc->name.size()) functionRoot->addChild(Fnc->name, Function);
|
---|
4402 | functionRoot->addChild("this", This);
|
---|
4403 | CScriptVarPtr arguments = functionRoot->addChild(TINYJS_ARGUMENTS_VAR, newScriptVar(Object));
|
---|
4404 |
|
---|
4405 | CScriptResult function_execute;
|
---|
4406 | int length_proto = Fnc->arguments.size();
|
---|
4407 | int length_arguments = Arguments.size();
|
---|
4408 | int length = max(length_proto, length_arguments);
|
---|
4409 | for(int arguments_idx = 0; arguments_idx<length; ++arguments_idx) {
|
---|
4410 | string arguments_idx_str = int2string(arguments_idx);
|
---|
4411 | CScriptVarLinkWorkPtr value;
|
---|
4412 | if(arguments_idx < length_arguments) {
|
---|
4413 | value = arguments->addChild(arguments_idx_str, Arguments[arguments_idx]);
|
---|
4414 | } else {
|
---|
4415 | value = constScriptVar(Undefined);
|
---|
4416 | }
|
---|
4417 | if(arguments_idx < length_proto) {
|
---|
4418 | CScriptToken &FncArguments = Fnc->arguments[arguments_idx];
|
---|
4419 | if(FncArguments.token == LEX_ID)
|
---|
4420 | functionRoot->addChildOrReplace(FncArguments.String(), value);
|
---|
4421 | else
|
---|
4422 | assign_destructuring_var(functionRoot, FncArguments.DestructuringVar(), value, function_execute);
|
---|
4423 | }
|
---|
4424 | }
|
---|
4425 | arguments->addChild("length", newScriptVar(length_arguments));
|
---|
4426 |
|
---|
4427 | // execute function!
|
---|
4428 | // add the function's execute space to the symbol table so we can recurse
|
---|
4429 | CScopeControl ScopeControl(this);
|
---|
4430 | ScopeControl.addFncScope(functionRoot);
|
---|
4431 | if (Function->isNative()) {
|
---|
4432 | try {
|
---|
4433 | CScriptVarFunctionNativePtr(Function)->callFunction(functionRoot);
|
---|
4434 | CScriptVarLinkPtr ret = functionRoot->findChild(TINYJS_RETURN_VAR);
|
---|
4435 | function_execute.set(CScriptResult::Return, ret ? CScriptVarPtr(ret) : constUndefined);
|
---|
4436 | } catch (CScriptVarPtr v) {
|
---|
4437 | if(haveTry) {
|
---|
4438 | function_execute.set(CScriptResult::Throw, v);
|
---|
4439 | } else if(v->isError()) {
|
---|
4440 | CScriptException *err = CScriptVarErrorPtr(v)->toCScriptException();
|
---|
4441 | if(err->fileName.empty()) err->fileName = "native function '"+Function->getFunctionData()->name+"'";
|
---|
4442 | throw err;
|
---|
4443 | }
|
---|
4444 | else
|
---|
4445 | throw new CScriptException(Error, v->toString(function_execute)+"' in: native function '"+Function->getFunctionData()->name+"'");
|
---|
4446 | }
|
---|
4447 | } else {
|
---|
4448 | /* we just want to execute the block, but something could
|
---|
4449 | * have messed up and left us with the wrong ScriptLex, so
|
---|
4450 | * we want to be careful here... */
|
---|
4451 | string oldFile = t->currentFile;
|
---|
4452 | t->currentFile = Fnc->file;
|
---|
4453 | t->pushTokenScope(Fnc->body);
|
---|
4454 | if(Fnc->body.front().token == '{')
|
---|
4455 | execute_block(function_execute);
|
---|
4456 | else {
|
---|
4457 | CScriptVarPtr ret = execute_base(function_execute);
|
---|
4458 | if(function_execute) function_execute.set(CScriptResult::Return, ret);
|
---|
4459 | }
|
---|
4460 | t->currentFile = oldFile;
|
---|
4461 |
|
---|
4462 | // because return will probably have called this, and set execute to false
|
---|
4463 | }
|
---|
4464 | if(function_execute.isReturnNormal()) {
|
---|
4465 | if(newThis) *newThis = functionRoot->findChild("this");
|
---|
4466 | if(function_execute.isReturn()) {
|
---|
4467 | CScriptVarPtr ret = function_execute.value;
|
---|
4468 | return ret;
|
---|
4469 | }
|
---|
4470 | } else
|
---|
4471 | execute = function_execute;
|
---|
4472 | return constScriptVar(Undefined);
|
---|
4473 | }
|
---|
4474 |
|
---|
4475 | CScriptVarPtr CTinyJS::mathsOp(CScriptResult &execute, const CScriptVarPtr &A, const CScriptVarPtr &B, int op) {
|
---|
4476 | if(!execute) return constUndefined;
|
---|
4477 | if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
|
---|
4478 | // check type first
|
---|
4479 | if( (A->getVarType() == B->getVarType()) ^ (op == LEX_TYPEEQUAL)) return constFalse;
|
---|
4480 | // check value second
|
---|
4481 | return mathsOp(execute, A, B, op == LEX_TYPEEQUAL ? LEX_EQUAL : LEX_NEQUAL);
|
---|
4482 | }
|
---|
4483 | if (!A->isPrimitive() && !B->isPrimitive()) { // Objects both
|
---|
4484 | // check pointers
|
---|
4485 | switch (op) {
|
---|
4486 | case LEX_EQUAL: return constScriptVar(A==B);
|
---|
4487 | case LEX_NEQUAL: return constScriptVar(A!=B);
|
---|
4488 | }
|
---|
4489 | }
|
---|
4490 |
|
---|
4491 | CScriptVarPtr a = A->toPrimitive_hintNumber(execute);
|
---|
4492 | CScriptVarPtr b = B->toPrimitive_hintNumber(execute);
|
---|
4493 | if(!execute) return constUndefined;
|
---|
4494 | // do maths...
|
---|
4495 | bool a_isString = a->isString();
|
---|
4496 | bool b_isString = b->isString();
|
---|
4497 | // both a String or one a String and op='+'
|
---|
4498 | if( (a_isString && b_isString) || ((a_isString || b_isString) && op == '+')) {
|
---|
4499 | string da = a->isNull() ? "" : a->toString(execute);
|
---|
4500 | string db = b->isNull() ? "" : b->toString(execute);
|
---|
4501 | switch (op) {
|
---|
4502 | case '+': return newScriptVar(da+db);
|
---|
4503 | case LEX_EQUAL: return constScriptVar(da==db);
|
---|
4504 | case LEX_NEQUAL: return constScriptVar(da!=db);
|
---|
4505 | case '<': return constScriptVar(da<db);
|
---|
4506 | case LEX_LEQUAL: return constScriptVar(da<=db);
|
---|
4507 | case '>': return constScriptVar(da>db);
|
---|
4508 | case LEX_GEQUAL: return constScriptVar(da>=db);
|
---|
4509 | }
|
---|
4510 | }
|
---|
4511 | // special for undefined and null --> every true: undefined==undefined, undefined==null, null==undefined and null=null
|
---|
4512 | else if( (a->isUndefined() || a->isNull()) && (b->isUndefined() || b->isNull()) ) {
|
---|
4513 | switch (op) {
|
---|
4514 | case LEX_EQUAL: return constScriptVar(true);
|
---|
4515 | case LEX_NEQUAL:
|
---|
4516 | case LEX_GEQUAL:
|
---|
4517 | case LEX_LEQUAL:
|
---|
4518 | case '<':
|
---|
4519 | case '>': return constScriptVar(false);
|
---|
4520 | }
|
---|
4521 | }
|
---|
4522 | CNumber da = a->toNumber();
|
---|
4523 | CNumber db = b->toNumber();
|
---|
4524 | switch (op) {
|
---|
4525 | case '+': return a->newScriptVar(da+db);
|
---|
4526 | case '-': return a->newScriptVar(da-db);
|
---|
4527 | case '*': return a->newScriptVar(da*db);
|
---|
4528 | case '/': return a->newScriptVar(da/db);
|
---|
4529 | case '%': return a->newScriptVar(da%db);
|
---|
4530 | case '&': return a->newScriptVar(da.toInt32()&db.toInt32());
|
---|
4531 | case '|': return a->newScriptVar(da.toInt32()|db.toInt32());
|
---|
4532 | case '^': return a->newScriptVar(da.toInt32()^db.toInt32());
|
---|
4533 | case '~': return a->newScriptVar(~da);
|
---|
4534 | case LEX_LSHIFT: return a->newScriptVar(da<<db);
|
---|
4535 | case LEX_RSHIFT: return a->newScriptVar(da>>db);
|
---|
4536 | case LEX_RSHIFTU: return a->newScriptVar(da.ushift(db));
|
---|
4537 | case LEX_EQUAL: return a->constScriptVar(da==db);
|
---|
4538 | case LEX_NEQUAL: return a->constScriptVar(da!=db);
|
---|
4539 | case '<': return a->constScriptVar(da<db);
|
---|
4540 | case LEX_LEQUAL: return a->constScriptVar(da<=db);
|
---|
4541 | case '>': return a->constScriptVar(da>db);
|
---|
4542 | case LEX_GEQUAL: return a->constScriptVar(da>=db);
|
---|
4543 | default: throw new CScriptException("This operation not supported on the int datatype");
|
---|
4544 | }
|
---|
4545 | }
|
---|
4546 |
|
---|
4547 | void CTinyJS::assign_destructuring_var(const CScriptVarPtr &Scope, const CScriptTokenDataDestructuringVar &Objc, const CScriptVarPtr &Val, CScriptResult &execute) {
|
---|
4548 | if(!execute) return;
|
---|
4549 | if(Objc.vars.size() == 1) {
|
---|
4550 | if(Scope)
|
---|
4551 | Scope->addChildOrReplace(Objc.vars.front().second, Val);
|
---|
4552 | else {
|
---|
4553 | CScriptVarLinkWorkPtr v(findInScopes(Objc.vars.front().second));
|
---|
4554 | ASSERT(v==true);
|
---|
4555 | if(v) v->setVarPtr(Val);
|
---|
4556 | }
|
---|
4557 | } else {
|
---|
4558 | vector<CScriptVarPtr> Path(1, Val);
|
---|
4559 | for(DESTRUCTURING_VARS_cit it=Objc.vars.begin()+1; it!=Objc.vars.end(); ++it) {
|
---|
4560 | if(it->second == "}" || it->second == "]")
|
---|
4561 | Path.pop_back();
|
---|
4562 | else {
|
---|
4563 | if(it->second.empty()) continue; // skip empty entries
|
---|
4564 | CScriptVarLinkWorkPtr var = Path.back()->findChildWithStringChars(it->first);
|
---|
4565 | if(var) var = var.getter(execute); else var = constUndefined;
|
---|
4566 | if(!execute) return;
|
---|
4567 | if(it->second == "{" || it->second == "[") {
|
---|
4568 | Path.push_back(var);
|
---|
4569 | } else if(Scope)
|
---|
4570 | Scope->addChildOrReplace(it->second, var);
|
---|
4571 | else {
|
---|
4572 | CScriptVarLinkWorkPtr v(findInScopes(it->second));
|
---|
4573 | ASSERT(v==true);
|
---|
4574 | if(v) v->setVarPtr(var);
|
---|
4575 | }
|
---|
4576 | }
|
---|
4577 | }
|
---|
4578 | }
|
---|
4579 | }
|
---|
4580 |
|
---|
4581 | void CTinyJS::execute_var_init( bool hideLetScope, CScriptResult &execute )
|
---|
4582 | {
|
---|
4583 | for(;;) {
|
---|
4584 | t->check(LEX_T_DESTRUCTURING_VAR);
|
---|
4585 | CScriptTokenDataDestructuringVar &Objc = t->getToken().DestructuringVar();
|
---|
4586 | t->match(LEX_T_DESTRUCTURING_VAR);
|
---|
4587 | if(t->tk == '=') {
|
---|
4588 | t->match('=');
|
---|
4589 | if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(true);
|
---|
4590 | CScriptVarPtr Val = execute_assignment(execute);
|
---|
4591 | if(hideLetScope) CScriptVarScopeLetPtr(scope())->setletExpressionInitMode(false);
|
---|
4592 | assign_destructuring_var(0, Objc, Val, execute);
|
---|
4593 | }
|
---|
4594 | if (t->tk == ',')
|
---|
4595 | t->match(',');
|
---|
4596 | else
|
---|
4597 | break;
|
---|
4598 | }
|
---|
4599 | }
|
---|
4600 | void CTinyJS::execute_destructuring(CScriptTokenDataObjectLiteral &Objc, const CScriptVarPtr &Val, CScriptResult &execute) {
|
---|
4601 | for(vector<CScriptTokenDataObjectLiteral::ELEMENT>::iterator it=Objc.elements.begin(); execute && it!=Objc.elements.end(); ++it) {
|
---|
4602 | if(it->value.empty()) continue;
|
---|
4603 | CScriptVarPtr rhs = Val->findChildWithStringChars(it->id).getter(execute);
|
---|
4604 | if(!rhs) rhs=constUndefined;
|
---|
4605 | if(it->value.front().token == LEX_T_OBJECT_LITERAL && it->value.front().Object().destructuring) {
|
---|
4606 | execute_destructuring(it->value.front().Object(), rhs, execute);
|
---|
4607 | } else {
|
---|
4608 | t->pushTokenScope(it->value);
|
---|
4609 | CScriptVarLinkWorkPtr lhs = execute_condition(execute);
|
---|
4610 | if(lhs->isWritable()) {
|
---|
4611 | if (!lhs->isOwned()) {
|
---|
4612 | CScriptVarPtr fakedOwner = lhs.getReferencedOwner();
|
---|
4613 | if(fakedOwner) {
|
---|
4614 | if(!fakedOwner->isExtensible())
|
---|
4615 | continue;
|
---|
4616 | lhs = fakedOwner->addChildOrReplace(lhs->getName(), lhs);
|
---|
4617 | } else
|
---|
4618 | lhs = root->addChildOrReplace(lhs->getName(), lhs);
|
---|
4619 | }
|
---|
4620 | lhs.setter(execute, rhs);
|
---|
4621 | }
|
---|
4622 | }
|
---|
4623 | }
|
---|
4624 | }
|
---|
4625 |
|
---|
4626 | CScriptVarLinkWorkPtr CTinyJS::execute_literals(CScriptResult &execute) {
|
---|
4627 | switch(t->tk) {
|
---|
4628 | case LEX_ID:
|
---|
4629 | if(execute) {
|
---|
4630 | CScriptVarLinkWorkPtr a(findInScopes(t->tkStr()));
|
---|
4631 | if (!a) {
|
---|
4632 | /* Variable doesn't exist! JavaScript says we should create it
|
---|
4633 | * (we won't add it here. This is done in the assignment operator)*/
|
---|
4634 | if(t->tkStr() == "this")
|
---|
4635 | a = root; // fake this
|
---|
4636 | else
|
---|
4637 | a = CScriptVarLinkPtr(constScriptVar(Undefined), t->tkStr());
|
---|
4638 | }
|
---|
4639 | /*
|
---|
4640 | prvention for assignment to this is now done by the tokenizer
|
---|
4641 | else if(t->tkStr() == "this")
|
---|
4642 | a(a->getVarPtr()); // prevent assign to this
|
---|
4643 | */
|
---|
4644 | t->match(LEX_ID);
|
---|
4645 | return a;
|
---|
4646 | }
|
---|
4647 | t->match(LEX_ID);
|
---|
4648 | break;
|
---|
4649 | case LEX_INT:
|
---|
4650 | {
|
---|
4651 | CScriptVarPtr a = newScriptVar(t->getToken().Int());
|
---|
4652 | a->setExtensible(false);
|
---|
4653 | t->match(LEX_INT);
|
---|
4654 | return a;
|
---|
4655 | }
|
---|
4656 | break;
|
---|
4657 | case LEX_FLOAT:
|
---|
4658 | {
|
---|
4659 | CScriptVarPtr a = newScriptVar(t->getToken().Float());
|
---|
4660 | t->match(LEX_FLOAT);
|
---|
4661 | return a;
|
---|
4662 | }
|
---|
4663 | break;
|
---|
4664 | case LEX_STR:
|
---|
4665 | {
|
---|
4666 | CScriptVarPtr a = newScriptVar(t->getToken().String());
|
---|
4667 | t->match(LEX_STR);
|
---|
4668 | return a;
|
---|
4669 | }
|
---|
4670 | break;
|
---|
4671 | #ifndef NO_REGEXP
|
---|
4672 | case LEX_REGEXP:
|
---|
4673 | {
|
---|
4674 | string::size_type pos = t->getToken().String().find_last_of('/');
|
---|
4675 | string source = t->getToken().String().substr(1, pos-1);
|
---|
4676 | string flags = t->getToken().String().substr(pos+1);
|
---|
4677 | CScriptVarPtr a = newScriptVar(source, flags);
|
---|
4678 | t->match(LEX_REGEXP);
|
---|
4679 | return a;
|
---|
4680 | }
|
---|
4681 | break;
|
---|
4682 | #endif /* NO_REGEXP */
|
---|
4683 | case LEX_T_OBJECT_LITERAL:
|
---|
4684 | if(execute) {
|
---|
4685 | CScriptTokenDataObjectLiteral &Objc = t->getToken().Object();
|
---|
4686 | t->match(LEX_T_OBJECT_LITERAL);
|
---|
4687 | if(Objc.destructuring) {
|
---|
4688 | t->match('=');
|
---|
4689 | CScriptVarPtr a = execute_assignment(execute);
|
---|
4690 | if(execute) execute_destructuring(Objc, a, execute);
|
---|
4691 | return a;
|
---|
4692 | } else {
|
---|
4693 | CScriptVarPtr a = Objc.type==CScriptTokenDataObjectLiteral::OBJECT ? newScriptVar(Object) : newScriptVar(Array);
|
---|
4694 | for(vector<CScriptTokenDataObjectLiteral::ELEMENT>::iterator it=Objc.elements.begin(); execute && it!=Objc.elements.end(); ++it) {
|
---|
4695 | if(it->value.empty()) continue;
|
---|
4696 | CScriptToken &tk = it->value.front();
|
---|
4697 | if(tk.token==LEX_T_GET || tk.token==LEX_T_SET) {
|
---|
4698 | CScriptTokenDataFnc &Fnc = tk.Fnc();
|
---|
4699 | if((tk.token == LEX_T_GET && Fnc.arguments.size()==0) || (tk.token == LEX_T_SET && Fnc.arguments.size()==1)) {
|
---|
4700 | CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(tk);
|
---|
4701 | CScriptVarLinkWorkPtr child = a->findChild(Fnc.name);
|
---|
4702 | if(child && !child->getVarPtr()->isAccessor()) child.clear();
|
---|
4703 | if(!child) child = a->addChildOrReplace(Fnc.name, newScriptVar(Accessor));
|
---|
4704 | child->getVarPtr()->addChildOrReplace((tk.token==LEX_T_GET?TINYJS_ACCESSOR_GET_VAR:TINYJS_ACCESSOR_SET_VAR), funcVar->getVarPtr());
|
---|
4705 | }
|
---|
4706 | } else {
|
---|
4707 | t->pushTokenScope(it->value);
|
---|
4708 | a->addChildOrReplace(it->id, execute_assignment(execute));
|
---|
4709 | while(0);
|
---|
4710 | }
|
---|
4711 | }
|
---|
4712 | return a;
|
---|
4713 | }
|
---|
4714 | } else
|
---|
4715 | t->match(LEX_T_OBJECT_LITERAL);
|
---|
4716 | break;
|
---|
4717 | case LEX_R_LET: // let as expression
|
---|
4718 | if(execute) {
|
---|
4719 | CScopeControl ScopeControl(this);
|
---|
4720 | t->match(LEX_R_LET);
|
---|
4721 | t->match('(');
|
---|
4722 | t->check(LEX_T_FORWARD);
|
---|
4723 | ScopeControl.addLetScope();
|
---|
4724 | execute_statement(execute); // execute forwarder
|
---|
4725 | execute_var_init(true, execute);
|
---|
4726 | t->match(')');
|
---|
4727 | return execute_assignment(execute);
|
---|
4728 | } else {
|
---|
4729 | t->skip(t->getToken().Int());
|
---|
4730 | }
|
---|
4731 | break;
|
---|
4732 | case LEX_T_FUNCTION_OPERATOR:
|
---|
4733 | if(execute) {
|
---|
4734 | CScriptVarLinkWorkPtr a = parseFunctionDefinition(t->getToken());
|
---|
4735 | t->match(LEX_T_FUNCTION_OPERATOR);
|
---|
4736 | return a;
|
---|
4737 | }
|
---|
4738 | t->match(LEX_T_FUNCTION_OPERATOR);
|
---|
4739 | break;
|
---|
4740 | case LEX_R_NEW: // new -> create a new object
|
---|
4741 | if (execute) {
|
---|
4742 | t->match(LEX_R_NEW);
|
---|
4743 | CScriptVarLinkWorkPtr parent = execute_literals(execute);
|
---|
4744 | CScriptVarLinkWorkPtr objClass = execute_member(parent, execute).getter(execute);
|
---|
4745 | if (execute) {
|
---|
4746 | if(objClass->getVarPtr()->isFunction()) {
|
---|
4747 | CScriptVarPtr obj(newScriptVar(Object));
|
---|
4748 | CScriptVarLinkPtr prototype = objClass->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
4749 | if(!prototype || prototype->getVarPtr()->isUndefined() || prototype->getVarPtr()->isNull()) {
|
---|
4750 | prototype = objClass->getVarPtr()->addChild(TINYJS_PROTOTYPE_CLASS, newScriptVar(Object), SCRIPTVARLINK_WRITABLE);
|
---|
4751 | obj->addChildOrReplace(TINYJS___PROTO___VAR, prototype, SCRIPTVARLINK_WRITABLE);
|
---|
4752 | }
|
---|
4753 | CScriptVarLinkPtr constructor = objClass->getVarPtr()->findChild("__constructor__");
|
---|
4754 | if(constructor && constructor->getVarPtr()->isFunction())
|
---|
4755 | objClass = constructor;
|
---|
4756 | vector<CScriptVarPtr> arguments;
|
---|
4757 | if (t->tk == '(') {
|
---|
4758 | t->match('(');
|
---|
4759 | while(t->tk!=')') {
|
---|
4760 | CScriptVarPtr value = execute_assignment(execute).getter(execute);
|
---|
4761 | if (execute)
|
---|
4762 | arguments.push_back(value);
|
---|
4763 | if (t->tk!=')') t->match(',', ')');
|
---|
4764 | }
|
---|
4765 | t->match(')');
|
---|
4766 | }
|
---|
4767 | if(execute) {
|
---|
4768 | CScriptVarPtr returnVar = callFunction(execute, objClass->getVarPtr(), arguments, obj, &obj);
|
---|
4769 | if(returnVar->isObject())
|
---|
4770 | return CScriptVarLinkWorkPtr(returnVar);
|
---|
4771 | return CScriptVarLinkWorkPtr(obj);
|
---|
4772 | }
|
---|
4773 | } else
|
---|
4774 | throwError(execute, TypeError, objClass->getName() + " is not a constructor");
|
---|
4775 | } else
|
---|
4776 | if (t->tk == '(') t->skip(t->getToken().Int());
|
---|
4777 | } else
|
---|
4778 | t->skip(t->getToken().Int());
|
---|
4779 | break;
|
---|
4780 | case LEX_R_TRUE:
|
---|
4781 | t->match(LEX_R_TRUE);
|
---|
4782 | return constScriptVar(true);
|
---|
4783 | case LEX_R_FALSE:
|
---|
4784 | t->match(LEX_R_FALSE);
|
---|
4785 | return constScriptVar(false);
|
---|
4786 | case LEX_R_NULL:
|
---|
4787 | t->match(LEX_R_NULL);
|
---|
4788 | return constScriptVar(Null);
|
---|
4789 | case '(':
|
---|
4790 | if(execute) {
|
---|
4791 | t->match('(');
|
---|
4792 | CScriptVarLinkWorkPtr a = execute_base(execute).getter(execute);
|
---|
4793 | t->match(')');
|
---|
4794 | return a;
|
---|
4795 | } else
|
---|
4796 | t->skip(t->getToken().Int());
|
---|
4797 | break;
|
---|
4798 | case LEX_T_EXCEPTION_VAR:
|
---|
4799 | t->match(LEX_T_EXCEPTION_VAR);
|
---|
4800 | if(execute.value) return execute.value;
|
---|
4801 | break;
|
---|
4802 | default:
|
---|
4803 | t->match(LEX_EOF);
|
---|
4804 | break;
|
---|
4805 | }
|
---|
4806 | return constScriptVar(Undefined);
|
---|
4807 |
|
---|
4808 | }
|
---|
4809 | CScriptVarLinkWorkPtr CTinyJS::execute_member(CScriptVarLinkWorkPtr &parent, CScriptResult &execute) {
|
---|
4810 | CScriptVarLinkWorkPtr a;
|
---|
4811 | parent.swap(a);
|
---|
4812 | if(t->tk == '.' || t->tk == '[') {
|
---|
4813 | while(t->tk == '.' || t->tk == '[') {
|
---|
4814 | parent.swap(a);
|
---|
4815 | a = parent.getter(execute); // a is now the "getted" var
|
---|
4816 | if(execute && (a->getVarPtr()->isUndefined() || a->getVarPtr()->isNull())) {
|
---|
4817 | throwError(execute, ReferenceError, a->getName() + " is " + a->toString(execute));
|
---|
4818 | }
|
---|
4819 | string name;
|
---|
4820 | if(t->tk == '.') {
|
---|
4821 | t->match('.');
|
---|
4822 | name = t->tkStr();
|
---|
4823 | t->match(LEX_ID);
|
---|
4824 | } else {
|
---|
4825 | if(execute) {
|
---|
4826 | t->match('[');
|
---|
4827 | name = execute_expression(execute)->toString(execute);
|
---|
4828 | t->match(']');
|
---|
4829 | } else
|
---|
4830 | t->skip(t->getToken().Int());
|
---|
4831 | }
|
---|
4832 | if (execute) {
|
---|
4833 | CScriptVarPtr aVar = a;
|
---|
4834 | a = aVar->findChildWithPrototypeChain(name);
|
---|
4835 | if(!a) {
|
---|
4836 | a(constScriptVar(Undefined), name);
|
---|
4837 | a.setReferencedOwner(aVar);
|
---|
4838 | }
|
---|
4839 | }
|
---|
4840 | }
|
---|
4841 | }
|
---|
4842 | return a;
|
---|
4843 | }
|
---|
4844 |
|
---|
4845 | CScriptVarLinkWorkPtr CTinyJS::execute_function_call(CScriptResult &execute) {
|
---|
4846 | CScriptVarLinkWorkPtr parent = execute_literals(execute);
|
---|
4847 | CScriptVarLinkWorkPtr a = execute_member(parent, execute);
|
---|
4848 | while (t->tk == '(') {
|
---|
4849 | if (execute) {
|
---|
4850 | if(a->getVarPtr()->isUndefined() || a->getVarPtr()->isNull())
|
---|
4851 | throwError(execute, ReferenceError, a->getName() + " is " + a->toString(execute));
|
---|
4852 | CScriptVarLinkWorkPtr fnc = a.getter(execute);
|
---|
4853 | if (!fnc->getVarPtr()->isFunction())
|
---|
4854 | throwError(execute, TypeError, a->getName() + " is not a function");
|
---|
4855 | t->match('('); // path += '(';
|
---|
4856 |
|
---|
4857 | // grab in all parameters
|
---|
4858 | vector<CScriptVarPtr> arguments;
|
---|
4859 | while(t->tk!=')') {
|
---|
4860 | CScriptVarLinkWorkPtr value = execute_assignment(execute).getter(execute);
|
---|
4861 | // path += (*value)->getString();
|
---|
4862 | if (execute) {
|
---|
4863 | arguments.push_back(value);
|
---|
4864 | }
|
---|
4865 | if (t->tk!=')') { t->match(','); /*path+=',';*/ }
|
---|
4866 | }
|
---|
4867 | t->match(')'); //path+=')';
|
---|
4868 | // setup a return variable
|
---|
4869 | CScriptVarLinkWorkPtr returnVar;
|
---|
4870 | if(execute) {
|
---|
4871 | if (!parent)
|
---|
4872 | parent = findInScopes("this");
|
---|
4873 | // if no parent use the root-scope
|
---|
4874 | CScriptVarPtr This(parent ? parent->getVarPtr() : (CScriptVarPtr )root);
|
---|
4875 | a = callFunction(execute, a->getVarPtr(), arguments, This);
|
---|
4876 | }
|
---|
4877 | } else {
|
---|
4878 | // function, but not executing - just parse args and be done
|
---|
4879 | t->match('(');
|
---|
4880 | while (t->tk != ')') {
|
---|
4881 | CScriptVarLinkWorkPtr value = execute_base(execute);
|
---|
4882 | // if (t->tk!=')') t->match(',');
|
---|
4883 | }
|
---|
4884 | t->match(')');
|
---|
4885 | }
|
---|
4886 | a = execute_member(parent = a, execute);
|
---|
4887 | }
|
---|
4888 | return a;
|
---|
4889 | }
|
---|
4890 | // R->L: Precedence 3 (in-/decrement) ++ --
|
---|
4891 | // R<-L: Precedence 4 (unary) ! ~ + - typeof void delete
|
---|
4892 | bool CTinyJS::execute_unary_rhs(CScriptResult &execute, CScriptVarLinkWorkPtr& a) {
|
---|
4893 | t->match(t->tk);
|
---|
4894 | a = execute_unary(execute).getter(execute);
|
---|
4895 | if(execute) CheckRightHandVar(execute, a);
|
---|
4896 | return execute;
|
---|
4897 | };
|
---|
4898 | CScriptVarLinkWorkPtr CTinyJS::execute_unary(CScriptResult &execute) {
|
---|
4899 | CScriptVarLinkWorkPtr a;
|
---|
4900 | switch(t->tk) {
|
---|
4901 | case '-':
|
---|
4902 | if(execute_unary_rhs(execute, a))
|
---|
4903 | a(newScriptVar(-a->getVarPtr()->toNumber(execute)));
|
---|
4904 | break;
|
---|
4905 | case '+':
|
---|
4906 | if(execute_unary_rhs(execute, a))
|
---|
4907 | a = newScriptVar(a->getVarPtr()->toNumber(execute));
|
---|
4908 | break;
|
---|
4909 | case '!':
|
---|
4910 | if(execute_unary_rhs(execute, a))
|
---|
4911 | a = constScriptVar(!a->getVarPtr()->toBoolean());
|
---|
4912 | break;
|
---|
4913 | case '~':
|
---|
4914 | if(execute_unary_rhs(execute, a))
|
---|
4915 | a = newScriptVar(~a->getVarPtr()->toNumber(execute));
|
---|
4916 | break;
|
---|
4917 | case LEX_R_TYPEOF:
|
---|
4918 | if(execute_unary_rhs(execute, a))
|
---|
4919 | a = newScriptVar(a->getVarPtr()->getVarType());
|
---|
4920 | break;
|
---|
4921 | case LEX_R_VOID:
|
---|
4922 | if(execute_unary_rhs(execute, a))
|
---|
4923 | a = constScriptVar(Undefined);
|
---|
4924 | break;
|
---|
4925 | case LEX_R_DELETE:
|
---|
4926 | t->match(LEX_R_DELETE); // delete
|
---|
4927 | a = execute_unary(execute); // no getter - delete can remove the accessor
|
---|
4928 | if (execute) {
|
---|
4929 | // !!! no right-hand-check by delete
|
---|
4930 | if(a->isOwned() && a->isConfigurable()) {
|
---|
4931 | a->getOwner()->removeLink(a); // removes the link from owner
|
---|
4932 | a = constScriptVar(true);
|
---|
4933 | }
|
---|
4934 | else
|
---|
4935 | a = constScriptVar(false);
|
---|
4936 | }
|
---|
4937 | break;
|
---|
4938 | case LEX_PLUSPLUS:
|
---|
4939 | case LEX_MINUSMINUS:
|
---|
4940 | {
|
---|
4941 | int op = t->tk;
|
---|
4942 | t->match(op); // pre increment/decrement
|
---|
4943 | CScriptTokenizer::ScriptTokenPosition ErrorPos = t->getPos();
|
---|
4944 | a = execute_function_call(execute);
|
---|
4945 | if (execute) {
|
---|
4946 | if(a->getName().empty())
|
---|
4947 | throwError(execute, SyntaxError, string("invalid ")+(op==LEX_PLUSPLUS ? "increment" : "decrement")+" operand", ErrorPos);
|
---|
4948 | else if(!a->isOwned() && !a.hasReferencedOwner() && !a->getName().empty())
|
---|
4949 | throwError(execute, ReferenceError, a->getName() + " is not defined", ErrorPos);
|
---|
4950 | CScriptVarPtr res = newScriptVar(a.getter(execute)->getVarPtr()->toNumber(execute).add(op==LEX_PLUSPLUS ? 1 : -1));
|
---|
4951 | if(a->isWritable()) {
|
---|
4952 | if(!a->isOwned() && a.hasReferencedOwner() && a.getReferencedOwner()->isExtensible())
|
---|
4953 | a.getReferencedOwner()->addChildOrReplace(a->getName(), res);
|
---|
4954 | else
|
---|
4955 | a.setter(execute, res);
|
---|
4956 | }
|
---|
4957 | a = res;
|
---|
4958 | }
|
---|
4959 | }
|
---|
4960 | break;
|
---|
4961 | default:
|
---|
4962 | a = execute_function_call(execute);
|
---|
4963 | break;
|
---|
4964 | }
|
---|
4965 | // post increment/decrement
|
---|
4966 | if (t->tk==LEX_PLUSPLUS || t->tk==LEX_MINUSMINUS) {
|
---|
4967 | int op = t->tk;
|
---|
4968 | t->match(op);
|
---|
4969 | if (execute) {
|
---|
4970 | if(a->getName().empty())
|
---|
4971 | throwError(execute, SyntaxError, string("invalid ")+(op==LEX_PLUSPLUS ? "increment" : "decrement")+" operand", t->getPrevPos());
|
---|
4972 | else if(!a->isOwned() && !a.hasReferencedOwner() && !a->getName().empty())
|
---|
4973 | throwError(execute, ReferenceError, a->getName() + " is not defined", t->getPrevPos());
|
---|
4974 | CNumber num = a.getter(execute)->getVarPtr()->toNumber(execute);
|
---|
4975 | CScriptVarPtr res = newScriptVar(num.add(op==LEX_PLUSPLUS ? 1 : -1));
|
---|
4976 | if(a->isWritable()) {
|
---|
4977 | if(!a->isOwned() && a.hasReferencedOwner() && a.getReferencedOwner()->isExtensible())
|
---|
4978 | a.getReferencedOwner()->addChildOrReplace(a->getName(), res);
|
---|
4979 | else
|
---|
4980 | a.setter(execute, res);
|
---|
4981 | }
|
---|
4982 | a = newScriptVar(num);
|
---|
4983 | }
|
---|
4984 | }
|
---|
4985 | return a;
|
---|
4986 | }
|
---|
4987 |
|
---|
4988 | // L->R: Precedence 5 (term) * / %
|
---|
4989 | CScriptVarLinkWorkPtr CTinyJS::execute_term(CScriptResult &execute) {
|
---|
4990 | CScriptVarLinkWorkPtr a = execute_unary(execute);
|
---|
4991 | if (t->tk=='*' || t->tk=='/' || t->tk=='%') {
|
---|
4992 | CheckRightHandVar(execute, a);
|
---|
4993 | while (t->tk=='*' || t->tk=='/' || t->tk=='%') {
|
---|
4994 | int op = t->tk;
|
---|
4995 | t->match(t->tk);
|
---|
4996 | CScriptVarLinkWorkPtr b = execute_unary(execute); // L->R
|
---|
4997 | if (execute) {
|
---|
4998 | CheckRightHandVar(execute, b);
|
---|
4999 | a = mathsOp(execute, a.getter(execute), b.getter(execute), op);
|
---|
5000 | }
|
---|
5001 | }
|
---|
5002 | }
|
---|
5003 | return a;
|
---|
5004 | }
|
---|
5005 |
|
---|
5006 | // L->R: Precedence 6 (addition/subtraction) + -
|
---|
5007 | CScriptVarLinkWorkPtr CTinyJS::execute_expression(CScriptResult &execute) {
|
---|
5008 | CScriptVarLinkWorkPtr a = execute_term(execute);
|
---|
5009 | if (t->tk=='+' || t->tk=='-') {
|
---|
5010 | CheckRightHandVar(execute, a);
|
---|
5011 | while (t->tk=='+' || t->tk=='-') {
|
---|
5012 | int op = t->tk;
|
---|
5013 | t->match(t->tk);
|
---|
5014 | CScriptVarLinkWorkPtr b = execute_term(execute); // L->R
|
---|
5015 | if (execute) {
|
---|
5016 | CheckRightHandVar(execute, b);
|
---|
5017 | a = mathsOp(execute, a.getter(execute), b.getter(execute), op);
|
---|
5018 | }
|
---|
5019 | }
|
---|
5020 | }
|
---|
5021 | return a;
|
---|
5022 | }
|
---|
5023 |
|
---|
5024 | // L->R: Precedence 7 (bitwise shift) << >> >>>
|
---|
5025 | CScriptVarLinkWorkPtr CTinyJS::execute_binary_shift(CScriptResult &execute) {
|
---|
5026 | CScriptVarLinkWorkPtr a = execute_expression(execute);
|
---|
5027 | if (t->tk==LEX_LSHIFT || t->tk==LEX_RSHIFT || t->tk==LEX_RSHIFTU) {
|
---|
5028 | CheckRightHandVar(execute, a);
|
---|
5029 | while (t->tk>=LEX_SHIFTS_BEGIN && t->tk<=LEX_SHIFTS_END) {
|
---|
5030 | int op = t->tk;
|
---|
5031 | t->match(t->tk);
|
---|
5032 |
|
---|
5033 | CScriptVarLinkWorkPtr b = execute_expression(execute); // L->R
|
---|
5034 | if (execute) {
|
---|
5035 | CheckRightHandVar(execute, a);
|
---|
5036 | // not in-place, so just replace
|
---|
5037 | a = mathsOp(execute, a.getter(execute), b.getter(execute), op);
|
---|
5038 | }
|
---|
5039 | }
|
---|
5040 | }
|
---|
5041 | return a;
|
---|
5042 | }
|
---|
5043 | // L->R: Precedence 8 (relational) < <= > <= in instanceof
|
---|
5044 | // L->R: Precedence 9 (equality) == != === !===
|
---|
5045 | CScriptVarLinkWorkPtr CTinyJS::execute_relation(CScriptResult &execute, int set, int set_n) {
|
---|
5046 | CScriptVarLinkWorkPtr a = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute);
|
---|
5047 | if ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END)
|
---|
5048 | || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) {
|
---|
5049 | CheckRightHandVar(execute, a);
|
---|
5050 | a = a.getter(execute);
|
---|
5051 | while ((set==LEX_EQUAL && t->tk>=LEX_RELATIONS_1_BEGIN && t->tk<=LEX_RELATIONS_1_END)
|
---|
5052 | || (set=='<' && (t->tk==LEX_LEQUAL || t->tk==LEX_GEQUAL || t->tk=='<' || t->tk=='>' || t->tk == LEX_R_IN || t->tk == LEX_R_INSTANCEOF))) {
|
---|
5053 | int op = t->tk;
|
---|
5054 | t->match(t->tk);
|
---|
5055 | CScriptVarLinkWorkPtr b = set_n ? execute_relation(execute, set_n, 0) : execute_binary_shift(execute); // L->R
|
---|
5056 | if (execute) {
|
---|
5057 | CheckRightHandVar(execute, b);
|
---|
5058 | string nameOf_b = b->getName();
|
---|
5059 | b = b.getter(execute);
|
---|
5060 | if(op == LEX_R_IN) {
|
---|
5061 | if(!b->getVarPtr()->isObject())
|
---|
5062 | throwError(execute, TypeError, "invalid 'in' operand "+nameOf_b);
|
---|
5063 | a(constScriptVar( (bool)b->getVarPtr()->findChildWithPrototypeChain(a->toString(execute))));
|
---|
5064 | } else if(op == LEX_R_INSTANCEOF) {
|
---|
5065 | CScriptVarLinkPtr prototype = b->getVarPtr()->findChild(TINYJS_PROTOTYPE_CLASS);
|
---|
5066 | if(!prototype)
|
---|
5067 | throwError(execute, TypeError, "invalid 'instanceof' operand "+nameOf_b);
|
---|
5068 | else {
|
---|
5069 | unsigned int uniqueID = getUniqueID();
|
---|
5070 | CScriptVarPtr object = a->getVarPtr()->findChild(TINYJS___PROTO___VAR);
|
---|
5071 | while( object && object!=prototype->getVarPtr() && object->getTempraryID() != uniqueID) {
|
---|
5072 | object->setTemporaryID(uniqueID); // prevents recursions
|
---|
5073 | object = object->findChild(TINYJS___PROTO___VAR);
|
---|
5074 | }
|
---|
5075 | a(constScriptVar(object && object==prototype->getVarPtr()));
|
---|
5076 | }
|
---|
5077 | } else
|
---|
5078 | a = mathsOp(execute, a, b, op);
|
---|
5079 | }
|
---|
5080 | }
|
---|
5081 | }
|
---|
5082 | return a;
|
---|
5083 | }
|
---|
5084 |
|
---|
5085 | // L->R: Precedence 10 (bitwise-and) &
|
---|
5086 | // L->R: Precedence 11 (bitwise-xor) ^
|
---|
5087 | // L->R: Precedence 12 (bitwise-or) |
|
---|
5088 | CScriptVarLinkWorkPtr CTinyJS::execute_binary_logic(CScriptResult &execute, int op, int op_n1, int op_n2) {
|
---|
5089 | CScriptVarLinkWorkPtr a = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute);
|
---|
5090 | if (t->tk==op) {
|
---|
5091 | CheckRightHandVar(execute, a);
|
---|
5092 | a = a.getter(execute);
|
---|
5093 | while (t->tk==op) {
|
---|
5094 | t->match(t->tk);
|
---|
5095 | CScriptVarLinkWorkPtr b = op_n1 ? execute_binary_logic(execute, op_n1, op_n2, 0) : execute_relation(execute); // L->R
|
---|
5096 | if (execute) {
|
---|
5097 | CheckRightHandVar(execute, b);
|
---|
5098 | a = mathsOp(execute, a, b.getter(execute), op);
|
---|
5099 | }
|
---|
5100 | }
|
---|
5101 | }
|
---|
5102 | return a;
|
---|
5103 | }
|
---|
5104 | // L->R: Precedence 13 ==> (logical-and) &&
|
---|
5105 | // L->R: Precedence 14 ==> (logical-or) ||
|
---|
5106 | CScriptVarLinkWorkPtr CTinyJS::execute_logic(CScriptResult &execute, int op /*= LEX_OROR*/, int op_n /*= LEX_ANDAND*/) {
|
---|
5107 | CScriptVarLinkWorkPtr a = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute);
|
---|
5108 | if (t->tk==op) {
|
---|
5109 | if(execute) {
|
---|
5110 | CScriptVarLinkWorkPtr b;
|
---|
5111 | CheckRightHandVar(execute, a);
|
---|
5112 | a(a.getter(execute)); // rebuild a
|
---|
5113 | do {
|
---|
5114 | if(execute && (op==LEX_ANDAND ? a->toBoolean() : !a->toBoolean())) {
|
---|
5115 | t->match(t->tk);
|
---|
5116 | b = op_n ? execute_logic(execute, op_n, 0) : execute_binary_logic(execute);
|
---|
5117 | CheckRightHandVar(execute, b); a(b.getter(execute)); // rebuild a
|
---|
5118 | } else
|
---|
5119 | t->skip(t->getToken().Int());
|
---|
5120 | } while(t->tk==op);
|
---|
5121 | } else
|
---|
5122 | t->skip(t->getToken().Int());
|
---|
5123 | }
|
---|
5124 | return a;
|
---|
5125 | }
|
---|
5126 |
|
---|
5127 | // L<-R: Precedence 15 (condition) ?:
|
---|
5128 | CScriptVarLinkWorkPtr CTinyJS::execute_condition(CScriptResult &execute) {
|
---|
5129 | CScriptVarLinkWorkPtr a = execute_logic(execute);
|
---|
5130 | if (t->tk=='?') {
|
---|
5131 | CheckRightHandVar(execute, a);
|
---|
5132 | bool cond = execute && a.getter(execute)->toBoolean();
|
---|
5133 | if(execute) {
|
---|
5134 | if(cond) {
|
---|
5135 | t->match('?');
|
---|
5136 | a = execute_condition(execute);
|
---|
5137 | t->check(':');
|
---|
5138 | t->skip(t->getToken().Int());
|
---|
5139 | } else {
|
---|
5140 | CScriptVarLinkWorkPtr b;
|
---|
5141 | t->skip(t->getToken().Int());
|
---|
5142 | t->match(':');
|
---|
5143 | return execute_condition(execute);
|
---|
5144 | }
|
---|
5145 | } else {
|
---|
5146 | t->skip(t->getToken().Int());
|
---|
5147 | t->skip(t->getToken().Int());
|
---|
5148 | }
|
---|
5149 | }
|
---|
5150 | return a;
|
---|
5151 | }
|
---|
5152 |
|
---|
5153 | // L<-R: Precedence 16 (assignment) = += -= *= /= %= <<= >>= >>>= &= |= ^=
|
---|
5154 | // now we can return CScriptVarLinkPtr execute_assignment returns always no setters/getters
|
---|
5155 | // force life of the Owner is no more needed
|
---|
5156 | CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptResult &execute) {
|
---|
5157 | return execute_assignment(execute_condition(execute), execute);
|
---|
5158 | }
|
---|
5159 | CScriptVarLinkPtr CTinyJS::execute_assignment(CScriptVarLinkWorkPtr lhs, CScriptResult &execute) {
|
---|
5160 | if (t->tk=='=' || (t->tk>=LEX_ASSIGNMENTS_BEGIN && t->tk<=LEX_ASSIGNMENTS_END) ) {
|
---|
5161 | int op = t->tk;
|
---|
5162 | CScriptTokenizer::ScriptTokenPosition leftHandPos = t->getPos();
|
---|
5163 | t->match(t->tk);
|
---|
5164 | CScriptVarLinkWorkPtr rhs = execute_assignment(execute).getter(execute); // L<-R
|
---|
5165 | if (execute) {
|
---|
5166 | if (!lhs->isOwned() && !lhs.hasReferencedOwner() && lhs->getName().empty()) {
|
---|
5167 | throw new CScriptException(ReferenceError, "invalid assignment left-hand side (at runtime)", t->currentFile, leftHandPos.currentLine(), leftHandPos.currentColumn());
|
---|
5168 | } else if (op != '=' && !lhs->isOwned()) {
|
---|
5169 | throwError(execute, ReferenceError, lhs->getName() + " is not defined");
|
---|
5170 | }
|
---|
5171 | else if(lhs->isWritable()) {
|
---|
5172 | if (op=='=') {
|
---|
5173 | if (!lhs->isOwned()) {
|
---|
5174 | CScriptVarPtr fakedOwner = lhs.getReferencedOwner();
|
---|
5175 | if(fakedOwner) {
|
---|
5176 | if(!fakedOwner->isExtensible())
|
---|
5177 | return rhs->getVarPtr();
|
---|
5178 | lhs = fakedOwner->addChildOrReplace(lhs->getName(), lhs);
|
---|
5179 | } else
|
---|
5180 | lhs = root->addChildOrReplace(lhs->getName(), lhs);
|
---|
5181 | }
|
---|
5182 | lhs.setter(execute, rhs);
|
---|
5183 | return rhs->getVarPtr();
|
---|
5184 | } else {
|
---|
5185 | CScriptVarPtr result;
|
---|
5186 | static int assignments[] = {'+', '-', '*', '/', '%', LEX_LSHIFT, LEX_RSHIFT, LEX_RSHIFTU, '&', '|', '^'};
|
---|
5187 | result = mathsOp(execute, lhs, rhs, assignments[op-LEX_PLUSEQUAL]);
|
---|
5188 | lhs.setter(execute, result);
|
---|
5189 | return result;
|
---|
5190 | }
|
---|
5191 | } else {
|
---|
5192 | // lhs is not writable we ignore lhs & use rhs
|
---|
5193 | return rhs->getVarPtr();
|
---|
5194 | }
|
---|
5195 | }
|
---|
5196 | }
|
---|
5197 | else
|
---|
5198 | CheckRightHandVar(execute, lhs);
|
---|
5199 | return lhs.getter(execute);
|
---|
5200 | }
|
---|
5201 | // L->R: Precedence 17 (comma) ,
|
---|
5202 | CScriptVarLinkPtr CTinyJS::execute_base(CScriptResult &execute) {
|
---|
5203 | CScriptVarLinkPtr a;
|
---|
5204 | for(;;)
|
---|
5205 | {
|
---|
5206 | a = execute_assignment(execute); // L->R
|
---|
5207 | if (t->tk == ',') {
|
---|
5208 | t->match(',');
|
---|
5209 | } else
|
---|
5210 | break;
|
---|
5211 | }
|
---|
5212 | return a;
|
---|
5213 | }
|
---|
5214 | void CTinyJS::execute_block(CScriptResult &execute) {
|
---|
5215 | if(execute) {
|
---|
5216 | t->match('{');
|
---|
5217 | CScopeControl ScopeControl(this);
|
---|
5218 | if(t->tk==LEX_T_FORWARD) // add a LetScope only if needed
|
---|
5219 | ScopeControl.addLetScope();
|
---|
5220 | while (t->tk && t->tk!='}')
|
---|
5221 | execute_statement(execute);
|
---|
5222 | t->match('}');
|
---|
5223 | // scopes.pop_back();
|
---|
5224 | }
|
---|
5225 | else
|
---|
5226 | t->skip(t->getToken().Int());
|
---|
5227 | }
|
---|
5228 | void CTinyJS::execute_statement(CScriptResult &execute) {
|
---|
5229 | switch(t->tk) {
|
---|
5230 | case '{': /* A block of code */
|
---|
5231 | execute_block(execute);
|
---|
5232 | break;
|
---|
5233 | case ';': /* Empty statement - to allow things like ;;; */
|
---|
5234 | t->match(';');
|
---|
5235 | break;
|
---|
5236 | case LEX_T_FORWARD:
|
---|
5237 | {
|
---|
5238 | CScriptVarPtr in_scope = scope()->scopeLet();
|
---|
5239 | STRING_SET_t *varNames = t->getToken().Forwarder().varNames;
|
---|
5240 | for(int i=0; i<CScriptTokenDataForwards::END; ++i) {
|
---|
5241 | for(STRING_SET_it it=varNames[i].begin(); it!=varNames[i].end(); ++it) {
|
---|
5242 | CScriptVarLinkPtr a = in_scope->findChild(*it);
|
---|
5243 | if(!a) in_scope->addChild(*it, constScriptVar(Undefined), i==CScriptTokenDataForwards::CONSTS ? SCRIPTVARLINK_CONSTDEFAULT : SCRIPTVARLINK_VARDEFAULT);
|
---|
5244 | }
|
---|
5245 | in_scope = scope()->scopeVar();
|
---|
5246 | }
|
---|
5247 | CScriptTokenDataForwards::FNC_SET_t &functions = t->getToken().Forwarder().functions;
|
---|
5248 | for(CScriptTokenDataForwards::FNC_SET_it it=functions.begin(); it!=functions.end(); ++it) {
|
---|
5249 | CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(*it);
|
---|
5250 | in_scope->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT);
|
---|
5251 | }
|
---|
5252 | t->match(LEX_T_FORWARD);
|
---|
5253 | }
|
---|
5254 | break;
|
---|
5255 | case LEX_R_VAR:
|
---|
5256 | case LEX_R_LET:
|
---|
5257 | case LEX_R_CONST:
|
---|
5258 | if(execute)
|
---|
5259 | {
|
---|
5260 | CScopeControl ScopeControl(this);
|
---|
5261 | bool isLet = t->tk==LEX_R_LET, let_ext=false;
|
---|
5262 | t->match(t->tk);
|
---|
5263 | if(isLet && t->tk=='(') {
|
---|
5264 | let_ext = true;
|
---|
5265 | t->match('(');
|
---|
5266 | t->check(LEX_T_FORWARD);
|
---|
5267 | ScopeControl.addLetScope();
|
---|
5268 | execute_statement(execute); // forwarder
|
---|
5269 | }
|
---|
5270 | execute_var_init(let_ext, execute);
|
---|
5271 | if(let_ext) {
|
---|
5272 | t->match(')');
|
---|
5273 | execute_statement(execute);
|
---|
5274 | } else
|
---|
5275 | t->match(';');
|
---|
5276 | } else
|
---|
5277 | t->skip(t->getToken().Int());
|
---|
5278 | break;
|
---|
5279 | case LEX_R_WITH:
|
---|
5280 | if(execute) {
|
---|
5281 | t->match(LEX_R_WITH);
|
---|
5282 | t->match('(');
|
---|
5283 | CScriptVarLinkPtr var = execute_base(execute);
|
---|
5284 | t->match(')');
|
---|
5285 | CScopeControl ScopeControl(this);
|
---|
5286 | ScopeControl.addWithScope(var);
|
---|
5287 | execute_statement(execute);
|
---|
5288 | } else
|
---|
5289 | t->skip(t->getToken().Int());
|
---|
5290 | break;
|
---|
5291 | case LEX_R_IF:
|
---|
5292 | if(execute) {
|
---|
5293 | t->match(LEX_R_IF);
|
---|
5294 | t->match('(');
|
---|
5295 | bool cond = execute_base(execute)->toBoolean();
|
---|
5296 | t->match(')');
|
---|
5297 | if(cond && execute) {
|
---|
5298 | t->match(LEX_T_SKIP);
|
---|
5299 | execute_statement(execute);
|
---|
5300 | } else {
|
---|
5301 | t->check(LEX_T_SKIP);
|
---|
5302 | t->skip(t->getToken().Int());
|
---|
5303 | }
|
---|
5304 | if (t->tk==LEX_R_ELSE) {
|
---|
5305 | if(!cond && execute) {
|
---|
5306 | t->match(LEX_R_ELSE);
|
---|
5307 | execute_statement(execute);
|
---|
5308 | }
|
---|
5309 | else
|
---|
5310 | t->skip(t->getToken().Int());
|
---|
5311 | }
|
---|
5312 | } else
|
---|
5313 | t->skip(t->getToken().Int());
|
---|
5314 | break;
|
---|
5315 | case LEX_T_FOR_IN:
|
---|
5316 | {
|
---|
5317 | CScriptTokenDataLoop &LoopData = t->getToken().Loop();
|
---|
5318 | t->match(LEX_T_FOR_IN);
|
---|
5319 | if(!execute) break;
|
---|
5320 |
|
---|
5321 | CScopeControl ScopeControl(this);
|
---|
5322 | if(LoopData.init.size()) {
|
---|
5323 | t->pushTokenScope(LoopData.init);
|
---|
5324 | ScopeControl.addLetScope();
|
---|
5325 | execute_statement(execute); // forwarder
|
---|
5326 | }
|
---|
5327 | if(!execute) break;
|
---|
5328 |
|
---|
5329 | t->pushTokenScope(LoopData.iter);
|
---|
5330 | CScriptVarPtr for_in_var = execute_base(execute);
|
---|
5331 |
|
---|
5332 | if(!execute) break;
|
---|
5333 |
|
---|
5334 | CScriptVarPtr Iterator(for_in_var->toIterator(execute, LoopData.type!=CScriptTokenDataLoop::FOR_IN ? 2:1));
|
---|
5335 | CScriptVarFunctionPtr Iterator_next(Iterator->findChildWithPrototypeChain("next").getter(execute));
|
---|
5336 | if(execute && !Iterator_next) throwError(execute, TypeError, "'" + for_in_var->toString(execute) + "' is not iterable", t->getPrevPos());
|
---|
5337 | if(!execute) break;
|
---|
5338 | CScriptResult tmp_execute;
|
---|
5339 | for(;;) {
|
---|
5340 | bool old_haveTry = haveTry;
|
---|
5341 | haveTry = true;
|
---|
5342 | tmp_execute.set(CScriptResult::Normal, Iterator);
|
---|
5343 | t->pushTokenScope(LoopData.condition);
|
---|
5344 | execute_statement(tmp_execute);
|
---|
5345 | haveTry = old_haveTry;
|
---|
5346 | if(tmp_execute.isThrow()){
|
---|
5347 | if(tmp_execute.value != constStopIteration) {
|
---|
5348 | if(!haveTry)
|
---|
5349 | throw new CScriptException("uncaught exception: ", t->currentFile, t->currentLine(), t->currentColumn());
|
---|
5350 | else
|
---|
5351 | execute = tmp_execute;
|
---|
5352 | }
|
---|
5353 | break;
|
---|
5354 | }
|
---|
5355 | t->pushTokenScope(LoopData.body);
|
---|
5356 | execute_statement(execute);
|
---|
5357 | if(!execute) {
|
---|
5358 | bool Continue = false;
|
---|
5359 | if(execute.isBreakContinue()
|
---|
5360 | &&
|
---|
5361 | (execute.target.empty() || find(LoopData.labels.begin(), LoopData.labels.end(), execute.target) != LoopData.labels.end())) {
|
---|
5362 | Continue = execute.isContinue();
|
---|
5363 | execute.set(CScriptResult::Normal, false);
|
---|
5364 | }
|
---|
5365 | if(!Continue) break;
|
---|
5366 | }
|
---|
5367 | }
|
---|
5368 | }
|
---|
5369 | break;
|
---|
5370 | case LEX_T_LOOP:
|
---|
5371 | {
|
---|
5372 | CScriptTokenDataLoop &LoopData = t->getToken().Loop();
|
---|
5373 | t->match(LEX_T_LOOP);
|
---|
5374 | if(!execute) break;
|
---|
5375 |
|
---|
5376 | CScopeControl ScopeControl(this);
|
---|
5377 | if(LoopData.type == CScriptTokenDataLoop::FOR) {
|
---|
5378 | CScriptResult tmp_execute;
|
---|
5379 | t->pushTokenScope(LoopData.init);
|
---|
5380 | if(t->tk == LEX_T_FORWARD) {
|
---|
5381 | ScopeControl.addLetScope();
|
---|
5382 | execute_statement(tmp_execute); // forwarder
|
---|
5383 | }
|
---|
5384 | if(t->tk==LEX_R_VAR || t->tk==LEX_R_LET)
|
---|
5385 | execute_statement(tmp_execute); // initialisation
|
---|
5386 | else
|
---|
5387 | execute_base(tmp_execute); // initialisation
|
---|
5388 | if(!execute(tmp_execute)) break;
|
---|
5389 | }
|
---|
5390 |
|
---|
5391 | bool loopCond = true; // Empty Condition -->always true
|
---|
5392 | if(LoopData.type != CScriptTokenDataLoop::DO && LoopData.condition.size()) {
|
---|
5393 | t->pushTokenScope(LoopData.condition);
|
---|
5394 | loopCond = execute_base(execute)->toBoolean();
|
---|
5395 | if(!execute) break;
|
---|
5396 | }
|
---|
5397 | while (loopCond && execute) {
|
---|
5398 | t->pushTokenScope(LoopData.body);
|
---|
5399 | execute_statement(execute);
|
---|
5400 | if(!execute) {
|
---|
5401 | bool Continue = false;
|
---|
5402 | if(execute.isBreakContinue()
|
---|
5403 | &&
|
---|
5404 | (execute.target.empty() || find(LoopData.labels.begin(), LoopData.labels.end(), execute.target) != LoopData.labels.end())) {
|
---|
5405 | Continue = execute.isContinue();
|
---|
5406 | execute.set(CScriptResult::Normal, false);
|
---|
5407 | }
|
---|
5408 | if(!Continue) break;
|
---|
5409 | }
|
---|
5410 | if(LoopData.type == CScriptTokenDataLoop::FOR && execute && LoopData.iter.size()) {
|
---|
5411 | t->pushTokenScope(LoopData.iter);
|
---|
5412 | execute_base(execute);
|
---|
5413 | }
|
---|
5414 | if(execute && LoopData.condition.size()) {
|
---|
5415 | t->pushTokenScope(LoopData.condition);
|
---|
5416 | loopCond = execute_base(execute)->toBoolean();
|
---|
5417 | }
|
---|
5418 | }
|
---|
5419 | }
|
---|
5420 | break;
|
---|
5421 | case LEX_R_BREAK:
|
---|
5422 | case LEX_R_CONTINUE:
|
---|
5423 | if (execute)
|
---|
5424 | {
|
---|
5425 | CScriptResult::TYPE type = t->tk==LEX_R_BREAK ? CScriptResult::Break : CScriptResult::Continue;
|
---|
5426 | string label;
|
---|
5427 | t->match(t->tk);
|
---|
5428 | if(t->tk == LEX_ID) {
|
---|
5429 | label = t->tkStr();
|
---|
5430 | t->match(LEX_ID);
|
---|
5431 | }
|
---|
5432 | t->match(';');
|
---|
5433 | execute.set(type, label);
|
---|
5434 | } else
|
---|
5435 | t->skip(t->getToken().Int());
|
---|
5436 | break;
|
---|
5437 | case LEX_R_RETURN:
|
---|
5438 | if (execute) {
|
---|
5439 | t->match(LEX_R_RETURN);
|
---|
5440 | CScriptVarPtr result;
|
---|
5441 | if (t->tk != ';')
|
---|
5442 | result = execute_base(execute);
|
---|
5443 | t->match(';');
|
---|
5444 | execute.set(CScriptResult::Return, result);
|
---|
5445 | } else
|
---|
5446 | t->skip(t->getToken().Int());
|
---|
5447 | break;
|
---|
5448 | case LEX_R_FUNCTION:
|
---|
5449 | if(execute) {
|
---|
5450 | CScriptVarLinkWorkPtr funcVar = parseFunctionDefinition(t->getToken());
|
---|
5451 | scope()->scopeVar()->addChildOrReplace(funcVar->getName(), funcVar, SCRIPTVARLINK_VARDEFAULT);
|
---|
5452 | }
|
---|
5453 | case LEX_T_FUNCTION_PLACEHOLDER:
|
---|
5454 | t->match(t->tk);
|
---|
5455 | break;
|
---|
5456 | case LEX_T_TRY:
|
---|
5457 | if(execute) {
|
---|
5458 | CScriptTokenDataTry &TryData = t->getToken().Try();
|
---|
5459 |
|
---|
5460 | bool old_haveTry = haveTry;
|
---|
5461 | haveTry = true;
|
---|
5462 |
|
---|
5463 | // execute try-block
|
---|
5464 | t->pushTokenScope(TryData.tryBlock);
|
---|
5465 | execute_block(execute);
|
---|
5466 |
|
---|
5467 | bool isThrow = execute.isThrow();
|
---|
5468 |
|
---|
5469 | if(isThrow) {
|
---|
5470 | // execute catch-blocks
|
---|
5471 | for(CScriptTokenDataTry::CatchBlock_it catchBlock = TryData.catchBlocks.begin(); catchBlock!=TryData.catchBlocks.end(); catchBlock++) {
|
---|
5472 | CScriptResult catch_execute;
|
---|
5473 | CScopeControl ScopeControl(this);
|
---|
5474 | ScopeControl.addLetScope();
|
---|
5475 | t->pushTokenScope(catchBlock->condition); // condition;
|
---|
5476 | execute_statement(catch_execute); // forwarder
|
---|
5477 | assign_destructuring_var(0, *catchBlock->indentifiers, execute.value, catch_execute);
|
---|
5478 | bool condition = true;
|
---|
5479 | if(catchBlock->condition.size()>1)
|
---|
5480 | condition = execute_base(catch_execute)->toBoolean();
|
---|
5481 | if(!catch_execute) {
|
---|
5482 | execute = catch_execute;
|
---|
5483 | break;
|
---|
5484 | } else if(condition) {
|
---|
5485 | t->pushTokenScope(catchBlock->block); // condition;
|
---|
5486 | execute_block(catch_execute);
|
---|
5487 | execute = catch_execute;
|
---|
5488 | break;
|
---|
5489 | }
|
---|
5490 | }
|
---|
5491 | }
|
---|
5492 | if(TryData.finallyBlock.size()) {
|
---|
5493 | CScriptResult finally_execute; // alway execute finally-block
|
---|
5494 | t->pushTokenScope(TryData.finallyBlock); // finally;
|
---|
5495 | execute_block(finally_execute);
|
---|
5496 | execute(finally_execute);
|
---|
5497 | }
|
---|
5498 | // restore haveTry
|
---|
5499 | haveTry = old_haveTry;
|
---|
5500 | if(execute.isThrow() && !haveTry) { // (exception in catch or finally or no catch-clause found) and no parent try-block
|
---|
5501 | if(execute.value->isError())
|
---|
5502 | throw CScriptVarErrorPtr(execute.value)->toCScriptException();
|
---|
5503 | throw new CScriptException("uncaught exception: '"+execute.value->toString()+"'", t->currentFile, t->currentLine(), t->currentColumn());
|
---|
5504 | }
|
---|
5505 |
|
---|
5506 | }
|
---|
5507 | t->match(LEX_T_TRY);
|
---|
5508 | break;
|
---|
5509 | case LEX_R_THROW:
|
---|
5510 | if(execute) {
|
---|
5511 | CScriptTokenizer::ScriptTokenPosition tokenPos = t->getPos();
|
---|
5512 | // int tokenStart = t->getToken().pos;
|
---|
5513 | t->match(LEX_R_THROW);
|
---|
5514 | CScriptVarPtr a = execute_base(execute);
|
---|
5515 | if(execute) {
|
---|
5516 | if(haveTry)
|
---|
5517 | execute.set(CScriptResult::Throw, a);
|
---|
5518 | else
|
---|
5519 | throw new CScriptException("uncaught exception: '"+a->toString(execute)+"'", t->currentFile, tokenPos.currentLine(), tokenPos.currentColumn());
|
---|
5520 | }
|
---|
5521 | } else
|
---|
5522 | t->skip(t->getToken().Int());
|
---|
5523 | break;
|
---|
5524 | case LEX_R_SWITCH:
|
---|
5525 | if(execute) {
|
---|
5526 | t->match(LEX_R_SWITCH);
|
---|
5527 | t->match('(');
|
---|
5528 | CScriptVarPtr SwitchValue = execute_base(execute);
|
---|
5529 | t->match(')');
|
---|
5530 | if(execute) {
|
---|
5531 | t->match('{');
|
---|
5532 | CScopeControl ScopeControl(this);
|
---|
5533 | if(t->tk == LEX_T_FORWARD) {
|
---|
5534 | ScopeControl.addLetScope(); // add let-scope only if needed
|
---|
5535 | execute_statement(execute); // execute forwarder
|
---|
5536 | }
|
---|
5537 | CScriptTokenizer::ScriptTokenPosition defaultStart = t->getPos();
|
---|
5538 | bool hasDefault = false, found = false;
|
---|
5539 | while (t->tk) {
|
---|
5540 | switch(t->tk) {
|
---|
5541 | case LEX_R_CASE:
|
---|
5542 | if(!execute)
|
---|
5543 | t->skip(t->getToken().Int()); // skip up to'}'
|
---|
5544 | else if(found) { // execute && found
|
---|
5545 | t->match(LEX_R_CASE);
|
---|
5546 | t->skip(t->getToken().Int()); // skip up to ':'
|
---|
5547 | t->match(':'); // skip ':' and execute all after ':'
|
---|
5548 | } else { // execute && !found
|
---|
5549 | t->match(LEX_R_CASE);
|
---|
5550 | t->match(LEX_T_SKIP); // skip 'L_T_SKIP'
|
---|
5551 | CScriptVarLinkPtr CaseValue = execute_base(execute);
|
---|
5552 | CaseValue = mathsOp(execute, CaseValue, SwitchValue, LEX_TYPEEQUAL);
|
---|
5553 | if(execute) {
|
---|
5554 | found = CaseValue->toBoolean();
|
---|
5555 | if(found) t->match(':'); // skip ':' and execute all after ':'
|
---|
5556 | else t->skip(t->getToken().Int()); // skip up to next 'case'/'default' or '}'
|
---|
5557 | } else
|
---|
5558 | t->skip(t->getToken().Int()); // skip up to next 'case'/'default' or '}'
|
---|
5559 | }
|
---|
5560 | break;
|
---|
5561 | case LEX_R_DEFAULT:
|
---|
5562 | if(!execute)
|
---|
5563 | t->skip(t->getToken().Int()); // skip up to'}' NOTE: no extra 'L_T_SKIP' for skipping tp ':'
|
---|
5564 | else {
|
---|
5565 | t->match(LEX_R_DEFAULT);
|
---|
5566 | if(found)
|
---|
5567 | t->match(':'); // skip ':' and execute all after ':'
|
---|
5568 | else {
|
---|
5569 | hasDefault = true; // in fist pass: skip default-area
|
---|
5570 | defaultStart = t->getPos(); // remember pos of default
|
---|
5571 | t->skip(t->getToken().Int()); // skip up to next 'case' or '}'
|
---|
5572 | }
|
---|
5573 | }
|
---|
5574 | break;
|
---|
5575 | case '}':
|
---|
5576 | if(execute && !found && hasDefault) { // if not found & have default -> execute default
|
---|
5577 | found = true;
|
---|
5578 | t->setPos(defaultStart);
|
---|
5579 | t->match(':');
|
---|
5580 | } else
|
---|
5581 | goto end_while; // goto isn't fine but C supports no "break lable;"
|
---|
5582 | break;
|
---|
5583 | default:
|
---|
5584 | ASSERT(found);
|
---|
5585 | execute_statement(execute);
|
---|
5586 | break;
|
---|
5587 | }
|
---|
5588 | }
|
---|
5589 | end_while:
|
---|
5590 | t->match('}');
|
---|
5591 | if(execute.isBreak() && execute.target.empty()) {
|
---|
5592 | execute.set(CScriptResult::Normal);
|
---|
5593 | }
|
---|
5594 | } else
|
---|
5595 | t->skip(t->getToken().Int());
|
---|
5596 | } else
|
---|
5597 | t->skip(t->getToken().Int());
|
---|
5598 | break;
|
---|
5599 | case LEX_T_DUMMY_LABEL:
|
---|
5600 | t->match(LEX_T_DUMMY_LABEL);
|
---|
5601 | t->match(':');
|
---|
5602 | break;
|
---|
5603 | case LEX_T_LABEL:
|
---|
5604 | {
|
---|
5605 | STRING_VECTOR_t Labels;
|
---|
5606 | while(t->tk == LEX_T_LABEL) {
|
---|
5607 | Labels.push_back(t->tkStr());
|
---|
5608 | t->match(LEX_T_LABEL);
|
---|
5609 | t->match(':');
|
---|
5610 | }
|
---|
5611 | if(execute) {
|
---|
5612 | execute_statement(execute);
|
---|
5613 | if(execute.isBreak() && find(Labels.begin(), Labels.end(), execute.target) != Labels.end()) { // break this label
|
---|
5614 | execute.set(CScriptResult::Normal, false);
|
---|
5615 | }
|
---|
5616 | }
|
---|
5617 | else
|
---|
5618 | execute_statement(execute);
|
---|
5619 | }
|
---|
5620 | break;
|
---|
5621 | case LEX_EOF:
|
---|
5622 | t->match(LEX_EOF);
|
---|
5623 | break;
|
---|
5624 | default:
|
---|
5625 | if(execute) {
|
---|
5626 | t->match(LEX_T_SKIP);
|
---|
5627 | /* Execute a simple statement that only contains basic arithmetic... */
|
---|
5628 | CScriptVarPtr ret = execute_base(execute);
|
---|
5629 | if(execute) execute.set(CScriptResult::Normal, CScriptVarPtr(ret));
|
---|
5630 | t->match(';');
|
---|
5631 | } else
|
---|
5632 | t->skip(t->getToken().Int());
|
---|
5633 | break;
|
---|
5634 | }
|
---|
5635 | }
|
---|
5636 |
|
---|
5637 |
|
---|
5638 | /// Finds a child, looking recursively up the scopes
|
---|
5639 | CScriptVarLinkPtr CTinyJS::findInScopes(const string &childName) {
|
---|
5640 | return scope()->findInScopes(childName);
|
---|
5641 | }
|
---|
5642 |
|
---|
5643 | //////////////////////////////////////////////////////////////////////////
|
---|
5644 | /// Object
|
---|
5645 | //////////////////////////////////////////////////////////////////////////
|
---|
5646 |
|
---|
5647 | void CTinyJS::native_Object(const CFunctionsScopePtr &c, void *data) {
|
---|
5648 | c->setReturnVar(c->getArgument(0)->toObject());
|
---|
5649 | }
|
---|
5650 | void CTinyJS::native_Object_getPrototypeOf(const CFunctionsScopePtr &c, void *data) {
|
---|
5651 | if(c->getArgumentsLength()>=1) {
|
---|
5652 | CScriptVarPtr obj = c->getArgument(0);
|
---|
5653 | if(obj->isObject()) {
|
---|
5654 | c->setReturnVar(obj->findChild(TINYJS___PROTO___VAR));
|
---|
5655 | return;
|
---|
5656 | }
|
---|
5657 | }
|
---|
5658 | c->throwError(TypeError, "argument is not an object");
|
---|
5659 | }
|
---|
5660 |
|
---|
5661 | void CTinyJS::native_Object_setObjectSecure(const CFunctionsScopePtr &c, void *data) {
|
---|
5662 | CScriptVarPtr obj = c->getArgument(0);
|
---|
5663 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
5664 | if(data==(void*)2)
|
---|
5665 | obj->freeze();
|
---|
5666 | else if(data==(void*)1)
|
---|
5667 | obj->seal();
|
---|
5668 | else
|
---|
5669 | obj->preventExtensions();
|
---|
5670 | c->setReturnVar(obj);
|
---|
5671 | }
|
---|
5672 |
|
---|
5673 | void CTinyJS::native_Object_isSecureObject(const CFunctionsScopePtr &c, void *data) {
|
---|
5674 | CScriptVarPtr obj = c->getArgument(0);
|
---|
5675 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
5676 | bool ret;
|
---|
5677 | if(data==(void*)2)
|
---|
5678 | ret = obj->isFrozen();
|
---|
5679 | else if(data==(void*)1)
|
---|
5680 | ret = obj->isSealed();
|
---|
5681 | else
|
---|
5682 | ret = obj->isExtensible();
|
---|
5683 | c->setReturnVar(constScriptVar(ret));
|
---|
5684 | }
|
---|
5685 |
|
---|
5686 | void CTinyJS::native_Object_keys(const CFunctionsScopePtr &c, void *data) {
|
---|
5687 | CScriptVarPtr obj = c->getArgument(0);
|
---|
5688 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
5689 | CScriptVarPtr returnVar = c->newScriptVar(Array);
|
---|
5690 | c->setReturnVar(returnVar);
|
---|
5691 |
|
---|
5692 | STRING_SET_t keys;
|
---|
5693 | obj->keys(keys, data==0);
|
---|
5694 |
|
---|
5695 | uint32_t idx=0;
|
---|
5696 | for(STRING_SET_it it=keys.begin(); it!=keys.end(); ++it)
|
---|
5697 | returnVar->setArrayIndex(idx++, newScriptVar(*it));
|
---|
5698 | }
|
---|
5699 |
|
---|
5700 | void CTinyJS::native_Object_getOwnPropertyDescriptor(const CFunctionsScopePtr &c, void *data) {
|
---|
5701 | CScriptVarPtr obj = c->getArgument(0);
|
---|
5702 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
5703 | c->setReturnVar(obj->getOwnPropertyDescriptor(c->getArgument(1)->toString()));
|
---|
5704 | }
|
---|
5705 |
|
---|
5706 | void CTinyJS::native_Object_defineProperty(const CFunctionsScopePtr &c, void *data) {
|
---|
5707 | CScriptVarPtr obj = c->getArgument(0);
|
---|
5708 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
5709 | string name = c->getArgument(1)->toString();
|
---|
5710 | CScriptVarPtr attributes = c->getArgument(2);
|
---|
5711 | if(!attributes->isObject()) c->throwError(TypeError, "attributes is not an object");
|
---|
5712 | const char *err = obj->defineProperty(name, attributes);
|
---|
5713 | if(err) c->throwError(TypeError, err);
|
---|
5714 | c->setReturnVar(obj);
|
---|
5715 | }
|
---|
5716 |
|
---|
5717 | void CTinyJS::native_Object_defineProperties(const CFunctionsScopePtr &c, void *data) {
|
---|
5718 | bool ObjectCreate = data!=0;
|
---|
5719 | CScriptVarPtr obj = c->getArgument(0);
|
---|
5720 | if(ObjectCreate) {
|
---|
5721 | if(!obj->isObject() && !obj->isNull()) c->throwError(TypeError, "argument is not an object or null");
|
---|
5722 | obj = newScriptVar(Object, obj);
|
---|
5723 | } else
|
---|
5724 | if(!obj->isObject()) c->throwError(TypeError, "argument is not an object");
|
---|
5725 | c->setReturnVar(obj);
|
---|
5726 | if(c->getArrayLength()<2) {
|
---|
5727 | if(ObjectCreate) return;
|
---|
5728 | c->throwError(TypeError, "Object.defineProperties requires 2 arguments");
|
---|
5729 | }
|
---|
5730 |
|
---|
5731 | CScriptVarPtr properties = c->getArgument(1);
|
---|
5732 |
|
---|
5733 | STRING_SET_t names;
|
---|
5734 | properties->keys(names, true);
|
---|
5735 |
|
---|
5736 | for(STRING_SET_it it=names.begin(); it!=names.end(); ++it) {
|
---|
5737 | CScriptVarPtr attributes = properties->findChildWithStringChars(*it).getter();
|
---|
5738 | if(!attributes->isObject()) c->throwError(TypeError, "descriptor for "+*it+" is not an object");
|
---|
5739 | const char *err = obj->defineProperty(*it, attributes);
|
---|
5740 | if(err) c->throwError(TypeError, err);
|
---|
5741 | }
|
---|
5742 | }
|
---|
5743 |
|
---|
5744 |
|
---|
5745 | //////////////////////////////////////////////////////////////////////////
|
---|
5746 | /// Object.prototype
|
---|
5747 | //////////////////////////////////////////////////////////////////////////
|
---|
5748 |
|
---|
5749 | void CTinyJS::native_Object_prototype_hasOwnProperty(const CFunctionsScopePtr &c, void *data) {
|
---|
5750 | CScriptVarPtr This = c->getArgument("this");
|
---|
5751 | string PropStr = c->getArgument("prop")->toString();
|
---|
5752 | CScriptVarLinkPtr Prop = This->findChild(PropStr);
|
---|
5753 | bool res = Prop && !Prop->getVarPtr()->isUndefined();
|
---|
5754 | if(!res) {
|
---|
5755 | CScriptVarStringPtr This_asString = This->getRawPrimitive();
|
---|
5756 | if(This_asString) {
|
---|
5757 | uint32_t Idx = isArrayIndex(PropStr);
|
---|
5758 | res = Idx!=uint32_t(-1) && Idx<This_asString->stringLength();
|
---|
5759 | }
|
---|
5760 | }
|
---|
5761 | c->setReturnVar(c->constScriptVar(res));
|
---|
5762 | }
|
---|
5763 | void CTinyJS::native_Object_prototype_valueOf(const CFunctionsScopePtr &c, void *data) {
|
---|
5764 | c->setReturnVar(c->getArgument("this")->valueOf_CallBack());
|
---|
5765 | }
|
---|
5766 | void CTinyJS::native_Object_prototype_toString(const CFunctionsScopePtr &c, void *data) {
|
---|
5767 | CScriptResult execute;
|
---|
5768 | int radix = 10;
|
---|
5769 | if(c->getArgumentsLength()>=1) radix = c->getArgument("radix")->toNumber().toInt32();
|
---|
5770 | c->setReturnVar(c->getArgument("this")->toString_CallBack(execute, radix));
|
---|
5771 | if(!execute) {
|
---|
5772 | // TODO
|
---|
5773 | }
|
---|
5774 | }
|
---|
5775 |
|
---|
5776 | //////////////////////////////////////////////////////////////////////////
|
---|
5777 | /// Array
|
---|
5778 | //////////////////////////////////////////////////////////////////////////
|
---|
5779 |
|
---|
5780 | void CTinyJS::native_Array(const CFunctionsScopePtr &c, void *data) {
|
---|
5781 | CScriptVarPtr returnVar = c->newScriptVar(Array);
|
---|
5782 | c->setReturnVar(returnVar);
|
---|
5783 | int length = c->getArgumentsLength();
|
---|
5784 | CScriptVarPtr Argument_0_Var = c->getArgument(0);
|
---|
5785 | if(data!=0 && length == 1 && Argument_0_Var->isNumber()) {
|
---|
5786 | CNumber Argument_0 = Argument_0_Var->toNumber();
|
---|
5787 | uint32_t new_size = Argument_0.toUInt32();
|
---|
5788 | if(Argument_0.isFinite() && Argument_0 == new_size)
|
---|
5789 | returnVar->setArrayIndex(new_size-1, constScriptVar(Undefined));
|
---|
5790 | else
|
---|
5791 | c->throwError(RangeError, "invalid array length");
|
---|
5792 | } else for(int i=0; i<length; i++)
|
---|
5793 | returnVar->setArrayIndex(i, c->getArgument(i));
|
---|
5794 | }
|
---|
5795 |
|
---|
5796 | //////////////////////////////////////////////////////////////////////////
|
---|
5797 | /// String
|
---|
5798 | //////////////////////////////////////////////////////////////////////////
|
---|
5799 |
|
---|
5800 | void CTinyJS::native_String(const CFunctionsScopePtr &c, void *data) {
|
---|
5801 | CScriptVarPtr arg;
|
---|
5802 | if(c->getArgumentsLength()==0)
|
---|
5803 | arg = newScriptVar("");
|
---|
5804 | else
|
---|
5805 | arg = newScriptVar(c->getArgument(0)->toString());
|
---|
5806 | if(data)
|
---|
5807 | c->setReturnVar(arg->toObject());
|
---|
5808 | else
|
---|
5809 | c->setReturnVar(arg);
|
---|
5810 | }
|
---|
5811 |
|
---|
5812 |
|
---|
5813 | //////////////////////////////////////////////////////////////////////////
|
---|
5814 | /// RegExp
|
---|
5815 | //////////////////////////////////////////////////////////////////////////
|
---|
5816 | #ifndef NO_REGEXP
|
---|
5817 |
|
---|
5818 | void CTinyJS::native_RegExp(const CFunctionsScopePtr &c, void *data) {
|
---|
5819 | int arglen = c->getArgumentsLength();
|
---|
5820 | string RegExp, Flags;
|
---|
5821 | if(arglen>=1) {
|
---|
5822 | RegExp = c->getArgument(0)->toString();
|
---|
5823 | try { regex(RegExp, regex_constants::ECMAScript); } catch(regex_error e) {
|
---|
5824 | c->throwError(SyntaxError, string(e.what())+" - "+CScriptVarRegExp::ErrorStr(e.code()));
|
---|
5825 | }
|
---|
5826 | if(arglen>=2) {
|
---|
5827 | Flags = c->getArgument(1)->toString();
|
---|
5828 | string::size_type pos = Flags.find_first_not_of("gimy");
|
---|
5829 | if(pos != string::npos) {
|
---|
5830 | c->throwError(SyntaxError, string("invalid regular expression flag ")+Flags[pos]);
|
---|
5831 | }
|
---|
5832 | }
|
---|
5833 | }
|
---|
5834 | c->setReturnVar(newScriptVar(RegExp, Flags));
|
---|
5835 | }
|
---|
5836 | #endif /* NO_REGEXP */
|
---|
5837 |
|
---|
5838 | //////////////////////////////////////////////////////////////////////////
|
---|
5839 | /// Number
|
---|
5840 | //////////////////////////////////////////////////////////////////////////
|
---|
5841 |
|
---|
5842 | void CTinyJS::native_Number(const CFunctionsScopePtr &c, void *data) {
|
---|
5843 | CScriptVarPtr arg;
|
---|
5844 | if(c->getArgumentsLength()==0)
|
---|
5845 | arg = newScriptVar(0);
|
---|
5846 | else
|
---|
5847 | arg = newScriptVar(c->getArgument(0)->toNumber());
|
---|
5848 | if(data)
|
---|
5849 | c->setReturnVar(arg->toObject());
|
---|
5850 | else
|
---|
5851 | c->setReturnVar(arg);
|
---|
5852 | }
|
---|
5853 |
|
---|
5854 |
|
---|
5855 | //////////////////////////////////////////////////////////////////////////
|
---|
5856 | /// Boolean
|
---|
5857 | //////////////////////////////////////////////////////////////////////////
|
---|
5858 |
|
---|
5859 | void CTinyJS::native_Boolean(const CFunctionsScopePtr &c, void *data) {
|
---|
5860 | CScriptVarPtr arg;
|
---|
5861 | if(c->getArgumentsLength()==0)
|
---|
5862 | arg = constScriptVar(false);
|
---|
5863 | else
|
---|
5864 | arg = constScriptVar(c->getArgument(0)->toBoolean());
|
---|
5865 | if(data)
|
---|
5866 | c->setReturnVar(arg->toObject());
|
---|
5867 | else
|
---|
5868 | c->setReturnVar(arg);
|
---|
5869 | }
|
---|
5870 |
|
---|
5871 | //////////////////////////////////////////////////////////////////////////
|
---|
5872 | /// Iterator
|
---|
5873 | //////////////////////////////////////////////////////////////////////////
|
---|
5874 |
|
---|
5875 | void CTinyJS::native_Iterator(const CFunctionsScopePtr &c, void *data) {
|
---|
5876 | if(c->getArgumentsLength()<1) c->throwError(TypeError, "missing argument 0 when calling function Iterator");
|
---|
5877 | c->setReturnVar(c->getArgument(0)->toIterator(c->getArgument(1)->toBoolean()?1:3));
|
---|
5878 | }
|
---|
5879 |
|
---|
5880 |
|
---|
5881 | //////////////////////////////////////////////////////////////////////////
|
---|
5882 | /// Function
|
---|
5883 | //////////////////////////////////////////////////////////////////////////
|
---|
5884 |
|
---|
5885 | void CTinyJS::native_Function(const CFunctionsScopePtr &c, void *data) {
|
---|
5886 | int length = c->getArgumentsLength();
|
---|
5887 | string params, body;
|
---|
5888 | if(length>=1)
|
---|
5889 | body = c->getArgument(length-1)->toString();
|
---|
5890 | if(length>=2) {
|
---|
5891 | params = c->getArgument(0)->toString();
|
---|
5892 | for(int i=1; i<length-1; i++)
|
---|
5893 | {
|
---|
5894 | params.append(",");
|
---|
5895 | params.append(c->getArgument(i)->toString());
|
---|
5896 | }
|
---|
5897 | }
|
---|
5898 | c->setReturnVar(parseFunctionsBodyFromString(params,body));
|
---|
5899 | }
|
---|
5900 |
|
---|
5901 | void CTinyJS::native_Function_prototype_call(const CFunctionsScopePtr &c, void *data) {
|
---|
5902 | int length = c->getArgumentsLength();
|
---|
5903 | CScriptVarPtr Fnc = c->getArgument("this");
|
---|
5904 | if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.call called on incompatible Object");
|
---|
5905 | CScriptVarPtr This = c->getArgument(0);
|
---|
5906 | vector<CScriptVarPtr> Args;
|
---|
5907 | for(int i=1; i<length; i++)
|
---|
5908 | Args.push_back(c->getArgument(i));
|
---|
5909 | c->setReturnVar(callFunction(Fnc, Args, This));
|
---|
5910 | }
|
---|
5911 | void CTinyJS::native_Function_prototype_apply(const CFunctionsScopePtr &c, void *data) {
|
---|
5912 | int length=0;
|
---|
5913 | CScriptVarPtr Fnc = c->getArgument("this");
|
---|
5914 | if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.apply called on incompatible Object");
|
---|
5915 | // Argument_0
|
---|
5916 | CScriptVarPtr This = c->getArgument(0)->toObject();
|
---|
5917 | if(This->isNull() || This->isUndefined()) This=root;
|
---|
5918 | // Argument_1
|
---|
5919 | CScriptVarPtr Array = c->getArgument(1);
|
---|
5920 | if(!Array->isNull() && !Array->isUndefined()) {
|
---|
5921 | CScriptVarLinkWorkPtr Length = Array->findChild("length");
|
---|
5922 | if(!Length) c->throwError(TypeError, "second argument to Function.prototype.apply must be an array or an array like object");
|
---|
5923 | length = Length.getter()->toNumber().toInt32();
|
---|
5924 | }
|
---|
5925 | vector<CScriptVarPtr> Args;
|
---|
5926 | for(int i=0; i<length; i++) {
|
---|
5927 | CScriptVarLinkPtr value = Array->findChild(int2string(i));
|
---|
5928 | if(value) Args.push_back(value);
|
---|
5929 | else Args.push_back(constScriptVar(Undefined));
|
---|
5930 | }
|
---|
5931 | c->setReturnVar(callFunction(Fnc, Args, This));
|
---|
5932 | }
|
---|
5933 | void CTinyJS::native_Function_prototype_bind(const CFunctionsScopePtr &c, void *data) {
|
---|
5934 | int length = c->getArgumentsLength();
|
---|
5935 | CScriptVarPtr Fnc = c->getArgument("this");
|
---|
5936 | if(!Fnc->isFunction()) c->throwError(TypeError, "Function.prototype.bind called on incompatible Object");
|
---|
5937 | CScriptVarPtr This = c->getArgument(0);
|
---|
5938 | if(This->isUndefined() || This->isNull()) This = root;
|
---|
5939 | vector<CScriptVarPtr> Args;
|
---|
5940 | for(int i=1; i<length; i++) Args.push_back(c->getArgument(i));
|
---|
5941 | c->setReturnVar(newScriptVarFunctionBounded(Fnc, This, Args));
|
---|
5942 | }
|
---|
5943 |
|
---|
5944 |
|
---|
5945 | //////////////////////////////////////////////////////////////////////////
|
---|
5946 | /// Error
|
---|
5947 | //////////////////////////////////////////////////////////////////////////
|
---|
5948 |
|
---|
5949 | static CScriptVarPtr _newError(CTinyJS *context, ERROR_TYPES type, const CFunctionsScopePtr &c) {
|
---|
5950 | int i = c->getArgumentsLength();
|
---|
5951 | string message, fileName;
|
---|
5952 | int line=-1, column=-1;
|
---|
5953 | if(i>0) message = c->getArgument(0)->toString();
|
---|
5954 | if(i>1) fileName = c->getArgument(1)->toString();
|
---|
5955 | if(i>2) line = c->getArgument(2)->toNumber().toInt32();
|
---|
5956 | if(i>3) column = c->getArgument(3)->toNumber().toInt32();
|
---|
5957 | return ::newScriptVarError(context, type, message.c_str(), fileName.c_str(), line, column);
|
---|
5958 | }
|
---|
5959 | void CTinyJS::native_Error(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, Error,c)); }
|
---|
5960 | void CTinyJS::native_EvalError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, EvalError,c)); }
|
---|
5961 | void CTinyJS::native_RangeError(const CFunctionsScopePtr &c, void *data) { c->setReturnVar(_newError(this, RangeError,c)); }
|
---|
5962 | void CTinyJS::native_ReferenceError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, ReferenceError,c)); }
|
---|
5963 | void CTinyJS::native_SyntaxError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, SyntaxError,c)); }
|
---|
5964 | void CTinyJS::native_TypeError(const CFunctionsScopePtr &c, void *data){ c->setReturnVar(_newError(this, TypeError,c)); }
|
---|
5965 |
|
---|
5966 | //////////////////////////////////////////////////////////////////////////
|
---|
5967 | /// global functions
|
---|
5968 | //////////////////////////////////////////////////////////////////////////
|
---|
5969 |
|
---|
5970 | void CTinyJS::native_eval(const CFunctionsScopePtr &c, void *data) {
|
---|
5971 | string Code = c->getArgument("jsCode")->toString();
|
---|
5972 | CScriptVarScopePtr scEvalScope = scopes.back(); // save scope
|
---|
5973 | scopes.pop_back(); // go back to the callers scope
|
---|
5974 | CScriptResult execute;
|
---|
5975 | CScriptTokenizer *oldTokenizer = t; t=0;
|
---|
5976 | try {
|
---|
5977 | CScriptTokenizer Tokenizer(Code.c_str(), "eval");
|
---|
5978 | t = &Tokenizer;
|
---|
5979 | do {
|
---|
5980 | execute_statement(execute);
|
---|
5981 | while (t->tk==';') t->match(';'); // skip empty statements
|
---|
5982 | } while (t->tk!=LEX_EOF);
|
---|
5983 | } catch (CScriptException *e) { // script exceptions
|
---|
5984 | t = oldTokenizer; // restore tokenizer
|
---|
5985 | scopes.push_back(scEvalScope); // restore Scopes;
|
---|
5986 | if(haveTry) { // an Error in eval is always catchable
|
---|
5987 | CScriptVarPtr E = newScriptVarError(this, e->errorType, e->message.c_str(), e->fileName.c_str(), e->lineNumber, e->column);
|
---|
5988 | delete e;
|
---|
5989 | throw E;
|
---|
5990 | } else
|
---|
5991 | throw e;
|
---|
5992 | } catch (...) { // all other exceptions
|
---|
5993 | t = oldTokenizer; // restore tokenizer
|
---|
5994 | scopes.push_back(scEvalScope); // restore Scopes;
|
---|
5995 | throw; // re-throw
|
---|
5996 | }
|
---|
5997 | t = oldTokenizer; // restore tokenizer
|
---|
5998 | scopes.push_back(scEvalScope); // restore Scopes;
|
---|
5999 | if(execute.value)
|
---|
6000 | c->setReturnVar(execute.value);
|
---|
6001 | }
|
---|
6002 |
|
---|
6003 | void CTinyJS::native_isNAN(const CFunctionsScopePtr &c, void *data) {
|
---|
6004 | c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isNaN()));
|
---|
6005 | }
|
---|
6006 |
|
---|
6007 | void CTinyJS::native_isFinite(const CFunctionsScopePtr &c, void *data) {
|
---|
6008 | c->setReturnVar(constScriptVar(c->getArgument("objc")->toNumber().isFinite()));
|
---|
6009 | }
|
---|
6010 |
|
---|
6011 | void CTinyJS::native_parseInt(const CFunctionsScopePtr &c, void *) {
|
---|
6012 | CNumber result;
|
---|
6013 | result.parseInt(c->getArgument("string")->toString(), c->getArgument("radix")->toNumber().toInt32());
|
---|
6014 | c->setReturnVar(c->newScriptVar(result));
|
---|
6015 | }
|
---|
6016 |
|
---|
6017 | void CTinyJS::native_parseFloat(const CFunctionsScopePtr &c, void *) {
|
---|
6018 | CNumber result;
|
---|
6019 | result.parseFloat(c->getArgument("string")->toString());
|
---|
6020 | c->setReturnVar(c->newScriptVar(result));
|
---|
6021 | }
|
---|
6022 |
|
---|
6023 |
|
---|
6024 |
|
---|
6025 | void CTinyJS::native_JSON_parse(const CFunctionsScopePtr &c, void *data) {
|
---|
6026 | string Code = "§" + c->getArgument("text")->toString();
|
---|
6027 | // "§" is a spezal-token - it's for the tokenizer and means the code begins not in Statement-level
|
---|
6028 | CScriptVarLinkWorkPtr returnVar;
|
---|
6029 | CScriptTokenizer *oldTokenizer = t; t=0;
|
---|
6030 | try {
|
---|
6031 | CScriptTokenizer Tokenizer(Code.c_str(), "JSON.parse", 0, -1);
|
---|
6032 | t = &Tokenizer;
|
---|
6033 | CScriptResult execute;
|
---|
6034 | returnVar = execute_literals(execute);
|
---|
6035 | t->match(LEX_EOF);
|
---|
6036 | } catch (CScriptException *e) {
|
---|
6037 | t = oldTokenizer;
|
---|
6038 | throw e;
|
---|
6039 | }
|
---|
6040 | t = oldTokenizer;
|
---|
6041 |
|
---|
6042 | if(returnVar)
|
---|
6043 | c->setReturnVar(returnVar);
|
---|
6044 | }
|
---|
6045 |
|
---|
6046 | void CTinyJS::setTemporaryID_recursive(uint32_t ID) {
|
---|
6047 | for(vector<CScriptVarPtr*>::iterator it = pseudo_refered.begin(); it!=pseudo_refered.end(); ++it)
|
---|
6048 | if(**it) (**it)->setTemporaryID_recursive(ID);
|
---|
6049 | for(int i=Error; i<ERROR_COUNT; i++)
|
---|
6050 | if(errorPrototypes[i]) errorPrototypes[i]->setTemporaryID_recursive(ID);
|
---|
6051 | root->setTemporaryID_recursive(ID);
|
---|
6052 | }
|
---|
6053 |
|
---|
6054 | void CTinyJS::ClearUnreferedVars(const CScriptVarPtr &extra/*=CScriptVarPtr()*/) {
|
---|
6055 | uint32_t UniqueID = getUniqueID();
|
---|
6056 | setTemporaryID_recursive(UniqueID);
|
---|
6057 | if(extra) extra->setTemporaryID_recursive(UniqueID);
|
---|
6058 | CScriptVar *p = first;
|
---|
6059 | while(p)
|
---|
6060 | {
|
---|
6061 | if(p->temporaryID != UniqueID)
|
---|
6062 | {
|
---|
6063 | CScriptVarPtr var = p;
|
---|
6064 | var->removeAllChildren();
|
---|
6065 | p = var->next;
|
---|
6066 | }
|
---|
6067 | else
|
---|
6068 | p = p->next;
|
---|
6069 | }
|
---|
6070 | }
|
---|
6071 |
|
---|