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 <math.h> |
43 |
|
44 |
#include "jsapi.h" |
45 |
#include "jsstdint.h" |
46 |
#include "jsarray.h" |
47 |
#include "jsbool.h" |
48 |
#include "jscntxt.h" |
49 |
#include "jsgc.h" |
50 |
#include "jsiter.h" |
51 |
#include "jsnum.h" |
52 |
#include "jslibmath.h" |
53 |
#include "jsmath.h" |
54 |
#include "jsnum.h" |
55 |
#include "prmjtime.h" |
56 |
#include "jsdate.h" |
57 |
#include "jsscope.h" |
58 |
#include "jsstr.h" |
59 |
#include "jsbuiltins.h" |
60 |
#include "jstracer.h" |
61 |
#include "jsvector.h" |
62 |
|
63 |
#include "jsatominlines.h" |
64 |
#include "jsobjinlines.h" |
65 |
|
66 |
using namespace avmplus; |
67 |
using namespace nanojit; |
68 |
|
69 |
extern jsdouble js_NaN; |
70 |
|
71 |
JS_FRIEND_API(void) |
72 |
js_SetTraceableNativeFailed(JSContext *cx) |
73 |
{ |
74 |
js_SetBuiltinError(cx); |
75 |
} |
76 |
|
77 |
/* |
78 |
* NB: bool FASTCALL is not compatible with Nanojit's calling convention usage. |
79 |
* Do not use bool FASTCALL, use JSBool only! |
80 |
*/ |
81 |
|
82 |
jsdouble FASTCALL |
83 |
js_dmod(jsdouble a, jsdouble b) |
84 |
{ |
85 |
if (b == 0.0) { |
86 |
jsdpun u; |
87 |
u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; |
88 |
u.s.lo = 0xffffffff; |
89 |
return u.d; |
90 |
} |
91 |
return js_fmod(a, b); |
92 |
} |
93 |
JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_dmod, DOUBLE, DOUBLE, 1, 1) |
94 |
|
95 |
int32 FASTCALL |
96 |
js_imod(int32 a, int32 b) |
97 |
{ |
98 |
if (a < 0 || b <= 0) |
99 |
return -1; |
100 |
int r = a % b; |
101 |
return r; |
102 |
} |
103 |
JS_DEFINE_CALLINFO_2(extern, INT32, js_imod, INT32, INT32, 1, 1) |
104 |
|
105 |
/* The following boxing/unboxing primitives we can't emit inline because |
106 |
they either interact with the GC and depend on Spidermonkey's 32-bit |
107 |
integer representation. */ |
108 |
|
109 |
jsval FASTCALL |
110 |
js_BoxDouble(JSContext* cx, jsdouble d) |
111 |
{ |
112 |
int32 i; |
113 |
if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) |
114 |
return INT_TO_JSVAL(i); |
115 |
JS_ASSERT(JS_ON_TRACE(cx)); |
116 |
jsval v; /* not rooted but ok here because we know GC won't run */ |
117 |
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
118 |
return JSVAL_ERROR_COOKIE; |
119 |
return v; |
120 |
} |
121 |
JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxDouble, CONTEXT, DOUBLE, 1, 1) |
122 |
|
123 |
jsval FASTCALL |
124 |
js_BoxInt32(JSContext* cx, int32 i) |
125 |
{ |
126 |
if (JS_LIKELY(INT_FITS_IN_JSVAL(i))) |
127 |
return INT_TO_JSVAL(i); |
128 |
JS_ASSERT(JS_ON_TRACE(cx)); |
129 |
jsval v; /* not rooted but ok here because we know GC won't run */ |
130 |
jsdouble d = (jsdouble)i; |
131 |
if (!js_NewDoubleInRootedValue(cx, d, &v)) |
132 |
return JSVAL_ERROR_COOKIE; |
133 |
return v; |
134 |
} |
135 |
JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxInt32, CONTEXT, INT32, 1, 1) |
136 |
|
137 |
jsdouble FASTCALL |
138 |
js_UnboxDouble(jsval v) |
139 |
{ |
140 |
if (JS_LIKELY(JSVAL_IS_INT(v))) |
141 |
return (jsdouble)JSVAL_TO_INT(v); |
142 |
return *JSVAL_TO_DOUBLE(v); |
143 |
} |
144 |
JS_DEFINE_CALLINFO_1(extern, DOUBLE, js_UnboxDouble, JSVAL, 1, 1) |
145 |
|
146 |
int32 FASTCALL |
147 |
js_UnboxInt32(jsval v) |
148 |
{ |
149 |
if (JS_LIKELY(JSVAL_IS_INT(v))) |
150 |
return JSVAL_TO_INT(v); |
151 |
return js_DoubleToECMAInt32(*JSVAL_TO_DOUBLE(v)); |
152 |
} |
153 |
JS_DEFINE_CALLINFO_1(extern, INT32, js_UnboxInt32, JSVAL, 1, 1) |
154 |
|
155 |
int32 FASTCALL |
156 |
js_DoubleToInt32(jsdouble d) |
157 |
{ |
158 |
return js_DoubleToECMAInt32(d); |
159 |
} |
160 |
JS_DEFINE_CALLINFO_1(extern, INT32, js_DoubleToInt32, DOUBLE, 1, 1) |
161 |
|
162 |
uint32 FASTCALL |
163 |
js_DoubleToUint32(jsdouble d) |
164 |
{ |
165 |
return js_DoubleToECMAUint32(d); |
166 |
} |
167 |
JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, 1) |
168 |
|
169 |
jsdouble FASTCALL |
170 |
js_StringToNumber(JSContext* cx, JSString* str) |
171 |
{ |
172 |
const jschar* bp; |
173 |
const jschar* end; |
174 |
const jschar* ep; |
175 |
jsdouble d; |
176 |
|
177 |
str->getCharsAndEnd(bp, end); |
178 |
if ((!js_strtod(cx, bp, end, &ep, &d) || |
179 |
js_SkipWhiteSpace(ep, end) != end) && |
180 |
(!js_strtointeger(cx, bp, end, &ep, 0, &d) || |
181 |
js_SkipWhiteSpace(ep, end) != end)) { |
182 |
return js_NaN; |
183 |
} |
184 |
return d; |
185 |
} |
186 |
JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, 1) |
187 |
|
188 |
int32 FASTCALL |
189 |
js_StringToInt32(JSContext* cx, JSString* str) |
190 |
{ |
191 |
const jschar* bp; |
192 |
const jschar* end; |
193 |
const jschar* ep; |
194 |
jsdouble d; |
195 |
|
196 |
if (str->length() == 1) { |
197 |
jschar c = str->chars()[0]; |
198 |
if ('0' <= c && c <= '9') |
199 |
return c - '0'; |
200 |
return 0; |
201 |
} |
202 |
|
203 |
str->getCharsAndEnd(bp, end); |
204 |
if ((!js_strtod(cx, bp, end, &ep, &d) || |
205 |
js_SkipWhiteSpace(ep, end) != end) && |
206 |
(!js_strtointeger(cx, bp, end, &ep, 0, &d) || |
207 |
js_SkipWhiteSpace(ep, end) != end)) { |
208 |
return 0; |
209 |
} |
210 |
return js_DoubleToECMAInt32(d); |
211 |
} |
212 |
JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, 1) |
213 |
|
214 |
SideExit* FASTCALL |
215 |
js_CallTree(InterpState* state, Fragment* f) |
216 |
{ |
217 |
union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u; |
218 |
|
219 |
u.code = f->code(); |
220 |
JS_ASSERT(u.code); |
221 |
|
222 |
GuardRecord* rec; |
223 |
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) |
224 |
SIMULATE_FASTCALL(rec, state, NULL, u.func); |
225 |
#else |
226 |
rec = u.func(state, NULL); |
227 |
#endif |
228 |
VMSideExit* lr = (VMSideExit*)rec->exit; |
229 |
|
230 |
if (lr->exitType == NESTED_EXIT) { |
231 |
/* This only occurs once a tree call guard mismatches and we unwind the tree call stack. |
232 |
We store the first (innermost) tree call guard in state and we will try to grow |
233 |
the outer tree the failing call was in starting at that guard. */ |
234 |
if (!state->lastTreeCallGuard) { |
235 |
state->lastTreeCallGuard = lr; |
236 |
FrameInfo** rp = (FrameInfo**)state->rp; |
237 |
state->rpAtLastTreeCall = rp + lr->calldepth; |
238 |
} |
239 |
} else { |
240 |
/* If the tree exits on a regular (non-nested) guard, keep updating lastTreeExitGuard |
241 |
with that guard. If we mismatch on a tree call guard, this will contain the last |
242 |
non-nested guard we encountered, which is the innermost loop or branch guard. */ |
243 |
state->lastTreeExitGuard = lr; |
244 |
} |
245 |
|
246 |
return lr; |
247 |
} |
248 |
JS_DEFINE_CALLINFO_2(extern, SIDEEXIT, js_CallTree, INTERPSTATE, FRAGMENT, 0, 0) |
249 |
|
250 |
JSBool FASTCALL |
251 |
js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) |
252 |
{ |
253 |
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
254 |
JS_LOCK_OBJ(cx, obj); |
255 |
|
256 |
JSScope* scope = OBJ_SCOPE(obj); |
257 |
uint32 slot; |
258 |
if (scope->owned()) { |
259 |
JS_ASSERT(!scope->has(sprop)); |
260 |
} else { |
261 |
scope = js_GetMutableScope(cx, obj); |
262 |
if (!scope) |
263 |
goto exit_trace; |
264 |
} |
265 |
|
266 |
slot = sprop->slot; |
267 |
if (!scope->table && sprop->parent == scope->lastProp && slot == scope->freeslot) { |
268 |
if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { |
269 |
JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); |
270 |
++scope->freeslot; |
271 |
} else { |
272 |
if (!js_AllocSlot(cx, obj, &slot)) |
273 |
goto exit_trace; |
274 |
|
275 |
if (slot != sprop->slot) { |
276 |
js_FreeSlot(cx, obj, slot); |
277 |
goto exit_trace; |
278 |
} |
279 |
} |
280 |
|
281 |
scope->extend(cx, sprop); |
282 |
} else { |
283 |
JSScopeProperty *sprop2 = scope->add(cx, sprop->id, |
284 |
sprop->getter, sprop->setter, |
285 |
SPROP_INVALID_SLOT, sprop->attrs, |
286 |
sprop->flags, sprop->shortid); |
287 |
if (sprop2 != sprop) |
288 |
goto exit_trace; |
289 |
} |
290 |
|
291 |
if (js_IsPropertyCacheDisabled(cx)) |
292 |
goto exit_trace; |
293 |
|
294 |
JS_UNLOCK_SCOPE(cx, scope); |
295 |
return JS_TRUE; |
296 |
|
297 |
exit_trace: |
298 |
JS_UNLOCK_SCOPE(cx, scope); |
299 |
return JS_FALSE; |
300 |
} |
301 |
JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SCOPEPROP, 0, 0) |
302 |
|
303 |
static JSBool |
304 |
HasProperty(JSContext* cx, JSObject* obj, jsid id) |
305 |
{ |
306 |
// Check that we know how the lookup op will behave. |
307 |
for (JSObject* pobj = obj; pobj; pobj = OBJ_GET_PROTO(cx, pobj)) { |
308 |
if (pobj->map->ops->lookupProperty != js_LookupProperty) |
309 |
return JSVAL_TO_SPECIAL(JSVAL_VOID); |
310 |
JSClass* clasp = OBJ_GET_CLASS(cx, pobj); |
311 |
if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass) |
312 |
return JSVAL_TO_SPECIAL(JSVAL_VOID); |
313 |
} |
314 |
|
315 |
JSObject* obj2; |
316 |
JSProperty* prop; |
317 |
if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) < 0) |
318 |
return JSVAL_TO_SPECIAL(JSVAL_VOID); |
319 |
if (prop) |
320 |
obj2->dropProperty(cx, prop); |
321 |
return prop != NULL; |
322 |
} |
323 |
|
324 |
JSBool FASTCALL |
325 |
js_HasNamedProperty(JSContext* cx, JSObject* obj, JSString* idstr) |
326 |
{ |
327 |
jsid id; |
328 |
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id)) |
329 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
330 |
|
331 |
return HasProperty(cx, obj, id); |
332 |
} |
333 |
JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedProperty, CONTEXT, OBJECT, STRING, 0, 0) |
334 |
|
335 |
JSBool FASTCALL |
336 |
js_HasNamedPropertyInt32(JSContext* cx, JSObject* obj, int32 index) |
337 |
{ |
338 |
jsid id; |
339 |
if (!js_Int32ToId(cx, index, &id)) |
340 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
341 |
|
342 |
return HasProperty(cx, obj, id); |
343 |
} |
344 |
JS_DEFINE_CALLINFO_3(extern, BOOL, js_HasNamedPropertyInt32, CONTEXT, OBJECT, INT32, 0, 0) |
345 |
|
346 |
JSString* FASTCALL |
347 |
js_TypeOfObject(JSContext* cx, JSObject* obj) |
348 |
{ |
349 |
JSType type = JS_TypeOfValue(cx, OBJECT_TO_JSVAL(obj)); |
350 |
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); |
351 |
} |
352 |
JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfObject, CONTEXT, OBJECT, 1, 1) |
353 |
|
354 |
JSString* FASTCALL |
355 |
js_TypeOfBoolean(JSContext* cx, int32 unboxed) |
356 |
{ |
357 |
/* Watch out for pseudo-booleans. */ |
358 |
jsval boxed = SPECIAL_TO_JSVAL(unboxed); |
359 |
JS_ASSERT(JSVAL_IS_VOID(boxed) || JSVAL_IS_BOOLEAN(boxed)); |
360 |
JSType type = JS_TypeOfValue(cx, boxed); |
361 |
return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); |
362 |
} |
363 |
JS_DEFINE_CALLINFO_2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32, 1, 1) |
364 |
|
365 |
jsdouble FASTCALL |
366 |
js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed) |
367 |
{ |
368 |
if (unboxed == JSVAL_TO_SPECIAL(JSVAL_VOID)) |
369 |
return js_NaN; |
370 |
JS_ASSERT(unboxed == JS_TRUE || unboxed == JS_FALSE); |
371 |
return unboxed; |
372 |
} |
373 |
JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1) |
374 |
|
375 |
JSString* FASTCALL |
376 |
js_BooleanOrUndefinedToString(JSContext *cx, int32 unboxed) |
377 |
{ |
378 |
JS_ASSERT(uint32(unboxed) <= 2); |
379 |
return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[unboxed]); |
380 |
} |
381 |
JS_DEFINE_CALLINFO_2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1) |
382 |
|
383 |
JSObject* FASTCALL |
384 |
js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent) |
385 |
{ |
386 |
JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); |
387 |
JS_ASSERT(HAS_FUNCTION_CLASS(proto)); |
388 |
JS_ASSERT(JS_ON_TRACE(cx)); |
389 |
|
390 |
JSFunction *fun = (JSFunction*) funobj; |
391 |
JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); |
392 |
|
393 |
JSObject* closure = js_NewGCObject(cx, GCX_OBJECT); |
394 |
if (!closure) |
395 |
return NULL; |
396 |
|
397 |
closure->initSharingEmptyScope(&js_FunctionClass, proto, parent, |
398 |
reinterpret_cast<jsval>(fun)); |
399 |
return closure; |
400 |
} |
401 |
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0) |
402 |
|
403 |
JSString* FASTCALL |
404 |
js_ConcatN(JSContext *cx, JSString **strArray, uint32 size) |
405 |
{ |
406 |
/* Calculate total size. */ |
407 |
size_t numChar = 1; |
408 |
for (uint32 i = 0; i < size; ++i) { |
409 |
size_t before = numChar; |
410 |
numChar += strArray[i]->length(); |
411 |
if (numChar < before) |
412 |
return NULL; |
413 |
} |
414 |
|
415 |
|
416 |
/* Allocate buffer. */ |
417 |
if (numChar & js::tl::MulOverflowMask<sizeof(jschar)>::result) |
418 |
return NULL; |
419 |
jschar *buf = (jschar *)cx->malloc(numChar * sizeof(jschar)); |
420 |
if (!buf) |
421 |
return NULL; |
422 |
|
423 |
/* Fill buffer. */ |
424 |
jschar *ptr = buf; |
425 |
for (uint32 i = 0; i < size; ++i) { |
426 |
const jschar *chars; |
427 |
size_t length; |
428 |
strArray[i]->getCharsAndLength(chars, length); |
429 |
js_strncpy(ptr, chars, length); |
430 |
ptr += length; |
431 |
} |
432 |
*ptr = '\0'; |
433 |
|
434 |
/* Create string. */ |
435 |
JSString *str = js_NewString(cx, buf, numChar - 1); |
436 |
if (!str) |
437 |
cx->free(buf); |
438 |
return str; |
439 |
} |
440 |
JS_DEFINE_CALLINFO_3(extern, STRING, js_ConcatN, CONTEXT, STRINGPTR, UINT32, 0, 0) |