package org.dynjs.runtime; import org.dynjs.exception.ThrowException; public class Reference { private Object base; private String referencedName; private boolean strict; public Reference(String referencedName, Object base, boolean strict) { this.referencedName = referencedName; this.base = base; this.strict = strict; } public Object getBase() { return this.base; } public String getReferencedName() { return this.referencedName; } public boolean isStrictReference() { return this.strict; } public boolean hasPrimitiveBase() { return (this.base instanceof String) || (this.base instanceof Number) || (this.base instanceof Boolean); } public boolean isPropertyReference() { //return (this.base instanceof JSObject) || hasPrimitiveBase(); return ( ! ( this.base instanceof EnvironmentRecord ) ); } public boolean isUnresolvableReference() { return this.base == Types.UNDEFINED; } public Object getValue(ExecutionContext context) { // 8.7.1 Object value = null; if (isUnresolvableReference()) { throw new ThrowException(context, context.createReferenceError(referencedName + " is not defined")); } if (isPropertyReference()) { if (!hasPrimitiveBase()) { value = ((JSObject) this.base).get(context, this.referencedName); } else { value = primitiveGet(context, Types.toObject(context, this.base), this.referencedName); } } else { value = ((EnvironmentRecord) this.base).getBindingValue(context, this.referencedName, this.strict); } return value; } protected Object primitiveGet(ExecutionContext context, JSObject o, String name) { // 8.7.1 primitive [[Get]] Object d = o.getProperty(context, name, false); if (d == Types.UNDEFINED) { return Types.UNDEFINED; } PropertyDescriptor desc = (PropertyDescriptor) d; if (desc.isDataDescriptor()) { Object value = desc.getValue(); if ( value == null ) { value = Types.UNDEFINED; } return value; } Object getter = desc.getGetter(); if (getter == Types.UNDEFINED) { return Types.UNDEFINED; } return context.call((JSFunction) getter, o); } public void putValue(ExecutionContext context, Object value) { // 8.7.2 if (isUnresolvableReference()) { if (isStrictReference()) { throw new ThrowException(context, context.createReferenceError(referencedName + " is not defined")); } else { context.getGlobalContext().getObject().put(context, this.referencedName, value, false); } } else if (isPropertyReference()) { if (!hasPrimitiveBase()) { ((JSObject) this.base).put(context, this.referencedName, value, this.strict); } else { // TODO: handle primitives } } else { ((EnvironmentRecord) this.base).setMutableBinding(context, this.referencedName, value, this.strict); } } public boolean isValidForPrePostIncrementDecrement() { if (isStrictReference() && getBase() instanceof EnvironmentRecord) { if (this.referencedName.equals("eval") || this.referencedName.equals("arguments")) { return false; } } return true; } public String toString() { return "[Reference: name=" + this.referencedName + "; base=" + this.base + " (" + this.base.getClass().getName() + ")]"; } }