1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; -*- |
2 |
* vim: set ts=8 sw=4 et tw=99: |
3 |
* |
4 |
* ***** BEGIN LICENSE BLOCK ***** |
5 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
6 |
* |
7 |
* The contents of this file are subject to the Mozilla Public License Version |
8 |
* 1.1 (the "License"); you may not use this file except in compliance with |
9 |
* the License. You may obtain a copy of the License at |
10 |
* http://www.mozilla.org/MPL/ |
11 |
* |
12 |
* Software distributed under the License is distributed on an "AS IS" basis, |
13 |
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
14 |
* for the specific language governing rights and limitations under the |
15 |
* License. |
16 |
* |
17 |
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released |
18 |
* May 28, 2008. |
19 |
* |
20 |
* The Initial Developer of the Original Code is |
21 |
* Andreas Gal <gal@mozilla.com> |
22 |
* |
23 |
* Contributor(s): |
24 |
* Brendan Eich <brendan@mozilla.org> |
25 |
* Mike Shaver <shaver@mozilla.org> |
26 |
* David Anderson <danderson@mozilla.com> |
27 |
* |
28 |
* Alternatively, the contents of this file may be used under the terms of |
29 |
* either of the GNU General Public License Version 2 or later (the "GPL"), |
30 |
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
31 |
* in which case the provisions of the GPL or the LGPL are applicable instead |
32 |
* of those above. If you wish to allow use of your version of this file only |
33 |
* under the terms of either the GPL or the LGPL, and not to allow others to |
34 |
* use your version of this file under the terms of the MPL, indicate your |
35 |
* decision by deleting the provisions above and replace them with the notice |
36 |
* and other provisions required by the GPL or the LGPL. If you do not delete |
37 |
* the provisions above, a recipient may use your version of this file under |
38 |
* the terms of any one of the MPL, the GPL or the LGPL. |
39 |
* |
40 |
* ***** END LICENSE BLOCK ***** */ |
41 |
|
42 |
#include "jsstddef.h" |
43 |
#include <math.h> |
44 |
|
45 |
#include "jsapi.h" |
46 |
#include "jsarray.h" |
47 |
#include "jsbool.h" |
48 |
#include "jscntxt.h" |
49 |
#include "jsgc.h" |
50 |
#include "jsiter.h" |
51 |
#include "jslibmath.h" |
52 |
#include "jsmath.h" |
53 |
#include "jsnum.h" |
54 |
#include "prmjtime.h" |
55 |
#include "jsdate.h" |
56 |
#include "jsscope.h" |
57 |
#include "jsstr.h" |
58 |
#include "jsbuiltins.h" |
59 |
#include "jstracer.h" |
60 |
|
61 |
using namespace avmplus; |
62 |
using namespace nanojit; |
63 |
|
64 |
extern jsdouble js_NaN; |
65 |
|
66 |
/* |
67 |
* NB: bool FASTCALL is not compatible with Nanojit's calling convention usage. |
68 |
* Do not use bool FASTCALL, use JSBool only! |
69 |
*/ |
70 |
|
71 |
jsdouble FASTCALL |
72 |
js_dmod(jsdouble a, jsdouble b) |
73 |
{ |
74 |
if (b == 0.0) { |
75 |
jsdpun u; |
76 |
u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; |
77 |
u.s.lo = 0xffffffff; |
78 |
return u.d; |
79 |
} |
80 |
jsdouble r; |
81 |
#ifdef XP_WIN |
82 |
/* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ |
83 |
if (JSDOUBLE_IS_FINITE(a) && JSDOUBLE_IS_INFINITE(b)) |
84 |
r = a; |
85 |
else |
86 |
#endif |
87 |
r = fmod(a, b); |
88 |
return r; |
89 |
} |
90 |
|
91 |
int32 FASTCALL |
92 |
js_imod(int32 a, int32 b) |
93 |
{ |
94 |
if (a < 0 || b <= 0) |
95 |
return -1; |
96 |
int r = a % b; |
97 |
return r; |
98 |
} |
99 |
|
100 |
/* The following boxing/unboxing primitives we can't emit inline because |
101 |
they either interact with the GC and depend on Spidermonkey's 32-bit |
102 |
integer representation. */ |
103 |
|
104 |
jsval FASTCALL |
105 |
js_BoxDouble(JSContext* cx, jsdouble d) |
106 |
{ |
107 |
int32 i; |
108 |
if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) |
109 |
return INT_TO_JSVAL(i); |
110 |
JS_ASSERT(JS_ON_TRACE(cx)); |
111 |
jsval v; /* not rooted but ok here because we know GC won't run */ |
112 |
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
113 |
return JSVAL_ERROR_COOKIE; |
114 |
return v; |
115 |
} |
116 |
|
117 |
jsval FASTCALL |
118 |
js_BoxInt32(JSContext* cx, int32 i) |
119 |
{ |
120 |
if (JS_LIKELY(INT_FITS_IN_JSVAL(i))) |
121 |
return INT_TO_JSVAL(i); |
122 |
JS_ASSERT(JS_ON_TRACE(cx)); |
123 |
jsval v; /* not rooted but ok here because we know GC won't run */ |
124 |
jsdouble d = (jsdouble)i; |
125 |
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
126 |
return JSVAL_ERROR_COOKIE; |
127 |
return v; |
128 |
} |
129 |
|
130 |
jsdouble FASTCALL |
131 |
js_UnboxDouble(jsval v) |
132 |
{ |
133 |
if (JS_LIKELY(JSVAL_IS_INT(v))) |
134 |
return (jsdouble)JSVAL_TO_INT(v); |
135 |
return *JSVAL_TO_DOUBLE(v); |
136 |
} |
137 |
|
138 |
int32 FASTCALL |
139 |
js_UnboxInt32(jsval v) |
140 |
{ |
141 |
if (JS_LIKELY(JSVAL_IS_INT(v))) |
142 |
return JSVAL_TO_INT(v); |
143 |
return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v)); |
144 |
} |
145 |
|
146 |
int32 FASTCALL |
147 |
js_DoubleToInt32(jsdouble d) |
148 |
{ |
149 |
return js_DoubleToECMAInt32(d); |
150 |
} |
151 |
|
152 |
uint32 FASTCALL |
153 |
js_DoubleToUint32(jsdouble d) |
154 |
{ |
155 |
return js_DoubleToECMAUint32(d); |
156 |
} |
157 |
|
158 |
jsdouble FASTCALL |
159 |
js_StringToNumber(JSContext* cx, JSString* str) |
160 |
{ |
161 |
const jschar* bp; |
162 |
const jschar* end; |
163 |
const jschar* ep; |
164 |
jsdouble d; |
165 |
|
166 |
JSSTRING_CHARS_AND_END(str, bp, end); |
167 |
if ((!js_strtod(cx, bp, end, &ep, &d) || |
168 |
js_SkipWhiteSpace(ep, end) != end) && |
169 |
(!js_strtointeger(cx, bp, end, &ep, 0, &d) || |
170 |
js_SkipWhiteSpace(ep, end) != end)) { |
171 |
return js_NaN; |
172 |
} |
173 |
return d; |
174 |
} |
175 |
|
176 |
int32 FASTCALL |
177 |
js_StringToInt32(JSContext* cx, JSString* str) |
178 |
{ |
179 |
const jschar* bp; |
180 |
const jschar* end; |
181 |
const jschar* ep; |
182 |
jsdouble d; |
183 |
|
184 |
JSSTRING_CHARS_AND_END(str, bp, end); |
185 |
if (!js_strtod(cx, bp, end, &ep, &d) || js_SkipWhiteSpace(ep, end) != end) |
186 |
return 0; |
187 |
return js_DoubleToECMAInt32(d); |
188 |
} |
189 |
|
190 |
static inline JSBool |
191 |
js_Int32ToId(JSContext* cx, int32 index, jsid* id) |
192 |
{ |
193 |
if (index <= JSVAL_INT_MAX) { |
194 |
*id = INT_TO_JSID(index); |
195 |
return JS_TRUE; |
196 |
} |
197 |
JSString* str = js_NumberToString(cx, index); |
198 |
if (!str) |
199 |
return JS_FALSE; |
200 |
return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id); |
201 |
} |
202 |
|
203 |
jsval FASTCALL |
204 |
js_Any_getprop(JSContext* cx, JSObject* obj, JSString* idstr) |
205 |
{ |
206 |
jsval v; |
207 |
jsid id; |
208 |
|
209 |
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id)) |
210 |
return JSVAL_ERROR_COOKIE; |
211 |
if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) |
212 |
return JSVAL_ERROR_COOKIE; |
213 |
return v; |
214 |
} |
215 |
|
216 |
JSBool FASTCALL |
217 |
js_Any_setprop(JSContext* cx, JSObject* obj, JSString* idstr, jsval v) |
218 |
{ |
219 |
jsid id; |
220 |
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id)) |
221 |
return JS_FALSE; |
222 |
return OBJ_SET_PROPERTY(cx, obj, id, &v); |
223 |
} |
224 |
|
225 |
jsval FASTCALL |
226 |
js_Any_getelem(JSContext* cx, JSObject* obj, int32 index) |
227 |
{ |
228 |
jsval v; |
229 |
jsid id; |
230 |
if (!js_Int32ToId(cx, index, &id)) |
231 |
return JSVAL_ERROR_COOKIE; |
232 |
if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) |
233 |
return JSVAL_ERROR_COOKIE; |
234 |
return v; |
235 |
} |
236 |
|
237 |
JSBool FASTCALL |
238 |
js_Any_setelem(JSContext* cx, JSObject* obj, int32 index, jsval v) |
239 |
{ |
240 |
jsid id; |
241 |
if (!js_Int32ToId(cx, index, &id)) |
242 |
return JSVAL_ERROR_COOKIE; |
243 |
return OBJ_SET_PROPERTY(cx, obj, id, &v); |
244 |
} |
245 |
|
246 |
JSObject* FASTCALL |
247 |
js_FastValueToIterator(JSContext* cx, jsuint flags, jsval v) |
248 |
{ |
249 |
if (!js_ValueToIterator(cx, flags, &v)) |
250 |
return NULL; |
251 |
return JSVAL_TO_OBJECT(v); |
252 |
} |
253 |
|
254 |
jsval FASTCALL |
255 |
js_FastCallIteratorNext(JSContext* cx, JSObject* iterobj) |
256 |
{ |
257 |
jsval v; |
258 |
if (!js_CallIteratorNext(cx, iterobj, &v)) |
259 |
return JSVAL_ERROR_COOKIE; |
260 |
return v; |
261 |
} |
262 |
|
263 |
SideExit* FASTCALL |
264 |
js_CallTree(InterpState* state, Fragment* f) |
265 |
{ |
266 |
union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u; |
267 |
|
268 |
u.code = f->code(); |
269 |
JS_ASSERT(u.code); |
270 |
|
271 |
GuardRecord* rec; |
272 |
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) |
273 |
SIMULATE_FASTCALL(rec, state, NULL, u.func); |
274 |
#else |
275 |
rec = u.func(state, NULL); |
276 |
#endif |
277 |
VMSideExit* lr = (VMSideExit*)rec->exit; |
278 |
|
279 |
if (lr->exitType == NESTED_EXIT) { |
280 |
/* This only occurs once a tree call guard mismatches and we unwind the tree call stack. |
281 |
We store the first (innermost) tree call guard in state and we will try to grow |
282 |
the outer tree the failing call was in starting at that guard. */ |
283 |
if (!state->lastTreeCallGuard) { |
284 |
state->lastTreeCallGuard = lr; |
285 |
FrameInfo* rp = (FrameInfo*)state->rp; |
286 |
state->rpAtLastTreeCall = rp + lr->calldepth; |
287 |
} |
288 |
} else { |
289 |
/* If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard |
290 |
with that guard. If we mismatch on a tree call guard, this will contain the last |
291 |
non-nested guard we encountered, which is the innermost loop or branch guard. */ |
292 |
state->lastTreeExitGuard = lr; |
293 |
} |
294 |
|
295 |
return lr; |
296 |
} |
297 |
|
298 |
JSObject* FASTCALL |
299 |
js_FastNewObject(JSContext* cx, JSObject* ctor) |
300 |
{ |
301 |
JS_ASSERT(HAS_FUNCTION_CLASS(ctor)); |
302 |
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, ctor); |
303 |
JSClass* clasp = (FUN_INTERPRETED(fun) || (fun->flags & JSFUN_TRACEABLE)) |
304 |
? &js_ObjectClass |
305 |
: FUN_CLASP(fun); |
306 |
JS_ASSERT(clasp != &js_ArrayClass); |
307 |
|
308 |
JS_LOCK_OBJ(cx, ctor); |
309 |
JSScope *scope = OBJ_SCOPE(ctor); |
310 |
JS_ASSERT(scope->object == ctor); |
311 |
JSAtom* atom = cx->runtime->atomState.classPrototypeAtom; |
312 |
|
313 |
JSScopeProperty *sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); |
314 |
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope)); |
315 |
jsval v = LOCKED_OBJ_GET_SLOT(ctor, sprop->slot); |
316 |
JS_UNLOCK_SCOPE(cx, scope); |
317 |
|
318 |
JSObject* proto; |
319 |
if (JSVAL_IS_PRIMITIVE(v)) { |
320 |
if (!js_GetClassPrototype(cx, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT]), |
321 |
INT_TO_JSID(JSProto_Object), &proto)) { |
322 |
return NULL; |
323 |
} |
324 |
} else { |
325 |
proto = JSVAL_TO_OBJECT(v); |
326 |
} |
327 |
|
328 |
JS_ASSERT(JS_ON_TRACE(cx)); |
329 |
JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); |
330 |
if (!obj) |
331 |
return NULL; |
332 |
|
333 |
obj->classword = jsuword(clasp); |
334 |
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
335 |
obj->fslots[JSSLOT_PARENT] = ctor->fslots[JSSLOT_PARENT]; |
336 |
for (unsigned i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i) |
337 |
obj->fslots[i] = JSVAL_VOID; |
338 |
|
339 |
obj->map = js_HoldObjectMap(cx, proto->map); |
340 |
obj->dslots = NULL; |
341 |
return obj; |
342 |
} |
343 |
|
344 |
JSBool FASTCALL |
345 |
js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) |
346 |
{ |
347 |
JSScopeProperty* sprop2 = NULL; // initialize early to make MSVC happy |
348 |
|
349 |
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
350 |
JS_ASSERT(SPROP_HAS_STUB_SETTER(sprop)); |
351 |
|
352 |
JS_LOCK_OBJ(cx, obj); |
353 |
JSScope* scope = OBJ_SCOPE(obj); |
354 |
if (scope->object == obj) { |
355 |
JS_ASSERT(!SCOPE_HAS_PROPERTY(scope, sprop)); |
356 |
} else { |
357 |
scope = js_GetMutableScope(cx, obj); |
358 |
if (!scope) { |
359 |
JS_UNLOCK_OBJ(cx, obj); |
360 |
return JS_FALSE; |
361 |
} |
362 |
} |
363 |
|
364 |
uint32 slot = sprop->slot; |
365 |
if (!scope->table && sprop->parent == scope->lastProp && slot == scope->map.freeslot) { |
366 |
if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { |
367 |
JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->map.freeslot))); |
368 |
++scope->map.freeslot; |
369 |
} else { |
370 |
if (!js_AllocSlot(cx, obj, &slot)) { |
371 |
JS_UNLOCK_SCOPE(cx, scope); |
372 |
return JS_FALSE; |
373 |
} |
374 |
|
375 |
if (slot != sprop->slot) |
376 |
goto slot_changed; |
377 |
} |
378 |
|
379 |
SCOPE_EXTEND_SHAPE(cx, scope, sprop); |
380 |
++scope->entryCount; |
381 |
scope->lastProp = sprop; |
382 |
JS_UNLOCK_SCOPE(cx, scope); |
383 |
return JS_TRUE; |
384 |
} |
385 |
|
386 |
sprop2 = js_AddScopeProperty(cx, scope, sprop->id, |
387 |
sprop->getter, sprop->setter, SPROP_INVALID_SLOT, |
388 |
sprop->attrs, sprop->flags, sprop->shortid); |
389 |
if (sprop2 == sprop) { |
390 |
JS_UNLOCK_SCOPE(cx, scope); |
391 |
return JS_TRUE; |
392 |
} |
393 |
slot = sprop2->slot; |
394 |
|
395 |
slot_changed: |
396 |
js_FreeSlot(cx, obj, slot); |
397 |
JS_UNLOCK_SCOPE(cx, scope); |
398 |
return JS_FALSE; |
399 |
} |
400 |
|
401 |
JSBool FASTCALL |
402 |
js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr) |
403 |
{ |
404 |
jsid id; |
405 |
if (!obj || !js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id)) |
406 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
407 |
|
408 |
JSObject* obj2; |
409 |
JSProperty* prop; |
410 |
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) |
411 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
412 |
if (prop) |
413 |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
414 |
return prop != NULL; |
415 |
} |
416 |
|
417 |
JSBool FASTCALL |
418 |
js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index) |
419 |
{ |
420 |
jsid id; |
421 |
if (!obj || !js_Int32ToId(cx, index, &id)) |
422 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
423 |
|
424 |
JSObject* obj2; |
425 |
JSProperty* prop; |
426 |
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) |
427 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
428 |
if (prop) |
429 |
OBJ_DROP_PROPERTY(cx, obj2, prop); |
430 |
return prop != NULL; |
431 |
} |
432 |
|
433 |
jsval FASTCALL |
434 |
js_CallGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) |
435 |
{ |
436 |
JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop)); |
437 |
jsval v; |
438 |
if (!SPROP_GET(cx, sprop, obj, obj, &v)) |
439 |
return JSVAL_ERROR_COOKIE; |
440 |
return v; |
441 |
} |
442 |
|
443 |
JSString* FASTCALL |
444 |
js_TypeOfObject(JSContext* cx, JSObject* obj) |
445 |
{ |
446 |
JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj)); |
447 |
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); |
448 |
} |
449 |
|
450 |
JSString* FASTCALL |
451 |
js_TypeOfBoolean(JSContext* cx, int32 unboxed) |
452 |
{ |
453 |
jsval boxed = BOOLEAN_TO_JSVAL(unboxed); |
454 |
JS_ASSERT(JSVAL_IS_VOID(boxed) || JSVAL_IS_BOOLEAN(boxed)); |
455 |
JSType type = JS_TypeOfValue(cx, boxed); |
456 |
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); |
457 |
} |
458 |
|
459 |
jsdouble FASTCALL |
460 |
js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed) |
461 |
{ |
462 |
if (unboxed == JSVAL_TO_BOOLEAN(JSVAL_VOID)) |
463 |
return js_NaN; |
464 |
return unboxed; |
465 |
} |
466 |
|
467 |
JSString* FASTCALL |
468 |
js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed) |
469 |
{ |
470 |
JS_ASSERT(uint32(unboxed) <= 2); |
471 |
return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[unboxed]); |
472 |
} |
473 |
|
474 |
JSString* FASTCALL |
475 |
js_ObjectToString(JSContext* cx, JSObject* obj) |
476 |
{ |
477 |
if (!obj) |
478 |
return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); |
479 |
jsval v; |
480 |
if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) |
481 |
return NULL; |
482 |
JS_ASSERT(JSVAL_IS_STRING(v)); |
483 |
return JSVAL_TO_STRING(v); |
484 |
} |
485 |
|
486 |
JSObject* FASTCALL |
487 |
js_Arguments(JSContext* cx) |
488 |
{ |
489 |
return NULL; |
490 |
} |
491 |
|
492 |
#define BUILTIN1 JS_DEFINE_CALLINFO_1 |
493 |
#define BUILTIN2 JS_DEFINE_CALLINFO_2 |
494 |
#define BUILTIN3 JS_DEFINE_CALLINFO_3 |
495 |
#define BUILTIN4 JS_DEFINE_CALLINFO_4 |
496 |
#define BUILTIN5 JS_DEFINE_CALLINFO_5 |
497 |
#include "builtins.tbl" |