/*
* Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.tools.asm;
import sun.tools.java.*;
import java.util.Enumeration;
import java.io.IOException;
import java.io.DataOutputStream;
import java.io.PrintStream;
import java.util.Vector;
// JCOV
import sun.tools.javac.*;
import java.io.File;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.String;
// end JCOV
/**
* This class is used to assemble the bytecode instructions for a method.
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*
* @author Arthur van Hoff
*/
public final
class Assembler implements Constants {
static final int NOTREACHED = 0;
static final int REACHED = 1;
static final int NEEDED = 2;
Label first = new Label();
Instruction last = first;
int maxdepth;
int maxvar;
int maxpc;
/**
* Add an instruction
*/
public void add(Instruction inst) {
if (inst != null) {
last.next = inst;
last = inst;
}
}
public void add(long where, int opc) {
add(new Instruction(where, opc, null));
}
public void add(long where, int opc, Object obj) {
add(new Instruction(where, opc, obj));
}
// JCOV
public void add(long where, int opc, Object obj, boolean flagCondInverted) {
add(new Instruction(where, opc, obj, flagCondInverted));
}
public void add(boolean flagNoCovered, long where, int opc, Object obj) {
add(new Instruction(flagNoCovered, where, opc, obj));
}
public void add(long where, int opc, boolean flagNoCovered) {
add(new Instruction(where, opc, flagNoCovered));
}
static Vector<String> SourceClassList = new Vector<>();
static Vector<String> TmpCovTable = new Vector<>();
static int[] JcovClassCountArray = new int[CT_LAST_KIND + 1];
static String JcovMagicLine = "JCOV-DATA-FILE-VERSION: 2.0";
static String JcovClassLine = "CLASS: ";
static String JcovSrcfileLine = "SRCFILE: ";
static String JcovTimestampLine = "TIMESTAMP: ";
static String JcovDataLine = "DATA: ";
static String JcovHeadingLine = "#kind\tcount";
static int[] arrayModifiers =
{M_PUBLIC, M_PRIVATE, M_PROTECTED, M_ABSTRACT, M_FINAL, M_INTERFACE};
static int[] arrayModifiersOpc =
{PUBLIC, PRIVATE, PROTECTED, ABSTRACT, FINAL, INTERFACE};
//end JCOV
/**
* Optimize instructions and mark those that can be reached
*/
void optimize(Environment env, Label lbl) {
lbl.pc = REACHED;
for (Instruction inst = lbl.next ; inst != null ; inst = inst.next) {
switch (inst.pc) {
case NOTREACHED:
inst.optimize(env);
inst.pc = REACHED;
break;
case REACHED:
return;
case NEEDED:
break;
}
switch (inst.opc) {
case opc_label:
case opc_dead:
if (inst.pc == REACHED) {
inst.pc = NOTREACHED;
}
break;
case opc_ifeq:
case opc_ifne:
case opc_ifgt:
case opc_ifge:
case opc_iflt:
case opc_ifle:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpgt:
case opc_if_icmpge:
case opc_if_icmplt:
case opc_if_icmple:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_ifnull:
case opc_ifnonnull:
optimize(env, (Label)inst.value);
break;
case opc_goto:
optimize(env, (Label)inst.value);
return;
case opc_jsr:
optimize(env, (Label)inst.value);
break;
case opc_ret:
case opc_return:
case opc_ireturn:
case opc_lreturn:
case opc_freturn:
case opc_dreturn:
case opc_areturn:
case opc_athrow:
return;
case opc_tableswitch:
case opc_lookupswitch: {
SwitchData sw = (SwitchData)inst.value;
optimize(env, sw.defaultLabel);
for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) {
optimize(env, e.nextElement());
}
return;
}
case opc_try: {
TryData td = (TryData)inst.value;
td.getEndLabel().pc = NEEDED;
for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) {
CatchData cd = e.nextElement();
optimize(env, cd.getLabel());
}
break;
}
}
}
}
/**
* Eliminate instructions that are not reached
*/
boolean eliminate() {
boolean change = false;
Instruction prev = first;
for (Instruction inst = first.next ; inst != null ; inst = inst.next) {
if (inst.pc != NOTREACHED) {
prev.next = inst;
prev = inst;
inst.pc = NOTREACHED;
} else {
change = true;
}
}
first.pc = NOTREACHED;
prev.next = null;
return change;
}
/**
* Optimize the byte codes
*/
public void optimize(Environment env) {
//listing(System.out);
do {
// Figure out which instructions are reached
optimize(env, first);
// Eliminate instructions that are not reached
} while (eliminate() && env.opt());
}
/**
* Collect all constants into the constant table
*/
public void collect(Environment env, MemberDefinition field, ConstantPool tab) {
// Collect constants for arguments only
// if a local variable table is generated
if ((field != null) && env.debug_vars()) {
@SuppressWarnings("unchecked")
Vector<MemberDefinition> v = (Vector<MemberDefinition>)field.getArguments();
if (v != null) {
for (Enumeration<MemberDefinition> e = v.elements() ; e.hasMoreElements() ;) {
MemberDefinition f = e.nextElement();
tab.put(f.getName().toString());
tab.put(f.getType().getTypeSignature());
}
}
}
// Collect constants from the instructions
for (Instruction inst = first ; inst != null ; inst = inst.next) {
inst.collect(tab);
}
}
/**
* Determine stack size, count local variables
*/
void balance(Label lbl, int depth) {
for (Instruction inst = lbl ; inst != null ; inst = inst.next) {
//Environment.debugOutput(inst.toString() + ": " + depth + " => " +
// (depth + inst.balance()));
depth += inst.balance();
if (depth < 0) {
throw new CompilerError("stack under flow: " + inst.toString() + " = " + depth);
}
if (depth > maxdepth) {
maxdepth = depth;
}
switch (inst.opc) {
case opc_label:
lbl = (Label)inst;
if (inst.pc == REACHED) {
if (lbl.depth != depth) {
throw new CompilerError("stack depth error " +
depth + "/" + lbl.depth +
": " + inst.toString());
}
return;
}
lbl.pc = REACHED;
lbl.depth = depth;
break;
case opc_ifeq:
case opc_ifne:
case opc_ifgt:
case opc_ifge:
case opc_iflt:
case opc_ifle:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpgt:
case opc_if_icmpge:
case opc_if_icmplt:
case opc_if_icmple:
case opc_if_acmpeq:
case opc_if_acmpne:
case opc_ifnull:
case opc_ifnonnull:
balance((Label)inst.value, depth);
break;
case opc_goto:
balance((Label)inst.value, depth);
return;
case opc_jsr:
balance((Label)inst.value, depth + 1);
break;
case opc_ret:
case opc_return:
case opc_ireturn:
case opc_lreturn:
case opc_freturn:
case opc_dreturn:
case opc_areturn:
case opc_athrow:
return;
case opc_iload:
case opc_fload:
case opc_aload:
case opc_istore:
case opc_fstore:
case opc_astore: {
int v = ((inst.value instanceof Number)
? ((Number)inst.value).intValue()
: ((LocalVariable)inst.value).slot) + 1;
if (v > maxvar)
maxvar = v;
break;
}
case opc_lload:
case opc_dload:
case opc_lstore:
case opc_dstore: {
int v = ((inst.value instanceof Number)
? ((Number)inst.value).intValue()
: ((LocalVariable)inst.value).slot) + 2;
if (v > maxvar)
maxvar = v;
break;
}
case opc_iinc: {
int v = ((int[])inst.value)[0] + 1;
if (v > maxvar)
maxvar = v + 1;
break;
}
case opc_tableswitch:
case opc_lookupswitch: {
SwitchData sw = (SwitchData)inst.value;
balance(sw.defaultLabel, depth);
for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) {
balance(e.nextElement(), depth);
}
return;
}
case opc_try: {
TryData td = (TryData)inst.value;
for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) {
CatchData cd = e.nextElement();
balance(cd.getLabel(), depth + 1);
}
break;
}
}
}
}
/**
* Generate code
*/
public void write(Environment env, DataOutputStream out,
MemberDefinition field, ConstantPool tab)
throws IOException {
//listing(System.out);
if ((field != null) && field.getArguments() != null) {
int sum = 0;
@SuppressWarnings("unchecked")
Vector<MemberDefinition> v = (Vector<MemberDefinition>)field.getArguments();
for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) {
MemberDefinition f = e.nextElement();
sum += f.getType().stackSize();
}
maxvar = sum;
}
// Make sure the stack balances. Also calculate maxvar and maxstack
try {
balance(first, 0);
} catch (CompilerError e) {
System.out.println("ERROR: " + e);
listing(System.out);
throw e;
}
// Assign PCs
int pc = 0, nexceptions = 0;
for (Instruction inst = first ; inst != null ; inst = inst.next) {
inst.pc = pc;
int sz = inst.size(tab);
if (pc<65536 && (pc+sz)>=65536) {
env.error(inst.where, "warn.method.too.long");
}
pc += sz;
if (inst.opc == opc_try) {
nexceptions += ((TryData)inst.value).catches.size();
}
}
// Write header
out.writeShort(maxdepth);
out.writeShort(maxvar);
out.writeInt(maxpc = pc);
// Generate code
for (Instruction inst = first.next ; inst != null ; inst = inst.next) {
inst.write(out, tab);
}
// write exceptions
out.writeShort(nexceptions);
if (nexceptions > 0) {
//listing(System.out);
writeExceptions(env, out, tab, first, last);
}
}
/**
* Write the exceptions table
*/
void writeExceptions(Environment env, DataOutputStream out, ConstantPool tab, Instruction first, Instruction last) throws IOException {
for (Instruction inst = first ; inst != last.next ; inst = inst.next) {
if (inst.opc == opc_try) {
TryData td = (TryData)inst.value;
writeExceptions(env, out, tab, inst.next, td.getEndLabel());
for (Enumeration<CatchData> e = td.catches.elements() ; e.hasMoreElements();) {
CatchData cd = e.nextElement();
//System.out.println("EXCEPTION: " + env.getSource() + ", pc=" + inst.pc + ", end=" + td.getEndLabel().pc + ", hdl=" + cd.getLabel().pc + ", tp=" + cd.getType());
out.writeShort(inst.pc);
out.writeShort(td.getEndLabel().pc);
out.writeShort(cd.getLabel().pc);
if (cd.getType() != null) {
out.writeShort(tab.index(cd.getType()));
} else {
out.writeShort(0);
}
}
inst = td.getEndLabel();
}
}
}
//JCOV
/**
* Write the coverage table
*/
public void writeCoverageTable(Environment env, ClassDefinition c, DataOutputStream out, ConstantPool tab, long whereField) throws IOException {
Vector<Cover> TableLot = new Vector<>(); /* Coverage table */
boolean begseg = false;
boolean begmeth = false;
@SuppressWarnings("deprecation")
long whereClass = ((SourceClass)c).getWhere();
Vector<Long> whereTry = new Vector<>();
int numberTry = 0;
int count = 0;
for (Instruction inst = first ; inst != null ; inst = inst.next) {
long n = (inst.where >> WHEREOFFSETBITS);
if (n > 0 && inst.opc != opc_label) {
if (!begmeth) {
if ( whereClass == inst.where)
TableLot.addElement(new Cover(CT_FIKT_METHOD, whereField, inst.pc));
else
TableLot.addElement(new Cover(CT_METHOD, whereField, inst.pc));
count++;
begmeth = true;
}
if (!begseg && !inst.flagNoCovered ) {
boolean findTry = false;
for (Enumeration<Long> e = whereTry.elements(); e.hasMoreElements();) {
if (e.nextElement().longValue() == inst.where) {
findTry = true;
break;
}
}
if (!findTry) {
TableLot.addElement(new Cover(CT_BLOCK, inst.where, inst.pc));
count++;
begseg = true;
}
}
}
switch (inst.opc) {
case opc_label:
begseg = false;
break;
case opc_ifeq:
case opc_ifne:
case opc_ifnull:
case opc_ifnonnull:
case opc_ifgt:
case opc_ifge:
case opc_iflt:
case opc_ifle:
case opc_if_icmpeq:
case opc_if_icmpne:
case opc_if_icmpgt:
case opc_if_icmpge:
case opc_if_icmplt:
case opc_if_icmple:
case opc_if_acmpeq:
case opc_if_acmpne: {
if ( inst.flagCondInverted ) {
TableLot.addElement(new Cover(CT_BRANCH_TRUE, inst.where, inst.pc));
TableLot.addElement(new Cover(CT_BRANCH_FALSE, inst.where, inst.pc));
} else {
TableLot.addElement(new Cover(CT_BRANCH_FALSE, inst.where, inst.pc));
TableLot.addElement(new Cover(CT_BRANCH_TRUE, inst.where, inst.pc));
}
count += 2;
begseg = false;
break;
}
case opc_goto: {
begseg = false;
break;
}
case opc_ret:
case opc_return:
case opc_ireturn:
case opc_lreturn:
case opc_freturn:
case opc_dreturn:
case opc_areturn:
case opc_athrow: {
break;
}
case opc_try: {
whereTry.addElement(Long.valueOf(inst.where));
begseg = false;
break;
}
case opc_tableswitch: {
SwitchData sw = (SwitchData)inst.value;
for (int i = sw.minValue; i <= sw.maxValue; i++) {
TableLot.addElement(new Cover(CT_CASE, sw.whereCase(new Integer(i)), inst.pc));
count++;
}
if (!sw.getDefault()) {
TableLot.addElement(new Cover(CT_SWITH_WO_DEF, inst.where, inst.pc));
count++;
} else {
TableLot.addElement(new Cover(CT_CASE, sw.whereCase("default"), inst.pc));
count++;
}
begseg = false;
break;
}
case opc_lookupswitch: {
SwitchData sw = (SwitchData)inst.value;
for (Enumeration<Integer> e = sw.sortedKeys(); e.hasMoreElements() ; ) {
Integer v = e.nextElement();
TableLot.addElement(new Cover(CT_CASE, sw.whereCase(v), inst.pc));
count++;
}
if (!sw.getDefault()) {
TableLot.addElement(new Cover(CT_SWITH_WO_DEF, inst.where, inst.pc));
count++;
} else {
TableLot.addElement(new Cover(CT_CASE, sw.whereCase("default"), inst.pc));
count++;
}
begseg = false;
break;
}
}
}
Cover Lot;
long ln, pos;
out.writeShort(count);
for (int i = 0; i < count; i++) {
Lot = TableLot.elementAt(i);
ln = (Lot.Addr >> WHEREOFFSETBITS);
pos = (Lot.Addr << (64 - WHEREOFFSETBITS)) >> (64 - WHEREOFFSETBITS);
out.writeShort(Lot.NumCommand);
out.writeShort(Lot.Type);
out.writeInt((int)ln);
out.writeInt((int)pos);
if ( !(Lot.Type == CT_CASE && Lot.Addr == 0) ) {
JcovClassCountArray[Lot.Type]++;
}
}
}
/*
* Increase count of methods for native methods
*/
public void addNativeToJcovTab(Environment env, ClassDefinition c) {
JcovClassCountArray[CT_METHOD]++;
}
/*
* Create class jcov element
*/
private String createClassJcovElement(Environment env, ClassDefinition c) {
String SourceClass = (Type.mangleInnerType((c.getClassDeclaration()).getName())).toString();
String ConvSourceClass;
String classJcovLine;
SourceClassList.addElement(SourceClass);
ConvSourceClass = SourceClass.replace('.', '/');
classJcovLine = JcovClassLine + ConvSourceClass;
classJcovLine = classJcovLine + " [";
String blank = "";
for (int i = 0; i < arrayModifiers.length; i++ ) {
if ((c.getModifiers() & arrayModifiers[i]) != 0) {
classJcovLine = classJcovLine + blank + opNames[arrayModifiersOpc[i]];
blank = " ";
}
}
classJcovLine = classJcovLine + "]";
return classJcovLine;
}
/*
* generate coverage data
*/
public void GenVecJCov(Environment env, ClassDefinition c, long Time) {
@SuppressWarnings("deprecation")
String SourceFile = ((SourceClass)c).getAbsoluteName();
TmpCovTable.addElement(createClassJcovElement(env, c));
TmpCovTable.addElement(JcovSrcfileLine + SourceFile);
TmpCovTable.addElement(JcovTimestampLine + Time);
TmpCovTable.addElement(JcovDataLine + "A"); // data format
TmpCovTable.addElement(JcovHeadingLine);
for (int i = CT_FIRST_KIND; i <= CT_LAST_KIND; i++) {
if (JcovClassCountArray[i] != 0) {
TmpCovTable.addElement(new String(i + "\t" + JcovClassCountArray[i]));
JcovClassCountArray[i] = 0;
}
}
}
/*
* generate file of coverage data
*/
@SuppressWarnings("deprecation") // for JCovd.readLine() calls
public void GenJCov(Environment env) {
try {
File outFile = env.getcovFile();
if( outFile.exists()) {
DataInputStream JCovd = new DataInputStream(
new BufferedInputStream(
new FileInputStream(outFile)));
String CurrLine = null;
boolean first = true;
String Class;
CurrLine = JCovd.readLine();
if ((CurrLine != null) && CurrLine.startsWith(JcovMagicLine)) {
// this is a good Jcov file
while((CurrLine = JCovd.readLine()) != null ) {
if ( CurrLine.startsWith(JcovClassLine) ) {
first = true;
for(Enumeration<String> e = SourceClassList.elements(); e.hasMoreElements();) {
String clsName = CurrLine.substring(JcovClassLine.length());
int idx = clsName.indexOf(' ');
if (idx != -1) {
clsName = clsName.substring(0, idx);
}
Class = e.nextElement();
if ( Class.compareTo(clsName) == 0) {
first = false;
break;
}
}
}
if (first) // re-write old class
TmpCovTable.addElement(CurrLine);
}
}
JCovd.close();
}
PrintStream CovFile = new PrintStream(new DataOutputStream(new FileOutputStream(outFile)));
CovFile.println(JcovMagicLine);
for(Enumeration<String> e = TmpCovTable.elements(); e.hasMoreElements();) {
CovFile.println(e.nextElement());
}
CovFile.close();
}
catch (FileNotFoundException e) {
System.out.println("ERROR: " + e);
}
catch (IOException e) {
System.out.println("ERROR: " + e);
}
}
// end JCOV
/**
* Write the linenumber table
*/
public void writeLineNumberTable(Environment env, DataOutputStream out, ConstantPool tab) throws IOException {
long ln = -1;
int count = 0;
for (Instruction inst = first ; inst != null ; inst = inst.next) {
long n = (inst.where >> WHEREOFFSETBITS);
if ((n > 0) && (ln != n)) {
ln = n;
count++;
}
}
ln = -1;
out.writeShort(count);
for (Instruction inst = first ; inst != null ; inst = inst.next) {
long n = (inst.where >> WHEREOFFSETBITS);
if ((n > 0) && (ln != n)) {
ln = n;
out.writeShort(inst.pc);
out.writeShort((int)ln);
//System.out.println("pc = " + inst.pc + ", ln = " + ln);
}
}
}
/**
* Figure out when registers contain a legal value. This is done
* using a simple data flow algorithm. This information is later used
* to generate the local variable table.
*/
void flowFields(Environment env, Label lbl, MemberDefinition locals[]) {
if (lbl.locals != null) {
// Been here before. Erase any conflicts.
MemberDefinition f[] = lbl.locals;
for (int i = 0 ; i < maxvar ; i++) {
if (f[i] != locals[i]) {
f[i] = null;
}
}
return;
}
// Remember the set of active registers at this point
lbl.locals = new MemberDefinition[maxvar];
System.arraycopy(locals, 0, lbl.locals, 0, maxvar);
MemberDefinition newlocals[] = new MemberDefinition[maxvar];
System.arraycopy(locals, 0, newlocals, 0, maxvar);
locals = newlocals;
for (Instruction inst = lbl.next ; inst != null ; inst = inst.next) {
switch (inst.opc) {
case opc_istore: case opc_istore_0: case opc_istore_1:
case opc_istore_2: case opc_istore_3:
case opc_fstore: case opc_fstore_0: case opc_fstore_1:
case opc_fstore_2: case opc_fstore_3:
case opc_astore: case opc_astore_0: case opc_astore_1:
case opc_astore_2: case opc_astore_3:
case opc_lstore: case opc_lstore_0: case opc_lstore_1:
case opc_lstore_2: case opc_lstore_3:
case opc_dstore: case opc_dstore_0: case opc_dstore_1:
case opc_dstore_2: case opc_dstore_3:
if (inst.value instanceof LocalVariable) {
LocalVariable v = (LocalVariable)inst.value;
locals[v.slot] = v.field;
}
break;
case opc_label:
flowFields(env, (Label)inst, locals);
return;
case opc_ifeq: case opc_ifne: case opc_ifgt:
case opc_ifge: case opc_iflt: case opc_ifle:
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpgt:
case opc_if_icmpge: case opc_if_icmplt: case opc_if_icmple:
case opc_if_acmpeq: case opc_if_acmpne:
case opc_ifnull: case opc_ifnonnull:
case opc_jsr:
flowFields(env, (Label)inst.value, locals);
break;
case opc_goto:
flowFields(env, (Label)inst.value, locals);
return;
case opc_return: case opc_ireturn: case opc_lreturn:
case opc_freturn: case opc_dreturn: case opc_areturn:
case opc_athrow: case opc_ret:
return;
case opc_tableswitch:
case opc_lookupswitch: {
SwitchData sw = (SwitchData)inst.value;
flowFields(env, sw.defaultLabel, locals);
for (Enumeration<Label> e = sw.tab.elements() ; e.hasMoreElements();) {
flowFields(env, e.nextElement(), locals);
}
return;
}
case opc_try: {
Vector<CatchData> catches = ((TryData)inst.value).catches;
for (Enumeration<CatchData> e = catches.elements(); e.hasMoreElements();) {
CatchData cd = e.nextElement();
flowFields(env, cd.getLabel(), locals);
}
break;
}
}
}
}
/**
* Write the local variable table. The necessary constants have already been
* added to the constant table by the collect() method. The flowFields method
* is used to determine which variables are alive at each pc.
*/
public void writeLocalVariableTable(Environment env, MemberDefinition field, DataOutputStream out, ConstantPool tab) throws IOException {
MemberDefinition locals[] = new MemberDefinition[maxvar];
int i = 0;
// Initialize arguments
if ((field != null) && (field.getArguments() != null)) {
int reg = 0;
@SuppressWarnings("unchecked")
Vector<MemberDefinition> v = (Vector<MemberDefinition>)field.getArguments();
for (Enumeration<MemberDefinition> e = v.elements(); e.hasMoreElements(); ) {
MemberDefinition f = e.nextElement();
locals[reg] = f;
reg += f.getType().stackSize();
}
}
flowFields(env, first, locals);
/**代码未完, 请加载全部代码(NowJava.com).**/