public void quit(Context cx, int exitCode) { if (type == SYSTEM_EXIT) { System.exit(exitCode); return; } throw Kit.codeBug(); }
private static MemberBox extractSetMethod(Class<?> type, MemberBox[] methods, boolean isStatic) { // // Note: it may be preferable to allow NativeJavaMethod.findFunction() // to find the appropriate setter; unfortunately, it requires an // instance of the target arg to determine that. // // Make two passes: one to find a method with direct type assignment, // and one to find a widening conversion. for (int pass = 1; pass <= 2; ++pass) { for (int i = 0; i < methods.length; ++i) { MemberBox method = methods[i]; if (!isStatic || method.isStatic()) { Class<?>[] params = method.argTypes; if (params.length == 1) { if (pass == 1) { if (params[0] == type) { return method; } } else { if (pass != 2) Kit.codeBug(); if (params[0].isAssignableFrom(type)) { return method; } } } } } } return null; }
/** * Generates code to push typed parameters onto the operand stack prior to a direct Java method * call. */ private static int generatePushParam(ClassFileWriter cfw, int paramOffset, Class<?> paramType) { if (!paramType.isPrimitive()) { cfw.addALoad(paramOffset); return 1; } String typeName = paramType.getName(); switch (typeName.charAt(0)) { case 'z': case 'b': case 'c': case 's': case 'i': // load an int value, convert to double. cfw.addILoad(paramOffset); return 1; case 'l': // load a long, convert to double. cfw.addLLoad(paramOffset); return 2; case 'f': // load a float, convert to double. cfw.addFLoad(paramOffset); return 1; case 'd': cfw.addDLoad(paramOffset); return 2; } throw Kit.codeBug(); }
protected void onContextReleased(Context cx) { Object listeners = this.listeners; for (int i = 0; ; ++i) { Listener l = (Listener) Kit.getListener(listeners, i); if (l == null) break; l.contextReleased(cx); } }
@Override public Ref nameRef(Context cx, Object name, Scriptable scope, int memberTypeFlags) { if ((memberTypeFlags & Node.ATTRIBUTE_FLAG) == 0) { // should only be called for cases like @name or @[expr] throw Kit.codeBug(); } XMLName xmlName = toAttributeName(cx, name); return xmlPrimaryReference(cx, xmlName, scope); }
public final void removeListener(Listener listener) { checkNotSealed(); synchronized (listenersLock) { if (disabledListening) { throw new IllegalStateException(); } listeners = Kit.removeListener(listeners, listener); } }
synchronized Object getPkgProperty(String name, Scriptable start, boolean createPkg) { Object cached = super.get(name, start); if (cached != NOT_FOUND) return cached; if (negativeCache != null && negativeCache.contains(name)) { // Performance optimization: see bug 421071 return null; } String className = (packageName.length() == 0) ? name : packageName + '.' + name; Context cx = Context.getContext(); ClassShutter shutter = cx.getClassShutter(); Scriptable newValue = null; if (shutter == null || shutter.visibleToScripts(className)) { Class<?> cl = null; if (classLoader != null) { cl = Kit.classOrNull(classLoader, className); } else { cl = Kit.classOrNull(className); } if (cl != null) { WrapFactory wrapFactory = cx.getWrapFactory(); newValue = wrapFactory.wrapJavaClass(cx, getTopLevelScope(this), cl); newValue.setPrototype(getPrototype()); } } if (newValue == null) { if (createPkg) { NativeJavaPackage pkg; pkg = new NativeJavaPackage(true, className, classLoader); ScriptRuntime.setObjectProtoAndParent(pkg, getParentScope()); newValue = pkg; } else { // add to negative cache if (negativeCache == null) negativeCache = new HashSet<String>(); negativeCache.add(name); } } if (newValue != null) { // Make it available for fast lookup and sharing of // lazily-reflected constructors and static members. super.put(name, start, newValue); } return newValue; }
static { // Are we running on a JDK 1.4 or later system? try { Class<?> ThrowableClass = Kit.classOrNull("java.lang.Throwable"); Class<?>[] signature = {ThrowableClass}; Throwable_initCause = ThrowableClass.getMethod("initCause", signature); } catch (Exception ex) { // Assume any exceptions means the method does not exist. } }
/** * Set explicit class loader to use when searching for Java classes. * * @see #getApplicationClassLoader() */ public final void initApplicationClassLoader(ClassLoader loader) { if (loader == null) throw new IllegalArgumentException("loader is null"); if (!Kit.testIfCanLoadRhinoClasses(loader)) throw new IllegalArgumentException("Loader can not resolve Rhino classes"); if (this.applicationClassLoader != null) throw new IllegalStateException("applicationClassLoader can only be set once"); checkNotSealed(); this.applicationClassLoader = loader; }
public Object action( Context cx, Scriptable scope, Scriptable thisObj, Object[] args, int actionType) { GlobData data = new GlobData(); data.mode = actionType; switch (actionType) { case RA_MATCH: { Object rval; data.optarg = 1; rval = matchOrReplace(cx, scope, thisObj, args, this, data, false); return data.arrayobj == null ? rval : data.arrayobj; } case RA_SEARCH: data.optarg = 1; return matchOrReplace(cx, scope, thisObj, args, this, data, false); case RA_REPLACE: { Object arg1 = args.length < 2 ? Undefined.instance : args[1]; String repstr = null; Function lambda = null; if (arg1 instanceof Function) { lambda = (Function) arg1; } else { repstr = ScriptRuntime.toString(arg1); } data.optarg = 2; data.lambda = lambda; data.repstr = repstr; data.dollar = repstr == null ? -1 : repstr.indexOf('$'); data.charBuf = null; data.leftIndex = 0; Object val = matchOrReplace(cx, scope, thisObj, args, this, data, true); SubString rc = this.rightContext; if (data.charBuf == null) { if (data.global || val == null || !val.equals(Boolean.TRUE)) { /* Didn't match even once. */ return data.str; } SubString lc = this.leftContext; replace_glob(data, cx, scope, this, lc.index, lc.length); } data.charBuf.append(rc.charArray, rc.index, rc.length); return data.charBuf.toString(); } default: throw Kit.codeBug(); } }
static { // Reflection in java is verbose Class<?>[] sig2 = new Class[2]; Class<?> cl = Kit.classOrNull("org.mozilla.javascript.JavaAdapter"); if (cl != null) { try { sig2[0] = ScriptRuntime.ObjectClass; sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream"); adapter_writeAdapterObject = cl.getMethod("writeAdapterObject", sig2); sig2[0] = ScriptRuntime.ScriptableClass; sig2[1] = Kit.classOrNull("java.io.ObjectInputStream"); adapter_readAdapterObject = cl.getMethod("readAdapterObject", sig2); } catch (Exception ex) { adapter_writeAdapterObject = null; adapter_readAdapterObject = null; } } }
/** Check that testClass is accessible from the given loader. */ static boolean testIfCanLoadRhinoClasses(ClassLoader loader) { Class<?> testClass = ScriptRuntime.ContextFactoryClass; Class<?> x = Kit.classOrNull(loader, testClass.getName()); if (x != testClass) { // The check covers the case when x == null => // loader does not know about testClass or the case // when x != null && x != testClass => // loader loads a class unrelated to testClass return false; } return true; }
private boolean isDom3Present() { Class<?> nodeClass = Kit.classOrNull("org.w3c.dom.Node"); if (nodeClass == null) return false; // Check to see whether DOM3 is present; use a new method defined in // DOM3 that is vital to our implementation try { nodeClass.getMethod("getUserData", new Class<?>[] {String.class}); return true; } catch (NoSuchMethodException e) { return false; } }
public Object run(Context cx) { if (type == PROCESS_FILES) { try { processFiles(cx, args); } catch (IOException ioe) { Context.reportError(ioe.toString()); } } else { throw Kit.codeBug(); } return null; }
/** * Provides a default {@link org.mozilla.javascript.xml.XMLLib.Factory XMLLib.Factory} to be used * by the <code>Context</code> instances produced by this factory. See {@link * Context#getE4xImplementationFactory} for details. * * <p>May return null, in which case E4X functionality is not supported in Rhino. * * <p>The default implementation now prefers the DOM3 E4X implementation. */ protected org.mozilla.javascript.xml.XMLLib.Factory getE4xImplementationFactory() { // Must provide default implementation, rather than abstract method, // so that past implementors of ContextFactory do not fail at runtime // upon invocation of this method. // Note that the default implementation returns null if we // neither have XMLBeans nor a DOM3 implementation present. if (isDom3Present()) { return org.mozilla.javascript.xml.XMLLib.Factory.create( "org.mozilla.javascript.xmlimpl.XMLLibImpl"); } else if (Kit.classOrNull("org.apache.xmlbeans.XmlCursor") != null) { return org.mozilla.javascript.xml.XMLLib.Factory.create( "org.mozilla.javascript.xml.impl.xmlbeans.XMLLibImpl"); } else { return null; } }
static void processFiles(Context cx, String[] files) throws IOException { StringBuffer cout = new StringBuffer(); if (files.length > 0) { for (int i = 0; i < files.length; i++) { try { String source = (String) readFileOrUrl(files[i], true); cout.append(Compressor.compressScript(source, 0, 1, escapeUnicode, stripConsole)); } catch (IOException ex) { // continue processing files } } } else { byte[] data = Kit.readStream(global.getIn(), 4096); // Convert to String using the default encoding String source = new String(data); if (source != null) { cout.append(Compressor.compressScript(source, 0, 1, escapeUnicode, stripConsole)); } } global.getOut().println(cout); }
private static Object readFileOrUrl(String path, boolean convertToString) throws IOException { URL url = null; // Assume path is URL if it contains a colon and there are at least // 2 characters in the protocol part. The later allows under Windows // to interpret paths with driver letter as file, not URL. if (path.indexOf(':') >= 2) { try { url = new URL(path); } catch (MalformedURLException ex) { } } InputStream is = null; int capacityHint = 0; if (url == null) { File file = new File(path); capacityHint = (int) file.length(); try { is = new FileInputStream(file); } catch (IOException ex) { Context.reportError(getMessage("msg.couldnt.open", path)); throw ex; } } else { try { URLConnection uc = url.openConnection(); is = uc.getInputStream(); capacityHint = uc.getContentLength(); // Ignore insane values for Content-Length if (capacityHint > (1 << 20)) { capacityHint = -1; } } catch (IOException ex) { Context.reportError(getMessage("msg.couldnt.open.url", url.toString(), ex.toString())); throw ex; } } if (capacityHint <= 0) { capacityHint = 4096; } byte[] data; try { try { is = new BufferedInputStream(is); data = Kit.readStream(is, capacityHint); } finally { is.close(); } } catch (IOException ex) { Context.reportError(ex.toString()); throw ex; } Object result; if (convertToString) { // Convert to String using the default encoding // TODO: Use 'charset=' argument of Content-Type if URL? result = new String(data); } else { result = data; } return result; }
/** Analog of C match_or_replace. */ private static Object matchOrReplace( Context cx, Scriptable scope, Scriptable thisObj, Object[] args, RegExpImpl reImpl, GlobData data, boolean forceFlat) { NativeRegExp re; String str = ScriptRuntime.toString(thisObj); data.str = str; Scriptable topScope = ScriptableObject.getTopLevelScope(scope); if (args.length == 0) { Object compiled = NativeRegExp.compileRE(cx, "", "", false); re = new NativeRegExp(topScope, compiled); } else if (args[0] instanceof NativeRegExp) { re = (NativeRegExp) args[0]; } else { String src = ScriptRuntime.toString(args[0]); String opt; if (data.optarg < args.length) { args[0] = src; opt = ScriptRuntime.toString(args[data.optarg]); } else { opt = null; } Object compiled = NativeRegExp.compileRE(cx, src, opt, forceFlat); re = new NativeRegExp(topScope, compiled); } data.regexp = re; data.global = (re.getFlags() & NativeRegExp.JSREG_GLOB) != 0; int[] indexp = {0}; Object result = null; if (data.mode == RA_SEARCH) { result = re.executeRegExp(cx, scope, reImpl, str, indexp, NativeRegExp.TEST); if (result != null && result.equals(Boolean.TRUE)) result = new Integer(reImpl.leftContext.length); else result = new Integer(-1); } else if (data.global) { re.lastIndex = 0; for (int count = 0; indexp[0] <= str.length(); count++) { result = re.executeRegExp(cx, scope, reImpl, str, indexp, NativeRegExp.TEST); if (result == null || !result.equals(Boolean.TRUE)) break; if (data.mode == RA_MATCH) { match_glob(data, cx, scope, count, reImpl); } else { if (data.mode != RA_REPLACE) Kit.codeBug(); SubString lastMatch = reImpl.lastMatch; int leftIndex = data.leftIndex; int leftlen = lastMatch.index - leftIndex; data.leftIndex = lastMatch.index + lastMatch.length; replace_glob(data, cx, scope, reImpl, leftIndex, leftlen); } if (reImpl.lastMatch.length == 0) { if (indexp[0] == str.length()) break; indexp[0]++; } } } else { result = re.executeRegExp( cx, scope, reImpl, str, indexp, ((data.mode == RA_REPLACE) ? NativeRegExp.TEST : NativeRegExp.MATCH)); } return result; }
/** @see Kit#codeBug */ public static RuntimeException codeBug() throws RuntimeException { throw Kit.codeBug(); }
/* * Analog of replace_glob() in jsstr.c */ private static void replace_glob( GlobData rdata, Context cx, Scriptable scope, RegExpImpl reImpl, int leftIndex, int leftlen) { int replen; String lambdaStr; if (rdata.lambda != null) { // invoke lambda function with args lastMatch, $1, $2, ... $n, // leftContext.length, whole string. SubString[] parens = reImpl.parens; int parenCount = (parens == null) ? 0 : parens.length; Object[] args = new Object[parenCount + 3]; args[0] = reImpl.lastMatch.toString(); for (int i = 0; i < parenCount; i++) { SubString sub = parens[i]; if (sub != null) { args[i + 1] = sub.toString(); } else { args[i + 1] = Undefined.instance; } } args[parenCount + 1] = new Integer(reImpl.leftContext.length); args[parenCount + 2] = rdata.str; // This is a hack to prevent expose of reImpl data to // JS function which can run new regexps modifing // regexp that are used later by the engine. // TODO: redesign is necessary if (reImpl != ScriptRuntime.getRegExpProxy(cx)) Kit.codeBug(); RegExpImpl re2 = new RegExpImpl(); re2.multiline = reImpl.multiline; re2.input = reImpl.input; ScriptRuntime.setRegExpProxy(cx, re2); try { Scriptable parent = ScriptableObject.getTopLevelScope(scope); Object result = rdata.lambda.call(cx, parent, parent, args); lambdaStr = ScriptRuntime.toString(result); } finally { ScriptRuntime.setRegExpProxy(cx, reImpl); } replen = lambdaStr.length(); } else { lambdaStr = null; replen = rdata.repstr.length(); if (rdata.dollar >= 0) { int[] skip = new int[1]; int dp = rdata.dollar; do { SubString sub = interpretDollar(cx, reImpl, rdata.repstr, dp, skip); if (sub != null) { replen += sub.length - skip[0]; dp += skip[0]; } else { ++dp; } dp = rdata.repstr.indexOf('$', dp); } while (dp >= 0); } } int growth = leftlen + replen + reImpl.rightContext.length; StringBuffer charBuf = rdata.charBuf; if (charBuf == null) { charBuf = new StringBuffer(growth); rdata.charBuf = charBuf; } else { charBuf.ensureCapacity(rdata.charBuf.length() + growth); } charBuf.append(reImpl.leftContext.charArray, leftIndex, leftlen); if (rdata.lambda != null) { charBuf.append(lambdaStr); } else { do_replace(rdata, cx, reImpl); } }
private static SubString interpretDollar( Context cx, RegExpImpl res, String da, int dp, int[] skip) { char dc; int num, tmp; if (da.charAt(dp) != '$') Kit.codeBug(); /* Allow a real backslash (literal "\\") to escape "$1" etc. */ int version = cx.getLanguageVersion(); if (version != Context.VERSION_DEFAULT && version <= Context.VERSION_1_4) { if (dp > 0 && da.charAt(dp - 1) == '\\') return null; } int daL = da.length(); if (dp + 1 >= daL) return null; /* Interpret all Perl match-induced dollar variables. */ dc = da.charAt(dp + 1); if (NativeRegExp.isDigit(dc)) { int cp; if (version != Context.VERSION_DEFAULT && version <= Context.VERSION_1_4) { if (dc == '0') return null; /* Check for overflow to avoid gobbling arbitrary decimal digits. */ num = 0; cp = dp; while (++cp < daL && NativeRegExp.isDigit(dc = da.charAt(cp))) { tmp = 10 * num + (dc - '0'); if (tmp < num) break; num = tmp; } } else { /* ECMA 3, 1-9 or 01-99 */ int parenCount = (res.parens == null) ? 0 : res.parens.length; num = dc - '0'; if (num > parenCount) return null; cp = dp + 2; if ((dp + 2) < daL) { dc = da.charAt(dp + 2); if (NativeRegExp.isDigit(dc)) { tmp = 10 * num + (dc - '0'); if (tmp <= parenCount) { cp++; num = tmp; } } } if (num == 0) return null; /* $0 or $00 is not valid */ } /* Adjust num from 1 $n-origin to 0 array-index-origin. */ num--; skip[0] = cp - dp; return res.getParenSubString(num); } skip[0] = 2; switch (dc) { case '$': return new SubString("$"); case '&': return res.lastMatch; case '+': return res.lastParen; case '`': if (version == Context.VERSION_1_2) { /* * JS1.2 imitated the Perl4 bug where left context at each step * in an iterative use of a global regexp started from last match, * not from the start of the target string. But Perl4 does start * $` at the beginning of the target string when it is used in a * substitution, so we emulate that special case here. */ res.leftContext.index = 0; res.leftContext.length = res.lastMatch.index; } return res.leftContext; case '\'': return res.rightContext; } return null; }
private void reflect(Scriptable scope, boolean includeProtected) { // We reflect methods first, because we want overloaded field/method // names to be allocated to the NativeJavaMethod before the field // gets in the way. Method[] methods = discoverAccessibleMethods(cl, includeProtected, includePrivate); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; int mods = method.getModifiers(); boolean isStatic = Modifier.isStatic(mods); Map<String, Object> ht = isStatic ? staticMembers : members; String name = method.getName(); Object value = ht.get(name); if (value == null) { ht.put(name, method); } else { ObjArray overloadedMethods; if (value instanceof ObjArray) { overloadedMethods = (ObjArray) value; } else { if (!(value instanceof Method)) Kit.codeBug(); // value should be instance of Method as at this stage // staticMembers and members can only contain methods overloadedMethods = new ObjArray(); overloadedMethods.add(value); ht.put(name, overloadedMethods); } overloadedMethods.add(method); } } // replace Method instances by wrapped NativeJavaMethod objects // first in staticMembers and then in members for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { boolean isStatic = (tableCursor == 0); Map<String, Object> ht = isStatic ? staticMembers : members; for (String name : ht.keySet()) { MemberBox[] methodBoxes; Object value = ht.get(name); if (value instanceof Method) { methodBoxes = new MemberBox[1]; methodBoxes[0] = new MemberBox((Method) value); } else { ObjArray overloadedMethods = (ObjArray) value; int N = overloadedMethods.size(); if (N < 2) Kit.codeBug(); methodBoxes = new MemberBox[N]; for (int i = 0; i != N; ++i) { Method method = (Method) overloadedMethods.get(i); methodBoxes[i] = new MemberBox(method); } } NativeJavaMethod fun = new NativeJavaMethod(methodBoxes); if (scope != null) { ScriptRuntime.setFunctionProtoAndParent(fun, scope); } ht.put(name, fun); } } // Reflect fields. Field[] fields = getAccessibleFields(); for (int i = 0; i < fields.length; i++) { Field field = fields[i]; String name = field.getName(); int mods = field.getModifiers(); if (!includePrivate && !Modifier.isPublic(mods)) { continue; } try { boolean isStatic = Modifier.isStatic(mods); Map<String, Object> ht = isStatic ? staticMembers : members; Object member = ht.get(name); if (member == null) { ht.put(name, field); } else if (member instanceof NativeJavaMethod) { NativeJavaMethod method = (NativeJavaMethod) member; FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field); Map<String, FieldAndMethods> fmht = isStatic ? staticFieldAndMethods : fieldAndMethods; if (fmht == null) { fmht = new HashMap<String, FieldAndMethods>(); if (isStatic) { staticFieldAndMethods = fmht; } else { fieldAndMethods = fmht; } } fmht.put(name, fam); ht.put(name, fam); } else if (member instanceof Field) { Field oldField = (Field) member; // If this newly reflected field shadows an inherited field, // then replace it. Otherwise, since access to the field // would be ambiguous from Java, no field should be // reflected. // For now, the first field found wins, unless another field // explicitly shadows it. if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) { ht.put(name, field); } } else { // "unknown member type" Kit.codeBug(); } } catch (SecurityException e) { // skip this field Context.reportWarning( "Could not access field " + name + " of class " + cl.getName() + " due to lack of privileges."); } } // Create bean properties from corresponding get/set methods first for // static members and then for instance members for (int tableCursor = 0; tableCursor != 2; ++tableCursor) { boolean isStatic = (tableCursor == 0); Map<String, Object> ht = isStatic ? staticMembers : members; Map<String, BeanProperty> toAdd = new HashMap<String, BeanProperty>(); // Now, For each member, make "bean" properties. for (String name : ht.keySet()) { // Is this a getter? boolean memberIsGetMethod = name.startsWith("get"); boolean memberIsSetMethod = name.startsWith("set"); boolean memberIsIsMethod = name.startsWith("is"); if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) { // Double check name component. String nameComponent = name.substring(memberIsIsMethod ? 2 : 3); if (nameComponent.length() == 0) continue; // Make the bean property name. String beanPropertyName = nameComponent; char ch0 = nameComponent.charAt(0); if (Character.isUpperCase(ch0)) { if (nameComponent.length() == 1) { beanPropertyName = nameComponent.toLowerCase(); } else { char ch1 = nameComponent.charAt(1); if (!Character.isUpperCase(ch1)) { beanPropertyName = Character.toLowerCase(ch0) + nameComponent.substring(1); } } } // If we already have a member by this name, don't do this // property. if (toAdd.containsKey(beanPropertyName)) continue; Object v = ht.get(beanPropertyName); if (v != null) { // A private field shouldn't mask a public getter/setter if (!includePrivate || !(v instanceof Member) || !Modifier.isPrivate(((Member) v).getModifiers())) { continue; } } // Find the getter method, or if there is none, the is- // method. MemberBox getter = null; getter = findGetter(isStatic, ht, "get", nameComponent); // If there was no valid getter, check for an is- method. if (getter == null) { getter = findGetter(isStatic, ht, "is", nameComponent); } // setter MemberBox setter = null; NativeJavaMethod setters = null; String setterName = "set".concat(nameComponent); if (ht.containsKey(setterName)) { // Is this value a method? Object member = ht.get(setterName); if (member instanceof NativeJavaMethod) { NativeJavaMethod njmSet = (NativeJavaMethod) member; if (getter != null) { // We have a getter. Now, do we have a matching // setter? Class<?> type = getter.method().getReturnType(); setter = extractSetMethod(type, njmSet.methods, isStatic); } else { // No getter, find any set method setter = extractSetMethod(njmSet.methods, isStatic); } if (njmSet.methods.length > 1) { setters = njmSet; } } } // Make the property. BeanProperty bp = new BeanProperty(getter, setter, setters); toAdd.put(beanPropertyName, bp); } } // Add the new bean properties. for (String key : toAdd.keySet()) { Object value = toAdd.get(key); ht.put(key, value); } } // Reflect constructors Constructor<?>[] constructors = getAccessibleConstructors(); ctors = new MemberBox[constructors.length]; for (int i = 0; i != constructors.length; ++i) { ctors[i] = new MemberBox(constructors[i]); } }
/** Type-munging for field setting and method invocation. Conforms to LC3 specification */ static Object coerceTypeImpl(Class<?> type, Object value) { if (value != null && value.getClass() == type) { return value; } switch (getJSTypeCode(value)) { case JSTYPE_NULL: // raise error if type.isPrimitive() if (type.isPrimitive()) { reportConversionError(value, type); } return null; case JSTYPE_UNDEFINED: if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) { return "undefined"; } else { reportConversionError("undefined", type); } break; case JSTYPE_BOOLEAN: // Under LC3, only JS Booleans can be coerced into a Boolean value if (type == Boolean.TYPE || type == ScriptRuntime.BooleanClass || type == ScriptRuntime.ObjectClass) { return value; } else if (type == ScriptRuntime.StringClass) { return value.toString(); } else { reportConversionError(value, type); } break; case JSTYPE_NUMBER: if (type == ScriptRuntime.StringClass) { return ScriptRuntime.toString(value); } else if (type == ScriptRuntime.ObjectClass) { return coerceToNumber(Double.TYPE, value); } else if ((type.isPrimitive() && type != Boolean.TYPE) || ScriptRuntime.NumberClass.isAssignableFrom(type)) { return coerceToNumber(type, value); } else { reportConversionError(value, type); } break; case JSTYPE_STRING: if (type == ScriptRuntime.StringClass || type.isInstance(value)) { return value; } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { // Special case for converting a single char string to a // character // Placed here because it applies *only* to JS strings, // not other JS objects converted to strings if (((String) value).length() == 1) { return new Character(((String) value).charAt(0)); } else { return coerceToNumber(type, value); } } else if ((type.isPrimitive() && type != Boolean.TYPE) || ScriptRuntime.NumberClass.isAssignableFrom(type)) { return coerceToNumber(type, value); } else { reportConversionError(value, type); } break; case JSTYPE_JAVA_CLASS: if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); } if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) { return value; } else if (type == ScriptRuntime.StringClass) { return value.toString(); } else { reportConversionError(value, type); } break; case JSTYPE_JAVA_OBJECT: case JSTYPE_JAVA_ARRAY: if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); } if (type.isPrimitive()) { if (type == Boolean.TYPE) { reportConversionError(value, type); } return coerceToNumber(type, value); } else { if (type == ScriptRuntime.StringClass) { return value.toString(); } else { if (type.isInstance(value)) { return value; } else { reportConversionError(value, type); } } } break; case JSTYPE_OBJECT: if (type == ScriptRuntime.StringClass) { return ScriptRuntime.toString(value); } else if (type.isPrimitive()) { if (type == Boolean.TYPE) { reportConversionError(value, type); } return coerceToNumber(type, value); } else if (type.isInstance(value)) { return value; } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) { double time = ((NativeDate) value).getJSTimeValue(); // XXX: This will replace NaN by 0 return new Date((long) time); } else if (type.isArray() && value instanceof NativeArray) { // Make a new java array, and coerce the JS array components // to the target (component) type. NativeArray array = (NativeArray) value; long length = array.getLength(); Class<?> arrayType = type.getComponentType(); Object Result = Array.newInstance(arrayType, (int) length); for (int i = 0; i < length; ++i) { try { Array.set(Result, i, coerceType(arrayType, array.get(i, array))); } catch (EvaluatorException ee) { reportConversionError(value, type); } } return Result; } else if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); if (type.isInstance(value)) return value; reportConversionError(value, type); } else if (type.isInterface() && value instanceof Callable) { // Try to use function as implementation of Java interface. // // XXX: Currently only instances of ScriptableObject are // supported since the resulting interface proxies should // be reused next time conversion is made and generic // Callable has no storage for it. Weak references can // address it but for now use this restriction. if (value instanceof ScriptableObject) { ScriptableObject so = (ScriptableObject) value; Object key = Kit.makeHashKeyFromPair(COERCED_INTERFACE_KEY, type); Object old = so.getAssociatedValue(key); if (old != null) { // Function was already wrapped return old; } Context cx = Context.getContext(); Object glue = InterfaceAdapter.create(cx, type, (Callable) value); // Store for later retrival glue = so.associateValue(key, glue); return glue; } reportConversionError(value, type); } else { reportConversionError(value, type); } break; } return value; }