зеркало из https://github.com/mozilla/pjs.git
2028 строки
77 KiB
C++
2028 строки
77 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.
|
|
*/
|
|
|
|
#ifndef js2runtime_h___
|
|
#define js2runtime_h___
|
|
|
|
#ifdef _WIN32
|
|
// Turn off warnings about identifiers too long in browser information
|
|
#pragma warning(disable: 4786)
|
|
#endif
|
|
|
|
#include <vector>
|
|
#include <stack>
|
|
#include <map>
|
|
|
|
#include "systemtypes.h"
|
|
#include "strings.h"
|
|
#include "formatter.h"
|
|
#include "property.h"
|
|
|
|
#include "tracer.h"
|
|
#include "collector.h"
|
|
|
|
namespace JavaScript {
|
|
namespace JS2Runtime {
|
|
|
|
class ByteCodeGen;
|
|
class ByteCodeModule;
|
|
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1])
|
|
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0])
|
|
#else
|
|
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0])
|
|
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1])
|
|
#endif
|
|
|
|
#define JSDOUBLE_HI32_SIGNBIT 0x80000000
|
|
#define JSDOUBLE_HI32_EXPMASK 0x7ff00000
|
|
#define JSDOUBLE_HI32_MANTMASK 0x000fffff
|
|
|
|
#define JSDOUBLE_IS_NaN(x) \
|
|
((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \
|
|
(JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK)))
|
|
|
|
#define JSDOUBLE_IS_INFINITE(x) \
|
|
((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \
|
|
!JSDOUBLE_LO32(x))
|
|
|
|
#define JSDOUBLE_IS_FINITE(x) \
|
|
((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK)
|
|
|
|
#define JSDOUBLE_IS_POSZERO(d) (JSDOUBLE_HI32(d) == 0 && JSDOUBLE_LO32(d) == 0)
|
|
|
|
#define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \
|
|
JSDOUBLE_LO32(d) == 0)
|
|
|
|
|
|
static const double two32minus1 = 4294967295.0;
|
|
static const double two32 = 4294967296.0;
|
|
static const double two16 = 65536.0;
|
|
static const double two31 = 2147483648.0;
|
|
|
|
#define NotABanana ((uint32)(-1))
|
|
|
|
class JSObject;
|
|
class JSFunction;
|
|
class JSType;
|
|
class JSArrayType;
|
|
class JSStringType;
|
|
class Context;
|
|
|
|
|
|
extern JSType *Object_Type; // the base type for all types
|
|
extern JSType *Number_Type;
|
|
extern JSType *Integer_Type;
|
|
extern JSStringType *String_Type;
|
|
extern JSType *Type_Type; // the type for variables that are types
|
|
// (e.g. the property 'C' from class C...
|
|
// has this type).
|
|
extern JSType *Boolean_Type;
|
|
extern JSType *Void_Type;
|
|
extern JSType *Null_Type;
|
|
extern JSArrayType *Array_Type;
|
|
extern JSType *Unit_Type;
|
|
extern JSType *Function_Type;
|
|
|
|
extern JSType *Attribute_Type; // used to define 'prototype' 'static' etc & Namespace values
|
|
extern JSType *Package_Type;
|
|
extern JSType *NamedArgument_Type;
|
|
|
|
extern JSType *Date_Type;
|
|
extern JSType *RegExp_Type;
|
|
|
|
|
|
const String *numberToString(float64 number);
|
|
float64 stringToNumber(const String *string);
|
|
|
|
|
|
class JSValue {
|
|
public:
|
|
union {
|
|
float64 f64;
|
|
JSObject *object;
|
|
JSFunction *function;
|
|
const String *string;
|
|
JSType *type;
|
|
bool boolean;
|
|
};
|
|
|
|
typedef enum {
|
|
undefined_tag,
|
|
f64_tag,
|
|
object_tag,
|
|
function_tag,
|
|
type_tag,
|
|
boolean_tag,
|
|
string_tag,
|
|
null_tag
|
|
} Tag;
|
|
Tag tag;
|
|
|
|
JSValue() : f64(0.0), tag(undefined_tag) {}
|
|
explicit JSValue(float64 f64) : f64(f64), tag(f64_tag) {}
|
|
explicit JSValue(JSObject *object) : object(object), tag(object_tag) { ASSERT(object); }
|
|
explicit JSValue(JSFunction *function) : function(function), tag(function_tag) { ASSERT(function); }
|
|
explicit JSValue(JSType *type) : type(type), tag(type_tag) { ASSERT(type); }
|
|
explicit JSValue(const String *string) : string(string), tag(string_tag) { ASSERT(string); }
|
|
explicit JSValue(bool boolean) : boolean(boolean), tag(boolean_tag) {}
|
|
explicit JSValue(Tag tag) : tag(tag) {}
|
|
|
|
float64& operator=(float64 f64) { return (tag = f64_tag, this->f64 = f64); }
|
|
JSObject*& operator=(JSObject* object) { return (tag = object_tag, this->object = object); }
|
|
JSType*& operator=(JSType* type) { return (tag = type_tag, this->type = type); }
|
|
JSFunction*& operator=(JSFunction* function) { return (tag = function_tag, this->function = function); }
|
|
bool& operator=(bool boolean) { return (tag = boolean_tag, this->boolean = boolean); }
|
|
|
|
bool isObject() const { return (tag == object_tag); }
|
|
bool isNumber() const { return (tag == f64_tag); }
|
|
bool isBool() const { return (tag == boolean_tag); }
|
|
bool isType() const { return (tag == type_tag); }
|
|
bool isFunction() const { return (tag == function_tag); }
|
|
bool isString() const { return (tag == string_tag); }
|
|
bool isPrimitive() const { return (tag != object_tag) && (tag != type_tag) && (tag != function_tag); }
|
|
|
|
bool isUndefined() const { return (tag == undefined_tag); }
|
|
bool isNull() const { return (tag == null_tag); }
|
|
bool isNaN() const { ASSERT(isNumber()); return JSDOUBLE_IS_NaN(f64); }
|
|
bool isNegativeInfinity() const { ASSERT(isNumber()); return (f64 < 0) && JSDOUBLE_IS_INFINITE(f64); }
|
|
bool isPositiveInfinity() const { ASSERT(isNumber()); return (f64 > 0) && JSDOUBLE_IS_INFINITE(f64); }
|
|
bool isNegativeZero() const { ASSERT(isNumber()); return JSDOUBLE_IS_NEGZERO(f64); }
|
|
bool isPositiveZero() const { ASSERT(isNumber()); return JSDOUBLE_IS_POSZERO(f64); }
|
|
|
|
bool isTrue() const { ASSERT(isBool()); return boolean; }
|
|
bool isFalse() const { ASSERT(isBool()); return !boolean; }
|
|
|
|
JSType *getType() const;
|
|
|
|
JSValue toString(Context *cx) const { return (isString() ? *this : valueToString(cx, *this)); }
|
|
JSValue toNumber(Context *cx) const { return (isNumber() ? *this : valueToNumber(cx, *this)); }
|
|
JSValue toInteger(Context *cx) const { return valueToInteger(cx, *this); }
|
|
JSValue toUInt32(Context *cx) const { return valueToUInt32(cx, *this); }
|
|
JSValue toUInt16(Context *cx) const { return valueToUInt16(cx, *this); }
|
|
JSValue toInt32(Context *cx) const { return valueToInt32(cx, *this); }
|
|
JSValue toObject(Context *cx) const { return ((isObject() || isType() || isFunction()) ?
|
|
*this : valueToObject(cx, *this)); }
|
|
JSValue toBoolean(Context *cx) const { return (isBool() ? *this : valueToBoolean(cx, *this)); }
|
|
|
|
float64 getNumberValue() const;
|
|
const String *getStringValue() const;
|
|
bool getBoolValue() const;
|
|
|
|
/* These are for use in 'toPrimitive' calls */
|
|
enum Hint {
|
|
NumberHint, StringHint, NoHint
|
|
};
|
|
JSValue toPrimitive(Context *cx, Hint hint = NoHint) const;
|
|
|
|
static JSValue valueToNumber(Context *cx, const JSValue& value);
|
|
static JSValue valueToInteger(Context *cx, const JSValue& value);
|
|
static JSValue valueToString(Context *cx, const JSValue& value);
|
|
static JSValue valueToObject(Context *cx, const JSValue& value);
|
|
static JSValue valueToUInt32(Context *cx, const JSValue& value);
|
|
static JSValue valueToUInt16(Context *cx, const JSValue& value);
|
|
static JSValue valueToInt32(Context *cx, const JSValue& value);
|
|
static JSValue valueToBoolean(Context *cx, const JSValue& value);
|
|
|
|
|
|
static float64 float64ToInteger(float64 d);
|
|
static int32 float64ToInt32(float64 d);
|
|
static uint32 float64ToUInt32(float64 d);
|
|
|
|
int operator==(const JSValue& value) const;
|
|
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
switch (tag) {
|
|
case object_tag: object = (JSObject*) collector->copy(object); break;
|
|
case function_tag: function = (JSFunction*) collector->copy(function); break;
|
|
case type_tag: type = (JSType*) collector->copy(type); break;
|
|
default: break;
|
|
}
|
|
return sizeof(JSValue);
|
|
}
|
|
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static Collector::InstanceOwner<JSValue> owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSValue", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSValue", t); STD::free(t); }
|
|
#endif
|
|
|
|
};
|
|
Formatter& operator<<(Formatter& f, const JSValue& value);
|
|
|
|
extern JSValue kUndefinedValue;
|
|
extern JSValue kNaNValue;
|
|
extern JSValue kTrueValue;
|
|
extern JSValue kFalseValue;
|
|
extern JSValue kNullValue;
|
|
extern JSValue kNegativeZero;
|
|
extern JSValue kPositiveZero;
|
|
extern JSValue kNegativeInfinity;
|
|
extern JSValue kPositiveInfinity;
|
|
|
|
|
|
|
|
typedef enum { Read, Write } Access;
|
|
Formatter& operator<<(Formatter& f, const Access& acc);
|
|
|
|
|
|
typedef enum {
|
|
None,
|
|
Posate,
|
|
Negate,
|
|
Complement,
|
|
Increment,
|
|
Decrement,
|
|
Call,
|
|
New,
|
|
Index,
|
|
IndexEqual,
|
|
DeleteIndex,
|
|
Plus,
|
|
Minus,
|
|
Multiply,
|
|
Divide,
|
|
Remainder,
|
|
ShiftLeft,
|
|
ShiftRight,
|
|
UShiftRight,
|
|
Less,
|
|
LessEqual,
|
|
In,
|
|
Equal,
|
|
SpittingImage,
|
|
BitAnd,
|
|
BitXor,
|
|
BitOr,
|
|
OperatorCount
|
|
} Operator;
|
|
|
|
/*
|
|
XXX ...couldn't get this to work...
|
|
|
|
class OperatorEntry {
|
|
public:
|
|
OperatorEntry(const String &, JS2Runtime::Operator) {}
|
|
OperatorEntry& operator=(const OperatorEntry &) { return *this; }
|
|
|
|
const String mName;
|
|
JS2Runtime::Operator op;
|
|
|
|
bool operator == (const String &name) { return name == mName; }
|
|
};
|
|
|
|
typedef HashTable<OperatorEntry, const String> OperatorHashTable;
|
|
extern OperatorHashTable operatorHashTable;
|
|
*/
|
|
|
|
typedef std::map<const String, JS2Runtime::Operator> OperatorMap;
|
|
extern OperatorMap operatorMap;
|
|
|
|
class Reference {
|
|
public:
|
|
Reference(JSType *type, PropertyAttribute attr) : mType(type), mAttr(attr) { }
|
|
JSType *mType;
|
|
PropertyAttribute mAttr;
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("Reference", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("Reference", t); STD::free(t); }
|
|
#endif
|
|
|
|
// used by the invocation sequence to calculate
|
|
// the stack depth and specify the 'this' flag
|
|
virtual bool needsThis() { return false; }
|
|
|
|
// issued as soon as the reference is generated to
|
|
// establish any required base object.
|
|
virtual void emitImplicitLoad(ByteCodeGen *) { }
|
|
|
|
// acquires the invokable object
|
|
virtual void emitInvokeSequence(ByteCodeGen *bcg) { emitCodeSequence(bcg); }
|
|
|
|
// issued before the rvalue is evaluated.
|
|
// returns true if it pushes anything on the stack
|
|
virtual bool emitPreAssignment(ByteCodeGen *) { return false; }
|
|
|
|
virtual void emitCodeSequence(ByteCodeGen *)
|
|
{ throw Exception(Exception::internalError, "gen code for base ref"); }
|
|
|
|
// returns the amount of stack used by the reference
|
|
virtual uint16 baseExpressionDepth() { return 0; }
|
|
|
|
// generate code sequence for typeof operator
|
|
virtual void emitTypeOf(ByteCodeGen *bcg);
|
|
|
|
// generate code sequence for delete operator
|
|
virtual void emitDelete(ByteCodeGen *bcg);
|
|
|
|
bool isConst() { return ((mAttr & Property::Const) == Property::Const); }
|
|
};
|
|
|
|
// a getter/setter function from an activation
|
|
// the function is known directly
|
|
class AccessorReference : public Reference {
|
|
public:
|
|
AccessorReference(JSFunction *f, PropertyAttribute attr);
|
|
JSFunction *mFunction;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
};
|
|
// a simple local variable reference - it's a slot
|
|
// in the current activation
|
|
class LocalVarReference : public Reference {
|
|
public:
|
|
LocalVarReference(uint32 index, Access acc, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mAccess(acc), mIndex(index) { }
|
|
Access mAccess;
|
|
uint32 mIndex;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
};
|
|
// a local variable 'n' activations up the
|
|
// execution stack
|
|
class ClosureVarReference : public LocalVarReference {
|
|
public:
|
|
ClosureVarReference(uint32 depth, uint32 index, Access acc, JSType *type, PropertyAttribute attr)
|
|
: LocalVarReference(index, acc, type, attr), mDepth(depth) { }
|
|
uint32 mDepth;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
};
|
|
// a member field in an instance
|
|
class FieldReference : public Reference {
|
|
public:
|
|
FieldReference(uint32 index, Access acc, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mAccess(acc), mIndex(index) { }
|
|
Access mAccess;
|
|
uint32 mIndex;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
void emitImplicitLoad(ByteCodeGen *bcg);
|
|
uint16 baseExpressionDepth() { return 1; }
|
|
};
|
|
// a static field
|
|
class StaticFieldReference : public Reference {
|
|
public:
|
|
StaticFieldReference(const String& name, Access acc, JSType *baseClass, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mAccess(acc), mName(name), mClass(baseClass) { }
|
|
Access mAccess;
|
|
const String& mName;
|
|
JSType *mClass;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
void emitInvokeSequence(ByteCodeGen *bcg);
|
|
void emitImplicitLoad(ByteCodeGen *bcg);
|
|
uint16 baseExpressionDepth() { return 1; }
|
|
void emitDelete(ByteCodeGen *bcg);
|
|
};
|
|
// a member function in a vtable
|
|
class MethodReference : public Reference {
|
|
public:
|
|
MethodReference(uint32 index, JSType *baseClass, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mIndex(index), mClass(baseClass) { }
|
|
uint32 mIndex;
|
|
JSType *mClass;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
virtual bool needsThis() { return true; }
|
|
virtual void emitImplicitLoad(ByteCodeGen *bcg);
|
|
virtual uint16 baseExpressionDepth() { return 1; }
|
|
void emitInvokeSequence(ByteCodeGen *bcg);
|
|
};
|
|
class GetterMethodReference : public MethodReference {
|
|
public:
|
|
GetterMethodReference(uint32 index, JSType *baseClass, JSType *type, PropertyAttribute attr)
|
|
: MethodReference(index, baseClass, type, attr) { }
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
};
|
|
class SetterMethodReference : public MethodReference {
|
|
public:
|
|
SetterMethodReference(uint32 index, JSType *baseClass, JSType *type, PropertyAttribute attr)
|
|
: MethodReference(index, baseClass, type, attr) { }
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
bool emitPreAssignment(ByteCodeGen *bcg);
|
|
};
|
|
|
|
// a function
|
|
class FunctionReference : public Reference {
|
|
public:
|
|
FunctionReference(JSFunction *f, PropertyAttribute attr);
|
|
JSFunction *mFunction;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
};
|
|
// a getter function
|
|
class GetterFunctionReference : public Reference {
|
|
public:
|
|
GetterFunctionReference(JSFunction *f, PropertyAttribute attr);
|
|
JSFunction *mFunction;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
};
|
|
// a setter function
|
|
class SetterFunctionReference : public Reference {
|
|
public:
|
|
SetterFunctionReference(JSFunction *f, JSType *type, PropertyAttribute attr);
|
|
JSFunction *mFunction;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
void emitImplicitLoad(ByteCodeGen *bcg);
|
|
};
|
|
// Either an existing value property (dynamic) or
|
|
// the "we don't know any field by that name".
|
|
class PropertyReference : public Reference {
|
|
public:
|
|
PropertyReference(const String& name, NamespaceList *nameSpace, Access acc, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mAccess(acc), mName(name), mNameSpace(nameSpace) { }
|
|
Access mAccess;
|
|
const String& mName;
|
|
NamespaceList *mNameSpace;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
void emitInvokeSequence(ByteCodeGen *bcg);
|
|
uint16 baseExpressionDepth() { return 1; }
|
|
bool needsThis() { return true; }
|
|
void emitImplicitLoad(ByteCodeGen *bcg);
|
|
void emitDelete(ByteCodeGen *bcg);
|
|
};
|
|
// a parameter slot (they can't have getter/setter, right?)
|
|
class ParameterReference : public Reference {
|
|
public:
|
|
ParameterReference(uint32 index, Access acc, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mAccess(acc), mIndex(index) { }
|
|
Access mAccess;
|
|
uint32 mIndex;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
};
|
|
|
|
// the generic "we don't know anybody by that name" - not bound to a specific object
|
|
// so it's a scope chain lookup at runtime
|
|
class NameReference : public Reference {
|
|
public:
|
|
NameReference(const String& name, NamespaceList *nameSpace, Access acc)
|
|
: Reference(Object_Type, 0), mAccess(acc), mName(name), mNameSpace(nameSpace) { }
|
|
NameReference(const String& name, NamespaceList *nameSpace, Access acc, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mAccess(acc), mName(name), mNameSpace(nameSpace) { }
|
|
Access mAccess;
|
|
const String& mName;
|
|
NamespaceList *mNameSpace;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
void emitTypeOf(ByteCodeGen *bcg);
|
|
void emitDelete(ByteCodeGen *bcg);
|
|
};
|
|
|
|
class ElementReference : public Reference {
|
|
public:
|
|
ElementReference(Access acc, uint16 depth)
|
|
: Reference(Object_Type, 0), mAccess(acc), mDepth(depth) { }
|
|
Access mAccess;
|
|
uint16 mDepth;
|
|
void emitCodeSequence(ByteCodeGen *bcg);
|
|
uint16 baseExpressionDepth() { return (uint16)(mDepth + 1); }
|
|
void emitDelete(ByteCodeGen *bcg);
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JSObject {
|
|
public:
|
|
// The generic Javascript object. Every JS2 object is one of these
|
|
JSObject(JSType *type = Object_Type) : mType(type), mPrivate(NULL), mPrototype(NULL) { }
|
|
|
|
virtual ~JSObject() { } // keeping gcc happy
|
|
|
|
// every object has a type
|
|
JSType *mType;
|
|
|
|
// the property data is kept (or referenced from) here
|
|
PropertyMap mProperties;
|
|
|
|
// Every JSObject has a private part
|
|
void *mPrivate;
|
|
|
|
// Every JSObject (except the Ur-object) has a prototype
|
|
JSObject *mPrototype;
|
|
|
|
JSType *getType() const { return mType; }
|
|
|
|
virtual bool isDynamic() { return true; }
|
|
|
|
|
|
// find a property by the given name, and then check to see if there's any
|
|
// overlap between the supplied attribute list and the property's list.
|
|
// ***** REWRITE ME -- matching attribute lists for inclusion is a bad idea.
|
|
virtual PropertyIterator findNamespacedProperty(const String &name, NamespaceList *names);
|
|
|
|
virtual bool deleteProperty(Context *cx, const String &name, NamespaceList *names);
|
|
|
|
// see if the property exists by a specific kind of access
|
|
virtual bool hasOwnProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p);
|
|
|
|
virtual bool hasProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p);
|
|
|
|
virtual JSValue getPropertyValue(PropertyIterator &i);
|
|
|
|
virtual void getProperty(Context *cx, const String &name, NamespaceList *names);
|
|
|
|
virtual void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v);
|
|
|
|
|
|
|
|
// add a property
|
|
virtual Property *defineVariable(Context *cx, const String &name, AttributeStmtNode *attr, JSType *type);
|
|
virtual Property *defineVariable(Context *cx, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type);
|
|
|
|
// add a property/value into the map
|
|
// - assumes the map doesn't already have this property
|
|
Property *insertNewProperty(const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, const JSValue &v);
|
|
|
|
virtual Property *defineStaticVariable(Context *cx, const String &name, AttributeStmtNode *attr, JSType *type)
|
|
{
|
|
return JSObject::defineVariable(cx, name, attr, type);
|
|
// XXX or error? (Note that this implementation is invoked by JSType::defineStaticXXXX
|
|
// - the question is, is static var X (etc) at global scope an error?
|
|
}
|
|
// add a method property
|
|
virtual void defineMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
defineVariable(cx, name, attr, Function_Type, JSValue(f));
|
|
}
|
|
virtual void defineStaticMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject::defineVariable(cx, name, attr, Function_Type, JSValue(f)); // XXX or error?
|
|
}
|
|
virtual void defineConstructor(Context *cx, const String& name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject::defineVariable(cx, name, attr, Function_Type, JSValue(f)); // XXX or error?
|
|
}
|
|
virtual void defineStaticGetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject::defineGetterMethod(cx, name, attr, f); // XXX or error?
|
|
}
|
|
virtual void defineStaticSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject::defineSetterMethod(cx, name, attr, f); // XXX or error?
|
|
}
|
|
virtual void defineGetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f);
|
|
virtual void defineSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f);
|
|
|
|
virtual Property *defineVariable(Context *cx, const String &name, AttributeStmtNode *attr, JSType *type, const JSValue v);
|
|
virtual Property *defineVariable(Context *cx, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, const JSValue v);
|
|
virtual Property *defineAlias(Context *cx, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, JSValue *vp);
|
|
|
|
virtual Reference *genReference(Context *cx, bool hasBase, const String& name, NamespaceList *names, Access acc, uint32 depth);
|
|
|
|
virtual JSType *topClass() { return NULL; }
|
|
virtual bool isNestedFunction() { return false; }
|
|
virtual JSFunction *getContainerFunction() { return NULL; }
|
|
|
|
virtual bool hasLocalVars() { return false; }
|
|
virtual uint32 localVarCount() { return 0; }
|
|
|
|
virtual void defineTempVariable(Context *cx, Reference *&readRef, Reference *&writeRef, JSType *type);
|
|
|
|
virtual JSValue getSlotValue(Context * /*cx*/, uint32 /*slotIndex*/) { ASSERT(false); return kUndefinedValue; }
|
|
virtual void setSlotValue(Context * /*cx*/, uint32 /*slotIndex*/, JSValue & /*v*/) { ASSERT(false); }
|
|
|
|
// debug only
|
|
void printProperties(Formatter &f) const
|
|
{
|
|
for (PropertyMap::const_iterator i = mProperties.begin(), end = mProperties.end(); (i != end); i++)
|
|
{
|
|
f << "[" << PROPERTY_NAME(i) << "] " << *PROPERTY(i);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
typedef Collector::InstanceOwner<JSObject> JSObjectOwner;
|
|
friend class Collector::InstanceOwner<JSObject>;
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
mType = (JSType*) collector->copy(mType);
|
|
// enumerate property map elements.
|
|
// what is mPrivate?
|
|
mPrivate = collector->copy(mPrivate);
|
|
mPrototype = (JSObject*) collector->copy(mPrototype);
|
|
return sizeof(JSObject);
|
|
}
|
|
|
|
public:
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static JSObjectOwner owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSObject", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSObject", t); STD::free(t); }
|
|
#endif
|
|
|
|
static uint32 tempVarCount;
|
|
};
|
|
|
|
Formatter& operator<<(Formatter& f, const JSObject& obj);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JSInstance : public JSObject {
|
|
public:
|
|
|
|
JSInstance(Context *cx, JSType *type)
|
|
: JSObject(type), mInstanceValues(NULL) { if (type) initInstance(cx, type); }
|
|
|
|
virtual ~JSInstance() { } // keeping gcc happy
|
|
|
|
void initInstance(Context *cx, JSType *type);
|
|
|
|
void getProperty(Context *cx, const String &name, NamespaceList *names);
|
|
void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v);
|
|
|
|
JSValue getField(uint32 index)
|
|
{
|
|
return mInstanceValues[index];
|
|
}
|
|
|
|
void setField(uint32 index, JSValue v)
|
|
{
|
|
mInstanceValues[index] = v;
|
|
}
|
|
|
|
virtual bool isDynamic();
|
|
|
|
|
|
JSValue *mInstanceValues;
|
|
|
|
protected:
|
|
typedef Collector::InstanceOwner<JSInstance> JSInstanceOwner;
|
|
friend class Collector::InstanceOwner<JSInstance>;
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
JSObject::scan(collector);
|
|
// FIXME: need some kind of array operator new[] (gc) thing.
|
|
// this will have to use an extra word to keep track of the
|
|
// element count.
|
|
mInstanceValues = (JSValue*) collector->copy(mInstanceValues);
|
|
return sizeof(JSInstance);
|
|
}
|
|
|
|
public:
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static JSInstanceOwner owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSInstance", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSInstance", t); STD::free(t); }
|
|
#endif
|
|
};
|
|
Formatter& operator<<(Formatter& f, const JSInstance& obj);
|
|
|
|
|
|
|
|
typedef std::vector<JSFunction *> MethodList;
|
|
|
|
class ScopeChain;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class JSType : public JSObject {
|
|
public:
|
|
JSType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL);
|
|
JSType(JSType *xClass); // used for constructing the static component type
|
|
|
|
virtual ~JSType() { } // keeping gcc happy
|
|
|
|
void setSuperType(JSType *super);
|
|
|
|
void setStaticInitializer(Context *cx, JSFunction *f);
|
|
void setInstanceInitializer(Context *cx, JSFunction *f);
|
|
|
|
virtual JSType *topClass() { return this; }
|
|
|
|
|
|
// construct a new (empty) instance of this class
|
|
virtual JSInstance *newInstance(Context *cx);
|
|
|
|
|
|
Property *defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type);
|
|
|
|
|
|
// XXX
|
|
// XXX why doesn't the virtual function in JSObject get found?
|
|
// XXX
|
|
Property *defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type, JSValue v)
|
|
{
|
|
return JSObject::defineVariable(cx, name, attr, type, v);
|
|
}
|
|
Property *defineVariable(Context *cx, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, const JSValue v)
|
|
{
|
|
return JSObject::defineVariable(cx, name, names, attrFlags, type, v);
|
|
}
|
|
|
|
|
|
|
|
|
|
void defineMethod(Context *cx, const String& name, AttributeStmtNode *attr, JSFunction *f);
|
|
|
|
void defineGetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f);
|
|
|
|
void defineSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f);
|
|
|
|
void defineUnaryOperator(Operator which, JSFunction *f)
|
|
{
|
|
mUnaryOperators[which] = f;
|
|
}
|
|
|
|
JSFunction *getUnaryOperator(Operator which)
|
|
{
|
|
return mUnaryOperators[which]; // XXX Umm, aren't these also getting inherited?
|
|
}
|
|
|
|
void setDefaultConstructor(Context * /*cx*/, JSFunction *f)
|
|
{
|
|
mDefaultConstructor = f;
|
|
}
|
|
|
|
void addMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f);
|
|
void addStaticMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f);
|
|
|
|
// return true if 'other' is on the chain of supertypes
|
|
bool derivesFrom(JSType *other);
|
|
|
|
virtual void getProperty(Context *cx, const String &name, NamespaceList *names);
|
|
virtual void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v);
|
|
|
|
virtual JSValue getPropertyValue(PropertyIterator &i);
|
|
|
|
virtual bool hasProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p);
|
|
|
|
virtual Reference *genReference(Context *cx, bool hasBase, const String& name, NamespaceList *names, Access acc, uint32 depth);
|
|
|
|
JSFunction *getDefaultConstructor() { return mDefaultConstructor; }
|
|
JSFunction *getTypeCastFunction() { return mTypeCast; }
|
|
|
|
JSValue getUninitializedValue() { return mUninitializedValue; }
|
|
|
|
// assumes that the super types have been completed already
|
|
void completeClass(Context *cx, ScopeChain *scopeChain);
|
|
|
|
virtual bool isDynamic() { return mIsDynamic; }
|
|
|
|
JSType *mSuperType; // NULL implies that this is the base Object
|
|
|
|
uint32 mVariableCount;
|
|
JSFunction *mInstanceInitializer;
|
|
JSFunction *mDefaultConstructor;
|
|
JSFunction *mTypeCast;
|
|
|
|
// the 'vtable'
|
|
MethodList mMethods;
|
|
|
|
const StringAtom *mClassName;
|
|
const StringAtom *mPrivateNamespace;
|
|
|
|
JSFunction *mUnaryOperators[OperatorCount]; // XXX too wasteful
|
|
|
|
bool mIsDynamic;
|
|
JSValue mUninitializedValue; // the value for uninitialized vars
|
|
|
|
JSObject *mPrototypeObject; // becomes the prototype for any instance
|
|
|
|
|
|
void printSlotsNStuff(Formatter& f) const;
|
|
|
|
protected:
|
|
typedef Collector::InstanceOwner<JSType> JSTypeOwner;
|
|
friend class Collector::InstanceOwner<JSType>;
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
JSObject::scan(collector);
|
|
mSuperType = (JSType*) collector->copy(mSuperType);
|
|
mInstanceInitializer = (JSFunction*) collector->copy(mInstanceInitializer);
|
|
mDefaultConstructor = (JSFunction*) collector->copy(mDefaultConstructor);
|
|
mTypeCast = (JSFunction*) collector->copy(mTypeCast);
|
|
// scan mMethods.
|
|
// scan mClassName.
|
|
// scan mPrivateNamespace.
|
|
uint32 i;
|
|
for (i = 0; i < OperatorCount; ++i)
|
|
mUnaryOperators[i] = (JSFunction*) collector->copy(mUnaryOperators[i]);
|
|
// scan mUninitializedValue.
|
|
mPrototypeObject = (JSObject*) collector->copy(mPrototypeObject);
|
|
return sizeof(JSType);
|
|
}
|
|
|
|
public:
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static JSTypeOwner owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSType", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSType", t); STD::free(t); }
|
|
#endif
|
|
};
|
|
|
|
Formatter& operator<<(Formatter& f, const JSType& obj);
|
|
|
|
class JSArrayInstance : public JSInstance {
|
|
public:
|
|
JSArrayInstance(Context *cx, JSType * /*type*/) : JSInstance(cx, NULL), mLength(0) { mType = (JSType *)Array_Type; mPrototype = Object_Type->mPrototypeObject; }
|
|
virtual ~JSArrayInstance() { } // keeping gcc happy
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSArrayInstance", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSArrayInstance", t); STD::free(t); }
|
|
#endif
|
|
|
|
// XXX maybe could have implemented length as a getter/setter pair?
|
|
void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v);
|
|
void getProperty(Context *cx, const String &name, NamespaceList *names);
|
|
|
|
bool hasOwnProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p);
|
|
bool deleteProperty(Context *cx, const String &name, NamespaceList *names);
|
|
|
|
uint32 mLength;
|
|
|
|
};
|
|
|
|
class JSArrayType : public JSType {
|
|
public:
|
|
JSArrayType(Context *cx, JSType *elementType, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL)
|
|
: JSType(cx, name, super, protoObj, typeProto), mElementType(elementType)
|
|
{
|
|
}
|
|
virtual ~JSArrayType() { } // keeping gcc happy
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSArrayType", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSArrayType", t); STD::free(t); }
|
|
#endif
|
|
|
|
JSInstance *newInstance(Context *cx);
|
|
|
|
JSType *mElementType;
|
|
|
|
};
|
|
|
|
class JSStringInstance : public JSInstance {
|
|
public:
|
|
JSStringInstance(Context *cx, JSType * /*type*/) : JSInstance(cx, NULL), mLength(0) { mType = (JSType *)String_Type; }
|
|
virtual ~JSStringInstance() { } // keeping gcc happy
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSStringInstance", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSStringInstance", t); STD::free(t); }
|
|
#endif
|
|
|
|
void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v);
|
|
void getProperty(Context *cx, const String &name, NamespaceList *names);
|
|
bool hasOwnProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p);
|
|
bool deleteProperty(Context *cx, const String &name, NamespaceList *names);
|
|
|
|
|
|
uint32 mLength;
|
|
|
|
};
|
|
|
|
class JSStringType : public JSType {
|
|
public:
|
|
JSStringType(Context *cx, const StringAtom *name, JSType *super, JSObject *protoObj = NULL, JSObject *typeProto = NULL)
|
|
: JSType(cx, name, super, protoObj, typeProto)
|
|
{
|
|
}
|
|
virtual ~JSStringType() { } // keeping gcc happy
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSStringType", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSStringType", t); STD::free(t); }
|
|
#endif
|
|
|
|
JSInstance *newInstance(Context *cx);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// captures the Parameter names scope
|
|
// it's a JSType simply because it's also a thing that
|
|
// maps from names to slots.
|
|
class ParameterBarrel : public JSType {
|
|
public:
|
|
|
|
ParameterBarrel() : JSType(NULL)
|
|
{
|
|
}
|
|
virtual ~ParameterBarrel() { } // keeping gcc happy
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("ParameterBarrel", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("ParameterBarrel", t); STD::free(t); }
|
|
#endif
|
|
|
|
Property *defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type);
|
|
Reference *genReference(Context *cx, bool hasBase, const String& name, NamespaceList *names, Access acc, uint32 depth);
|
|
|
|
JSValue getSlotValue(Context *cx, uint32 slotIndex);
|
|
void setSlotValue(Context *cx, uint32 slotIndex, JSValue &v);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// an Activation has two jobs:
|
|
// 1. At compile time it handles the function/method being compiled and collects
|
|
// the local vars/consts being defined in that function.
|
|
// 2. At runtime it is the container for the values of those local vars
|
|
// (although it's only constructed as such when the function
|
|
// either calls another function - so the activation represents
|
|
// the saved state, or when a closure object is constructed)
|
|
|
|
class Activation : public JSType {
|
|
public:
|
|
|
|
Activation()
|
|
: JSType(NULL),
|
|
mLocals(NULL),
|
|
mStack(NULL),
|
|
mStackTop(0),
|
|
mPC(0),
|
|
mModule(NULL),
|
|
mContainer(NULL),
|
|
mNamespaceList(NULL)
|
|
{}
|
|
|
|
Activation(JSValue *locals,
|
|
JSValue *stack, uint32 stackTop,
|
|
ScopeChain *scopeChain,
|
|
JSValue *argBase, JSValue curThis,
|
|
uint8 *pc,
|
|
ByteCodeModule *module,
|
|
NamespaceList *namespaceList )
|
|
: JSType(NULL),
|
|
mLocals(locals),
|
|
mStack(stack),
|
|
mStackTop(stackTop),
|
|
mScopeChain(scopeChain),
|
|
mArgumentBase(argBase),
|
|
mThis(curThis),
|
|
mPC(pc),
|
|
mModule(module),
|
|
mContainer(NULL),
|
|
mNamespaceList(namespaceList)
|
|
{}
|
|
|
|
virtual ~Activation() { } // keeping gcc happy
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("Activation", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("Activation", t); STD::free(t); }
|
|
#endif
|
|
|
|
virtual bool isNestedFunction() { return true; }
|
|
|
|
void defineMethod(Context *cx, const String& name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject::defineMethod(cx, name, attr, f);
|
|
}
|
|
|
|
JSType *topClass() { return NULL; }
|
|
|
|
|
|
// saved values from a previous execution
|
|
JSValue *mLocals;
|
|
JSValue *mStack;
|
|
uint32 mStackTop;
|
|
ScopeChain *mScopeChain;
|
|
JSValue *mArgumentBase;
|
|
JSValue mThis;
|
|
uint8 *mPC;
|
|
ByteCodeModule *mModule;
|
|
JSFunction *mContainer;
|
|
NamespaceList *mNamespaceList;
|
|
|
|
virtual JSFunction *getContainerFunction() { return mContainer; }
|
|
|
|
|
|
bool hasLocalVars() { return true; }
|
|
virtual uint32 localVarCount() { return mVariableCount; }
|
|
|
|
void defineTempVariable(Context *cx, Reference *&readRef, Reference *&writeRef, JSType *type);
|
|
|
|
Reference *genReference(Context *cx, bool hasBase, const String& name, NamespaceList *names, Access acc, uint32 depth);
|
|
|
|
JSValue getSlotValue(Context *cx, uint32 slotIndex);
|
|
void setSlotValue(Context *cx, uint32 slotIndex, JSValue &v);
|
|
|
|
};
|
|
|
|
|
|
class ScopeChain {
|
|
public:
|
|
|
|
ScopeChain(Context *cx, World &) :
|
|
m_cx(cx)
|
|
{
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("ScopeChain", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("ScopeChain", t); STD::free(t); }
|
|
#endif
|
|
|
|
Context *m_cx;
|
|
|
|
std::vector<JSObject *> mScopeStack;
|
|
typedef std::vector<JSObject *>::reverse_iterator ScopeScanner;
|
|
|
|
|
|
void addScope(JSObject *s)
|
|
{
|
|
mScopeStack.push_back(s);
|
|
}
|
|
|
|
void popScope()
|
|
{
|
|
ASSERT(mScopeStack.size());
|
|
mScopeStack.pop_back();
|
|
}
|
|
|
|
// add a new name to the current scope
|
|
Property *defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
return top->defineVariable(cx, name, attr, type);
|
|
}
|
|
Property *defineVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type, JSValue v)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
return top->defineVariable(cx, name, attr, type, v);
|
|
}
|
|
Property *defineAlias(Context *cx, const String &name, NamespaceList *names, PropertyAttribute attrFlags, JSType *type, JSValue *vp)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
return top->defineAlias(cx, name, names, attrFlags, type, vp);
|
|
}
|
|
Property *defineStaticVariable(Context *cx, const String& name, AttributeStmtNode *attr, JSType *type)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
ASSERT(dynamic_cast<JSType *>(top));
|
|
return top->defineStaticVariable(cx, name, attr, type);
|
|
}
|
|
void defineMethod(Context *cx, const String& name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
top->defineMethod(cx, name, attr, f);
|
|
}
|
|
void defineStaticMethod(Context *cx, const String& name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
ASSERT(dynamic_cast<JSType *>(top));
|
|
top->defineStaticMethod(cx, name, attr, f);
|
|
}
|
|
void defineConstructor(Context *cx, const String& name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
ASSERT(dynamic_cast<JSType *>(top));
|
|
top->defineConstructor(cx, name, attr, f);
|
|
}
|
|
void defineGetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
top->defineGetterMethod(cx, name, attr, f);
|
|
}
|
|
void defineSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
top->defineSetterMethod(cx, name, attr, f);
|
|
}
|
|
void defineStaticGetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
ASSERT(dynamic_cast<JSType *>(top));
|
|
top->defineStaticGetterMethod(cx, name, attr, f);
|
|
}
|
|
void defineStaticSetterMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
ASSERT(dynamic_cast<JSType *>(top));
|
|
top->defineStaticSetterMethod(cx, name, attr, f);
|
|
}
|
|
void defineUnaryOperator(Operator which, JSFunction *f)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
ASSERT(dynamic_cast<JSType *>(top));
|
|
((JSType *)top)->defineUnaryOperator(which, f);
|
|
}
|
|
|
|
// see if the current scope contains a name already
|
|
bool hasProperty(Context *cx, const String& name, NamespaceList *names, Access acc, PropertyIterator *p)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
return top->hasProperty(cx, name, names, acc, p);
|
|
}
|
|
|
|
bool deleteName(Context *cx, const String& name, NamespaceList *names);
|
|
|
|
// delete a property from the top object (already know it's there)
|
|
bool deleteProperty(Context *cx, const String &name, NamespaceList *names)
|
|
{
|
|
JSObject *top = mScopeStack.back();
|
|
return top->deleteProperty(cx, name, names);
|
|
}
|
|
|
|
// generate a reference to the given name
|
|
Reference *getName(Context *cx, const String& name, NamespaceList *names, Access acc);
|
|
|
|
bool hasNameValue(Context *cx, const String& name, NamespaceList *names);
|
|
|
|
// pushes the value of the name and returns it's container object
|
|
JSObject *getNameValue(Context *cx, const String& name, NamespaceList *names);
|
|
|
|
// return the class on the top of the stack (or NULL if there
|
|
// isn't one there).
|
|
JSType *topClass()
|
|
{
|
|
JSObject *obj = mScopeStack.back();
|
|
return obj->topClass();
|
|
}
|
|
|
|
JSFunction *getContainerFunction()
|
|
{
|
|
JSObject *obj = mScopeStack.back();
|
|
return (obj->getContainerFunction());
|
|
}
|
|
|
|
// return 'true' if the current top of scope stack is an
|
|
// activation - which would make any function declarations
|
|
// be local declarations.
|
|
bool isNestedFunction()
|
|
{
|
|
JSObject *obj = mScopeStack.back();
|
|
return obj->isNestedFunction();
|
|
}
|
|
|
|
bool isPossibleUncheckedFunction(FunctionDefinition &f);
|
|
|
|
void defineTempVariable(Context *cx, Reference *&readRef, Reference *&writeRef, JSType *type)
|
|
{
|
|
mScopeStack.back()->defineTempVariable(cx, readRef, writeRef, type);
|
|
}
|
|
|
|
// a compile time request to get the value for a name
|
|
// (i.e. we're accessing a constant value)
|
|
JSValue getCompileTimeValue(Context *cx, const String& name, NamespaceList *names);
|
|
|
|
|
|
|
|
void setNameValue(Context *cx, const String& name, NamespaceList *names);
|
|
|
|
// return the number of local vars used by all the
|
|
// Activations on the top of the chain
|
|
uint32 countVars()
|
|
{
|
|
uint32 result = 0;
|
|
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++)
|
|
{
|
|
if ((*s)->hasLocalVars())
|
|
result += (*s)->localVarCount();
|
|
else
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint32 countActivations()
|
|
{
|
|
uint32 result = 0;
|
|
for (ScopeScanner s = mScopeStack.rbegin(), end = mScopeStack.rend(); (s != end); s++)
|
|
{
|
|
if ((*s)->hasLocalVars())
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void collectNames(StmtNode *p);
|
|
|
|
// Lookup a name as a type in the chain
|
|
JSType *findType(Context *cx, const StringAtom& typeName, size_t pos);
|
|
|
|
// Get a type from an ExprNode
|
|
JSType *extractType(ExprNode *t);
|
|
|
|
// concoct a package name from an id list
|
|
String getPackageName(IdentifierList *packageIdList);
|
|
|
|
};
|
|
|
|
|
|
class JSFunction : public JSObject {
|
|
protected:
|
|
JSFunction() : JSObject(Function_Type), mActivation() { mActivation.mContainer = this; mPrototype = Function_Type->mPrototypeObject; } // for JSBoundFunction (XXX ask Patrick about this structure)
|
|
public:
|
|
|
|
typedef enum { Invalid, RequiredParameter, OptionalParameter, RestParameter, NamedParameter } ParameterFlag;
|
|
|
|
class ParameterData {
|
|
public:
|
|
ParameterData() : mName(NULL), mType(NULL), mInitializer((uint32)(-1)), mFlag(Invalid) { }
|
|
const String *mName;
|
|
JSType *mType;
|
|
uint32 mInitializer;
|
|
ParameterFlag mFlag;
|
|
};
|
|
|
|
|
|
typedef JSValue (NativeCode)(Context *cx, const JSValue &thisValue, JSValue argv[], uint32 argc);
|
|
|
|
// XXX these should be Function_Type->newInstance() calls, no?
|
|
|
|
JSFunction(Context *cx, JSType *resultType, ScopeChain *scopeChain);
|
|
JSFunction(Context *cx, NativeCode *code, JSType *resultType);
|
|
|
|
~JSFunction() { } // keeping gcc happy
|
|
|
|
#ifdef DEBUG
|
|
uint32 maxParameterIndex() { return mRequiredParameters + mOptionalParameters + mNamedParameters + ((mHasRestParameter) ? 1 : 0); }
|
|
#endif
|
|
void setByteCode(ByteCodeModule *b) { ASSERT(!isNative()); mByteCode = b; }
|
|
void setResultType(JSType *r) { mResultType = r; }
|
|
void setParameterCounts(Context *cx, uint32 r, uint32 o, uint32 n, bool hasRest);
|
|
void setParameter(uint32 index, const String *n, JSType *t, ParameterFlag flag)
|
|
{ ASSERT(mParameters && (index < maxParameterIndex()));
|
|
mParameters[index].mType = t; mParameters[index].mName = n; mParameters[index].mFlag = flag; }
|
|
void setParameterInitializer(uint32 index, uint32 offset)
|
|
{ ASSERT(mParameters && (index < maxParameterIndex())); mParameters[index].mInitializer = offset; }
|
|
|
|
void setIsPrototype(bool i) { mIsPrototype = i; }
|
|
void setIsConstructor(bool i) { mIsConstructor = i; }
|
|
void setIsUnchecked() { mIsChecked = false; }
|
|
void setFunctionName(FunctionName &n) { mFunctionName = new FunctionName(); mFunctionName->prefix = n.prefix; mFunctionName->name = n.name; }
|
|
void setFunctionName(const StringAtom *n)
|
|
{ mFunctionName = new FunctionName(); mFunctionName->name = n; }
|
|
void setClass(JSType *c) { mClass = c; }
|
|
|
|
virtual bool hasBoundThis() { return false; }
|
|
virtual bool isNative() { return (mCode != NULL); }
|
|
virtual bool isPrototype() { return mIsPrototype; }
|
|
virtual bool isConstructor() { return mIsConstructor; }
|
|
virtual bool isMethod() { return (mClass != NULL); }
|
|
virtual ByteCodeModule *getByteCode() { ASSERT(!isNative()); return mByteCode; }
|
|
virtual NativeCode *getNativeCode() { ASSERT(isNative()); return mCode; }
|
|
virtual ParameterBarrel *getParameterBarrel()
|
|
{ return mParameterBarrel; }
|
|
virtual Activation *getActivation() { return &mActivation; }
|
|
|
|
virtual ScopeChain *getScopeChain() { return mScopeChain; }
|
|
virtual JSValue getThisValue() { return kNullValue; }
|
|
virtual JSType *getClass() { return mClass; }
|
|
virtual FunctionName *getFunctionName() { return mFunctionName; }
|
|
|
|
virtual bool isChecked() { return mIsChecked; }
|
|
|
|
virtual JSType *getResultType() { return mResultType; }
|
|
virtual JSType *getParameterType(uint32 a)
|
|
{ ASSERT(mParameters && (a < maxParameterIndex())); return mParameters[a].mType; }
|
|
virtual bool parameterHasInitializer(uint32 a)
|
|
{ ASSERT(mParameters && (a < maxParameterIndex())); return (mParameters[a].mInitializer != (uint32)(-1)); }
|
|
virtual JSValue runParameterInitializer(Context *cx, uint32 a, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
|
|
virtual uint32 getRequiredParameterCount()
|
|
{ return mRequiredParameters; }
|
|
virtual uint32 getOptionalParameterCount()
|
|
{ return mOptionalParameters; }
|
|
virtual uint32 getNamedParameterCount() { return mNamedParameters; }
|
|
virtual bool hasOptionalParameters() { return (mOptionalParameters > 0); }
|
|
virtual bool parameterIsRequired(uint32 index)
|
|
{ ASSERT(mParameters && (index < maxParameterIndex())); return (mParameters[index].mFlag == RequiredParameter); }
|
|
virtual bool parameterIsOptional(uint32 index)
|
|
{ ASSERT(mParameters && (index < maxParameterIndex())); return (mParameters[index].mFlag == OptionalParameter); }
|
|
virtual bool parameterIsNamed(uint32 index)
|
|
{ ASSERT(mParameters && (index < maxParameterIndex())); return (mParameters[index].mFlag == NamedParameter); }
|
|
virtual uint32 getRestParameterIndex() { ASSERT(mHasRestParameter); return (mRequiredParameters + mOptionalParameters); }
|
|
|
|
virtual const String *getParameterName(uint32 index)
|
|
{ ASSERT(mParameters && (index < maxParameterIndex())); return mParameters[index].mName; }
|
|
virtual bool hasRestParameter() { return mHasRestParameter; }
|
|
|
|
virtual JSFunction *getFunction() { return this; }
|
|
bool isEqual(JSFunction *f) { return (getFunction() == f->getFunction()); }
|
|
|
|
void countParameters(Context *cx, FunctionDefinition &f);
|
|
|
|
|
|
ParameterBarrel *mParameterBarrel;
|
|
Activation mActivation; // not used during execution (XXX so maybe we should handle it differently, hmmm?)
|
|
|
|
private:
|
|
ByteCodeModule *mByteCode;
|
|
NativeCode *mCode;
|
|
JSType *mResultType;
|
|
uint32 mRequiredParameters;
|
|
uint32 mOptionalParameters;
|
|
uint32 mNamedParameters;
|
|
ParameterData *mParameters;
|
|
ScopeChain *mScopeChain;
|
|
bool mIsPrototype; // set for functions with prototype attribute
|
|
bool mIsConstructor;
|
|
bool mIsChecked;
|
|
bool mHasRestParameter;
|
|
JSType *mClass; // pointer to owning class if this function is a method
|
|
FunctionName *mFunctionName;
|
|
|
|
protected:
|
|
typedef Collector::InstanceOwner<JSFunction> JSFunctionOwner;
|
|
friend class Collector::InstanceOwner<JSFunction>;
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
JSObject::scan(collector);
|
|
mParameterBarrel = (ParameterBarrel*) collector->copy(mParameterBarrel);
|
|
mResultType = (JSType*) collector->copy(mResultType);
|
|
mClass = (JSType*) collector->copy(mClass);
|
|
mPrototype = (JSObject*) collector->copy(mPrototype);
|
|
return sizeof(JSFunction);
|
|
}
|
|
|
|
public:
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static JSFunctionOwner owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSFunction", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSFunction", t); STD::free(t); }
|
|
#endif
|
|
};
|
|
|
|
class JSBoundFunction : public JSFunction {
|
|
private:
|
|
JSFunction *mFunction;
|
|
JSObject *mThis;
|
|
public:
|
|
JSBoundFunction(JSFunction *f, JSObject *thisObj)
|
|
: mFunction(NULL), mThis(thisObj) { if (f->hasBoundThis()) mFunction = f->getFunction(); else mFunction = f; }
|
|
|
|
~JSBoundFunction() { } // keeping gcc happy
|
|
|
|
bool hasBoundThis() { return true; }
|
|
bool isNative() { return mFunction->isNative(); }
|
|
bool isPrototype() { return mFunction->isPrototype(); }
|
|
bool isConstructor() { return mFunction->isConstructor(); }
|
|
bool isMethod() { return mFunction->isMethod(); }
|
|
ByteCodeModule *getByteCode() { return mFunction->getByteCode(); }
|
|
NativeCode *getNativeCode() { return mFunction->getNativeCode(); }
|
|
ParameterBarrel *getParameterBarrel()
|
|
{ return mFunction->mParameterBarrel; }
|
|
Activation *getActivation() { return &mFunction->mActivation; }
|
|
JSType *getResultType() { return mFunction->getResultType(); }
|
|
JSType *getParameterType(uint32 a) { return mFunction->getParameterType(a); }
|
|
bool parameterHasInitializer(uint32 a){ return mFunction->parameterHasInitializer(a); }
|
|
JSValue runParameterInitializer(Context *cx, uint32 a, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{ return mFunction->runParameterInitializer(cx, a, thisValue, argv, argc); }
|
|
ScopeChain *getScopeChain() { return mFunction->getScopeChain(); }
|
|
JSValue getThisValue() { return (mThis) ? JSValue(mThis) : kNullValue; }
|
|
JSType *getClass() { return mFunction->getClass(); }
|
|
FunctionName *getFunctionName() { return mFunction->getFunctionName(); }
|
|
|
|
uint32 getRequiredParameterCount()
|
|
{ return mFunction->getRequiredParameterCount(); }
|
|
uint32 getOptionalParameterCount()
|
|
{ return mFunction->getOptionalParameterCount(); }
|
|
uint32 getNamedParameterCount() { return mFunction->getNamedParameterCount(); }
|
|
virtual bool parameterIsRequired(uint32 index)
|
|
{ return mFunction->parameterIsRequired(index); }
|
|
virtual bool parameterIsOptional(uint32 index)
|
|
{ return mFunction->parameterIsOptional(index); }
|
|
virtual bool parameterIsNamed(uint32 index)
|
|
{ return mFunction->parameterIsNamed(index); }
|
|
virtual uint32 getRestParameterIndex()
|
|
{ return mFunction->getRestParameterIndex(); }
|
|
virtual const String *getParameterName(uint32 index)
|
|
{ return mFunction->getParameterName(index); }
|
|
bool isChecked() { return mFunction->isChecked(); }
|
|
bool hasRestParameter() { return mFunction->hasRestParameter(); }
|
|
|
|
void getProperty(Context *cx, const String &name, NamespaceList *names)
|
|
{ mFunction->getProperty(cx, name, names); }
|
|
void setProperty(Context *cx, const String &name, NamespaceList *names, const JSValue &v)
|
|
{ mFunction->setProperty(cx, name, names, v); }
|
|
bool hasProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p)
|
|
{ return mFunction->hasProperty(cx, name, names, acc, p); }
|
|
bool hasOwnProperty(Context *cx, const String &name, NamespaceList *names, Access acc, PropertyIterator *p)
|
|
{ return mFunction->hasOwnProperty(cx, name, names, acc, p); }
|
|
PropertyIterator findNamespacedProperty(const String &name, NamespaceList *names)
|
|
{ return mFunction->findNamespacedProperty(name, names); }
|
|
bool deleteProperty(Context *cx, const String &name, NamespaceList *names)
|
|
{ return mFunction->deleteProperty(cx, name, names); }
|
|
|
|
JSFunction *getFunction() { return mFunction; }
|
|
|
|
protected:
|
|
typedef Collector::InstanceOwner<JSBoundFunction> JBoundFunctionOwner;
|
|
friend class Collector::InstanceOwner<JSBoundFunction>;
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
JSFunction::scan(collector);
|
|
// copy the appropriate members.
|
|
mFunction = (JSFunction*) collector->copy(mFunction);
|
|
return sizeof(JSBoundFunction);
|
|
}
|
|
|
|
public:
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static JBoundFunctionOwner owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("JSBoundFunction", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("JSBoundFunction", t); STD::free(t); }
|
|
#endif
|
|
};
|
|
|
|
// This is for binary operators, it collects together the operand
|
|
// types and the function pointer for the given operand. See also
|
|
// Context::initOperators where the default operators are set up.
|
|
class OperatorDefinition {
|
|
public:
|
|
|
|
OperatorDefinition(JSType *type1, JSType *type2, JSFunction *imp)
|
|
: mType1(type1), mType2(type2), mImp(imp) { ASSERT(mType1); ASSERT(mType2); }
|
|
|
|
|
|
JSType *mType1;
|
|
JSType *mType2;
|
|
JSFunction *mImp;
|
|
|
|
// see if this operator is applicable when
|
|
// being invoked by the given types
|
|
bool isApplicable(JSType *tx, JSType *ty)
|
|
{
|
|
return ( ((tx == mType1) || tx->derivesFrom(mType1))
|
|
&&
|
|
((ty == mType2) || ty->derivesFrom(mType2)) );
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// provide access to the Error object constructors so that runtime exceptions
|
|
// can be constructed for Javascript catches.
|
|
extern JSValue Error_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
extern JSValue EvalError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
extern JSValue RangeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
extern JSValue ReferenceError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
extern JSValue SyntaxError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
extern JSValue TypeError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
extern JSValue UriError_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
|
|
// called by bytecodegen for RegExp literals
|
|
extern JSValue RegExp_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
|
|
// called directly by String.match
|
|
extern JSValue RegExp_exec(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
|
|
class Attribute;
|
|
|
|
class Package : public JSObject {
|
|
public:
|
|
typedef enum { OnItsWay, InHand } PackageStatus;
|
|
|
|
Package(const String &name) : JSObject(Package_Type), mName(name), mStatus(OnItsWay) { }
|
|
|
|
String mName;
|
|
PackageStatus mStatus;
|
|
};
|
|
|
|
typedef std::vector<Package *> PackageList;
|
|
|
|
#define PACKAGE_NAME(pi) ((*pi)->mName)
|
|
#define PACKAGE_STATUS(pi) ((*pi)->mStatus)
|
|
|
|
class Context {
|
|
public:
|
|
struct ProtoFunDef {
|
|
char *name;
|
|
JSType *result;
|
|
uint32 length;
|
|
JSFunction::NativeCode *imp;
|
|
};
|
|
class PrototypeFunctions {
|
|
public:
|
|
PrototypeFunctions(ProtoFunDef *p)
|
|
{
|
|
uint32 count = 0;
|
|
mDef = NULL;
|
|
if (p) {
|
|
while (p[count].name) count++;
|
|
mDef = new ProtoFunDef[count];
|
|
for (uint32 i = 0; i < count; i++)
|
|
mDef[i] = *p++;
|
|
}
|
|
mCount = count;
|
|
}
|
|
~PrototypeFunctions()
|
|
{
|
|
if (mDef) delete mDef;
|
|
}
|
|
ProtoFunDef *mDef;
|
|
uint32 mCount;
|
|
};
|
|
|
|
struct ClassDef {
|
|
char *name;
|
|
JSFunction::NativeCode *defCon;
|
|
const JSValue *uninit;
|
|
};
|
|
|
|
Context(JSObject **global, World &world, Arena &a, Pragma::Flags flags);
|
|
|
|
#ifdef DEBUG
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("Context", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("Context", t); STD::free(t); }
|
|
#endif
|
|
|
|
StringAtom& Virtual_StringAtom;
|
|
StringAtom& Constructor_StringAtom;
|
|
StringAtom& Operator_StringAtom;
|
|
StringAtom& Fixed_StringAtom;
|
|
StringAtom& Dynamic_StringAtom;
|
|
StringAtom& Extend_StringAtom;
|
|
StringAtom& Prototype_StringAtom;
|
|
StringAtom& Forin_StringAtom;
|
|
StringAtom& Value_StringAtom;
|
|
StringAtom& Next_StringAtom;
|
|
StringAtom& Done_StringAtom;
|
|
StringAtom& Undefined_StringAtom;
|
|
StringAtom& Object_StringAtom;
|
|
StringAtom& Boolean_StringAtom;
|
|
StringAtom& Number_StringAtom;
|
|
StringAtom& String_StringAtom;
|
|
StringAtom& Function_StringAtom;
|
|
StringAtom& HasInstance_StringAtom;
|
|
StringAtom& True_StringAtom;
|
|
StringAtom& False_StringAtom;
|
|
StringAtom& Null_StringAtom;
|
|
StringAtom& ToString_StringAtom;
|
|
StringAtom& ValueOf_StringAtom;
|
|
StringAtom& Length_StringAtom;
|
|
StringAtom& FromCharCode_StringAtom;
|
|
StringAtom& Math_StringAtom;
|
|
StringAtom& NaN_StringAtom;
|
|
StringAtom& Eval_StringAtom;
|
|
StringAtom& Infinity_StringAtom;
|
|
StringAtom& Empty_StringAtom;
|
|
StringAtom& Arguments_StringAtom;
|
|
StringAtom& Message_StringAtom;
|
|
StringAtom& Name_StringAtom;
|
|
StringAtom& Error_StringAtom;
|
|
StringAtom& EvalError_StringAtom;
|
|
StringAtom& RangeError_StringAtom;
|
|
StringAtom& ReferenceError_StringAtom;
|
|
StringAtom& SyntaxError_StringAtom;
|
|
StringAtom& TypeError_StringAtom;
|
|
StringAtom& UriError_StringAtom;
|
|
StringAtom& Source_StringAtom;
|
|
StringAtom& Global_StringAtom;
|
|
StringAtom& IgnoreCase_StringAtom;
|
|
StringAtom& Multiline_StringAtom;
|
|
StringAtom& Input_StringAtom;
|
|
StringAtom& Index_StringAtom;
|
|
StringAtom& LastIndex_StringAtom;
|
|
StringAtom& LastMatch_StringAtom;
|
|
StringAtom& LastParen_StringAtom;
|
|
StringAtom& LeftContext_StringAtom;
|
|
StringAtom& RightContext_StringAtom;
|
|
StringAtom& Dollar_StringAtom;
|
|
StringAtom& UnderbarPrototype_StringAtom;
|
|
|
|
void initBuiltins();
|
|
void initClass(JSType *type, ClassDef *cdef, PrototypeFunctions *pdef);
|
|
void initOperators();
|
|
void initAttributeValue(char *name, uint32 trueFlags, uint32 falseFlags);
|
|
|
|
|
|
void defineOperator(Operator which, JSType *t1, JSType *t2, JSFunction *imp)
|
|
{
|
|
OperatorDefinition *op = new OperatorDefinition(t1, t2, imp);
|
|
mOperatorTable[which].push_back(op);
|
|
}
|
|
|
|
JSValue *buildArgumentBlock(JSFunction *target, uint32 &argCount);
|
|
|
|
|
|
// compiles attribute expression into an attribute object
|
|
// which is stored back into the statement node.
|
|
void setAttributeValue(AttributeStmtNode *s, PropertyAttribute defaultValue);
|
|
Attribute *executeAttributes(ExprNode *attr);
|
|
|
|
|
|
|
|
bool executeOperator(Operator op, JSType *t1, JSType *t2);
|
|
JSValue mapValueToType(JSValue v, JSType *t);
|
|
|
|
JSValue invokeFunction(JSFunction *target, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
|
|
// This reader is used to generate source information
|
|
// to go with exception messages.
|
|
void setReader(Reader *r) { mReader = r; }
|
|
|
|
JSObject *getGlobalObject() { ASSERT(mGlobal); return *mGlobal; }
|
|
|
|
World &mWorld;
|
|
ScopeChain *mScopeChain;
|
|
Arena &mArena;
|
|
Pragma::Flags mFlags; // The flags to use for the next parse; updated by the parser
|
|
bool mDebugFlag;
|
|
|
|
// the currently executing 'function'
|
|
ByteCodeModule *mCurModule;
|
|
|
|
uint8 *mPC;
|
|
|
|
JSValue mThis;
|
|
|
|
// this is the execution stack (for the current function)
|
|
JSValue *mStack;
|
|
uint32 mStackTop;
|
|
uint32 mStackMax;
|
|
|
|
NamespaceList *mNamespaceList;
|
|
|
|
void pushValue(const JSValue &v)
|
|
{
|
|
ASSERT(mStackTop < mStackMax);
|
|
mStack[mStackTop++] = v;
|
|
}
|
|
|
|
JSValue popValue()
|
|
{
|
|
ASSERT(mStackTop > 0);
|
|
return mStack[--mStackTop];
|
|
}
|
|
|
|
JSValue topValue()
|
|
{
|
|
return mStack[mStackTop - 1];
|
|
}
|
|
|
|
void resizeStack(uint32 n)
|
|
{
|
|
ASSERT(n <= mStackMax);
|
|
mStackTop = n;
|
|
}
|
|
|
|
uint32 stackSize()
|
|
{
|
|
return mStackTop;
|
|
}
|
|
|
|
JSValue getValue(uint32 n)
|
|
{
|
|
ASSERT(n < mStackTop);
|
|
return mStack[n];
|
|
}
|
|
|
|
// put the value in at 'index', lifting everything above that up by one
|
|
void insertValue(JSValue v, uint32 index)
|
|
{
|
|
ASSERT(mStackTop < mStackMax); // we're effectively pushing one entry
|
|
for (uint32 i = mStackTop - 1; i >= index; i--)
|
|
mStack[i + 1] = mStack[i];
|
|
mStack[index] = v;
|
|
mStackTop++;
|
|
}
|
|
/*
|
|
void setValue(uint32 n, JSValue v)
|
|
{
|
|
ASSERT(n < mStackTop);
|
|
mStack[n] = v;
|
|
}
|
|
*/
|
|
JSValue *getBase(uint32 n)
|
|
{
|
|
ASSERT(n <= mStackTop); // s'ok to point beyond the end
|
|
return &mStack[n];
|
|
}
|
|
|
|
void assureStackSpace(uint32 s);
|
|
|
|
|
|
// the activation stack
|
|
std::stack<Activation *> mActivationStack;
|
|
|
|
struct HandlerData {
|
|
HandlerData(uint8 *pc, uint32 stackSize, Activation *curAct)
|
|
: mPC(pc), mStackSize(stackSize), mActivation(curAct) { }
|
|
|
|
uint8 *mPC;
|
|
uint32 mStackSize;
|
|
Activation *mActivation;
|
|
};
|
|
|
|
std::stack<HandlerData *> mTryStack;
|
|
std::stack<uint8 *> mSubStack;
|
|
|
|
// the locals for the current function (an array, constructed on function entry)
|
|
JSValue *mLocals;
|
|
|
|
// the base of the incoming arguments for this function
|
|
JSValue *mArgumentBase;
|
|
|
|
typedef std::vector<OperatorDefinition *> OperatorList;
|
|
|
|
OperatorList mOperatorTable[OperatorCount];
|
|
|
|
|
|
PackageList mPackages; // the currently loaded packages, mPackages.back() is the current package
|
|
bool checkForPackage(const String &packageName); // return true if loaded, throw exception if loading
|
|
void loadPackage(const String &packageName, const String &filename); // load package from file
|
|
|
|
JSValue readEvalFile(const String& fileName);
|
|
JSValue readEvalString(const String &str, const String& fileName, ScopeChain *scopeChain, const JSValue& thisValue);
|
|
|
|
void buildRuntime(StmtNode *p);
|
|
void buildRuntimeForFunction(FunctionDefinition &f, JSFunction *fnc);
|
|
void buildRuntimeForStmt(StmtNode *p);
|
|
ByteCodeModule *genCode(StmtNode *p, const String &sourceName);
|
|
|
|
JSValue interpret(ByteCodeModule *bcm, int offset, ScopeChain *scopeChain, const JSValue& thisValue, JSValue *argv, uint32 argc);
|
|
JSValue interpret(uint8 *pc, uint8 *endPC);
|
|
|
|
void reportError(Exception::Kind kind, char *message, size_t pos, const char *arg = NULL);
|
|
void reportError(Exception::Kind kind, char *message, const char *arg = NULL);
|
|
void reportError(Exception::Kind kind, char *message, const String& name);
|
|
void reportError(Exception::Kind kind, char *message, size_t pos, const String& name);
|
|
|
|
|
|
/* utility routines */
|
|
|
|
// Extract the operator from the string literal function name
|
|
// - requires the paramter count in order to distinguish
|
|
// between unary and binary operators.
|
|
Operator getOperator(uint32 parameterCount, const String &name);
|
|
|
|
// Get the type of the nth parameter.
|
|
JSType *getParameterType(FunctionDefinition &function, int index);
|
|
|
|
|
|
Reader *mReader;
|
|
|
|
private:
|
|
JSObject **mGlobal;
|
|
|
|
|
|
};
|
|
|
|
/*
|
|
(a local instance of) This class is used when a function in the
|
|
interpreter execution codepath may need to re-invoke the interpreter
|
|
(by calling an internal method that MAY have an override). The stack
|
|
replacement simply inserts a stack big enough for whatever action is
|
|
about to occur.
|
|
*/
|
|
class ContextStackReplacement {
|
|
public:
|
|
enum { ReplacementStackSize = 4 };
|
|
ContextStackReplacement(Context *cx)
|
|
{
|
|
m_cx = cx;
|
|
mOldStack = cx->mStack;
|
|
mOldStackTop = cx->mStackTop;
|
|
mOldStackMax = cx->mStackMax;
|
|
cx->mStack = &mStack[0];
|
|
cx->mStackTop = 0;
|
|
cx->mStackMax = ReplacementStackSize;
|
|
}
|
|
|
|
~ContextStackReplacement()
|
|
{
|
|
m_cx->mStack = mOldStack;
|
|
m_cx->mStackTop = mOldStackTop;
|
|
m_cx->mStackMax = mOldStackMax;
|
|
}
|
|
|
|
JSValue mStack[ReplacementStackSize];
|
|
|
|
Context *m_cx;
|
|
|
|
JSValue *mOldStack;
|
|
uint32 mOldStackTop;
|
|
uint32 mOldStackMax;
|
|
|
|
};
|
|
|
|
|
|
class NamedArgument : public JSObject {
|
|
public:
|
|
NamedArgument(JSValue &v, const String *n) : JSObject(NamedArgument_Type), mValue(v), mName(n) { }
|
|
|
|
JSValue mValue;
|
|
const String *mName;
|
|
|
|
protected:
|
|
typedef Collector::InstanceOwner<NamedArgument> NamedArgumentOwner;
|
|
friend class Collector::InstanceOwner<NamedArgument>;
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
JSObject::scan(collector);
|
|
mValue.scan(collector);
|
|
return sizeof(NamedArgument);
|
|
}
|
|
|
|
public:
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static NamedArgumentOwner owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("NamedArgument", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("NamedArgument", t); STD::free(t); }
|
|
#endif
|
|
};
|
|
|
|
|
|
class Attribute : public JSObject {
|
|
public:
|
|
Attribute(PropertyAttribute t, PropertyAttribute f)
|
|
: JSObject(Attribute_Type), mTrueFlags(t), mFalseFlags(f), mExtendArgument(NULL), mNamespaceList(NULL) { }
|
|
|
|
PropertyAttribute mTrueFlags;
|
|
PropertyAttribute mFalseFlags;
|
|
|
|
JSType *mExtendArgument;
|
|
NamespaceList *mNamespaceList;
|
|
|
|
protected:
|
|
typedef Collector::InstanceOwner<Attribute> AttributeOwner;
|
|
friend class Collector::InstanceOwner<Attribute>;
|
|
/**
|
|
* Scans through the object, and copies all references.
|
|
*/
|
|
Collector::size_type scan(Collector* collector)
|
|
{
|
|
JSObject::scan(collector);
|
|
mExtendArgument = (JSType*) collector->copy(mExtendArgument);
|
|
return sizeof(Attribute);
|
|
}
|
|
|
|
public:
|
|
void* operator new(size_t n, Collector& gc)
|
|
{
|
|
static AttributeOwner owner;
|
|
return gc.allocateObject(n, &owner);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
void* operator new(size_t s) { void *t = STD::malloc(s); trace_alloc("Attribute", s, t); return t; }
|
|
void operator delete(void* t) { trace_release("Attribute", t); STD::free(t); }
|
|
#endif
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline AccessorReference::AccessorReference(JSFunction *f, PropertyAttribute attr)
|
|
: Reference(f->getResultType(), attr), mFunction(f)
|
|
{
|
|
}
|
|
|
|
inline FunctionReference::FunctionReference(JSFunction *f, PropertyAttribute attr)
|
|
: Reference(f->getResultType(), attr), mFunction(f)
|
|
{
|
|
}
|
|
|
|
inline GetterFunctionReference::GetterFunctionReference(JSFunction *f, PropertyAttribute attr)
|
|
: Reference(f->getResultType(), attr), mFunction(f)
|
|
{
|
|
}
|
|
|
|
inline SetterFunctionReference::SetterFunctionReference(JSFunction *f, JSType *type, PropertyAttribute attr)
|
|
: Reference(type, attr), mFunction(f)
|
|
{
|
|
}
|
|
|
|
inline void JSType::addStaticMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
defineStaticMethod(cx, name, attr, f);
|
|
}
|
|
|
|
inline void JSType::addMethod(Context *cx, const String &name, AttributeStmtNode *attr, JSFunction *f)
|
|
{
|
|
defineMethod(cx, name, attr, f);
|
|
}
|
|
|
|
|
|
inline bool JSInstance::isDynamic() { return mType->isDynamic(); }
|
|
|
|
}
|
|
}
|
|
|
|
#endif //js2runtime_h___
|