Mozilla/mozilla/js2/src/js2runtime.cpp
rogerl%netscape.com 7ad4b4026a Fixing gcc errors.
git-svn-id: svn://10.0.0.236/trunk@115161 18797224-902f-48f8-a5cc-f745e15eee43
2002-02-21 18:14:56 +00:00

3726 lines
141 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the JavaScript 2 Prototype.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
#ifdef _WIN32
// Turn off warnings about identifiers too long in browser information
#pragma warning(disable: 4786)
#pragma warning(disable: 4711)
#pragma warning(disable: 4710)
#endif
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "parser.h"
#include "numerics.h"
#include "js2runtime.h"
#include "bytecodegen.h"
#include "jsstring.h"
#include "jsarray.h"
#include "jsmath.h"
#include "jsdate.h"
#include "fdlibm_ns.h"
// this is the AttributeList passed to the name lookup routines
#define CURRENT_ATTR (NULL)
namespace JavaScript {
namespace JS2Runtime {
//
// XXX don't these belong in the context? But don't
// they need to compare equal across contexts?
//
JSType *Object_Type = NULL;
JSType *Number_Type;
JSType *Integer_Type;
JSType *Function_Type;
JSStringType *String_Type;
JSType *Boolean_Type;
JSType *Type_Type;
JSType *Void_Type;
JSType *Null_Type;
JSType *Unit_Type;
JSType *Attribute_Type;
JSType *Package_Type;
JSType *NamedArgument_Type;
JSArrayType *Array_Type;
JSType *Date_Type;
JSType *RegExp_Type;
JSType *Error_Type;
JSType *EvalError_Type;
JSType *RangeError_Type;
JSType *ReferenceError_Type;
JSType *SyntaxError_Type;
JSType *TypeError_Type;
JSType *UriError_Type;
Attribute *Context::executeAttributes(ExprNode *attr)
{
ASSERT(attr);
ByteCodeGen bcg(this, mScopeChain);
ByteCodeModule *bcm = bcg.genCodeForExpression(attr);
// stdOut << *bcm;
JSValue result = interpret(bcm, 0, NULL, JSValue(getGlobalObject()), NULL, 0);
ASSERT(result.isAttribute());
return result.attribute;
}
// Find a property with the given name, but make sure it's in
// the supplied namespace. XXX speed up! XXX
//
PropertyIterator JSObject::findNamespacedProperty(const String &name, NamespaceList *names)
{
for (PropertyIterator i = mProperties.lower_bound(name),
end = mProperties.upper_bound(name); (i != end); i++) {
NamespaceList *propNames = PROPERTY_NAMESPACELIST(i);
if (names) {
if (propNames == NULL)
continue; // a namespace list was specified, no match
while (names) {
NamespaceList *propNameEntry = propNames;
while (propNameEntry) {
if (names->mName == propNameEntry->mName)
return i;
propNameEntry = propNameEntry->mNext;
}
names = names->mNext;
}
}
else {
if (propNames) // entry is in a namespace, but none called for, no match
continue;
return i;
}
}
return mProperties.end();
}
/*---------------------------------------------------------------------------------------------*/
// see if the property exists by a specific kind of access
bool JSObject::hasOwnProperty(Context * /*cx*/, const String &name, NamespaceList *names, Access acc, PropertyIterator *p)
{
*p = findNamespacedProperty(name, names);
if (*p != mProperties.end()) {
Property *prop = PROPERTY(*p);
if (prop->mFlag == FunctionPair)
return (acc == Read) ? (prop->mData.fPair.getterF != NULL)
: (prop->mData.fPair.setterF != NULL);
else
if (prop->mFlag == IndexPair)
return (acc == Read) ? (prop->mData.iPair.getterI != toUInt32(-1))
: (prop->mData.iPair.setterI != toUInt32(-1));
else
return true;
}
else
return false;
}
bool JSObject::hasProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p)
{
if (hasOwnProperty(cx, name, names, acc, p))
return true;
else
if (!mPrototype.isNull())
return mPrototype.getObjectValue()->hasProperty(cx, name, names, acc, p);
else
return false;
}
bool JSObject::deleteProperty(Context * /*cx*/, const String &name, NamespaceList *names)
{
PropertyIterator i = findNamespacedProperty(name, names);
if (i != mProperties.end()) {
if ((PROPERTY_ATTR(i) & Property::DontDelete) == 0) {
mProperties.erase(i);
return true;
}
}
return false;
}
// get a property value
JSValue JSObject::getPropertyValue(PropertyIterator &i)
{
Property *prop = PROPERTY(i);
ASSERT(prop->mFlag == ValuePointer);
return *prop->mData.vp;
}
Property *JSObject::insertNewProperty(const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, const JSValue &v)
{
Property *prop = new Property(new JSValue(v), type, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
void JSObject::defineGetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
PropertyIterator i;
if (hasProperty(cx, name, names, Write, &i)) {
ASSERT(PROPERTY_KIND(i) == FunctionPair);
ASSERT(PROPERTY_GETTERF(i) == NULL);
PROPERTY_GETTERF(i) = f;
}
else {
const PropertyMap::value_type e(name, new NamespacedProperty(new Property(Function_Type, f, NULL, attrFlags), names));
mProperties.insert(e);
}
}
void JSObject::defineSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
PropertyIterator i;
if (hasProperty(cx, name, names, Read, &i)) {
ASSERT(PROPERTY_KIND(i) == FunctionPair);
ASSERT(PROPERTY_SETTERF(i) == NULL);
PROPERTY_SETTERF(i) = f;
}
else {
const PropertyMap::value_type e(name, new NamespacedProperty(new Property(Function_Type, NULL, f, attrFlags), names));
mProperties.insert(e);
}
}
uint32 JSObject::tempVarCount = 0;
void JSObject::defineTempVariable(Context *cx, Reference *&readRef, Reference *&writeRef, JSType *type)
{
char buf[32];
sprintf(buf, "%%tempvar%%_%d", tempVarCount++);
const String &name = cx->mWorld.identifiers[buf];
/* Property *prop = */defineVariable(cx, name, (NamespaceList *)NULL, Property::NoAttribute, type);
readRef = new NameReference(name, NULL, Read, Object_Type, 0);
writeRef = new NameReference(name, NULL, Write, Object_Type, 0);
}
// add a property
Property *JSObject::defineVariable(Context *cx, const String &name, AttributeStmtNode *attr, JSType *type)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
PropertyIterator it;
if (hasOwnProperty(cx, name, names, Read, &it)) {
// not a problem if neither are consts
if ((attrFlags & Property::Const)
|| (PROPERTY_ATTR(it) & Property::Const)) {
if (attr)
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", attr->pos, name);
else
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", name);
}
}
Property *prop = new Property(new JSValue(), type, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
Property *JSObject::defineVariable(Context *cx, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type)
{
PropertyIterator it;
if (hasOwnProperty(cx, name, names, Read, &it)) {
// not a problem if neither are consts
if (PROPERTY_ATTR(it) & Property::Const) {
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", name);
}
}
Property *prop = new Property(new JSValue(), type, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
// add a property (with a value)
Property *JSObject::defineVariable(Context *cx, const String &name, AttributeStmtNode *attr, JSType *type, const JSValue v)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
PropertyIterator it;
if (hasOwnProperty(cx, name, names, Read, &it)) {
// not a problem if neither are consts
if ((attrFlags & Property::Const)
|| (PROPERTY_ATTR(it) & Property::Const)) {
if (attr)
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", attr->pos, name);
else
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", name);
}
else {
// override the existing value
PROPERTY_VALUEPOINTER(it) = new JSValue(v);
return PROPERTY(it);
}
}
Property *prop = new Property(new JSValue(v), type, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
Property *JSObject::defineVariable(Context *cx, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, const JSValue v)
{
PropertyIterator it;
if (hasOwnProperty(cx, name, names, Read, &it)) {
if (PROPERTY_ATTR(it) & Property::Const) {
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", name);
}
}
Property *prop = new Property(new JSValue(v), type, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
Property *JSObject::defineAlias(Context * /*cx*/, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, JSValue *vp)
{
Property *prop = new Property(vp, type, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
Reference *JSObject::genReference(Context *cx, bool hasBase, const String& name, NamespaceList *names, Access acc, uint32 /*depth*/)
{
PropertyIterator i;
if (hasProperty(cx, name, names, acc, &i)) {
Property *prop = PROPERTY(i);
switch (prop->mFlag) {
case ValuePointer:
if (hasBase)
return new PropertyReference(name, names, acc, prop->mType, prop->mAttributes);
else
return new NameReference(name, names, acc, prop->mType, prop->mAttributes);
case FunctionPair:
if (acc == Read)
return new GetterFunctionReference(prop->mData.fPair.getterF, prop->mAttributes);
else {
JSFunction *f = prop->mData.fPair.setterF;
return new SetterFunctionReference(f, f->getParameterType(0), prop->mAttributes);
}
default:
NOT_REACHED("bad storage kind");
return NULL;
}
}
NOT_REACHED("bad genRef call");
return NULL;
}
void JSObject::getProperty(Context *cx, const String &name, NamespaceList *names)
{
PropertyIterator i;
if (hasOwnProperty(cx, name, names, Read, &i)) {
Property *prop = PROPERTY(i);
switch (prop->mFlag) {
case ValuePointer:
cx->pushValue(*prop->mData.vp);
break;
case FunctionPair:
cx->pushValue(cx->invokeFunction(prop->mData.fPair.getterF, JSValue(this), NULL, 0));
break;
default:
ASSERT(false); // XXX more to implement
break;
}
}
else {
if (!mPrototype.isNull())
mPrototype.getObjectValue()->getProperty(cx, name, names);
else
cx->pushValue(kUndefinedValue);
}
}
void JSType::getProperty(Context *cx, const String &name, NamespaceList *names)
{
PropertyIterator i;
if (hasOwnProperty(cx, name, names, Read, &i))
JSObject::getProperty(cx, name, names);
else
if (mSuperType)
mSuperType->getProperty(cx, name, names);
else
JSObject::getProperty(cx, name, names);
}
void JSInstance::getProperty(Context *cx, const String &name, NamespaceList *names)
{
PropertyIterator i;
if (hasOwnProperty(cx, name, names, Read, &i)) {
Property *prop = PROPERTY(i);
switch (prop->mFlag) {
case Slot:
cx->pushValue(mInstanceValues[prop->mData.index]);
break;
case ValuePointer:
cx->pushValue(*prop->mData.vp);
break;
case FunctionPair:
cx->pushValue(cx->invokeFunction(prop->mData.fPair.getterF, JSValue(this), NULL, 0));
break;
case Constructor:
case Method:
cx->pushValue(JSValue(mType->mMethods[prop->mData.index]));
break;
case IndexPair:
cx->pushValue(cx->invokeFunction(mType->mMethods[prop->mData.iPair.getterI], JSValue(this), NULL, 0));
break;
default:
ASSERT(false); // XXX more to implement
break;
}
}
else
/*
XXX this path allows an instance to access static fields of it's type
if (mType->hasOwnProperty(cx, name, names, Read, &i))
mType->getProperty(cx, name, names);
else
*/
JSObject::getProperty(cx, name, names);
}
void JSObject::setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v)
{
PropertyIterator i;
if (hasOwnProperty(cx, name, names, Write, &i)) {
if (PROPERTY_ATTR(i) & Property::ReadOnly) return;
Property *prop = PROPERTY(i);
switch (prop->mFlag) {
case ValuePointer:
if (name.compare(cx->UnderbarPrototype_StringAtom) == 0)
mPrototype = v;
*prop->mData.vp = v;
break;
case FunctionPair:
{
JSValue argv = v;
cx->invokeFunction(prop->mData.fPair.setterF, JSValue(this), &argv, 1);
}
break;
default:
ASSERT(false); // XXX more to implement ?
break;
}
}
else
defineVariable(cx, name, names, Property::Enumerable, Object_Type, v);
}
void JSType::setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v)
{
PropertyIterator i;
if (hasOwnProperty(cx, name, names, Read, &i))
JSObject::setProperty(cx, name, names, v);
else
if (mSuperType)
mSuperType->setProperty(cx, name, names, v);
else
JSObject::setProperty(cx, name, names, v);
}
void JSInstance::setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v)
{
PropertyIterator i;
if (hasOwnProperty(cx, name, names, Write, &i)) {
if (PROPERTY_ATTR(i) & Property::ReadOnly) return;
Property *prop = PROPERTY(i);
switch (prop->mFlag) {
case Slot:
mInstanceValues[prop->mData.index] = v;
break;
case ValuePointer:
*prop->mData.vp = v;
break;
case FunctionPair:
{
JSValue argv = v;
cx->invokeFunction(prop->mData.fPair.setterF, JSValue(this), &argv, 1);
}
break;
case IndexPair:
{
JSValue argv = v;
cx->invokeFunction(mType->mMethods[prop->mData.iPair.setterI], JSValue(this), &argv, 1);
}
break;
default:
ASSERT(false); // XXX more to implement ?
break;
}
}
else {
// the instance doesn't have this property, see if
// the type does...
if (mType->hasOwnProperty(cx, name, names, Write, &i))
mType->setProperty(cx, name, names, v);
else
defineVariable(cx, name, names, Property::Enumerable, Object_Type, v);
}
}
void JSArrayInstance::getProperty(Context *cx, const String &name, NamespaceList *names)
{
if (name.compare(cx->Length_StringAtom) == 0)
cx->pushValue(JSValue((float64)mLength));
else
JSInstance::getProperty(cx, name, names);
}
void JSArrayInstance::setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v)
{
if (name.compare(cx->Length_StringAtom) == 0) {
uint32 newLength = (uint32)(v.toUInt32(cx).f64);
if (newLength != v.toNumber(cx).f64)
cx->reportError(Exception::rangeError, "out of range value for length");
for (uint32 i = newLength; i < mLength; i++) {
const String *id = numberToString(i);
if (findNamespacedProperty(*id, NULL) != mProperties.end())
deleteProperty(cx, *id, NULL);
delete id;
}
mLength = newLength;
}
else {
PropertyIterator it = findNamespacedProperty(name, names);
if (it == mProperties.end())
insertNewProperty(name, names, Property::Enumerable, Object_Type, v);
else {
Property *prop = PROPERTY(it);
ASSERT(prop->mFlag == ValuePointer);
*prop->mData.vp = v;
}
JSValue v = JSValue(&name);
JSValue v_int = v.toUInt32(cx);
if ((v_int.f64 != two32minus1) && (v_int.toString(cx).string->compare(name) == 0)) {
if (v_int.f64 >= mLength)
mLength = (uint32)(v_int.f64) + 1;
}
}
}
bool JSArrayInstance::hasOwnProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p)
{
if (name.compare(cx->Length_StringAtom) == 0)
return true;
else
return JSInstance::hasOwnProperty(cx, name, names, acc, p);
}
bool JSArrayInstance::deleteProperty(Context *cx, const String &name, NamespaceList *names)
{
if (name.compare(cx->Length_StringAtom) == 0)
return false;
else
return JSInstance::deleteProperty(cx, name, names);
}
// get a named property from a string instance, but intercept
// 'length' by returning the known value
void JSStringInstance::getProperty(Context *cx, const String &name, NamespaceList *names)
{
if (name.compare(cx->Length_StringAtom) == 0)
cx->pushValue(JSValue((float64)(mValue->size())));
else
JSInstance::getProperty(cx, name, names);
}
void JSStringInstance::setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v)
{
if (name.compare(cx->Length_StringAtom) == 0) {
}
else
JSInstance::setProperty(cx, name, names, v);
}
bool JSStringInstance::hasOwnProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p)
{
if (name.compare(cx->Length_StringAtom) == 0)
return true;
else
return JSInstance::hasOwnProperty(cx, name, names, acc, p);
}
bool JSStringInstance::deleteProperty(Context *cx, const String &name, NamespaceList *names)
{
if (name.compare(cx->Length_StringAtom) == 0)
return false;
else
return JSInstance::deleteProperty(cx, name, names);
}
// construct an instance of a type
// - allocate memory for the slots, load the instance variable names into the
// property map - in order to provide dynamic access to those properties.
void JSInstance::initInstance(Context *cx, JSType *type)
{
if (type->mVariableCount)
mInstanceValues = new JSValue[type->mVariableCount];
// copy the instance variable names into the property map
for (PropertyIterator pi = type->mProperties.begin(),
end = type->mProperties.end();
(pi != end); pi++) {
if (PROPERTY_KIND(pi) == Slot) {
const PropertyMap::value_type e(PROPERTY_NAME(pi), NAMESPACED_PROPERTY(pi));
mProperties.insert(e);
}
}
// and then do the same for the super types
JSType *t = type->mSuperType;
while (t) {
for (PropertyIterator i = t->mProperties.begin(),
end = t->mProperties.end();
(i != end); i++) {
if (PROPERTY_KIND(i) == Slot) {
const PropertyMap::value_type e(PROPERTY_NAME(i), NAMESPACED_PROPERTY(i));
mProperties.insert(e);
}
}
if (t->mInstanceInitializer)
cx->invokeFunction(t->mInstanceInitializer, JSValue(this), NULL, 0);
t = t->mSuperType;
}
// run the initializer
if (type->mInstanceInitializer) {
cx->invokeFunction(type->mInstanceInitializer, JSValue(this), NULL, 0);
}
mType = type;
}
// Create a new (empty) instance of this class. The prototype
// link for this new instance is established from the type's
// prototype object.
JSValue JSType::newInstance(Context *cx)
{
JSInstance *result = new JSInstance(cx, this);
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
JSValue JSObjectType::newInstance(Context *cx)
{
JSObject *result = new JSObject();
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
// the function 'f' gets executed each time a new instance is created
void JSType::setInstanceInitializer(Context * /*cx*/, JSFunction *f)
{
mInstanceInitializer = f;
}
// Run the static initializer against this type
void JSType::setStaticInitializer(Context *cx, JSFunction *f)
{
if (f)
cx->interpret(f->getByteCode(), 0, f->getScopeChain(), JSValue(this), NULL, 0);
}
Property *JSType::defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
PropertyIterator it;
if (hasOwnProperty(cx, name, names, Read, &it)) {
/*
XXX error for classes, right? but not for local variables under what circumstances (hoisting impact?)
if (attr)
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", attr->pos, name);
else
cx->reportError(Exception::typeError, "Duplicate definition '{0}'", name);
*/
}
Property *prop = new Property(mVariableCount++, type, Slot, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
JSValue JSArrayType::newInstance(Context *cx)
{
JSInstance *result = new JSArrayInstance(cx);
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
JSValue JSStringType::newInstance(Context *cx)
{
JSInstance *result = new JSStringInstance(cx);
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
JSValue JSBooleanType::newInstance(Context *cx)
{
JSInstance *result = new JSBooleanInstance(cx);
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
JSValue JSRegExpType::newInstance(Context *cx)
{
JSInstance *result = new JSRegExpInstance(cx);
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
JSValue JSDateType::newInstance(Context *cx)
{
JSInstance *result = new JSDateInstance(cx);
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
JSValue JSNumberType::newInstance(Context *cx)
{
JSInstance *result = new JSNumberInstance(cx);
result->mPrototype = mPrototypeObject;
result->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototypeObject);
return JSValue(result);
}
// don't add to instances etc., climb all the way down (likely to the global object)
// and add the property there.
void ScopeChain::setNameValue(Context *cx, const String& name, NamespaceList *names)
{
JSValue v = cx->topValue();
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++)
{
PropertyIterator i;
if ((*s)->hasProperty(cx, name, names, Write, &i)) {
PropertyFlag flag = PROPERTY_KIND(i);
switch (flag) {
case ValuePointer:
*PROPERTY_VALUEPOINTER(i) = v;
break;
case Slot:
(*s)->setSlotValue(cx, PROPERTY_INDEX(i), v);
break;
default:
ASSERT(false); // what else needs to be implemented ?
}
return;
}
}
cx->getGlobalObject()->defineVariable(cx, name, names, 0, Object_Type, v);
}
bool ScopeChain::deleteName(Context *cx, const String& name, NamespaceList *names)
{
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++)
{
PropertyIterator i;
if ((*s)->hasOwnProperty(cx, name, names, Read, &i))
return (*s)->deleteProperty(cx, name, names);
}
return true;
}
inline char narrow(char16 ch) { return char(ch); }
JSObject *ScopeChain::getNameValue(Context *cx, const String& name, NamespaceList *names)
{
uint32 depth = 0;
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++, depth++)
{
PropertyIterator i;
if ((*s)->hasProperty(cx, name, names, Read, &i)) {
PropertyFlag flag = PROPERTY_KIND(i);
switch (flag) {
case ValuePointer:
cx->pushValue(*PROPERTY_VALUEPOINTER(i));
break;
case Slot:
cx->pushValue((*s)->getSlotValue(cx, PROPERTY_INDEX(i)));
break;
default:
ASSERT(false); // what else needs to be implemented ?
}
return *s;
}
}
m_cx->reportError(Exception::referenceError, "'{0}' not defined", name );
return NULL;
}
// it'd be much better if the property iterator returned by hasProperty could be
// used by the genReference call
Reference *ScopeChain::getName(Context *cx, const String& name, NamespaceList *names, Access acc)
{
uint32 depth = 0;
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++, depth++)
{
PropertyIterator i;
if ((*s)->hasProperty(cx, name, names, acc, &i))
return (*s)->genReference(cx, false, name, names, acc, depth);
else
if ((*s)->isDynamic())
return NULL;
}
return NULL;
}
bool ScopeChain::hasNameValue(Context *cx, const String& name, NamespaceList *names)
{
uint32 depth = 0;
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++, depth++)
{
PropertyIterator i;
if ((*s)->hasProperty(cx, name, names, Read, &i))
return true;
}
return false;
}
// a compile time request to get the value for a name
// (i.e. we're accessing a constant value)
JSValue ScopeChain::getCompileTimeValue(Context *cx, const String& name, NamespaceList *names)
{
uint32 depth = 0;
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++, depth++)
{
PropertyIterator i;
if ((*s)->hasProperty(cx, name, names, Read, &i))
return (*s)->getPropertyValue(i);
}
return kUndefinedValue;
}
// in the case of duplicate parameter names, pick the last one
// XXX does the namespace handling make any sense here? Can parameters be in a namespace?
Reference *ParameterBarrel::genReference(Context *cx, bool /* hasBase */, const String& name, NamespaceList *names, Access acc, uint32 /*depth*/)
{
Property *selectedProp = NULL;
for (PropertyIterator i = mProperties.lower_bound(name),
end = mProperties.upper_bound(name); (i != end); i++) {
NamespaceList *propNames = PROPERTY_NAMESPACELIST(i);
if (names) {
if (propNames == NULL)
continue; // a namespace list was specified, no match
while (names) {
NamespaceList *propNameEntry = propNames;
while (propNameEntry) {
if (names->mName == propNameEntry->mName) {
Property *prop = PROPERTY(i);
ASSERT(prop->mFlag == Slot);
if (selectedProp == NULL)
selectedProp = prop;
else {
if (PROPERTY_INDEX(i) > selectedProp->mData.index)
selectedProp = prop;
}
break;
}
propNameEntry = propNameEntry->mNext;
}
names = names->mNext;
}
}
else {
if (propNames) // entry is in a namespace, but none called for, no match
continue;
Property *prop = PROPERTY(i);
ASSERT(prop->mFlag == Slot);
if (selectedProp == NULL)
selectedProp = prop;
else {
if (PROPERTY_INDEX(i) > selectedProp->mData.index)
selectedProp = prop;
}
}
}
ASSERT(selectedProp);
return new ParameterReference(selectedProp->mData.index, acc, selectedProp->mType, selectedProp->mAttributes);
}
JSValue ParameterBarrel::getSlotValue(Context *cx, uint32 slotIndex)
{
// find the appropriate activation object:
if (cx->mArgumentBase == NULL) {// then must be in eval code,
Activation *prev = cx->mActivationStack.top();
return prev->mArgumentBase[slotIndex];
}
return cx->mArgumentBase[slotIndex];
}
void ParameterBarrel::setSlotValue(Context *cx, uint32 slotIndex, JSValue &v)
{
// find the appropriate activation object:
if (cx->mArgumentBase == NULL) {// then must be in eval code,
Activation *prev = cx->mActivationStack.top();
prev->mArgumentBase[slotIndex] = v;
}
else
cx->mArgumentBase[slotIndex] = v;
}
Property *ParameterBarrel::defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
PropertyIterator it;
if (hasOwnProperty(cx, name, names, Read, &it)) {
// XXX duplicate parameter name, ok for all functions, or just unchecked ones?
}
Property *prop = new Property(mVariableCount++, type, Slot, attrFlags);
const PropertyMap::value_type e(name, new NamespacedProperty(prop, names));
mProperties.insert(e);
return prop;
}
JSValue Activation::getSlotValue(Context *cx, uint32 slotIndex)
{
// find the appropriate activation object:
if (cx->mArgumentBase == NULL) {// then must be in eval code,
Activation *prev = cx->mActivationStack.top();
return prev->mLocals[slotIndex];
}
return cx->mLocals[slotIndex];
}
void Activation::setSlotValue(Context *cx, uint32 slotIndex, JSValue &v)
{
// find the appropriate activation object:
if (cx->mArgumentBase == NULL) {// then must be in eval code,
Activation *prev = cx->mActivationStack.top();
prev->mLocals[slotIndex] = v;
}
else
cx->mLocals[slotIndex] =v;
}
JSType *ScopeChain::findType(Context *cx, const StringAtom& typeName, size_t pos)
{
JSValue v = getCompileTimeValue(cx, typeName, NULL);
if (!v.isUndefined()) {
if (v.isType())
return v.type;
else {
// Allow finding a function that has the same name as it's containing class
// i.e. the default constructor.
FunctionName *fnName = v.function->getFunctionName();
if ((fnName->prefix == FunctionName::normal)
&& v.isFunction() && v.function->getClass()
&& (v.function->getClass()->mClassName->compare(*fnName->name) == 0))
return v.function->getClass();
m_cx->reportError(Exception::semanticError, "Unknown type", pos);
return NULL;
}
}
return NULL;
}
// Take the specified type in 't' and see if we have a compile-time
// type value for it. FindType will throw an error if a type by
// that name doesn't exist.
JSType *ScopeChain::extractType(ExprNode *t)
{
JSType *type = Object_Type;
if (t) {
switch (t->getKind()) {
case ExprNode::identifier:
{
IdentifierExprNode* typeExpr = checked_cast<IdentifierExprNode *>(t);
type = findType(m_cx, typeExpr->name, t->pos);
}
break;
case ExprNode::index:
// array type
{
InvokeExprNode *i = checked_cast<InvokeExprNode *>(t);
JSType *base = extractType(i->op);
JSType *element = Object_Type;
ExprPairList *p = i->pairs;
if (p != NULL) {
element = extractType(p->value);
ASSERT(p->next == NULL);
}
ASSERT(base == Array_Type);
if (element == Object_Type)
type = Array_Type;
else {
type = new JSArrayType(m_cx, element, NULL, Object_Type, kNullValue, kNullValue); // XXX or is this a descendant of Array[Object]?
type->setDefaultConstructor(m_cx, Array_Type->getDefaultConstructor());
}
}
break;
default:
NOT_REACHED("implement me - more complex types");
break;
}
}
return type;
}
// return the type of the index'th parameter in function
JSType *Context::getParameterType(FunctionDefinition &function, int index)
{
VariableBinding *v = function.parameters;
while (v) {
if (index-- == 0)
return mScopeChain->extractType(v->type);
else
v = v->next;
}
return NULL;
}
// Iterates over the linked list of statements, p.
// 1. Adds 'symbol table' entries for each class, var & function by defining
// them in the object at the top of the scope chain
// 2. Using information from pass 1, evaluate types (and XXX later XXX other
// compile-time constants) to complete the definitions
void Context::buildRuntime(StmtNode *p)
{
ContextStackReplacement csr(this);
mScopeChain->addScope(getGlobalObject());
while (p) {
mScopeChain->collectNames(p); // adds declarations for each top-level entity in p
buildRuntimeForStmt(p); // adds definitions as they exist for ditto
p = p->next;
}
mScopeChain->popScope();
}
// Generate bytecode for the linked list of statements in p
JS2Runtime::ByteCodeModule *Context::genCode(StmtNode *p, const String &/*sourceName*/)
{
mScopeChain->addScope(getGlobalObject());
JS2Runtime::ByteCodeGen bcg(this, mScopeChain);
JS2Runtime::ByteCodeModule *result = bcg.genCodeForScript(p);
mScopeChain->popScope();
return result;
}
/* Make sure that:
the function is not a class or interface member;
the function has no optional, named, or rest parameters;
none of the function's parameters has a declared type;
the function does not have a declared return type;
the function is not a getter or setter.
*/
bool ScopeChain::isPossibleUncheckedFunction(FunctionDefinition &f)
{
bool result = false;
if ((f.resultType == NULL)
&& (f.optParameters == NULL)
&& (f.prefix == FunctionName::normal)
&& (topClass() == NULL)) {
result = true;
VariableBinding *b = f.parameters;
while (b) {
if (b->type != NULL) {
result = false;
break;
}
b = b->next;
}
}
return result;
}
/*
Build a name for the package from the identifier list
*/
String ScopeChain::getPackageName(IdentifierList *packageIdList)
{
String packagePath;
IdentifierList *idList = packageIdList;
while (idList) {
packagePath += idList->name;
idList = idList->next;
if (idList)
packagePath += '/'; // XXX how to get path separator for OS?
}
return packagePath;
}
// counts the number of pigs that can fit in a small wicker basket
void JSFunction::countParameters(Context *cx, FunctionDefinition &f)
{
uint32 requiredParameterCount = 0;
uint32 optionalParameterCount = 0;
uint32 namedParameterCount = 0;
VariableBinding *b = f.parameters;
while (b != f.optParameters) {
requiredParameterCount++;
b = b->next;
}
while (b != f.restParameter) {
optionalParameterCount++;
b = b->next;
}
b = f.namedParameters;
while (b) {
namedParameterCount++;
b = b->next;
}
setParameterCounts(cx, requiredParameterCount, optionalParameterCount, namedParameterCount, f.restParameter != f.namedParameters);
}
// The first pass over the tree - it just installs the names of each declaration
void ScopeChain::collectNames(StmtNode *p)
{
switch (p->getKind()) {
// XXX - other statements, execute them (assuming they have constant control values) ?
// or simply visit the contained blocks and process any references that need to be hoisted
case StmtNode::Class:
{
ClassStmtNode *classStmt = checked_cast<ClassStmtNode *>(p);
const StringAtom *name = &classStmt->name;
JSType *thisClass = new JSType(m_cx, name, NULL, kNullValue, kNullValue);
m_cx->setAttributeValue(classStmt, 0); // XXX default attribute for a class?
PropertyIterator it;
if (hasProperty(m_cx, *name, NULL, Read, &it))
m_cx->reportError(Exception::referenceError, "Duplicate class definition", p->pos);
defineVariable(m_cx, *name, classStmt, Type_Type, JSValue(thisClass));
classStmt->mType = thisClass;
}
break;
case StmtNode::label:
{
LabelStmtNode *l = checked_cast<LabelStmtNode *>(p);
collectNames(l->stmt);
}
break;
case StmtNode::block:
case StmtNode::group:
{
// should push a new Activation scope here?
BlockStmtNode *b = checked_cast<BlockStmtNode *>(p);
StmtNode *s = b->statements;
while (s) {
collectNames(s);
s = s->next;
}
}
break;
case StmtNode::With:
case StmtNode::If:
case StmtNode::DoWhile:
case StmtNode::While:
{
UnaryStmtNode *u = checked_cast<UnaryStmtNode *>(p);
collectNames(u->stmt);
}
break;
case StmtNode::IfElse:
{
BinaryStmtNode *b = checked_cast<BinaryStmtNode *>(p);
collectNames(b->stmt);
collectNames(b->stmt2);
}
break;
case StmtNode::Try:
{
TryStmtNode *t = checked_cast<TryStmtNode *>(p);
collectNames(t->stmt);
if (t->catches) {
CatchClause *c = t->catches;
while (c) {
collectNames(c->stmt);
c->prop = defineVariable(m_cx, c->name, NULL, NULL);
c = c->next;
}
}
if (t->finally) collectNames(t->finally);
}
break;
case StmtNode::For:
case StmtNode::ForIn:
{
ForStmtNode *f = checked_cast<ForStmtNode *>(p);
if (f->initializer) collectNames(f->initializer);
collectNames(f->stmt);
}
break;
case StmtNode::Const:
case StmtNode::Var:
{
VariableStmtNode *vs = checked_cast<VariableStmtNode *>(p);
VariableBinding *v = vs->bindings;
m_cx->setAttributeValue(vs, Property::Final);
if (p->getKind() == StmtNode::Const)
vs->attributeValue->mTrueFlags |= Property::Const;
bool isStatic = (vs->attributeValue->mTrueFlags & Property::Static) == Property::Static;
if ((vs->attributeValue->mTrueFlags & Property::Private) == Property::Private) {
JSType *theClass = topClass();
if (theClass == NULL)
m_cx->reportError(Exception::typeError, "Private can only be used inside a class");
vs->attributeValue->mNamespaceList = new NamespaceList(*theClass->mPrivateNamespace, vs->attributeValue->mNamespaceList);
}
while (v) {
if (isStatic)
v->prop = defineStaticVariable(m_cx, *v->name, vs, NULL);
else
v->prop = defineVariable(m_cx, *v->name, vs, NULL);
v->scope = mScopeStack.back();
v = v->next;
}
}
break;
case StmtNode::Function:
{
FunctionStmtNode *f = checked_cast<FunctionStmtNode *>(p);
m_cx->setAttributeValue(f, Property::Virtual);
bool isStatic = (f->attributeValue->mTrueFlags & Property::Static) == Property::Static;
bool isConstructor = (f->attributeValue->mTrueFlags & Property::Constructor) == Property::Constructor;
bool isOperator = (f->attributeValue->mTrueFlags & Property::Operator) == Property::Operator;
bool isPrototype = (f->attributeValue->mTrueFlags & Property::Prototype) == Property::Prototype;
JSFunction *fnc = new JSFunction(m_cx, NULL, this);
/* Determine whether a function is unchecked, which is the case if -
XXX strict mode is disabled at the point of the function definition;
the function is not a class or interface member;
the function has no optional, named, or rest parameters;
none of the function's parameters has a declared type;
the function does not have a declared return type;
the function is not a getter or setter.
*/
if (!isPrototype
&& (!isOperator)
&& isPossibleUncheckedFunction(f->function)) {
isPrototype = true;
fnc->setIsUnchecked();
}
fnc->setIsPrototype(isPrototype);
fnc->setIsConstructor(isConstructor);
fnc->setFunctionName(f->function);
f->mFunction = fnc;
fnc->countParameters(m_cx, f->function);
if (isOperator) {
// no need to do anything yet, all operators are 'pre-declared'
}
else {
const StringAtom& name = *f->function.name;
if (topClass())
fnc->setClass(topClass());
if ((f->attributeValue->mTrueFlags & Property::Extend) == Property::Extend) {
JSType *extendedClass = f->attributeValue->mExtendArgument;
// sort of want to fall into the code below, but use 'extendedClass' instead
// of whatever the topClass will turn out to be.
if (extendedClass->mClassName->compare(name) == 0) {
isConstructor = true; // can you add constructors?
fnc->setIsConstructor(true);
}
if (isConstructor)
extendedClass->defineConstructor(m_cx, name, f, fnc);
else {
switch (f->function.prefix) {
case FunctionName::Get:
if (isStatic)
extendedClass->defineStaticGetterMethod(m_cx, name, f, fnc);
else
extendedClass->defineGetterMethod(m_cx, name, f, fnc);
break;
case FunctionName::Set:
if (isStatic)
extendedClass->defineStaticSetterMethod(m_cx, name, f, fnc);
else
extendedClass->defineSetterMethod(m_cx, name, f, fnc);
break;
case FunctionName::normal:
f->attributeValue->mTrueFlags |= Property::Const;
if (isStatic)
extendedClass->defineStaticMethod(m_cx, name, f, fnc);
else
extendedClass->defineMethod(m_cx, name, f, fnc);
break;
default:
NOT_REACHED("***** implement me -- throw an error because the user passed a quoted function name");
break;
}
}
}
else {
bool isDefaultConstructor = false;
if (topClass() && (topClass()->mClassName->compare(name) == 0)) {
isConstructor = true;
fnc->setIsConstructor(true);
isDefaultConstructor = true;
}
if (!isNestedFunction()) {
if (isConstructor) {
defineConstructor(m_cx, name, f, fnc);
if (isDefaultConstructor)
topClass()->setDefaultConstructor(m_cx, fnc);
}
else {
switch (f->function.prefix) {
case FunctionName::Get:
if (isStatic)
defineStaticGetterMethod(m_cx, name, f, fnc);
else
defineGetterMethod(m_cx, name, f, fnc);
break;
case FunctionName::Set:
if (isStatic)
defineStaticSetterMethod(m_cx, name, f, fnc);
else
defineSetterMethod(m_cx, name, f, fnc);
break;
case FunctionName::normal:
// make a function into a const declaration, but only if any types
// have been specified - otherwise it's a 1.5 atyle definition and
// duplicates are allowed
if (!isPossibleUncheckedFunction(f->function))
f->attributeValue->mTrueFlags |= Property::Const;
if (isStatic)
defineStaticMethod(m_cx, name, f, fnc);
else
defineMethod(m_cx, name, f, fnc);
break;
default:
NOT_REACHED("***** implement me -- throw an error because the user passed a quoted function name");
break;
}
}
}
}
}
}
break;
case StmtNode::Import:
{
ImportStmtNode *i = checked_cast<ImportStmtNode *>(p);
String packageName;
if (i->packageIdList)
packageName = getPackageName(i->packageIdList);
else
packageName = *i->packageString;
if (!m_cx->checkForPackage(packageName))
m_cx->loadPackage(packageName, packageName + ".js");
JSValue packageValue = getCompileTimeValue(m_cx, packageName, NULL);
ASSERT(packageValue.isPackage());
Package *package = packageValue.package;
if (i->varName)
defineVariable(m_cx, *i->varName, NULL, Package_Type, JSValue(package));
for (PropertyIterator it = package->mProperties.begin(), end = package->mProperties.end();
(it != end); it++)
{
ASSERT(PROPERTY_KIND(it) == ValuePointer);
bool makeAlias = true;
if (i->includeExclude) {
makeAlias = i->exclude;
IdentifierList *idList = i->includeExclude;
while (idList) {
if (idList->name.compare(PROPERTY_NAME(it)) == 0) {
makeAlias = !makeAlias;
break;
}
idList = idList->next;
}
}
if (makeAlias)
defineAlias(m_cx, PROPERTY_NAME(it), PROPERTY_NAMESPACELIST(it), PROPERTY_ATTR(it), PROPERTY_TYPE(it), PROPERTY_VALUEPOINTER(it));
}
}
break;
case StmtNode::Namespace:
{
NamespaceStmtNode *n = checked_cast<NamespaceStmtNode *>(p);
Attribute *x = new Attribute(0, 0);
x->mNamespaceList = new NamespaceList(n->name, x->mNamespaceList);
m_cx->getGlobalObject()->defineVariable(m_cx, n->name, (NamespaceList *)(NULL), Property::NoAttribute, Attribute_Type, JSValue(x));
}
break;
case StmtNode::Package:
{
PackageStmtNode *ps = checked_cast<PackageStmtNode *>(p);
String packageName = getPackageName(ps->packageIdList);
Package *package = new Package(packageName);
ps->scope = package;
defineVariable(m_cx, packageName, NULL, Package_Type, JSValue(package));
m_cx->mPackages.push_back(package);
addScope(ps->scope);
collectNames(ps->body);
popScope();
package->mStatus = Package::InHand;
}
break;
default:
break;
}
}
// Make sure there's a default constructor. XXX anything else?
void JSType::completeClass(Context *cx, ScopeChain *scopeChain)
{
// if none exists, build a default constructor that calls 'super()'
if (getDefaultConstructor() == NULL) {
JSFunction *fnc = new JSFunction(cx, Object_Type, scopeChain);
fnc->setIsConstructor(true);
fnc->setFunctionName(mClassName);
fnc->setClass(this);
ByteCodeGen bcg(cx, scopeChain);
if (mSuperType && mSuperType->getDefaultConstructor()) {
bcg.addOp(LoadTypeOp);
bcg.addPointer(this);
bcg.addOp(NewThisOp);
bcg.addOp(LoadThisOp);
bcg.addOp(LoadFunctionOp);
bcg.addPointer(mSuperType->getDefaultConstructor());
bcg.addOpAdjustDepth(InvokeOp, -1);
bcg.addLong(0);
bcg.addByte(Explicit);
bcg.addOp(PopOp);
}
bcg.addOp(LoadThisOp);
ASSERT(bcg.mStackTop == 1);
bcg.addOpSetDepth(ReturnOp, 0);
ByteCodeModule *bcm = new JS2Runtime::ByteCodeModule(&bcg, fnc);
if (cx->mReader)
bcm->setSource(cx->mReader->source, cx->mReader->sourceLocation);
fnc->setByteCode(bcm);
scopeChain->defineConstructor(cx, *mClassName, NULL, fnc); // XXX attributes?
setDefaultConstructor(cx, fnc);
}
}
void JSType::defineMethod(Context *cx, const String& name, AttributeStmtNode *attr, JSFunction *f)
{
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
PropertyIterator it;
if (hasOwnProperty(cx, name, names, Read, &it))
cx->reportError(Exception::typeError, "Duplicate method definition", attr->pos);
// now check if the method exists in the supertype
if (mSuperType && mSuperType->hasOwnProperty(cx, name, names, Read, &it)) {
// if it does, it must have been overridable:
PropertyAttribute superAttr = PROPERTY_ATTR(it);
if (superAttr & Property::Final)
cx->reportError(Exception::typeError, "Attempting to override a final method", attr->pos);
// if it was marked as virtual, then the new one must specifiy 'override' or 'mayoverride'
if (superAttr & Property::Virtual) {
if ((attr->attributeValue->mTrueFlags & (Property::Override | Property::MayOverride)) == 0)
cx->reportError(Exception::typeError, "Must specify 'override' or 'mayOverride'", attr->pos);
}
}
uint32 vTableIndex = mMethods.size();
mMethods.push_back(f);
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
const PropertyMap::value_type e(name, new NamespacedProperty(new Property(vTableIndex, Function_Type, Method, attrFlags), names));
mProperties.insert(e);
}
void JSType::defineGetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
{
PropertyIterator i;
uint32 vTableIndex = mMethods.size();
mMethods.push_back(f);
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
if (hasProperty(cx, name, names, Write, &i)) {
ASSERT(PROPERTY_KIND(i) == IndexPair);
ASSERT(PROPERTY_GETTERI(i) == 0);
PROPERTY_GETTERI(i) = vTableIndex;
}
else {
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
const PropertyMap::value_type e(name, new NamespacedProperty(new Property(vTableIndex, 0, Function_Type, attrFlags), names));
mProperties.insert(e);
}
}
void JSType::defineSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
{
PropertyIterator i;
uint32 vTableIndex = mMethods.size();
mMethods.push_back(f);
NamespaceList *names = (attr) ? attr->attributeValue->mNamespaceList : NULL;
if (hasProperty(cx, name, names, Read, &i)) {
ASSERT(PROPERTY_KIND(i) == IndexPair);
ASSERT(PROPERTY_SETTERI(i) == 0);
PROPERTY_SETTERI(i) = vTableIndex;
}
else {
PropertyAttribute attrFlags = (attr) ? attr->attributeValue->mTrueFlags : 0;
const PropertyMap::value_type e(name, new NamespacedProperty(new Property(0, vTableIndex, Function_Type, attrFlags), names));
mProperties.insert(e);
}
}
bool JSType::derivesFrom(JSType *other)
{
if (mSuperType == other)
return true;
else
if (mSuperType)
return mSuperType->derivesFrom(other);
else
return false;
}
JSValue JSType::getPropertyValue(PropertyIterator &i)
{
Property *prop = PROPERTY(i);
switch (prop->mFlag) {
case ValuePointer:
return *prop->mData.vp;
case Constructor:
return JSValue(mMethods[prop->mData.index]);
default:
return kUndefinedValue;
}
}
bool JSType::hasProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p)
{
if (hasOwnProperty(cx, name, names, acc, p))
return true;
else
if (mSuperType)
return mSuperType->hasProperty(cx, name, names, acc, p);
else
return false;
}
Reference *JSType::genReference(Context *cx, bool hasBase, const String& name, NamespaceList *names, Access acc, uint32 depth)
{
PropertyIterator i;
if (hasOwnProperty(cx, name, names, acc, &i)) {
Property *prop = PROPERTY(i);
switch (prop->mFlag) {
case FunctionPair:
if (acc == Read)
return new GetterFunctionReference(prop->mData.fPair.getterF, prop->mAttributes);
else {
JSFunction *f = prop->mData.fPair.setterF;
return new SetterFunctionReference(f, f->getParameterType(0), prop->mAttributes);
}
case ValuePointer:
return new StaticFieldReference(name, acc, this, prop->mType, prop->mAttributes);
case IndexPair:
if (acc == Read)
return new GetterMethodReference(prop->mData.iPair.getterI, this, prop->mType, prop->mAttributes);
else {
JSFunction *f = mMethods[prop->mData.iPair.setterI];
return new SetterMethodReference(prop->mData.iPair.setterI, this, f->getParameterType(0), prop->mAttributes);
}
case Slot:
return new FieldReference(prop->mData.index, acc, prop->mType, prop->mAttributes);
case Method:
return new MethodReference(prop->mData.index, this, prop->mType, prop->mAttributes);
default:
NOT_REACHED("bad storage kind");
return NULL;
}
}
// walk the supertype chain
if (mSuperType)
return mSuperType->genReference(cx, hasBase, name, names, acc, depth);
return NULL;
}
// Construct a type object, hook up the prototype value (mPrototypeObject) and the __proto__ value
// (mPrototype). Special handling throughout for handling the initialization of Object_Type which has
// no super type. (Note though that it's prototype link __proto__ is the Type_Type object and it has
// a prototype object - whose __proto__ is null)
JSType::JSType(Context *cx, const StringAtom *name, JSType *super, JSValue &protoObj, JSValue &typeProto)
: JSInstance(cx, Type_Type),
mSuperType(super),
mVariableCount(0),
mInstanceInitializer(NULL),
mDefaultConstructor(NULL),
mTypeCast(NULL),
mClassName(name),
mIsDynamic(false),
mUninitializedValue(kNullValue),
mPrototypeObject(kNullValue)
{
if (mClassName)
mPrivateNamespace = &cx->mWorld.identifiers[*mClassName + " private"];
else
mPrivateNamespace = &cx->mWorld.identifiers["unique id needed? private"]; // XXX. No, really?
// every class gets a prototype object (used to set the __proto__ value of new instances)
if (!protoObj.isNull())
mPrototypeObject = protoObj;
else {
JSObject *protoObj = new JSObject();
mPrototypeObject = JSValue(protoObj);
// and that object is prototype-linked to the super-type's prototype object
if (mSuperType)
protoObj->mPrototype = mSuperType->mPrototypeObject;
protoObj->defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, this, protoObj->mPrototype);
}
if (mSuperType)
defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::ReadOnly | Property::DontDelete, Object_Type, mPrototypeObject);
else // must be Object_Type being initialized
defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::ReadOnly | Property::DontDelete, this, mPrototypeObject);
if (!typeProto.isNull())
mPrototype = typeProto;
else {
// the __proto__ of a type is the super-type's prototype object, or for the
// 'class Object' type object it's the Object's prototype object
if (mSuperType)
mPrototype = mSuperType->mPrototypeObject;
else // must be Object_Type being initialized
mPrototype = mPrototypeObject;
}
if (mSuperType) {
defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, Object_Type, mPrototype);
mPrototypeObject.getObjectValue()->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(this));
}
else { // must be Object_Type being initialized
defineVariable(cx, cx->UnderbarPrototype_StringAtom, NULL, 0, this, mPrototype);
mPrototypeObject.getObjectValue()->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, 0, this, JSValue(this));
}
}
// Establish the super class - connects the prototype's prototype
// and accounts for the super class's instance fields & methods
void JSType::setSuperType(JSType *super)
{
mSuperType = super;
if (mSuperType) {
mPrototypeObject.getObjectValue()->mPrototype = mSuperType->mPrototypeObject;
// inherit supertype instance field and vtable slots
mVariableCount = mSuperType->mVariableCount;
mMethods.insert(mMethods.begin(),
mSuperType->mMethods.begin(),
mSuperType->mMethods.end());
}
}
void Activation::defineTempVariable(Context * /*cx*/, Reference *&readRef, Reference *&writeRef, JSType *type)
{
readRef = new LocalVarReference(mVariableCount, Read, type, Property::NoAttribute);
writeRef = new LocalVarReference(mVariableCount, Write, type, Property::NoAttribute);
mVariableCount++;
}
Reference *Activation::genReference(Context * cx, bool /* hasBase */, const String& name, NamespaceList *names, Access acc, uint32 depth)
{
PropertyIterator i;
if (hasProperty(cx, name, names, acc, &i)) {
Property *prop = PROPERTY(i);
ASSERT((prop->mFlag == ValuePointer) || (prop->mFlag == Slot) || (prop->mFlag == FunctionPair));
switch (prop->mFlag) {
case FunctionPair:
return (acc == Read) ? new AccessorReference(prop->mData.fPair.getterF, prop->mAttributes)
: new AccessorReference(prop->mData.fPair.setterF, prop->mAttributes);
case Slot:
if (depth)
return new ClosureVarReference(depth, prop->mData.index, acc, prop->mType, prop->mAttributes);
else
return new LocalVarReference(prop->mData.index, acc, prop->mType, prop->mAttributes);
case ValuePointer:
return new NameReference(name, names, acc, prop->mType, prop->mAttributes);
default:
NOT_REACHED("bad genRef call");
break;
}
}
NOT_REACHED("bad genRef call");
return NULL;
}
/*
Process the statements in the function body, handling parameters and local
variables to collect names & types.
*/
void Context::buildRuntimeForFunction(FunctionDefinition &f, JSFunction *fnc)
{
fnc->mParameterBarrel = new ParameterBarrel();
mScopeChain->addScope(fnc->mParameterBarrel);
VariableBinding *v = f.parameters;
while (v) {
if (v->name) {
JSType *pType = mScopeChain->extractType(v->type); // XXX already extracted for argument Data
mScopeChain->defineVariable(this, *v->name, NULL, pType); // XXX attributes?
}
v = v->next;
}
if (f.body) {
mScopeChain->addScope(&fnc->mActivation);
mScopeChain->collectNames(f.body);
buildRuntimeForStmt(f.body);
mScopeChain->popScope();
}
mScopeChain->popScope();
}
/*
The incoming AttributeStmtNode has a list of attributes - evaluate those and return
the resultant Attribute value. If there are no attributes, return the default value.
*/
void Context::setAttributeValue(AttributeStmtNode *s, PropertyAttribute defaultValue)
{
Attribute *attributeValue = NULL;
if (s->attributes == NULL)
attributeValue = new Attribute(defaultValue, 0);
else
attributeValue = executeAttributes(s->attributes);
s->attributeValue = attributeValue;
}
// Second pass, collect type information and finish
// off the definitions made in pass 1
void Context::buildRuntimeForStmt(StmtNode *p)
{
switch (p->getKind()) {
case StmtNode::block:
case StmtNode::group:
{
BlockStmtNode *b = checked_cast<BlockStmtNode *>(p);
StmtNode *s = b->statements;
while (s) {
buildRuntimeForStmt(s);
s = s->next;
}
}
break;
case StmtNode::label:
{
LabelStmtNode *l = checked_cast<LabelStmtNode *>(p);
buildRuntimeForStmt(l->stmt);
}
break;
case StmtNode::Try:
{
TryStmtNode *t = checked_cast<TryStmtNode *>(p);
buildRuntimeForStmt(t->stmt);
if (t->catches) {
CatchClause *c = t->catches;
while (c) {
buildRuntimeForStmt(c->stmt);
c->prop->mType = mScopeChain->extractType(c->type);
c = c->next;
}
}
if (t->finally) buildRuntimeForStmt(t->finally);
}
break;
case StmtNode::With:
case StmtNode::If:
case StmtNode::DoWhile:
case StmtNode::While:
{
UnaryStmtNode *u = checked_cast<UnaryStmtNode *>(p);
buildRuntimeForStmt(u->stmt);
}
break;
case StmtNode::IfElse:
{
BinaryStmtNode *b = checked_cast<BinaryStmtNode *>(p);
buildRuntimeForStmt(b->stmt);
buildRuntimeForStmt(b->stmt2);
}
break;
case StmtNode::For:
case StmtNode::ForIn:
{
ForStmtNode *f = checked_cast<ForStmtNode *>(p);
if (f->initializer) buildRuntimeForStmt(f->initializer);
buildRuntimeForStmt(f->stmt);
}
break;
case StmtNode::Var:
case StmtNode::Const:
{
VariableStmtNode *vs = checked_cast<VariableStmtNode *>(p);
VariableBinding *v = vs->bindings;
while (v) {
JSType *type = mScopeChain->extractType(v->type);
v->prop->mType = type;
v = v->next;
}
}
break;
case StmtNode::Function:
{
FunctionStmtNode *f = checked_cast<FunctionStmtNode *>(p);
bool isOperator = (f->attributeValue->mTrueFlags & Property::Operator) == Property::Operator;
JSType *resultType = mScopeChain->extractType(f->function.resultType);
JSFunction *fnc = f->mFunction;
fnc->setResultType(resultType);
VariableBinding *v = f->function.parameters;
uint32 parameterCount = 0;
JSFunction::ParameterFlag flag = JSFunction::RequiredParameter;
while (v) {
// XXX if no type is specified for the rest parameter - is it Array?
if (v == f->function.optParameters)
flag = JSFunction::OptionalParameter;
else
if (v == f->function.restParameter)
flag = JSFunction::RestParameter;
else
if (v == f->function.namedParameters)
flag = JSFunction::NamedParameter;
fnc->setParameter(parameterCount++, v->name, mScopeChain->extractType(v->type), flag);
v = v->next;
}
if (isOperator) {
if (f->function.prefix != FunctionName::op) {
NOT_REACHED("***** Implement me -- signal an error here because the user entered an unquoted operator name");
}
ASSERT(f->function.name);
const StringAtom& name = *f->function.name;
Operator op = getOperator(parameterCount, name);
// Operators added to the Context's operator table.
// The indexing operators are considered unary since
// they only dispatch on the base type.
if ((parameterCount == 1)
|| (op == Index)
|| (op == IndexEqual)
|| (op == DeleteIndex))
defineOperator(op, mScopeChain->topClass(), fnc);
else
defineOperator(op, getParameterType(f->function, 0),
getParameterType(f->function, 1), fnc);
}
// if it's an extending function, rediscover the extended class
// and push the class scope onto the scope chain
/*
bool isExtender = false;
if (hasAttribute(f->attributes, ExtendKeyWord)) {
JSType *extendedClass = mScopeChain->extractType( <extend attribute argument> );
mScopeChain->addScope(extendedClass->mStatics);
mScopeChain->addScope(extendedClass);
}
*/
buildRuntimeForFunction(f->function, fnc);
/*
if (isExtender) { // blow off the extended class's scope
mScopeChain->popScope();
mScopeChain->popScope();
}
*/
}
break;
case StmtNode::Class:
{
ClassStmtNode *classStmt = checked_cast<ClassStmtNode *>(p);
JSType *superClass = Object_Type;
if (classStmt->superclass) {
ASSERT(classStmt->superclass->getKind() == ExprNode::identifier); // XXX
IdentifierExprNode *superClassExpr = checked_cast<IdentifierExprNode *>(classStmt->superclass);
superClass = mScopeChain->findType(this, superClassExpr->name, superClassExpr->pos);
}
JSType *thisClass = classStmt->mType;
thisClass->setSuperType(superClass);
mScopeChain->addScope(thisClass);
if (classStmt->body) {
StmtNode* s = classStmt->body->statements;
while (s) {
mScopeChain->collectNames(s);
s = s->next;
}
s = classStmt->body->statements;
while (s) {
buildRuntimeForStmt(s);
s = s->next;
}
}
thisClass->completeClass(this, mScopeChain);
mScopeChain->popScope();
}
break;
case StmtNode::Namespace:
{
// do anything ?
}
break;
case StmtNode::Package:
{
PackageStmtNode *ps = checked_cast<PackageStmtNode *>(p);
mScopeChain->addScope(ps->scope);
buildRuntimeForStmt(ps->body);
mScopeChain->popScope();
}
break;
default:
break;
}
}
static JSValue Object_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
JSValue thatValue = thisValue; // incoming 'new this' potentially supplied by constructor sequence
if (argc != 0) {
if (argv[0].isInstance() || argv[0].isObject() || argv[0].isFunction() || argv[0].isType())
thatValue = argv[0];
else
if (argv[0].isString() || argv[0].isBool() || argv[0].isNumber())
thatValue = argv[0].toObject(cx);
else {
if (thatValue.isNull())
thatValue = Object_Type->newInstance(cx);
}
}
else {
if (thatValue.isNull())
thatValue = Object_Type->newInstance(cx);
}
return thatValue;
}
static JSValue Object_toString(Context * /* cx */, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
if (thisValue.isObject() || thisValue.isInstance())
return JSValue(new String(widenCString("[object ") + *thisValue.getType()->mClassName + widenCString("]")));
else
if (thisValue.isType()) // XXX why wouldn't this get handled by the above?
return JSValue(new String(widenCString("[object ") + widenCString("Type") + widenCString("]")));
else
if (thisValue.isFunction())
return JSValue(new String(widenCString("[object ") + widenCString("Function") + widenCString("]")));
else {
NOT_REACHED("Object.prototype.toString on non-object");
return kUndefinedValue;
}
}
static JSValue Object_valueOf(Context * /* cx */, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
return thisValue;
}
class JSIteratorInstance : public JSInstance {
public:
JSIteratorInstance(Context *cx) : JSInstance(cx, NULL) { mType = (JSType *)Object_Type; }
virtual ~JSIteratorInstance() { } // keeping gcc happy
#ifdef DEBUG
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSIteratorInstance", s, t); return t; }
void operator delete(void* t) { trace_release("JSIteratorInstance", t); STD::free(t); }
#endif
JSObject *obj;
PropertyIterator it;
};
static JSValue Object_forin(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
JSObject *obj = thisValue.getObjectValue();
JSIteratorInstance *itInst = new JSIteratorInstance(cx);
itInst->obj = obj;
itInst->it = obj->mProperties.begin();
while (true) {
while (itInst->it == itInst->obj->mProperties.end()) {
if (itInst->obj->mPrototype.isNull())
return kNullValue;
itInst->obj = itInst->obj->mPrototype.getObjectValue();
itInst->it = itInst->obj->mProperties.begin();
}
if (PROPERTY_ATTR(itInst->it) & Property::Enumerable)
break;
itInst->it++;
}
JSValue v(&PROPERTY_NAME(itInst->it));
itInst->setProperty(cx, cx->mWorld.identifiers["value"], 0, v);
return JSValue(itInst);
}
static JSValue Object_next(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/)
{
JSValue iteratorValue = argv[0];
ASSERT(iteratorValue.isInstance());
JSIteratorInstance *itInst = checked_cast<JSIteratorInstance *>(iteratorValue.instance);
itInst->it++;
while (true) {
while (itInst->it == itInst->obj->mProperties.end()) {
if (itInst->obj->mPrototype.isNull())
return kNullValue;
itInst->obj = itInst->obj->mPrototype.getObjectValue();
itInst->it = itInst->obj->mProperties.begin();
}
if (PROPERTY_ATTR(itInst->it) & Property::Enumerable)
break;
itInst->it++;
}
JSValue v(&PROPERTY_NAME(itInst->it));
itInst->setProperty(cx, cx->mWorld.identifiers["value"], 0, v);
return iteratorValue;
}
static JSValue Object_done(Context *, const JSValue& /*thisValue*/, JSValue * /*argv*/, uint32 /*argc*/)
{
return kUndefinedValue;
}
static JSValue Function_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
JSValue v = thisValue;
if (v.isNull())
v = Function_Type->newInstance(cx);
ASSERT(v.isInstance());
String s;
if (argc == 0)
s = widenCString("() { }");
else {
if (argc == 1)
s = widenCString("() {") + *argv[0].toString(cx).string + "}";
else {
s = widenCString("("); // ')'
for (uint32 i = 0; i < (argc - 1); i++) {
s += *argv[i].toString(cx).string;
if (i < (argc - 2))
s += widenCString(", ");
}
/* ( */ s += ") {" + *argv[argc - 1].toString(cx).string + "}";
}
}
JSFunction *fnc = NULL;
/***************************************************************/
{
Arena a;
Parser p(cx->mWorld, a, cx->mFlags, s, widenCString("function constructor"));
Reader *oldReader = cx->mReader;
cx->mReader = &p.lexer.reader;
FunctionExprNode *f = p.parseFunctionExpression(0);
if (!p.lexer.peek(true).hasKind(Token::end))
cx->reportError(Exception::syntaxError, "Unexpected stuff after the function body");
fnc = new JSFunction(cx, NULL, cx->mScopeChain);
fnc->setResultType(Object_Type);
fnc->countParameters(cx, f->function);
if (cx->mScopeChain->isPossibleUncheckedFunction(f->function)) {
fnc->setIsPrototype(true);
fnc->setIsUnchecked();
}
cx->buildRuntimeForFunction(f->function, fnc);
ByteCodeGen bcg(cx, cx->mScopeChain);
bcg.genCodeForFunction(f->function, f->pos, fnc, false, NULL);
cx->setReader(oldReader);
}
/***************************************************************/
JSValue fncPrototype = Object_Type->newInstance(cx);
ASSERT(fncPrototype.isObject());
fncPrototype.object->defineVariable(cx, cx->Constructor_StringAtom, (NamespaceList *)NULL, Property::Enumerable, Object_Type, JSValue(fnc));
fnc->defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, Property::Enumerable, Object_Type, fncPrototype);
v = JSValue(fnc);
return v;
}
static JSValue Function_toString(Context *, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
ASSERT(thisValue.getType() == Function_Type);
return JSValue(new String(widenCString("function () { }")));
}
static JSValue Function_hasInstance(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
ASSERT(argc == 1);
JSValue v = argv[0];
if (!v.isObject())
return kFalseValue;
ASSERT(thisValue.isFunction());
thisValue.function->getProperty(cx, cx->Prototype_StringAtom, CURRENT_ATTR);
JSValue p = cx->popValue();
if (!p.isObject())
cx->reportError(Exception::typeError, "HasInstance: Function has non-object prototype");
JSValue V = v.object->mPrototype;
while (!V.isNull()) {
if (V.getObjectValue() == p.object)
return kTrueValue;
V = V.getObjectValue()->mPrototype;
}
return kFalseValue;
}
static JSValue Function_call(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
if (!thisValue.isFunction())
cx->reportError(Exception::typeError, "Non-callable object for Function.call");
JSValue thisArg;
if (argc == 0)
thisArg = JSValue(cx->getGlobalObject());
else {
if (argv[0].isUndefined() || argv[0].isNull())
thisArg = JSValue(cx->getGlobalObject());
else
thisArg = JSValue(argv[0].toObject(cx));
--argc;
++argv;
}
return cx->invokeFunction(thisValue.function, thisArg, argv, argc);
}
static JSValue Function_apply(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
if (!thisValue.isFunction())
cx->reportError(Exception::typeError, "Non-callable object for Function.call");
ContextStackReplacement csr(cx);
JSValue thisArg;
if (argc == 0)
thisArg = JSValue(cx->getGlobalObject());
else {
if (argv[0].isUndefined() || argv[0].isNull())
thisArg = JSValue(cx->getGlobalObject());
else
thisArg = JSValue(argv[0].toObject(cx));
}
if (argc <= 1) {
argv = NULL;
argc = 0;
}
else {
if (argv[1].getType() != Array_Type)
cx->reportError(Exception::typeError, "Function.apply must have Array type argument list");
ASSERT(argv[1].isObject());
JSObject *argsObj = argv[1].object;
argsObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
JSValue result = cx->popValue();
argc = (uint32)(result.toUInt32(cx).f64);
argv = new JSValue[argc];
for (uint32 i = 0; i < argc; i++) {
const String *id = numberToString(i);
argsObj->getProperty(cx, *id, CURRENT_ATTR);
argv[i] = cx->popValue();
delete id;
}
}
return cx->invokeFunction(thisValue.function, thisArg, argv, argc);
}
static JSValue Number_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
JSValue v = thisValue;
if (v.isNull())
v = Number_Type->newInstance(cx);
ASSERT(v.isInstance());
JSNumberInstance *numInst = checked_cast<JSNumberInstance *>(v.instance);
if (argc > 0)
numInst->mValue = argv[0].toNumber(cx).f64;
else
numInst->mValue = 0.0;
return v;
}
static JSValue Number_TypeCast(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc == 0)
return kPositiveZero;
else
return argv[0].toNumber(cx);
}
static JSValue Number_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
if (thisValue.getType() != Number_Type)
cx->reportError(Exception::typeError, "Number.toString called on something other than a Number object");
JSNumberInstance *numInst = checked_cast<JSNumberInstance *>(thisValue.instance);
return JSValue(numberToString(numInst->mValue));
}
static JSValue Number_valueOf(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
if (thisValue.getType() != Number_Type)
cx->reportError(Exception::typeError, "Number.valueOf called on something other than a Number object");
JSNumberInstance *numInst = checked_cast<JSNumberInstance *>(thisValue.instance);
return JSValue(numberToString(numInst->mValue));
}
static JSValue Integer_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
JSValue v = thisValue;
if (v.isNull())
v = Integer_Type->newInstance(cx);
ASSERT(v.isInstance());
JSNumberInstance *numInst = checked_cast<JSNumberInstance *>(v.instance);
if (argc > 0) {
float64 d = argv[0].toNumber(cx).f64;
bool neg = (d < 0);
d = fd::floor(neg ? -d : d);
d = neg ? -d : d;
numInst->mValue = d;
}
else
numInst->mValue = 0.0;
return v;
}
static JSValue Integer_toString(Context *, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
JSNumberInstance *numInst = checked_cast<JSNumberInstance *>(thisValue.instance);
return JSValue(numberToString(numInst->mValue));
}
static JSValue Boolean_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
JSValue v = thisValue;
if (v.isNull())
v = Boolean_Type->newInstance(cx);
ASSERT(v.isInstance());
JSBooleanInstance *thisObj = checked_cast<JSBooleanInstance *>(v.instance);
if (argc > 0)
thisObj->mValue = argv[0].toBoolean(cx).boolean;
else
thisObj->mValue = false;
return v;
}
static JSValue Boolean_TypeCast(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc == 0)
return kFalseValue;
else
return argv[0].toBoolean(cx);
}
static JSValue Boolean_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
if (thisValue.getType() != Boolean_Type)
cx->reportError(Exception::typeError, "Boolean.toString can only be applied to Boolean objects");
ASSERT(thisValue.isInstance());
JSBooleanInstance *thisObj = checked_cast<JSBooleanInstance *>(thisValue.instance);
if (thisObj->mValue)
return JSValue(&cx->True_StringAtom);
else
return JSValue(&cx->False_StringAtom);
}
static JSValue Boolean_valueOf(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
if (thisValue.getType() != Boolean_Type)
cx->reportError(Exception::typeError, "Boolean.valueOf can only be applied to Boolean objects");
ASSERT(thisValue.isInstance());
JSBooleanInstance *thisObj = checked_cast<JSBooleanInstance *>(thisValue.instance);
if (thisObj->mValue)
return kTrueValue;
else
return kFalseValue;
}
static JSValue GenericError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc, JSType *errType)
{
JSValue v = thisValue;
if (v.isNull())
v = errType->newInstance(cx);
ASSERT(v.isInstance());
JSObject *thisInst = v.instance;
JSValue msg;
if (argc > 0)
msg = argv[0].toString(cx);
else
msg = JSValue(&cx->Empty_StringAtom);
thisInst->defineVariable(cx, cx->Message_StringAtom, NULL, Property::NoAttribute, String_Type, msg);
thisInst->defineVariable(cx, cx->Name_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(errType->mClassName));
return v;
}
JSValue RegExp_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
JSValue thatValue = thisValue;
if (thatValue.isNull())
thatValue = RegExp_Type->newInstance(cx);
ASSERT(thatValue.isInstance());
JSRegExpInstance *thisInst = checked_cast<JSRegExpInstance *>(thatValue.instance);
REuint32 flags = 0;
const String *regexpStr = &cx->Empty_StringAtom;
const String *flagStr = &cx->Empty_StringAtom;
if (argc > 0) {
if (argv[0].getType() == RegExp_Type) {
ASSERT(argv[0].isInstance());
if ((argc == 1) || argv[1].isUndefined()) {
ContextStackReplacement csr(cx);
argv[0].instance->getProperty(cx, cx->Source_StringAtom, CURRENT_ATTR);
JSValue src = cx->popValue();
ASSERT(src.isString());
regexpStr = src.string;
REState *other = (checked_cast<JSRegExpInstance *>(argv[0].instance))->mRegExp;
flags = other->flags;
}
else
cx->reportError(Exception::typeError, "Illegal RegExp constructor args");
}
else
regexpStr = argv[0].toString(cx).string;
if ((argc > 1) && !argv[1].isUndefined()) {
flagStr = argv[1].toString(cx).string;
if (parseFlags(flagStr->begin(), flagStr->length(), &flags) != RE_NO_ERROR) {
cx->reportError(Exception::syntaxError, "Failed to parse RegExp : '{0}'", *regexpStr + "/" + *flagStr); // XXX error message?
}
}
}
REState *pState = REParse(regexpStr->begin(), regexpStr->length(), flags, RE_VERSION_1);
if (pState) {
thisInst->mRegExp = pState;
// XXX ECMA spec says these are DONTENUM, but SpiderMonkey and test suite disagree
/*
thisInst->defineVariable(cx, cx->Source_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), String_Type, JSValue(regexpStr));
thisInst->defineVariable(cx, cx->Global_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (pState->flags & GLOBAL) ? kTrueValue : kFalseValue);
thisInst->defineVariable(cx, cx->IgnoreCase_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (pState->flags & IGNORECASE) ? kTrueValue : kFalseValue);
thisInst->defineVariable(cx, cx->Multiline_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly), Boolean_Type, (pState->flags & MULTILINE) ? kTrueValue : kFalseValue);
thisInst->defineVariable(cx, cx->LastIndex_StringAtom, NULL, Property::DontDelete, Number_Type, kPositiveZero);
*/
thisInst->defineVariable(cx, cx->Source_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), String_Type, JSValue(regexpStr));
thisInst->defineVariable(cx, cx->Global_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (pState->flags & RE_GLOBAL) ? kTrueValue : kFalseValue);
thisInst->defineVariable(cx, cx->IgnoreCase_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (pState->flags & RE_IGNORECASE) ? kTrueValue : kFalseValue);
thisInst->defineVariable(cx, cx->Multiline_StringAtom, NULL, (Property::DontDelete | Property::ReadOnly | Property::Enumerable), Boolean_Type, (pState->flags & RE_MULTILINE) ? kTrueValue : kFalseValue);
thisInst->defineVariable(cx, cx->LastIndex_StringAtom, NULL, (Property::DontDelete | Property::Enumerable), Number_Type, kPositiveZero);
}
else {
cx->reportError(Exception::syntaxError, "Failed to parse RegExp : '{0}'", "/" + *regexpStr + "/" + *flagStr); // XXX error message?
}
return thatValue;
}
static JSValue RegExp_TypeCast(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
if (argc > 0) {
if ((argv[0].getType() == RegExp_Type)
&& ((argc == 1) || argv[1].isUndefined()))
return argv[0];
}
return RegExp_Constructor(cx, thisValue, argv, argc);
}
static JSValue RegExp_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
ContextStackReplacement csr(cx);
if (thisValue.getType() != RegExp_Type)
cx->reportError(Exception::typeError, "RegExp.toString can only be applied to RegExp objects");
ASSERT(thisValue.isInstance());
JSRegExpInstance *thisInst = checked_cast<JSRegExpInstance *>(thisValue.instance);
thisInst->getProperty(cx, cx->Source_StringAtom, CURRENT_ATTR);
JSValue src = cx->popValue();
// XXX not ever expecting this except in the one case of RegExp.prototype, which isn't a fully formed
// RegExp instance, but has the appropriate type pointer.
if (src.isUndefined()) {
ASSERT(thisInst == RegExp_Type->mPrototypeObject.instance);
return JSValue(&cx->Empty_StringAtom);
}
String *result = new String("/" + *src.toString(cx).string + "/");
REState *state = (REState *)thisInst->mRegExp;
if (state->flags & RE_GLOBAL) *result += "g";
if (state->flags & RE_IGNORECASE) *result += "i";
if (state->flags & RE_MULTILINE) *result += "m";
return JSValue(result);
}
JSValue RegExp_exec(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
if (thisValue.getType() != RegExp_Type)
cx->reportError(Exception::typeError, "RegExp.exec can only be applied to RegExp objects");
ASSERT(thisValue.isInstance());
JSRegExpInstance *thisInst = checked_cast<JSRegExpInstance *>(thisValue.instance);
JSValue result = kNullValue;
if (argc > 0) {
ContextStackReplacement csr(cx);
int32 index = 0;
const String *str = argv[0].toString(cx).string;
RegExp_Type->getProperty(cx, cx->Multiline_StringAtom, CURRENT_ATTR);
JSValue globalMultiline = cx->popValue();
if (thisInst->mRegExp->flags & RE_GLOBAL) {
// XXX implement lastIndex as a setter/getter pair instead ???
thisInst->getProperty(cx, cx->LastIndex_StringAtom, CURRENT_ATTR);
JSValue lastIndex = cx->popValue();
index = (int32)(lastIndex.toInteger(cx).f64);
}
REMatchState *match = REExecute(thisInst->mRegExp, str->begin(), index, str->length(), globalMultiline.toBoolean(cx).boolean);
if (match) {
result = Array_Type->newInstance(cx);
String *matchStr = new String(str->substr(match->startIndex, match->endIndex - match->startIndex));
result.instance->setProperty(cx, *numberToString(0), NULL, JSValue(matchStr));
String *parenStr = &cx->Empty_StringAtom;
for (int32 i = 0; i < match->parenCount; i++) {
if (match->parens[i].index != -1) {
String *parenStr = new String(str->substr((uint32)(match->parens[i].index), (uint32)(match->parens[i].length)));
result.instance->setProperty(cx, *numberToString(i + 1), NULL, JSValue(parenStr));
}
else
result.instance->setProperty(cx, *numberToString(i + 1), NULL, kUndefinedValue);
}
// XXX SpiderMonkey also adds 'index' and 'input' properties to the result
result.instance->setProperty(cx, cx->Index_StringAtom, CURRENT_ATTR, JSValue((float64)(match->startIndex)));
result.instance->setProperty(cx, cx->Input_StringAtom, CURRENT_ATTR, JSValue(str));
// XXX Set up the SpiderMonkey 'RegExp statics'
RegExp_Type->setProperty(cx, cx->LastMatch_StringAtom, CURRENT_ATTR, JSValue(matchStr));
RegExp_Type->setProperty(cx, cx->LastParen_StringAtom, CURRENT_ATTR, JSValue(parenStr));
String *contextStr = new String(str->substr(0, match->startIndex));
RegExp_Type->setProperty(cx, cx->LeftContext_StringAtom, CURRENT_ATTR, JSValue(contextStr));
contextStr = new String(str->substr(match->endIndex, str->length() - match->endIndex));
RegExp_Type->setProperty(cx, cx->RightContext_StringAtom, CURRENT_ATTR, JSValue(contextStr));
if (thisInst->mRegExp->flags & RE_GLOBAL) {
index = match->endIndex;
thisInst->setProperty(cx, cx->LastIndex_StringAtom, CURRENT_ATTR, JSValue((float64)index));
}
}
}
return result;
}
static JSValue RegExp_test(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
if (thisValue.getType() != RegExp_Type)
cx->reportError(Exception::typeError, "RegExp.test can only be applied to RegExp objects");
ASSERT(thisValue.isInstance());
JSRegExpInstance *thisInst = checked_cast<JSRegExpInstance *>(thisValue.instance);
if (argc > 0) {
ContextStackReplacement csr(cx);
const String *str = argv[0].toString(cx).string;
RegExp_Type->getProperty(cx, cx->Multiline_StringAtom, CURRENT_ATTR);
JSValue globalMultiline = cx->popValue();
REMatchState *match = REExecute(thisInst->mRegExp, str->begin(), 0, str->length(), globalMultiline.toBoolean(cx).boolean);
if (match)
return kTrueValue;
}
return kFalseValue;
}
JSValue Error_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, Error_Type);
}
static JSValue Error_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
{
ContextStackReplacement csr(cx);
if ((thisValue.getType() != Error_Type) && !thisValue.getType()->derivesFrom(Error_Type))
cx->reportError(Exception::typeError, "Error.toString can only be applied to Error objects");
ASSERT(thisValue.isInstance());
JSInstance *thisInstance = thisValue.instance;
thisInstance->getProperty(cx, cx->Message_StringAtom, CURRENT_ATTR);
JSValue msg = cx->popValue();
thisInstance->getProperty(cx, cx->Name_StringAtom, CURRENT_ATTR);
JSValue name = cx->popValue();
String *result = new String(*name.toString(cx).string + ":" + *msg.toString(cx).string);
return JSValue(result);
}
JSValue EvalError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, EvalError_Type);
}
JSValue RangeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, RangeError_Type);
}
JSValue ReferenceError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, ReferenceError_Type);
}
JSValue SyntaxError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, SyntaxError_Type);
}
JSValue TypeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, TypeError_Type);
}
JSValue UriError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
return GenericError_Constructor(cx, thisValue, argv, argc, UriError_Type);
}
static JSValue ExtendAttribute_Invoke(Context * /*cx*/, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
ASSERT(argc == 1);
Attribute *x = new Attribute(Property::Extend, Property::Extend | Property::Virtual);
ASSERT(argv[0].isType());
x->mExtendArgument = (JSType *)(argv[0].type);
return JSValue(x);
}
static JSValue GlobalObject_Eval(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc > 0) {
if (!argv[0].isString())
return argv[0];
const String *sourceStr = argv[0].toString(cx).string;
Activation *prev = cx->mActivationStack.top();
return cx->readEvalString(*sourceStr, widenCString("eval source"), cx->mScopeChain, prev->mThis);
}
return kUndefinedValue;
}
static JSValue GlobalObject_ParseInt(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
ASSERT(argc >= 1);
int radix = 0; // default for stringToInteger
if (argc == 2)
radix = (int)(argv[1].toInt32(cx).f64);
if ((radix < 0) || (radix > 36))
return kNaNValue;
const String *string = argv[0].toString(cx).string;
const char16 *numEnd;
const char16 *sBegin = string->begin();
float64 f = 0.0;
if (sBegin)
f = stringToInteger(sBegin, string->end(), numEnd, (uint)radix);
return JSValue(f);
}
static JSValue GlobalObject_ParseFloat(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
ASSERT(argc == 1);
const String *string = argv[0].toString(cx).string;
const char16 *numEnd;
const char16 *sBegin = string->begin();
float64 f = 0.0;
if (sBegin)
f = stringToDouble(sBegin, string->end(), numEnd);
return JSValue(f);
}
static JSValue GlobalObject_isNaN(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/)
{
float64 f = argv[0].toNumber(cx).f64;
if (JSDOUBLE_IS_NaN(f))
return kTrueValue;
else
return kFalseValue;
}
static JSValue GlobalObject_isFinite(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 /*argc*/)
{
float64 f = argv[0].toNumber(cx).f64;
if (JSDOUBLE_IS_FINITE(f))
return kTrueValue;
else
return kFalseValue;
}
/*
* Stuff to emulate the old libmocha escape, which took a second argument
* giving the type of escape to perform. Retained for compatibility, and
* copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
*/
#define URL_XALPHAS ((uint8) 1)
#define URL_XPALPHAS ((uint8) 2)
#define URL_PATH ((uint8) 4)
static const uint8 urlCharType[256] =
/* Bit 0 xalpha -- the alphas
* Bit 1 xpalpha -- as xalpha but
* converts spaces to plus and plus to %20
* Bit 2 ... path -- as xalphas but doesn't escape '/'
*/
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
0, };
/* This matches the ECMA escape set when mask is 7 (default.) */
#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
/* See ECMA-262 15.1.2.4. */
static JSValue GlobalObject_escape(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc > 0) {
uint32 i, ni, length, newlength;
char16 ch;
const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
uint32 mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
if (argc > 1) {
float64 d = argv[1].toNumber(cx).f64;
if (!JSDOUBLE_IS_FINITE(d) || (mask = (uint32)d) != d || mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
cx->reportError(Exception::runtimeError, "Bad string mask for escape");
}
const String *str = argv[0].toString(cx).string;
const char16 *chars = str->begin();
length = newlength = str->size();
/* Take a first pass and see how big the result string will need to be. */
for (i = 0; i < length; i++) {
if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
continue;
if (ch < 256) {
if (mask == URL_XPALPHAS && ch == ' ')
continue; /* The character will be encoded as '+' */
newlength += 2; /* The character will be encoded as %XX */
} else {
newlength += 5; /* The character will be encoded as %uXXXX */
}
}
char16 *newchars = new char16[newlength + 1];
for (i = 0, ni = 0; i < length; i++) {
if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
newchars[ni++] = ch;
} else if (ch < 256) {
if (mask == URL_XPALPHAS && ch == ' ') {
newchars[ni++] = '+'; /* convert spaces to pluses */
} else {
newchars[ni++] = '%';
newchars[ni++] = digits[ch >> 4];
newchars[ni++] = digits[ch & 0xF];
}
} else {
newchars[ni++] = '%';
newchars[ni++] = 'u';
newchars[ni++] = digits[ch >> 12];
newchars[ni++] = digits[(ch & 0xF00) >> 8];
newchars[ni++] = digits[(ch & 0xF0) >> 4];
newchars[ni++] = digits[ch & 0xF];
}
}
ASSERT(ni == newlength);
newchars[newlength] = 0;
String *result = new String(newchars, newlength);
delete[] newchars;
return JSValue(result);
}
return JSValue(&cx->Undefined_StringAtom);
}
#undef IS_OK
/* See ECMA-262 15.1.2.5 */
static JSValue GlobalObject_unescape(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
if (argc > 0) {
uint32 i, ni;
char16 ch;
const String *str = argv[0].toString(cx).string;
const char16 *chars = str->begin();
uint32 length = str->size();
/* Don't bother allocating less space for the new string. */
char16 *newchars = new char16[length + 1];
ni = i = 0;
while (i < length) {
uint hexValue[4];
ch = chars[i++];
if (ch == '%') {
if (i + 1 < length &&
isASCIIHexDigit(chars[i], hexValue[0]) && isASCIIHexDigit(chars[i + 1], hexValue[1]))
{
ch = static_cast<char16>(hexValue[0] * 16 + hexValue[1]);
i += 2;
} else if (i + 4 < length && chars[i] == 'u' &&
isASCIIHexDigit(chars[i + 1], hexValue[0]) && isASCIIHexDigit(chars[i + 2], hexValue[1]) &&
isASCIIHexDigit(chars[i + 3], hexValue[2]) && isASCIIHexDigit(chars[i + 4], hexValue[3]))
{
ch = static_cast<char16>((((((hexValue[0] << 4)
+ hexValue[1]) << 4)
+ hexValue[2]) << 4)
+ hexValue[3]);
i += 5;
}
}
newchars[ni++] = ch;
}
newchars[ni] = 0;
String *result = new String(newchars, ni);
delete[] newchars;
return JSValue(result);
}
return JSValue(&cx->Undefined_StringAtom);
}
JSFunction::JSFunction(Context *cx, JSType *resultType, ScopeChain *scopeChain)
: JSInstance(cx, Function_Type),
mParameterBarrel(NULL),
mActivation(),
mByteCode(NULL),
mCode(NULL),
mResultType(resultType),
mRequiredParameters(0),
mOptionalParameters(0),
mNamedParameters(0),
mParameters(NULL),
mScopeChain(NULL),
mIsPrototype(false),
mIsConstructor(false),
mIsChecked(true),
mHasRestParameter(false),
mClass(NULL),
mFunctionName(NULL)
{
if (scopeChain) {
mScopeChain = new ScopeChain(*scopeChain);
}
if (Function_Type) // protect against bootstrap
mPrototype = Function_Type->mPrototypeObject;
mActivation.mContainer = this;
defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(Object_Type->newInstance(cx)));
if (!mPrototype.isNull())
defineVariable(cx, cx->UnderbarPrototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, mPrototype);
}
JSFunction::JSFunction(Context *cx, NativeCode *code, JSType *resultType)
: JSInstance(cx, Function_Type),
mParameterBarrel(NULL),
mActivation(),
mByteCode(NULL),
mCode(code),
mResultType(resultType),
mRequiredParameters(0),
mOptionalParameters(0),
mNamedParameters(0),
mParameters(NULL),
mScopeChain(NULL),
mIsPrototype(false),
mIsConstructor(false),
mIsChecked(false), // native functions aren't checked (?)
mHasRestParameter(false),
mClass(NULL),
mFunctionName(NULL)
{
if (Function_Type) // protect against bootstrap
mPrototype = Function_Type->mPrototypeObject;
mActivation.mContainer = this;
defineVariable(cx, cx->Prototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, JSValue(Object_Type->newInstance(cx)));
defineVariable(cx, cx->UnderbarPrototype_StringAtom, (NamespaceList *)NULL, 0, Object_Type, mPrototype);
}
JSValue JSFunction::runParameterInitializer(Context *cx, uint32 a, const JSValue& thisValue, JSValue *argv, uint32 argc)
{
ASSERT(mParameters && (a < maxParameterIndex()));
return cx->interpret(getByteCode(), (int32)mParameters[a].mInitializer, getScopeChain(), thisValue, argv, argc);
}
void JSFunction::setParameterCounts(Context *cx, uint32 r, uint32 o, uint32 n, bool hasRest)
{
mHasRestParameter = hasRest;
mRequiredParameters = r;
mOptionalParameters = o;
mNamedParameters = n;
mParameters = new ParameterData[mRequiredParameters + mOptionalParameters + + mNamedParameters + ((hasRest) ? 1 : 0)];
defineVariable(cx, cx->Length_StringAtom, (NamespaceList *)NULL, Property::DontDelete | Property::ReadOnly, Number_Type, JSValue((float64)mRequiredParameters));
}
void Context::assureStackSpace(uint32 s)
{
if ((mStackMax - mStackTop) < s) {
JSValue *newStack = new JSValue[mStackMax + s];
for (uint32 i = 0; i < mStackTop; i++)
newStack[i] = mStack[i];
delete[] mStack;
mStack = newStack;
mStackMax += s;
}
}
// Initialize a built-in class - setting the functions into the prototype object
void Context::initClass(JSType *type, ClassDef *cdef, PrototypeFunctions *pdef)
{
mScopeChain->addScope(type);
JSFunction *constructor = new JSFunction(this, cdef->defCon, Object_Type);
constructor->setClass(type);
constructor->setFunctionName(type->mClassName);
type->setDefaultConstructor(this, constructor);
// the prototype functions are defined in the prototype object...
if (pdef) {
for (uint32 i = 0; i < pdef->mCount; i++) {
JSFunction *fun = new JSFunction(this, pdef->mDef[i].imp, pdef->mDef[i].result);
// fun->setClass(type); don't do this, it makes the function a method
StringAtom *name = &mWorld.identifiers[widenCString(pdef->mDef[i].name)];
fun->setFunctionName(name);
fun->setParameterCounts(this, pdef->mDef[i].length, 0, 0, false);
type->mPrototypeObject.getObjectValue()->defineVariable(this, *name,
(NamespaceList *)(NULL),
Property::NoAttribute,
pdef->mDef[i].result,
JSValue(fun));
}
}
type->completeClass(this, mScopeChain);
type->setStaticInitializer(this, NULL);
type->mUninitializedValue = *cdef->uninit;
getGlobalObject()->defineVariable(this, widenCString(cdef->name), (NamespaceList *)(NULL), Property::NoAttribute, Type_Type, JSValue(type));
mScopeChain->popScope();
if (pdef) delete pdef;
}
static JSValue arrayMaker(Context *cx, const JSValue& /*thisValue*/, JSValue *argv, uint32 argc)
{
ASSERT(argc == 2);
ASSERT(argv[0].isType());
JSType *baseType = argv[0].type;
if ((baseType == Array_Type) && argv[1].isType()) {
JSType *elementType = argv[1].type;
JSType *result = new JSArrayType(cx, elementType, NULL, Object_Type, kNullValue, kNullValue);
result->setDefaultConstructor(cx, Array_Type->getDefaultConstructor());
return JSValue(result);
}
else {
// then it's just <type>[] - an element reference
argv[0].type->getProperty(cx, *argv[1].toString(cx).string, NULL);
return cx->popValue();
}
}
void Context::initBuiltins()
{
ClassDef builtInClasses[] =
{
{ "Object", Object_Constructor, &kUndefinedValue },
{ "Type", NULL, &kNullValue },
{ "Function", Function_Constructor, &kNullValue },
{ "Number", Number_Constructor, &kPositiveZero },
{ "Integer", Integer_Constructor, &kPositiveZero },
{ "String", String_Constructor, &kNullValue },
{ "Array", Array_Constructor, &kNullValue },
{ "Boolean", Boolean_Constructor, &kFalseValue },
{ "Void", NULL, &kUndefinedValue },
{ "Unit", NULL, &kNullValue },
{ "Attribute", NULL, &kNullValue },
{ "NamedArgument", NULL, &kNullValue },
{ "Date", Date_Constructor, &kPositiveZero },
{ "Null", NULL, &kNullValue },
{ "Error", Error_Constructor, &kNullValue },
{ "EvalError", EvalError_Constructor, &kNullValue },
{ "RangeError", RangeError_Constructor, &kNullValue },
{ "ReferenceError", ReferenceError_Constructor, &kNullValue },
{ "SyntaxError", SyntaxError_Constructor, &kNullValue },
{ "TypeError", TypeError_Constructor, &kNullValue },
{ "UriError", UriError_Constructor, &kNullValue },
{ "RegExp", RegExp_Constructor, &kNullValue },
{ "Package", NULL, &kNullValue },
};
Object_Type = new JSObjectType(this, &mWorld.identifiers[widenCString(builtInClasses[0].name)], NULL, kNullValue, kNullValue);
// Object_Type->mPrototype->mType = Object_Type;
Object_Type->mIsDynamic = true; // XXX aren't all the built-ins thus?
Type_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[1].name)], Object_Type, kNullValue, kNullValue);
Object_Type->mType = Type_Type;
//
// ECMA 1.5 says Function.prototype is a Function object, it's [[class]] property is "Function"
// ECMA 2.0 says Function.prototype is an Object NOT an instance of the class
// here we sort of manage that by having a JSFunction object as the prototype object, not a JSFunctionInstance
// (which we don't actually have, hmm).
// For String, etc. this same issue needs to be finessed
JSValue protoVal;
JSFunction *funProto = new JSFunction(this, Object_Type, NULL);
funProto->mPrototype = Object_Type->mPrototypeObject;
funProto->defineVariable(this, UnderbarPrototype_StringAtom, NULL, 0, Object_Type, funProto->mPrototype);
protoVal = JSValue(funProto);
Function_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[2].name)], Object_Type, protoVal, kNullValue);
Function_Type->mPrototype = Function_Type->mPrototypeObject;
funProto->mType = Function_Type;
// now we can bootstrap the Object prototype (__proto__) to be the Function prototype
Object_Type->mPrototype = Function_Type->mPrototypeObject;
Object_Type->setProperty(this, UnderbarPrototype_StringAtom, (NamespaceList *)NULL, Object_Type->mPrototype);
JSNumberInstance *numProto = new JSNumberInstance(this);
protoVal = JSValue(numProto);
Number_Type = new JSNumberType(this, &mWorld.identifiers[widenCString(builtInClasses[3].name)], Object_Type, protoVal, Function_Type->mPrototypeObject);
numProto->mType = Number_Type;
Integer_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[4].name)], Object_Type, kNullValue, kNullValue);
JSStringInstance *strProto = new JSStringInstance(this);
protoVal = JSValue(strProto);
String_Type = new JSStringType(this, &mWorld.identifiers[widenCString(builtInClasses[5].name)], Object_Type, protoVal, Function_Type->mPrototypeObject);
strProto->mValue = &Empty_StringAtom;
strProto->mPrototype = Function_Type->mPrototypeObject;
strProto->mType = String_Type;
JSArrayInstance *arrayProto = new JSArrayInstance(this);
protoVal = JSValue(arrayProto);
Array_Type = new JSArrayType(this, Object_Type, &mWorld.identifiers[widenCString(builtInClasses[6].name)], Object_Type, protoVal, Function_Type->mPrototypeObject);
arrayProto->mType = Array_Type;
JSBooleanInstance *boolProto = new JSBooleanInstance(this);
protoVal = JSValue(boolProto);
Boolean_Type = new JSBooleanType(this, &mWorld.identifiers[widenCString(builtInClasses[7].name)], Object_Type, protoVal, Function_Type->mPrototypeObject);
boolProto->mValue = false;
boolProto->mType = Boolean_Type;
JSDateInstance *dateProto = new JSDateInstance(this);
protoVal = JSValue(dateProto);
Date_Type = new JSDateType(this, &mWorld.identifiers[widenCString(builtInClasses[12].name)], Object_Type, protoVal, Function_Type->mPrototypeObject);
dateProto->mType = Date_Type;
Void_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[8].name)], Object_Type, kNullValue, kNullValue);
Unit_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[9].name)], Object_Type, kNullValue, kNullValue);
Attribute_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[10].name)], Object_Type, kNullValue, kNullValue);
NamedArgument_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[11].name)], Object_Type, kNullValue, kNullValue);
Null_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[13].name)], Object_Type, kNullValue, kNullValue);
Error_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[14].name)], Object_Type, kNullValue, kNullValue);
EvalError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[15].name)], Error_Type, kNullValue, kNullValue);
RangeError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[16].name)], Error_Type, kNullValue, kNullValue);
ReferenceError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[17].name)], Error_Type, kNullValue, kNullValue);
SyntaxError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[18].name)], Error_Type, kNullValue, kNullValue);
TypeError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[19].name)], Error_Type, kNullValue, kNullValue);
UriError_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[20].name)], Error_Type, kNullValue, kNullValue);
// XXX RegExp.prototype is set to a RegExp instance, which isn't ECMA (it's supposed to be an Object instance) but
// is SpiderMonkey compatible.
JSRegExpInstance *regExpProto = new JSRegExpInstance(this);
protoVal = JSValue(regExpProto);
RegExp_Type = new JSRegExpType(this, &mWorld.identifiers[widenCString(builtInClasses[21].name)], Object_Type, protoVal, Function_Type->mPrototypeObject);
regExpProto->mPrototype = Object_Type->mPrototypeObject;
regExpProto->mType = RegExp_Type;
Package_Type = new JSType(this, &mWorld.identifiers[widenCString(builtInClasses[22].name)], Object_Type, kNullValue, kNullValue);
ProtoFunDef objectProtos[] =
{
{ "toString", String_Type, 0, Object_toString },
{ "toSource", String_Type, 0, Object_toString },
{ "forin", Object_Type, 0, Object_forin },
{ "next", Object_Type, 0, Object_next },
{ "done", Object_Type, 0, Object_done },
{ "valueOf", Object_Type, 0, Object_valueOf },
{ NULL }
};
ProtoFunDef functionProtos[] =
{
{ "toString", String_Type, 0, Function_toString },
{ "toSource", String_Type, 0, Function_toString },
{ "hasInstance", Boolean_Type, 1, Function_hasInstance },
{ "call", Object_Type, 1, Function_call },
{ "apply", Object_Type, 2, Function_apply },
{ NULL }
};
ProtoFunDef numberProtos[] =
{
{ "toString", String_Type, 0, Number_toString },
{ "toSource", String_Type, 0, Number_toString },
{ "valueOf", Number_Type, 0, Number_valueOf },
{ NULL }
};
ProtoFunDef integerProtos[] =
{
{ "toString", String_Type, 0, Number_toString },
{ "toSource", String_Type, 0, Number_toString },
{ NULL }
};
ProtoFunDef booleanProtos[] =
{
{ "toString", String_Type, 0, Boolean_toString },
{ "toSource", String_Type, 0, Boolean_toString },
{ "valueOf", Number_Type, 0, Boolean_valueOf },
{ NULL }
};
ProtoFunDef errorProtos[] =
{
{ "toString", String_Type, 0, Error_toString },
{ "toSource", String_Type, 0, Error_toString },
{ NULL }
};
ProtoFunDef regexpProtos[] =
{
{ "toString", String_Type, 0, RegExp_toString },
{ "toSource", String_Type, 0, RegExp_toString },
{ "exec", Array_Type, 0, RegExp_exec },
{ "test", Boolean_Type,0, RegExp_test },
{ NULL }
};
ASSERT(mGlobal);
*mGlobal = Object_Type->newInstance(this).object;
initClass(Object_Type, &builtInClasses[0], new PrototypeFunctions(&objectProtos[0]) );
initClass(Type_Type, &builtInClasses[1], NULL );
initClass(Function_Type, &builtInClasses[2], new PrototypeFunctions(&functionProtos[0]) );
initClass(Number_Type, &builtInClasses[3], new PrototypeFunctions(&numberProtos[0]) );
initClass(Integer_Type, &builtInClasses[4], new PrototypeFunctions(&integerProtos[0]) );
initClass(String_Type, &builtInClasses[5], getStringProtos() );
initClass(Array_Type, &builtInClasses[6], getArrayProtos() );
initClass(Boolean_Type, &builtInClasses[7], new PrototypeFunctions(&booleanProtos[0]) );
initClass(Void_Type, &builtInClasses[8], NULL);
initClass(Unit_Type, &builtInClasses[9], NULL);
initClass(Attribute_Type, &builtInClasses[10], NULL);
initClass(NamedArgument_Type, &builtInClasses[11], NULL);
initClass(Date_Type, &builtInClasses[12], getDateProtos() );
initClass(Null_Type, &builtInClasses[13], NULL);
initClass(Error_Type, &builtInClasses[14], new PrototypeFunctions(&errorProtos[0]));
initClass(EvalError_Type, &builtInClasses[15], new PrototypeFunctions(&errorProtos[0])); // XXX shouldn't these be inherited from prototype?
initClass(RangeError_Type, &builtInClasses[16], new PrototypeFunctions(&errorProtos[0]));
initClass(ReferenceError_Type, &builtInClasses[17], new PrototypeFunctions(&errorProtos[0]));
initClass(SyntaxError_Type, &builtInClasses[18], new PrototypeFunctions(&errorProtos[0]));
initClass(TypeError_Type, &builtInClasses[19], new PrototypeFunctions(&errorProtos[0]));
initClass(UriError_Type, &builtInClasses[20], new PrototypeFunctions(&errorProtos[0]));
initClass(RegExp_Type, &builtInClasses[21], new PrototypeFunctions(&regexpProtos[0]));
defineOperator(Index, Type_Type, new JSFunction(this, arrayMaker, Type_Type));
Object_Type->mTypeCast = new JSFunction(this, Object_Constructor, Object_Type);
Function_Type->mTypeCast = new JSFunction(this, Function_Constructor, Object_Type);
Number_Type->mTypeCast = new JSFunction(this, Number_TypeCast, Number_Type);
defineOperator(Index, Array_Type, new JSFunction(this, Array_GetElement, Object_Type));
defineOperator(IndexEqual, Array_Type, new JSFunction(this, Array_SetElement, Object_Type));
Array_Type->mTypeCast = new JSFunction(this, Array_Constructor, Array_Type);
Array_Type->defineVariable(this, Length_StringAtom, NULL, Property::NoAttribute, Number_Type, JSValue(1.0));
Boolean_Type->mTypeCast = new JSFunction(this, Boolean_TypeCast, Boolean_Type);
Boolean_Type->defineVariable(this, Length_StringAtom, NULL, Property::NoAttribute, Number_Type, JSValue(1.0));
Date_Type->mTypeCast = new JSFunction(this, Date_TypeCast, String_Type);
Date_Type->defineStaticMethod(this, widenCString("parse"), NULL, new JSFunction(this, Date_parse, Number_Type));
Date_Type->defineStaticMethod(this, widenCString("UTC"), NULL, new JSFunction(this, Date_UTC, Number_Type));
JSFunction *fromCharCode = new JSFunction(this, String_fromCharCode, String_Type);
fromCharCode->defineVariable(this, Length_StringAtom, (NamespaceList *)NULL, Property::DontDelete | Property::ReadOnly, Number_Type, JSValue(1.0));
String_Type->defineVariable(this, FromCharCode_StringAtom, NULL, String_Type, JSValue(fromCharCode));
String_Type->mTypeCast = new JSFunction(this, String_TypeCast, String_Type);
String_Type->defineVariable(this, Length_StringAtom, NULL, Property::NoAttribute, Number_Type, JSValue(1.0));
Error_Type->mTypeCast = new JSFunction(this, Error_Constructor, Error_Type);
RegExp_Type->mTypeCast = new JSFunction(this, RegExp_TypeCast, Error_Type);
// XXX these RegExp statics are not specified by ECMA, but implemented by SpiderMonkey
RegExp_Type->defineVariable(this, Input_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(&Empty_StringAtom));
RegExp_Type->defineVariable(this, LastMatch_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(&Empty_StringAtom));
RegExp_Type->defineVariable(this, LastParen_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(&Empty_StringAtom));
RegExp_Type->defineVariable(this, LeftContext_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(&Empty_StringAtom));
RegExp_Type->defineVariable(this, RightContext_StringAtom, NULL, Property::NoAttribute, String_Type, JSValue(&Empty_StringAtom));
}
OperatorMap operatorMap;
//OperatorHashTable operatorHashTable;
struct OperatorInitData {
char *operatorString;
JS2Runtime::Operator op;
} operatorInitData[] =
{
{ "~", JS2Runtime::Complement, },
{ "++", JS2Runtime::Increment, },
{ "--", JS2Runtime::Decrement, },
{ "()", JS2Runtime::Call, },
{ "new", JS2Runtime::New, },
{ "[]", JS2Runtime::Index, },
{ "[]=", JS2Runtime::IndexEqual, },
{ "delete[]", JS2Runtime::DeleteIndex, },
{ "+", JS2Runtime::Plus, },
{ "-", JS2Runtime::Minus, },
{ "*", JS2Runtime::Multiply, },
{ "/", JS2Runtime::Divide, },
{ "%", JS2Runtime::Remainder, },
{ "<<", JS2Runtime::ShiftLeft, },
{ ">>", JS2Runtime::ShiftRight, },
{ ">>>", JS2Runtime::UShiftRight, },
{ "<", JS2Runtime::Less, },
{ "<=", JS2Runtime::LessEqual, },
{ "in", JS2Runtime::In, },
{ "==", JS2Runtime::Equal, },
{ "===", JS2Runtime::SpittingImage, },
{ "&", JS2Runtime::BitAnd, },
{ "^", JS2Runtime::BitXor, },
{ "|", JS2Runtime::BitOr, },
};
static void initOperatorTable()
{
static bool mapped = false;
if (!mapped) {
for (uint32 i = 0; (i < sizeof(operatorInitData) / sizeof(OperatorInitData)); i++) {
const String& name = widenCString(operatorInitData[i].operatorString);
operatorMap[name] = operatorInitData[i].op;
/*
operatorHashTable.insert(name, OperatorEntry(name, operatorInitData[i].op) );
*/
}
mapped = true;
}
}
JS2Runtime::Operator Context::getOperator(uint32 parameterCount, const String &name)
{
OperatorMap::iterator it = operatorMap.find(name);
if (it == operatorMap.end())
return JS2Runtime::None;
if ((it->second == JS2Runtime::Plus) && (parameterCount == 1))
return JS2Runtime::Posate;
if ((it->second == JS2Runtime::Minus) && (parameterCount == 1))
return JS2Runtime::Negate;
return it->second;
}
Context::Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags)
: Virtual_StringAtom (world.identifiers["virtual"]),
Constructor_StringAtom (world.identifiers["constructor"]),
Operator_StringAtom (world.identifiers["operator"]),
Fixed_StringAtom (world.identifiers["fixed"]),
Dynamic_StringAtom (world.identifiers["dynamic"]),
Extend_StringAtom (world.identifiers["extend"]),
Prototype_StringAtom (world.identifiers["prototype"]),
Forin_StringAtom (world.identifiers["forin"]),
Value_StringAtom (world.identifiers["value"]),
Next_StringAtom (world.identifiers["next"]),
Done_StringAtom (world.identifiers["done"]),
Undefined_StringAtom (world.identifiers["undefined"]),
Object_StringAtom (world.identifiers["object"]),
Boolean_StringAtom (world.identifiers["boolean"]),
Number_StringAtom (world.identifiers["number"]),
String_StringAtom (world.identifiers["string"]),
Function_StringAtom (world.identifiers["function"]),
HasInstance_StringAtom (world.identifiers["hasInstance"]),
True_StringAtom (world.identifiers["true"]),
False_StringAtom (world.identifiers["false"]),
Null_StringAtom (world.identifiers["null"]),
ToString_StringAtom (world.identifiers["toString"]),
ValueOf_StringAtom (world.identifiers["valueOf"]),
Length_StringAtom (world.identifiers["length"]),
FromCharCode_StringAtom (world.identifiers["fromCharCode"]),
Math_StringAtom (world.identifiers["Math"]),
NaN_StringAtom (world.identifiers["NaN"]),
Eval_StringAtom (world.identifiers["eval"]),
Infinity_StringAtom (world.identifiers["Infinity"]),
Empty_StringAtom (world.identifiers[""]),
Arguments_StringAtom (world.identifiers["arguments"]),
Message_StringAtom (world.identifiers["message"]),
Name_StringAtom (world.identifiers["name"]),
Error_StringAtom (world.identifiers["Error"]),
EvalError_StringAtom (world.identifiers["EvalError"]),
RangeError_StringAtom (world.identifiers["RangeError"]),
ReferenceError_StringAtom (world.identifiers["ReferenceError"]),
SyntaxError_StringAtom (world.identifiers["SyntaxError"]),
TypeError_StringAtom (world.identifiers["TypeError"]),
UriError_StringAtom (world.identifiers["UriError"]),
Source_StringAtom (world.identifiers["source"]),
Global_StringAtom (world.identifiers["global"]),
IgnoreCase_StringAtom (world.identifiers["ignoreCase"]),
Multiline_StringAtom (world.identifiers["multiline"]),
Input_StringAtom (world.identifiers["input"]),
Index_StringAtom (world.identifiers["index"]),
LastIndex_StringAtom (world.identifiers["lastIndex"]),
LastMatch_StringAtom (world.identifiers["lastMatch"]),
LastParen_StringAtom (world.identifiers["lastParen"]),
LeftContext_StringAtom (world.identifiers["leftContext"]),
RightContext_StringAtom (world.identifiers["rightContext"]),
Dollar_StringAtom (world.identifiers["$"]),
UnderbarPrototype_StringAtom (world.identifiers["__proto__"]),
mWorld(world),
mScopeChain(NULL),
mArena(a),
mFlags(flags),
mDebugFlag(false),
mCurModule(NULL),
mPC(NULL),
mThis(kNullValue),
mStack(NULL),
mStackTop(0),
mStackMax(0),
mNamespaceList(NULL),
mLocals(NULL),
mArgumentBase(NULL),
mReader(NULL),
mGlobal(global)
{
uint32 i;
initOperatorTable();
mScopeChain = new ScopeChain(this, mWorld);
if (Object_Type == NULL) {
initBuiltins();
}
JSType *MathType = new JSType(this, &mWorld.identifiers[widenCString("Math")], Object_Type, Object_Type->mPrototypeObject, kNullValue);
JSObject *mathObj = MathType->newInstance(this).instance;
getGlobalObject()->defineVariable(this, Math_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Object_Type, JSValue(mathObj));
initMathObject(this, mathObj);
initDateObject(this);
Number_Type->defineVariable(this, widenCString("MAX_VALUE"), NULL, Property::ReadOnly | Property::DontDelete, Number_Type, JSValue(maxValue));
Number_Type->defineVariable(this, widenCString("MIN_VALUE"), NULL, Property::ReadOnly | Property::DontDelete, Number_Type, JSValue(minValue));
Number_Type->defineVariable(this, widenCString("NaN"), NULL, Property::ReadOnly | Property::DontDelete, Number_Type, JSValue(nan));
Number_Type->defineVariable(this, widenCString("POSITIVE_INFINITY"), NULL, Property::ReadOnly | Property::DontDelete, Number_Type, JSValue(positiveInfinity));
Number_Type->defineVariable(this, widenCString("NEGATIVE_INFINITY"), NULL, Property::ReadOnly | Property::DontDelete, Number_Type, JSValue(negativeInfinity));
initOperators();
struct Attribute_Init {
char *name;
uint32 trueFlags;
uint32 falseFlags;
} attribute_init[] =
{
// XXX these false flags are WAY NOT COMPLETE!!
//
{ "indexable", Property::Indexable, 0 },
{ "enumerable", Property::Enumerable, 0 },
{ "virtual", Property::Virtual, Property::Static | Property::Constructor },
{ "constructor", Property::Constructor, Property::Virtual },
{ "operator", Property::Operator, Property::Virtual | Property::Constructor },
{ "dynamic", Property::Dynamic, 0 },
{ "fixed", 0, Property::Dynamic },
{ "prototype", Property::Prototype, 0 },
{ "static", Property::Static, Property::Virtual },
{ "abstract", Property::Abstract, Property::Static },
{ "override", Property::Override, Property::Static },
{ "mayOverride", Property::MayOverride, Property::Static },
{ "true", Property::True, 0 },
{ "false", 0, Property::True },
{ "public", Property::Public, Property::Private },
{ "private", Property::Private, Property::Public },
{ "final", Property::Final, Property::Virtual | Property::Abstract },
{ "const", Property::Const, 0 },
};
for (i = 0; i < (sizeof(attribute_init) / sizeof(Attribute_Init)); i++) {
Attribute *attr = new Attribute(attribute_init[i].trueFlags, attribute_init[i].falseFlags);
getGlobalObject()->defineVariable(this, widenCString(attribute_init[i].name), (NamespaceList *)(NULL), Property::NoAttribute, Attribute_Type, JSValue(attr));
}
JSFunction *x = new JSFunction(this, ExtendAttribute_Invoke, Attribute_Type);
getGlobalObject()->defineVariable(this, Extend_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Attribute_Type, JSValue(x));
ProtoFunDef globalObjectFunctions[] = {
{ "eval", Object_Type, 1, GlobalObject_Eval },
{ "parseInt", Number_Type, 2, GlobalObject_ParseInt },
{ "parseFloat", Number_Type, 2, GlobalObject_ParseFloat },
{ "isNaN", Boolean_Type, 2, GlobalObject_isNaN },
{ "isFinite", Boolean_Type, 2, GlobalObject_isFinite },
{ "escape", String_Type, 1, GlobalObject_escape },
{ "unescape", String_Type, 1, GlobalObject_unescape },
};
for (i = 0; i < (sizeof(globalObjectFunctions) / sizeof(ProtoFunDef)); i++) {
x = new JSFunction(this, globalObjectFunctions[i].imp, globalObjectFunctions[i].result);
x->setParameterCounts(this, globalObjectFunctions[i].length, 0, 0, false);
x->setIsPrototype(true);
getGlobalObject()->defineVariable(this, widenCString(globalObjectFunctions[i].name), (NamespaceList *)(NULL), Property::NoAttribute, globalObjectFunctions[i].result, JSValue(x));
}
getGlobalObject()->defineVariable(this, Undefined_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kUndefinedValue);
getGlobalObject()->defineVariable(this, NaN_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kNaNValue);
getGlobalObject()->defineVariable(this, Infinity_StringAtom, (NamespaceList *)(NULL), Property::NoAttribute, Void_Type, kPositiveInfinity);
}
/*
See if the specified package is already loaded - return true
Throw an exception if the package is being loaded already
*/
bool Context::checkForPackage(const String &packageName)
{
// XXX linear search
for (PackageList::iterator pi = mPackages.begin(), end = mPackages.end(); (pi != end); pi++) {
if (PACKAGE_NAME(pi).compare(packageName) == 0) {
if (PACKAGE_STATUS(pi) == Package::OnItsWay)
reportError(Exception::referenceError, "Package circularity");
else
return true;
}
}
return false;
}
/*
Load the specified package from the file
*/
void Context::loadPackage(const String & /*packageName*/, const String &filename)
{
// XXX need some rules for search path
// XXX need to extract just the target package from the file
readEvalFile(filename);
}
void Context::reportError(Exception::Kind kind, char *message, size_t pos, const char *arg)
{
const char16 *lineBegin;
const char16 *lineEnd;
String x = widenCString(message);
if (arg) {
uint32 a = x.find(widenCString("{0}"));
x.replace(a, 3, widenCString(arg));
}
if (mReader) {
uint32 lineNum = mReader->posToLineNum(pos);
size_t linePos = mReader->getLine(lineNum, lineBegin, lineEnd);
ASSERT(lineBegin && lineEnd && linePos <= pos);
throw Exception(kind, x,
mReader->sourceLocation,
lineNum, pos - linePos, pos, lineBegin, lineEnd);
}
else {
if (mCurModule) {
Reader reader(mCurModule->mSource, mCurModule->mSourceLocation);
reader.fillLineStartsTable();
uint32 lineNum = reader.posToLineNum(pos);
size_t linePos = reader.getLine(lineNum, lineBegin, lineEnd);
ASSERT(lineBegin && lineEnd && linePos <= pos);
throw Exception(kind, x,
reader.sourceLocation,
lineNum, pos - linePos, pos, lineBegin, lineEnd);
}
}
throw Exception(kind, x);
}
// assumes mPC has been set inside the interpreter loop prior
// to dispatch to whatever routine invoked this error reporter
void Context::reportError(Exception::Kind kind, char *message, const char *arg)
{
size_t pos = 0;
if (mCurModule)
pos = mCurModule->getPositionForPC(toUInt32(mPC - mCurModule->mCodeBase));
reportError(kind, message, pos, arg);
}
void Context::reportError(Exception::Kind kind, char *message, const String& name)
{
std::string str(name.length(), char());
std::transform(name.begin(), name.end(), str.begin(), narrow);
reportError(kind, message, str.c_str() );
}
void Context::reportError(Exception::Kind kind, char *message, size_t pos, const String& name)
{
std::string str(name.length(), char());
std::transform(name.begin(), name.end(), str.begin(), narrow);
reportError(kind, message, pos, str.c_str());
}
Formatter& operator<<(Formatter& f, const JSValue& value)
{
switch (value.tag) {
case JSValue::f64_tag:
f << value.f64;
break;
case JSValue::object_tag:
printFormat(f, "Object @ 0x%08X\n", value.object);
f << *value.object;
break;
case JSValue::type_tag:
printFormat(f, "Type @ 0x%08X\n", value.type);
f << *value.type;
break;
case JSValue::boolean_tag:
f << ((value.boolean) ? "true" : "false");
break;
case JSValue::string_tag:
f << *value.string;
break;
case JSValue::undefined_tag:
f << "undefined";
break;
case JSValue::null_tag:
f << "null";
break;
case JSValue::function_tag:
if (!value.function->isNative()) {
FunctionName *fnName = value.function->getFunctionName();
if (fnName) {
StringFormatter s;
PrettyPrinter pp(s);
fnName->print(pp);
f << "function '" << s.getString() << "'\n" << *value.function->getByteCode();
}
else {
f << "function anonymous\n" << *value.function->getByteCode();
}
}
else
f << "function\n";
break;
default:
NOT_REACHED("Bad tag");
}
return f;
}
void JSType::printSlotsNStuff(Formatter& f) const
{
f << "var. count = " << mVariableCount << "\n";
f << "method count = " << (uint32)(mMethods.size()) << "\n";
uint32 index = 0;
for (MethodList::const_iterator i = mMethods.begin(), end = mMethods.end(); (i != end); i++) {
f << "[#" << index++ << "]";
if (*i == NULL)
f << "NULL\n";
else
if (!(*i)->isNative()) {
ByteCodeModule *m = (*i)->getByteCode();
if (m)
f << *m;
}
}
// if (mStatics)
// f << "Statics :\n" << *mStatics;
}
Formatter& operator<<(Formatter& f, const JSObject& obj)
{
obj.printProperties(f);
return f;
}
Formatter& operator<<(Formatter& f, const JSType& obj)
{
printFormat(f, "super @ 0x%08X\n", obj.mSuperType);
f << "properties\n";
obj.printProperties(f);
f << "slotsnstuff\n";
obj.printSlotsNStuff(f);
f << "done with type\n";
return f;
}
Formatter& operator<<(Formatter& f, const Access& slot)
{
switch (slot) {
case Read : f << "Read\n"; break;
case Write : f << "Write\n"; break;
}
return f;
}
Formatter& operator<<(Formatter& f, const Property& prop)
{
switch (prop.mFlag) {
case ValuePointer :
{
JSValue v = *prop.mData.vp;
f << "ValuePointer --> ";
if (v.isObject())
printFormat(f, "Object @ 0x%08X\n", v.object);
else
if (v.isType())
printFormat(f, "Type @ 0x%08X\n", v.type);
else
if (v.isFunction())
printFormat(f, "Function @ 0x%08X\n", v.function);
else
f << v << "\n";
}
break;
case FunctionPair : f << "FunctionPair\n"; break;
case IndexPair : f << "IndexPair\n"; break;
case Slot : f << "Slot\n"; break;
case Constructor : f << "Constructor\n"; break;
case Method : f << "Method\n"; break;
}
return f;
}
Formatter& operator<<(Formatter& f, const JSInstance& obj)
{
for (PropertyMap::const_iterator i = obj.mProperties.begin(), end = obj.mProperties.end(); (i != end); i++) {
const Property *prop = PROPERTY(i);
f << "[" << PROPERTY_NAME(i) << "] ";
switch (prop->mFlag) {
case ValuePointer : f << "ValuePointer --> " << *prop->mData.vp; break;
case FunctionPair : f << "FunctionPair\n"; break;
case IndexPair : f << "IndexPair\n"; break;
case Slot : f << "Slot #" << prop->mData.index
<< " --> " << obj.mInstanceValues[prop->mData.index] << "\n"; break;
case Method : f << "Method #" << prop->mData.index << "\n"; break;
case Constructor : f << "Constructor #" << prop->mData.index << "\n"; break;
}
}
return f;
}
}
}