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 SpiderMonkey global object code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * the Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Jeff Walden <jwalden+code@mit.edu> (original author)
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "GlobalObject.h"
42 :
43 : #include "jscntxt.h"
44 : #include "jsexn.h"
45 : #include "jsmath.h"
46 : #include "json.h"
47 : #include "jsweakmap.h"
48 :
49 : #include "builtin/MapObject.h"
50 : #include "builtin/RegExp.h"
51 : #include "frontend/BytecodeEmitter.h"
52 : #include "vm/GlobalObject-inl.h"
53 :
54 : #include "jsobjinlines.h"
55 : #include "vm/RegExpObject-inl.h"
56 : #include "vm/RegExpStatics-inl.h"
57 :
58 : #ifdef JS_METHODJIT
59 : #include "methodjit/Retcon.h"
60 : #endif
61 :
62 : using namespace js;
63 :
64 : JSObject *
65 22858 : js_InitObjectClass(JSContext *cx, JSObject *obj)
66 : {
67 22858 : JS_ASSERT(obj->isNative());
68 :
69 22858 : return obj->asGlobal().getOrCreateObjectPrototype(cx);
70 : }
71 :
72 : JSObject *
73 16 : js_InitFunctionClass(JSContext *cx, JSObject *obj)
74 : {
75 16 : JS_ASSERT(obj->isNative());
76 :
77 16 : return obj->asGlobal().getOrCreateFunctionPrototype(cx);
78 : }
79 :
80 : static JSBool
81 0 : ThrowTypeError(JSContext *cx, unsigned argc, Value *vp)
82 : {
83 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
84 0 : JSMSG_THROW_TYPE_ERROR);
85 0 : return false;
86 : }
87 :
88 : namespace js {
89 :
90 : JSObject *
91 38637 : GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
92 : {
93 77274 : RootedVar<GlobalObject*> self(cx, this);
94 :
95 38637 : JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
96 38637 : JS_ASSERT(isNative());
97 :
98 : /*
99 : * Calling a function from a cleared global triggers this (yeah, I know).
100 : * Uncomment this once bug 470510 is fixed (if that bug doesn't remove
101 : * isCleared entirely).
102 : */
103 : // JS_ASSERT(!isCleared());
104 :
105 : /* If cx has no global object, make this the global object. */
106 38637 : if (!cx->globalObject)
107 31622 : JS_SetGlobalObject(cx, self);
108 :
109 77274 : RootedVarObject objectProto(cx);
110 :
111 : /*
112 : * Create |Object.prototype| first, mirroring CreateBlankProto but for the
113 : * prototype of the created object.
114 : */
115 38637 : objectProto = NewObjectWithGivenProto(cx, &ObjectClass, NULL, self);
116 38637 : if (!objectProto || !objectProto->setSingletonType(cx))
117 0 : return NULL;
118 :
119 : /*
120 : * The default 'new' type of Object.prototype is required by type inference
121 : * to have unknown properties, to simplify handling of e.g. heterogenous
122 : * objects in JSON and script literals.
123 : */
124 38637 : if (!objectProto->setNewTypeUnknown(cx))
125 0 : return NULL;
126 :
127 : /* Create |Function.prototype| next so we can create other functions. */
128 77274 : RootedVarFunction functionProto(cx);
129 : {
130 38637 : JSObject *functionProto_ = NewObjectWithGivenProto(cx, &FunctionClass, objectProto, self);
131 38637 : if (!functionProto_)
132 0 : return NULL;
133 38637 : functionProto = functionProto_->toFunction();
134 :
135 : /*
136 : * Bizarrely, |Function.prototype| must be an interpreted function, so
137 : * give it the guts to be one.
138 : */
139 : JSObject *proto = js_NewFunction(cx, functionProto,
140 38637 : NULL, 0, JSFUN_INTERPRETED, self, NULL);
141 38637 : if (!proto)
142 0 : return NULL;
143 38637 : JS_ASSERT(proto == functionProto);
144 38637 : functionProto->flags |= JSFUN_PROTOTYPE;
145 :
146 : JSScript *script =
147 38637 : JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
148 38637 : if (!script)
149 0 : return NULL;
150 38637 : script->noScriptRval = true;
151 38637 : script->code[0] = JSOP_STOP;
152 38637 : script->code[1] = SRC_NULL;
153 38637 : functionProto->initScript(script);
154 38637 : functionProto->getType(cx)->interpretedFunction = functionProto;
155 38637 : script->setFunction(functionProto);
156 :
157 38637 : if (!functionProto->setSingletonType(cx))
158 0 : return NULL;
159 :
160 : /*
161 : * The default 'new' type of Function.prototype is required by type
162 : * inference to have unknown properties, to simplify handling of e.g.
163 : * CloneFunctionObject.
164 : */
165 38637 : if (!functionProto->setNewTypeUnknown(cx))
166 0 : return NULL;
167 : }
168 :
169 : /* Create the Object function now that we have a [[Prototype]] for it. */
170 77274 : RootedVarFunction objectCtor(cx);
171 : {
172 38637 : JSObject *ctor = NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self);
173 38637 : if (!ctor)
174 0 : return NULL;
175 : objectCtor = js_NewFunction(cx, ctor, js_Object, 1, JSFUN_CONSTRUCTOR, self,
176 38637 : CLASS_ATOM(cx, Object));
177 38637 : if (!objectCtor)
178 0 : return NULL;
179 :
180 38637 : objectCtor->setConstructorClass(&ObjectClass);
181 : }
182 :
183 : /*
184 : * Install |Object| and |Object.prototype| for the benefit of subsequent
185 : * code that looks for them.
186 : */
187 38637 : self->setObjectClassDetails(objectCtor, objectProto);
188 :
189 : /* Create |Function| so it and |Function.prototype| can be installed. */
190 77274 : RootedVarFunction functionCtor(cx);
191 : {
192 : JSObject *ctor =
193 38637 : NewObjectWithGivenProto(cx, &FunctionClass, functionProto, self);
194 38637 : if (!ctor)
195 0 : return NULL;
196 : functionCtor = js_NewFunction(cx, ctor, Function, 1, JSFUN_CONSTRUCTOR, self,
197 38637 : CLASS_ATOM(cx, Function));
198 38637 : if (!functionCtor)
199 0 : return NULL;
200 38637 : JS_ASSERT(ctor == functionCtor);
201 :
202 38637 : functionCtor->setConstructorClass(&FunctionClass);
203 : }
204 :
205 : /*
206 : * Install |Function| and |Function.prototype| so that we can freely create
207 : * functions and objects without special effort.
208 : */
209 38637 : self->setFunctionClassDetails(functionCtor, functionProto);
210 :
211 : /*
212 : * The hard part's done: now go back and add all the properties these
213 : * primordial values have.
214 : */
215 231822 : if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) ||
216 38637 : !DefinePropertiesAndBrand(cx, objectProto, object_props, object_methods) ||
217 38637 : !DefinePropertiesAndBrand(cx, objectCtor, NULL, object_static_methods) ||
218 38637 : !LinkConstructorAndPrototype(cx, functionCtor, functionProto) ||
219 38637 : !DefinePropertiesAndBrand(cx, functionProto, NULL, function_methods) ||
220 38637 : !DefinePropertiesAndBrand(cx, functionCtor, NULL, NULL))
221 : {
222 0 : return NULL;
223 : }
224 :
225 : /* Add the global Function and Object properties now. */
226 38637 : jsid objectId = ATOM_TO_JSID(CLASS_ATOM(cx, Object));
227 38637 : if (!self->addDataProperty(cx, objectId, JSProto_Object + JSProto_LIMIT * 2, 0))
228 0 : return NULL;
229 38637 : jsid functionId = ATOM_TO_JSID(CLASS_ATOM(cx, Function));
230 38637 : if (!self->addDataProperty(cx, functionId, JSProto_Function + JSProto_LIMIT * 2, 0))
231 0 : return NULL;
232 :
233 : /* Heavy lifting done, but lingering tasks remain. */
234 :
235 : /* ES5 15.1.2.1. */
236 38637 : jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
237 38637 : JSObject *evalobj = js_DefineFunction(cx, self, id, eval, 1, JSFUN_STUB_GSOPS);
238 38637 : if (!evalobj)
239 0 : return NULL;
240 38637 : self->setOriginalEval(evalobj);
241 :
242 : /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
243 77274 : RootedVarFunction throwTypeError(cx);
244 38637 : throwTypeError = js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, self, NULL);
245 38637 : if (!throwTypeError)
246 0 : return NULL;
247 77274 : AutoIdVector ids(cx);
248 38637 : if (!throwTypeError->preventExtensions(cx, &ids))
249 0 : return NULL;
250 38637 : self->setThrowTypeError(throwTypeError);
251 :
252 : /*
253 : * The global object should have |Object.prototype| as its [[Prototype]].
254 : * Eventually we'd like to have standard classes be there from the start,
255 : * and thus we would know we were always setting what had previously been a
256 : * null [[Prototype]], but right now some code assumes it can set the
257 : * [[Prototype]] before standard classes have been initialized. For now,
258 : * only set the [[Prototype]] if it hasn't already been set.
259 : */
260 38637 : if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, objectProto))
261 0 : return NULL;
262 :
263 : /*
264 : * Notify any debuggers about the creation of the script for
265 : * |Function.prototype| -- after all initialization, for simplicity.
266 : */
267 38637 : js_CallNewScriptHook(cx, functionProto->script(), functionProto);
268 38637 : return functionProto;
269 : }
270 :
271 : GlobalObject *
272 38635 : GlobalObject::create(JSContext *cx, Class *clasp)
273 : {
274 38635 : JS_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
275 :
276 77270 : RootedVar<GlobalObject*> obj(cx);
277 :
278 38635 : JSObject *obj_ = NewObjectWithGivenProto(cx, clasp, NULL, NULL);
279 38635 : if (!obj_)
280 0 : return NULL;
281 38635 : obj = &obj_->asGlobal();
282 :
283 38635 : if (!obj->setSingletonType(cx) || !obj->setVarObj(cx))
284 0 : return NULL;
285 :
286 : /* Construct a regexp statics object for this global object. */
287 38635 : JSObject *res = RegExpStatics::create(cx, obj);
288 38635 : if (!res)
289 0 : return NULL;
290 38635 : obj->initSlot(REGEXP_STATICS, ObjectValue(*res));
291 38635 : obj->initFlags(0);
292 :
293 38635 : return obj;
294 : }
295 :
296 : bool
297 533 : GlobalObject::initStandardClasses(JSContext *cx)
298 : {
299 533 : JSAtomState &state = cx->runtime->atomState;
300 :
301 : /* Define a top-level property 'undefined' with the undefined value. */
302 533 : if (!defineProperty(cx, state.typeAtoms[JSTYPE_VOID], UndefinedValue(),
303 533 : JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY))
304 : {
305 0 : return false;
306 : }
307 :
308 533 : if (!initFunctionAndObjectClasses(cx))
309 0 : return false;
310 :
311 : /* Initialize the rest of the standard objects and functions. */
312 533 : return js_InitArrayClass(cx, this) &&
313 533 : js_InitBooleanClass(cx, this) &&
314 533 : js_InitExceptionClasses(cx, this) &&
315 533 : js_InitMathClass(cx, this) &&
316 533 : js_InitNumberClass(cx, this) &&
317 533 : js_InitJSONClass(cx, this) &&
318 533 : js_InitRegExpClass(cx, this) &&
319 533 : js_InitStringClass(cx, this) &&
320 533 : js_InitTypedArrayClasses(cx, this) &&
321 : #if JS_HAS_XML_SUPPORT
322 533 : js_InitXMLClasses(cx, this) &&
323 : #endif
324 : #if JS_HAS_GENERATORS
325 533 : js_InitIteratorClasses(cx, this) &&
326 : #endif
327 533 : js_InitDateClass(cx, this) &&
328 533 : js_InitWeakMapClass(cx, this) &&
329 533 : js_InitProxyClass(cx, this) &&
330 533 : js_InitMapClass(cx, this) &&
331 7995 : js_InitSetClass(cx, this);
332 : }
333 :
334 : void
335 13212 : GlobalObject::clear(JSContext *cx)
336 : {
337 1559016 : for (int key = JSProto_Null; key < JSProto_LIMIT * 3; key++)
338 1545804 : setSlot(key, UndefinedValue());
339 :
340 : /* Clear regexp statics. */
341 13212 : getRegExpStatics()->clear();
342 :
343 : /* Clear the runtime-codegen-enabled cache. */
344 13212 : setSlot(RUNTIME_CODEGEN_ENABLED, UndefinedValue());
345 :
346 : /*
347 : * Clear the original-eval and [[ThrowTypeError]] slots, in case throwing
348 : * trying to execute a script for this global must reinitialize standard
349 : * classes. See bug 470150.
350 : */
351 13212 : setSlot(EVAL, UndefinedValue());
352 13212 : setSlot(THROWTYPEERROR, UndefinedValue());
353 :
354 : /*
355 : * Mark global as cleared. If we try to execute any compile-and-go
356 : * scripts from here on, we will throw.
357 : */
358 13212 : int32_t flags = getSlot(FLAGS).toInt32();
359 13212 : flags |= FLAGS_CLEARED;
360 13212 : setSlot(FLAGS, Int32Value(flags));
361 :
362 : /*
363 : * Reset the new object cache in the compartment, which assumes that
364 : * prototypes cached on the global object are immutable.
365 : */
366 13212 : cx->compartment->newObjectCache.reset();
367 :
368 : #ifdef JS_METHODJIT
369 : /*
370 : * Destroy compiled code for any scripts parented to this global. Call ICs
371 : * can directly call scripts which have associated JIT code, and do so
372 : * without checking whether the script's global has been cleared.
373 : */
374 12306111 : for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
375 12292899 : JSScript *script = i.get<JSScript>();
376 12292899 : if (script->compileAndGo && script->hasJITCode() && script->hasClearedGlobal()) {
377 3 : mjit::Recompiler::clearStackReferences(cx, script);
378 3 : mjit::ReleaseScriptCode(cx, script);
379 : }
380 : }
381 : #endif
382 13212 : }
383 :
384 : bool
385 108318 : GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx)
386 : {
387 108318 : HeapSlot &v = getSlotRef(RUNTIME_CODEGEN_ENABLED);
388 108318 : if (v.isUndefined()) {
389 : /*
390 : * If there are callbacks, make sure that the CSP callback is installed
391 : * and that it permits runtime code generation, then cache the result.
392 : */
393 5253 : JSCSPEvalChecker allows = cx->runtime->securityCallbacks->contentSecurityPolicyAllows;
394 5253 : v.set(this, RUNTIME_CODEGEN_ENABLED, BooleanValue(!allows || allows(cx)));
395 : }
396 108318 : return !v.isFalse();
397 : }
398 :
399 : JSFunction *
400 111277 : GlobalObject::createConstructor(JSContext *cx, Native ctor, Class *clasp, JSAtom *name,
401 : unsigned length, gc::AllocKind kind)
402 : {
403 222554 : RootedVarObject self(cx, this);
404 :
405 : JSFunction *fun = js_NewFunction(cx, NULL, ctor, length,
406 111277 : JSFUN_CONSTRUCTOR, self, name, kind);
407 111277 : if (!fun)
408 0 : return NULL;
409 :
410 : /*
411 : * Remember the class this function is a constructor for so that we know to
412 : * create an object of this class when we call the constructor.
413 : */
414 111277 : fun->setConstructorClass(clasp);
415 111277 : return fun;
416 : }
417 :
418 : static JSObject *
419 116291 : CreateBlankProto(JSContext *cx, Class *clasp, JSObject &proto, GlobalObject &global)
420 : {
421 116291 : JS_ASSERT(clasp != &ObjectClass);
422 116291 : JS_ASSERT(clasp != &FunctionClass);
423 :
424 116291 : JSObject *blankProto = NewObjectWithGivenProto(cx, clasp, &proto, &global);
425 116291 : if (!blankProto || !blankProto->setSingletonType(cx))
426 0 : return NULL;
427 :
428 116291 : return blankProto;
429 : }
430 :
431 : JSObject *
432 80539 : GlobalObject::createBlankPrototype(JSContext *cx, Class *clasp)
433 : {
434 80539 : JSObject *objectProto = getOrCreateObjectPrototype(cx);
435 80539 : if (!objectProto)
436 0 : return NULL;
437 :
438 80539 : return CreateBlankProto(cx, clasp, *objectProto, *this);
439 : }
440 :
441 : JSObject *
442 35752 : GlobalObject::createBlankPrototypeInheriting(JSContext *cx, Class *clasp, JSObject &proto)
443 : {
444 35752 : return CreateBlankProto(cx, clasp, proto, *this);
445 : }
446 :
447 : bool
448 391646 : LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor, JSObject *proto)
449 : {
450 783292 : RootObject ctorRoot(cx, &ctor);
451 783292 : RootObject protoRoot(cx, &proto);
452 :
453 : return ctor->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom,
454 391646 : ObjectValue(*proto), JS_PropertyStub, JS_StrictPropertyStub,
455 391646 : JSPROP_PERMANENT | JSPROP_READONLY) &&
456 : proto->defineProperty(cx, cx->runtime->atomState.constructorAtom,
457 391646 : ObjectValue(*ctor), JS_PropertyStub, JS_StrictPropertyStub, 0);
458 : }
459 :
460 : bool
461 683297 : DefinePropertiesAndBrand(JSContext *cx, JSObject *obj, JSPropertySpec *ps, JSFunctionSpec *fs)
462 : {
463 1366594 : RootObject root(cx, &obj);
464 :
465 683297 : if ((ps && !JS_DefineProperties(cx, obj, ps)) || (fs && !JS_DefineFunctions(cx, obj, fs)))
466 0 : return false;
467 683297 : return true;
468 : }
469 :
470 : void
471 3260 : GlobalDebuggees_finalize(JSContext *cx, JSObject *obj)
472 : {
473 3260 : cx->delete_((GlobalObject::DebuggerVector *) obj->getPrivate());
474 3260 : }
475 :
476 : static Class
477 : GlobalDebuggees_class = {
478 : "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
479 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
480 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, GlobalDebuggees_finalize
481 : };
482 :
483 : GlobalObject::DebuggerVector *
484 130671 : GlobalObject::getDebuggers()
485 : {
486 130671 : Value debuggers = getReservedSlot(DEBUGGERS);
487 130671 : if (debuggers.isUndefined())
488 4461 : return NULL;
489 126210 : JS_ASSERT(debuggers.toObject().getClass() == &GlobalDebuggees_class);
490 126210 : return (DebuggerVector *) debuggers.toObject().getPrivate();
491 : }
492 :
493 : GlobalObject::DebuggerVector *
494 4358 : GlobalObject::getOrCreateDebuggers(JSContext *cx)
495 : {
496 4358 : assertSameCompartment(cx, this);
497 4358 : DebuggerVector *debuggers = getDebuggers();
498 4358 : if (debuggers)
499 1098 : return debuggers;
500 :
501 3260 : JSObject *obj = NewObjectWithGivenProto(cx, &GlobalDebuggees_class, NULL, this);
502 3260 : if (!obj)
503 0 : return NULL;
504 3260 : debuggers = cx->new_<DebuggerVector>();
505 3260 : if (!debuggers)
506 0 : return NULL;
507 3260 : obj->setPrivate(debuggers);
508 3260 : setReservedSlot(DEBUGGERS, ObjectValue(*obj));
509 3260 : return debuggers;
510 : }
511 :
512 : bool
513 0 : GlobalObject::addDebugger(JSContext *cx, Debugger *dbg)
514 : {
515 0 : DebuggerVector *debuggers = getOrCreateDebuggers(cx);
516 0 : if (!debuggers)
517 0 : return false;
518 : #ifdef DEBUG
519 0 : for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++)
520 0 : JS_ASSERT(*p != dbg);
521 : #endif
522 0 : if (debuggers->empty() && !compartment()->addDebuggee(cx, this))
523 0 : return false;
524 0 : if (!debuggers->append(dbg)) {
525 0 : compartment()->removeDebuggee(cx, this);
526 0 : return false;
527 : }
528 0 : return true;
529 : }
530 :
531 : } // namespace js
|