Compare commits

..

1 Commits

Author SHA1 Message Date
(no author)
973a54719f This commit was manufactured by cvs2svn to create tag 'Last140MergeSource'.
git-svn-id: svn://10.0.0.236/tags/Last140MergeSource@10343 18797224-902f-48f8-a5cc-f745e15eee43
1998-09-18 02:28:51 +00:00
78 changed files with 11682 additions and 6812 deletions

View File

@@ -0,0 +1,5 @@
# This is a list of local files which get copied to the mozilla:dist directory
#
jsjava.h
nsILiveconnect.h

View File

@@ -0,0 +1,89 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../..
include $(DEPTH)/config/config.mk
ifdef NSJVM
DIRS = classes
endif
MODULE = java
LIBRARY_NAME = jsj
REQUIRES = java js applet nspr img util layer xpcom
CSRCS = jsj.c \
jsj_JSObject.c \
jsj_JavaArray.c \
jsj_JavaClass.c \
jsj_JavaMember.c \
jsj_JavaObject.c \
jsj_JavaPackage.c \
jsj_array.c \
jsj_class.c \
jsj_convert.c \
jsj_field.c \
jsj_hash.c \
jsj_method.c \
jsj_utils.c \
nsCLiveconnect.c \
nsCLiveconnectFactory.c
LLIBS=$(LIBNSPR) $(DIST)/lib/js$(MOZ_BITS)$(VERSION_NUMBER).lib \
$(DIST)/lib/xpcom32.lib
ifdef NSJVM
JNI_GEN = netscape.javascript.JSObject netscape.javascript.JSException
endif
EXPORTS = jsjava.h \
$(JNI_GEN_DIR)/netscape_javascript_JSObject.h \
$(JNI_GEN_DIR)/netscape_javascript_JSException.h \
nsILiveconnect.h \
$(NULL)
include $(DEPTH)/config/rules.mk
######################################################################
# Generate jsj_nodl.c (so that you can check it in)
# These make rules only works on IRIX...sigh
ifeq ($(OS_ARCH),IRIX)
jsj_nodl.c: $(OBJDIR)/stubs.o Makefile $(DEPTH)/config/nodl.pl
rm -f $@
$(PERL) $(DEPTH)/config/nodl.pl "jsj_nodl_tab" \
`nm -Bn $(OBJDIR)/stubs.o | egrep Java_.*_stub | awk '{print $$3;}'` > $@
endif
ifdef JAVA_OR_OJI
$(OBJDIR)/stubs.o: \
$(JNI_GEN_DIR)/netscape_javascript_JSObject.c \
$(JNI_GEN_DIR)/netscape_javascript_JSException.c \
$(NULL)
else
$(OBJDIR)/stubs.o:
endif

View File

@@ -0,0 +1,90 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/config.mk
ifdef NSJVM
DIRS = classes
endif
MODULE = java
LIBRARY_NAME = jsj
REQUIRES = java js applet nspr img util layer xpcom caps oji plugin
CSRCS = jsj.c \
jsj_JSObject.c \
jsj_JavaArray.c \
jsj_JavaClass.c \
jsj_JavaMember.c \
jsj_JavaObject.c \
jsj_JavaPackage.c \
jsj_array.c \
jsj_class.c \
jsj_convert.c \
jsj_field.c \
jsj_hash.c \
jsj_method.c \
jsj_utils.c
nsCLiveconnect.c \
nsCLiveconnectFactory.c
ifdef NSJVM
JNI_GEN = netscape.javascript.JSObject netscape.javascript.JSException
endif
EXPORTS = $(srcdir)/jsjava.h \
$(srcdir)/$(JNI_GEN_DIR)/netscape_javascript_JSObject.h \
$(srcdir)/$(JNI_GEN_DIR)/netscape_javascript_JSException.h \
$(NULL)
include $(topsrcdir)/config/rules.mk
######################################################################
# Generate jsj_nodl.c (so that you can check it in)
# These make rules only works on IRIX...sigh
ifeq ($(OS_ARCH),IRIX)
jsj_nodl.c: $(OBJDIR)/stubs.o Makefile $(topsrcdir)/config/nodl.pl
rm -f $@
$(PERL) $(topsrcdir)/config/nodl.pl "jsj_nodl_tab" \
`nm -Bn $(OBJDIR)/stubs.o | egrep Java_.*_stub | awk '{print $$3;}'` > $@
endif
ifdef JAVA_OR_OJI
$(OBJDIR)/stubs.o: \
$(JRI_GEN_DIR)/netscape_javascript_JSObject.c \
$(JRI_GEN_DIR)/netscape_javascript_JSException.c \
$(NULL)
else
$(OBJDIR)/stubs.o:
endif

View File

@@ -0,0 +1,14 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class netscape_javascript_JSException */
#ifndef _Included_netscape_javascript_JSException
#define _Included_netscape_javascript_JSException
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,102 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class netscape_javascript_JSObject */
#ifndef _Included_netscape_javascript_JSObject
#define _Included_netscape_javascript_JSObject
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: netscape_javascript_JSObject
* Method: initClass
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_initClass
(JNIEnv *, jclass);
/*
* Class: netscape_javascript_JSObject
* Method: getMember
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: getSlot
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getSlot
(JNIEnv *, jobject, jint);
/*
* Class: netscape_javascript_JSObject
* Method: setMember
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setMember
(JNIEnv *, jobject, jstring, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: setSlot
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setSlot
(JNIEnv *, jobject, jint, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: removeMember
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_removeMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: call
* Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_call
(JNIEnv *, jobject, jstring, jobjectArray);
/*
* Class: netscape_javascript_JSObject
* Method: eval
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_eval
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: toString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_netscape_javascript_JSObject_toString
(JNIEnv *, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: getWindow
* Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getWindow
(JNIEnv *, jclass, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: finalize
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_finalize
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,65 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../../..
MODULE = java
#
# the idea is that the install phase for those modules will
# make their own signed jar files
# there are some exceptions here that we deal with
# stuff in modules and in ns/js/jsj
#
JMODS = netscape/javascript netscape/javascript/adapters
#
# JDIRS is dependant on JAVA_DESTPATH in config/rules.m[a]k.
# Be sure to touch that directory if you add a new directory to
# JDIRS, or else it will not build. FIXME
#
JDIRS = $(JMODS)
JAR_JSJ = jsj10.jar
JAR_JSJ_CLASSES = $(JMODS)
#
# jars to build at install time
#
JARS = $(JAR_JSJ)
include $(DEPTH)/config/rules.mk
JAVA_SOURCEPATH = $(DEPTH)/js/src/liveconnect/classes
doc::
$(JAVADOC) -d $(DIST)/doc netscape.javascript
natives_list:: FORCE
rm -rf $@
find . -name "*.class" -print | sed 's@\./\(.*\)\.class$$@\1@' | \
sed 's@/@.@g' | xargs $(JVH) -natives | sort > $@
check_natives:: natives_list
rm -f found_natives
nm -B ../$(OBJDIR)/*.o \
| egrep "Java.*_stub" | awk '{ print $$3; }' | sort > found_natives
diff found_natives natives_list
FORCE:

View File

@@ -0,0 +1,70 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = java
#
# the idea is that the install phase for those modules will
# make their own signed jar files
# there are some exceptions here that we deal with
# stuff in modules and in ns/js/jsj
#
JMODS = netscape/javascript netscape/javascript/adapters
#
# JDIRS is dependant on JAVA_DESTPATH in config/rules.m[a]k.
# Be sure to touch that directory if you add a new directory to
# JDIRS, or else it will not build. FIXME
#
JDIRS = $(JMODS)
JAR_JSJ = jsj10.jar
JAR_JSJ_CLASSES = $(JMODS)
#
# jars to build at install time
#
JARS = $(JAR_JSJ)
include $(topsrcdir)/config/rules.mk
JAVA_SOURCEPATH = $(topsrcdir)/js/src/liveconnect/classes
doc::
$(JAVADOC) -d $(DIST)/doc netscape.javascript
natives_list:: FORCE
rm -rf $@
find . -name "*.class" -print | sed 's@\./\(.*\)\.class$$@\1@' | \
sed 's@/@.@g' | xargs $(JVH) -natives | sort > $@
check_natives:: natives_list
rm -f found_natives
nm -B ../$(OBJDIR)/*.o \
| egrep "Java.*_stub" | awk '{ print $$3; }' | sort > found_natives
diff found_natives natives_list
FORCE:

View File

@@ -0,0 +1,78 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
IGNORE_MANIFEST=1
#
#//------------------------------------------------------------------------
#//
#// Makefile to build the JavaScript-Java tree
#//
#//------------------------------------------------------------------------
DEPTH = ..\..\..\..
JAVA_SOURCEPATH=$(DEPTH)\js\src\liveconnect\classes
#//------------------------------------------------------------------------
#//
#// Define the files necessary to build the target (ie. OBJS)
#//
#//------------------------------------------------------------------------
include <$(DEPTH)\config\config.mak>
JMOZ = \
netscape/javascript \
$(NULL)
all::
MODULE=java
JMODS=netscape/javascript
!ifdef JAVA_OR_NSJVM
JDIRS=$(JMODS)
JAR_JSJ_CLASSES=$(JMODS)
JAR_NAME = jsj10.jar
!endif
include <$(DEPTH)\config\rules.mak>
!ifdef JAVA_OR_NSJVM
install::
cd $(JAVA_DESTPATH)
@echo +++ building/updating $(JAR_NAME)
-for %i in ($(JAR_JSJ_CLASSES:/=\)) do @$(ZIP_PROG) $(ZIP_FLAGS) $(JAR_NAME) %i\*.class
cd $(MAKEDIR)
!ifdef REGENERATE
install::
cd $(JAVA_DESTPATH)
@echo +++ updating $(DEPTH)\nav-java\stubs\classes\$(JAR_NAME) for checkin
-for %i in ($(JAR_JSJ_CLASSES:/=\)) do @$(ZIP_PROG) $(ZIP_FLAGS) ..\..\nav-java\stubs\classes\$(JAR_NAME) %i\*.class
cd $(MAKEDIR)
!endif
!endif
javadoc:
-mkdir $(XPDIST)\javadoc 2> NUL
echo $(JAVADOC) -sourcepath . -d $(XPDIST)\javadoc $(JDIRS:/=.)
$(JAVADOC) -sourcepath . -d $(XPDIST)\javadoc $(JDIRS:/=.)

View File

@@ -0,0 +1,56 @@
/* Insert copyright and license here 19** */
package netscape.javascript;
/**
* JSException is an exception which is thrown when JavaScript code
* returns an error.
*/
public
class JSException extends Exception {
String filename;
int lineno;
String source;
int tokenIndex;
/**
* Constructs a JSException without a detail message.
* A detail message is a String that describes this particular exception.
*/
public JSException() {
super();
filename = "unknown";
lineno = 0;
source = "";
tokenIndex = 0;
}
/**
* Constructs a JSException with a detail message.
* A detail message is a String that describes this particular exception.
* @param s the detail message
*/
public JSException(String s) {
super(s);
filename = "unknown";
lineno = 0;
source = "";
tokenIndex = 0;
}
/**
* Constructs a JSException with a detail message and all the
* other info that usually comes with a JavaScript error.
* @param s the detail message
*/
public JSException(String s, String filename, int lineno,
String source, int tokenIndex) {
super(s);
this.filename = filename;
this.lineno = lineno;
this.source = source;
this.tokenIndex = tokenIndex;
}
}

View File

@@ -0,0 +1,150 @@
/* -*- Mode: C; tab-width: 4; -*- */
/* Insert copyright and license here 19** */
/* more doc todo:
* threads
* gc
*
*
*/
package netscape.javascript;
import java.applet.Applet;
/**
* JSObject allows Java to manipulate objects that are
* defined in JavaScript.
* Values passed from Java to JavaScript are converted as
* follows:<ul>
* <li>JSObject is converted to the original JavaScript object
* <li>Any other Java object is converted to a JavaScript wrapper,
* which can be used to access methods and fields of the java object.
* Converting this wrapper to a string will call the toString method
* on the original object, converting to a number will call the
* floatValue method if possible and fail otherwise. Converting
* to a boolean will try to call the booleanValue method in the
* same way.
* <li>Java arrays are wrapped with a JavaScript object that understands
* array.length and array[index]
* <li>A Java boolean is converted to a JavaScript boolean
* <li>Java byte, char, short, int, long, float, and double are converted
* to JavaScript numbers
* </ul>
* Values passed from JavaScript to Java are converted as follows:<ul>
* <li>objects which are wrappers around java objects are unwrapped
* <li>other objects are wrapped with a JSObject
* <li>strings, numbers and booleans are converted to String, Float,
* and Boolean objects respectively
* </ul>
* This means that all JavaScript values show up as some kind
* of java.lang.Object in Java. In order to make much use of them,
* you will have to cast them to the appropriate subclass of Object,
* e.g. <code>(String) window.getMember("name");</code> or
* <code>(JSObject) window.getMember("document");</code>.
*/
public final class JSObject {
/* the internal object data */
private int internal;
/**
* initialize
*/
private static native void initClass();
static {
// On MRJ, this property won't exist, because the library is preloaded.
String liveConnectLibrary = System.getProperty("netscape.jsj.dll", null);
if (liveConnectLibrary != null) {
System.loadLibrary(liveConnectLibrary);
initClass();
}
}
/**
* it is illegal to construct a JSObject manually
*/
private JSObject(int jsobj_addr) {
internal = jsobj_addr;
}
/**
* Retrieves a named member of a JavaScript object.
* Equivalent to "this.<i>name</i>" in JavaScript.
*/
public native Object getMember(String name);
/**
* Retrieves an indexed member of a JavaScript object.
* Equivalent to "this[<i>index</i>]" in JavaScript.
*/
// public Object getMember(int index) { return getSlot(index); }
public native Object getSlot(int index);
/**
* Sets a named member of a JavaScript object.
* Equivalent to "this.<i>name</i> = <i>value</i>" in JavaScript.
*/
public native void setMember(String name, Object value);
/**
* Sets an indexed member of a JavaScript object.
* Equivalent to "this[<i>index</i>] = <i>value</i>" in JavaScript.
*/
// public void setMember(int index, Object value) {
// setSlot(index, value);
// }
public native void setSlot(int index, Object value);
/**
* Removes a named member of a JavaScript object.
*/
public native void removeMember(String name);
/**
* Calls a JavaScript method.
* Equivalent to "this.<i>methodName</i>(<i>args</i>[0], <i>args</i>[1], ...)" in JavaScript.
*/
public native Object call(String methodName, Object args[]);
/**
* Evaluates a JavaScript expression. The expression is a string
* of JavaScript source code which will be evaluated in the context
* given by "this".
*/
public native Object eval(String s);
/**
* Converts a JSObject to a String.
*/
public native String toString();
// should use some sort of identifier rather than String
// is "property" the right word?
// native String[] listProperties();
/**
* get a JSObject for the window containing the given applet
*/
public static native JSObject getWindow(Applet applet);
/**
* Finalization decrements the reference count on the corresponding
* JavaScript object.
*/
protected native void finalize();
/**
* Override java.lang.Object.equals() because identity is not preserved
* with instances of JSObject.
*/
public boolean equals(Object obj) {
JSObject jsobj;
if (!(obj instanceof JSObject))
return false;
jsobj = (JSObject)obj;
return (internal == jsobj.internal);
}
}

View File

@@ -0,0 +1,37 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* ** */
/**
* The JSProxy interface allows applets and plugins to
* share javascript contexts.
*/
package netscape.javascript;
import java.applet.Applet;
public interface JSProxy {
Object getMember(JSObject jso, String name);
Object getSlot(JSObject jso, int index);
void setMember(JSObject jso, String name, Object value);
void setSlot(JSObject jso, int index, Object value);
void removeMember(JSObject jso, String name);
Object call(JSObject jso, String methodName, Object args[]);
Object eval(JSObject jso, String s);
String toString(JSObject jso);
JSObject getWindow(Applet applet);
}

View File

@@ -0,0 +1,31 @@
package netscape.javascript;
/**
* Runs a JavaScript object with a run() method in a separate thread.
*/
public class JSRunnable implements Runnable {
private JSObject runnable;
public JSRunnable(JSObject runnable) {
this.runnable = runnable;
synchronized(this) {
new Thread(this).start();
try {
this.wait();
} catch (InterruptedException ie) {
}
}
}
public void run() {
try {
runnable.call("run", null);
synchronized(this) {
notifyAll();
}
} catch (Throwable t) {
System.err.println(t);
t.printStackTrace(System.err);
}
}
}

View File

@@ -0,0 +1,38 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* ** */
package netscape.javascript;
import java.io.*;
public class JSUtil {
/* Return the stack trace of an exception or error as a String */
public static String getStackTrace(Throwable t) {
ByteArrayOutputStream captureStream;
PrintStream p;
captureStream = new ByteArrayOutputStream();
p = new PrintStream(captureStream);
t.printStackTrace(p);
p.flush();
return captureStream.toString();
}
}

View File

@@ -0,0 +1,876 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the top-level initialization code and the implementation of the
* public API.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h" /* LiveConnect external API */
/*
* At certain times during initialization, there may be no JavaScript context
* available to direct error reports to, in which case the error messages
* are sent to this function. The caller is responsible for free'ing
* the js_error_msg argument.
*/
static void
report_java_initialization_error(JNIEnv *jEnv, const char *js_error_msg)
{
const char *error_msg, *java_error_msg;
java_error_msg = NULL;
#if 0 /* This can never work here, because jsj_GetJavaErrorMessage relies on
jlThrowable_toString which is set up by the initialization that calls
this function. */
if (jEnv) {
java_error_msg = jsj_GetJavaErrorMessage(jEnv);
(*jEnv)->ExceptionClear(jEnv);
}
#endif
if (java_error_msg) {
error_msg = PR_smprintf("initialization error: %s (%s)\n",
js_error_msg, java_error_msg);
free((void*)java_error_msg);
} else {
error_msg = PR_smprintf("initialization error: %s\n",
js_error_msg);
}
jsj_LogError(error_msg);
}
/*
* Opaque JVM handles to Java classes and methods required for Java reflection.
* These are computed and cached during initialization.
*/
jclass jlObject; /* java.lang.Object */
jclass jlrMethod; /* java.lang.reflect.Method */
jclass jlrField; /* java.lang.reflect.Field */
jclass jlVoid; /* java.lang.Void */
jclass jlrConstructor; /* java.lang.reflect.Constructor */
jclass jlThrowable; /* java.lang.Throwable */
jclass jlSystem; /* java.lang.System */
jclass jlClass; /* java.lang.Class */
jclass jlBoolean; /* java.lang.Boolean */
jclass jlDouble; /* java.lang.Double */
jclass jlString; /* java.lang.String */
jclass njJSObject; /* netscape.javascript.JSObject */
jclass njJSException; /* netscape.javascript.JSException */
jclass njJSUtil; /* netscape.javascript.JSUtil */
jmethodID jlClass_getMethods; /* java.lang.Class.getMethods() */
jmethodID jlClass_getConstructors; /* java.lang.Class.getConstructors() */
jmethodID jlClass_getFields; /* java.lang.Class.getFields() */
jmethodID jlClass_getName; /* java.lang.Class.getName() */
jmethodID jlClass_getComponentType; /* java.lang.Class.getComponentType() */
jmethodID jlClass_getModifiers; /* java.lang.Class.getModifiers() */
jmethodID jlClass_isArray; /* java.lang.Class.isArray() */
jmethodID jlrMethod_getName; /* java.lang.reflect.Method.getName() */
jmethodID jlrMethod_getParameterTypes; /* java.lang.reflect.Method.getParameterTypes() */
jmethodID jlrMethod_getReturnType; /* java.lang.reflect.Method.getReturnType() */
jmethodID jlrMethod_getModifiers; /* java.lang.reflect.Method.getModifiers() */
jmethodID jlrConstructor_getParameterTypes; /* java.lang.reflect.Constructor.getParameterTypes() */
jmethodID jlrConstructor_getModifiers; /* java.lang.reflect.Constructor.getModifiers() */
jmethodID jlrField_getName; /* java.lang.reflect.Field.getName() */
jmethodID jlrField_getType; /* java.lang.reflect.Field.getType() */
jmethodID jlrField_getModifiers; /* java.lang.reflect.Field.getModifiers() */
jmethodID jlBoolean_Boolean; /* java.lang.Boolean constructor */
jmethodID jlBoolean_booleanValue; /* java.lang.Boolean.booleanValue() */
jmethodID jlDouble_Double; /* java.lang.Double constructor */
jmethodID jlDouble_doubleValue; /* java.lang.Double.doubleValue() */
jmethodID jlThrowable_toString; /* java.lang.Throwable.toString() */
jmethodID jlThrowable_getMessage; /* java.lang.Throwable.getMessage() */
jmethodID jlSystem_identityHashCode; /* java.lang.System.identityHashCode() */
jobject jlVoid_TYPE; /* java.lang.Void.TYPE value */
jmethodID njJSException_JSException; /* netscape.javascript.JSexception constructor */
jmethodID njJSObject_JSObject; /* netscape.javascript.JSObject constructor */
jmethodID njJSUtil_getStackTrace; /* netscape.javascript.JSUtil.getStackTrace() */
jfieldID njJSObject_internal; /* netscape.javascript.JSObject.internal */
jfieldID njJSException_lineno; /* netscape.javascript.JSException.lineno */
jfieldID njJSException_tokenIndex; /* netscape.javascript.JSException.tokenIndex */
jfieldID njJSException_source; /* netscape.javascript.JSException.source */
jfieldID njJSException_filename; /* netscape.javascript.JSException.filename */
/* Obtain a reference to a Java class */
#define LOAD_CLASS(qualified_name, class) \
{ \
jclass _##class = (*jEnv)->FindClass(jEnv, #qualified_name); \
if (_##class == 0) { \
(*jEnv)->ExceptionClear(jEnv); \
report_java_initialization_error(jEnv, \
"Can't load class " #qualified_name); \
return JS_FALSE; \
} \
class = (*jEnv)->NewGlobalRef(jEnv, _##class); \
}
/* Obtain a methodID reference to a Java method or constructor */
#define _LOAD_METHOD(qualified_class, method, mvar, signature, class, is_static)\
if (is_static) { \
class##_##mvar = \
(*jEnv)->GetStaticMethodID(jEnv, class, #method, signature); \
} else { \
class##_##mvar = \
(*jEnv)->GetMethodID(jEnv, class, #method, signature); \
} \
if (class##_##mvar == 0) { \
report_java_initialization_error(jEnv, \
"Can't get mid for " #qualified_class "." #method "()"); \
return JS_FALSE; \
}
/* Obtain a methodID reference to a Java instance method */
#define LOAD_METHOD(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class, method, method, signature, class, JS_FALSE)
/* Obtain a methodID reference to a Java static method */
#define LOAD_STATIC_METHOD(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class, method, method, signature, class, JS_TRUE)
/* Obtain a methodID reference to a Java constructor */
#define LOAD_CONSTRUCTOR(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class,<init>, method, signature, class, JS_FALSE)
/* Obtain a fieldID reference to a Java instance or static field */
#define _LOAD_FIELDID(qualified_class, field, signature, class, is_static) \
if (is_static) { \
class##_##field = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
} else { \
class##_##field = (*jEnv)->GetFieldID(jEnv, class, #field, signature);\
} \
if (class##_##field == 0) { \
report_java_initialization_error(jEnv, \
"Can't get fid for " #qualified_class "." #field); \
return JS_FALSE; \
}
/* Obtain a fieldID reference to a Java instance field */
#define LOAD_FIELDID(qualified_class, field, signature, class) \
_LOAD_FIELDID(qualified_class, field, signature, class, JS_FALSE)
/* Obtain the value of a static field in a Java class */
#define LOAD_FIELD_VAL(qualified_class, field, signature, class, type) \
{ \
jfieldID field_id; \
field_id = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
if (field_id == 0) { \
report_java_initialization_error(jEnv, \
"Can't get fid for " #qualified_class "." #field); \
return JS_FALSE; \
} \
class##_##field = \
(*jEnv)->GetStatic##type##Field(jEnv, class, field_id); \
if (class##_##field == 0) { \
report_java_initialization_error(jEnv, \
"Can't read static field " #qualified_class "." #field); \
return JS_FALSE; \
} \
}
/* Obtain the value of a static field in a Java class, which is known to
contain an object value. */
#define LOAD_FIELD_OBJ(qualified_class, field, signature, class) \
LOAD_FIELD_VAL(qualified_class, field, signature, class, Object); \
class##_##field = (*jEnv)->NewGlobalRef(jEnv, class##_##field);
/*
* Load the Java classes, and the method and field descriptors required for Java reflection.
* Returns JS_TRUE on success, JS_FALSE on failure.
*/
static JSBool
init_java_VM_reflection(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
{
/* Load Java system classes and method, including java.lang.reflect classes */
LOAD_CLASS(java/lang/Object, jlObject);
LOAD_CLASS(java/lang/Class, jlClass);
LOAD_CLASS(java/lang/reflect/Method, jlrMethod);
LOAD_CLASS(java/lang/reflect/Constructor, jlrConstructor);
LOAD_CLASS(java/lang/reflect/Field, jlrField);
LOAD_CLASS(java/lang/Throwable, jlThrowable);
LOAD_CLASS(java/lang/System, jlSystem);
LOAD_CLASS(java/lang/Boolean, jlBoolean);
LOAD_CLASS(java/lang/Double, jlDouble);
LOAD_CLASS(java/lang/String, jlString);
LOAD_CLASS(java/lang/Void, jlVoid);
LOAD_METHOD(java.lang.Class, getMethods, "()[Ljava/lang/reflect/Method;",jlClass);
LOAD_METHOD(java.lang.Class, getConstructors, "()[Ljava/lang/reflect/Constructor;",jlClass);
LOAD_METHOD(java.lang.Class, getFields, "()[Ljava/lang/reflect/Field;", jlClass);
LOAD_METHOD(java.lang.Class, getName, "()Ljava/lang/String;", jlClass);
LOAD_METHOD(java.lang.Class, isArray, "()Z", jlClass);
LOAD_METHOD(java.lang.Class, getComponentType, "()Ljava/lang/Class;", jlClass);
LOAD_METHOD(java.lang.Class, getModifiers, "()I", jlClass);
LOAD_METHOD(java.lang.reflect.Method, getName, "()Ljava/lang/String;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getParameterTypes, "()[Ljava/lang/Class;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getReturnType, "()Ljava/lang/Class;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getModifiers, "()I", jlrMethod);
LOAD_METHOD(java.lang.reflect.Constructor, getParameterTypes, "()[Ljava/lang/Class;", jlrConstructor);
LOAD_METHOD(java.lang.reflect.Constructor, getModifiers, "()I", jlrConstructor);
LOAD_METHOD(java.lang.reflect.Field, getName, "()Ljava/lang/String;", jlrField);
LOAD_METHOD(java.lang.reflect.Field, getType, "()Ljava/lang/Class;", jlrField);
LOAD_METHOD(java.lang.reflect.Field, getModifiers, "()I", jlrField);
LOAD_METHOD(java.lang.Throwable, toString, "()Ljava/lang/String;", jlThrowable);
LOAD_METHOD(java.lang.Throwable, getMessage, "()Ljava/lang/String;", jlThrowable);
LOAD_METHOD(java.lang.Double, doubleValue, "()D", jlDouble);
LOAD_METHOD(java.lang.Boolean, booleanValue, "()Z", jlBoolean);
LOAD_STATIC_METHOD(java.lang.System, identityHashCode, "(Ljava/lang/Object;)I", jlSystem);
LOAD_CONSTRUCTOR(java.lang.Boolean, Boolean, "(Z)V", jlBoolean);
LOAD_CONSTRUCTOR(java.lang.Double, Double, "(D)V", jlDouble);
LOAD_FIELD_OBJ(java.lang.Void, TYPE, "Ljava/lang/Class;", jlVoid);
return JS_TRUE;
}
#if XP_MAC
/**
* Workaround for the fact that MRJ loads a different instance of the shared library.
*/
#include "netscape_javascript_JSObject.h"
static JSObject_RegisterNativeMethods(JNIEnv* jEnv)
{
// Manually load the required native methods.
static JNINativeMethod nativeMethods[] = {
"initClass", "()V", (void*)&Java_netscape_javascript_JSObject_initClass,
"getMember", "(Ljava/lang/String;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_getMember,
"getSlot", "(I)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_getSlot,
"setMember", "(Ljava/lang/String;Ljava/lang/Object;)V", (void*)&Java_netscape_javascript_JSObject_setMember,
"setSlot", "(ILjava/lang/Object;)V", (void*)&Java_netscape_javascript_JSObject_setSlot,
"removeMember", "(Ljava/lang/String;)V", (void*)&Java_netscape_javascript_JSObject_removeMember,
"call", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_call,
"eval", "(Ljava/lang/String;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_eval,
"toString", "()Ljava/lang/String;", (void*)&Java_netscape_javascript_JSObject_toString,
"getWindow", "(Ljava/applet/Applet;)Lnetscape/javascript/JSObject;", (void*)&Java_netscape_javascript_JSObject_getWindow,
"finalize", "()V", (void*)&Java_netscape_javascript_JSObject_finalize,
/* "equals", "(Ljava/lang/Object;)Z", (void*)&Java_netscape_javascript_JSObject_equals */
};
(*jEnv)->RegisterNatives(jEnv, njJSObject, nativeMethods, sizeof(nativeMethods) / sizeof(JNINativeMethod));
if ((*jEnv)->ExceptionOccurred(jEnv)) {
report_java_initialization_error(jEnv, "Couldn't initialize JSObject native methods.");
(*jEnv)->ExceptionClear(jEnv);
}
/* call the initClass method, since we nailed the static initializer for testing. */
Java_netscape_javascript_JSObject_initClass(jEnv, njJSObject);
}
#endif
/* Load Netscape-specific Java extension classes, methods, and fields */
static JSBool
init_netscape_java_classes(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
{
LOAD_CLASS(netscape/javascript/JSObject, njJSObject);
LOAD_CLASS(netscape/javascript/JSException, njJSException);
LOAD_CLASS(netscape/javascript/JSUtil, njJSUtil);
#if XP_MAC
JSObject_RegisterNativeMethods(jEnv);
#endif
LOAD_CONSTRUCTOR(netscape.javascript.JSObject,
JSObject, "(I)V", njJSObject);
LOAD_CONSTRUCTOR(netscape.javascript.JSException,
JSException, "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)V",
njJSException);
LOAD_FIELDID(netscape.javascript.JSObject,
internal, "I", njJSObject);
LOAD_FIELDID(netscape.javascript.JSException,
lineno, "I", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
tokenIndex, "I", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
source, "Ljava/lang/String;", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
filename, "Ljava/lang/String;", njJSException);
LOAD_STATIC_METHOD(netscape.javascript.JSUtil,
getStackTrace, "(Ljava/lang/Throwable;)Ljava/lang/String;",
njJSUtil);
return JS_TRUE;
}
JSJavaVM *jsjava_vm_list = NULL;
/*
* Called once per Java VM, this function initializes the classes, fields, and
* methods required for Java reflection. If java_vm is NULL, a new Java VM is
* created, using the provided classpath in addition to any default classpath.
* The classpath argument is ignored, however, if java_vm_arg is non-NULL.
*/
JSJavaVM *
JSJ_ConnectToJavaVM(SystemJavaVM *java_vm_arg, void* initargs)
{
SystemJavaVM* java_vm;
JSJavaVM *jsjava_vm;
JNIEnv *jEnv;
PR_ASSERT(JSJ_callbacks);
PR_ASSERT(JSJ_callbacks->attach_current_thread);
PR_ASSERT(JSJ_callbacks->detach_current_thread);
PR_ASSERT(JSJ_callbacks->get_java_vm);
jsjava_vm = (JSJavaVM*)malloc(sizeof(JSJavaVM));
if (!jsjava_vm)
return NULL;
memset(jsjava_vm, 0, sizeof(JSJavaVM));
java_vm = java_vm_arg;
/* If a Java VM was passed in, try to attach to it on the current thread. */
if (java_vm) {
jEnv = JSJ_callbacks->attach_current_thread(java_vm);
if (jEnv == NULL) {
jsj_LogError("Failed to attach to Java VM thread\n");
free(jsjava_vm);
return NULL;
}
}
else {
PRBool ok;
PR_ASSERT(JSJ_callbacks->create_java_vm);
PR_ASSERT(JSJ_callbacks->destroy_java_vm);
ok = JSJ_callbacks->create_java_vm(&java_vm, &jEnv, initargs);
if (!ok || java_vm == NULL) {
jsj_LogError("Failed to create Java VM\n");
free(jsjava_vm);
return NULL;
}
/* Remember that we created the VM so that we know to destroy it later */
jsjava_vm->jsj_created_java_vm = JS_TRUE;
}
jsjava_vm->java_vm = java_vm;
jsjava_vm->main_thread_env = jEnv;
/* Load the Java classes, and the method and field descriptors required for
Java reflection. */
if (!init_java_VM_reflection(jsjava_vm, jEnv)) {
JSJ_DisconnectFromJavaVM(jsjava_vm);
return NULL;
}
/*
* JVM initialization for netscape.javascript.JSObject is performed
* independently of the other classes that are initialized in
* init_java_VM_reflection, because we allow it to fail. In the case
* of failure, LiveConnect is still operative, but only when calling
* from JS to Java and not vice-versa.
*/
init_netscape_java_classes(jsjava_vm, jEnv);
/* Put this VM on the list of all created VMs */
jsjava_vm->next = jsjava_vm_list;
jsjava_vm_list = jsjava_vm;
return jsjava_vm;
}
JSJCallbacks *JSJ_callbacks = NULL;
/* Called once to set up callbacks for all instances of LiveConnect */
void
JSJ_Init(JSJCallbacks *callbacks)
{
PR_ASSERT(callbacks);
JSJ_callbacks = callbacks;
}
/*
* Initialize the provided JSContext by setting up the JS classes necessary for
* reflection and by defining JavaPackage objects for the default Java packages
* as properties of global_obj. Additional packages may be pre-defined by
* setting the predefined_packages argument. (Pre-defining a Java package at
* initialization time is not necessary, but it will make package lookup faster
* and, more importantly, will avoid unnecessary network accesses if classes
* are being loaded over the network.)
*/
JSBool
JSJ_InitJSContext(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages)
{
/* Initialize the JavaScript classes used for reflection */
if (!jsj_init_JavaObject(cx, global_obj))
return JS_FALSE;
/* if (!jsj_init_JavaMember(cx, global_obj))
return JS_FALSE; */
if (!jsj_init_JavaPackage(cx, global_obj, predefined_packages))
return JS_FALSE;
if (!jsj_init_JavaClass(cx, global_obj))
return JS_FALSE;
if (!jsj_init_JavaArray(cx, global_obj))
return JS_FALSE;
if (!jsj_init_JavaMember(cx, global_obj))
return JS_FALSE;
return JS_TRUE;
}
/* Eliminate a reference to a Java class */
#define UNLOAD_CLASS(qualified_name, class) \
if (class) { \
(*jEnv)->DeleteGlobalRef(jEnv, class); \
class = NULL; \
}
/*
* This routine severs the connection to a Java VM, freeing all related resources.
* It shouldn't be called until the global scope has been cleared in all related
* JSContexts (so that all LiveConnect objects are finalized) and a JavaScript
* GC is performed. Otherwise, accessed to free'ed memory could result.
*/
void
JSJ_DisconnectFromJavaVM(JSJavaVM *jsjava_vm)
{
JNIEnv *jEnv;
SystemJavaVM *java_vm;
JSJavaVM *j, **jp;
java_vm = jsjava_vm->java_vm;
jEnv = jsjava_vm->main_thread_env;
/* Drop all references to Java objects and classes */
jsj_DiscardJavaObjReflections(jEnv);
jsj_DiscardJavaClassReflections(jEnv);
if (jsjava_vm->jsj_created_java_vm) {
(void)JSJ_callbacks->destroy_java_vm(java_vm, jEnv);
} else {
UNLOAD_CLASS(java/lang/Object, jlObject);
UNLOAD_CLASS(java/lang/Class, jlClass);
UNLOAD_CLASS(java/lang/reflect/Method, jlrMethod);
UNLOAD_CLASS(java/lang/reflect/Constructor, jlrConstructor);
UNLOAD_CLASS(java/lang/reflect/Field, jlrField);
UNLOAD_CLASS(java/lang/Throwable, jlThrowable);
UNLOAD_CLASS(java/lang/System, jlSystem);
UNLOAD_CLASS(java/lang/Boolean, jlBoolean);
UNLOAD_CLASS(java/lang/Double, jlDouble);
UNLOAD_CLASS(java/lang/String, jlString);
UNLOAD_CLASS(java/lang/Void, jlVoid);
UNLOAD_CLASS(netscape/javascript/JSObject, njJSObject);
UNLOAD_CLASS(netscape/javascript/JSException, njJSException);
UNLOAD_CLASS(netscape/javascript/JSUtil, njJSUtil);
}
/* Remove this VM from the list of all JSJavaVM objects. */
for (jp = &jsjava_vm_list; (j = *jp) != NULL; jp = &j->next) {
if (j == jsjava_vm) {
*jp = jsjava_vm->next;
break;
}
}
PR_ASSERT(j);
free(jsjava_vm);
}
static JSJavaThreadState *thread_list = NULL;
static JSJavaThreadState *
new_jsjava_thread_state(JSJavaVM *jsjava_vm, const char *thread_name, JNIEnv *jEnv)
{
JSJavaThreadState *jsj_env;
jsj_env = (JSJavaThreadState *)malloc(sizeof(JSJavaThreadState));
if (!jsj_env)
return NULL;
memset(jsj_env, 0, sizeof(JSJavaThreadState));
jsj_env->jEnv = jEnv;
jsj_env->jsjava_vm = jsjava_vm;
if (thread_name)
jsj_env->name = strdup(thread_name);
/* THREADSAFETY - need to protect against races */
jsj_env->next = thread_list;
thread_list = jsj_env;
return jsj_env;
}
static JSJavaThreadState *
find_jsjava_thread(JNIEnv *jEnv)
{
JSJavaThreadState *e, **p, *jsj_env;
jsj_env = NULL;
/* THREADSAFETY - need to protect against races in manipulating the thread list */
/* Search for the thread state among the list of all created
LiveConnect threads */
for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
if (e->jEnv == jEnv) {
jsj_env = e;
*p = jsj_env->next;
break;
}
}
/* Move a found thread to head of list for faster search next time. */
if (jsj_env)
thread_list = jsj_env;
return jsj_env;
}
PR_IMPLEMENT(JSJavaThreadState *)
JSJ_AttachCurrentThreadToJava(JSJavaVM *jsjava_vm, const char *name, JNIEnv **java_envp)
{
JNIEnv *jEnv;
SystemJavaVM *java_vm;
JSJavaThreadState *jsj_env;
/* Try to attach a Java thread to the current native thread */
java_vm = jsjava_vm->java_vm;
jEnv = JSJ_callbacks->attach_current_thread(java_vm);
if (jEnv == NULL)
return NULL;
/* If we found an existing thread state, just return it. */
jsj_env = find_jsjava_thread(jEnv);
if (jsj_env)
return jsj_env;
/* Create a new wrapper around the thread/VM state */
jsj_env = new_jsjava_thread_state(jsjava_vm, name, jEnv);
if (java_envp)
*java_envp = jEnv;
return jsj_env;
}
static JSJavaVM *
map_java_vm_to_jsjava_vm(SystemJavaVM *java_vm)
{
JSJavaVM *v;
for (v = jsjava_vm_list; v; v = v->next) {
if (v->java_vm == java_vm)
return v;
}
return NULL;
}
/*
* Unfortunately, there's no standard means to associate any private data with
* a JNI thread environment, so we need to use the Java environment pointer as
* the key in a lookup table that maps it to a JSJavaThreadState structure,
* where we store all our per-thread private data. If no existing thread state
* is found, a new one is created.
*
* If an error occurs, returns NULL and sets the errp argument to an error
* message, which the caller is responsible for free'ing.
*/
JSJavaThreadState *
jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp)
{
JSJavaThreadState *jsj_env;
SystemJavaVM *java_vm;
JSJavaVM *jsjava_vm;
/* If we found an existing thread state, just return it. */
jsj_env = find_jsjava_thread(jEnv);
if (jsj_env)
return jsj_env;
/* No one set up a LiveConnect thread state for a given Java thread.
Invoke the callback to create one on-the-fly. */
/* First, figure out which Java VM is calling us */
java_vm = JSJ_callbacks->get_java_vm(jEnv);
if (jsjava_vm == NULL)
return NULL;
/* Get our private JavaVM data */
jsjava_vm = map_java_vm_to_jsjava_vm(java_vm);
if (!jsjava_vm) {
*errp = PR_smprintf("Total weirdness: No JSJavaVM wrapper ever created "
"for JavaVM 0x%08x", java_vm);
return NULL;
}
jsj_env = new_jsjava_thread_state(jsjava_vm, NULL, jEnv);
if (!jsj_env)
return NULL;
return jsj_env;
}
/*
* This function is used to specify a particular JSContext as *the* JavaScript
* execution environment to be used when LiveConnect is accessed from the given
* Java thread, i.e. by using one of the methods of netscape.javascript.JSObject.
* (There can only be one such JS context for a given Java thread. To
* multiplex JSContexts among a single thread, this function must be called
* before Java is invoked on that thread.) The return value is the previous
* context associated with the given Java thread.
*/
PR_IMPLEMENT(JSContext *)
JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env)
{
JSContext *old_context;
old_context = jsj_env->cx;
jsj_env->cx = cx;
return old_context;
}
PR_IMPLEMENT(JSBool)
JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env)
{
SystemJavaVM *java_vm;
JNIEnv* jEnv;
JSJavaThreadState *e, **p;
/* Disassociate the current native thread from its corresponding Java thread */
java_vm = jsj_env->jsjava_vm->java_vm;
jEnv = jsj_env->jEnv;
if (!JSJ_callbacks->detach_current_thread(java_vm, jEnv))
return JS_FALSE;
/* Destroy the LiveConnect execution environment passed in */
jsj_ClearPendingJSErrors(jsj_env);
/* THREADSAFETY - need to protect against races */
for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
if (e == jsj_env) {
*p = jsj_env->next;
break;
}
}
free(jsj_env);
return JS_TRUE;
}
JSBool
JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp)
{
JNIEnv *jEnv;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, vp);
}
/*===========================================================================*/
#ifndef MOZILLA_CLIENT
/* The convenience functions below present a complete, but simplified
LiveConnect API which is designed to handle the special case of a single
Java-VM, single-threaded operation, and use of only one JSContext. */
/* We can get away with global variables in our single-threaded,
single-JSContext case. */
static JSJavaVM * the_jsj_vm = NULL;
static JSContext * the_cx = NULL;
static JSJavaThreadState * the_jsj_thread = NULL;
static JSObject * the_global_js_obj = NULL;
/* Trivial implementation of callback function */
static JSJavaThreadState *
default_map_js_context_to_jsj_thread(JSContext *cx, char **errp)
{
return the_jsj_thread;
}
/* Trivial implementation of callback function */
static JSContext *
default_map_jsj_thread_to_js_context(JSJavaThreadState *jsj_env, JNIEnv *jEnv, char **errp)
{
return the_cx;
}
/* Trivial implementation of callback function */
static JSObject *
default_map_java_object_to_js_object(JNIEnv *jEnv, jobject hint, char **errp)
{
return the_global_js_obj;
}
static PRBool PR_CALLBACK
default_create_java_vm(SystemJavaVM* *jvm, JNIEnv* *initialEnv, void* initargs)
{
jint err;
const char* user_classpath = (const char*)initargs;
/* No Java VM supplied, so create our own */
JDK1_1InitArgs vm_args;
/* Magic constant indicates JRE version 1.1 */
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Prepend the classpath argument to the default JVM classpath */
if (user_classpath) {
#ifdef XP_UNIX
const char *full_classpath = PR_smprintf("%s:%s", user_classpath, vm_args.classpath);
#else
const char *full_classpath = PR_smprintf("%s;%s", user_classpath, vm_args.classpath);
#endif
if (!full_classpath) {
return PR_FALSE;
}
vm_args.classpath = (char*)full_classpath;
}
err = JNI_CreateJavaVM((JavaVM**)jvm, initialEnv, &vm_args);
return err == 0;
}
static PRBool PR_CALLBACK
default_destroy_java_vm(SystemJavaVM* jvm, JNIEnv* initialEnv)
{
JavaVM* java_vm = (JavaVM*)jvm;
jint err = (*java_vm)->DestroyJavaVM(java_vm);
return err == 0;
}
static JNIEnv* PR_CALLBACK
default_attach_current_thread(SystemJavaVM* jvm)
{
JavaVM* java_vm = (JavaVM*)jvm;
JNIEnv* env = NULL;
jint err = (*java_vm)->AttachCurrentThread(java_vm, &env, NULL);
return env;
}
static PRBool PR_CALLBACK
default_detach_current_thread(SystemJavaVM* jvm, JNIEnv* env)
{
JavaVM* java_vm = (JavaVM*)jvm;
/* assert that env is the JNIEnv of the current thread */
jint err = (*java_vm)->DetachCurrentThread(java_vm);
return err == 0;
}
static SystemJavaVM* PR_CALLBACK
default_get_java_vm(JNIEnv* env)
{
JavaVM* java_vm = NULL;
jint err = (*env)->GetJavaVM(env, &java_vm);
return (SystemJavaVM*)java_vm;
}
/* Trivial implementations of callback functions */
JSJCallbacks jsj_default_callbacks = {
default_map_jsj_thread_to_js_context,
default_map_js_context_to_jsj_thread,
default_map_java_object_to_js_object,
NULL,
NULL,
NULL,
NULL,
default_create_java_vm,
default_destroy_java_vm,
default_attach_current_thread,
default_detach_current_thread,
default_get_java_vm
};
/*
* Initialize the provided JSContext by setting up the JS classes necessary for
* reflection and by defining JavaPackage objects for the default Java packages
* as properties of global_obj. If java_vm is NULL, a new Java VM is
* created, using the provided classpath in addition to any default classpath.
* The classpath argument is ignored, however, if java_vm is non-NULL.
*/
JSBool
JSJ_SimpleInit(JSContext *cx, JSObject *global_obj, SystemJavaVM *java_vm, const char *classpath)
{
JNIEnv *jEnv;
PR_ASSERT(!the_jsj_vm);
the_jsj_vm = JSJ_ConnectToJavaVM(java_vm, (void*)classpath);
if (!the_jsj_vm)
return JS_FALSE;
JSJ_Init(&jsj_default_callbacks);
if (!JSJ_InitJSContext(cx, global_obj, NULL))
goto error;
the_cx = cx;
the_global_js_obj = global_obj;
the_jsj_thread = JSJ_AttachCurrentThreadToJava(the_jsj_vm, "main thread", &jEnv);
if (!the_jsj_thread)
goto error;
JSJ_SetDefaultJSContextForJavaThread(cx, the_jsj_thread);
return JS_TRUE;
error:
JSJ_SimpleShutdown();
return JS_FALSE;
}
/*
* Free up all LiveConnect resources. Destroy the Java VM if it was
* created by LiveConnect.
*/
PR_IMPLEMENT(void)
JSJ_SimpleShutdown()
{
PR_ASSERT(the_jsj_vm);
JSJ_DisconnectFromJavaVM(the_jsj_vm);
the_jsj_vm = NULL;
the_cx = NULL;
the_global_js_obj = NULL;
the_jsj_thread = NULL;
}
#endif /* MOZILLA_CLIENT */

View File

@@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// Version stamp for this .DLL
#include <windows.h>
#include <ver.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4 // major, minor, release (alpha 1), build #
PRODUCTVERSION 4
FILEFLAGSMASK 0
FILEFLAGS 0 // final version
FILEOS VOS_DOS_WINDOWS16
FILETYPE VFT_DLL
FILESUBTYPE 0 // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
BEGIN
VALUE "CompanyName", "Netscape Communications Corporation\0"
VALUE "FileDescription", "Netscape 16-bit JavaScript-Java Module\0"
VALUE "FileVersion", "4.0\0"
VALUE "InternalName", "JSJ1640\0"
VALUE "LegalCopyright", "Copyright Netscape Communications. 1994-96\0"
VALUE "LegalTrademarks", "Netscape, Mozilla\0"
VALUE "OriginalFilename","JSJ1640.DLL\0"
VALUE "ProductName", "NETSCAPE\0"
VALUE "ProductVersion", "4.0\0"
END
END
END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,403 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the definition of the JavaScript JavaArray class.
* Instances of JavaArray are used to reflect Java arrays.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
/* Shorthands for ASCII (7-bit) decimal and hex conversion. */
#define JS7_ISDEC(c) (((c) >= '0') && ((c) <= '9'))
#define JS7_UNDEC(c) ((c) - '0')
/*
* Convert any jsval v to an integer jsval if ToString(v)
* contains a base-10 integer that fits into 31 bits.
* Otherwise return v.
*/
static jsval
try_convert_to_jsint(JSContext *cx, jsval idval)
{
const jschar *cp;
JSString *jsstr;
jsstr = JS_ValueToString(cx, idval);
if (!jsstr)
return idval;
cp = JS_GetStringChars(jsstr);
if (JS7_ISDEC(*cp)) {
jsuint index = JS7_UNDEC(*cp++);
jsuint oldIndex = 0;
jsuint c = 0;
if (index != 0) {
while (JS7_ISDEC(*cp)) {
oldIndex = index;
c = JS7_UNDEC(*cp);
index = 10*index + c;
cp++;
}
}
if (*cp == 0 &&
(oldIndex < (JSVAL_INT_MAX / 10) ||
(oldIndex == (JSVAL_INT_MAX / 10) && c < (JSVAL_INT_MAX % 10)))) {
return INT_TO_JSVAL(index);
}
}
return idval;
}
static JSBool
access_java_array_element(JSContext *cx,
JNIEnv *jEnv,
JSObject *obj,
jsid id,
jsval *vp,
JSBool do_assignment)
{
jsval idval;
jarray java_array;
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
jsize array_length, index;
JavaSignature *array_component_signature;
/* printf("In JavaArray_getProperty\n"); */
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
const char *property_name;
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval))) != NULL) {
if (!strcmp(property_name, "constructor")) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
}
JS_ReportError(cx, "illegal operation on JavaArray prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
java_array = java_wrapper->java_obj;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_ARRAY);
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_INT(idval))
idval = try_convert_to_jsint(cx, idval);
if (!JSVAL_IS_INT(idval)) {
/*
* Usually, properties of JavaArray objects are indexed by integers, but
* Java arrays also inherit all the methods of java.lang.Object, so a
* string-valued property is also possible.
*/
if (JSVAL_IS_STRING(idval)) {
const char *member_name;
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
if (do_assignment) {
JSVersion version = JS_GetVersion(cx);
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Attempt to write to invalid Java array "
"element \"%s\"", member_name);
return JS_FALSE;
} else {
*vp = JSVAL_VOID;
return JS_TRUE;
}
} else {
if (!strcmp(member_name, "length")) {
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
if (array_length < 0)
return JS_FALSE;
if (vp)
*vp = INT_TO_JSVAL(array_length);
return JS_TRUE;
}
/* Check to see if we're reflecting a Java array method */
return JavaObject_getPropertyById(cx, obj, id, vp);
}
}
JS_ReportError(cx, "invalid Java array index expression");
return JS_FALSE;
}
index = JSVAL_TO_INT(idval);
#if 0
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
if (array_length < 0)
return JS_FALSE;
/* Just let Java throw an exception instead of checking array bounds here */
if (index < 0 || index >= array_length) {
JS_ReportError(cx, "Java array index %d out of range", index);
return JS_FALSE;
}
#endif
array_component_signature = class_descriptor->array_component_signature;
if (!vp)
return JS_TRUE;
if (do_assignment) {
return jsj_SetJavaArrayElement(cx, jEnv, java_array, index,
array_component_signature, *vp);
} else {
return jsj_GetJavaArrayElement(cx, jEnv, java_array, index,
array_component_signature, vp);
}
}
PR_STATIC_CALLBACK(JSBool)
JavaArray_getPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_FALSE);
}
PR_STATIC_CALLBACK(JSBool)
JavaArray_setPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_TRUE);
}
static JSBool
JavaArray_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, NULL, JS_FALSE);
}
static JSBool
JavaArray_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Cannot define a new property in a JavaArray");
return JS_FALSE;
}
static JSBool
JavaArray_getAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
return JS_FALSE;
}
static JSBool
JavaArray_setAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaArray_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSVersion version = JS_GetVersion(cx);
*vp = JSVAL_FALSE;
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Properties of JavaArray objects may not be deleted");
return JS_FALSE;
} else {
/* Attempts to delete permanent properties are silently ignored
by ECMAScript. */
return JS_TRUE;
}
}
static JSBool
JavaArray_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaArray_defaultValue()\n"); */
return JavaObject_convert(cx, obj, JSTYPE_STRING, vp);
}
static JSBool
JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaObjectWrapper *java_wrapper;
JNIEnv *jEnv;
jsize array_length, index;
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
if (!java_wrapper) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_wrapper->java_obj);
if (array_length < 0)
return JS_FALSE;
switch(enum_op) {
case JSENUMERATE_INIT:
*statep = INT_TO_JSVAL(0);
if (idp)
*idp = INT_TO_JSVAL(array_length);
return JS_TRUE;
case JSENUMERATE_NEXT:
index = JSVAL_TO_INT(*statep);
if (index < array_length) {
JS_ValueToId(cx, INT_TO_JSVAL(index), idp);
index++;
*statep = INT_TO_JSVAL(index);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaArray_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaArray object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaArray object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
JSObjectOps JavaArray_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaArray_lookupProperty,
JavaArray_defineProperty,
JavaArray_getPropertyById, /* getProperty */
JavaArray_setPropertyById, /* setProperty */
JavaArray_getAttributes,
JavaArray_setAttributes,
JavaArray_deleteProperty,
JavaArray_defaultValue,
JavaArray_newEnumerate,
JavaArray_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL /* hasInstance */
};
static JSObjectOps *
JavaArray_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaArray_ops;
}
JSClass JavaArray_class = {
"JavaArray", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaObject_convert, JavaObject_finalize,
JavaArray_getObjectOps,
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
/* Initialize the JS JavaArray class */
JSBool
jsj_init_JavaArray(JSContext *cx, JSObject *global_obj)
{
JavaArray_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaArray_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
if (!JS_InitClass(cx, global_obj,
0, &JavaArray_class, 0, 0,
0, 0, 0, 0))
return JS_FALSE;
return JS_TRUE;
}

View File

@@ -0,0 +1,586 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaClass class.
*
* A JavaClass is JavaScript's representation of a Java class.
* Its parent JS object is always a JavaPackage object. A JavaClass is not an
* exact reflection of Java's corresponding java.lang.Class object. Rather,
* the properties of a JavaClass are the static methods and properties of the
* corresponding Java class.
*
* Note that there is no runtime equivalent to the JavaClass class in Java.
* (Although there are instances of java.lang.String and there are static
* methods of java.lang.String that can be invoked, there's no such thing as
* a first-class object that can be referenced simply as "java.lang.String".)
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
static JSBool
JavaClass_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
char *name;
JSString *str;
JavaClassDescriptor *class_descriptor;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return JS_FALSE;
switch(type) {
case JSTYPE_STRING:
/* Convert '/' to '.' so that it looks like Java language syntax. */
if (!class_descriptor->name)
break;
name = PR_smprintf("[JavaClass %s]", class_descriptor->name);
if (!name) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = JS_NewString(cx, name, strlen(name));
if (!str) {
free(name);
/* It's not necessary to call JS_ReportOutOfMemory(), as
JS_NewString() will do so on failure. */
return JS_FALSE;
}
*vp = STRING_TO_JSVAL(str);
return JS_TRUE;
default:
break;
}
return JS_TRUE;
}
static JSBool
lookup_static_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
JavaClassDescriptor **class_descriptorp,
jsid id, JavaMemberDescriptor **memberp)
{
jsval idval;
JavaMemberDescriptor *member_descriptor;
const char *member_name;
JavaClassDescriptor *class_descriptor;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor) {
*class_descriptorp = NULL;
*memberp = NULL;
return JS_TRUE;
}
if (class_descriptorp)
*class_descriptorp = class_descriptor;
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (!member_descriptor) {
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_STRING(idval)) {
JS_ReportError(cx, "invalid JavaClass property expression. "
"(methods and fields of a JavaClass object can only be identified by their name)");
return JS_FALSE;
}
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
/* Why do we have to do this ? */
if (!strcmp(member_name, "prototype")) {
*memberp = NULL;
return JS_TRUE;
}
JS_ReportError(cx, "Java class %s has no public static field or method named \"%s\"",
class_descriptor->name, member_name);
return JS_FALSE;
}
if (memberp)
*memberp = member_descriptor;
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jsval idval;
jclass java_class;
const char *member_name;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
JNIEnv *jEnv;
/* printf("In JavaClass_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
return JS_FALSE;
if (!member_descriptor) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
java_class = class_descriptor->java_class;
if (member_descriptor->field) {
if (!member_descriptor->methods) {
return jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp);
} else {
PR_ASSERT(0);
}
} else {
JSFunction *function;
/* TODO - eliminate JSFUN_BOUND_METHOD */
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0,
JSFUN_BOUND_METHOD, obj, member_name);
if (!function)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(function));
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
JavaClass_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jclass java_class;
const char *member_name;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
/* printf("In JavaClass_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
return JS_FALSE;
/* Check for the case where there is a method with the given name, but no field
with that name */
if (!member_descriptor->field)
goto no_such_field;
/* Silently fail if field value is final (immutable), as required by ECMA spec */
if (member_descriptor->field->modifiers & ACC_FINAL)
return JS_TRUE;
java_class = class_descriptor->java_class;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp);
no_such_field:
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
JS_ReportError(cx, "No static field named \"%s\" in Java class %s",
member_name, class_descriptor->name);
return JS_FALSE;
}
/*
* Free the private native data associated with the JavaPackage object.
*/
PR_STATIC_CALLBACK(void)
JavaClass_finalize(JSContext *cx, JSObject *obj)
{
JNIEnv *jEnv;
JavaClassDescriptor *class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
/* printf("Finalizing %s\n", class_descriptor->name); */
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
}
static JSBool
JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
JSErrorReporter old_reporter;
/* printf("In JavaClass_lookupProperty()\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
old_reporter = JS_SetErrorReporter(cx, NULL);
if (lookup_static_member_by_id(cx, jEnv, obj, NULL, id, NULL)) {
*objp = obj;
*propp = (JSProperty*)1;
} else {
*objp = NULL;
*propp = NULL;
}
JS_SetErrorReporter(cx, old_reporter);
return JS_TRUE;
}
static JSBool
JavaClass_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Cannot define a new property in a JavaClass");
return JS_FALSE;
}
static JSBool
JavaClass_getAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
return JS_FALSE;
}
static JSBool
JavaClass_setAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaClass_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSVersion version = JS_GetVersion(cx);
*vp = JSVAL_FALSE;
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Properties of JavaClass objects may not be deleted");
return JS_FALSE;
} else {
/* Attempts to delete permanent properties are silently ignored
by ECMAScript. */
return JS_TRUE;
}
}
static JSBool
JavaClass_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaClass_defaultValue()\n"); */
return JavaClass_convert(cx, obj, JSTYPE_STRING, vp);
}
static JSBool
JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
class_descriptor = JS_GetPrivate(cx, obj);
/* Check for prototype JavaClass object */
if (!class_descriptor) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
switch(enum_op) {
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
return JS_TRUE;
case JSENUMERATE_NEXT:
member_descriptor = JSVAL_TO_PRIVATE(*statep);
if (member_descriptor) {
*idp = member_descriptor->id;
*statep = PRIVATE_TO_JSVAL(member_descriptor->next);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaClass_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaClass object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaClass object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
/*
* Implement the JavaScript instanceof operator for JavaClass objects by using
* the equivalent Java instanceof operation.
*/
static JSBool
JavaClass_hasInstance(JSContext *cx, JSObject *obj, jsval candidate_jsval,
JSBool *has_instancep)
{
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
JSClass *js_class;
JSBool has_instance;
JSObject *candidate_obj;
jclass java_class;
jobject java_obj;
JNIEnv *jEnv;
has_instance = JS_FALSE;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor) {
JS_ReportError(cx, "illegal operation on JavaClass prototype object");
return JS_FALSE;
}
/*
* Make sure that the thing to the left of the instanceof operator is a
* Java object.
*/
if (!JSVAL_IS_OBJECT(candidate_jsval))
goto done;
candidate_obj = JSVAL_TO_OBJECT(candidate_jsval);
js_class = JS_GetClass(candidate_obj);
if ((js_class != &JavaObject_class) && (js_class != &JavaArray_class))
goto done;
java_class = class_descriptor->java_class;
java_wrapper = JS_GetPrivate(cx, candidate_obj);
if (!java_wrapper) {
JS_ReportError(cx, "illegal operation on prototype object");
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
/* Get JNI pointer */
jsj_MapJSContextToJSJThread(cx, &jEnv);
has_instance = (*jEnv)->IsInstanceOf(jEnv, java_obj, java_class);
done:
*has_instancep = has_instance;
return JS_TRUE;
}
JSObjectOps JavaClass_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaClass_lookupProperty,
JavaClass_defineProperty,
JavaClass_getPropertyById, /* getProperty */
JavaClass_setPropertyById, /* setProperty */
JavaClass_getAttributes,
JavaClass_setAttributes,
JavaClass_deleteProperty,
JavaClass_defaultValue,
JavaClass_newEnumerate,
JavaClass_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
jsj_JavaConstructorWrapper, /* call */
jsj_JavaConstructorWrapper, /* construct */
NULL, /* xdrObject */
JavaClass_hasInstance, /* hasInstance */
};
static JSObjectOps *
JavaClass_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaClass_ops;
}
JSClass JavaClass_class = {
"JavaClass", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaClass_convert, JavaClass_finalize,
JavaClass_getObjectOps,
};
static JSObject *
jsj_new_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
JavaClassDescriptor *class_descriptor)
{
JSObject *JavaClass_obj;
JavaClass_obj = JS_NewObject(cx, &JavaClass_class, 0, parent_obj);
if (!JavaClass_obj)
return NULL;
JS_SetPrivate(cx, JavaClass_obj, (void *)class_descriptor);
#ifdef DEBUG
/* printf("JavaClass \'%s\' created\n", class_descriptor->name); */
#endif
return JavaClass_obj;
}
JSObject *
jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
const char *simple_class_name,
jclass java_class)
{
JavaClassDescriptor *class_descriptor;
JSObject *JavaClass_obj;
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
return NULL;
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, parent_obj, class_descriptor);
if (!JavaClass_obj)
return NULL;
if (!JS_DefineProperty(cx, parent_obj, simple_class_name,
OBJECT_TO_JSVAL(JavaClass_obj), 0, 0,
JSPROP_PERMANENT|JSPROP_READONLY|JSPROP_ENUMERATE))
return NULL;
return JavaClass_obj;
}
/*
* The getClass() native JS method is defined as a property of the global
* object. Given a JavaObject it returns the corresponding JavaClass. This
* is useful for accessing static methods and fields.
*
* js> getClass(new java.lang.String("foo"))
* [JavaClass java.lang.String]
*/
static JSBool
getClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSObject *obj_arg, *JavaClass_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (argc != 1 ||
!JSVAL_IS_OBJECT(argv[0]) ||
!(obj_arg = JSVAL_TO_OBJECT(argv[0])) ||
(!JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) &&
!JS_InstanceOf(cx, obj_arg, &JavaArray_class, 0))) {
JS_ReportError(cx, "getClass expects a Java object argument");
return JS_FALSE;
}
java_wrapper = JS_GetPrivate(cx, obj_arg);
if (!java_wrapper) {
JS_ReportError(cx, "getClass called on prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
if (!JavaClass_obj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(JavaClass_obj);
return JS_TRUE;
}
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaClass(JSContext *cx, JSObject *global_obj)
{
JavaClass_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaClass_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
/* Define JavaClass class */
if (!JS_InitClass(cx, global_obj, 0, &JavaClass_class, 0, 0, 0, 0, 0, 0))
return JS_FALSE;
if (!JS_DefineFunction(cx, global_obj, "getClass", getClass, 0,
JSPROP_READONLY))
return JS_FALSE;
return jsj_InitJavaClassReflectionsTable();
}

View File

@@ -0,0 +1,134 @@
/* -*- Mode: C; tab-width: 8 -*-
* Copyright (C) 1998 Netscape Communications Corporation, All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaMember class.
* JavaMember's are a strange beast required only to handle the special case
* of a public field and a public method that appear in the same class and
* have the same name. When such a field/method is used in Java, the compiler
* can statically determine from context whether the field or the method is
* being referenced, but that is not possible with JavaScript. For example:
*
* ambiguousVal = javaObj.fieldOrMethod; // ambiguousVal is a JavaMember object
* a = ambiguousVal(); // ambiguousVal used as a method value
* b = ambiguousVal + 4; // ambiguousVal used as a field value
*
* A JavaMember instance carries both the captured value of the Java field and
* the method value until the context that the value is to be used in is known,
* at which point conversion forces the use of one or the other.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
/* Private, native portion of a JavaMember */
typedef struct JavaMethodOrFieldValue {
jsval method_val;
jsval field_val;
} JavaMethodOrFieldValue;
JSObject *
jsj_CreateJavaMember(JSContext *cx, jsval method_val, jsval field_val)
{
JavaMethodOrFieldValue *member_val;
JSObject *JavaMember_obj;
member_val = (JavaMethodOrFieldValue *)JS_malloc(cx, sizeof(*member_val));
if (!member_val)
return NULL;
JavaMember_obj = JS_NewObject(cx, &JavaMember_class, 0, 0);
if (!JavaMember_obj) {
JS_free(cx, member_val);
return NULL;
}
JS_SetPrivate(cx, JavaMember_obj, (void *)member_val);
member_val->method_val = method_val;
JS_AddRoot(cx, &member_val->method_val);
member_val->field_val = field_val;
if (JSVAL_IS_GCTHING(field_val))
JS_AddRoot(cx, &member_val->field_val);
return JavaMember_obj;
}
static void
JavaMember_finalize(JSContext *cx, JSObject *obj)
{
JavaMethodOrFieldValue *member_val;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
member_val = JS_GetPrivate(cx, obj);
if (!member_val)
return;
JS_RemoveRoot(cx, &member_val->method_val);
if (JSVAL_IS_GCTHING(member_val->method_val))
JS_RemoveRoot(cx, &member_val->method_val);
JS_free(cx, member_val);
}
static JSBool
JavaMember_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JavaMethodOrFieldValue *member_val;
member_val = JS_GetPrivate(cx, obj);
if (!member_val) {
if (type == JSTYPE_OBJECT) {
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
switch (type) {
case JSTYPE_VOID:
case JSTYPE_STRING:
case JSTYPE_NUMBER:
case JSTYPE_BOOLEAN:
case JSTYPE_OBJECT:
*vp = member_val->field_val;
return JS_TRUE;
case JSTYPE_FUNCTION:
*vp = member_val->method_val;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
JSClass JavaMember_class = {
"JavaMember", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JavaMember_convert, JavaMember_finalize
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaMember(JSContext *cx, JSObject *global_obj)
{
if (!JS_InitClass(cx, global_obj,
0, &JavaMember_class, 0, 0,
0, 0,
0, 0))
return JS_FALSE;
return JS_TRUE;
}

View File

@@ -0,0 +1,686 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaObject class.
*
* An instance of JavaObject is the JavaScript reflection of a Java object.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsj_hash.h" /* Hash table with Java object as key */
/*
* This is a hash table that maps from Java objects to JS objects.
* It is used to ensure that the same JS object is obtained when a Java
* object is reflected more than once, so that JS object equality tests
* work in the expected manner, i.e. the "==" and "===" operators.
*
* The table entry keys are Java objects (of type jobject) and the entry values
* are JSObject pointers. Because the jobject type is an opaque handle and
* not necessarily a pointer, the hashing and key comparison functions must
* invoke the appropriate JVM functions.
*
* When the corresponding JS object instance is finalized, the entry is
* removed from the table, and a Java GC root for the Java object is removed.
*/
static JSJHashTable *java_obj_reflections = NULL;
#ifdef JS_THREADSAFE
static PRMonitor *java_obj_reflections_monitor = NULL;
#endif
static JSBool
init_java_obj_reflections_table()
{
java_obj_reflections =
JSJ_NewHashTable(512, jsj_HashJavaObject, jsj_JavaObjectComparator,
NULL, NULL, NULL);
if (!java_obj_reflections)
return JS_FALSE;
#ifdef JS_THREADSAFE
java_obj_reflections_monitor = PR_NewNamedMonitor("java_obj_reflections");
if (!java_obj_reflections_monitor) {
PR_HashTableDestroy(java_obj_reflections);
return JS_FALSE;
}
#endif
return JS_TRUE;
}
JSObject *
jsj_WrapJavaObject(JSContext *cx,
JNIEnv *jEnv,
jobject java_obj,
jclass java_class)
{
JSJHashNumber hash_code;
JSClass *js_class;
JSObject *js_wrapper_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JSJHashEntry *he, **hep;
js_wrapper_obj = NULL;
hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);
#ifdef JS_THREADSAFE
PR_EnterMonitor(java_obj_reflections_monitor);
#endif
hep = JSJ_HashTableRawLookup(java_obj_reflections,
hash_code, java_obj, (void*)jEnv);
he = *hep;
if (he) {
js_wrapper_obj = (JSObject *)he->value;
if (js_wrapper_obj)
goto done;
}
/* No existing reflection found. Construct a new one */
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
goto done;
if (class_descriptor->type == JAVA_SIGNATURE_ARRAY) {
js_class = &JavaArray_class;
} else {
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS);
js_class = &JavaObject_class;
}
/* Create new JS object to reflect Java object */
js_wrapper_obj = JS_NewObject(cx, js_class, NULL, NULL);
if (!js_wrapper_obj)
goto done;
/* Create private, native portion of JavaObject */
java_wrapper =
(JavaObjectWrapper *)JS_malloc(cx, sizeof(JavaObjectWrapper));
if (!java_wrapper) {
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
goto done;
}
JS_SetPrivate(cx, js_wrapper_obj, java_wrapper);
java_wrapper->class_descriptor = class_descriptor;
java_obj = (*jEnv)->NewGlobalRef(jEnv, java_obj);
java_wrapper->java_obj = java_obj;
if (!java_obj)
goto out_of_memory;
/* Add the JavaObject to the hash table */
he = JSJ_HashTableRawAdd(java_obj_reflections, hep, hash_code,
java_obj, js_wrapper_obj, (void*)jEnv);
if (!he) {
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
goto out_of_memory;
}
done:
#ifdef JS_THREADSAFE
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
return js_wrapper_obj;
out_of_memory:
/* No need to free js_wrapper_obj, as it will be finalized by GC. */
JS_ReportOutOfMemory(cx);
js_wrapper_obj = NULL;
goto done;
}
static void
remove_java_obj_reflection_from_hashtable(jobject java_obj, JNIEnv *jEnv)
{
JSJHashNumber hash_code;
JSJHashEntry *he, **hep;
hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);
#ifdef JS_THREADSAFE
PR_EnterMonitor(java_obj_reflections_monitor);
#endif
hep = JSJ_HashTableRawLookup(java_obj_reflections, hash_code,
java_obj, (void*)jEnv);
he = *hep;
PR_ASSERT(he);
if (he)
JSJ_HashTableRawRemove(java_obj_reflections, hep, he, (void*)jEnv);
#ifdef JS_THREADSAFE
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
}
void
JavaObject_finalize(JSContext *cx, JSObject *obj)
{
JavaObjectWrapper *java_wrapper;
jobject java_obj;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper)
return;
java_obj = java_wrapper->java_obj;
if (java_obj) {
remove_java_obj_reflection_from_hashtable(java_obj, jEnv);
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
}
jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor);
JS_free(cx, java_wrapper);
}
/* Trivial helper for jsj_DiscardJavaObjReflections(), below */
static PRIntn
enumerate_remove_java_obj(JSJHashEntry *he, PRIntn i, void *arg)
{
JNIEnv *jEnv = (JNIEnv*)arg;
jobject java_obj;
JavaObjectWrapper *java_wrapper;
JSObject *java_wrapper_obj;
java_wrapper_obj = (JSObject *)he->value;
java_wrapper = JS_GetPrivate(NULL, java_wrapper_obj);
java_obj = java_wrapper->java_obj;
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
java_wrapper->java_obj = NULL;
return HT_ENUMERATE_REMOVE;
}
/* This shutdown routine discards all JNI references to Java objects
that have been reflected into JS, even if there are still references
to them from JS. */
void
jsj_DiscardJavaObjReflections(JNIEnv *jEnv)
{
if (java_obj_reflections) {
JSJ_HashTableEnumerateEntries(java_obj_reflections,
enumerate_remove_java_obj,
(void*)jEnv);
JSJ_HashTableDestroy(java_obj_reflections);
java_obj_reflections = NULL;
}
}
PR_CALLBACK JSBool
JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
jobject java_obj;
JNIEnv *jEnv;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
if (type == JSTYPE_OBJECT) {
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
class_descriptor = java_wrapper->class_descriptor;
switch (type) {
case JSTYPE_OBJECT:
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
case JSTYPE_FUNCTION:
JS_ReportError(cx, "can't convert Java object to function");
return JS_FALSE;
case JSTYPE_VOID:
case JSTYPE_STRING:
/* Either extract a C-string from the java.lang.String object
or call the Java toString() method */
return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, vp);
case JSTYPE_NUMBER:
/* Call Java doubleValue() method, if applicable */
return jsj_ConvertJavaObjectToJSNumber(cx, jEnv, class_descriptor, java_obj, vp);
case JSTYPE_BOOLEAN:
/* Call booleanValue() method, if applicable */
return jsj_ConvertJavaObjectToJSBoolean(cx, jEnv, class_descriptor, java_obj, vp);
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
lookup_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
JavaObjectWrapper **java_wrapperp,
jsid id,
JavaMemberDescriptor **member_descriptorp)
{
jsval idval;
JavaObjectWrapper *java_wrapper;
JavaMemberDescriptor *member_descriptor;
const char *member_name, *property_name;
JavaClassDescriptor *class_descriptor;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval))) != NULL) {
if (!strcmp(property_name, "constructor")) {
*java_wrapperp = NULL;
*member_descriptorp = NULL;
return JS_TRUE;
}
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS ||
class_descriptor->type == JAVA_SIGNATURE_ARRAY);
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (!member_descriptor) {
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_STRING(idval)) {
JS_ReportError(cx, "invalid JavaObject property expression. "
"(methods and field properties of a JavaObject object can only be strings)");
return JS_FALSE;
}
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
JS_ReportError(cx, "Java class %s has no public instance field or "
"method named \"%s\"",
class_descriptor->name, member_name);
return JS_FALSE;
}
/* Success. Handle the multiple return values */
if (java_wrapperp)
*java_wrapperp = java_wrapper;
if (member_descriptorp)
*member_descriptorp = member_descriptor;
return JS_TRUE;
}
PR_CALLBACK JSBool
JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jobject java_obj;
JavaMemberDescriptor *member_descriptor;
JavaObjectWrapper *java_wrapper;
JNIEnv *jEnv;
JSObject *funobj;
jsval field_val, method_val;
JSBool success;
/* printf("In JavaObject_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor))
return JS_FALSE;
/* Handle access to "constructor" property of prototype object with
silent failure. */
if (!member_descriptor) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
java_obj = java_wrapper->java_obj;
field_val = method_val = JSVAL_VOID;
/* If a field member, get the value of the field */
if (member_descriptor->field) {
success = jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, &field_val);
if (!success)
return JS_FALSE;
}
/* If a method member, build a wrapper around the Java method */
if (member_descriptor->methods) {
/* Create a function object with this JavaObject as its parent, so that
JSFUN_BOUND_METHOD binds it as the default 'this' for the function. */
funobj = JS_CloneFunctionObject(cx, member_descriptor->invoke_func_obj, obj);
if (!funobj)
return JS_FALSE;
method_val = OBJECT_TO_JSVAL(funobj);
}
#if TEST_JAVAMEMBER
/* Always create a JavaMember object, even though it's inefficient */
obj = jsj_CreateJavaMember(cx, method_val, field_val);
if (!obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(obj);
#else /* !TEST_JAVAMEMBER */
if (member_descriptor->field) {
if (!member_descriptor->methods) {
/* Return value of Java field */
*vp = field_val;
} else {
/* Handle special case of access to a property that could refer
to either a Java field or a method that share the same name.
In Java, such ambiguity is not possible because the compiler
can statically determine which is being accessed. */
obj = jsj_CreateJavaMember(cx, method_val, field_val);
if (!obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(obj);
}
} else {
/* Return wrapper around Java method */
*vp = method_val;
}
#endif /* !TEST_JAVAMEMBER */
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
JavaObject_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jobject java_obj;
const char *member_name;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
/* printf("In JavaObject_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor))
return JS_FALSE;
/* Check for the case where there is a method with the give name, but no field
with that name */
if (!member_descriptor->field)
goto no_such_field;
/* Silently fail if field value is final (immutable), as required by ECMA spec */
if (member_descriptor->field->modifiers & ACC_FINAL)
return JS_TRUE;
java_obj = java_wrapper->java_obj;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, *vp);
no_such_field:
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
class_descriptor = java_wrapper->class_descriptor;
JS_ReportError(cx, "No instance field named \"%s\" in Java class %s",
member_name, class_descriptor->name);
return JS_FALSE;
}
static JSBool
JavaObject_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
JSErrorReporter old_reporter;
/* printf("In JavaObject_lookupProperty()\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
old_reporter = JS_SetErrorReporter(cx, NULL);
if (lookup_member_by_id(cx, jEnv, obj, NULL, id, NULL)) {
*objp = obj;
*propp = (JSProperty*)1;
} else {
*objp = NULL;
*propp = NULL;
}
JS_SetErrorReporter(cx, old_reporter);
return JS_TRUE;
}
static JSBool
JavaObject_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Cannot define a new property in a JavaObject");
return JS_FALSE;
}
static JSBool
JavaObject_getAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
return JS_FALSE;
}
static JSBool
JavaObject_setAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaObject_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSVersion version = JS_GetVersion(cx);
*vp = JSVAL_FALSE;
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Properties of JavaObject objects may not be deleted");
return JS_FALSE;
} else {
/* Attempts to delete permanent properties are silently ignored
by ECMAScript. */
return JS_TRUE;
}
}
static JSBool
JavaObject_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaObject_defaultValue()\n"); */
return JavaObject_convert(cx, obj, type, vp);
}
static JSBool
JavaObject_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaObjectWrapper *java_wrapper;
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
if (!java_wrapper) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
class_descriptor = java_wrapper->class_descriptor;
switch(enum_op) {
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
return JS_TRUE;
case JSENUMERATE_NEXT:
member_descriptor = JSVAL_TO_PRIVATE(*statep);
if (member_descriptor) {
*idp = member_descriptor->id;
*statep = PRIVATE_TO_JSVAL(member_descriptor->next);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaObject_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaObject object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaObject object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
JSObjectOps JavaObject_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaObject_lookupProperty,
JavaObject_defineProperty,
JavaObject_getPropertyById, /* getProperty */
JavaObject_setPropertyById, /* setProperty */
JavaObject_getAttributes,
JavaObject_setAttributes,
JavaObject_deleteProperty,
JavaObject_defaultValue,
JavaObject_newEnumerate,
JavaObject_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
};
static JSObjectOps *
JavaObject_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaObject_ops;
}
JSClass JavaObject_class = {
"JavaObject", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaObject_convert, JavaObject_finalize,
JavaObject_getObjectOps,
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaObject(JSContext *cx, JSObject *global_obj)
{
JavaObject_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaObject_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
if (!JS_InitClass(cx, global_obj,
0, &JavaObject_class, 0, 0,
0, 0,
0, 0))
return JS_FALSE;
return init_java_obj_reflections_table();
}

View File

@@ -0,0 +1,500 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of the JavaPackage class.
*
* A JavaPackage is JavaScript's representation of a Java package. The
* JavaPackage object contains only a string, which is the path to the package,
* e.g. "java/lang". The JS properties of a JavaPackage are either nested packages
* or a JavaClass object, which represents the path to a Java class.
*
* Note that there is no equivalent to a JavaPackage object in Java. Example:
* Although there are instances of java.lang.String and there are static methods
* of java.lang.String that can be invoked, there's no such thing as a java.lang
* object in Java that exists at run time.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h"
JSClass JavaPackage_class; /* Forward declaration */
/*
* The native part of a JavaPackage object. It gets stored in the object's
* private slot.
*/
typedef struct {
const char * path; /* e.g. "java/lang" or NULL if top level package */
int flags; /* e.g. PKG_SYSTEM, PKG_CLASS */
} JavaPackage_Private;
static JSObject *
define_JavaPackage(JSContext *cx, JSObject *parent_obj,
const char *obj_name, const char *path, int flags)
{
JSObject *package_obj;
JavaPackage_Private *package;
package_obj = JS_DefineObject(cx, parent_obj, obj_name, &JavaPackage_class, 0,
JSPROP_PERMANENT | JSPROP_READONLY);
if (!package_obj)
return NULL;
/* Attach private, native data to the JS object */
package = (JavaPackage_Private *)JS_malloc(cx, sizeof(JavaPackage_Private));
JS_SetPrivate(cx, package_obj, (void *)package);
if (path)
package->path = JS_strdup(cx, path);
else
package->path = "";
package->flags = flags;
/* Check for OOM */
if (!package->path) {
JS_DeleteProperty(cx, parent_obj, obj_name);
JS_free(cx, package);
return NULL;
}
return package_obj;
}
/* JavaPackage uses standard JS getProperty */
/*
* Don't allow user-defined properties to be set on Java package objects, e.g.
* it is illegal to write "java.lang.myProperty = 4". We probably could relax
* this restriction, but it's potentially confusing and not clearly useful.
*/
static JSBool
JavaPackage_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
{
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
if (!package) {
JS_ReportError(cx, "illegal attempt to add property to "
"JavaPackage prototype object");
return JS_FALSE;
}
JS_ReportError(cx, "You may not add properties to a JavaPackage object");
return JS_FALSE;
}
static JSBool quiet_resolve_failure;
/*
* Resolve a component name to be either the name of a class or a package.
*/
static JSBool
JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id)
{
JavaPackage_Private *package;
JSBool ok = JS_TRUE;
jclass jclazz;
char *subPath, *newPath;
const char *path;
JNIEnv *jEnv;
package = (JavaPackage_Private *)JS_GetPrivate(cx, obj);
if (!package)
return JS_TRUE;
if (!JSVAL_IS_STRING(id))
return JS_TRUE;
subPath = JS_GetStringBytes(JSVAL_TO_STRING(id));
/*
* There will be an attempt to invoke the toString() method when producing
* the string representation of a JavaPackage. When this occurs, avoid
* creating a bogus toString package. (This means that no one can ever
* create a package with the simple name "toString", but we'll live with
* that limitation.)
*/
if (!strcmp(subPath, "toString"))
return JS_FALSE;
path = package->path;
newPath = PR_smprintf("%s%s%s", path, (path[0] ? "/" : ""), subPath);
if (!newPath) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
/*
Unfortunately, Java provides no way to find out whether a particular
name is a package or not. The only way to tell is to try to load the
name as a class file and, if that fails, assume it's a package. This
makes things work as expected for the most part, but it has three
noticeable problems that keep coming up:
- You can refer to a package like java.lang.i.buried.paul without
generating a complaint. Of course, you'll never be able to refer to
any classes through it.
- An annoying consequence of the above is that misspelling a class name
results in a cryptic error about packages.
- In a browser context, i.e. where applets are involved, figuring out
whether something is a class may require looking for it over the net
using the current classloader. This means that the first time you
refer to java.lang.System in a js context, there will be an attempt
to search for [[DOCBASE]]/java.class on the server.
A solution is to explicitly tell jsjava the names of all the (local)
packages on the CLASSPATH. (Not implemented yet.)
*/
jclazz = (*jEnv)->FindClass(jEnv, newPath);
if (jclazz) {
JSObject *newClass;
newClass = jsj_define_JavaClass(cx, jEnv, obj, subPath, jclazz);
if (!newClass) {
ok = JS_FALSE;
goto out;
}
} else {
/* We assume that any failed attempt to load a class is because it
doesn't exist. If we wanted to do a better job, we would check
the exception type and make sure that it's NoClassDefFoundError */
(*jEnv)->ExceptionClear(jEnv);
/* beard: this has to be done here, so built-in classes will be defined. */
/* Painful hack for pre_define_java_packages() */
if (quiet_resolve_failure) {
ok = JS_FALSE;
goto out;
}
/*
* If there's no class of the given name, then we must be referring to
* a package. However, don't allow bogus sub-packages of pre-defined
* system packages to be created.
*/
if (JS_InstanceOf(cx, obj, &JavaPackage_class, NULL)) {
JavaPackage_Private *package;
package = JS_GetPrivate(cx, obj);
if (package->flags & PKG_SYSTEM) {
char *msg, *cp;
msg = PR_smprintf("No Java system package with name \"%s\" was identified "
"and no Java class with that name exists either",
newPath);
/* Check for OOM */
if (msg) {
/* Convert package of form "java/lang" to "java.lang" */
for (cp = msg; *cp != '\0'; cp++)
if (*cp == '/')
*cp = '.';
JS_ReportError(cx, msg);
free((char*)msg);
}
ok = JS_FALSE;
goto out;
}
}
if (!define_JavaPackage(cx, obj, subPath, newPath, 0)) {
ok = JS_FALSE;
goto out;
}
#ifdef DEBUG
/* printf("JavaPackage \'%s\' created\n", newPath); */
#endif
}
out:
free(newPath);
return ok;
}
static JSBool
JavaPackage_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JSString *str;
char *name, *cp;
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
if (!package) {
fprintf(stderr, "JavaPackage_resolve: no private data!\n");
return JS_FALSE;
}
switch (type) {
/* Pretty-printing of JavaPackage */
case JSTYPE_VOID: /* Default value */
case JSTYPE_NUMBER:
case JSTYPE_STRING:
/* Convert '/' to '.' so that it looks like Java language syntax. */
if (!package->path)
break;
name = PR_smprintf("[JavaPackage %s]", package->path);
if (!name) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
for (cp = name; *cp != '\0'; cp++)
if (*cp == '/')
*cp = '.';
str = JS_NewString(cx, name, strlen(name));
if (!str) {
free(name);
/* It's not necessary to call JS_ReportOutOfMemory(), as
JS_NewString() will do so on failure. */
return JS_FALSE;
}
*vp = STRING_TO_JSVAL(str);
break;
case JSTYPE_OBJECT:
*vp = OBJECT_TO_JSVAL(obj);
break;
default:
break;
}
return JS_TRUE;
}
/*
* Free the private native data associated with the JavaPackage object.
*/
static void
JavaPackage_finalize(JSContext *cx, JSObject *obj)
{
JavaPackage_Private *package = JS_GetPrivate(cx, obj);
if (!package)
return;
if (package->path)
JS_free(cx, (char *)package->path);
JS_free(cx, package);
}
/*
* The definition of the JavaPackage class
*/
JSClass JavaPackage_class = {
"JavaPackage", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JavaPackage_setProperty,
JS_EnumerateStub, JavaPackage_resolve,
JavaPackage_convert, JavaPackage_finalize
};
JavaPackageDef
standard_java_packages[] = {
{"java", NULL, PKG_USER},
{"java.applet", NULL, PKG_USER},
{"java.awt", NULL, PKG_USER},
{"java.awt.datatransfer",
NULL, PKG_SYSTEM},
{"java.awt.event", NULL, PKG_SYSTEM},
{"java.awt.image", NULL, PKG_SYSTEM},
{"java.awt.peer", NULL, PKG_SYSTEM},
{"java.beans", NULL, PKG_USER},
{"java.io", NULL, PKG_SYSTEM},
{"java.lang", NULL, PKG_USER},
{"java.lang.reflect", NULL, PKG_SYSTEM},
{"java.math", NULL, PKG_SYSTEM},
{"java.net", NULL, PKG_USER},
{"java.rmi", NULL, PKG_USER},
{"java.rmi.dgc", NULL, PKG_USER},
{"java.rmi.user", NULL, PKG_USER},
{"java.rmi.registry", NULL, PKG_USER},
{"java.rmi.server", NULL, PKG_USER},
{"java.security", NULL, PKG_USER},
{"java.security.acl", NULL, PKG_SYSTEM},
{"java.security.interfaces",
NULL, PKG_SYSTEM},
{"java.sql", NULL, PKG_USER},
{"java.text", NULL, PKG_USER},
{"java.text.resources", NULL, PKG_SYSTEM},
{"java.util", NULL, PKG_USER},
{"java.util.zip", NULL, PKG_SYSTEM},
{"netscape", NULL, PKG_USER},
{"netscape.applet", NULL, PKG_SYSTEM},
{"netscape.application",NULL, PKG_SYSTEM},
{"netscape.debug", NULL, PKG_SYSTEM},
{"netscape.javascript", NULL, PKG_SYSTEM},
{"netscape.ldap", NULL, PKG_SYSTEM},
{"netscape.misc", NULL, PKG_SYSTEM},
{"netscape.net", NULL, PKG_SYSTEM},
{"netscape.plugin", NULL, PKG_SYSTEM},
{"netscape.util", NULL, PKG_SYSTEM},
{"netscape.secfile", NULL, PKG_SYSTEM},
{"netscape.security", NULL, PKG_SYSTEM},
{"netscape.WAI", NULL, PKG_SYSTEM},
{"sun", NULL, PKG_USER},
{"Packages", "", PKG_USER},
{NULL, NULL, 0}
};
/*
* Pre-define a hierarchy of JavaPackage objects.
* Pre-defining a Java package at initialization time is not necessary, but
* it will make package lookup faster and, more importantly, will avoid
* unnecessary network accesses if classes are being loaded over the network.
*/
static JSBool
pre_define_java_packages(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages)
{
JSBool package_exists;
JSObject *parent_obj;
JavaPackageDef *package_def;
char *simple_name, *cp, *package_name, *path;
int flags;
if (!predefined_packages)
return JS_TRUE;
/* Iterate over all pre-defined Java packages */
for (package_def = predefined_packages; package_def->name; package_def++) {
package_name = path = NULL;
parent_obj = global_obj;
package_name = strdup(package_def->name);
if (!package_name)
goto out_of_memory;
/* Walk the chain of JavaPackage objects to get to the parent of the
rightmost sub-package in the fully-qualified package name. */
for (simple_name = strtok(package_name, "."); 1; simple_name = strtok(NULL, ".")) {
jsval v;
if (!simple_name) {
JS_ReportError(cx, "Package %s defined twice ?", package_name);
goto error;
}
/* Check to see if the sub-package already exists */
quiet_resolve_failure = JS_TRUE;
package_exists = JS_LookupProperty(cx, parent_obj, simple_name, &v) && JSVAL_IS_OBJECT(v);
quiet_resolve_failure = JS_FALSE;
if (package_exists) {
parent_obj = JSVAL_TO_OBJECT(v);
continue;
} else {
/* New package objects should only be created at the terminal
sub-package in a fully-qualified package-name */
if (strtok(NULL, ".")) {
JS_ReportError(cx, "Illegal predefined package definition for %s",
package_def->name);
goto error;
}
if (package_def->path) {
path = strdup(package_def->path);
if (!path)
goto out_of_memory;
} else {
/*
* The default path is specified, so create it from the
* fully-qualified package name.
*/
path = strdup(package_def->name);
if (!path)
goto out_of_memory;
/* Transform package name, e.g. "java.lang" ==> "java/lang" */
for (cp = path; *cp != '\0'; cp++) {
if (*cp == '.')
*cp = '/';
}
}
flags = package_def->flags;
parent_obj = define_JavaPackage(cx, parent_obj, simple_name, path, flags);
if (!parent_obj)
goto error;
free(path);
break;
}
}
free(package_name);
}
return JS_TRUE;
out_of_memory:
JS_ReportOutOfMemory(cx);
error:
JS_FREE_IF(cx, package_name);
JS_FREE_IF(cx, path);
return JS_FALSE;
}
static JSBool
JavaPackage_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval)
{
if (!JS_InstanceOf(cx, obj, &JavaPackage_class, argv))
return JS_FALSE;
return JavaPackage_convert(cx, obj, JSTYPE_STRING, rval);
}
static JSFunctionSpec JavaPackage_methods[] = {
{"toString", JavaPackage_toString, 0},
{0}
};
/*
* One-time initialization for the JavaPackage class. (This is not
* run once per thread, rather it's run once for a given JSContext.)
*/
JSBool
jsj_init_JavaPackage(JSContext *cx, JSObject *global_obj,
JavaPackageDef *additional_predefined_packages) {
/* Define JavaPackage class */
if (!JS_InitClass(cx, global_obj, 0, &JavaPackage_class,
0, 0, 0, JavaPackage_methods, 0, 0))
return JS_FALSE;
/* Add top-level packages, e.g. : java, netscape, sun */
if (!pre_define_java_packages(cx, global_obj, standard_java_packages))
return JS_FALSE;
if (!pre_define_java_packages(cx, global_obj, additional_predefined_packages))
return JS_FALSE;
return JS_TRUE;
}

View File

@@ -0,0 +1,180 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code for reading and writing elements of a Java array.
*/
#include "jsj_private.h" /* LiveConnect internals */
/*
* Read the Java value at a given index into a Java array and convert it
* to a JS value. The array_component_signature describes the type of
* the resulting Java value, which can be a primitive type or an object type.
* More specifically it can be an array type in the case of multidimensional
* arrays.
*/
JSBool
jsj_GetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array, jsize index,
JavaSignature *array_component_signature,
jsval *vp)
{
jvalue java_value;
JavaSignatureChar component_type;
#define GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Type,member) \
(*jEnv)->Get##Type##ArrayRegion(jEnv, java_array, index, 1, \
&java_value.member); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error reading element of " \
"Java primitive array"); \
return JS_FALSE; \
}
component_type = array_component_signature->type;
switch(component_type) {
case JAVA_SIGNATURE_BYTE:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Short,s);
break;
case JAVA_SIGNATURE_INT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
java_value.l = (*jEnv)->GetObjectArrayElement(jEnv, java_array, index);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_ReportJavaError(cx, jEnv, "Error reading Java object array");
return JS_FALSE;
}
break;
#undef GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return jsj_ConvertJavaValueToJSValue(cx, jEnv, array_component_signature, &java_value, vp);
}
JSBool
jsj_SetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array, jsize index,
JavaSignature *array_component_signature,
jsval js_val)
{
int dummy_cost;
jvalue java_value;
JavaSignatureChar component_type;
JSBool is_local_ref;
if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, array_component_signature,
&dummy_cost, &java_value, &is_local_ref))
return JS_FALSE;
#define SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Type,member) \
(*jEnv)->Set##Type##ArrayRegion(jEnv, java_array, index, 1, \
&java_value.member); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error assigning to element of " \
"Java primitive array"); \
return JS_FALSE; \
}
component_type = array_component_signature->type;
switch(component_type) {
case JAVA_SIGNATURE_BYTE:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Short,s);
break;
case JAVA_SIGNATURE_INT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
(*jEnv)->SetObjectArrayElement(jEnv, java_array, index, java_value.l);
if (is_local_ref) \
(*jEnv)->DeleteLocalRef(jEnv, java_value.l);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_ReportJavaError(cx, jEnv, "Error assigning to Java object array");
return JS_FALSE;
}
break;
#undef SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return JS_TRUE;
}

View File

@@ -0,0 +1,605 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code that constructs and manipulates JavaClassDescriptor
* structs, which are the native wrappers for Java classes.
* JavaClassDescriptors are used to describe the signatures of methods and
* fields. There is a JavaClassDescriptor associated with the reflection of
* each Java Object.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsj_hash.h" /* Hash tables */
/* A one-to-one mapping between all referenced java.lang.Class objects and
their corresponding JavaClassDescriptor objects */
static JSJHashTable *java_class_reflections;
/*
* Given a JVM handle to a java.lang.Class object, malloc a C-string
* containing the UTF8 encoding of the fully qualified name of the class.
* It's the caller's responsibility to free the returned string.
*
* If an error occurs, NULL is returned and the error reporter called.
*/
const char *
jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
jstring java_class_name_jstr;
const char *java_class_name;
/* Get java.lang.String object containing class name */
java_class_name_jstr =
(*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getName);
if (!java_class_name_jstr)
goto error;
/* Convert to UTF8 encoding and copy */
java_class_name = jsj_DupJavaStringUTF(cx, jEnv, java_class_name_jstr);
if (!java_class_name)
return NULL;
return java_class_name;
error:
jsj_UnexpectedJavaError(cx, jEnv, "Can't get Java class name using"
"java.lang.Class.getName()");
return NULL;
}
/*
* Convert in-place a string of the form "java.lang.String" into "java/lang/String".
* Though the former style is conventionally used by Java programmers, the latter is
* what the JNI functions require.
*/
void
jsj_MakeJNIClassname(char * class_name)
{
char * c;
for (c = class_name; *c; c++)
if (*c == '.')
*c = '/';
}
/*
* Classify an instance of java.lang.Class as either one of the primitive
* types, e.g. int, char, etc., as an array type or as a non-array object type
* (subclass of java.lang.Object) by returning the appropriate enum member.
*
*/
static JavaSignatureChar
get_signature_type(JSContext *cx, JavaClassDescriptor *class_descriptor)
{
JavaSignatureChar type;
const char *java_class_name;
/* Get UTF8 encoding of class name */
java_class_name = class_descriptor->name;
PR_ASSERT(java_class_name);
if (!java_class_name)
return JAVA_SIGNATURE_UNKNOWN;
if (!strcmp(java_class_name, "byte"))
type = JAVA_SIGNATURE_BYTE;
else if (!strcmp(java_class_name, "char"))
type = JAVA_SIGNATURE_CHAR;
else if (!strcmp(java_class_name, "float"))
type = JAVA_SIGNATURE_FLOAT;
else if (!strcmp(java_class_name, "double"))
type = JAVA_SIGNATURE_DOUBLE;
else if (!strcmp(java_class_name, "int"))
type = JAVA_SIGNATURE_INT;
else if (!strcmp(java_class_name, "long"))
type = JAVA_SIGNATURE_LONG;
else if (!strcmp(java_class_name, "short"))
type = JAVA_SIGNATURE_SHORT;
else if (!strcmp(java_class_name, "boolean"))
type = JAVA_SIGNATURE_BOOLEAN;
else if (!strcmp(java_class_name, "void"))
type = JAVA_SIGNATURE_VOID;
else
/* Well, I guess it's a Java class, then. */
type = JAVA_SIGNATURE_CLASS;
return type;
}
static JSBool
is_java_array_class(JNIEnv *jEnv, jclass java_class)
{
return (*jEnv)->CallBooleanMethod(jEnv, java_class, jlClass_isArray);
}
/*
* Return the class of a Java array's component type. This is not the same
* as the array's element type. For example, the component type of an array
* of type SomeType[][][] is SomeType[][], but its element type is SomeType.
*
* If an error occurs, NULL is returned and an error reported.
*/
static jclass
get_java_array_component_class(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
jclass result;
result = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getComponentType);
if (!result) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't get Java array component class using "
"java.lang.Class.getComponentType()");
return NULL;
}
return result;
}
/*
* Given a Java class, fill in the signature structure that describes the class.
* If an error occurs, JS_FALSE is returned and the error reporter called.
*/
static JSBool
compute_java_class_signature(JSContext *cx, JNIEnv *jEnv, JavaSignature *signature)
{
jclass java_class = signature->java_class;
if (is_java_array_class(jEnv, java_class)) {
jclass component_class;
signature->type = JAVA_SIGNATURE_ARRAY;
component_class = get_java_array_component_class(cx, jEnv, java_class);
if (!component_class)
return JS_FALSE;
signature->array_component_signature =
jsj_GetJavaClassDescriptor(cx, jEnv, component_class);
if (!signature->array_component_signature)
return JS_FALSE;
} else {
signature->type = get_signature_type(cx, signature);
}
return JS_TRUE;
}
/*
* Convert a JavaSignature object into a string format as used by
* the JNI functions, e.g. java.lang.Object ==> "Ljava/lang/Object;"
* The caller is responsible for freeing the resulting string.
*
* If an error is encountered, NULL is returned and an error reported.
*/
const char *
jsj_ConvertJavaSignatureToString(JSContext *cx, JavaSignature *signature)
{
char *sig;
if (signature->type == JAVA_SIGNATURE_CLASS) {
/* A non-array object class */
sig = PR_smprintf("L%s;", signature->name);
if (sig)
jsj_MakeJNIClassname(sig);
} else if (signature->type == JAVA_SIGNATURE_ARRAY) {
/* An array class */
const char *component_signature_string;
component_signature_string =
jsj_ConvertJavaSignatureToString(cx, signature->array_component_signature);
if (!component_signature_string)
return NULL;
sig = PR_smprintf("[%s", component_signature_string);
JS_free(cx, (char*)component_signature_string);
} else {
/* A primitive class */
sig = PR_smprintf("%c", (char)signature->type);
}
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
/*
* Convert a JavaSignature object into a human-readable string format as seen
* in Java source files, e.g. "byte", or "int[][]" or "java.lang.String".
* The caller is responsible for freeing the resulting string.
*
* If an error is encountered, NULL is returned and an error reported.
*/
const char *
jsj_ConvertJavaSignatureToHRString(JSContext *cx,
JavaSignature *signature)
{
char *sig;
JavaSignature *acs;
if (signature->type == JAVA_SIGNATURE_ARRAY) {
/* An array class */
const char *component_signature_string;
acs = signature->array_component_signature;
component_signature_string =
jsj_ConvertJavaSignatureToHRString(cx, acs);
if (!component_signature_string)
return NULL;
sig = PR_smprintf("%s[]", component_signature_string);
JS_free(cx, (char*)component_signature_string);
} else {
/* A primitive class or a non-array object class */
sig = JS_strdup(cx, signature->name);
}
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
static void
destroy_java_member_descriptor(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor)
{
JavaMethodSpec *method, *next_method;
if (member_descriptor->field)
jsj_DestroyFieldSpec(cx, jEnv, member_descriptor->field);
method = member_descriptor->methods;
while (method) {
next_method = method->next;
jsj_DestroyMethodSpec(cx, jEnv, method);
method = next_method;
}
if (member_descriptor->invoke_func_obj)
JS_RemoveRoot(cx, &member_descriptor->invoke_func_obj);
}
static void
destroy_class_member_descriptors(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor)
{
JavaMemberDescriptor *next_member;
while (member_descriptor) {
next_member = member_descriptor->next;
destroy_java_member_descriptor(cx, jEnv, member_descriptor);
member_descriptor = next_member;
}
}
static void
destroy_class_descriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor)
{
JS_FREE_IF(cx, (char *)class_descriptor->name);
if (class_descriptor->java_class) {
JSJ_HashTableRemove(java_class_reflections,
class_descriptor->java_class, (void*)jEnv);
(*jEnv)->DeleteGlobalRef(jEnv, class_descriptor->java_class);
}
if (class_descriptor->array_component_signature)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor->array_component_signature);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->instance_members);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->static_members);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->constructors);
JS_free(cx, class_descriptor);
}
static JavaClassDescriptor *
new_class_descriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
JavaClassDescriptor *class_descriptor;
class_descriptor = (JavaClassDescriptor *)JS_malloc(cx, sizeof(JavaClassDescriptor));
if (!class_descriptor)
return NULL;
memset(class_descriptor, 0, sizeof(JavaClassDescriptor));
class_descriptor->name = jsj_GetJavaClassName(cx, jEnv, java_class);
if (!class_descriptor->name)
goto error;
java_class = (*jEnv)->NewGlobalRef(jEnv, java_class);
if (!java_class) {
jsj_UnexpectedJavaError(cx, jEnv, "Unable to reference Java class");
goto error;
}
class_descriptor->java_class = java_class;
if (!compute_java_class_signature(cx, jEnv, class_descriptor))
goto error;
class_descriptor->modifiers =
(*jEnv)->CallIntMethod(jEnv, java_class, jlClass_getModifiers);
class_descriptor->ref_count = 1;
if (!JSJ_HashTableAdd(java_class_reflections, java_class, class_descriptor,
(void*)jEnv))
goto error;
return class_descriptor;
error:
destroy_class_descriptor(cx, jEnv, class_descriptor);
return NULL;
}
/* Trivial helper for jsj_DiscardJavaClassReflections(), below */
static PRIntn
enumerate_remove_java_class(JSJHashEntry *he, PRIntn i, void *arg)
{
JNIEnv *jEnv = (JNIEnv*)arg;
jclass java_class;
JavaClassDescriptor *class_descriptor;
class_descriptor = (JavaClassDescriptor*)he->value;
java_class = class_descriptor->java_class;
(*jEnv)->DeleteGlobalRef(jEnv, java_class);
class_descriptor->java_class = NULL;
return HT_ENUMERATE_REMOVE;
}
/* This shutdown routine discards all JNI references to Java objects
that have been reflected into JS, even if there are still references
to them from JS. */
void
jsj_DiscardJavaClassReflections(JNIEnv *jEnv)
{
if (java_class_reflections) {
JSJ_HashTableEnumerateEntries(java_class_reflections,
enumerate_remove_java_class,
(void*)jEnv);
JSJ_HashTableDestroy(java_class_reflections);
java_class_reflections = NULL;
}
}
extern JavaClassDescriptor *
jsj_GetJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
JavaClassDescriptor *class_descriptor;
class_descriptor = JSJ_HashTableLookup(java_class_reflections,
(const void *)java_class,
(void*)jEnv);
if (!class_descriptor)
return new_class_descriptor(cx, jEnv, java_class);
PR_ASSERT(class_descriptor->ref_count > 0);
class_descriptor->ref_count++;
return class_descriptor;
}
void
jsj_ReleaseJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor)
{
if (!--class_descriptor->ref_count)
destroy_class_descriptor(cx, jEnv, class_descriptor);
}
static JSBool
reflect_java_methods_and_fields(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_statics_only)
{
JavaMemberDescriptor *member_descriptor;
if (reflect_statics_only)
class_descriptor->static_members_reflected = JS_TRUE;
else
class_descriptor->instance_members_reflected = JS_TRUE;
if (!jsj_ReflectJavaMethods(cx, jEnv, class_descriptor, reflect_statics_only))
return JS_FALSE;
if (!jsj_ReflectJavaFields(cx, jEnv, class_descriptor, reflect_statics_only))
return JS_FALSE;
if (reflect_statics_only) {
member_descriptor = class_descriptor->static_members;
while (member_descriptor) {
class_descriptor->num_static_members++;
member_descriptor = member_descriptor->next;
}
} else {
member_descriptor = class_descriptor->instance_members;
while (member_descriptor) {
class_descriptor->num_instance_members++;
member_descriptor = member_descriptor->next;
}
}
return JS_TRUE;
}
JavaMemberDescriptor *
jsj_GetClassStaticMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->static_members;
}
JavaMemberDescriptor *
jsj_GetClassInstanceMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->instance_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_FALSE);
return class_descriptor->instance_members;
}
JavaMemberDescriptor *
jsj_LookupJavaStaticMemberDescriptorById(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id)
{
JavaMemberDescriptor *member_descriptor;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
if (id == member_descriptor->id)
return member_descriptor;
member_descriptor = member_descriptor->next;
}
return NULL;
}
JavaMemberDescriptor *
jsj_GetJavaStaticMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name_jstr)
{
JavaMemberDescriptor *member_descriptor;
jsid id;
if (!JavaStringToId(cx, jEnv, member_name_jstr, &id))
return NULL;
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor)
return member_descriptor;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = jsj_DupJavaStringUTF(cx, jEnv, member_name_jstr);
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
member_descriptor->id = id;
member_descriptor->next = class_descriptor->static_members;
class_descriptor->static_members = member_descriptor;
return member_descriptor;
}
JavaMemberDescriptor *
jsj_GetJavaClassConstructors(JSContext *cx,
JavaClassDescriptor *class_descriptor)
{
JavaMemberDescriptor *member_descriptor;
if (class_descriptor->constructors)
return class_descriptor->constructors;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = JS_strdup(cx, "<init>");
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
class_descriptor->constructors = member_descriptor;
return member_descriptor;
}
JavaMemberDescriptor *
jsj_LookupJavaClassConstructors(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->constructors;
}
JavaMemberDescriptor *
jsj_LookupJavaMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id)
{
JavaMemberDescriptor *member_descriptor;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
if (id == member_descriptor->id)
return member_descriptor;
member_descriptor = member_descriptor->next;
}
return NULL;
}
JavaMemberDescriptor *
jsj_GetJavaMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name_jstr)
{
JavaMemberDescriptor *member_descriptor;
jsid id;
if (!JavaStringToId(cx, jEnv, member_name_jstr, &id))
return NULL;
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor)
return member_descriptor;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = jsj_DupJavaStringUTF(cx, jEnv, member_name_jstr);
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
member_descriptor->id = id;
member_descriptor->next = class_descriptor->instance_members;
class_descriptor->instance_members = member_descriptor;
return member_descriptor;
}
JSBool
jsj_InitJavaClassReflectionsTable()
{
java_class_reflections =
JSJ_NewHashTable(64, jsj_HashJavaObject, jsj_JavaObjectComparator,
NULL, NULL, NULL);
if (!java_class_reflections)
return JS_FALSE;
return JS_TRUE;
}

View File

@@ -0,0 +1,806 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Below is the code that converts between Java and JavaScript values of all
* types.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
/* Floating-point double utilities, stolen from jsnum.h */
#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))
static JSBool
convert_js_obj_to_JSObject_wrapper(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj,
JavaSignature *signature,
int *cost, jobject *java_value)
{
if (!njJSObject) {
if (java_value)
JS_ReportError(cx, "Couldn't convert JavaScript object to an "
"instance of netscape.javascript.JSObject "
"because that class could not be loaded.");
return JS_FALSE;
}
if (!(*jEnv)->IsAssignableFrom(jEnv, njJSObject, signature->java_class))
return JS_FALSE;
if (!java_value)
return JS_TRUE;
*java_value = jsj_WrapJSObject(cx, jEnv, js_obj);
return (*java_value != NULL);
}
jstring
jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str)
{
jstring result;
result = (*jEnv)->NewString(jEnv, JS_GetStringChars(js_str),
JS_GetStringLength(js_str));
if (!result) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance "
"of java.lang.String");
}
return result;
}
/*
* Convert a JS value to an instance of java.lang.Object or one of its subclasses,
* performing any necessary type coercion. If non-trivial coercion is required,
* the cost value is incremented. If the java_value pass-by-reference argument
* is non-NULL, the resulting Java value is stored there.
*
* Returns JS_TRUE if the conversion is possible, JS_FALSE otherwise
*/
JSBool
jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jobject *java_value, JSBool *is_local_refp)
{
JSString *jsstr;
jclass target_java_class;
PR_ASSERT(signature->type == JAVA_SIGNATURE_CLASS ||
signature->type == JAVA_SIGNATURE_ARRAY);
/* Initialize to default case, in which no new Java object is
synthesized to perform the conversion and, therefore, no JNI local
references are being held. */
*is_local_refp = JS_FALSE;
/* Get the Java type of the target value */
target_java_class = signature->java_class;
if (JSVAL_IS_OBJECT(v)) {
JSObject *js_obj = JSVAL_TO_OBJECT(v);
/* JS null is always assignable to a Java object */
if (!js_obj) {
if (java_value)
*java_value = NULL;
return JS_TRUE;
}
if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0) ||
JS_InstanceOf(cx, js_obj, &JavaArray_class, 0)) {
/* The source value is a Java object wrapped inside a JavaScript
object. Unwrap the JS object and return the original Java
object if it's class makes it assignment-compatible with the
target class using Java's assignability rules. */
JavaObjectWrapper *java_wrapper = JS_GetPrivate(cx, js_obj);
jobject java_obj = java_wrapper->java_obj;
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, target_java_class)) {
if (java_value)
*java_value = java_obj;
return JS_TRUE;
}
#ifdef LIVECONNECT_IMPROVEMENTS
/* Don't allow wrapped Java objects to be converted to strings */
goto conversion_error;
#else
/* Fall through, to attempt conversion to a Java string */
#endif
} else if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0)) {
/* We're dealing with the reflection of a Java class */
JavaClassDescriptor *java_class_descriptor = JS_GetPrivate(cx, js_obj);
/* Check if target type is java.lang.Class class */
if ((*jEnv)->IsAssignableFrom(jEnv, jlClass, target_java_class)) {
if (java_value)
*java_value = java_class_descriptor->java_class;
return JS_TRUE;
}
/* Check if target type is netscape.javascript.JSObject wrapper class */
if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) {
if (*java_value)
*is_local_refp = JS_TRUE;
return JS_TRUE;
}
/* Fall through, to attempt conversion to a Java string */
} else if (JS_InstanceOf(cx, js_obj, &JavaMember_class, 0)) {
if (!JS_ConvertValue(cx, v, JSTYPE_OBJECT, &v))
return JS_FALSE;
return jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost,
java_value, is_local_refp);
} else {
/* Otherwise, see if the target type is the netscape.javascript.JSObject
wrapper class or one of its subclasses, in which case a
reference is passed to the original JS object by wrapping it
inside an instance of netscape.javascript.JSObject */
if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) {
return JS_TRUE;
}
/* Fall through, to attempt conversion to a Java string */
}
} else if (JSVAL_IS_NUMBER(v)) {
/* JS numbers, integral or not, can be converted to instances of java.lang.Double */
if ((*jEnv)->IsAssignableFrom(jEnv, jlDouble, target_java_class)) {
if (java_value) {
jsdouble d;
if (!JS_ValueToNumber(cx, v, &d))
goto conversion_error;
*java_value = (*jEnv)->NewObject(jEnv, jlDouble, jlDouble_Double, d);
if (*java_value) {
*is_local_refp = JS_TRUE;
} else {
jsj_UnexpectedJavaError(cx, jEnv,
"Couldn't construct instance of java.lang.Double");
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
(*cost)++;
#endif
return JS_TRUE;
}
/* Fall through, to attempt conversion to a java.lang.String ... */
} else if (JSVAL_IS_BOOLEAN(v)) {
/* JS boolean values can be converted to instances of java.lang.Boolean */
if ((*jEnv)->IsAssignableFrom(jEnv, jlBoolean, target_java_class)) {
if (java_value) {
JSBool b;
if (!JS_ValueToBoolean(cx, v, &b))
goto conversion_error;
*java_value =
(*jEnv)->NewObject(jEnv, jlBoolean, jlBoolean_Boolean, b);
if (*java_value) {
*is_local_refp = JS_TRUE;
} else {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance "
"of java.lang.Boolean");
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
(*cost)++;
#endif
return JS_TRUE;
}
/* Fall through, to attempt conversion to a java.lang.String ... */
}
/* If no other conversion is possible, see if the target type is java.lang.String */
if ((*jEnv)->IsAssignableFrom(jEnv, jlString, target_java_class)) {
#ifdef LIVECONNECT_IMPROVEMENTS
JSBool is_string = JSVAL_IS_STRING(v);
#endif
/* Convert to JS string, if necessary, and then to a Java Unicode string */
jsstr = JS_ValueToString(cx, v);
if (jsstr) {
if (java_value) {
*java_value = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr);
if (*java_value) {
*is_local_refp = JS_TRUE;
} else {
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
if (!is_string)
(*cost)++;
#endif
return JS_TRUE;
}
}
conversion_error:
return JS_FALSE;
}
/* Valid ranges for Java numeric types */
#define jbyte_MAX_VALUE 127.0
#define jbyte_MIN_VALUE -128.0
#define jchar_MAX_VALUE 65535.0
#define jchar_MIN_VALUE 0.0
#define jshort_MAX_VALUE 32767.0
#define jshort_MIN_VALUE -32768.0
#define jint_MAX_VALUE 2147483647.0
#define jint_MIN_VALUE -2147483648.0
#define jlong_MAX_VALUE 9223372036854775807.0
#define jlong_MIN_VALUE -9223372036854775808.0
/* Utility macro for jsj_ConvertJSValueToJavaValue(), below */
#define JSVAL_TO_INTEGRAL_JVALUE(type_name, member_name, member_type, jsval, java_value) \
if (!JSVAL_IS_NUMBER(v)) { \
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v)) \
goto conversion_error; \
(*cost)++; \
} \
{ \
member_type member_name; \
\
if (JSVAL_IS_INT(v)) { \
jsint ival = JSVAL_TO_INT(v); \
member_name = (member_type) ival; \
\
/* Check to see if the jsval's magnitude is too large to be \
representable in the target java type */ \
if (member_name != ival) \
goto numeric_conversion_error; \
} else { \
jdouble dval = *JSVAL_TO_DOUBLE(v); \
\
/* NaN becomes zero when converted to integral value */ \
if (JSDOUBLE_IS_NaN(dval)) \
member_name = 0; \
\
/* Unrepresentably large numbers, including infinities, */ \
/* cause an error. */ \
else if ((dval > member_type ## _MAX_VALUE) || \
(dval < member_type ## _MIN_VALUE)) { \
goto numeric_conversion_error; \
} else \
member_name = (member_type) dval; \
\
/* Don't allow a non-integral number to be converted \
to an integral type */ \
/* Actually, we have to allow this for LC1 compatibility */ \
/* if ((jdouble)member_name != dval) \
(*cost)++; */ \
} \
if (java_value) \
java_value->member_name = member_name; \
}
#if XP_MAC
/* on MRJ jlong is typedef'd to wide, which is a struct. */
#include <Math64.h>
static jsint jlong_to_jsint(jlong lvalue)
{
SInt64 val = WideToSInt64(lvalue);
return S32Set(val);
}
static jlong jsint_to_jlong(jsint ivalue)
{
SInt64 val = S64Set(ivalue);
wide wval =SInt64ToWide(val);
return *(jlong*)&wval;
}
static jdouble jlong_to_jdouble(jlong lvalue)
{
SInt64 val = WideToSInt64(lvalue);
return SInt64ToLongDouble(val);
}
static jlong jdouble_to_jlong(jdouble dvalue)
{
SInt64 val = LongDoubleToSInt64(dvalue);
wide wval = SInt64ToWide(val);
return *(jlong*)&wval;
}
/* Mac utility macro for jsj_ConvertJSValueToJavaValue(), below */
#define JSVAL_TO_JLONG_JVALUE(member_name, member_type, jsvalue, java_value) \
if (!JSVAL_IS_NUMBER(jsvalue)) { \
if (!JS_ConvertValue(cx, jsvalue, JSTYPE_NUMBER, &jsvalue)) \
goto conversion_error; \
(*cost)++; \
} \
{ \
member_type member_name; \
\
if (JSVAL_IS_INT(jsvalue)) { \
jsint ival = JSVAL_TO_INT(jsvalue); \
member_name = jsint_to_jlong(ival); \
\
} else { \
jdouble dval = *JSVAL_TO_DOUBLE(jsvalue); \
\
/* NaN becomes zero when converted to integral value */ \
if (JSDOUBLE_IS_NaN(dval)) \
member_name = jsint_to_jlong(0); \
\
/* Unrepresentably large numbers, including infinities, */ \
/* cause an error. */ \
else if ((dval > member_type ## _MAX_VALUE) || \
(dval < member_type ## _MIN_VALUE)) { \
goto numeric_conversion_error; \
} else \
member_name = jdouble_to_jlong(dval); \
\
/* Don't allow a non-integral number to be converted \
to an integral type */ \
/* Actually, we have to allow this for LC1 compatibility */ \
/*if (jlong_to_jdouble(member_name) != dval) \
(*cost)++;*/ \
} \
if (java_value) \
java_value->member_name = member_name; \
}
#else
#define jlong_to_jdouble(lvalue) ((jdouble) lvalue)
#endif
/*
* Convert a JS value to a Java value of the given type signature. The cost
* variable is incremented if coercion is required, e.g. the source value is
* a string, but the target type is a boolean.
*
* Returns JS_FALSE if no conversion is possible, either because the jsval has
* a type that is wholly incompatible with the Java value, or because a scalar
* jsval can't be represented in a variable of the target type without loss of
* precision, e.g. the source value is "4.2" but the destination type is byte.
* If conversion is not possible and java_value is non-NULL, the JS error
* reporter is called with an appropriate message.
*/
JSBool
jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v,
JavaSignature *signature,
int *cost, jvalue *java_value, JSBool *is_local_refp)
{
JavaSignatureChar type;
JSBool success = JS_FALSE;
/* Initialize to default case, in which no new Java object is
synthesized to perform the conversion and, therefore, no JNI local
references are being held. */
*is_local_refp = JS_FALSE;
type = signature->type;
switch (type) {
case JAVA_SIGNATURE_BOOLEAN:
if (!JSVAL_IS_BOOLEAN(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v))
goto conversion_error;
(*cost)++;
}
if (java_value)
java_value->z = (jboolean)(JSVAL_TO_BOOLEAN(v) == JS_TRUE);
break;
case JAVA_SIGNATURE_SHORT:
JSVAL_TO_INTEGRAL_JVALUE(short, s, jshort, v, java_value);
break;
case JAVA_SIGNATURE_BYTE:
JSVAL_TO_INTEGRAL_JVALUE(byte, b, jbyte, v, java_value);
break;
case JAVA_SIGNATURE_CHAR:
/* A one-character string can be converted into a character */
if (JSVAL_IS_STRING(v) && (JS_GetStringLength(JSVAL_TO_STRING(v)) == 1)) {
v = INT_TO_JSVAL(*JS_GetStringChars(JSVAL_TO_STRING(v)));
}
JSVAL_TO_INTEGRAL_JVALUE(char, c, jchar, v, java_value);
break;
case JAVA_SIGNATURE_INT:
JSVAL_TO_INTEGRAL_JVALUE(int, i, jint, v, java_value);
break;
case JAVA_SIGNATURE_LONG:
#if XP_MAC
JSVAL_TO_JLONG_JVALUE(j, jlong, v, java_value);
#else
JSVAL_TO_INTEGRAL_JVALUE(long, j, jlong, v, java_value);
#endif
break;
case JAVA_SIGNATURE_FLOAT:
if (!JSVAL_IS_NUMBER(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
goto conversion_error;
(*cost)++;
}
if (java_value) {
if (JSVAL_IS_INT(v))
java_value->f = (jfloat) JSVAL_TO_INT(v);
else
java_value->f = (jfloat) *JSVAL_TO_DOUBLE(v);
}
break;
case JAVA_SIGNATURE_DOUBLE:
if (!JSVAL_IS_NUMBER(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
goto conversion_error;
(*cost)++;
}
if (java_value) {
if (JSVAL_IS_INT(v))
java_value->d = (jdouble) JSVAL_TO_INT(v);
else
java_value->d = (jdouble) *JSVAL_TO_DOUBLE(v);
}
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost,
&java_value->l, is_local_refp))
goto conversion_error;
break;
default:
PR_ASSERT(0);
return JS_FALSE;
}
/* Success */
return JS_TRUE;
numeric_conversion_error:
success = JS_TRUE;
/* Fall through ... */
conversion_error:
if (java_value) {
const char *jsval_string;
JSString *jsstr;
jsval_string = NULL;
jsstr = JS_ValueToString(cx, v);
if (jsstr)
jsval_string = JS_GetStringBytes(jsstr);
if (!jsval_string)
jsval_string = "";
JS_ReportError(cx, "Unable to convert JavaScript value %s to "
"Java value of type %s",
jsval_string, signature->name);
return JS_FALSE;
}
return success;
}
/*
* A utility routine to create a JavaScript Unicode string from a
* java.lang.String (Unicode) string.
*/
JSString *
jsj_ConvertJavaStringToJSString(JSContext *cx, JNIEnv *jEnv, jstring java_str)
{
JSString *js_str;
jboolean is_copy;
const jchar *ucs2_str;
jchar *copy_ucs2_str;
jsize ucs2_str_len, num_bytes;
ucs2_str_len = (*jEnv)->GetStringLength(jEnv, java_str);
ucs2_str = (*jEnv)->GetStringChars(jEnv, java_str, &is_copy);
if (!ucs2_str) {
jsj_UnexpectedJavaError(cx, jEnv,
"Unable to extract native Unicode from Java string");
return NULL;
}
js_str = NULL;
/* The string data passed into JS_NewUCString() is
not copied, so make a copy of the Unicode character vector. */
num_bytes = ucs2_str_len * sizeof(jchar);
copy_ucs2_str = (jchar*)JS_malloc(cx, num_bytes);
if (!copy_ucs2_str)
goto done;
memcpy(copy_ucs2_str, ucs2_str, num_bytes);
js_str = JS_NewUCString(cx, (jschar*)copy_ucs2_str, ucs2_str_len);
done:
(*jEnv)->ReleaseStringChars(jEnv, java_str, ucs2_str);
return js_str;
}
/*
* Attempt to obtain a JS string representation of a Java object.
* The java_obj argument must be of type java.lang.Object or a subclass.
* If java_obj is a Java string, it's value is simply extracted and
* copied into a JS string. Otherwise, the toString() method is called
* on java_obj.
*/
JSBool
jsj_ConvertJavaObjectToJSString(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp)
{
JSString *js_str;
jstring java_str;
jmethodID toString;
/* Create a Java string, unless java_obj is already a java.lang.String */
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, jlString)) {
java_str = java_obj;
} else {
jclass java_class;
java_class = class_descriptor->java_class;
toString = (*jEnv)->GetMethodID(jEnv, java_class, "toString",
"()Ljava/lang/String;");
if (!toString) {
/* All Java objects have a toString method */
jsj_UnexpectedJavaError(cx, jEnv, "No toString() method for class %s!",
class_descriptor->name);
return JS_FALSE;
}
java_str = (*jEnv)->CallObjectMethod(jEnv, java_obj, toString);
if (!java_str) {
jsj_ReportJavaError(cx, jEnv, "toString() method failed");
return JS_FALSE;
}
}
/* Extract Unicode from java.lang.String instance and convert to JS string */
js_str = jsj_ConvertJavaStringToJSString(cx, jEnv, java_str);
if (!js_str)
return JS_FALSE;
*vp = STRING_TO_JSVAL(js_str);
return JS_TRUE;
}
/*
* Convert a Java object to a number by attempting to call the
* doubleValue() method on a Java object to get a double result.
* This usually only works on instances of java.lang.Double, but the code
* is generalized to work with any Java object that supports this method.
*
* Returns JS_TRUE if the call was successful.
* Returns JS_FALSE if conversion is not possible or an error occurs.
*/
JSBool
jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp)
{
jdouble d;
jmethodID doubleValue;
jclass java_class;
java_class = class_descriptor->java_class;
doubleValue = (*jEnv)->GetMethodID(jEnv, java_class, "doubleValue", "()D");
if (!doubleValue) {
/* There is no doubleValue() method for the object. Try toString()
instead and the JS engine will attempt to convert the result to
a number. */
(*jEnv)->ExceptionClear(jEnv);
return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor,
java_obj, vp);
}
d = (*jEnv)->CallDoubleMethod(jEnv, java_obj, doubleValue);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "doubleValue() method failed");
return JS_FALSE;
}
return JS_NewDoubleValue(cx, d, vp);
}
/*
* Convert a Java object to a boolean by attempting to call the
* booleanValue() method on a Java object to get a boolean result.
* This usually only works on instances of java.lang.Boolean, but the code
* is generalized to work with any Java object that supports this method.
*
* Returns JS_TRUE if the call was successful.
* Returns JS_FALSE if conversion is not possible or an error occurs.
*/
extern JSBool
jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp)
{
jboolean b;
jmethodID booleanValue;
jclass java_class;
/* Null converts to false. */
if (!java_obj) {
*vp = JSVAL_FALSE;
return JS_TRUE;
}
java_class = class_descriptor->java_class;
booleanValue = (*jEnv)->GetMethodID(jEnv, java_obj, "booleanValue", "()Z");
/* Non-null Java object does not have a booleanValue() method, so
it converts to true. */
if (!booleanValue) {
(*jEnv)->ExceptionClear(jEnv);
*vp = JSVAL_TRUE;
return JS_TRUE;
}
b = (*jEnv)->CallBooleanMethod(jEnv, java_obj, booleanValue);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "booleanValue() method failed");
return JS_FALSE;
}
*vp = BOOLEAN_TO_JSVAL(b);
return JS_TRUE;
}
/*
* Reflect a Java object into a JS value. The source object, java_obj, must
* be of type java.lang.Object or a subclass and may, therefore, be an array.
*/
JSBool
jsj_ConvertJavaObjectToJSValue(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp)
{
jclass java_class;
JSObject *js_obj;
/* A null in Java-land is also null in JS */
if (!java_obj) {
*vp = JSVAL_NULL;
return JS_TRUE;
}
java_class = (*jEnv)->GetObjectClass(jEnv, java_obj);
/*
* If it's an instance of netscape.javascript.JSObject, i.e. a wrapper
* around a JS object that has been passed into the Java world, unwrap
* it to obtain the original JS object.
*/
if (njJSObject && (*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) {
#ifdef PRESERVE_JSOBJECT_IDENTITY
js_obj = (JSObject *)((*jEnv)->GetIntField(jEnv, java_obj, njJSObject_internal));
#else
js_obj = jsj_UnwrapJSObjectWrapper(jEnv, java_obj);
#endif
PR_ASSERT(js_obj);
if (!js_obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(js_obj);
return JS_TRUE;
}
/*
* Instances of java.lang.String are wrapped so we can call methods on
* them, but they convert to a JS string if used in a string context.
*/
/* TODO - let's get rid of this annoying "feature" */
/* otherwise, wrap it inside a JavaObject */
js_obj = jsj_WrapJavaObject(cx, jEnv, java_obj, java_class);
if (!js_obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(js_obj);
return JS_TRUE;
}
/*
* Convert a Java value (primitive or object) to a JS value.
*
* This is usually an infallible operation, but JS_FALSE is returned
* on an out-of-memory condition and the error reporter is called.
*/
JSBool
jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv,
JavaSignature *signature,
jvalue *java_value,
jsval *vp)
{
int32 ival32;
switch (signature->type) {
case JAVA_SIGNATURE_VOID:
*vp = JSVAL_VOID;
return JS_TRUE;
case JAVA_SIGNATURE_BYTE:
*vp = INT_TO_JSVAL((jsint)java_value->b);
return JS_TRUE;
case JAVA_SIGNATURE_CHAR:
*vp = INT_TO_JSVAL((jsint)java_value->c);
return JS_TRUE;
case JAVA_SIGNATURE_SHORT:
*vp = INT_TO_JSVAL((jsint)java_value->s);
return JS_TRUE;
case JAVA_SIGNATURE_INT:
ival32 = java_value->i;
if (INT_FITS_IN_JSVAL(ival32)) {
*vp = INT_TO_JSVAL((jsint) ival32);
return JS_TRUE;
} else {
return JS_NewDoubleValue(cx, ival32, vp);
}
case JAVA_SIGNATURE_BOOLEAN:
*vp = BOOLEAN_TO_JSVAL((JSBool) java_value->z);
return JS_TRUE;
case JAVA_SIGNATURE_LONG:
return JS_NewDoubleValue(cx, jlong_to_jdouble(java_value->j), vp);
case JAVA_SIGNATURE_FLOAT:
return JS_NewDoubleValue(cx, java_value->f, vp);
case JAVA_SIGNATURE_DOUBLE:
return JS_NewDoubleValue(cx, java_value->d, vp);
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_value->l, vp);
default:
PR_ASSERT(0);
return JS_FALSE;
}
}

View File

@@ -0,0 +1,383 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code used to reflect Java fields as properties of
* JavaObject objects and the code to access those fields.
*
*/
#include <stdlib.h>
#include "jsj_private.h" /* LiveConnect internals */
/*
* Add a single field, described by java_field, to the JavaMemberDescriptor
* named by field_name within the given JavaClassDescriptor.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
static JSBool
add_java_field_to_class_descriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring field_name_jstr,
jobject java_field, /* a java.lang.reflect.Field */
jint modifiers)
{
jclass fieldType;
jfieldID fieldID;
jclass java_class;
JSBool is_static_field;
JavaMemberDescriptor *member_descriptor = NULL;
const char *sig_cstr = NULL;
const char *field_name = NULL;
JavaSignature *signature = NULL;
JavaFieldSpec *field_spec = NULL;
is_static_field = modifiers & ACC_STATIC;
if (is_static_field) {
member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
} else {
member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
}
if (!member_descriptor)
goto error;
field_spec = (JavaFieldSpec*)JS_malloc(cx, sizeof(JavaFieldSpec));
if (!field_spec)
goto error;
field_spec->modifiers = modifiers;
/* Get the Java class corresponding to the type of the field */
fieldType = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getType);
if (!fieldType) {
jsj_UnexpectedJavaError(cx, jEnv,
"Unable to determine type of field using"
" java.lang.reflect.Field.getType()");
goto error;
}
signature = jsj_GetJavaClassDescriptor(cx, jEnv, fieldType);
if (!signature)
goto error;
field_spec->signature = signature;
field_name = jsj_DupJavaStringUTF(cx, jEnv, field_name_jstr);
if (!field_name)
goto error;
field_spec->name = field_name;
/* Compute the JNI-style (string-based) signature of the field type */
sig_cstr = jsj_ConvertJavaSignatureToString(cx, signature);
if (!sig_cstr)
goto error;
/* Compute the JNI fieldID and cache it for quick field access */
java_class = class_descriptor->java_class;
if (is_static_field)
fieldID = (*jEnv)->GetStaticFieldID(jEnv, java_class, field_name, sig_cstr);
else
fieldID = (*jEnv)->GetFieldID(jEnv, java_class, field_name, sig_cstr);
if (!fieldID) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't get Java field ID for class %s, field %s (sig=%s)",
class_descriptor->name, field_name, sig_cstr);
goto error;
}
field_spec->fieldID = fieldID;
JS_free(cx, (char*)sig_cstr);
member_descriptor->field = field_spec;
/* Success */
return JS_TRUE;
error:
if (field_spec) {
JS_FREE_IF(cx, (char*)field_spec->name);
JS_free(cx, field_spec);
}
JS_FREE_IF(cx, (char*)sig_cstr);
if (signature)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, signature);
return JS_FALSE;
}
/*
* Free up a JavaFieldSpec and all its resources.
*/
void
jsj_DestroyFieldSpec(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field)
{
JS_FREE_IF(cx, (char*)field->name);
jsj_ReleaseJavaClassDescriptor(cx, jEnv, field->signature);
JS_free(cx, field);
}
/*
* Add a JavaMemberDescriptor to the collection of members in class_descriptor
* for every public field of the identified Java class. (A separate collection
* is kept in class_descriptor for static and instance members.)
* If reflect_only_static_fields is set, instance fields are not reflected. If
* it isn't set, only instance fields are reflected and static fields are not
* reflected.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
JSBool
jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_fields)
{
int i;
JSBool ok;
jint modifiers;
jobject java_field;
jstring field_name_jstr;
jarray joFieldArray;
jsize num_fields;
jclass java_class;
/* Get a java array of java.lang.reflect.Field objects, by calling
java.lang.Class.getFields(). */
java_class = class_descriptor->java_class;
joFieldArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getFields);
if (!joFieldArray) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't determine Java object's fields "
"using java.lang.Class.getFields()");
return JS_FALSE;
}
/* Iterate over the class fields */
num_fields = (*jEnv)->GetArrayLength(jEnv, joFieldArray);
for (i = 0; i < num_fields; i++) {
/* Get the i'th reflected field */
java_field = (*jEnv)->GetObjectArrayElement(jEnv, joFieldArray, i);
if (!java_field) {
jsj_UnexpectedJavaError(cx, jEnv, "Can't access a Field[] array");
return JS_FALSE;
}
/* Get the field modifiers, e.g. static, public, private, etc. */
modifiers = (*jEnv)->CallIntMethod(jEnv, java_field, jlrField_getModifiers);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't access a Field's modifiers using"
"java.lang.reflect.Field.getModifiers()");
return JS_FALSE;
}
/* Don't allow access to private or protected Java fields. */
if (!(modifiers & ACC_PUBLIC))
continue;
/* Reflect all instance fields or all static fields, but not both */
if (reflect_only_static_fields != ((modifiers & ACC_STATIC) != 0))
continue;
/* Determine the unqualified name of the field */
field_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getName);
if (!field_name_jstr) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't obtain a Field's name"
"java.lang.reflect.Field.getName()");
return JS_FALSE;
}
/* Add a JavaFieldSpec object to the JavaClassDescriptor */
ok = add_java_field_to_class_descriptor(cx, jEnv, class_descriptor, field_name_jstr,
java_field, modifiers);
if (!ok)
return JS_FALSE;
}
/* Success */
return JS_TRUE;
}
/*
* Read the value of a Java field and return it as a JavaScript value.
* If the field is static, then java_obj is a Java class, otherwise
* it's a Java instance object.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
JSBool
jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval *vp)
{
JSBool is_static_field;
jvalue java_value;
JavaSignature *signature;
JavaSignatureChar field_type;
jfieldID fieldID = field_spec->fieldID;
is_static_field = field_spec->modifiers & ACC_STATIC;
#define GET_JAVA_FIELD(Type,member) \
PR_BEGIN_MACRO \
if (is_static_field) \
java_value.member = \
(*jEnv)->GetStatic##Type##Field(jEnv, java_obj, fieldID); \
else \
java_value.member = \
(*jEnv)->Get##Type##Field(jEnv, java_obj, fieldID); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_UnexpectedJavaError(cx, jEnv, "Error reading Java field"); \
return JS_FALSE; \
} \
PR_END_MACRO
signature = field_spec->signature;
field_type = signature->type;
switch(field_type) {
case JAVA_SIGNATURE_BYTE:
GET_JAVA_FIELD(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
GET_JAVA_FIELD(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
GET_JAVA_FIELD(Short,s);
break;
case JAVA_SIGNATURE_INT:
GET_JAVA_FIELD(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
GET_JAVA_FIELD(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
GET_JAVA_FIELD(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
GET_JAVA_FIELD(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
GET_JAVA_FIELD(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
GET_JAVA_FIELD(Object,l);
break;
#undef GET_JAVA_FIELD
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return jsj_ConvertJavaValueToJSValue(cx, jEnv, signature, &java_value, vp);
}
JSBool
jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jclass java_obj, jsval js_val)
{
JSBool is_static_field, is_local_ref;
int dummy_cost;
jvalue java_value;
JavaSignature *signature;
JavaSignatureChar field_type;
jfieldID fieldID = field_spec->fieldID;
is_static_field = field_spec->modifiers & ACC_STATIC;
#define SET_JAVA_FIELD(Type,member) \
PR_BEGIN_MACRO \
if (is_static_field) { \
(*jEnv)->SetStatic##Type##Field(jEnv, java_obj, fieldID, \
java_value.member); \
} else { \
(*jEnv)->Set##Type##Field(jEnv, java_obj, fieldID,java_value.member);\
} \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_UnexpectedJavaError(cx, jEnv, "Error assigning to Java field"); \
return JS_FALSE; \
} \
PR_END_MACRO
signature = field_spec->signature;
if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, signature, &dummy_cost,
&java_value, &is_local_ref))
return JS_FALSE;
field_type = signature->type;
switch(field_type) {
case JAVA_SIGNATURE_BYTE:
SET_JAVA_FIELD(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
SET_JAVA_FIELD(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
SET_JAVA_FIELD(Short,s);
break;
case JAVA_SIGNATURE_INT:
SET_JAVA_FIELD(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
SET_JAVA_FIELD(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
SET_JAVA_FIELD(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
SET_JAVA_FIELD(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
SET_JAVA_FIELD(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
SET_JAVA_FIELD(Object,l);
if (is_local_ref)
(*jEnv)->DeleteLocalRef(jEnv, java_value.l);
break;
#undef SET_JAVA_FIELD
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return JS_TRUE;
}

View File

@@ -0,0 +1,487 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This is a copy of the NSPR hash-table library, but it has been slightly
* modified to allow an additional argument to be passed into the hash
* key-comparision function. This is used to maintain thread-safety by
* passing in a JNIEnv pointer to the key-comparison function rather
* than storing it in a global. All types,function names, etc. have
* been renamed from their original NSPR names to protect the innocent.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_hash.h"
#include "prtypes.h"
#ifdef NSPR20
# include "prlog.h"
# include "prbit.h"
#else
# include "prassert.h"
#endif
/* Compute the number of buckets in ht */
#define NBUCKETS(ht) (1 << (JSJ_HASH_BITS - (ht)->shift))
/* The smallest table has 16 buckets */
#define MINBUCKETSLOG2 4
#define MINBUCKETS (1 << MINBUCKETSLOG2)
/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
#define OVERLOADED(n) ((n) - ((n) >> 3))
/* Compute the number of entries below which we shrink the table by half */
#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
/*
** Stubs for default hash allocator ops.
*/
static void *
DefaultAllocTable(void *pool, size_t size)
{
#if defined(XP_MAC)
#pragma unused (pool)
#endif
return malloc(size);
}
static void
DefaultFreeTable(void *pool, void *item)
{
#if defined(XP_MAC)
#pragma unused (pool)
#endif
free(item);
}
static JSJHashEntry *
DefaultAllocEntry(void *pool, const void *key)
{
#if defined(XP_MAC)
#pragma unused (pool,key)
#endif
return malloc(sizeof(JSJHashEntry));
}
static void
DefaultFreeEntry(void *pool, JSJHashEntry *he, PRUintn flag)
{
#if defined(XP_MAC)
#pragma unused (pool)
#endif
if (flag == HT_FREE_ENTRY)
free(he);
}
static JSJHashAllocOps defaultHashAllocOps = {
DefaultAllocTable, DefaultFreeTable,
DefaultAllocEntry, DefaultFreeEntry
};
PR_IMPLEMENT(JSJHashTable *)
JSJ_NewHashTable(PRUint32 n, JSJHashFunction keyHash,
JSJHashComparator keyCompare, JSJHashComparator valueCompare,
JSJHashAllocOps *allocOps, void *allocPriv)
{
JSJHashTable *ht;
PRUint32 nb;
if (n <= MINBUCKETS) {
n = MINBUCKETSLOG2;
} else {
n = PR_CeilingLog2(n);
if ((PRInt32)n < 0)
return 0;
}
if (!allocOps) allocOps = &defaultHashAllocOps;
ht = (*allocOps->allocTable)(allocPriv, sizeof *ht);
if (!ht)
return 0;
memset(ht, 0, sizeof *ht);
ht->shift = JSJ_HASH_BITS - n;
n = 1 << n;
#if defined(XP_PC) && !defined(_WIN32)
if (n > 16000) {
(*allocOps->freeTable)(allocPriv, ht);
return 0;
}
#endif /* WIN16 */
nb = n * sizeof(JSJHashEntry *);
ht->buckets = (*allocOps->allocTable)(allocPriv, nb);
if (!ht->buckets) {
(*allocOps->freeTable)(allocPriv, ht);
return 0;
}
memset(ht->buckets, 0, nb);
ht->keyHash = keyHash;
ht->keyCompare = keyCompare;
ht->valueCompare = valueCompare;
ht->allocOps = allocOps;
ht->allocPriv = allocPriv;
return ht;
}
PR_IMPLEMENT(void)
JSJ_HashTableDestroy(JSJHashTable *ht)
{
PRUint32 i, n;
JSJHashEntry *he, *next;
JSJHashAllocOps *allocOps = ht->allocOps;
void *allocPriv = ht->allocPriv;
n = NBUCKETS(ht);
for (i = 0; i < n; i++) {
for (he = ht->buckets[i]; he; he = next) {
next = he->next;
(*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
}
}
#ifdef DEBUG
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
#endif
(*allocOps->freeTable)(allocPriv, ht->buckets);
#ifdef DEBUG
memset(ht, 0xDB, sizeof *ht);
#endif
(*allocOps->freeTable)(allocPriv, ht);
}
/*
** Multiplicative hash, from Knuth 6.4.
*/
#define GOLDEN_RATIO 0x9E3779B9U
PR_IMPLEMENT(JSJHashEntry **)
JSJ_HashTableRawLookup(JSJHashTable *ht, JSJHashNumber keyHash, const void *key, void *arg)
{
JSJHashEntry *he, **hep, **hep0;
JSJHashNumber h;
#ifdef HASHMETER
ht->nlookups++;
#endif
h = keyHash * GOLDEN_RATIO;
h >>= ht->shift;
hep = hep0 = &ht->buckets[h];
while ((he = *hep) != 0) {
if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key, arg)) {
/* Move to front of chain if not already there */
if (hep != hep0) {
*hep = he->next;
he->next = *hep0;
*hep0 = he;
}
return hep0;
}
hep = &he->next;
#ifdef HASHMETER
ht->nsteps++;
#endif
}
return hep;
}
PR_IMPLEMENT(JSJHashEntry *)
JSJ_HashTableRawAdd(JSJHashTable *ht, JSJHashEntry **hep,
JSJHashNumber keyHash, const void *key, void *value,
void *arg)
{
PRUint32 i, n;
JSJHashEntry *he, *next, **oldbuckets;
PRUint32 nb;
/* Grow the table if it is overloaded */
n = NBUCKETS(ht);
if (ht->nentries >= OVERLOADED(n)) {
#ifdef HASHMETER
ht->ngrows++;
#endif
ht->shift--;
oldbuckets = ht->buckets;
#if defined(XP_PC) && !defined(_WIN32)
if (2 * n > 16000)
return 0;
#endif /* WIN16 */
nb = 2 * n * sizeof(JSJHashEntry *);
ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb);
if (!ht->buckets) {
ht->buckets = oldbuckets;
return 0;
}
memset(ht->buckets, 0, nb);
for (i = 0; i < n; i++) {
for (he = oldbuckets[i]; he; he = next) {
next = he->next;
hep = JSJ_HashTableRawLookup(ht, he->keyHash, he->key, arg);
PR_ASSERT(*hep == 0);
he->next = 0;
*hep = he;
}
}
#ifdef DEBUG
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
#endif
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
}
/* Make a new key value entry */
he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
if (!he)
return 0;
he->keyHash = keyHash;
he->key = key;
he->value = value;
he->next = *hep;
*hep = he;
ht->nentries++;
return he;
}
PR_IMPLEMENT(JSJHashEntry *)
JSJ_HashTableAdd(JSJHashTable *ht, const void *key, void *value, void *arg)
{
JSJHashNumber keyHash;
JSJHashEntry *he, **hep;
keyHash = (*ht->keyHash)(key, arg);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
if ((he = *hep) != 0) {
/* Hit; see if values match */
if ((*ht->valueCompare)(he->value, value, arg)) {
/* key,value pair is already present in table */
return he;
}
if (he->value)
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
he->value = value;
return he;
}
return JSJ_HashTableRawAdd(ht, hep, keyHash, key, value, arg);
}
PR_IMPLEMENT(void)
JSJ_HashTableRawRemove(JSJHashTable *ht, JSJHashEntry **hep, JSJHashEntry *he, void *arg)
{
PRUint32 i, n;
JSJHashEntry *next, **oldbuckets;
PRUint32 nb;
*hep = he->next;
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
/* Shrink table if it's underloaded */
n = NBUCKETS(ht);
if (--ht->nentries < UNDERLOADED(n)) {
#ifdef HASHMETER
ht->nshrinks++;
#endif
ht->shift++;
oldbuckets = ht->buckets;
nb = n * sizeof(JSJHashEntry*) / 2;
ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb);
if (!ht->buckets) {
ht->buckets = oldbuckets;
return;
}
memset(ht->buckets, 0, nb);
for (i = 0; i < n; i++) {
for (he = oldbuckets[i]; he; he = next) {
next = he->next;
hep = JSJ_HashTableRawLookup(ht, he->keyHash, he->key, arg);
PR_ASSERT(*hep == 0);
he->next = 0;
*hep = he;
}
}
#ifdef DEBUG
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
#endif
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
}
}
PR_IMPLEMENT(PRBool)
JSJ_HashTableRemove(JSJHashTable *ht, const void *key, void *arg)
{
JSJHashNumber keyHash;
JSJHashEntry *he, **hep;
keyHash = (*ht->keyHash)(key, arg);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
if ((he = *hep) == 0)
return PR_FALSE;
/* Hit; remove element */
JSJ_HashTableRawRemove(ht, hep, he, arg);
return PR_TRUE;
}
PR_IMPLEMENT(void *)
JSJ_HashTableLookup(JSJHashTable *ht, const void *key, void *arg)
{
JSJHashNumber keyHash;
JSJHashEntry *he, **hep;
keyHash = (*ht->keyHash)(key, arg);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
if ((he = *hep) != 0) {
return he->value;
}
return 0;
}
/*
** Iterate over the entries in the hash table calling func for each
** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
** Return a count of the number of elements scanned.
*/
PR_IMPLEMENT(int)
JSJ_HashTableEnumerateEntries(JSJHashTable *ht, JSJHashEnumerator f, void *arg)
{
JSJHashEntry *he, **hep;
PRUint32 i, nbuckets;
int rv, n = 0;
JSJHashEntry *todo = 0;
nbuckets = NBUCKETS(ht);
for (i = 0; i < nbuckets; i++) {
hep = &ht->buckets[i];
while ((he = *hep) != 0) {
rv = (*f)(he, n, arg);
n++;
if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
*hep = he->next;
if (rv & HT_ENUMERATE_REMOVE) {
he->next = todo;
todo = he;
}
} else {
hep = &he->next;
}
if (rv & HT_ENUMERATE_STOP) {
goto out;
}
}
}
out:
hep = &todo;
while ((he = *hep) != 0) {
JSJ_HashTableRawRemove(ht, hep, he, arg);
}
return n;
}
#ifdef HASHMETER
#include <math.h>
#include <stdio.h>
PR_IMPLEMENT(void)
JSJ_HashTableDumpMeter(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp)
{
double mean, variance;
PRUint32 nchains, nbuckets;
PRUint32 i, n, maxChain, maxChainLen;
JSJHashEntry *he;
variance = 0;
nchains = 0;
maxChainLen = 0;
nbuckets = NBUCKETS(ht);
for (i = 0; i < nbuckets; i++) {
he = ht->buckets[i];
if (!he)
continue;
nchains++;
for (n = 0; he; he = he->next)
n++;
variance += n * n;
if (n > maxChainLen) {
maxChainLen = n;
maxChain = i;
}
}
mean = (double)ht->nentries / nchains;
variance = fabs(variance / nchains - mean * mean);
fprintf(fp, "\nHash table statistics:\n");
fprintf(fp, " number of lookups: %u\n", ht->nlookups);
fprintf(fp, " number of entries: %u\n", ht->nentries);
fprintf(fp, " number of grows: %u\n", ht->ngrows);
fprintf(fp, " number of shrinks: %u\n", ht->nshrinks);
fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps
/ ht->nlookups);
fprintf(fp, "mean hash chain length: %g\n", mean);
fprintf(fp, " standard deviation: %g\n", sqrt(variance));
fprintf(fp, " max hash chain length: %u\n", maxChainLen);
fprintf(fp, " max hash chain: [%u]\n", maxChain);
for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
break;
}
#endif /* HASHMETER */
PR_IMPLEMENT(int)
JSJ_HashTableDump(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp)
{
int count;
count = JSJ_HashTableEnumerateEntries(ht, dump, fp);
#ifdef HASHMETER
JSJ_HashTableDumpMeter(ht, dump, fp);
#endif
return count;
}
PR_IMPLEMENT(JSJHashNumber)
JSJ_HashString(const void *key)
{
JSJHashNumber h;
const unsigned char *s;
h = 0;
for (s = key; *s; s++)
h = (h >> 28) ^ (h << 4) ^ *s;
return h;
}
PR_IMPLEMENT(int)
JSJ_CompareStrings(const void *v1, const void *v2)
{
return strcmp(v1, v2) == 0;
}
PR_IMPLEMENT(int)
JSJ_CompareValues(const void *v1, const void *v2)
{
return v1 == v2;
}

View File

@@ -0,0 +1,141 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This is a copy of the NSPR hash-table library, but it has been slightly
* modified to allow an additional argument to be passed into the hash
* key-comparision function. This is used to maintain thread-safety by
* passing in a JNIEnv pointer to the key-comparison function rather
* than storing it in a global. All types,function names, etc. have
* been renamed from their original NSPR names to protect the innocent.
*/
#ifndef jsj_hash_h___
#define jsj_hash_h___
/*
* API to portable hash table code.
*/
#include <stddef.h>
#include <stdio.h>
#include "prtypes.h"
PR_BEGIN_EXTERN_C
typedef struct JSJHashEntry JSJHashEntry;
typedef struct JSJHashTable JSJHashTable;
typedef PRUint32 JSJHashNumber;
#define JSJ_HASH_BITS 32
typedef JSJHashNumber (*JSJHashFunction)(const void *key, void *arg);
typedef PRIntn (*JSJHashComparator)(const void *v1, const void *v2, void *arg);
typedef PRIntn (*JSJHashEnumerator)(JSJHashEntry *he, PRIntn i, void *arg);
/* Flag bits in JSJHashEnumerator's return value */
#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */
#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */
#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */
#define HT_ENUMERATE_UNHASH 4 /* just unhash the current entry */
typedef struct JSJHashAllocOps {
void * (*allocTable)(void *pool, size_t size);
void (*freeTable)(void *pool, void *item);
JSJHashEntry * (*allocEntry)(void *pool, const void *key);
void (*freeEntry)(void *pool, JSJHashEntry *he, PRUintn flag);
} JSJHashAllocOps;
#define HT_FREE_VALUE 0 /* just free the entry's value */
#define HT_FREE_ENTRY 1 /* free value and entire entry */
struct JSJHashEntry {
JSJHashEntry *next; /* hash chain linkage */
JSJHashNumber keyHash; /* key hash function result */
const void *key; /* ptr to opaque key */
void *value; /* ptr to opaque value */
};
struct JSJHashTable {
JSJHashEntry **buckets; /* vector of hash buckets */
PRUint32 nentries; /* number of entries in table */
PRUint32 shift; /* multiplicative hash shift */
JSJHashFunction keyHash; /* key hash function */
JSJHashComparator keyCompare; /* key comparison function */
JSJHashComparator valueCompare; /* value comparison function */
JSJHashAllocOps *allocOps; /* allocation operations */
void *allocPriv; /* allocation private data */
#ifdef HASHMETER
PRUint32 nlookups; /* total number of lookups */
PRUint32 nsteps; /* number of hash chains traversed */
PRUint32 ngrows; /* number of table expansions */
PRUint32 nshrinks; /* number of table contractions */
#endif
};
/*
* Create a new hash table.
* If allocOps is null, use default allocator ops built on top of malloc().
*/
PR_EXTERN(JSJHashTable *)
JSJ_NewHashTable(PRUint32 n, JSJHashFunction keyHash,
JSJHashComparator keyCompare, JSJHashComparator valueCompare,
JSJHashAllocOps *allocOps, void *allocPriv);
PR_EXTERN(void)
JSJ_HashTableDestroy(JSJHashTable *ht);
/* Low level access methods */
PR_EXTERN(JSJHashEntry **)
JSJ_HashTableRawLookup(JSJHashTable *ht, JSJHashNumber keyHash, const void *key, void *arg);
PR_EXTERN(JSJHashEntry *)
JSJ_HashTableRawAdd(JSJHashTable *ht, JSJHashEntry **hep, JSJHashNumber keyHash,
const void *key, void *value, void *arg);
PR_EXTERN(void)
JSJ_HashTableRawRemove(JSJHashTable *ht, JSJHashEntry **hep, JSJHashEntry *he, void *arg);
/* Higher level access methods */
PR_EXTERN(JSJHashEntry *)
JSJ_HashTableAdd(JSJHashTable *ht, const void *key, void *value, void *arg);
PR_EXTERN(PRBool)
JSJ_HashTableRemove(JSJHashTable *ht, const void *key, void *arg);
PR_EXTERN(PRIntn)
JSJ_HashTableEnumerateEntries(JSJHashTable *ht, JSJHashEnumerator f, void *arg);
PR_EXTERN(void *)
JSJ_HashTableLookup(JSJHashTable *ht, const void *key, void *arg);
PR_EXTERN(PRIntn)
JSJ_HashTableDump(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp);
/* General-purpose C string hash function. */
PR_EXTERN(JSJHashNumber)
JSJ_HashString(const void *key);
/* Compare strings using strcmp(), return true if equal. */
PR_EXTERN(int)
JSJ_CompareStrings(const void *v1, const void *v2);
/* Stub function just returns v1 == v2 */
PR_EXTERN(PRIntn)
JSJ_CompareValues(const void *v1, const void *v2);
PR_END_EXTERN_C
#endif /* jsj_hash_h___ */

File diff suppressed because it is too large Load Diff

View File

View File

@@ -0,0 +1,552 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Declarations of private (internal) functions/data/types for
* JavaScript <==> Java communication.
*
*/
#ifndef _JSJAVA_PVT_H
#define _JSJAVA_PVT_H
#include "prtypes.h"
/* NSPR1 compatibility definitions */
#ifdef NSPR20
# include "prprf.h"
# include "prlog.h"
# include "plhash.h" /* NSPR hash-tables */
#else
# include "prprintf.h"
# include "prassert.h"
# include "prhash.h" /* NSPR hash-tables */
# define PRHashNumber prhashcode
#endif
#ifdef XP_MAC
# include "prosdep.h"
#endif
#include "jsj_hash.h" /* Hash tables */
#include "jni.h" /* Java Native Interface */
#include "jsapi.h" /* JavaScript engine API */
#include "jsjava.h" /* LiveConnect public API */
/*************************** Type Declarations ******************************/
/* Forward type declarations */
typedef struct JavaMemberDescriptor JavaMemberDescriptor;
typedef struct JavaMethodSpec JavaMethodSpec;
typedef struct JavaClassDescriptor JavaClassDescriptor;
typedef struct JavaClassDescriptor JavaSignature;
typedef struct CapturedJSError CapturedJSError;
typedef struct JavaMemberVal JavaMemberVal;
/*
* This enum uses the same character encoding used by the JDK to encode
* Java type signatures, but the enum is easier to debug/compile with.
*/
typedef enum {
JAVA_SIGNATURE_ARRAY = '[',
JAVA_SIGNATURE_BYTE = 'B',
JAVA_SIGNATURE_CHAR = 'C',
JAVA_SIGNATURE_CLASS = 'L',
JAVA_SIGNATURE_FLOAT = 'F',
JAVA_SIGNATURE_DOUBLE = 'D',
JAVA_SIGNATURE_INT = 'I',
JAVA_SIGNATURE_LONG = 'J',
JAVA_SIGNATURE_SHORT = 'S',
JAVA_SIGNATURE_VOID = 'V',
JAVA_SIGNATURE_BOOLEAN = 'Z',
JAVA_SIGNATURE_UNKNOWN = 0
} JavaSignatureChar;
/* The signature of a Java method consists of the signatures of all its
arguments and its return type signature. */
typedef struct JavaMethodSignature {
jsize num_args; /* Length of arg_signatures array */
JavaSignature ** arg_signatures; /* Array of argument signatures */
JavaSignature * return_val_signature; /* Return type signature */
} JavaMethodSignature;
/* A descriptor for the reflection of a single Java field */
typedef struct JavaFieldSpec {
jfieldID fieldID; /* JVM opaque access handle for field */
JavaSignature * signature; /* Java type of field */
int modifiers; /* Bitfield indicating field qualifiers */
const char * name; /* UTF8; TODO - Should support Unicode field names */
} JavaFieldSpec;
/* A descriptor for the reflection of a single Java method.
Each overloaded method has a separate corresponding JavaMethodSpec. */
struct JavaMethodSpec {
jmethodID methodID; /* JVM opaque access handle for method */
JavaMethodSignature signature;
const char * name; /* UTF8; TODO - Should support Unicode method names */
JavaMethodSpec * next; /* next method in chain of overloaded methods */
};
/*
* A descriptor for the reflection of a single member of a Java object.
* This can represent one or more Java methods and/or a single field.
* (When there is more than one method attached to a single JavaMemberDescriptor
* they are overloaded methods sharing the same simple name.) This same
* descriptor type is used for both static or instance members.
*/
struct JavaMemberDescriptor {
const char * name; /* simple name of field and/or method */
jsid id; /* hashed name for quick JS property lookup */
JavaFieldSpec * field; /* field with the given name, if any */
JavaMethodSpec * methods; /* Overloaded methods which share the same name, if any */
JavaMemberDescriptor * next; /* next descriptor in same defining class */
JSObject * invoke_func_obj; /* If non-null, JSFunction obj to invoke method */
};
/* This is the native portion of a reflected Java class */
struct JavaClassDescriptor {
const char * name; /* Name of class, e.g. "java/lang/Byte" */
JavaSignatureChar type; /* class category: primitive type, object, array */
jclass java_class; /* Opaque JVM handle to corresponding java.lang.Class */
int num_instance_members;
int num_static_members;
JSBool instance_members_reflected;
JavaMemberDescriptor * instance_members;
JSBool static_members_reflected;
JavaMemberDescriptor * static_members;
JavaMemberDescriptor * constructors;
int modifiers; /* Class declaration qualifiers,
e.g. abstract, private */
int ref_count; /* # of references to this struct */
JavaSignature * array_component_signature; /* Only non-NULL for array classes */
};
/* This is the native portion of a reflected Java method or field */
struct JavaMemberVal {
jsval field_val; /* Captured value of Java field */
jsval invoke_method_func_val; /* JSFunction wrapper around Java method invoker */
JavaMemberDescriptor * descriptor;
JavaMemberVal * next;
};
/* This is the native portion of a reflected Java object */
typedef struct JavaObjectWrapper {
jobject java_obj; /* Opaque JVM ref to Java object */
JavaClassDescriptor * class_descriptor; /* Java class info */
} JavaObjectWrapper;
/* These are definitions of the Java class/method/field modifier bits.
These really shouldn't be hard-coded here. Rather,
they should be read from java.lang.reflect.Modifier */
#define ACC_PUBLIC 0x0001 /* visible to everyone */
#define ACC_STATIC 0x0008 /* instance variable is static */
#define ACC_FINAL 0x0010 /* no further subclassing,overriding */
#define ACC_INTERFACE 0x0200 /* class is an interface */
#define ACC_ABSTRACT 0x0400 /* no definition provided */
/* A JSJavaVM structure must be created for each Java VM that is accessed
via LiveConnect */
struct JSJavaVM {
/* TODO - all LiveConnect global variables should be migrated into this
structure in order to allow more than one LiveConnect-enabled
Java VM to exist within the same process. */
SystemJavaVM * java_vm;
JNIEnv * main_thread_env; /* Main-thread Java environment */
JSBool jsj_created_java_vm;
int num_attached_threads;
JSJavaVM * next; /* next VM among all created VMs */
};
/* Per-thread state that encapsulates the connection to the Java VM */
struct JSJavaThreadState {
const char * name; /* Thread name, for debugging */
JSJavaVM * jsjava_vm; /* All per-JVM state */
JNIEnv * jEnv; /* Per-thread opaque handle to Java VM */
CapturedJSError * pending_js_errors; /* JS errors to be thrown as Java exceptions */
JSContext * cx; /* current JS context for thread */
JSJavaThreadState * next; /* next thread state among all created threads */
};
struct JavaToJSSavedState {
JSErrorReporter error_reporter;
JSJavaThreadState* java_jsj_env;
};
typedef struct JavaToJSSavedState JavaToJSSavedState;
/******************************** Globals ***********************************/
extern JSJCallbacks *JSJ_callbacks;
/* JavaScript classes that reflect Java objects */
extern JSClass JavaObject_class;
extern JSClass JavaArray_class;
extern JSClass JavaClass_class;
extern JSClass JavaMember_class;
/*
* Opaque JVM handles to Java classes, methods and objects required for
* Java reflection. These are computed and cached during initialization.
* TODO: These should be moved inside the JSJavaVM struct
*/
extern jclass jlObject; /* java.lang.Object */
extern jclass jlrConstructor; /* java.lang.reflect.Constructor */
extern jclass jlThrowable; /* java.lang.Throwable */
extern jclass jlSystem; /* java.lang.System */
extern jclass jlClass; /* java.lang.Class */
extern jclass jlBoolean; /* java.lang.Boolean */
extern jclass jlDouble; /* java.lang.Double */
extern jclass jlString; /* java.lang.String */
extern jclass njJSObject; /* netscape.javascript.JSObject */
extern jclass njJSException; /* netscape.javascript.JSException */
extern jclass njJSUtil; /* netscape.javascript.JSUtil */
extern jmethodID jlClass_getMethods; /* java.lang.Class.getMethods() */
extern jmethodID jlClass_getConstructors; /* java.lang.Class.getConstructors() */
extern jmethodID jlClass_getFields; /* java.lang.Class.getFields() */
extern jmethodID jlClass_getName; /* java.lang.Class.getName() */
extern jmethodID jlClass_getComponentType; /* java.lang.Class.getComponentType() */
extern jmethodID jlClass_getModifiers; /* java.lang.Class.getModifiers() */
extern jmethodID jlClass_isArray; /* java.lang.Class.isArray() */
extern jmethodID jlrMethod_getName; /* java.lang.reflect.Method.getName() */
extern jmethodID jlrMethod_getParameterTypes; /* java.lang.reflect.Method.getParameterTypes() */
extern jmethodID jlrMethod_getReturnType; /* java.lang.reflect.Method.getReturnType() */
extern jmethodID jlrMethod_getModifiers; /* java.lang.reflect.Method.getModifiers() */
extern jmethodID jlrConstructor_getParameterTypes; /* java.lang.reflect.Constructor.getParameterTypes() */
extern jmethodID jlrConstructor_getModifiers; /* java.lang.reflect.Constructor.getModifiers() */
extern jmethodID jlrField_getName; /* java.lang.reflect.Field.getName() */
extern jmethodID jlrField_getType; /* java.lang.reflect.Field.getType() */
extern jmethodID jlrField_getModifiers; /* java.lang.reflect.Field.getModifiers() */
extern jmethodID jlThrowable_getMessage; /* java.lang.Throwable.getMessage() */
extern jmethodID jlThrowable_toString; /* java.lang.Throwable.toString() */
extern jmethodID jlBoolean_Boolean; /* java.lang.Boolean constructor */
extern jmethodID jlBoolean_booleanValue; /* java.lang.Boolean.booleanValue() */
extern jmethodID jlDouble_Double; /* java.lang.Double constructor */
extern jmethodID jlDouble_doubleValue; /* java.lang.Double.doubleValue() */
extern jmethodID jlSystem_identityHashCode; /* java.lang.System.identityHashCode() */
extern jobject jlVoid_TYPE; /* java.lang.Void.TYPE value */
extern jmethodID njJSException_JSException; /* netscape.javascipt.JSexception constructor */
extern jmethodID njJSObject_JSObject; /* netscape.javascript.JSObject constructor */
extern jmethodID njJSUtil_getStackTrace; /* netscape.javascript.JSUtil.getStackTrace() */
extern jfieldID njJSObject_internal; /* netscape.javascript.JSObject.internal */
extern jfieldID njJSException_lineno; /* netscape.javascript.JSException.lineno */
extern jfieldID njJSException_tokenIndex; /* netscape.javascript.JSException.tokenIndex */
extern jfieldID njJSException_source; /* netscape.javascript.JSException.source */
extern jfieldID njJSException_filename; /* netscape.javascript.JSException.filename */
/**************** Java <==> JS conversions and Java types *******************/
extern JSBool
jsj_ComputeJavaClassSignature(JSContext *cx,
JavaSignature *signature,
jclass java_class);
extern const char *
jsj_ConvertJavaSignatureToString(JSContext *cx, JavaSignature *signature);
extern const char *
jsj_ConvertJavaSignatureToHRString(JSContext *cx,
JavaSignature *signature);
extern JavaMethodSignature *
jsj_InitJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, jobject method,
JavaMethodSignature *method_signature);
extern const char *
jsj_ConvertJavaMethodSignatureToString(JSContext *cx,
JavaMethodSignature *method_signature);
extern const char *
jsj_ConvertJavaMethodSignatureToHRString(JSContext *cx,
const char *method_name,
JavaMethodSignature *method_signature);
extern void
jsj_PurgeJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, JavaMethodSignature *signature);
extern JSBool
jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jvalue *java_value, JSBool *is_local_refp);
extern JSBool
jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jobject *java_value, JSBool *is_local_refp);
extern jstring
jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str);
extern JSBool
jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv, JavaSignature *signature,
jvalue *java_value, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSValue(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp);
extern JSString *
jsj_ConvertJavaStringToJSString(JSContext *cx, JNIEnv *jEnv, jstring java_str);
extern JSBool
jsj_ConvertJavaObjectToJSString(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp);
extern JSJavaThreadState *
jsj_enter_js(JNIEnv *jEnv, jobject java_wrapper_obj,
JSContext **cxp, JSObject **js_objp, JavaToJSSavedState* saved_state);
extern JSBool
jsj_exit_js(JSContext *cx, JSJavaThreadState *jsj_env, JavaToJSSavedState* original_state);
extern JavaClassDescriptor *
jsj_get_jlObject_descriptor(JSContext *cx, JNIEnv *jEnv);
extern JSBool
jsj_remove_js_obj_reflection_from_hashtable(JSContext *cx, JSObject *js_obj);
extern JSBool
jsj_init_js_obj_reflections_table();
/************************ Java package reflection **************************/
extern JSBool
jsj_init_JavaPackage(JSContext *, JSObject *,
JavaPackageDef *predefined_packages);
/************************* Java class reflection ***************************/
extern JSBool
jsj_init_JavaClass(JSContext *cx, JSObject *global_obj);
extern void
jsj_DiscardJavaClassReflections(JNIEnv *jEnv);
extern const char *
jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class);
extern JavaClassDescriptor *
jsj_GetJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class);
extern void
jsj_ReleaseJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor);
extern JSObject *
jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
const char *unqualified_class_name,
jclass jclazz);
extern JavaMemberDescriptor *
jsj_GetJavaMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name);
extern JavaMemberDescriptor *
jsj_LookupJavaMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id);
extern JavaMemberDescriptor *
jsj_LookupJavaStaticMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id);
extern JavaMemberDescriptor *
jsj_GetJavaStaticMemberDescriptor(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name);
extern JavaMemberDescriptor *
jsj_GetJavaClassConstructors(JSContext *cx,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_LookupJavaClassConstructors(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_GetClassInstanceMembers(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_GetClassStaticMembers(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JSBool
jsj_InitJavaClassReflectionsTable();
/************************* Java field reflection ***************************/
extern JSBool
jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval *vp);
extern JSBool
jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval js_val);
extern JSBool
jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_fields);
extern void
jsj_DestroyFieldSpec(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field);
/************************* Java method reflection ***************************/
extern JSBool
jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_ReflectJavaMethods(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_methods);
extern void
jsj_DestroyMethodSpec(JSContext *cx, JNIEnv *jEnv, JavaMethodSpec *method_spec);
/************************* Java member reflection ***************************/
extern JSBool
jsj_init_JavaMember(JSContext *, JSObject *);
extern JSBool
jsj_ReflectJavaMethodsAndFields(JSContext *cx, JavaClassDescriptor *class_descriptor,
JSBool reflect_only_statics);
extern JSObject *
jsj_CreateJavaMember(JSContext *cx, jsval method_val, jsval field_val);
/************************* Java object reflection **************************/
extern JSBool
jsj_init_JavaObject(JSContext *, JSObject *);
extern JSObject *
jsj_WrapJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_obj, jclass java_class);
extern void
jsj_DiscardJavaObjReflections(JNIEnv *jEnv);
extern JSBool
JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
extern void
JavaObject_finalize(JSContext *cx, JSObject *obj);
extern JSBool
JavaObject_resolve(JSContext *cx, JSObject *obj, jsval id);
extern JSBool
JavaObject_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
extern JSBool
JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
/************************* Java array reflection ***************************/
extern JSBool
jsj_init_JavaArray(JSContext *cx, JSObject *global_obj);
extern JSBool
jsj_GetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array,
jsize index, JavaSignature *array_component_signature,
jsval *vp);
extern JSBool
jsj_SetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array,
jsize index, JavaSignature *array_component_signature,
jsval js_val);
/********************* JavaScript object reflection ************************/
extern jobject
jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj);
extern JSObject *
jsj_UnwrapJSObjectWrapper(JNIEnv *jEnv, jobject java_wrapper_obj);
extern void
jsj_ClearPendingJSErrors(JSJavaThreadState *jsj_env);
extern JSBool
jsj_ReportUncaughtJSException(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception);
/**************************** Utilities ************************************/
extern void
jsj_ReportJavaError(JSContext *cx, JNIEnv *env, const char *format, ...);
extern void
jsj_UnexpectedJavaError(JSContext *cx, JNIEnv *env, const char *format, ...);
extern const char *
jsj_GetJavaErrorMessage(JNIEnv *env);
extern void
jsj_LogError(const char *error_msg);
PR_CALLBACK JSJHashNumber
jsj_HashJavaObject(const void *key, void* env);
PR_CALLBACK intN
jsj_JavaObjectComparator(const void *v1, const void *v2, void *arg);
extern JSJavaThreadState *
jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp);
extern void
jsj_MakeJNIClassname(char *jClassName);
extern const char *
jsj_ClassNameOfJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_object);
extern jsize
jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array);
extern JSBool
JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp);
extern const char *
jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr);
JSJavaThreadState *
jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp);
JSJavaThreadState *
jsj_SetJavaJSJEnv(JSJavaThreadState* java_jsj_env);
#ifdef DEBUG
#define DEBUG_LOG(args) printf args
#endif
#define JS_FREE_IF(cx, x) \
PR_BEGIN_MACRO \
if (x) \
JS_free(cx, x); \
PR_END_MACRO
#endif /* _JSJAVA_PVT_H */

View File

@@ -0,0 +1,361 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains low-level utility code.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h" /* External LiveConnect API */
/*
* This is a hash-table utility routine that computes the hash code of a Java
* object by calling java.lang.System.identityHashCode()
*/
PR_CALLBACK JSJHashNumber
jsj_HashJavaObject(const void *key, void* env)
{
PRHashNumber hash_code;
jobject java_obj;
JNIEnv *jEnv;
java_obj = (jobject)key;
jEnv = (JNIEnv*) env;
hash_code = (*jEnv)->CallStaticIntMethod(jEnv, jlSystem,
jlSystem_identityHashCode, java_obj);
PR_ASSERT(!(*jEnv)->ExceptionOccurred(jEnv));
return hash_code;
}
/*
* This is a hash-table utility routine for comparing two Java objects.
* It's not possible to use the == operator to directly compare two jobject's,
* since they're opaque references and aren't guaranteed to be simple pointers
* or handles (though they may be in some JVM implementations). Instead,
* use the JNI routine for comparing the two objects.
*/
PR_CALLBACK intN
jsj_JavaObjectComparator(const void *v1, const void *v2, void *arg)
{
jobject java_obj1, java_obj2;
JNIEnv *jEnv;
jEnv = (JNIEnv*)arg;
java_obj1 = (jobject)v1;
java_obj2 = (jobject)v2;
if (java_obj1 == java_obj2)
return 1;
return (*jEnv)->IsSameObject(jEnv, java_obj1, java_obj2);
}
/*
* Return a UTF8, null-terminated encoding of a Java string. The string must
* be free'ed by the caller.
*
* If an error occurs, returns NULL and calls the JS error reporter.
*/
const char *
jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr)
{
const char *str, *retval;
str = (*jEnv)->GetStringUTFChars(jEnv, jstr, 0);
if (!str) {
jsj_UnexpectedJavaError(cx, jEnv, "Can't get UTF8 characters from "
"Java string");
return NULL;
}
retval = JS_strdup(cx, str);
(*jEnv)->ReleaseStringUTFChars(jEnv, jstr, str);
return retval;
}
JSBool
JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp)
{
const jschar *ucs2;
JSString *jsstr;
jsize ucs2_len;
jsval val;
ucs2 = (*jEnv)->GetStringChars(jEnv, jstr, 0);
if (!ucs2) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain Unicode characters"
"from Java string");
return JS_FALSE;
}
ucs2_len = (*jEnv)->GetStringLength(jEnv, jstr);
jsstr = JS_InternUCStringN(cx, ucs2, ucs2_len);
(*jEnv)->ReleaseStringChars(jEnv, jstr, ucs2);
if (!jsstr)
return JS_FALSE;
val = STRING_TO_JSVAL(jsstr);
JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp);
return JS_TRUE;
}
/* Not used ?
const char *
jsj_ClassNameOfJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_object)
{
jobject java_class;
java_class = (*jEnv)->GetObjectClass(jEnv, java_object);
if (!java_class) {
PR_ASSERT(0);
return NULL;
}
return jsj_GetJavaClassName(cx, jEnv, java_class);
}
*/
/*
* Return, as a C string, the error message associated with a Java exception
* that occurred as a result of a JNI call, preceded by the class name of
* the exception. As a special case, if the class of the exception is
* netscape.javascript.JSException, the exception class name is omitted.
*
* NULL is returned if no Java exception is pending. The caller is
* responsible for free'ing the returned string. On exit, the Java exception
* is *not* cleared.
*/
const char *
jsj_GetJavaErrorMessage(JNIEnv *jEnv)
{
const char *java_error_msg;
char *error_msg = NULL;
jthrowable exception;
jstring java_exception_jstring;
exception = (*jEnv)->ExceptionOccurred(jEnv);
if (exception && jlThrowable_toString) {
java_exception_jstring =
(*jEnv)->CallObjectMethod(jEnv, exception, jlThrowable_toString);
java_error_msg = (*jEnv)->GetStringUTFChars(jEnv, java_exception_jstring, NULL);
error_msg = strdup((char*)java_error_msg);
(*jEnv)->ReleaseStringUTFChars(jEnv, java_exception_jstring, java_error_msg);
#ifdef DEBUG
/* (*jEnv)->ExceptionDescribe(jEnv); */
#endif
}
return error_msg;
}
/*
* Return, as a C string, the JVM stack trace associated with a Java
* exception, as would be printed by java.lang.Throwable.printStackTrace().
* The caller is responsible for free'ing the returned string.
*
* Returns NULL if an error occurs.
*/
static const char *
get_java_stack_trace(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception)
{
const char *backtrace;
jstring backtrace_jstr;
backtrace = NULL;
if (java_exception && njJSUtil_getStackTrace) {
backtrace_jstr = (*jEnv)->CallStaticObjectMethod(jEnv, njJSUtil,
njJSUtil_getStackTrace,
java_exception);
if (!backtrace_jstr) {
jsj_UnexpectedJavaError(cx, jEnv, "Unable to get exception stack trace");
return NULL;
}
backtrace = jsj_DupJavaStringUTF(cx, jEnv, backtrace_jstr);
}
return backtrace;
}
/* Full Java backtrace when Java exceptions reported to JavaScript */
#define REPORT_JAVA_EXCEPTION_STACK_TRACE
/*
* This is a wrapper around JS_ReportError(), useful when an error condition
* is the result of a JVM failure or exception condition. It appends the
* message associated with the pending Java exception to the passed in
* printf-style format string and arguments.
*/
static void
vreport_java_error(JSContext *cx, JNIEnv *jEnv, const char *format, va_list ap)
{
char *error_msg, *js_error_msg;
const char *java_stack_trace;
const char *java_error_msg;
jthrowable java_exception;
java_error_msg = NULL;
java_exception = (*jEnv)->ExceptionOccurred(jEnv);
if (java_exception && njJSException &&
(*jEnv)->IsInstanceOf(jEnv, java_exception, njJSException)) {
(*jEnv)->ExceptionClear(jEnv);
jsj_ReportUncaughtJSException(cx, jEnv, java_exception);
return;
}
js_error_msg = PR_vsmprintf(format, ap);
if (!js_error_msg) {
PR_ASSERT(0); /* Out-of-memory */
return;
}
#ifdef REPORT_JAVA_EXCEPTION_STACK_TRACE
java_stack_trace = get_java_stack_trace(cx, jEnv, java_exception);
if (java_stack_trace) {
error_msg = PR_smprintf("%s\n%s", js_error_msg, java_stack_trace);
free((char*)java_stack_trace);
if (!error_msg) {
PR_ASSERT(0); /* Out-of-memory */
return;
}
} else
#endif
{
java_error_msg = jsj_GetJavaErrorMessage(jEnv);
if (java_error_msg) {
error_msg = PR_smprintf("%s (%s)\n", js_error_msg, java_error_msg);
free((char*)java_error_msg);
free(js_error_msg);
} else {
error_msg = js_error_msg;
}
}
JS_ReportError(cx, error_msg);
/* Important: the Java exception must not be cleared until the reporter
has been called, because the capture_js_error_reports_for_java(),
called from JS_ReportError(), needs to read the exception from the JVM */
(*jEnv)->ExceptionClear(jEnv);
free(error_msg);
}
void
jsj_ReportJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vreport_java_error(cx, env, format, ap);
va_end(ap);
}
/*
* Same as jsj_ReportJavaError, except "internal error: " is prepended
* to message.
*/
void
jsj_UnexpectedJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
{
va_list ap;
const char *format2;
va_start(ap, format);
format2 = PR_smprintf("internal error: %s", format);
if (format2) {
vreport_java_error(cx, env, format2, ap);
free((void*)format2);
}
va_end(ap);
}
/*
* Most LiveConnect errors are signaled by calling JS_ReportError(),
* but in some circumstances, the target JSContext for such errors
* is not determinable, e.g. during initialization. In such cases
* any error messages are routed to this function.
*/
void
jsj_LogError(const char *error_msg)
{
if (JSJ_callbacks && JSJ_callbacks->error_print)
JSJ_callbacks->error_print(error_msg);
else
fputs(error_msg, stderr);
}
jsize
jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array)
{
jsize array_length = (*jEnv)->GetArrayLength(jEnv, java_array);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain array length");
return -1;
}
return array_length;
}
static JSJavaThreadState *the_java_jsj_env = NULL;
JSJavaThreadState *
jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp)
{
JSJavaThreadState *jsj_env;
char *err_msg;
*envp = NULL;
err_msg = NULL;
jsj_env = the_java_jsj_env;
if (jsj_env == NULL)
jsj_env = JSJ_callbacks->map_js_context_to_jsj_thread(cx, &err_msg);
if (!jsj_env) {
if (err_msg) {
JS_ReportError(cx, err_msg);
free(err_msg);
}
return NULL;
}
if (envp)
*envp = jsj_env->jEnv;
return jsj_env;
}
/**
* Since only one Java thread is allowed to enter JavaScript, this function is
* used to enforce the use of that thread's state. The static global the_java_jsj_env
* overrides using JSJ_callbacks->map_js_context_to_jsj_thread, which maps
* native threads to JSJavaThreadStates. This isn't appropriate when Java calls
* JavaScript, as there can be a many to one mapping from Java threads to native
* threads.
*/
JSJavaThreadState *
jsj_SetJavaJSJEnv(JSJavaThreadState* java_jsj_env)
{
JSJavaThreadState *old_jsj_env = the_java_jsj_env;
the_java_jsj_env = java_jsj_env;
return old_jsj_env;
}

View File

@@ -0,0 +1,264 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Publicly exported functions for JavaScript <==> Java communication.
*
*/
#ifndef _JSJAVA_H
#define _JSJAVA_H
#ifndef prtypes_h___
#include "prtypes.h"
#endif
PR_BEGIN_EXTERN_C
#include "jni.h" /* Java Native Interface */
#include "jsapi.h" /* JavaScript engine API */
/*
* A JSJavaVM structure is a wrapper around a JavaVM which incorporates
* additional LiveConnect state.
*/
typedef struct JSJavaVM JSJavaVM;
/* LiveConnect and Java state, one per thread */
typedef struct JSJavaThreadState JSJavaThreadState;
/*
* An opaque type that represents the connection to the Java VM. In stand-alone
* Java environments, this may be a JNI JavaVM object; in the browser environment
* it is a reference to a JVM plugin. A set of callbacks in the JSJCallbacks
* struct allow it to be manipulated.
*/
typedef struct SystemJavaVM SystemJavaVM;
/*
* This callback table provides hooks to external functions that implement
* functionality specific to the embedding. For example, these callbacks are
* necessary in multi-threaded environments or to implement a security
* policy.
*/
typedef struct JSJCallbacks {
/* This callback is invoked when there is no JavaScript execution
environment (JSContext) associated with the current Java thread and
a call is made from Java into JavaScript. (A JSContext is associated
with a Java thread by calling the JSJ_SetJSContextForJavaThread()
function.) This callback is only invoked when Java spontaneously calls
into JavaScript, i.e. it is not called when JS calls into Java which
calls back into JS.
This callback can be used to create a JSContext lazily, or obtain
one from a pool of available JSContexts. The implementation of this
callback can call JSJ_SetJSContextForJavaThread() to avoid any further
callbacks of this type for this Java thread. */
JSContext * (*map_jsj_thread_to_js_context)(JSJavaThreadState *jsj_env,
JNIEnv *jEnv,
char **errp);
/* This callback is invoked whenever a call is made into Java from
JavaScript. It's responsible for mapping from a JavaScript execution
environment (JSContext) to a Java thread. (A JavaContext can only
be associated with one Java thread at a time.) */
JSJavaThreadState * (*map_js_context_to_jsj_thread)(JSContext *cx,
char **errp);
/* This callback implements netscape.javascript.JSObject.getWindow(),
a method named for its behavior in the browser environment, where it
returns the JS "Window" object corresponding to the HTML window that an
applet is embedded within. More generally, it's a way for Java to get
hold of a JS object that has not been explicitly passed to it. */
JSObject * (*map_java_object_to_js_object)(JNIEnv *jEnv, void *pJavaObject,
char **errp);
/* An interim callback function until the LiveConnect security story is
straightened out. This function pointer can be set to NULL. */
JSPrincipals * (*get_JSPrincipals_from_java_caller)(JNIEnv *jEnv, JSContext *pJSContext);
/* The following two callbacks sandwich any JS evaluation performed
from Java. They may be used to implement concurrency constraints, e.g.
by suspending the current thread until some condition is met. In the
browser embedding, these are used to maintain the run-to-completion
semantics of JavaScript. It is acceptable for either function pointer
to be NULL. */
JSBool (*enter_js_from_java)(JNIEnv *jEnv, char **errp);
void (*exit_js)(JNIEnv *jEnv);
/* Most LiveConnect errors are signaled by calling JS_ReportError(), but in
some circumstances, the target JSContext for such errors is not
determinable, e.g. during initialization. In such cases any error
messages are routed to this function. If the function pointer is set to
NULL, error messages are sent to stderr. */
void (*error_print)(const char *error_msg);
/* This enables liveconnect to ask the VM for a java wrapper so that VM gets a chance to
store a mapping between a jsobject and java wrapper. So the unwrapping can be done on the
VM side before calling nsILiveconnect apis. This saves on a round trip request. */
jobject (*get_java_wrapper)(JNIEnv *jEnv, jint jsobject);
/* The following set of methods abstract over the JavaVM object. */
PRBool (*create_java_vm)(SystemJavaVM* *jvm, JNIEnv* *initialEnv, void* initargs);
PRBool (*destroy_java_vm)(SystemJavaVM* jvm, JNIEnv* initialEnv);
JNIEnv* (*attach_current_thread)(SystemJavaVM* jvm);
PRBool (*detach_current_thread)(SystemJavaVM* jvm, JNIEnv* env);
SystemJavaVM* (*get_java_vm)(JNIEnv* env);
/* Reserved for future use */
void * reserved[10];
} JSJCallbacks;
/*===========================================================================*/
/* A flag that denotes that a Java package has no sub-packages other than those
explicitly pre-defined at the time of initialization. An access
to a simple name within such a package, therefore, must either correspond to
one of these explicitly pre-defined sub-packages or to a class within this
package. It is reasonable for LiveConnect to signal an error if a simple
name does not comply with these criteria. */
#define PKG_SYSTEM 1
/* A flag that denotes that a Java package which might contain sub-packages
that are not pre-defined at initialization time, because the sub-packages
may not be the same in all installations. Therefore, an access to a simple
name within such a a package which does not correspond to either a
pre-defined sub-package or to a class, must be assummed to refer to an
unknown sub-package. This behavior may cause bogus JavaPackage objects to be
created if a package name is misspelled, e.g. sun.oi.net. */
#define PKG_USER 2
/* A Java package defined at initialization time. */
typedef struct JavaPackageDef {
const char * name; /* e.g. "java.lang" */
const char * path; /* e.g. "java/lang", or NULL for default */
int flags; /* PKG_USER, PKG_SYSTEM, etc. */
} JavaPackageDef;
/*===========================================================================*/
/* The following two convenience functions present a complete, but simplified
LiveConnect API which is designed to handle the special case of a single
Java-VM, with single-threaded operation, and the use of only one JSContext.
The full API is in the section below. */
/* Initialize the provided JSContext by setting up the JS classes necessary for
reflection and by defining JavaPackage objects for the default Java packages
as properties of global_obj. If java_vm is NULL, a new Java VM is
created, using the provided classpath in addition to any default classpath.
The classpath argument is ignored, however, if java_vm is non-NULL. */
PR_IMPLEMENT(JSBool)
JSJ_SimpleInit(JSContext *cx, JSObject *global_obj,
SystemJavaVM *java_vm, const char *classpath);
/* Free up all resources. Destroy the Java VM if it was created by LiveConnect */
PR_IMPLEMENT(void)
JSJ_SimpleShutdown();
/*===========================================================================*/
/* The "full" LiveConnect API, required when more than one thread, Java VM, or
JSContext is involved. Initialization pseudocode might go roughly like
this:
JSJ_Init() // Setup callbacks
for each JavaVM {
JSJ_ConnectToJavaVM(...)
}
for each JSContext {
JSJ_InitJSContext(...)
}
for each JS evaluation {
run JavaScript code in the JSContext;
}
*/
/* Called once for all instances of LiveConnect to set up callbacks */
PR_IMPLEMENT(void)
JSJ_Init(JSJCallbacks *callbacks);
/* Called once per Java VM, this function initializes the classes, fields, and
methods required for Java reflection. If java_vm is NULL, a new Java VM is
created according to the create_java_vm callback in the JSJCallbacks,
using the provided classpath in addition to any default initargs.
The initargs argument is ignored, however, if java_vm is non-NULL. */
PR_IMPLEMENT(JSJavaVM *)
JSJ_ConnectToJavaVM(SystemJavaVM *java_vm, void* initargs);
/* Initialize the provided JSContext by setting up the JS classes necessary for
reflection and by defining JavaPackage objects for the default Java packages
as properties of global_obj. Additional packages may be pre-defined by
setting the predefined_packages argument. (Pre-defining a Java package at
initialization time is not necessary, but it will make package lookup faster
and, more importantly, will avoid unnecessary network accesses if classes
are being loaded over the network.) */
PR_IMPLEMENT(JSBool)
JSJ_InitJSContext(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages);
/* This function returns a structure that encapsulates the Java and JavaScript
execution environment for the current native thread. It is intended to
be called from the embedder's implementation of JSJCallback's
map_js_context_to_jsj_thread() function. The thread_name argument is only
used for debugging purposes and can be set to NULL. The Java JNI
environment associated with this thread is returned through the java_envp
argument if java_envp is non-NULL. */
PR_IMPLEMENT(JSJavaThreadState *)
JSJ_AttachCurrentThreadToJava(JSJavaVM *jsjava_vm, const char *thread_name,
JNIEnv **java_envp);
/* Destructor routine for per-thread JSJavaThreadState structure */
PR_IMPLEMENT(JSBool)
JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env);
/* This function is used to specify a particular JSContext as *the* JavaScript
execution environment to be used when LiveConnect is accessed from the given
Java thread, i.e. when one of the methods of netscape.javascript.JSObject
has been called. There can only be one such JS context for any given Java
thread at a time. (To multiplex JSContexts among a single thread, this
function could be called before Java is invoked on that thread.) The return
value is the previous JSContext associated with the given Java thread.
If this function has not been called for a thread and a crossing is made
into JavaScript from Java, the map_jsj_thread_to_js_context() callback will
be invoked to determine the JSContext for the thread. The purpose of the
function is to improve performance by avoiding the expense of the callback.
*/
PR_IMPLEMENT(JSContext *)
JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env);
/* This routine severs the connection to a Java VM, freeing all related resources.
It shouldn't be called until the global scope has been cleared in all related
JSContexts (so that all LiveConnect objects are finalized) and a JavaScript
GC is performed. Otherwise, accessed to free'ed memory could result. */
PR_IMPLEMENT(void)
JSJ_DisconnectFromJavaVM(JSJavaVM *);
/*
* Reflect a Java object into a JS value. The source object, java_obj, must
* be of type java.lang.Object or a subclass and may, therefore, be an array.
*/
PR_IMPLEMENT(JSBool)
JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp);
PR_END_EXTERN_C
#endif /* _JSJAVA_H */

View File

@@ -0,0 +1,182 @@
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
IGNORE_MANIFEST=1
#//------------------------------------------------------------------------
#//
#// Makefile to build the Java reflections of JavaScript objects
#//
#//------------------------------------------------------------------------
#//------------------------------------------------------------------------
#//
#// Specify the depth of the current directory relative to the
#// root of NS
#//
#//------------------------------------------------------------------------
DEPTH= ..\..\..
include <$(DEPTH)/config/config.mak>
!ifdef NSJVM
DIRS = classes
!endif
#//------------------------------------------------------------------------
#//
#// Define any Public Make Variables here: (ie. PDFFILE, MAPFILE, ...)
#//
#//------------------------------------------------------------------------
DLLNAME=jsj$(MOZ_BITS)$(VERSION_NUMBER)
PDBFILE=$(DLLNAME).pdb
MAPFILE = $(DLLNAME).map
RESFILE = jsj1640.res
DLL=.\$(OBJDIR)\$(DLLNAME).dll
MAKE_OBJ_TYPE = DLL
!if "$(MOZ_BITS)" == "16"
DEFFILE = $(DLLNAME).def
!endif
LLIBS=$(LIBNSPR) $(DIST)\lib\js$(MOZ_BITS)$(VERSION_NUMBER).lib \
$(DIST)\lib\xpcom32.lib
!if "$(MOZ_BITS)"=="32" && defined(MOZ_DEBUG) && defined(GLOWCODE)
LLIBS=$(LLIBS) $(GLOWDIR)\glowcode.lib
!endif
#//------------------------------------------------------------------------
#//
#// Define the files necessary to build the target (ie. OBJS)
#//
#//------------------------------------------------------------------------
OBJS= \
.\$(OBJDIR)\jsj.obj \
.\$(OBJDIR)\jsj_JSObject.obj \
.\$(OBJDIR)\jsj_JavaArray.obj \
.\$(OBJDIR)\jsj_JavaClass.obj \
.\$(OBJDIR)\jsj_JavaMember.obj \
.\$(OBJDIR)\jsj_JavaObject.obj \
.\$(OBJDIR)\jsj_JavaPackage.obj \
.\$(OBJDIR)\jsj_array.obj \
.\$(OBJDIR)\jsj_class.obj \
.\$(OBJDIR)\jsj_convert.obj \
.\$(OBJDIR)\jsj_field.obj \
.\$(OBJDIR)\jsj_hash.obj \
.\$(OBJDIR)\jsj_method.obj \
.\$(OBJDIR)\jsj_utils.obj \
.\$(OBJDIR)\nsCLiveconnect.obj \
.\$(OBJDIR)\nsCLiveconnectFactory.obj \
!if "$(MOZ_BITS)" == "16"
.\$(OBJDIR)\jsj_nodl.obj \
!endif
$(NULL)
#//------------------------------------------------------------------------
#//
#// install headers
#//
#//------------------------------------------------------------------------
INSTALL_DIR=$(PUBLIC)\js
INSTALL_FILE_LIST= \
jsjava.h nsILiveconnect.h
#//------------------------------------------------------------------------
#//
#// Define any Public Targets here (ie. PROGRAM, LIBRARY, DLL, ...)
#// (these must be defined before the common makefiles are included)
#//
#//------------------------------------------------------------------------
!ifdef NSJVM
JNI_GEN= \
netscape.javascript.JSObject \
netscape.javascript.JSException \
$(NULL)
!endif
MODULE = java
EXPORTS = \
$(JNI_GEN_DIR)\netscape_javascript_JSObject.h \
$(JNI_GEN_DIR)\netscape_javascript_JSException.h \
$(NULL)
#//------------------------------------------------------------------------
#//
#// Define any local options for the make tools
#// (ie. LCFLAGS, LLFLAGS, LLIBS, LINCS)
#//
#//------------------------------------------------------------------------
LINCS=$(LINCS) -I$(JNI_GEN_DIR) \
-I$(PUBLIC)\js \
-I$(PUBLIC)\java \
-I$(PUBLIC)\xpcom \
$(NULL)
#!ifdef SERVER_BUILD
#LLIBS=$(DIST)/lib/httpdlw.lib $(DIST)/lib/libsjboot.lib
#!endif
#//------------------------------------------------------------------------
#//
#// Include the common makefile rules
#//
#//------------------------------------------------------------------------
include <$(DEPTH)/config/rules.mak>
export:: INSTALL_FILES
libs:: $(DLL)
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).dll $(DIST)\bin
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).lib $(DIST)\lib
####
# this bit of extreme scariness came from the js/src makefile
# reproduced here since that's where jsjava.c lives now...
!if ("$(MOZ_BITS)" == "16")
#//
#// Win16 Hoovers SO BAD!!!
#//
!if !defined(MOZ_DEBUG)
#//
#// We must turn off codeview debug info so jni.c can build.
#// Otherwise the linker gives errors about data in the $SYMBOLS
#// segment being beyond a segment boundary.
#//
$(OBJDIR)\jsjava.obj: jsjava.c
@$(CC) @<<$(CFGFILE)
-c
-Od
$(CFLAGS)
$(LCFLAGS)
$(LINCS)
$(LINCS_1)
$(INCS)
-Fd$(PDBFILE)
-Fo.\$(OBJDIR)\
$(*B).c
<<KEEP
!endif
!endif

View File

@@ -0,0 +1,111 @@
/* -*- Mode: C; tab-width: 8 -*-
* Copyright (C) 1998 Netscape Communications Corporation, All Rights Reserved.
*/
#include <jni.h>
/* Header for class netscape_javascript_JSObject */
#ifndef _Included_netscape_javascript_JSObject
#define _Included_netscape_javascript_JSObject
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: netscape_javascript_JSObject
* Method: initClass
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_initClass
(JNIEnv *, jclass);
/*
* Class: netscape_javascript_JSObject
* Method: getMember
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: getSlot
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getSlot
(JNIEnv *, jobject, jint);
/*
* Class: netscape_javascript_JSObject
* Method: setMember
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setMember
(JNIEnv *, jobject, jstring, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: setSlot
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setSlot
(JNIEnv *, jobject, jint, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: removeMember
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_removeMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: call
* Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_call
(JNIEnv *, jobject, jstring, jobjectArray);
/*
* Class: netscape_javascript_JSObject
* Method: eval
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_eval
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: toString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_netscape_javascript_JSObject_toString
(JNIEnv *, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: getWindow
* Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getWindow
(JNIEnv *, jclass, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: finalize
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_finalize
(JNIEnv *, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: equals
* Signature: (Ljava/lang/Object;)Z
*/
JNIEXPORT jboolean JNICALL Java_netscape_javascript_JSObject_equals
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,567 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the implementation providing nsIFactory XP-COM interface.
*
*/
#include <stdlib.h>
#include <string.h>
#include "prtypes.h"
#include "prprf.h"
#include "prlog.h"
PR_BEGIN_EXTERN_C
#ifdef XP_MAC
#include "prosdep.h"
#endif
#include "jsj_private.h"
#include "jsjava.h"
#include "jscntxt.h" /* For js_ReportErrorAgain().
TODO - get rid of private header */
#include "netscape_javascript_JSObject.h" /* javah-generated headers */
/* A captured JavaScript error, created when JS_ReportError() is called while
running JavaScript code that is itself called from Java. */
struct CapturedJSError {
char * message;
JSErrorReport report; /* Line # of error, etc. */
jthrowable java_exception; /* Java exception, error, or null */
CapturedJSError * next; /* Next oldest captured JS error */
};
PR_END_EXTERN_C
#include "nsCLiveconnect.h"
static NS_DEFINE_IID(kILiveconnectIID, NS_ILIVECONNECT_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
////////////////////////////////////////////////////////////////////////////
// from nsISupports and AggregatedQueryInterface:
// Thes macro expands to the aggregated query interface scheme.
NS_IMPL_AGGREGATED(nsCLiveconnect);
NS_METHOD
nsCLiveconnect::AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr)
{
if (aIID.Equals(kISupportsIID)) {
*aInstancePtr = GetInner();
AddRef();
return NS_OK;
}
if (aIID.Equals(kILiveconnectIID)) {
*aInstancePtr = this;
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
////////////////////////////////////////////////////////////////////////////
// from nsILiveconnect:
/**
* get member of a Native JSObject for a given name.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param pjobj - return parameter as a java object representing
* the member. If it is a basic data type it is converted to
* a corresponding java type. If it is a NJSObject, then it is
* wrapped up as java wrapper netscape.javascript.JSObject.
*/
NS_METHOD
nsCLiveconnect::GetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject *pjobj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!name) {
JS_ReportError(cx, "illegal null member name");
member = NULL;
goto done;
}
if (!JS_GetProperty(cx, js_obj, name, &js_val))
goto done;
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &member, &dummy_bool);
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NS_ERROR_FAILURE;
*pjobj = member;
return NS_OK;
}
/**
* get member of a Native JSObject for a given index.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param pjobj - return parameter as a java object representing
* the member.
*/
NS_METHOD
nsCLiveconnect::GetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject *pjobj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
// =-= sudu: check to see if slot can be passed in as is.
// Should it be converted to a jsint?
if (!JS_GetElement(cx, js_obj, slot, &js_val))
goto done;
if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &member, &dummy_bool))
goto done;
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NS_ERROR_FAILURE;
*pjobj = member;
return NS_OK;
}
/**
* set member of a Native JSObject for a given name.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_METHOD
nsCLiveconnect::SetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject java_obj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!name) {
JS_ReportError(cx, "illegal null member name");
goto done;
}
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
goto done;
JS_SetProperty(cx, js_obj, name, &js_val);
done:
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
/**
* set member of a Native JSObject for a given index.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_METHOD
nsCLiveconnect::SetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject java_obj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jsval js_val = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
goto done;
JS_SetElement(cx, js_obj, slot, &js_val);
done:
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
/**
* remove member of a Native JSObject for a given name.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a member.
*/
NS_METHOD
nsCLiveconnect::RemoveMember(JNIEnv *jEnv, jsobject obj, const char *name)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jsval js_val = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!name) {
JS_ReportError(cx, "illegal null member name");
goto done;
}
JS_DeleteProperty2(cx, js_obj, name, &js_val);
done:
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
/**
* call a method of Native JSObject.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a method.
* @param jobjArr - Array of jobjects representing parameters of method being caled.
* @param pjobj - return value.
*/
NS_METHOD
nsCLiveconnect::Call(JNIEnv *jEnv, jsobject obj, const char *name, jobjectArray java_args, jobject *pjobj)
{
int i = 0;
int argc = 0;
int arg_num = 0;
jsval *argv = 0;
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
jsval function_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
jobject result = NULL;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
result = NULL;
if (!name) {
JS_ReportError(cx, "illegal null JavaScript function name");
goto done;
}
/* FIXME: What about security stuff ? Don't principals need to be set here ? */
/* Allocate space for JS arguments */
if (java_args) {
argc = jEnv->GetArrayLength(java_args);
argv = (jsval*)JS_malloc(cx, argc * sizeof(jsval));
} else {
argc = 0;
argv = 0;
}
/* Convert arguments from Java to JS values */
for (arg_num = 0; arg_num < argc; arg_num++) {
jobject arg = jEnv->GetObjectArrayElement(java_args, arg_num);
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, arg, &argv[arg_num]))
goto cleanup_argv;
JS_AddRoot(cx, &argv[arg_num]);
}
if (!JS_GetProperty(cx, js_obj, name, &function_val))
goto cleanup_argv;
if (!JS_CallFunctionValue(cx, js_obj, function_val, argc, argv, &js_val))
goto cleanup_argv;
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &result, &dummy_bool);
cleanup_argv:
if (argv) {
for (i = 0; i < arg_num; i++)
JS_RemoveRoot(cx, &argv[i]);
JS_free(cx, argv);
}
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NS_ERROR_FAILURE;
*pjobj = result;
return NS_OK;
}
/**
* Evaluate a script with a Native JS Object representing scope.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param pNSIPrincipaArray - Array of principals to be used to compare privileges.
* @param numPrincipals - Number of principals being passed.
* @param script - Script to be executed.
* @param pjobj - return value.
*/
NS_METHOD
nsCLiveconnect::Eval(JNIEnv *jEnv, jsobject obj, const char *script, jobject *pjobj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
jsval function_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
jobject result = NULL;
const char *codebase = NULL;
JSPrincipals *principals = NULL;
JSBool eval_succeeded = PR_FALSE;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NULL;
result = NULL;
if (!script) {
JS_ReportError(cx, "illegal null string eval argument");
goto done;
}
/* Set up security stuff */
principals = NULL;
if (JSJ_callbacks->get_JSPrincipals_from_java_caller)
principals = JSJ_callbacks->get_JSPrincipals_from_java_caller(jEnv, cx);
codebase = principals ? principals->codebase : NULL;
/* Have the JS engine evaluate the unicode string */
eval_succeeded = JS_EvaluateScriptForPrincipals(cx, js_obj, principals,
script, strlen(script),
codebase, 0, &js_val);
if (!eval_succeeded)
goto done;
/* Convert result to a subclass of java.lang.Object */
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &result, &dummy_bool);
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NULL;
*pjobj = result;
return NS_OK;
}
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param pJavaObject - Either a jobject or a pointer to a plugin instance
* representing the java object.
* @param pjobj - return value. This is a native js object
* representing the window object of a frame
* in which a applet/bean resides.
*/
NS_METHOD
nsCLiveconnect::GetWindow(JNIEnv *jEnv, void *pJavaObject, jsobject *pobj)
{
char *err_msg = NULL;
JSContext *cx = NULL;
JSObject *js_obj = NULL;
jsval js_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
jobject java_obj = NULL;
JSJavaThreadState *jsj_env = NULL;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NULL;
err_msg = NULL;
java_obj = NULL;
js_obj = JSJ_callbacks->map_java_object_to_js_object(jEnv, pJavaObject, &err_msg);
if (!js_obj) {
if (err_msg) {
JS_ReportError(cx, err_msg);
free(err_msg);
}
goto done;
}
#if 0
js_val = OBJECT_TO_JSVAL(js_obj);
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &java_obj, &dummy_bool);
#endif
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NULL;
//*pjobj = java_obj;
*pobj = (jint)js_obj;
return NS_OK;
}
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
*/
NS_METHOD
nsCLiveconnect::FinalizeJSObject(JNIEnv *jEnv, jsobject obj)
{
JSContext *cx = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env) /* Note: memory leak if we exit here */
return NS_ERROR_FAILURE;
#ifdef PRESERVE_JSOBJECT_IDENTITY
jsj_remove_js_obj_reflection_from_hashtable(cx, js_obj);
#endif /* PRESERVE_JSOBJECT_IDENTITY */
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnect:
nsCLiveconnect::nsCLiveconnect(nsISupports *aOuter)
{
NS_INIT_AGGREGATED(aOuter);
jsj_init_js_obj_reflections_table();
}
nsCLiveconnect::~nsCLiveconnect()
{
}

View File

@@ -0,0 +1,166 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains class definition implementing the public interface.
*
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the class definition to implement nsILiveconnect XP-COM interface.
*
*/
#ifndef nsCLiveconnect_h___
#define nsCLiveconnect_h___
#include "nsILiveconnect.h"
#include "nsAgg.h"
/**
* nsCLiveconnect implements nsILiveconnect interface for navigator.
* This is used by a JVM to implement netscape.javascript.JSObject functionality.
*/
class nsCLiveconnect :public nsILiveconnect {
public:
////////////////////////////////////////////////////////////////////////////
// from nsISupports and AggregatedQueryInterface:
NS_DECL_AGGREGATED
////////////////////////////////////////////////////////////////////////////
// from nsILiveconnect:
/**
* get member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param pjobj - return parameter as a java object representing
* the member. If it is a basic data type it is converted to
* a corresponding java type. If it is a NJSObject, then it is
* wrapped up as java wrapper netscape.javascript.JSObject.
*/
NS_IMETHOD
GetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject *pjobj);
/**
* get member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param pjobj - return parameter as a java object representing
* the member.
*/
NS_IMETHOD
GetSlot(JNIEnv *jEnv, jsobject obj, int index, jobject *pjobj);
/**
* set member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject jobj);
/**
* set member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject jobj);
/**
* remove member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
*/
NS_IMETHOD
RemoveMember(JNIEnv *jEnv, jsobject obj, const char *name);
/**
* call a method of Native JSObject.
*
* @param obj - A Native JS Object.
* @param name - Name of a method.
* @param jobjArr - Array of jobjects representing parameters of method being caled.
* @param pjobj - return value.
*/
NS_IMETHOD
Call(JNIEnv *jEnv, jsobject obj, const char *name, jobjectArray jobjArr, jobject *pjobj);
/**
* Evaluate a script with a Native JS Object representing scope.
*
* @param obj - A Native JS Object.
* @param pNSIPrincipaArray - Array of principals to be used to compare privileges.
* @param numPrincipals - Number of principals being passed.
* @param script - Script to be executed.
* @param pjobj - return value.
*/
NS_IMETHOD
//Eval(JNIEnv *jEnv, jsobject obj, nsIPrincipal **pNSIPrincipaArray, PRInt32 numPrincipals, const char *script, jobject *pjobj);
Eval(JNIEnv *jEnv, jsobject obj, const char *script, jobject *pjobj);
/**
* Get the window object for a plugin instance.
*
* @param pJavaObject - Either a jobject or a pointer to a plugin instance
* representing the java object.
* @param pjobj - return value. This is a native js object
* representing the window object of a frame
* in which a applet/bean resides.
*/
NS_IMETHOD
GetWindow(JNIEnv *jEnv, void *pJavaObject, jsobject *pobj);
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
*/
NS_IMETHOD
FinalizeJSObject(JNIEnv *jEnv, jsobject obj);
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnect:
nsCLiveconnect(nsISupports *aOuter);
virtual ~nsCLiveconnect(void);
protected:
};
#endif // nsCLiveconnect_h___

View File

@@ -0,0 +1,168 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the implementation providing nsILiveconnect XP-COM interface.
*
*/
#include "prtypes.h"
#include "nspr.h"
#include "prmem.h"
#include "prmon.h"
#include "prlog.h"
#include "nsCLiveconnect.h"
#include "nsCLiveconnectFactory.h"
#include "nsRepository.h"
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);
static NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
static NS_DEFINE_IID(kILiveconnectIID, NS_ILIVECONNECT_IID);
nsIFactory *nsCLiveconnectFactory::m_pNSIFactory = NULL;
nsCLiveconnect *nsCLiveconnectFactory::m_pNSCLiveconnect = NULL;
/*+++++++++++++++++++++++++++++++++++++++++++++++++
* NSGetFactory:
* Provides entry point to liveconnect dll.
+++++++++++++++++++++++++++++++++++++++++++++++++*/
extern "C" NS_EXPORT nsresult
NSGetFactory(const nsCID &aClass, nsIFactory **aFactory)
{
if (!aClass.Equals(kCLiveconnectCID)) {
return NS_ERROR_FACTORY_NOT_LOADED; // XXX right error?
}
nsCLiveconnectFactory* pCLiveConnectFactory = new nsCLiveconnectFactory();
if (pCLiveConnectFactory == NULL)
return NS_ERROR_OUT_OF_MEMORY;
pCLiveConnectFactory->AddRef();
*aFactory = pCLiveConnectFactory;
return NS_OK;
}
extern "C" NS_EXPORT PRBool
NSCanUnload(void)
{
return PR_FALSE;
}
////////////////////////////////////////////////////////////////////////////
// from nsISupports
NS_METHOD
nsCLiveconnectFactory::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
PR_ASSERT(NULL != aInstancePtr);
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIFactoryIID) ||
aIID.Equals(kISupportsIID)) {
*aInstancePtr = (void*) this;
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
NS_IMPL_ADDREF(nsCLiveconnectFactory)
NS_IMPL_RELEASE(nsCLiveconnectFactory)
////////////////////////////////////////////////////////////////////////////
// from nsIFactory:
NS_METHOD
nsCLiveconnectFactory::CreateInstance(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{
nsCLiveconnect *pNSCLiveconnect = NULL;
*aResult = NULL;
if (aOuter && !aIID.Equals(kISupportsIID))
return NS_NOINTERFACE; // XXX right error?
if (m_pNSCLiveconnect == NULL)
{
m_pNSCLiveconnect = new nsCLiveconnect(aOuter);
}
if (m_pNSCLiveconnect == NULL)
{
return NS_ERROR_FAILURE;
}
if (m_pNSCLiveconnect->QueryInterface(aIID,
(void**)aResult) != NS_OK) {
// then we're trying get a interface other than nsISupports and
// nsICapsManager
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_METHOD
nsCLiveconnectFactory::LockFactory(PRBool aLock)
{
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnectFactory:
nsCLiveconnectFactory::nsCLiveconnectFactory(void)
{
if( m_pNSIFactory != NULL)
{
return;
}
NS_INIT_REFCNT();
nsresult err = NS_OK;
NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);
err = this->QueryInterface(kIFactoryIID, (void**)&m_pNSIFactory);
if ( (err == NS_OK) && (m_pNSIFactory != NULL) )
{
NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
nsRepository::RegisterFactory(kCLiveconnectCID, m_pNSIFactory,
PR_FALSE);
}
}
nsCLiveconnectFactory::~nsCLiveconnectFactory()
{
if(mRefCnt == 0)
{
NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
nsRepository::UnregisterFactory(kCLiveconnectCID, (nsIFactory *)m_pNSIFactory);
}
}

View File

@@ -0,0 +1,59 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the class definition to implement nsIFactory XP-COM interface.
*
*/
#ifndef nsCLiveconnectFactory_h___
#define nsCLiveconnectFactory_h___
#include "nsISupports.h"
#include "nsIFactory.h"
class nsCLiveconnectFactory : public nsIFactory {
public:
////////////////////////////////////////////////////////////////////////////
// from nsISupports and AggregatedQueryInterface:
NS_DECL_ISUPPORTS
////////////////////////////////////////////////////////////////////////////
// from nsIFactory:
NS_IMETHOD
CreateInstance(nsISupports *aOuter, REFNSIID aIID, void **aResult);
NS_IMETHOD
LockFactory(PRBool aLock);
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnectFactory:
nsCLiveconnectFactory(void);
virtual ~nsCLiveconnectFactory(void);
protected:
static nsIFactory *m_pNSIFactory;
static nsCLiveconnect *m_pNSCLiveconnect;
};
#endif // nsCLiveconnectFactory_h___

View File

@@ -0,0 +1,157 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the public XP-COM based interface for java to javascript communication.
*
*/
#ifndef nsILiveconnect_h___
#define nsILiveconnect_h___
#include "nsISupports.h"
#include "nsIFactory.h"
#include "jni.h"
typedef jint jsobject;
class nsILiveconnect : public nsISupports {
public:
/**
* get member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param pjobj - return parameter as a java object representing
* the member. If it is a basic data type it is converted to
* a corresponding java type. If it is a NJSObject, then it is
* wrapped up as java wrapper netscape.javascript.JSObject.
*/
NS_IMETHOD
GetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject *pjobj) = 0;
/**
* get member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param pjobj - return parameter as a java object representing
* the member.
*/
NS_IMETHOD
GetSlot(JNIEnv *jEnv, jsobject obj, int index, jobject *pjobj) = 0;
/**
* set member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject jobj) = 0;
/**
* set member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject jobj) = 0;
/**
* remove member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
*/
NS_IMETHOD
RemoveMember(JNIEnv *jEnv, jsobject obj, const char *name) = 0;
/**
* call a method of Native JSObject.
*
* @param obj - A Native JS Object.
* @param name - Name of a method.
* @param jobjArr - Array of jobjects representing parameters of method being caled.
* @param pjobj - return value.
*/
NS_IMETHOD
Call(JNIEnv *jEnv, jsobject obj, const char *name, jobjectArray jobjArr, jobject *pjobj) = 0;
/**
* Evaluate a script with a Native JS Object representing scope.
*
* @param obj - A Native JS Object.
* @param pNSIPrincipaArray - Array of principals to be used to compare privileges.
* @param numPrincipals - Number of principals being passed.
* @param script - Script to be executed.
* @param pjobj - return value.
*/
NS_IMETHOD
//Eval(jsobject obj, nsIPrincipal **pNSIPrincipaArray, PRInt32 numPrincipals, const char *script, jobject *pjobj) = 0;
Eval(JNIEnv *jEnv, jsobject obj, const char *script, jobject *pjobj) = 0;
/**
* Get the window object for a plugin instance.
*
* @param pJavaObject - Either a jobject or a pointer to a plugin instance
* representing the java object.
* @param pjobj - return value. This is a native js object
* representing the window object of a frame
* in which a applet/bean resides.
*/
NS_IMETHOD
GetWindow(JNIEnv *jEnv, void *pJavaObject, jsobject *pobj) = 0;
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
*/
NS_IMETHOD
FinalizeJSObject(JNIEnv *jEnv, jsobject obj) = 0;
};
#define NS_ILIVECONNECT_IID \
{ /* 68190910-3318-11d2-97f0-00805f8a28d0 */ \
0x68190910, \
0x3318, \
0x11d2, \
{0x97, 0xf0, 0x00, 0x80, 0x5f, 0x8a, 0x28, 0xd0} \
};
#define NS_CLIVECONNECT_CID \
{ /* b8f0cef0-3931-11d2-97f0-00805f8a28d0 */ \
0xb8f0cef0, \
0x3931, \
0x11d2, \
{0x97, 0xf0, 0x00, 0x80, 0x5f, 0x8a, 0x28, 0xd0} \
};
#endif // nsILiveconnect_h___

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,216 +0,0 @@
# Backwards.pm
# Copyright (C) 1999 Uri Guttman. All rights reserved.
# mail bugs, comments and feedback to uri@sysarch.com
use strict ;
package Backwards ;
use Symbol ;
use Fcntl ;
use Carp ;
use integer ;
#my $max_read_size = 3 ;
my $max_read_size = 1 << 13 ;
# support tied handles. the tied calls map directly to the object methods
*TIEHANDLE = \&new ;
*READLINE = \&readline ;
# create a new Backwards object
sub new {
my( $class, $filename ) = @_ ;
my( $handle, $seek_pos, $read_size, $self ) ;
# get a new handle symbol and the real file handle
$handle = gensym() ;
# open the file for reading
unless( sysopen( $handle, $filename, O_RDONLY ) ) {
carp "Can't open $filename $!" ;
return ;
}
# seek to the end of the file
seek( $handle, 0, 2 ) ;
$seek_pos = tell( $handle ) ;
# get the size of the first block to read,
# either a trailing partial one (the % size) or full sized one (max read size)
$read_size = $seek_pos % $max_read_size || $max_read_size ;
# create the hash for the object, bless and return it
$self = {
'file_name' => $filename,
'handle' => $handle,
'read_size' => $read_size,
'seek_pos' => $seek_pos,
'lines' => [],
} ;
return( bless( $self, $class ) ) ;
}
sub readline {
my( $self, $line_ref ) = @_ ;
my( $handle, $lines_ref, $seek_pos, $read_cnt, $read_buf,
$file_size, $read_size, $text ) ;
# get the buffer of lines
$lines_ref = $self->{'lines'} ;
while( 1 ) {
# see if there is more than 1 line in the buffer
if ( @{$lines_ref} > 1 ) {
# we have a complete line so return it
return( pop @{$lines_ref} ) ;
}
# we don't have a complete, so have to read blocks until we do
$seek_pos = $self->{'seek_pos'} ;
# see if we are at the beginning of the file
if ( $seek_pos == 0 ) {
# the last read never made more lines, so return the last line in the buffer
# if no lines left then undef will be returned
return( pop @{$lines_ref} ) ;
}
#print "c size $read_size\n" ;
# we have to read more text so get the handle and the current read size
$handle = $self->{'handle'} ;
$read_size = $self->{'read_size'} ;
# after the first read, always read the maximum size
$self->{'read_size'} = $max_read_size ;
# seek to the beginning of this block and save the new seek position
$seek_pos -= $read_size ;
$self->{'seek_pos'} = $seek_pos ;
seek( $handle, $seek_pos, 0 ) ;
#print "seek $seek_pos\n" ;
# read in the next (previous) block of text
$read_cnt = sysread( $handle, $read_buf, $read_size ) ;
#print "Read <$read_buf>\n" ;
# if ( $read_cnt != $read_size ) {
# print "bad read cnt $read_cnt != size $read_size\n" ;
# return( undef ) ;
# }
# prepend the read buffer to the leftover (possibly partial) line
$text = $read_buf . ( pop @{$lines_ref} || '' ) ;
# split the buffer into a list of lines
# this may want to be $/ but reading files backwards assumes plain text and
# newline separators
@{$lines_ref} = $text =~ m[(^.*\n|^.+)]mg ;
#print "Lines \n=>", join( "<=\n=>", @{$lines_ref} ), "<=\n" ;
}
}
__END__
=head1 NAME
Backwards.pm -- Read a file backwards by lines.
=head1 SYNOPSIS
use Backwards ;
# Object interface
$bw = Backwards->new( 'log_file' ) ;
while( $log_line = $bw->readline ) {
print $log_line ;
}
# Tied Handle Interface
tie *BW, 'log_file' ;
while( <BW> ) {
print ;
}
=head1 DESCRIPTION
This module reads a file backwards line by line. It is simple to use,
memory efficient and fast. It supports both an object and a tied handle
interface.
It is intended for processing log and other similar text files which
typically have new entries appended. It uses newline as the separator
and not $/ since it is only meant to be used for text.
It works by reading large (8kb) blocks of text from the end of the file,
splits them on newlines and stores the other lines until the buffer runs
out. Then it seeks to the previous block and splits it. When it reaches
the beginning of the file, it stops reading more blocks. All boundary
conditions are handled correctly. If there is a trailing partial line
(no newline) it will be the first line returned. Lines larger than the
read buffer size are ok.
=head2 Object Interface
There are only 2 methods in Backwards' object interface, new and
readline.
=head2 new
New takes just a filename for an argument and it either returns the
object on a successful open on that file or undef.
=head2 readline
Readline takes no arguments and it returns the previous line in the file
or undef when there are no more lines in the file.
=head2 Tied Handle Interface
The only tied handle calls supported are TIEHANDLE and READLINE and they
are typeglobbed to new and readline respectively. All other tied handle
operations will generate an unknown method error. Do not seek, write or
do any other operation other than <> on the handle.

View File

@@ -1 +0,0 @@
<font size=+2>Click on the <b>filename and line number</b> in the log and the source will appear in this window.</font>

View File

@@ -1,77 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
# This Makefile helps you install Tinderbox. Define PERL and MYSQLTCL to
# the full pathnames of where you have these utilities. Define PREFIX
# to where you will install the running Tinderbox. Then "make install" should
# copy things for you.
#BONSAI = /home/httpd/html/bonsai
#CVSROOT = /cvsroot
#GZIP = /usr/bin/gzip
#PERL = /usr/bin/perl
#PREFIX = /home/httpd/html/tinderbox
#UUDECODE = /usr/bin/uudecode
FILES = addimage.cgi \
addnote.cgi \
admintree.cgi \
buildwho.pl \
clean.pl \
copydata.pl \
doadmin.cgi \
ep_mac.pl \
ep_unix.pl \
ep_windows.pl \
fixupimages.pl \
globals.pl \
handlemail.pl \
imagelog.pl \
processbuild.pl \
showbuilds.cgi \
showimages.cgi \
showlog.cgi \
Empty.html \
faq.html \
index.html \
examples/mozilla-unix.pl \
examples/mozilla-windows.pl
PICS = \
1afi003r.gif \
reledanim.gif \
star.gif
install:
mkdir -p $(PREFIX) && \
for I in $(FILES); do \
echo Installing $$I && \
sed -e s#/usr/bonsaitools/bin/perl#$(PERL)#g \
-e s#/tools/ns/bin/perl5#$(PERL)#g \
-e s#/m/src#$(CVSROOT)#g \
-e s#/usr/local/bin/gzip#$(GZIP)#g \
-e s#/tools/ns/bin/uudecode#$(UUDECODE)#g \
-e s#/d/webdocs/projects/bonsai#$(BONSAI)#g \
$$I > $(PREFIX)/$$I && \
chmod 755 $(PREFIX)/$$I; done && \
for I in $(PICS); do \
echo Installing $$I && \
cp -f $$I $(PREFIX) && \
chmod 755 $(PREFIX)/$$I; done

View File

@@ -1,287 +0,0 @@
This is Tinderbox. See <http://www.mozilla.org/tinderbox.html>.
==========
DISCLAIMER
==========
This is not very well packaged code. It's not packaged at all. Don't
come here expecting something you plop in a directory, twiddle a few
things, and you're off and using it. Much work has to be done to get
there. We'd like to get there, but it wasn't clear when that would be,
and so we decided to let people see it first.
Don't believe for a minute that you can use this stuff without first
understanding most of the code.
============
DEPENDENCIES
============
To use tinderbox, you must first have bonsai up and running.
See <http://www.mozilla.org/bonsai.html>.
Be warned now that bonsai is not easily installed.
====================================
What's What in the Tinderbox sources:
====================================
This is a rough first pass at cataloging and documenting the Tinderbox
sources. Many hands have been in this code over the years, and it has
accreted wildly. There is probably quite a lot of dead code in here.
PROGRAMS
========
handlemail.pl This is the mail deliverty agent (MDA) for tinderbox,
the local message transfer agent (MTA) calls this
program to deliver the tinderbox mail into the system.
It is a wrapper for processbuild.pl.
processbuild.pl Update the "$tbx{tree}/build.dat" database as new
mail comes in then run "./buildwho.pl $tree" and
"./showbuilds.cgi" (to build static versions of
tinderbox data.
buildwho.pl Update the who.dat file with the list of authors who
checked in in the last two days. This is run from
processbuild.pl, always, and from showbuild.cgi when
'rebuildguilty' is clicked
Conceptually, the three programs, handlemail.pl, processbuild.pl, and
buildwho.pl, make up the MDA for tinderbox.
showbuilds.cgi Create the Tinderbox web page.
showlog.cgi Show a build log (brief and full) update the brief_log if
necessary. Requires the ep_$form{errorparser}.pl error
parser.
ep_unix.pl Knows how to parse Unix build error logs. Used by
processbuild.pl There needs to be one ep_* file for
each distinct parsing algorithm used, typically this
is per platform. This file defines the regular
expressions which locate error lines: has_error()
has_warning() and the function has_errorline() which
parses the line into the global variables:
$error_file, $error_file_ref, $error_line, $error_guess,
admintree.cgi Displays the admin cgi which depends on:
$message_of_day, $ignore_builds
doadmin.cgi Actually do the work to admin a tinderbox tree
(change message of the day, turn off displays for a
channel)
clean.pl Run `find . -name \"*.gz\" -mtime +7 -print ` and
unlink those files. Does not appear to be run from
other tools. It is a good candidate for a cron job.
OPTIONS to showbuilds.cgi
=========================
Options to showbuilds are specified in the URL and are undocumented
elsewhere.
If called with no 'tree' option display the possible build trees to
pick from. The 'tree' picks the build to display. An additional tree
can be specified with 'tree2'.
Interesting visual params are:
current state monitoring mode:
express=1
or
panel=1
text mode state monitoring:
quickparse=1
These modes do not show on my browser:
flash=1
rdf=1
static=1
These are self explanatory:
nocrap=1;
hours=n;
EMAIL FORMAT
============
Each tinderbox client mails status updates to the tinderbox server.
These mails contain special tinderbox data lines describing the
progress of the build it may also contain the log of the build process
and could contain a uuencoded binary.
The email to the tinderbox server looks like:
tinderbox: tree: Mozilla
tinderbox: builddate: 900002087
tinderbox: status: building
tinderbox: build: IRIX 6.3 Depend
tinderbox: errorparser: unix
If binaryname is set then the mail message is run through uudecode to
create a file called "$binaryname" on the tinderbox server.
# NOT USED tinderbox: buildfamily: unix
DATA STRUCTURES IN showbuilds.cgi
=================================
load_buildlog() creates build_list a list of hash refernces of this
type
$buildrec = {
mailtime => $mailtime,
buildtime => $buildtime,
buildname => ($tree2 ne '' ? $t->{name} . ' ' : '' ) . $buildname,
errorparser => $errorparser,
buildstatus => $buildstatus,
logfile => $logfile,
binaryname => $binaryname,
td => $t
};
the $buildrec->{rowspan} variable holds the number of rows that the build
should occupy on the table and is not stored in the build.dat file.
These are other add ons to the data structure
$buildrec->{hasnote}=1;
$buildrec->{noteid} = (0+@note_array);
commonly buildrec's are accessed through:
$build_table->[$build_time][$build_name] = $buildrec;
The list of users who updated this build is stored as:
$who_list->[$checkin_time]->{$author} = 1;
There are numerous duplicate data structures which hold partial
information:
hashes which hold all indices:
$build_name_index->{$br->{buildname}} = 1;
$build_time_index->{$br->{buildtime}} = 1;
other access into $build_table:
$build_name_names->[$i] = $n;
$build_time_times->[$i] = $n;
loadquickparseinfo creates these references
$build->{$buildname} = $buildstatus;
$times->{$buildname} = $buildtime;
DATA FILES
==========
These files are used to store data structures. They are a persistent
store of data between executions of the same program and they pass the
data between cooperating program. This data is often databases with
rows separated by "\n" and columns by '|'.
$tree/${logfile}
$tree/${logfile}.brief.html
$tree/ignorebuilds.pl
$tree/mod.pl
$tree/notes.txt
$tree/treedata.pl
$tree/who.dat
$treename/build.dat
The logfile that the tinderbox client sent stored in gziped format.
The filename is ${tree}/$builddate.$$.gz so its quite random and does
not depend on the clients$build string and multiple logs are kept for
each build, one for each mail message sent.
The brief.html file is a cache of the error log summary for this log
file and is recreated when the logfile gets updated.
ignorebuilds.pl is a file which specifies builds that should not be
performed. It is valid perl code which sets the hash reference
$ignore_builds, each key is a tree name.
mod.pl stores the tree specific message of the day. This is not to be
confused with mod perl the CGI library. Its contents looks like:
$message_of_day = 'message';
notes.txt store the database of notes:
$buildtime|$buildname||$author|$time_now|$note
treedata.pl stores the cvs information pertaining to this tree and
looks like this:
$cvs_module='$modulename';
$cvs_branch='$branchname';
$cvs_root='$repository';
who.dat file has lines like:
$checkin_time|$author
This gets stored in the data structure, where checkin_time
gets fudged up so that is is a time already in the
build_table:
$who_list->[$checkin_time]->{$author} = 1;
build.dat stores the build results table. It is a flat file
representation of $build_table
build.dat is a database file each row is a build and has pipe
separated columns:
1) the time stamp of the tinderbox server
2) time stamp of the build machine
3) the official build name (should include build machine name)
( note: that 2 & 3 together uniquely identify the build
and all relevant build data)
4) the architecture dependent error parser to use on the log files
5) status of the build (success|busted|building|testfailed)
6) The log file for this build (if completed)
7) the name of the binary (if any) that came from the build
Other Files
====================
1afi003r.gif The "flames" animation used by showbuilds.cgi
Empty.html Document used for an empty frame by ???
addimage.cgi The form that lets you add a new image to the list of
images that Tinderbox picks from randomly.
addnote.cgi Add a note to a build log.
admintree.cgi Lets you perform various admin tasks on a Tinderbox tree.
This just prompts for a password and posts to doadmin.cgi.
clean.pl ???
copydata.pl ???
doadmin.cgi Actually do the work to admin a tinderbox tree
ep_mac.pl Knows how to parse Mac build error logs. Used by ???
ep_unix.pl Knows how to parse Unix build error logs. Used by ???
ep_windows.pl Knows how to parse Windows build error logs. Used by ???
faq.html Wildly out of date.
fixupimages.pl ???
globals.pl ???
imagelog.pl ???
index.html ???
reledanim.gif ???
showimages.cgi Show all the images in the Tinderbox list. Password-protected.
star.gif The "star" image used to annotate builds by ???

View File

@@ -1,404 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use Socket;
use lib "../bonsai";
require 'header.pl';
print "Content-type: text/html\n\n";
EmitHtmlTitleAndHeader("tinderbox: add images", "add images");
$| = 1;
require "globals.pl";
require "imagelog.pl";
&split_cgi_args;
sub Error {
my ($msg) = @_;
print "<BR><BR><BR>";
print "<UL><FONT SIZE='+1'><B>Something went wrong:</B><P>";
print "<UL>";
print $msg;
print "</UL>";
print "<P>";
print "Hit <B>\`Back'</B> and try again.";
print "</UL>";
exit 1;
}
if( $url = $form{"url"} ){
$quote = $form{"quote"};
$quote =~ s/[\r\n]/ /g;
$url =~ s/[\r\n]/ /g;
$width = "";
$height = "";
# I think we don't want to allow this --jwz
# $width = $form{"width"};
# $height = $form{"height"};
if ($width eq "" || $height eq "") {
$size = &URLsize($url);
if ($size =~ /WIDTH=([0-9]*)/) {
$width = $1;
}
if ($size =~ /HEIGHT=([0-9]*)/) {
$height = $1;
}
if ($width eq "" || $height eq "") {
Error "Couldn't get image size for \"$url\".\n";
}
}
print "
<P><center><img border=2 src='$url' width=$width height=$height><br>
<i>$quote</i><br><br>
";
if( $form{"submit"} ne "Yes" ){
my $u2 = $url;
my $q2 = $quote;
$u2 =~ s@&@&amp;@g; $u2 =~ s@<@&lt;@g; $u2 =~ s@\"@&quot;@g;
$q2 =~ s@&@&amp;@g; $q2 =~ s@<@&lt;@g; $q2 =~ s@\"@&quot;@g;
print "
<form action='addimage.cgi' METHOD='get'>
<input type=hidden name=url value=\"$u2\">
<input type=hidden name=quote value=\"$q2\">
<HR>
<TABLE>
<TR>
<TH ALIGN=RIGHT NOWRAP>Image URL:</TH>
<TD><TT><B>$u2</B></TT></TD>
</TR><TR>
<TH ALIGN=RIGHT>Caption:</TH>
<TD><TT><B>$q2</B></TT></TD>
</TR>
<TR>
<TD></TD>
<TD>
<FONT SIZE=+2><B>
Does that look right?
<SPACER SIZE=10>
<INPUT Type='submit' name='submit' value='Yes'>
</B><BR>(If not, hit \`Back' and fix it.)
</FONT>
</TD>
</TABLE>
</form>
";
}
else {
&add_imagelog( $url, $quote, $width, $height );
print "<br><br>
<font size=+2>Has been added</font><br><br>
<a href=showbuilds.cgi>Return to Log</a>";
}
}
else {
print "
<h2>Add an image and a funny caption.</h2>
<ul>
<p>This is about fun, and making your daily excursion to
<A HREF=http://www.mozilla.org/tinderbox.html>Tinderbox</A> a
novel experience. Engineers spend a lot of time here; it might as well
have some entertainment value.
<p>Please play nice. We don't have the time or inclination to look at
everything you people submit, but if we get nastygrams or legalgrams
and have to take something down, we will curse your IP address, and you
might even make it so the whole thing goes away forever. Please don't
make us go there. You might also avoid links to big images or slow
servers.
<p><ul><B>Thank you for playing nice.</B></UL>
<p>If you really find an image offensive, please
<A HREF=mailto:terry\@netscape.com?Subject=offensive%20tinderbox%20image>tell us</A>
nicely before someone causes a stink. Be sure to include the URL of
the image. Remember, we don't screen these submissions and may not
have even seen it.
<p><ul><B>P.S. Please, no more pictures of Bill Gates.</B></UL>
</ul>
<p><form action='addimage.cgi' METHOD='get'>
<TABLE>
<TR>
<TH ALIGN=RIGHT NOWRAP>Image URL:</TH>
<TD><INPUT NAME='url' SIZE=60></TD>
</TR><TR>
<TH ALIGN=RIGHT>Caption:</TH>
<TD><INPUT NAME='quote' SIZE=60></TD>
</TR><TR>
<TD></TD>
<TD><B>
<INPUT Type='submit' name='submit' value='Test'>
<SPACER SIZE=25>
<INPUT Type='reset' name='reset' value='Reset'>
</B></TD>
</TR>
</TABLE>
</form>
<br><br>
";
}
sub split_cgi_args {
local($i,$var,$value, $s);
$s = $ENV{"QUERY_STRING"};
@args= split(/\&/, $s );
for $i (@args) {
($var, $value) = split(/=/, $i);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$form{$var} = $value;
}
}
#sub imgsize {
# local($file)= @_;
#
# #first try to open the file
# if( !open(STREAM, "<$file") ){
# Error "Can't open IMG $file";
## $size="";
# } else {
# if ($file =~ /.jpg/i || $file =~ /.jpeg/i) {
# $size = &jpegsize(STREAM);
# } elsif($file =~ /.gif/i) {
# $size = &gifsize(STREAM);
# } elsif($file =~ /.xbm/i) {
# $size = &xbmsize(STREAM);
# } else {
# return "";
# }
# $_ = $size;
# if( /\s*width\s*=\s*([0-9]*)\s*/i ){
# ($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
# }
# if( /\s*height\s*=\s*([0-9]*)\s*/i ){
# ($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
# }
# close(STREAM);
# }
# return $size;
#}
###########################################################################
# Subroutine gets the size of the specified GIF
###########################################################################
# bug: it thinks that
# http://cvs1.mozilla.org/webtools/tinderbox/data/knotts.gif
# is 640x400, but it's really 200x245.
# giftrans says of that image:
#
# Header: "GIF87a"
# Logical Screen Descriptor:
# Logical Screen Width: 640 pixels
# Logical Screen Height: 480 pixels
# Image Descriptor:
# Image Width: 200 pixels
# Image Height: 245 pixels
sub gifsize {
local($GIF) = @_;
read($GIF, $type, 6);
if(!($type =~ /GIF8[7,9]a/) ||
!(read($GIF, $s, 4) == 4) ){
Error "Invalid or Corrupted GIF";
$size="";
} else {
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join ("", 'WIDTH=', $b<<8|$a, ' HEIGHT=', $d<<8|$c);
}
return $size;
}
sub xbmsize {
local($XBM) = @_;
local($input)="";
$input .= <$XBM>;
$input .= <$XBM>;
$_ = $input;
if( /#define\s*\S*\s*\d*\s*\n#define\s*\S*\s*\d*\s*\n/i ){
($a,$b)=/#define\s*\S*\s*(\d*)\s*\n#define\s*\S*\s*(\d*)\s*\n/i;
$size=join ("", 'WIDTH=', $a, ' HEIGHT=', $b );
} else {
Error "Doesn't look like an XBM file";
}
return $size;
}
# jpegsize : gets the width and height (in pixels) of a jpeg file
# Andrew Tong, werdna@ugcs.caltech.edu February 14, 1995
# modified slightly by alex@ed.ac.uk
sub jpegsize {
local($JPEG) = @_;
local($done)=0;
$size="";
read($JPEG, $c1, 1); read($JPEG, $c2, 1);
if( !((ord($c1) == 0xFF) && (ord($c2) == 0xD8))){
my $s = sprintf "This is not a JPEG! (Codes %02X %02X)\n", ord($c1), ord($c2);
Error $s;
$done=1;
}
while (ord($ch) != 0xDA && !$done) {
# Find next marker (JPEG markers begin with 0xFF)
# This can hang the program!!
while (ord($ch) != 0xFF) { read($JPEG, $ch, 1); }
# JPEG markers can be padded with unlimited 0xFF's
while (ord($ch) == 0xFF) { read($JPEG, $ch, 1); }
# Now, $ch contains the value of the marker.
$marker=ord($ch);
if (($marker >= 0xC0) && ($marker <= 0xCF) &&
($marker != 0xC4) && ($marker != 0xCC)) { # it's a SOFn marker
read ($JPEG, $junk, 3); read($JPEG, $s, 4);
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join("", 'HEIGHT=',$a<<8|$b,' WIDTH=',$c<<8|$d );
$done=1;
} else {
# We **MUST** skip variables, since FF's within variable
# names are NOT valid JPEG markers
read ($JPEG, $s, 2);
($c1, $c2) = unpack("C"x2,$s);
$length = $c1<<8|$c2;
if( ($length < 2) ){
Error "Bad JPEG file: erroneous marker length";
$done=1;
} else {
read($JPEG, $junk, $length-2);
}
}
}
return $size;
}
###########################################################################
# Subroutine grabs a gif from another server and gets its size
###########################################################################
sub URLsize {
my ($fullurl) = @_;
$_ = $fullurl;
if ( ! m@^http://@ ) {
Error "HTTP URLs only, please: \"$_\" is no good.";
}
my($dummy, $dummy, $serverstring, $url) = split(/\//, $fullurl, 4);
my($them,$port) = split(/:/, $serverstring);
my $port = 80 unless $port;
my $size="";
$_ = $them;
if ( m@^[^.]*$@ ) {
Error "Fully-qualified host names only, please: \"$_\" is no good.";
}
$_=$url;
my ($remote, $iaddr, $paddr, $proto, $line);
$remote = $them;
if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') }
die "No port" unless $port;
$iaddr = inet_aton($remote) || die "no host: $remote";
$paddr = sockaddr_in($port, $iaddr);
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
connect(S, $paddr) || die "connect: $!";
select(S); $| = 1; select(STDOUT);
print S "GET /$url HTTP/1.0\r\n";
print S "Host: $them\r\n";
print S "User-Agent: Tinderbox/0.0\r\n";
print S "\r\n";
$_ = <S>;
if (! m@^HTTP/[0-9.]+ 200@ ) {
Error "$them responded:<BR> $_";
}
my $ctype = "";
while (<S>) {
# print "read: $_<br>\n";
if ( m@^Content-Type:[ \t]*([^ \t\r\n]+)@io ) {
$ctype = $1;
}
last if (/^[\r\n]/);
}
$_ = $ctype;
if ( $_ eq "" ) {
Error "Server returned no content-type for \"$fullurl\"?";
} elsif ( m@image/jpeg@i || m@image/pjpeg@i ) {
$size = &jpegsize(S);
} elsif ( m@image/gif@i ) {
$size = &gifsize(S);
} elsif ( m@image/xbm@i || m@image/x-xbm@i || m@image/x-xbitmap@i ) {
$size = &xbmsize(S);
} else {
Error "Not a GIF, JPEG, or XBM: that was of type \"$ctype\".";
}
$_ = $size;
if( /\s*width\s*=\s*([0-9]*)\s*/i ){
($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
}
if( /\s*height\s*=\s*([0-9]*)\s*/i ){
($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
}
if ( $newwidth eq "" || $newheight eq "" ) {
return "";
} else {
if ( $newwidth <= 5 || $newheight <= 5 ) {
Error "${newwidth}x${newheight} seems small, don't you think?";
} elsif ( $newwidth >= 400 || $newheight >= 400 ) {
Error "${newwidth}x${newheight} is too big; please" .
" keep it under 400x400."
}
return $size;
}
}
sub dokill {
kill 9,$child if $child;
}

View File

@@ -1,206 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
use Fcntl;
require "globals.pl";
require 'lloydcgi.pl';
if (defined($args = $form{log})) {
($tree, $logfile) = split /\//, $args;
my $br = find_build_record($tree, $logfile);
$errorparser = $br->{errorparser};
$buildname = $br->{buildname};
$buildtime = $br->{buildtime};
} else {
$tree = $form{'tree'};
$logfile = $form{'logfile'};
$errorparser = $form{'errorparser'};
$buildname = $form{'buildname'};
$buildtime = $form{'buildtime'};
}
$enc_buildname = &url_encode($buildname);
$note = $form{'note'};
$who = $form{'who'};
$now = time;
$now_str = &print_time($now);
$|=1;
if( -r "$tree/ignorebuilds.pl" ){
require "$tree/ignorebuilds.pl";
}
print "Content-Type:text/html\n";
if ($ENV{"REQUEST_METHOD"} eq 'POST' && defined($form{'note'})) {
# Expire the cookie 5 months from now
print "Set-Cookie: email=$form{who}; expires="
. toGMTString(time + 86400 * 152) . "; path=/\n";
}
print "\n<HTML>\n";
if( $url = $form{"note"} ){
$note =~ s/\&/&amp;/gi;
$note =~ s/\</&lt;/gi;
$note =~ s/\>/&gt;/gi;
$enc_note = url_encode( $note );
open( NOTES,">>$tree/notes.txt");
flock(NOTES, LOCK_EX);
print NOTES "$buildtime|$buildname|$who|$now|$enc_note\n";
&LoadBuildTable;
foreach $element (keys %form) {
if(exists ${$build_name_index}{$element}) {
print NOTES "${$build_name_index}{$element}|$element|$who|$now|$enc_note\n";
} #EndIf
} #Endforeach
close(NOTES);
print "<H1>The following comment has been added to the log</h1>\n";
#print "$buildname \n $buildtime \n $errorparser \n $logfile \n $tree \n $enc_buildname \n";
print "<pre>\n[<b>$who - $now_str</b>]\n$note\n</pre>";
print"
<p><a href=\"showlog.cgi?tree=$tree\&buildname=$enc_buildname\&buildtime=$buildtime\&logfile=$logfile\&errorparser=$errorparser\">
Go back to the Error Log</a>
<a href=\"showbuilds.cgi?tree=$tree\">
<br>Go back to the build Page</a>";
# Build tinderbox static pages
$ENV{QUERY_STRING}="tree=$tree&static=1";
$ENV{REQUEST_METHOD}="GET";
system './showbuilds.cgi >/dev/null';
} else {
&GetBuildNameIndex;
@names = sort (keys %$build_name_index);
if ($buildname eq '' || $buildtime == 0) {
print "<h1>Invalid parameters</h1>\n";
die "\n";
}
#print "$buildname \n $buildtime \n $errorparser \n $logfile \n $tree \n $enc_buildname \n";
$emailvalue = '';
$emailvalue = " value='$cookie_jar{email}'" if defined($cookie_jar{email});
print qq(
<head><title>Add a Comment to $buildname log</title></head>
<body BGCOLOR="#FFFFFF" TEXT="#000000"LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
<table><tr><td>
<b><font size="+2">Add a Log Comment</font></b>
</td></tr><tr><td>
<b><code>$buildname</code></b>
</td></tr></table>
<form action='addnote.cgi' METHOD='post'>
<INPUT Type='hidden' name='buildname' value='${buildname}'>
<INPUT Type='hidden' name='buildtime' value='${buildtime}'>
<INPUT Type='hidden' name='errorparser' value='$errorparser'>
<INPUT Type='hidden' name='logfile' value='$logfile'>
<INPUT Type='hidden' name='tree' value='$tree'>
<table border=0 cellpadding=4 cellspacing=1>
<tr valign=top>
<td align=right>
<NOWRAP>Email address:</NOWRAP>
</td><td>
<INPUT Type='input' name='who' size=32$emailvalue><BR>
</td>
</tr><tr valign=top>
<td align=right>
Comment:
</td><td>
<TEXTAREA NAME=note ROWS=10 COLS=30 WRAP=HARD></textarea>
</td>
</tr>
</table>
<br><b><font size="+2">Addition Builds</font></b><br>
(Comment will be added to the most recent cycle.)<br>
);
for $other_build (@names){
if( $other_build ne "" ){
if (not exists ${$ignore_builds}{$other_build}) {
if( $other_build ne $buildname ){
print "<INPUT TYPE=checkbox NAME=\"$other_build\">";
print "$other_build<BR>\n";
}
} #EndIf
}
} #Endfor
print "<INPUT Type='submit' name='submit' value='Add Comment'><BR>
</form>\n</body>\n</html>";
}
sub GetBuildNameIndex {
local($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname);
open(BUILDLOG, "$tree/build.dat") or die "Couldn't open build.dat: $!\n";
while(<BUILDLOG>) {
chomp;
($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname) =
split( /\|/ );
$build_name_index->{$buildname} = 0;
} #EndWhile
close(BUILDLOG);
}
sub LoadBuildTable {
local($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname);
open(BUILDLOG, "$tree/build.dat") or die "Couldn't open build.dat: $!\n";
while(<BUILDLOG>) {
chomp;
($mailtime, $buildtime, $buildname, $errorparser, $buildstatus, $logfile, $binaryname) =
split( /\|/ );
if ($buildtime > $build_name_index->{$buildname} ) {
$build_name_index->{$buildname} = $buildtime;
}
} #EndWhile
close(BUILDLOG);
}

View File

@@ -1,131 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'lloydcgi.pl';
require 'globals.pl';
require 'header.pl';
$|=1;
print "Content-type: text/html\n\n<HTML>\n";
EmitHtmlHeader("administer tinderbox", "tree: $tree");
$form{noignore} = 1; # Force us to load all build info, not
# paying any attention to ignore_builds stuff.
$maxdate = time();
$mindate = $maxdate - 24*60*60;
&load_data;
if (defined($tree)) {
if( -r "$tree/mod.pl" ){
require "$tree/mod.pl";
}
else {
$message_of_day = "";
}
print "
<FORM method=post action=doadmin.cgi>
<B>Password:</B> <INPUT NAME=password TYPE=password>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=set_message>
<br><b>Message of the Day
<br><TEXTAREA NAME=message ROWS=10 COLS=70 WRAP=SOFT>$message_of_day
</TEXTAREA>
<br><INPUT TYPE=SUBMIT VALUE='Set Message of the Day'>
</FORM>
<hr>
";
print "
<FORM method=post action=doadmin.cgi>
<B>Password:</B> <INPUT NAME=password TYPE=password>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=trim_logs>
<br><b>Trim Logs to <INPUT NAME=days size=5 VALUE='7'> days.</b> (Tinderbox
shows 2 days history by default. You can see more by clicking show all).
<br><INPUT TYPE=SUBMIT VALUE='Trim Logs'>
</FORM>
<FORM method=post action=doadmin.cgi>
<hr>
" ;
}
print "
<FORM method=post action=doadmin.cgi>
<B>Password:</B> <INPUT NAME=password TYPE=password> <BR>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=create_tree>
<TABLE>
<TR>
<TD><B>tinderbox tree name:</B></TD>
<TD><INPUT NAME=treename VALUE=''></TD>
</TR><TR>
<TD><B>cvs repository:</B></TD>
<TD><INPUT NAME=repository VALUE=''></TD>
</TR><TR>
<TD><B>cvs module name:</B></TD>
<TD><INPUT NAME=modulename VALUE=''></TD>
</TR><TR>
<TD><B>cvs branch:</B></TD>
<TD><INPUT NAME=branchname VALUE='HEAD'></TD>
</TR>
</TABLE>
<INPUT TYPE=SUBMIT VALUE='Create a new Tinderbox page'>
</FORM>
<FORM method=post action=doadmin.cgi>
<hr>
";
if (defined($tree)) {
print "
<B><font size=+1>If builds are behaving badly you can turn them off.</font></b><br> Uncheck
the build that is misbehaving and click the button. You can still see all the
builds even if some are disabled by adding the parameter <b><tt>&noignore=1</tt></b> to
the tinderbox URL.<br>
<B>Password:</B> <INPUT NAME=password TYPE=password> <BR>
<INPUT TYPE=HIDDEN NAME=tree VALUE=$tree>
<INPUT TYPE=HIDDEN NAME=command VALUE=disable_builds>
";
@names = sort (@$build_name_names) ;
for $i (@names){
if( $i ne "" ){
$checked = ($ignore_builds->{$i} != 0 ? "": "CHECKED" );
print "<INPUT TYPE=checkbox NAME='build_$i' $checked >";
print "$i<br>\n";
}
}
print "
<INPUT TYPE=SUBMIT VALUE='Show only checked builds'>
</FORM>
<hr>
";
}

View File

@@ -1,97 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'globals.pl';
$F_DEBUG=1;
$days = 2;
if ($ARGV[0] eq "-days") {
shift @ARGV;
$days = shift @ARGV;
}
$tree = $ARGV[0];
open(SEMFILE, ">>$tree/buildwho.sem") || die "Couldn't open semaphore file!";
if (!flock(SEMFILE, 2 + 4)) { # 2 means "lock"; 4 means "fail immediately if
# lock already taken".
print "buildwho.pl: Another process is currently building the database.\n";
exit(0);
}
require "$tree/treedata.pl";
if( $cvs_root eq '' ){
$CVS_ROOT = '/m/src';
}
else {
$CVS_ROOT = $cvs_root;
}
$CVS_REPOS_SUFIX = $CVS_ROOT;
$CVS_REPOS_SUFIX =~ s/\//_/g;
$CHECKIN_DATA_FILE = "/d/webdocs/projects/bonsai/data/checkinlog${CVS_REPOS_SUFIX}";
$CHECKIN_INDEX_FILE = "/d/webdocs/projects/bonsai/data/index${CVS_REPOS_SUFIX}";
require 'cvsquery.pl';
print "cvsroot='$CVS_ROOT'\n";
&build_who;
flock(SEMFILE, 8); # '8' is magic 'unlock' const.
close SEMFILE;
sub build_who {
open(BUILDLOG, "<$tree/build.dat" );
$line = <BUILDLOG>;
close(BUILDLOG);
#($j,$query_date_min) = split(/\|/, $line);
$query_date_min = time - (60 * 60 * 24 * $days);
if( $F_DEBUG ){
print "Minimum date: $query_date_min\n";
}
$query_module=$cvs_module;
$query_branch=$cvs_branch;
$result = &query_checkins;
$last_who='';
$last_date=0;
open(WHOLOG, ">$tree/who.dat" );
for $ci (@$result) {
if( $ci->[$CI_DATE] != $last_date || $ci->[$CI_WHO] != $last_who ){
print WHOLOG "$ci->[$CI_DATE]|$ci->[$CI_WHO]\n";
}
$last_who=$ci->[$CI_WHO];
$last_date=$ci->[$CI_DATE];
}
close( WHOLOG );
}

View File

@@ -1,124 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'lloydcgi.pl';
require 'globals.pl';
require 'header.pl';
use Date::Parse;
use Date::Format;
my $TIMEFORMAT = "%D %T";
$|=1;
print "Content-type: text/html\n\n<HTML>\n";
# &load_data;
EmitHtmlHeader("Statistics");
my $tree = $form{'tree'};
my $start = $form{'start'};
my $end = $form{'end'};
sub str2timeAndCheck {
my ($str) = (@_);
my $result = str2time($str);
if (defined $result && $result > 7000000) {
return $result;
}
print "<p><font color=red>Can't parse as a date: $str</font><p>\n";
return 0;
}
if (defined $tree && defined $start && defined $end) {
my $first = str2timeAndCheck($start);
my $last = str2timeAndCheck($end);
if ($first > 0 && $last > 0) {
if (open(IN, "<$tree/build.dat")) {
print "<hr><center><h1>Bustage stats for $tree</H1><H3>from " .
time2str($TIMEFORMAT, $first) . " to " .
time2str($TIMEFORMAT, $last) . "</H3></center>\n";
my %stats;
while (<IN>) {
chomp;
my ($mailtime, $buildtime, $buildname, $errorparser,
$buildstatus, $logfile, $binaryname) =
split( /\|/ );
if ($buildtime >= $first && $buildtime <= $last) {
if (!defined $stats{$buildname}) {
$stats{$buildname} = 0;
}
if ($buildstatus eq "busted") {
$stats{$buildname}++;
}
}
}
print "<table>\n";
print "<tr><th>Build name</th><th>Number of bustages</th></tr>\n";
foreach my $key (sort (keys %stats)) {
print "<tr><td>$key</td><td>$stats{$key}</td></tr>\n";
}
print "</table>\n";
} else {
print "<p><font color=red>There does not appear to be a tree " .
"named '$tree'.</font><p>";
}
}
print "<hr>\n";
}
if (!defined $tree) {
$tree = "";
}
if (!defined $start) {
$start = time2str($TIMEFORMAT, time() - 7*24*60*60); # One week ago.
}
if (!defined $end) {
$end = time2str($TIMEFORMAT, time()); # #now
}
print qq|
<form>
<table>
<tr>
<th align=right>Tree:</th>
<td><input name=tree size=30 value="$tree"></td>
</tr>
<tr>
<th align=right>Start time:</th>
<td><input name=start size=30 value="$start"></td>
</tr>
<tr>
<th align=right>End time:</th>
<td><input name=end size=30 value="$end"></td>
</tr>
</table>
<INPUT TYPE=\"submit\" VALUE=\"Generate stats \">
</form>
|;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

View File

@@ -1,42 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
# Figure out which directory tinderbox is in by looking at argv[0]
$tinderboxdir = $0;
$tinderboxdir =~ s:/[^/]*$::; # Remove last word, and slash before it.
if ($tinderboxdir eq "") {
$tinderboxdir = ".";
}
#print "tinderbox = $tinderboxdir\n";
chdir $tinderboxdir || die "Couldn't chdir to $tinderboxdir";
#print "cd ok\n";
open FL, "find . -name \"*.gz\" -mtime +7 -print |";
#print "find ok\n";
while( <FL> ){
chop();
#print "unlink $_\n";
unlink $_;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,91 +0,0 @@
#!/tools/ns/bin/perl5
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
#
# to run this script execute 'nohup copydata.pl &' from the tinderbox directory
#
$start_dir = `pwd`;
chop($start_dir);
$scp_cmd = "scp -o 'User snapshot' -o'Port 22' -o 'UserKnownHostsFile /u/shaver/snapshot/known_hosts' -o 'IdentityFile /u/shaver/snapshot/identity'";
$last_time = 0;
$min_cycle_time = 3 * 60; # 3 minutes
print "starting dir is :$start_dir\n";
while( 1 ){
chdir("$start_dir");
if( time - $last_time < $min_cycle_time ){
$sleep_time = $min_cycle_time - (time - $last_time);
print "\n\nSleeping $sleep_time seconds ...\n";
sleep( $sleep_time );
}
&copy_data("Mozilla");
&copy_data("raptor");
chdir( "$start_dir");
print "$scp_cmd * cvs1.mozilla.org:/e/webtools/tinderbox\n";
system "$scp_cmd * cvs1.mozilla.org:/e/webtools/tinderbox";
chdir( "$start_dir/../bonsai");
print "$scp_cmd * cvs1.mozilla.org:/e/webtools/bonsai\n";
system "$scp_cmd * cvs1.mozilla.org:/e/webtools/bonsai";
$last_time = time;
}
1;
sub copy_data {
local($data_dir) = @_;
local($zips,$qry);
chdir $data_dir || die "couldn't chdir to $data_dir";
system "echo hello >lastup.new";
if( -r 'lastup' ) {
$qry = '-newer lastup.old';
rename 'lastup', 'lastup.old'
}
rename 'lastup.new', 'lastup';
open( FINDER, "find . $qry -name \"*.gz\" -print|" );
while(<FINDER>){
print;
chop;
$zips .= "$_ ";
}
close( FINDER );
unlink 'lastup.old';
print "$scp_cmd *.txt $zips *.dat cvs1.mozilla.org:/e/webtools/tinderbox/$data_dir\n";
system "$scp_cmd *.txt $zips *.dat cvs1.mozilla.org:/e/webtools/tinderbox/$data_dir";
chdir $start_dir || die "couldn't chdir to $start_dir";
}

View File

@@ -1,217 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib "../bonsai";
require 'lloydcgi.pl';
require 'globals.pl';
umask O666;
$|=1;
check_password();
print "Content-type: text/html\n\n<HTML>\n";
$command = $form{'command'};
$tree= $form{'tree'};
if( $command eq 'create_tree' ){
&create_tree;
}
elsif( $command eq 'remove_build' ){
&remove_build;
}
elsif( $command eq 'trim_logs' ){
&trim_logs;
}
elsif( $command eq 'set_message' ){
&set_message;
}
elsif( $command eq 'disable_builds' ){
&disable_builds;
} else {
print "Unknown command: \"$command\".";
}
sub trim_logs {
$days = $form{'days'};
$tree = $form{'tree'};
print "<h2>Trimming Log files for $tree...</h2>\n<p>";
$min_date = time - (60*60*24 * $days);
#
# Nuke the old log files
#
$i = 0;
opendir( D, 'DogbertTip' );
while( $fn = readdir( D ) ){
if( $fn =~ /\.gz$/ ){
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
$ctime,$blksize,$blocks) = stat("$tree/$fn");
if( $mtime && ($mtime < $min_date) ){
print "$fn\n";
$tblocks += $blocks;
unlink( "$tree/$fn" );
$i++;
}
}
}
closedir( D );
$k = $tblocks*512/1024;
print "<br><b>$i Logfiles ( $k K bytes ) removed</b><br>\n";
#
# Trim build.dat
#
$builds_removed = 0;
open(BD, "<$tree/build.dat");
open(NBD, ">$tree/build.dat.new");
while( <BD> ){
($mailtime,$buildtime,$buildname) = split( /\|/ );
if( $buildtime >= $min_date ){
print NBD $_;
}
else {
$builds_removed++;
}
}
close( BD );
close( NBD );
rename( "$tree/build.dat", "$tree/build.dat.old" );
rename( "$tree/build.dat.new", "$tree/build.dat" );
print "<h2>$builds_removed Builds removed from build.dat</h2>\n";
}
sub create_tree {
$treename = $form{'treename'};
my $repository = $form{'repository'};
$modulename = $form{'modulename'};
$branchname = $form{'branchname'};
if( -r $treename ){
chmod 0777, $treename;
}
else {
mkdir( $treename, 0777 ) || die "<h1> Cannot mkdir $treename</h1>";
}
open( F, ">$treename/treedata.pl" );
print F "\$cvs_module='$modulename';\n";
print F "\$cvs_branch='$branchname';\n";
if ($repository ne "") {
print F "\$cvs_root='$repository';\n";
}
close( F );
open( F, ">$treename/build.dat" );
close( F );
open( F, ">$treename/who.dat" );
close( F );
open( F, ">$treename/notes.txt" );
close( F );
chmod 0777, "$treename/build.dat", "$treename/who.dat", "$treename/notes.txt",
"$treename/treedata.pl";
print "<h2><a href=showbuilds.cgi?tree=$treename>Tree created or modified</a></h2>\n";
}
sub remove_build {
$build_name = $form{'build'};
#
# Trim build.dat
#
$builds_removed = 0;
open(BD, "<$tree/build.dat");
open(NBD, ">$tree/build.dat.new");
while( <BD> ){
($mailtime,$buildtime,$bname) = split( /\|/ );
if( $bname ne $build_name ){
print NBD $_;
}
else {
$builds_removed++;
}
}
close( BD );
close( NBD );
chmod( 0777, "$tree/build.dat.new");
rename( "$tree/build.dat", "$tree/build.dat.old" );
rename( "$tree/build.dat.new", "$tree/build.dat" );
print "<h2><a href=showbuilds.cgi?tree=$tree>
$builds_removed Builds removed from build.dat</a></h2>\n";
}
sub disable_builds {
my $i,%buildnames;
$build_name = $form{'build'};
#
# Trim build.dat
#
open(BD, "<$tree/build.dat");
while( <BD> ){
($mailtime,$buildtime,$bname) = split( /\|/ );
$buildnames{$bname} = 0;
}
close( BD );
for $i (keys %form) {
if ($i =~ /^build_/ ){
$i =~ s/^build_//;
$buildnames{$i} = 1;
}
}
open(IGNORE, ">$tree/ignorebuilds.pl");
print IGNORE '$ignore_builds = {' . "\n";
for $i ( sort keys %buildnames ){
if( $buildnames{$i} == 0 ){
print IGNORE "\t\t'$i' => 1,\n";
}
}
print IGNORE "\t};\n";
chmod( 0777, "$tree/ignorebuilds.pl");
print "<h2><a href=showbuilds.cgi?tree=$treename>Build state Changed</a></h2>\n";
}
sub set_message {
$m = $form{'message'};
$m =~ s/\'/\\'/g;
open(MOD, ">$tree/mod.pl");
print MOD "\$message_of_day = \'$m\'\;\n1;";
close(MOD);
chmod( 0777, "$tree/mod.pl");
print "<h2><a href=showbuilds.cgi?tree=$tree>
Message Changed</a></h2>\n";
}

View File

@@ -1,45 +0,0 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
1;
#
# Scan a line and see if it has an error
#
sub has_error {
$line =~ /fatal error/ # link error
|| $line =~ /Error / # C error
|| $line =~ /\[checkout aborted\]/ #cvs error
|| $line =~ /Couldn\'t find project file / # CW project error
;
}
sub has_warning {
$line =~ /^[A-Za-z0-9_]+\.[A-Za-z0-9]+\ line [0-9]+/ ;
}
sub has_errorline {
local( $line ) = @_;
if( $line =~ /^(([A-Za-z0-9_]+\.[A-Za-z0-9]+) line ([0-9]+))/ ){
$error_file = $1;
$error_file_ref = $2;
$error_line = $3;
$error_guess = 1;
return 1;
}
return 0;
}

View File

@@ -1,59 +0,0 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
1;
#
# Scan a line and see if it has an error
#
sub has_error {
$line =~ /fatal error/ # link error
|| $line =~ /^C / # cvs merge conflict
|| $line =~ / Error: / # C error
|| $line =~ / error\([0-9]*\)\:/ # C error
|| ($line =~ /gmake/ && $line =~ / Error /)
|| $line =~ /\[checkout aborted\]/ #cvs error
|| $line =~ /\: cannot find module/ #cvs error
;
}
sub has_warning {
$line =~ /^[A-Za-z0-9_]+\.[A-Za-z0-9]+\:[0-9]+\:/
|| $line =~ /^\"[A-Za-z0-9_]+\.[A-Za-z0-9]+\"\, line [0-9]+\:/
;
}
sub has_errorline {
local( $line ) = @_;
if( $line =~ /^(([A-Za-z0-9_]+\.[A-Za-z0-9]+)\:([0-9]+)\:)/ ){
$error_file = $1;
$error_file_ref = $2;
$error_line = $3;
$error_guess = 1;
return 1;
}
if ( $line =~ /^(\"([A-Za-z0-9_]+\.[A-Za-z0-9]+)\"\, line ([0-9]+)\:)/ ){
$error_file = $1;
$error_file_ref = $2;
$error_line = $3;
$error_guess = 1;
return 1;
}
return 0;
}

View File

@@ -1,73 +0,0 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
1;
#
# Scan a line and see if it has an error
#
sub has_error {
$line =~ /fatal error/ # link error
|| $line =~ / error / # C error
|| $line =~ /^C / # cvs merge conflict
|| $line =~ /error C/ # C error
|| $line =~ /Creating new precompiled header/ # Wastes time.
|| $line =~ /error:/ # java error
|| $line =~ /jmake.MakerFailedException:/ # java error
|| $line =~ /Unknown host / # cvs error
|| $line =~ /: build failed\;/ # nmake error
|| ($line =~ /gmake/ && $line =~ / Error /)
|| $line =~ /\[checkout aborted\]/ #cvs error
|| $line =~ /\: cannot find module/ #cvs error
;
}
sub has_warning {
$line =~ /: warning/ # link error
|| $line =~ / error / # C error
;
}
sub has_errorline {
local( $line ) = @_;
$error_file = ''; #'NS\CMD\WINFE\CXICON.cpp';
$error_line = 0;
if( $line =~ m@(ns([\\/][a-z0-9\._]+)*)@i ){
$error_file = $1;
$error_file_ref = lc $error_file;
$error_file_ref =~ s@\\@/@g;
$line =~ m/\(([0-9]+)\)/;
$error_line = $1;
return 1;
}
if( $line =~ m@(^([A-Za-z0-9_]+\.[A-Za-z])+\(([0-9]+)\))@ ){
$error_file = $1;
$error_file_ref = lc $2;
$error_line = $3;
$error_guess=1;
$error_file_ref =~ s@\\@/@g;
return 1;
}
return 0;
}

View File

@@ -1,44 +0,0 @@
This directory contains example Tinderbox client scripts. These scripts are
for illustration/documentation purposes only and are not maintained
regularly. Current scripts to build mozilla will live in an another spot
in the mozilla tree.
Three examples have been provided:
mozilla-windows.pl: perl script that drives mozilla tinderbox builds for Win32
mozilla-unix.pl : perl script that drives mozilla tinderbox builds for UNIX
build-moz-smoke.pl: perl script that drives mozilla tinderbox builds for UNIX,
and subsequently runs the executable returning a green tree only if
it does not crash.
These scripts show the basic elements of a Tinderbox client script. These
elements are:
1) Sending a start e-mail to the Tinderbox server, in the form of a formatted
mail message. Example:
tinderbox: tree: Mozilla
tinderbox: builddate: 900002087
tinderbox: status: building
tinderbox: build: IRIX 6.3 Depend
tinderbox: errorparser: unix
tinderbox: buildfamily: unix
2) Obtain a source tree by performing a cvs checkout.
3) Perform the build, saving the output to a log file.
4) Determine if the build was successful or failed. This could be done either
by checking for the presence of a binary, or being using error codes returned
from the compile command.
5) Send a completion message to Tinderbox, identifying build success or
failure. Example:
tinderbox: tree: Mozilla
tinderbox: builddate: 900002087
tinderbox: status: success
tinderbox: build: IRIX 6.3 Depend
tinderbox: errorparser: unix
tinderbox: buildfamily: unix

View File

@@ -1,594 +0,0 @@
#!/usr/bin/perl
require 5.000;
use Sys::Hostname;
use POSIX "sys_wait_h";
use Cwd;
$Version = "1.000";
sub InitVars {
# PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
$BuildAdministrator = "$ENV{'USER'}\@$ENV{'HOST'}";
#Default values of cmdline opts
$BuildDepend = 1; #depend or clobber
$ReportStatus = 1; # Send results to server or not
$BuildOnce = 0; # Build once, don't send results to server
$BuildClassic = 0; # Build classic source
#relative path to binary
$BinaryName{'x'} = 'mozilla-export';
$BinaryName{'qt'} = 'qtmozilla-export';
$BinaryName{'gnome'} = 'gnuzilla-export';
$BinaryName{'webshell'} = '/webshell/tests/viewer/viewer';
# Set these to what makes sense for your system
$cpus = 1;
$Make = 'gmake'; # Must be gnu make
$mail = '/bin/mail';
$Autoconf = 'autoconf -l build/autoconf';
$CVS = 'cvs -z3';
$CVSCO = 'co -P';
# Set these proper values for your tinderbox server
$Tinderbox_server = 'tinderbox-daemon\@cvs-mirror.mozilla.org';
#$Tinderbox_server = 'external-tinderbox-incoming\@tinderbox.seawood.org';
# These shouldn't really need to be changed
$BuildSleep = 10; # Minimum wait period from start of build to start
# of next build in minutes
$BuildTree = 'SeaMonkey';
$BuildTag = '';
$BuildName = '';
$TopLevel = '.';
$Topsrcdir = 'mozilla';
$BuildObjName = '';
$BuildConfigDir = 'mozilla/config';
$ClobberStr = 'realclean';
$ConfigureEnvArgs = 'CFLAGS=-pipe CXXFLAGS=-pipe';
#$ConfigureEnvArgs = '';
$ConfigureArgs = "--with-nspr=/usr/local/nspr --cache-file=/dev/null --with-static-gtk --enable-editor";
$ConfigGuess = './build/autoconf/config.guess';
$Logfile = '${BuildDir}.log';
} #EndSub-InitVars
sub ConditionalArgs {
if ( $BuildClassic ) {
$FE = 'x';
$ConfigureArgs .= " --enable-fe=$FE";
# $BuildTree = 'raptor';
$BuildModule = 'Raptor';
$BuildTag = ''
if ($BuildTag eq '');
$TopLevel = "mozilla-classic";
} else {
# $BuildTree = 'raptor';
# $Toolkit = 'gtk';
$FE = 'webshell';
# $BuildModule = 'Raptor';
$BuildModule = 'SeaMonkeyAll';
# $ConfigureArgs .= " --enable-toolkit=$Toolkit";
}
$CVSCO .= " -r $BuildTag" if ( $BuildTag ne '');
}
sub SetupEnv {
umask(0);
$ENV{"CVSROOT"} = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot';
$ENV{"LD_LIBRARY_PATH"} = '/usr/local/nspr:/builds/tinderbox/SeaMonkey/Linux_2.0.34_clobber/mozilla/obj-i586-pc-linux-gnu/dist/bin:/usr/lib/png:/usr/local/lib';
$ENV{"DISPLAY"} = 'crucible.mcom.com:0.0';
} #EndSub-SetupEnv
sub SetupPath {
my($Path);
$Path = $ENV{PATH};
print "Path before: $Path\n";
if ( $OS eq 'SunOS' ) {
$ENV{'PATH'} = '/usr/ccs/bin:' . $ENV{'PATH'};
}
$Path = $ENV{PATH};
print "Path After: $Path\n";
} #EndSub-SetupPath
##########################################################################
# NO USER CONFIGURABLE PIECES BEYOND THIS POINT #
##########################################################################
sub GetSystemInfo {
$OS = `uname -s`;
$OSVer = `uname -r`;
chop($OS, $OSVer);
if ( $OS eq 'AIX' ) {
$OSVer = `uname -v`;
chop($OSVer);
$OSVer = $OSVer . "." . `uname -r`;
chop($OSVer);
}
if ( $OS eq 'IRIX64' ) {
$OS = 'IRIX';
}
if ( $OS eq 'SCO_SV' ) {
$OS = 'SCOOS';
$OSVer = '5.0';
}
my $host, $myhost = hostname;
chomp($myhost);
($host, $junk) = split(/\./, $myhost);
$BuildName = "";
if ( "$host" ne "" ) {
$BuildName = $host . ' ';
}
$BuildName .= $OS . ' ' . $OSVer . ' ' . ($BuildDepend?'Depend':'Clobber');
$DirName = $OS . '_' . $OSVer . '_' . ($BuildDepend?'depend':'clobber');
$RealOS = $OS;
$RealOSVer = $OSVer;
if ( $OS eq 'HP-UX' ) {
$RealOSVer = substr($OSVer,0,4);
}
if ( $OS eq 'Linux' ) {
$RealOSVer = substr($OSVer,0,3);
}
if ($BuildClassic) {
$logfile = "${DirName}-classic.log";
} else {
$logfile = "${DirName}.log";
}
} #EndSub-GetSystemInfo
sub BuildIt {
my ($fe, @felist, $EarlyExit, $LastTime, $StartTimeStr);
mkdir("$DirName", 0777);
chdir("$DirName") || die "Couldn't enter $DirName";
$StartDir = getcwd();
$LastTime = 0;
print "Starting dir is : $StartDir\n";
$EarlyExit = 0;
while ( ! $EarlyExit ) {
chdir("$StartDir");
if ( time - $LastTime < (60 * $BuildSleep) ) {
$SleepTime = (60 * $BuildSleep) - (time - $LastTime);
print "\n\nSleeping $SleepTime seconds ...\n";
sleep($SleepTime);
}
$LastTime = time;
$StartTime = time - 60 * 10;
$StartTimeStr = &CVSTime($StartTime);
&StartBuild if ($ReportStatus);
$CurrentDir = getcwd();
if ( $CurrentDir ne $StartDir ) {
print "startdir: $StartDir, curdir $CurrentDir\n";
die "curdir != startdir";
}
$BuildDir = $CurrentDir;
unlink( "$logfile" );
print "opening $logfile\n";
open( LOG, ">$logfile" ) || print "can't open $?\n";
print LOG "current dir is -- $hostname:$CurrentDir\n";
print LOG "Build Administrator is $BuildAdministrator\n";
&PrintEnv;
$BuildStatus = 0;
mkdir($TopLevel, 0777);
chdir($TopLevel) || die "chdir($TopLevel): $!\n";
if ( $BuildClassic ) {
print"$CVS $CVSCO $BuildModule\n";
print LOG "$CVS $CVSCO $BuildModule\n";
open (PULL, "$CVS $CVSCO $BuildModule 2>&1 |") || die "open: $!\n";
} else {
# print "$CVS $CVSCO mozilla/nglayout.mk\n";
# print LOG "$CVS $CVSCO mozilla/nglayout.mk\n";
# open (PULL, "$CVS $CVSCO mozilla/nglayout.mk 2>&1 |") || die "open: $!\n";
print "$CVS $CVSCO mozilla/client.mk\n";
print LOG "$CVS $CVSCO mozilla/client.mk\n";
open (PULL, "$CVS $CVSCO mozilla/client.mk 2>&1 |") || die "open: $!\n";
}
while (<PULL>) {
print $_;
print LOG $_;
}
close(PULL);
# Move to topsrcdir
#chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
# Do a separate checkout with toplevel makefile
if (! $BuildClassic) {
# print LOG "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO'|\n";
# open (PULLALL, "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO' |\n");
print LOG "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO'|\n";
open (PULLALL, "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO' |\n");
while (<PULLALL>) {
print LOG $_;
print $_;
}
close(PULLALL);
}
chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
print LOG "$Autoconf\n";
open (AUTOCONF, "$Autoconf 2>&1 | ") || die "$Autoconf: $!\n";
while (<AUTOCONF>) {
print LOG $_;
print $_;
}
close(AUTOCONF);
print LOG "$ConfigGuess\n";
$BuildObjName = "obj-";
open (GETOBJ, "$ConfigGuess 2>&1 |") || die "$ConfigGuess: $!\n";
while (<GETOBJ>) {
print $_;
print LOG $_;
chomp($BuildObjName .= $_);
}
close (GETOBJ);
mkdir($BuildObjName, 0777);
chdir($BuildObjName) || die "chdir($BuildObjName): $!\n";
print LOG "$ConfigureEnvArgs ../configure $ConfigureArgs\n";
open (CONFIGURE, "$ConfigureEnvArgs ../configure $ConfigureArgs 2>&1 |") || die "../configure: $!\n";
while (<CONFIGURE>) {
print $_;
print LOG $_;
}
close(CONFIGURE);
# if we are building depend, rebuild dependencies
if ($BuildDepend) {
print LOG "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n";
open ( MAKEDEPEND, "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n");
while ( <MAKEDEPEND> ) {
print $_;
print LOG $_;
}
close (MAKEDEPEND);
system("rm -rf dist");
} else {
# Building clobber
print LOG "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |\n";
open( MAKECLOBBER, "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |");
while ( <MAKECLOBBER> ) {
print $_;
print LOG $_;
}
close( MAKECLOBBER );
}
@felist = split(/,/, $FE);
foreach $fe ( @felist ) {
if (&BinaryExists($fe)) {
print LOG "deleting existing binary\n";
&DeleteBinary($fe);
}
}
if ($BuildClassic) {
# Build the BE only
print LOG "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |\n";
open( BEBUILD, "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |");
while ( <BEBUILD> ) {
print $_;
print LOG $_;
}
close( BEBUILD );
foreach $fe ( @felist ) {
# Now build each front end
print LOG "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n";
open(FEBUILD, "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n");
while (<FEBUILD>) {
print $_;
print LOG $_;
}
close(FEBUILD);
}
} else {
print LOG "$Make MAKE='$Make -j $cpus' 2>&1 |\n";
open(BUILD, "$Make MAKE='$Make -j $cpus' 2>&1 |\n");
while (<BUILD>) {
print $_;
print LOG $_;
}
close(BUILD);
}
foreach $fe (@felist) {
if (&BinaryExists($fe)) {
print LOG "export binary exists, build successful. Testing...\n";
#return 0 if no problem, else 333 for a runtime error
$BuildStatus = &RunSmokeTest($fe);
}
else {
print LOG "export binary missing, build FAILED\n";
$BuildStatus = 666;
}
if ( $BuildStatus == 0 ) {
$BuildStatusStr = 'success';
}
elsif ( $BuildStatus == 333 ) {
$BuildStatusStr = 'testfailed';
}
else {
$BuildStatusStr = 'busted';
}
# replaced by above lines
# $BuildStatusStr = ( $BuildStatus ? 'busted' : 'success' );
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: $BuildStatusStr\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
}
close(LOG);
chdir("$StartDir");
# this fun line added on 2/5/98. do not remove. Translated to english,
# that's "take any line longer than 1000 characters, and split it into less
# than 1000 char lines. If any of the resulting lines is
# a dot on a line by itself, replace that with a blank line."
# This is to prevent cases where a <cr>.<cr> occurs in the log file. Sendmail
# interprets that as the end of the mail, and truncates the log before
# it gets to Tinderbox. (terry weismann, chris yeh)
#
# This was replaced by a perl 'port' of the above, writen by
# preed@netscape.com; good things: no need for system() call, and now it's
# all in perl, so we don't have to do OS checking like before.
open(LOG, "$logfile") || die "Couldn't open logfile: $!\n";
open(OUTLOG, ">${logfile}.last") || die "Couldn't open logfile: $!\n";
while (<LOG>) {
$q = 0;
for (;;) {
$val = $q * 1000;
$Output = substr($_, $val, 1000);
last if $Output eq undef;
$Output =~ s/^\.$//g;
$Output =~ s/\n//g;
print OUTLOG "$Output\n";
$q++;
} #EndFor
} #EndWhile
close(LOG);
close(OUTLOG);
system( "$mail $Tinderbox_server < ${logfile}.last" )
if ($ReportStatus );
unlink("$logfile");
# if this is a test run, set early_exit to 0.
#This mean one loop of execution
$EarlyExit++ if ($BuildOnce);
}
} #EndSub-BuildIt
sub CVSTime {
my($StartTimeArg) = @_;
my($RetTime, $StartTimeArg, $sec, $minute, $hour, $mday, $mon, $year);
($sec,$minute,$hour,$mday,$mon,$year) = localtime($StartTimeArg);
$mon++; # month is 0 based.
sprintf("%02d/%02d/%02d %02d:%02d:00", $mon,$mday,$year,$hour,$minute );
}
sub StartBuild {
my($fe, @felist);
@felist = split(/,/, $FE);
# die "SERVER: " . $Tinderbox_server . "\n";
open( LOG, "|$mail $Tinderbox_server" );
foreach $fe ( @felist ) {
print LOG "\n";
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: building\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
print LOG "\n";
}
close( LOG );
}
# check for the existence of the binary
sub BinaryExists {
my($fe) = @_;
my($Binname);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/'. $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG $BinName . "\n";
if ((-e $BinName) && (-x $BinName) && (-s $BinName)) {
1;
}
else {
0;
}
}
sub DeleteBinary {
my($fe) = @_;
my($BinName);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG "unlinking $BinName\n";
unlink ($BinName) || print LOG "unlinking $BinName failed\n";
}
sub ParseArgs {
my($i, $manArg);
if( @ARGV == 0 ) {
&PrintUsage;
}
$i = 0;
$manArg = 0;
while( $i < @ARGV ) {
if ($ARGV[$i] eq '--depend') {
$BuildDepend = 1;
$manArg++;
}
elsif ($ARGV[$i] eq '--clobber') {
$BuildDepend = 0;
$manArg++;
}
elsif ( $ARGV[$i] eq '--once' ) {
$BuildOnce = 1;
#$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--classic') {
$BuildClassic = 1;
}
elsif ($ARGV[$i] eq '--noreport') {
$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--version' || $ARGV[$i] eq '-v') {
die "$0: version $Version\n";
}
elsif ( $ARGV[$i] eq '-tag' ) {
$i++;
$BuildTag = $ARGV[$i];
if ( $BuildTag eq '' || $BuildTag eq '-t') {
&PrintUsage;
}
}
elsif ( $ARGV[$i] eq '-t' ) {
$i++;
$BuildTree = $ARGV[$i];
if ( $BuildTree eq '' ) {
&PrintUsage;
}
} else {
&PrintUsage;
}
$i++;
} #EndWhile
if ( $BuildTree =~ /^\s+$/i ) {
&PrintUsage;
}
if ($BuildDepend eq undef) {
&PrintUsage;
}
&PrintUsage if (! $manArg );
} #EndSub-ParseArgs
sub PrintUsage {
die "usage: $0 [--depend | --clobber] [-v | --version ] [--once --classic --noreport -tag TREETAG -t TREENAME ]\n";
}
sub PrintEnv {
my ($key);
foreach $key (keys %ENV) {
print LOG "$key = $ENV{$key}\n";
print "$key = $ENV{$key}\n";
}
} #EndSub-PrintEnv
sub RunSmokeTest {
my($fe) = @_;
my($Binary);
$fe = 'x' if (!defined($fe));
$Binary = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
print LOG $BinName . "\n";
$waittime = 30;
$pid = fork;
exec $Binary if !$pid;
# parent - wait $waittime seconds then check on child
sleep $waittime;
$status = waitpid $pid, WNOHANG();
if ($status != 0) {
print LOG "$BinName has crashed or quit. Turn the tree orange now.\n";
return 333;
}
print LOG "Success! $BinName is still running. Killing..\n";
# try to kill 3 times, then try a kill -9
for ($i=0 ; $i<3 ; $i++) {
kill 'TERM',$pid,;
# give it 3 seconds to actually die
sleep 3;
$status = waitpid $pid, WNOHANG();
last if ($status != 0);
}
return 0;
} #EndSub-RunSmokeTest
# Main function
&InitVars;
&ParseArgs;
&ConditionalArgs;
&GetSystemInfo;
&SetupEnv;
&SetupPath;
&BuildIt;
1;

View File

@@ -1,63 +0,0 @@
#!/usr/bin/perl
sub InitVars {
#initialize variables
#$binary_name = '/netscape-export';
$BinaryName = '/mozilla-export';
$BuildDepend = 1; #depend or clobber
$BuildTree = '';
$BuildTag = '';
$BuildName = '';
#$BuildContinue = 0;
$TopLevel = 'mozilla/';
$BuildObjName = '';
$BuildSleep = 10;
$BuildUnixclasses = 0;
$EarlyExit = 1;
$BuildStartDir = 'ns/';
$BuildConfigDir = 'mozilla/config';
} #EndSub-InitVars
sub SetupEnv {
umask(0);
$ENV{'MOZILLA_CLIENT'} = 1;
$ENV{'NETSCAPE_HIERARCHY'} = 1;
$ENV{'BUILD_OFFICIAL'} = 1;
$ENV{'NSPR20'} = 1;
$ENV{'AWT_11'} = 1;
$ENV{'MOZ_SECURITY'} = 1;
$ENV{"CVSROOT"} = '/m/src';
$ENV{"MAKE"} = 'gmake -e';
$ENV{'MOZ_MEDIUM'} = 1;
$ENV{'NO_MDUPDATE'} = 1;
$ENV{'EDITOR'} = 1;
} #EndSub-SetupEnv
sub SetupPath {
my($Path);
$Path = $ENV{PATH};
print "Path before: $Path\n";
$ENV{'PATH'} = '/tools/ns/bin:/tools/contrib/bin:/usr/local/bin:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/hosts:/usr/ucb:';
# This won't work on x86 or sunos4 systems....
if ( $OS eq 'SunOS' ) {
$ENV{'PATH'} = '/usr/ccs/bin:/tools/ns/soft/gcc-2.6.3/run/default/sparc_sun_solaris2.4/bin:' . $ENV{'PATH'};
$ENV{'NO_MDUPDATE'} = 1;
}
if ( $OS eq 'AIX' ) {
$ENV{'PATH'} = $ENV{'PATH'} . '/usr/lpp/xlC/bin:/usr/local-aix/bin:';
}
$Path = $ENV{PATH};
print "Path After: $Path\n";
} #EndSub-SetupPath
1;

View File

@@ -1,548 +0,0 @@
#!/usr/bin/perl
require 5.000;
use Sys::Hostname;
use Cwd;
$Version = "1.000";
sub InitVars {
# PLEASE FILL THIS IN WITH YOUR PROPER EMAIL ADDRESS
$BuildAdministrator = "$ENV{'USER'}\@$ENV{'HOST'}";
#Default values of cmdline opts
$BuildDepend = 1; #depend or clobber
$ReportStatus = 1; # Send results to server or not
$BuildOnce = 0; # Build once, don't send results to server
$BuildClassic = 0; # Build classic source
#relative path to binary
$BinaryName{'x'} = 'mozilla-export';
$BinaryName{'qt'} = 'qtmozilla-export';
$BinaryName{'gnome'} = 'gnuzilla-export';
$BinaryName{'webshell'} = '/webshell/tests/viewer/viewer';
$BinaryName{'xpfe'} = '/xpfe/xpviewer/src/xpviewer';
# Set these to what makes sense for your system
$cpus = 1;
$Make = 'gmake'; # Must be gnu make
$mail = '/bin/mail';
$Autoconf = 'autoconf -l build/autoconf';
$CVS = 'cvs -z3';
$CVSCO = 'co -P';
# Set these proper values for your tinderbox server
$Tinderbox_server = 'tinderbox-daemon\@warp.mcom.com';
#$Tinderbox_server = 'external-tinderbox-incoming\@tinderbox.seawood.org';
# These shouldn't really need to be changed
$BuildSleep = 10; # Minimum wait period from start of build to start
# of next build in minutes
$BuildTree = 'raptor';
$BuildTag = '';
$BuildName = '';
$TopLevel = '.';
$Topsrcdir = 'mozilla';
$BuildObjName = '';
$BuildConfigDir = 'mozilla/config';
$ClobberStr = 'realclean';
$ConfigureEnvArgs = 'CFLAGS=-pipe CXXFLAGS=-pipe';
#$ConfigureEnvArgs = '';
$ConfigureArgs = "--cache-file=/dev/null";
$ConfigGuess = './build/autoconf/config.guess';
$Logfile = '${BuildDir}.log';
} #EndSub-InitVars
sub ConditionalArgs {
if ( $BuildClassic ) {
$FE = 'x';
$ConfigureArgs .= " --enable-fe=$FE";
$BuildTree = 'raptor';
$BuildModule = 'Raptor';
$BuildTag = ''
if ($BuildTag eq '');
$TopLevel = "mozilla-classic";
} else {
$BuildTree = 'raptor';
# $Toolkit = 'gtk';
$FE = 'webshell,xpfe';
$BuildModule = 'Raptor';
# $ConfigureArgs .= " --enable-toolkit=$Toolkit";
}
$CVSCO .= " -r $BuildTag" if ( $BuildTag ne '');
}
sub SetupEnv {
umask(0);
$ENV{"CVSROOT"} = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot';
} #EndSub-SetupEnv
sub SetupPath {
my($Path);
$Path = $ENV{PATH};
print "Path before: $Path\n";
if ( $OS eq 'SunOS' ) {
$ENV{'PATH'} = '/usr/ccs/bin:' . $ENV{'PATH'};
}
$Path = $ENV{PATH};
print "Path After: $Path\n";
} #EndSub-SetupPath
##########################################################################
# NO USER CONFIGURABLE PIECES BEYOND THIS POINT #
##########################################################################
sub GetSystemInfo {
$OS = `uname -s`;
$OSVer = `uname -r`;
chop($OS, $OSVer);
if ( $OS eq 'AIX' ) {
$OSVer = `uname -v`;
chop($OSVer);
$OSVer = $OSVer . "." . `uname -r`;
chop($OSVer);
}
if ( $OS eq 'IRIX64' ) {
$OS = 'IRIX';
}
if ( $OS eq 'SCO_SV' ) {
$OS = 'SCOOS';
$OSVer = '5.0';
}
my $host, $myhost = hostname;
chomp($myhost);
($host, $junk) = split(/\./, $myhost);
$BuildName = "";
if ( "$host" ne "" ) {
$BuildName = $host . ' ';
}
$BuildName .= $OS . ' ' . $OSVer . ' ' . ($BuildDepend?'Depend':'Clobber');
$DirName = $OS . '_' . $OSVer . '_' . ($BuildDepend?'depend':'clobber');
$RealOS = $OS;
$RealOSVer = $OSVer;
if ( $OS eq 'HP-UX' ) {
$RealOSVer = substr($OSVer,0,4);
}
if ( $OS eq 'Linux' ) {
$RealOSVer = substr($OSVer,0,3);
}
if ($BuildClassic) {
$logfile = "${DirName}-classic.log";
} else {
$logfile = "${DirName}.log";
}
} #EndSub-GetSystemInfo
sub BuildIt {
my ($fe, @felist, $EarlyExit, $LastTime, $StartTimeStr);
mkdir("$DirName", 0777);
chdir("$DirName") || die "Couldn't enter $DirName";
$StartDir = getcwd();
$LastTime = 0;
print "Starting dir is : $StartDir\n";
$EarlyExit = 0;
while ( ! $EarlyExit ) {
chdir("$StartDir");
if ( time - $LastTime < (60 * $BuildSleep) ) {
$SleepTime = (60 * $BuildSleep) - (time - $LastTime);
print "\n\nSleeping $SleepTime seconds ...\n";
sleep($SleepTime);
}
$LastTime = time;
$StartTime = time - 60 * 10;
$StartTimeStr = &CVSTime($StartTime);
&StartBuild if ($ReportStatus);
$CurrentDir = getcwd();
if ( $CurrentDir ne $StartDir ) {
print "startdir: $StartDir, curdir $CurrentDir\n";
die "curdir != startdir";
}
$BuildDir = $CurrentDir;
unlink( "$logfile" );
print "opening $logfile\n";
open( LOG, ">$logfile" ) || print "can't open $?\n";
print LOG "current dir is -- $hostname:$CurrentDir\n";
print LOG "Build Administrator is $BuildAdministrator\n";
&PrintEnv;
$BuildStatus = 0;
mkdir($TopLevel, 0777);
chdir($TopLevel) || die "chdir($TopLevel): $!\n";
if ( $BuildClassic ) {
print"$CVS $CVSCO $BuildModule\n";
print LOG "$CVS $CVSCO $BuildModule\n";
open (PULL, "$CVS $CVSCO $BuildModule 2>&1 |") || die "open: $!\n";
} else {
# print "$CVS $CVSCO mozilla/nglayout.mk\n";
# print LOG "$CVS $CVSCO mozilla/nglayout.mk\n";
# open (PULL, "$CVS $CVSCO mozilla/nglayout.mk 2>&1 |") || die "open: $!\n";
print "$CVS $CVSCO mozilla/client.mk\n";
print LOG "$CVS $CVSCO mozilla/client.mk\n";
open (PULL, "$CVS $CVSCO mozilla/client.mk 2>&1 |") || die "open: $!\n";
}
while (<PULL>) {
print $_;
print LOG $_;
}
close(PULL);
# Move to topsrcdir
#chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
# Do a separate checkout with toplevel makefile
if (! $BuildClassic) {
# print LOG "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO'|\n";
# open (PULLALL, "$Make -f nglayout.mk pull_all CVSCO='$CVS $CVSCO' |\n");
print LOG "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO'|\n";
open (PULLALL, "$Make -f mozilla/client.mk checkout CVSCO='$CVS $CVSCO' |\n");
while (<PULLALL>) {
print LOG $_;
print $_;
}
close(PULLALL);
}
chdir($Topsrcdir) || die "chdir($Topsrcdir): $!\n";
print LOG "$Autoconf\n";
open (AUTOCONF, "$Autoconf 2>&1 | ") || die "$Autoconf: $!\n";
while (<AUTOCONF>) {
print LOG $_;
print $_;
}
close(AUTOCONF);
print LOG "$ConfigGuess\n";
$BuildObjName = "obj-";
open (GETOBJ, "$ConfigGuess 2>&1 |") || die "$ConfigGuess: $!\n";
while (<GETOBJ>) {
print $_;
print LOG $_;
chomp($BuildObjName .= $_);
}
close (GETOBJ);
mkdir($BuildObjName, 0777);
chdir($BuildObjName) || die "chdir($BuildObjName): $!\n";
print LOG "$ConfigureEnvArgs ../configure $ConfigureArgs\n";
open (CONFIGURE, "$ConfigureEnvArgs ../configure $ConfigureArgs 2>&1 |") || die "../configure: $!\n";
while (<CONFIGURE>) {
print $_;
print LOG $_;
}
close(CONFIGURE);
# if we are building depend, rebuild dependencies
if ($BuildDepend) {
print LOG "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n";
open ( MAKEDEPEND, "$Make MAKE='$Make -j $cpus' depend 2>&1 |\n");
while ( <MAKEDEPEND> ) {
print $_;
print LOG $_;
}
close (MAKEDEPEND);
system("rm -rf dist");
} else {
# Building clobber
print LOG "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |\n";
open( MAKECLOBBER, "$Make MAKE='$Make -j $cpus' $ClobberStr 2>&1 |");
while ( <MAKECLOBBER> ) {
print $_;
print LOG $_;
}
close( MAKECLOBBER );
}
@felist = split(/,/, $FE);
foreach $fe ( @felist ) {
if (&BinaryExists($fe)) {
print LOG "deleting existing binary\n";
&DeleteBinary($fe);
}
}
if ($BuildClassic) {
# Build the BE only
print LOG "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |\n";
open( BEBUILD, "$Make MAKE='$Make -j $cpus' MOZ_FE= 2>&1 |");
while ( <BEBUILD> ) {
print $_;
print LOG $_;
}
close( BEBUILD );
foreach $fe ( @felist ) {
# Now build each front end
print LOG "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n";
open(FEBUILD, "$Make MAKE='$Make -j $cpus' -C cmd/${fe}fe 2>&1 |\n");
while (<FEBUILD>) {
print $_;
print LOG $_;
}
close(FEBUILD);
}
} else {
print LOG "$Make MAKE='$Make -j $cpus' 2>&1 |\n";
open(BUILD, "$Make MAKE='$Make -j $cpus' 2>&1 |\n");
while (<BUILD>) {
print $_;
print LOG $_;
}
close(BUILD);
}
foreach $fe (@felist) {
if (&BinaryExists($fe)) {
print LOG "export binary exists, build SUCCESSFUL!\n";
$BuildStatus = 0;
}
else {
print LOG "export binary missing, build FAILED\n";
$BuildStatus = 666;
}
print LOG "\nBuild Status = $BuildStatus\n";
$BuildStatusStr = ( $BuildStatus ? 'busted' : 'success' );
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: $BuildStatusStr\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
}
close(LOG);
chdir("$StartDir");
# this fun line added on 2/5/98. do not remove. Translated to english,
# that's "take any line longer than 1000 characters, and split it into less
# than 1000 char lines. If any of the resulting lines is
# a dot on a line by itself, replace that with a blank line."
# This is to prevent cases where a <cr>.<cr> occurs in the log file. Sendmail
# interprets that as the end of the mail, and truncates the log before
# it gets to Tinderbox. (terry weismann, chris yeh)
#
# This was replaced by a perl 'port' of the above, writen by
# preed@netscape.com; good things: no need for system() call, and now it's
# all in perl, so we don't have to do OS checking like before.
open(LOG, "$logfile") || die "Couldn't open logfile: $!\n";
open(OUTLOG, ">${logfile}.last") || die "Couldn't open logfile: $!\n";
while (<LOG>) {
$q = 0;
for (;;) {
$val = $q * 1000;
$Output = substr($_, $val, 1000);
last if $Output eq undef;
$Output =~ s/^\.$//g;
$Output =~ s/\n//g;
print OUTLOG "$Output\n";
$q++;
} #EndFor
} #EndWhile
close(LOG);
close(OUTLOG);
system( "$mail $Tinderbox_server < ${logfile}.last" )
if ($ReportStatus );
unlink("$logfile");
# if this is a test run, set early_exit to 0.
#This mean one loop of execution
$EarlyExit++ if ($BuildOnce);
}
} #EndSub-BuildIt
sub CVSTime {
my($StartTimeArg) = @_;
my($RetTime, $StartTimeArg, $sec, $minute, $hour, $mday, $mon, $year);
($sec,$minute,$hour,$mday,$mon,$year) = localtime($StartTimeArg);
$mon++; # month is 0 based.
sprintf("%02d/%02d/%02d %02d:%02d:00", $mon,$mday,$year,$hour,$minute );
}
sub StartBuild {
my($fe, @felist);
@felist = split(/,/, $FE);
# die "SERVER: " . $Tinderbox_server . "\n";
open( LOG, "|$mail $Tinderbox_server" );
foreach $fe ( @felist ) {
print LOG "\n";
print LOG "tinderbox: tree: $BuildTree\n";
print LOG "tinderbox: builddate: $StartTime\n";
print LOG "tinderbox: status: building\n";
print LOG "tinderbox: build: $BuildName $fe\n";
print LOG "tinderbox: errorparser: unix\n";
print LOG "tinderbox: buildfamily: unix\n";
print LOG "tinderbox: END\n";
print LOG "\n";
}
close( LOG );
}
# check for the existence of the binary
sub BinaryExists {
my($fe) = @_;
my($Binname);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/'. $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG $BinName . "\n";
if ((-e $BinName) && (-x $BinName) && (-s $BinName)) {
1;
}
else {
0;
}
}
sub DeleteBinary {
my($fe) = @_;
my($BinName);
$fe = 'x' if (!defined($fe));
if ($BuildClassic) {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . "/cmd/${fe}fe/" . $BinaryName{"$fe"};
} else {
$BinName = $BuildDir . '/' . $TopLevel . '/' . $Topsrcdir . '/' . $BuildObjName . $BinaryName{"$fe"};
}
print LOG "unlinking $BinName\n";
unlink ($BinName) || print LOG "unlinking $BinName failed\n";
}
sub ParseArgs {
my($i, $manArg);
if( @ARGV == 0 ) {
&PrintUsage;
}
$i = 0;
$manArg = 0;
while( $i < @ARGV ) {
if ($ARGV[$i] eq '--depend') {
$BuildDepend = 1;
$manArg++;
}
elsif ($ARGV[$i] eq '--clobber') {
$BuildDepend = 0;
$manArg++;
}
elsif ( $ARGV[$i] eq '--once' ) {
$BuildOnce = 1;
#$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--classic') {
$BuildClassic = 1;
}
elsif ($ARGV[$i] eq '--noreport') {
$ReportStatus = 0;
}
elsif ($ARGV[$i] eq '--version' || $ARGV[$i] eq '-v') {
die "$0: version $Version\n";
}
elsif ( $ARGV[$i] eq '-tag' ) {
$i++;
$BuildTag = $ARGV[$i];
if ( $BuildTag eq '' || $BuildTag eq '-t') {
&PrintUsage;
}
}
elsif ( $ARGV[$i] eq '-t' ) {
$i++;
$BuildTree = $ARGV[$i];
if ( $BuildTree eq '' ) {
&PrintUsage;
}
} else {
&PrintUsage;
}
$i++;
} #EndWhile
if ( $BuildTree =~ /^\s+$/i ) {
&PrintUsage;
}
if ($BuildDepend eq undef) {
&PrintUsage;
}
&PrintUsage if (! $manArg );
} #EndSub-ParseArgs
sub PrintUsage {
die "usage: $0 [--depend | --clobber] [-v | --version ] [--once --classic --noreport -tag TREETAG -t TREENAME ]\n";
}
sub PrintEnv {
my ($key);
foreach $key (keys %ENV) {
print LOG "$key = $ENV{$key}\n";
print "$key = $ENV{$key}\n";
}
} #EndSub-PrintEnv
# Main function
&InitVars;
&ParseArgs;
&ConditionalArgs;
&GetSystemInfo;
&SetupEnv;
&SetupPath;
&BuildIt;
1;

View File

@@ -1,416 +0,0 @@
#!c:/nstools/bin/perl5
use Cwd;
$build_depend=1; #depend or clobber
$build_tree = '';
$build_tag = '';
$build_name = '';
$build_continue = 0;
$build_sleep=10;
$no32 = 0;
$no16 = 0;
$original_path = $ENV{'PATH'};
$early_exit = 1;
$doawt11 = 0;
$do_clobber = '';
$client_param = 'pull_and_build_all';
&parse_args;
if( $build_test ){
$build_sleep=1;
}
$dirname = ($build_depend?'dep':'clob');
$logfile = "${dirname}.log";
if( $build_depend ){
$clobber_str = 'depend';
}
else {
$clobber_str = 'clobber_all';
}
mkdir("$dirname", 0777);
chdir("$dirname") || die "couldn't cd to $dirname";
$start_dir = cwd;
$last_time = 0;
print "starting dir is :$start_dir\n";
while( $early_exit ){
chdir("$start_dir");
if( time - $last_time < (60 * $build_sleep) ){
$sleep_time = (60 * $build_sleep) - (time - $last_time);
print "\n\nSleeping $sleep_time seconds ...\n";
sleep( $sleep_time );
}
$last_time = time;
$start_time = time-60*10;
$start_time_str = &cvs_time( $start_time );
# call setup_env here in the loop, to update MOZ_DATE with each pass.
# setup_env uses start_time_str for MOZ_DATE.
&setup_env;
$cur_dir = cwd;
if( $cur_dir ne $start_dir ){
print "startdir: $start_dir, curdir $cur_dir\n";
die "curdir != startdir";
}
# build 32-bit with AWT_11=1
&setup32("1");
if( !$no32 ){
if( !$noawt11 ){
&do_build(1,$do_clobber);
}
}
if ($build_test) {
$early_exit = 0; # stops this while loop after one pass.
}
if( !$no16 ){
# build 32-bit with AWT_11=0
# necessary before building 16-bit because 16-bit cannot use AWT 1.1 classes
&setup32("0");
if( !$no32 ){
if( !$noawt11 ){
&do_build(0,'');
} else {
&do_build(1,$do_clobber);
}
}
&setup16;
# strip_conf fails to strip any variables from the real environemnt
# &strip_config;
&do_build(0,'');
# &restore_config;
}
}
sub copy_win16_dist {
system 'xcopy w:\ y:\ns\dist /S /E /F';
print "COPYCOPYCOPY\n";
}
sub build_NSPR20_Win16 {
&start_build;
unlink( "${logname}.last" );
rename( "${logname}","${logname}.last");
print "opening ${logname}\n";
open( LOG, ">${logname}" ) || print "can't open $?\n";
print LOG "current dir is :$cur_dir\n";
&print_env;
chdir("$moz_src/ns/nspr20") || die "couldn't chdir to '$moz_src/ns/nspr20'";
print LOG "gmake |\n";
open( BUILDNSPR, "gmake 2>&1 |") || print "couldn't execute gmake\n";;
while( <BUILDNSPR> ) {
print $_;
print LOG $_;
}
close ( BUILDNSPR );
close( LOG );
}
sub setup32 {
local ($awt) = @_;
$ENV{"MOZ_BITS"} = '32';
$ENV{"AWT_11"} = $awt;
$doawt11 = $awt;
$ENV{"INCLUDE"} = "$msdev\\include;$msdev\\mfc\\include";
$ENV{"LIB"} = "$msdev\\lib;$msdev\\mfc\\lib";
$ENV{"PATH"} = $original_path . ";$msdev\\bin";
$ENV{"OS_TARGET"} = 'WIN95';
$moz_src = $ENV{'MOZ_SRC'} = $start_dir;
$build_name = 'Win32 ' . ($build_depend?'Depend':'Clobber');
$do_clobber = $clobber_str;
$logname = "win32.log";
}
sub setup16 {
$moz_src = $ENV{'MOZ_SRC'} = $start_dir;
$ENV{"MOZ_BITS"} = '16';
# perl 5 is fucked up. you MUST set AWT_11=0. deleting the environment
# variable doesn't work. it's removed from the environment entry, but
# is still defined as true for a build.
$ENV{"AWT_11"} = '0';
$moz_src = $ENV{'MOZ_SRC'} = "$src_16_drive";
$ENV{"OS_TARGET"} = 'WIN16';
$msvc_inc = "$moz_src\\ns\\msvc15\\include;$moz_src\\ns\\msvc15\\mfc\\include";
$msvc_lib = "$msvc\\lib;$msvc\\mfc\\lib";
$msvcpath = "$msvc\\bin;c:\\nstools\\bin;c:\\WINNT40;c:\\WINNT40\\system32;c:\\utils";
$ENV{"MSVC_INC"} = $msvc_inc;
$ENV{"MSVC_LIB"} = $msvc_lib;
$ENV{"MSVCPATH"} = $msvcpath;
$ENV{"INCLUDE"} = $msvc_inc;
$ENV{"LIB"} = $msvc_lib;
$ENV{"PATH"} = $msvcpath;
$watcom = $ENV{"WATCOM"} = "C:\\WATCOM";
$ENV{"EDPATH"} = "$watcom\\EDDAT";
$ENV{"WATC_INC"} = "$watcom\\h;$watcom\\h\win;$msvc_inc";
$ENV{"WATC_LIB"} = $msvc_lib;
$ENV{"WATCPATH"} = "$watcom\\BINNT;$watcom\\BINW;c:\\nstools\\bin";
$build_name = 'Win16 ' . ($build_depend?'Depend':'Clobber');
$do_clobber = $clobber_str;
$logname = "win16.log";
system "subst l: /d";
system "subst r: /d";
system "subst $src_16_drive /d";
system "subst $src_16_drive $start_dir";
system "subst r: $src_16_drive\\ns\\netsite\\ldap\\libraries\\msdos\\winsock";
system "subst l: $src_16_drive\\ns\\netsite";
}
sub do_build {
local ($pull, $do_clobber) = @_;
&start_build;
print "opening ${logname}\n";
open( LOG, ">${logname}" ) || print "can't open $?\n";
print LOG "current dir is :$cur_dir\n";
&print_env;
$build_status = 0;
if( $pull ){
if ( $build_tag eq '' ){
print LOG "cvs co -D\"$start_time_str\" mozilla/client.mak 2>&1 |\n";
open( PULL, "cvs co -D\"$start_time_str\" mozilla/client.mak 2>&1 |") || print "couldn't execute cvs\n";;
} else{
print LOG "cvs co -r $build_tag mozilla/client.mak 2>&1 |\n";
open( PULL, "cvs co -r $build_tag mozilla/client.mak 2>&1 |") || print "couldn't execute cvs\n";;
}
# tee the output
while( <PULL> ){
print $_;
print LOG $_;
}
close( PULL );
$build_status = $?;
}
chdir("$moz_src/mozilla") || die "couldn't chdir to '$moz_src/mozilla'";
if( $do_clobber ne '' ){
print LOG "nmake -f client.mak $do_clobber |\n";
print "nmake -f client.mak $do_clobber |\n";
open( PULL, "nmake -f client.mak $do_clobber 2>&1 |") || print "couldn't execute nmake\n";;
# tee the output
while( <PULL> ){
print $_;
print LOG $_;
}
close( PULL );
}
if (!$pull) {
$client_param = 'build_all';
}
else {
$client_param = 'pull_and_build_all';
}
if (!$doawt11) {
$client_param = 'build_dist';
}
print LOG "nmake -f client.mak $client_param 2>&1 |\n";
open( BUILD, "nmake -f client.mak $client_param 2>&1 |");
# tee the output
while( <BUILD> ){
print $_;
print LOG $_;
}
close( BUILD );
$build_status |= $?;
$build_status_str = ( $build_status ? 'busted' : 'success' );
print LOG "tinderbox: tree: $build_tree\n";
print LOG "tinderbox: builddate: $start_time\n";
print LOG "tinderbox: status: $build_status_str\n";
print LOG "tinderbox: build: $build_name\n";
print LOG "tinderbox: errorparser: windows\n";
print LOG "tinderbox: buildfamily: windows\n";
close( LOG );
chdir("$start_dir");
system( "$nstools\\bin\\blat ${logname} -t tinderbox-daemon\@warp" );
}
sub cvs_time {
local( $ret_time );
($sec,$minute,$hour,$mday,$mon,$year) = localtime( $_[0] );
$mon++; # month is 0 based.
sprintf("%02d/%02d/%02d %02d:%02d:00",
$mon,$mday,$year,$hour,$minute );
}
sub start_build {
open( LOG, ">>logfile" );
print LOG "\n";
print LOG "tinderbox: tree: $build_tree\n";
print LOG "tinderbox: builddate: $start_time\n";
print LOG "tinderbox: status: building\n";
print LOG "tinderbox: build: $build_name\n";
print LOG "tinderbox: errorparser: windows\n";
print LOG "tinderbox: buildfamily: windows\n";
print LOG "\n";
close( LOG );
system("$nstools\\bin\\blat logfile -t tinderbox-daemon\@warp" );
}
sub parse_args {
local($i);
if( @ARGV == 0 ){
&usage;
}
$i = 0;
while( $i < @ARGV ){
if( $ARGV[$i] eq '--depend' ){
$build_depend = 1;
}
elsif ( $ARGV[$i] eq '--clobber' ){
$build_depend = 0;
}
elsif ( $ARGV[$i] eq '--continue' ){
$build_continue = 1;
}
elsif ( $ARGV[$i] eq '--noawt11' ){
$noawt11 = 1;
}
elsif ( $ARGV[$i] eq '--no32' ){
$no32 = 1;
}
elsif ( $ARGV[$i] eq '--no16' ){
$no16 = 1;
}
elsif ( $ARGV[$i] eq '--test' ){
$build_test = 1;
}
elsif ( $ARGV[$i] eq '-tag' ){
$i++;
$build_tag = $ARGV[$i];
if( $build_tag eq '' || $build_tag eq '-t'){
&usage;
}
}
elsif ( $ARGV[$i] eq '-t' ){
$i++;
$build_tree = $ARGV[$i];
if( $build_tree eq '' ){
&usage;
}
}
$i++;
}
if( $build_tree eq '' ){
&usage;
}
}
sub usage {
die "usage: buildit.pl [--depend | --clobber] [--no16] [--continue] [--test] [-tag TAGNAME] -t TREENAME\n";
}
sub setup_env {
local($p);
$ENV{"MOZ_DEBUG"} = '1';
$ENV{"MOZ_GOLD"} = '1';
$ENV{"NO_SECURITY"} = '1';
$ENV{"MOZ_MEDIUM"} = '1';
$ENV{"MOZ_CAFE"} = '1';
$ENV{"NSPR20"} = '1';
$ENV{"VERBOSE"} = '1';
$nstools = $ENV{"MOZ_TOOLS"};
if( $nstools eq '' ){
die "error: environment variable MOZ_TOOLS not set\n";
}
$msdev = $ENV{"MOZ_MSDEV"} = 'c:\msdev';
if( $msdev eq '' ){
die "error: environment variable MOZ_MSDEV not set\n";
}
$msvc = $ENV{"MOZ_MSVC"} = 'c:\msvc';
if( $msvc eq '' ){
die "error: environment variable MOZ_VC not set\n";
}
if ( $build_tag ne '' ) {
$ENV{"MOZ_BRANCH"} = $build_tag;
}
$moz_src = $ENV{"MOZ_SRC"} = $start_dir;
$ENV{"MOZ_DATE"} = $start_time_str;
$src_16_drive = 'y:';
}
sub print_env {
local( $k, $v);
print LOG "\nEnvironment\n";
print "\nEnvironment\n";
for $k (sort keys %ENV){
$v = $ENV{$k};
print LOG " $k=$v\n";
print " $k=$v\n";
}
print LOG "\n";
print "\n";
system 'set';
}
sub strip_config {
$save_compname=$ENV{"COMPUTERNAME"};
$save_userdomain=$ENV{"USERDOMAIN"};
$save_username=$ENV{"USERNAME"};
$save_userprofile=$ENV{"USERPROFILE"};
# most of these deletes have no effect.
delete($ENV{"COMPUTERNAME"});
delete($ENV{"USERDOMAIN"});
delete($ENV{"USERNAME"});
delete($ENV{"USERPROFILE"});
# delete($ENV{"WATCOM"});
}
sub restore_config {
$ENV{"COMPUTERNAME"}=$save_compname;
$ENV{"USERDOMAIN"}=$save_userdomain;
$ENV{"USERNAME"}=$save_username;
$ENV{"MSDevDir"}=$save_userprofile;
&print_env;
}

View File

@@ -1,31 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="GENERATOR" CONTENT="Mozilla/4.0b2 (WinNT; I) [Netscape]">
</HEAD>
<BODY>
<H1>
FAQ on Tinderbox&nbsp;</H1>
<B><FONT SIZE=+2>Q. What is Tinderbox.</FONT></B>
<BR><FONT SIZE=+2>A. Your very own automated build page.&nbsp; It shows you how builds
are going on various platforms. fs</FONT>
<BR><FONT SIZE=+2></FONT>
<BR><B><FONT SIZE=+2>Q. I just checked in some code.&nbsp; How can I tell when
I'm OK.</FONT></B>
<BR><FONT SIZE=+2>A. You name will appear in the <I>guilty </I>column.&nbsp;
When there are successful (<FONT COLOR="#00FF00">green</FONT>) builds in
all the columns in a row <B>above</B> your name, you know you are ok.</FONT>
<BR>
<BR><B><FONT SIZE=+2>Q. The tree is broken, how do I find out what is busted
(or who busted it).</FONT></B>
<BR><FONT SIZE=+2>A. Clicking 'L' in the first red box (first build to break)
above a green will show you a build log for the broken build.&nbsp; You
can also click 'C' in this box and see what code was checked in.</FONT>
<BR>
<BR><B><FONT SIZE=+2>More Questions? Mail me <A HREF="mailto:ltabb@netscape.com">ltabb@netscape.com</A></FONT></B>
<BR>
</BODY>
</HTML>

View File

@@ -1,217 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use Socket;
require 'globals.pl';
require 'imagelog.pl';
# Port an old-style imagelog thing to a newstyle one
open( IMAGELOG, "<$data_dir/imagelog.txt" ) || die "can't open file";
open (OUT, ">$data_dir/newimagelog.txt") || die "can't open output file";
select(OUT); $| = 1; select(STDOUT);
while( <IMAGELOG> ){
chop;
($url,$quote) = split(/\`/);
print "$url\n";
$size = &URLsize($url);
$width = "";
$height = "";
if ($size =~ /WIDTH=([0-9]*)/) {
$width = $1;
}
if ($size =~ /HEIGHT=([0-9]*)/) {
$height = $1;
}
if ($width eq "" || $height eq "") {
print "Couldn't get image size; skipping.\n";
} else {
print OUT "$url`$width`$height`$quote\n";
}
}
sub imgsize {
local($file)= @_;
#first try to open the file
if( !open(STREAM, "<$file") ){
print "Can't open IMG $file";
$size="";
} else {
if ($file =~ /.jpg/i || $file =~ /.jpeg/i) {
$size = &jpegsize(STREAM);
} elsif($file =~ /.gif/i) {
$size = &gifsize(STREAM);
} elsif($file =~ /.xbm/i) {
$size = &xbmsize(STREAM);
} else {
return "";
}
$_ = $size;
if( /\s*width\s*=\s*([0-9]*)\s*/i ){
($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
}
if( /\s*height\s*=\s*([0-9]*)\s*/i ){
($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
}
close(STREAM);
}
return $size;
}
###########################################################################
# Subroutine gets the size of the specified GIF
###########################################################################
sub gifsize {
local($GIF) = @_;
read($GIF, $type, 6);
if(!($type =~ /GIF8[7,9]a/) ||
!(read($GIF, $s, 4) == 4) ){
print "Invalid or Corrupted GIF";
$size="";
} else {
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join ("", 'WIDTH=', $b<<8|$a, ' HEIGHT=', $d<<8|$c);
}
return $size;
}
sub xbmsize {
local($XBM) = @_;
local($input)="";
$input .= <$XBM>;
$input .= <$XBM>;
$_ = $input;
if( /#define\s*\S*\s*\d*\s*\n#define\s*\S*\s*\d*\s*\n/i ){
($a,$b)=/#define\s*\S*\s*(\d*)\s*\n#define\s*\S*\s*(\d*)\s*\n/i;
$size=join ("", 'WIDTH=', $a, ' HEIGHT=', $b );
} else {
print "Hmmm... Doesn't look like an XBM file";
}
return $size;
}
# jpegsize : gets the width and height (in pixels) of a jpeg file
# Andrew Tong, werdna@ugcs.caltech.edu February 14, 1995
# modified slightly by alex@ed.ac.uk
sub jpegsize {
local($JPEG) = @_;
local($done)=0;
$size="";
read($JPEG, $c1, 1); read($JPEG, $c2, 1);
if( !((ord($c1) == 0xFF) && (ord($c2) == 0xD8))){
printf "This is not a JPEG! (Codes %02X %02X)\n", ord($c1), ord($c2);
$done=1;
}
while (ord($ch) != 0xDA && !$done) {
# Find next marker (JPEG markers begin with 0xFF)
# This can hang the program!!
while (ord($ch) != 0xFF) { read($JPEG, $ch, 1); }
# JPEG markers can be padded with unlimited 0xFF's
while (ord($ch) == 0xFF) { read($JPEG, $ch, 1); }
# Now, $ch contains the value of the marker.
$marker=ord($ch);
if (($marker >= 0xC0) && ($marker <= 0xCF) &&
($marker != 0xC4) && ($marker != 0xCC)) { # it's a SOFn marker
read ($JPEG, $junk, 3); read($JPEG, $s, 4);
($a,$b,$c,$d)=unpack("C"x4,$s);
$size=join("", 'HEIGHT=',$a<<8|$b,' WIDTH=',$c<<8|$d );
$done=1;
} else {
# We **MUST** skip variables, since FF's within variable
# names are NOT valid JPEG markers
read ($JPEG, $s, 2);
($c1, $c2) = unpack("C"x2,$s);
$length = $c1<<8|$c2;
if( ($length < 2) ){
print "Erroneous JPEG marker length";
$done=1;
} else {
read($JPEG, $junk, $length-2);
}
}
}
return $size;
}
###########################################################################
# Subroutine grabs a gif from another server and gets its size
###########################################################################
sub URLsize {
my ($fullurl) = @_;
my($dummy, $dummy, $serverstring, $url) = split(/\//, $fullurl, 4);
my($them,$port) = split(/:/, $serverstring);
my $port = 80 unless $port;
$them = 'localhost' unless $them;
my $size="";
$_=$url;
if( /gif/i || /jpeg/i || /jpg/i || /xbm/i ) {
my ($remote, $iaddr, $paddr, $proto, $line);
$remote = $them;
if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') }
die "No port" unless $port;
$iaddr = inet_aton($remote) || die "no host: $remote";
$paddr = sockaddr_in($port, $iaddr);
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto) || return "socket: $!";
connect(S, $paddr) || return "connect: $!";
select(S); $| = 1; select(STDOUT);
print S "GET /$url\n";
if ($url =~ /.jpg/i || $url =~ /.jpeg/i) {
$size = &jpegsize(S);
} elsif($url =~ /.gif/i) {
$size = &gifsize(S);
} elsif($url =~ /.xbm/i) {
$size = &xbmsize(S);
} else {
return "";
}
$_ = $size;
if( /\s*width\s*=\s*([0-9]*)\s*/i ){
($newwidth)= /\s*width\s*=\s*(\d*)\s*/i;
}
if( /\s*height\s*=\s*([0-9]*)\s*/i ){
($newheight)=/\s*height\s*=\s*(\d*)\s*/i;
}
} else {
$size="";
}
return $size;
}
sub dokill {
kill 9,$child if $child;
}

View File

@@ -1,556 +0,0 @@
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
#
# Global variabls and functions for tinderbox
#
#
# Global variables
#
$td1 = {};
$td2 = {};
$build_list = []; # array of all build records
$build_name_index = {};
$ignore_builds = {};
$build_name_names = [];
$name_count = 0;
$build_time_index = {};
$build_time_times = [];
$time_count = 0;
$mindate_time_count = 0; # time_count that corresponds to the mindate
$build_table = [];
$who_list = [];
$who_list2 = [];
@note_array = ();
#$body_tag = "<BODY TEXT=#000000 BGCOLOR=#8080C0 LINK=#FFFFFF VLINK=#800080 ALINK=#FFFF00>";
#$body_tag = "<BODY TEXT=#000000 BGCOLOR=#FFFFC0 LINK=#0000FF VLINK=#800080 ALINK=#FF00FF>";
if( $ENV{'USERNAME'} eq 'ltabb' ){
$gzip = 'gzip';
}
else {
$gzip = '/usr/local/bin/gzip';
}
$data_dir='data';
$lock_count = 0;
1;
sub lock{
#if( $lock_count == 0 ){
# print "locking $tree/LOCKFILE.lck\n";
# open( LOCKFILE_LOCK, ">$tree/LOCKFILE.lck" );
# flock( LOCKFILE_LOCK, 2 );
#}
#$lock_count++;
}
sub unlock{
#$lock_count--;
#if( $lock_count == 0 ){
# flock( LOCKFILE_LOCK, 8 );
# close( LOCKFILE_LOCK );
#}
}
sub print_time {
my ($t) = @_;
my ($minute,$hour,$mday,$mon);
(undef,$minute,$hour,$mday,$mon,undef) = localtime($t);
sprintf("%02d/%02d&nbsp;%02d:%02d",$mon+1,$mday,$hour,$minute);
}
sub url_encode {
my ($s) = @_;
$s =~ s/\%/\%25/g;
$s =~ s/\=/\%3d/g;
$s =~ s/\?/\%3f/g;
$s =~ s/ /\%20/g;
$s =~ s/\n/\%0a/g;
$s =~ s/\r//g;
$s =~ s/\"/\%22/g;
$s =~ s/\'/\%27/g;
$s =~ s/\|/\%7c/g;
$s =~ s/\&/\%26/g;
return $s;
}
sub url_decode {
my ($value) = @_;
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
return $value;
}
sub value_encode {
my ($s) = @_;
$s =~ s@&@&amp;@g;
$s =~ s@<@&lt;@g;
$s =~ s@>@&gt;@g;
$s =~ s@\"@&quot;@g;
return $s;
}
sub load_data {
$tree2 = $form{'tree2'};
if( $tree2 ne '' ){
require "$tree2/treedata.pl";
if( -r "$tree2/ignorebuilds.pl" ){
require "$tree2/ignorebuilds.pl";
}
$td2 = {};
$td2->{name} = $tree2;
$td2->{cvs_module} = $cvs_module;
$td2->{cvs_branch} = $cvs_branch;
$td2->{num} = 1;
$td2->{ignore_builds} = $ignore_builds;
if( $cvs_root eq '' ){
$cvs_root = '/m/src';
}
$td2->{cvs_root} = $cvs_root;
$tree = $form{'tree'};
require "$tree/treedata.pl";
if( $cvs_root eq '' ){
$cvs_root = '/m/src';
}
}
$tree = $form{'tree'};
return unless $tree;
#die "the 'tree' parameter must be provided\n" unless $tree;
if ( -r "$tree/treedata.pl" ) {
require "$tree/treedata.pl";
}
$ignore_builds = {};
if( -r "$tree/ignorebuilds.pl" ){
require "$tree/ignorebuilds.pl";
}
$td1 = {};
$td1->{name} = $tree;
$td1->{num} = 0;
$td1->{cvs_module} = $cvs_module;
$td1->{cvs_branch} = $cvs_branch;
$td1->{ignore_builds} = $ignore_builds;
if( $cvs_root eq '' ){
$cvs_root = '/m/src';
}
$td1->{cvs_root} = $cvs_root;
&lock;
&load_buildlog;
&unlock;
&get_build_name_index;
&get_build_time_index;
&load_who($who_list, $td1);
if( $tree2 ne '' ){
&load_who($who_list2, $td2);
}
&make_build_table;
}
sub load_buildlog {
my $mailtime, $buildtime, $buildname, $errorparser;
my $buildstatus, $logfile,$binaryname;
my $buildrec, @treelist, $t;
if (not defined $maxdate) {
$maxdate = time();
}
if (not defined $mindate) {
$mindate = $maxdate - 24*60*60;
}
if ($tree2 ne '') {
@treelist = ($td1, $td2);
}
else {
@treelist = ($td1);
}
for $t (@treelist) {
use Backwards;
my ($bw) = Backwards->new("$t->{name}/build.dat") or die;
my $tooearly = 0;
while( $_ = $bw->readline ) {
chomp;
($mailtime, $buildtime, $buildname,
$errorparser, $buildstatus, $logfile, $binaryname) = split /\|/;
# Ignore stuff in the future.
next if $buildtime > $maxdate;
# Ignore stuff in the past (but get a 2 hours of extra data)
if ($buildtime < $mindate - 2*60*60) {
# Occasionally, a build might show up with a bogus time. So,
# we won't judge ourselves as having hit the end until we
# hit a full 20 lines in a row that are too early.
last if $tooearly++ > 20;
next;
}
$tooearly = 0;
$buildrec = {
mailtime => $mailtime,
buildtime => $buildtime,
buildname => ($tree2 ne '' ? $t->{name} . ' ' : '' ) . $buildname,
errorparser => $errorparser,
buildstatus => $buildstatus,
logfile => $logfile,
binaryname => $binaryname,
td => $t
};
if ($form{noignore} or not $t->{ignore_builds}->{$buildname}) {
push @{$build_list}, $buildrec;
}
}
}
}
sub loadquickparseinfo {
my ($tree, $build, $times) = (@_);
do "$tree/ignorebuilds.pl";
use Backwards;
my ($bw) = Backwards->new("$form{tree}/build.dat") or die;
my $latest_time = 0;
my $tooearly = 0;
while( $_ = $bw->readline ) {
chop;
my ($buildtime, $buildname, $buildstatus) = (split /\|/)[1,2,4];
if ($buildstatus =~ /^success|busted|testfailed$/) {
# Ignore stuff in the future.
next if $buildtime > $maxdate;
$latest_time = $buildtime if $buildtime > $latest_time;
# Ignore stuff more than 12 hours old
if ($buildtime < $latest_time - 12*60*60) {
# Hack: A build may give a bogus time. To compensate, we will
# not stop until we hit 20 consecutive lines that are too early.
last if $tooearly++ > 20;
next;
}
$tooearly = 0;
next if exists $ignore_builds->{$buildname};
next if exists $build->{$buildname}
and $times->{$buildname} >= $buildtime;
$build->{$buildname} = $buildstatus;
$times->{$buildname} = $buildtime;
}
}
}
sub load_who {
my ($who_list, $td) = @_;
my $d, $w, $i, $bfound;
open(WHOLOG, "<$td->{name}/who.dat");
while (<WHOLOG>) {
$i = $time_count;
chop;
($d,$w) = split /\|/;
$bfound = 0;
while ($i > 0 and not $bfound) {
if ($d <= $build_time_times->[$i]) {
$who_list->[$i+1]->{$w} = 1;
$bfound = 1;
}
else {
$i--;
}
}
}
# Ignore the last one
#
if ($time_count > 0) {
$who_list->[$time_count] = {};
}
}
sub get_build_name_index {
my $i,$br;
# Get all the unique build names.
#
foreach $br (@{$build_list}) {
$build_name_index->{$br->{buildname}} = 1;
}
$i = 1;
foreach $n (sort keys (%{$build_name_index})) {
$build_name_names->[$i] = $n;
$i++;
}
$name_count = @{$build_name_names}-1;
# Update the map so it points to the right index
#
for ($i=1; $i < $name_count+1; $i++) {
$build_name_index->{$build_name_names->[$i]} = $i;
}
}
sub get_build_time_index {
my $i,$br;
# Get all the unique build names.
#
foreach $br (@{$build_list}) {
$build_time_index->{$br->{buildtime}} = 1;
}
$i = 1;
foreach $n (sort {$b <=> $a} keys (%{$build_time_index})) {
$build_time_times->[$i] = $n;
$mindate_time_count = $i if $n >= $mindate;
$i++;
}
$time_count = @{$build_time_times}-1;
# Update the map so it points to the right index
#
for ($i=1; $i < $time_count+1; $i++) {
$build_time_index->{$build_time_times->[$i]} = $i;
}
#for $i (@{$build_time_times}) {
# print $i . "\n";
#}
#while( ($k,$v) = each(%{$build_time_index})) {
# print "$k=$v\n";
#}
}
sub make_build_table {
my $i,$ti,$bi,$ti1,$br;
# Create the build table
#
for ($i=1; $i <= $time_count; $i++){
$build_table->[$i] = [];
}
# Populate the build table with build data
#
foreach $br (reverse @{$build_list}) {
$ti = $build_time_index->{$br->{buildtime}};
$bi = $build_name_index->{$br->{buildname}};
$build_table->[$ti][$bi] = $br;
}
&load_notes;
for ($bi = $name_count; $bi > 0; $bi--) {
for ($ti = $time_count; $ti > 0; $ti--) {
if (defined($br = $build_table->[$ti][$bi])
and not defined($br->{rowspan})) {
# If the cell immediately after us is defined, then we
# can have a previousbuildtime.
if (defined($br1 = $build_table->[$ti+1][$bi])) {
$br->{previousbuildtime} = $br1->{buildtime};
}
$ti1 = $ti-1;
while ($ti1 > 0 and not defined($build_table->[$ti1][$bi])) {
$build_table->[$ti1][$bi] = -1;
$ti1--;
}
$br->{rowspan} = $ti - $ti1;
if ($br->{rowspan} != 1) {
$build_table->[$ti1+1][$bi] = $br;
$build_table->[$ti][$bi] = -1;
}
}
}
}
}
sub load_notes {
if ($tree2 ne '') {
@treelist = ($td1, $td2);
}
else {
@treelist = ($td1);
}
foreach $t (@treelist) {
open(NOTES,"<$t->{name}/notes.txt")
or print "<h2>warning: Couldn't open $t->{name}/notes.txt </h2>\n";
while (<NOTES>) {
chop;
($nbuildtime,$nbuildname,$nwho,$nnow,$nenc_note) = split /\|/;
$nbuildname = "$t->{name} $nbuildname" if $tree2 ne '';
$ti = $build_time_index->{$nbuildtime};
$bi = $build_name_index->{$nbuildname};
#print "[ti = $ti][bi=$bi][buildname='$nbuildname' $_<br>";
if ($ti != 0 and $bi != 0) {
$build_table->[$ti][$bi]->{hasnote} = 1;
if (not defined($build_table->[$ti][$bi]->{noteid})) {
$build_table->[$ti][$bi]->{noteid} = (0+@note_array);
}
$noteid = $build_table->[$ti][$bi]->{noteid};
$now_str = &print_time($nnow);
$note = &url_decode($nenc_note);
$note_array[$noteid] = "<pre>\n[<b><a href=mailto:$nwho>"
."$nwho</a> - $now_str</b>]\n$note\n</pre>"
.$note_array[$noteid];
}
}
close(NOTES);
}
}
sub last_success_time {
my ($row) = @_;
for (my $tt=1; $tt <= $time_count; $tt++) {
my $br = $build_table->[$tt][$row];
next unless defined $br;
next unless $br->{buildstatus} eq 'success';
return $build_time_times->[$tt + $br->{rowspan} ];
}
return 0;
}
sub last_status {
my ($row) = @_;
for (my $tt=1; $tt <= $time_count; $tt++) {
my $br = $build_table->[$tt][$row];
next unless defined $br;
next unless $br->{buildstatus} =~ /^(success|busted|testfailed)$/;
return $br->{buildstatus};
}
return 'building';
}
sub check_password {
if ($form{password} eq '') {
if (defined $cookie_jar{tinderbox_password}) {
$form{password} = $cookie_jar{tinderbox_password};
}
}
my $correct = '';
if (open(REAL, '<data/passwd')) {
$correct = <REAL>;
close REAL;
$correct =~ s/\s+$//; # Strip trailing whitespace.
}
$form{password} =~ s/\s+$//; # Strip trailing whitespace.
if ($form{password} ne '') {
open(TRAPDOOR, "../bonsai/data/trapdoor $form{'password'} |")
or die "Can't run trapdoor func!";
my $encoded = <TRAPDOOR>;
close TRAPDOOR;
$encoded =~ s/\s+$//; # Strip trailing whitespace.
if ($encoded eq $correct) {
if ($form{rememberpassword} ne '') {
print "Set-Cookie: tinderbox_password=$form{'password'} ;"
." path=/ ; expires = Sun, 1-Mar-2020 00:00:00 GMT\n";
}
return;
}
}
require 'header.pl';
print "Content-type: text/html\n";
print "Set-Cookie: tinderbox_password= ; path=/ ; "
." expires = Sun, 1-Mar-2020 00:00:00 GMT\n";
print "\n";
EmitHtmlHeader("What's the magic word?",
"You need to know the magic word to use this page.");
if ($form{password} ne '') {
print "<B>Invalid password; try again.<BR></B>";
}
print q(
<FORM method=post>
<B>Password:</B>
<INPUT NAME=password TYPE=password><BR>
<INPUT NAME=rememberpassword TYPE=checkbox>
If correct, remember password as a cookie<BR>
);
while (my ($key,$value) = each %form) {
next if $key eq "password" or $key eq "rememberpassword";
my $enc = value_encode($value);
print "<INPUT TYPE=HIDDEN NAME=$key VALUE='$enc'>\n";
}
print "<INPUT TYPE=SUBMIT value=Submit></FORM>\n";
exit;
}
sub find_build_record {
my ($tree, $logfile) = @_;
my $log_entry = `grep $logfile $tree/build.dat`;
chomp($log_entry);
my ($mailtime, $buildtime, $buildname, $errorparser,
$buildstatus, $logfile, $binaryname) = split /\|/, $log_entry;
$buildrec = {
mailtime => $mailtime,
buildtime => $buildtime,
buildname => $buildname,
errorparser => $errorparser,
buildstatus => $buildstatus,
logfile => $logfile,
binaryname => $binaryname,
td => undef
};
return $buildrec;
}

View File

@@ -1,49 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
# Figure out which directory tinderbox is in by looking at argv[0]. Unless
# there is a command line argument; if there is, just use that.
$tinderboxdir = $0;
$tinderboxdir =~ s:/[^/]*$::; # Remove last word, and slash before it.
if ($tinderboxdir eq "") {
$tinderboxdir = ".";
}
if (@ARGV > 0) {
$tinderboxdir = $ARGV[0];
}
print "tinderbox = $tinderboxdir\n";
chdir $tinderboxdir || die "Couldn't chdir to $tinderboxdir";
open(DF, ">data/tbx.$$") || die "could not open data/tbx.$$";
while(<STDIN>){
print DF $_;
}
close(DF);
$err = system("./processbuild.pl data/tbx.$$");
if( $err ) {
die "processbuild.pl returned an error\n";
}

View File

@@ -1,43 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
1;
sub add_imagelog {
local($url,$quote,$width,$height) = @_;
open( IMAGELOG, ">>$data_dir/imagelog.txt" ) || die "Oops; can't open imagelog.txt";
print IMAGELOG "$url`$width`$height`$quote\n";
close( IMAGELOG );
}
sub get_image{
local(@log,@ret,$i);
open( IMAGELOG, "<$data_dir/imagelog.txt" );
@log = <IMAGELOG>;
# return a random line
srand;
@ret = split(/\`/,$log[rand @log]);
close( IMAGELOG );
@ret;
}

View File

@@ -1,12 +0,0 @@
<TITLE>tinderbox</TITLE>
<META HTTP-EQUIV="Refresh" CONTENT="1; URL=showbuilds.cgi">
<BODY BGCOLOR="#FFFFFF" TEXT="#000000"
LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
<CENTER>
<TABLE BORDER=0 WIDTH="100%" HEIGHT="100%"><TR><TD ALIGN=CENTER VALIGN=CENTER>
<FONT SIZE="+2">
You're looking for
<A HREF="showbuilds.cgi">showbuilds.cgi</A>.
</FONT>
</TD></TR></TABLE>
</CENTER>

View File

@@ -1,241 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
require 'globals.pl';
require 'timelocal.pl';
umask 0;
#$logfile = '';
%MAIL_HEADER = ();
$DONE = 0;
$building = 0;
$endsection = 0;
open( LOG, "<$ARGV[0]") || die "cant open $!";
&parse_mail_header;
while ($DONE == 0) {
%tbx = ();
&get_variables;
# run thru if EOF and we haven't hit our section end marker
if ( !$DONE || !$endsection) {
&check_required_vars;
$tree = $tbx{'tree'} if (!defined($tree));
$logfile = "$builddate.$$.gz" if (!defined($logfile));
$building++ if ($tbx{'status'} =~ m/building/);
&lock;
&write_build_data;
&unlock;
}
}
close(LOG);
&compress_log_file;
&unlink_log_file;
system "./buildwho.pl $tree";
# Build static pages for Sidebar flash and tinderbox panels.
$ENV{QUERY_STRING}="tree=$tree&static=1";
system './showbuilds.cgi';
# end of main
######################################################################
# This routine will scan through log looking for 'tinderbox:' variables
#
sub get_variables{
#while( ($k,$v) = each( %MAIL_HEADER ) ){
# print "$k='$v'\n";
#}
&parse_log_variables;
#while( ($k,$v) = each( %tbx ) ){
# print "$k='$v'\n";
#}
}
sub parse_log_variables {
my ($line, $stop);
$stop = 0;
while($stop == 0){
$line = <LOG>;
$DONE++, return if !defined($line);
chomp($line);
if( $line =~ /^tinderbox\:/ ){
if( $line =~ /^tinderbox\:[ \t]*([^:]*)\:[ \t]*([^\n]*)/ ){
$tbx{$1} = $2;
} elsif ( $line =~ /^tinderbox: END/ ) {
$stop++, $endsection++;
}
}
}
}
sub parse_mail_header {
my $line;
my $name = '';
while($line = <LOG> ){
chomp($line);
if( $line eq '' ){
return;
}
if( $line =~ /([^ :]*)\:[ \t]+([^\n]*)/ ){
$name = $1;
$name =~ tr/A-Z/a-z/;
$MAIL_HEADER{$name} = $2;
#print "$name $2\n";
}
elsif( $name ne '' ){
$MAIL_HEADER{$name} .= $2;
}
}
}
sub check_required_vars {
$err_string = '';
if( $tbx{'tree'} eq ''){
$err_string .= "Variable 'tinderbox:tree' not set.\n";
}
elsif( ! -r $tbx{'tree'} ){
$err_string .= "Variable 'tinderbox:tree' not set to a valid tree.\n";
}
elsif(($MAIL_HEADER{'to'} =~ /external/i ||
$MAIL_HEADER{'cc'} =~ /external/i) &&
$tbx{'tree'} !~ /external/i) {
$err_string .= "Data from an external source didn't specify an 'external' tree.";
}
if( $tbx{'build'} eq ''){
$err_string .= "Variable 'tinderbox:build' not set.\n";
}
if( $tbx{'errorparser'} eq ''){
$err_string .= "Variable 'tinderbox:errorparser' not set.\n";
}
#
# Grab the date in the form of mm/dd/yy hh:mm:ss
#
# Or a GMT unix date
#
if( $tbx{'builddate'} eq ''){
$err_string .= "Variable 'tinderbox:builddate' not set.\n";
}
else {
if( $tbx{'builddate'} =~
/([0-9]*)\/([0-9]*)\/([0-9]*)[ \t]*([0-9]*)\:([0-9]*)\:([0-9]*)/ ){
$builddate = timelocal($6,$5,$4,$2,$1-1,$3);
}
elsif( $tbx{'builddate'} > 7000000 ){
$builddate = $tbx{'builddate'};
}
else {
$err_string .= "Variable 'tinderbox:builddate' not of the form MM/DD/YY HH:MM:SS or unix date\n";
}
}
#
# Build Status
#
if( $tbx{'status'} eq ''){
$err_string .= "Variable 'tinderbox:status' not set.\n";
}
elsif( ! $tbx{'status'} =~ /success|busted|building|testfailed/ ){
$err_string .= "Variable 'tinderbox:status' must be 'success', 'busted', 'testfailed', or 'building'\n";
}
#
# Report errors
#
if( $err_string ne '' ){
die $err_string;
}
}
sub write_build_data {
$t = time;
open( BUILDDATA, ">>$tbx{'tree'}/build.dat" )|| die "can't open $! for writing";
print BUILDDATA "$t|$builddate|$tbx{'build'}|$tbx{'errorparser'}|$tbx{'status'}|$logfile|$tbx{binaryname}\n";
close( BUILDDATA );
}
sub compress_log_file {
local( $done, $line);
return if ( $building );
open( LOG2, "<$ARGV[0]") || die "cant open $!";
#
# Skip past the the RFC822.HEADER
#
$done = 0;
while( !$done && ($line = <LOG2>) ){
chomp($line);
$done = ($line eq '');
}
open( ZIPLOG, "| $gzip -c > ${tree}/$logfile" ) || die "can't open $! for writing";
$inBinary = 0;
$hasBinary = ($tbx{'binaryname'} ne '');
while( $line = <LOG2> ){
if( !$inBinary ){
print ZIPLOG $line;
if( $hasBinary ){
$inBinary = ($line =~ /^begin [0-7][0-7][0-7] /);
}
}
else {
if( $line =~ /^end\n/ ){
$inBinary = 0;
}
}
}
close( ZIPLOG );
close( LOG2 );
#
# If a uuencoded binary is part of the build, unpack it.
#
if( $hasBinary ){
$bin_dir = "$tbx{'tree'}/bin/$builddate/$tbx{'build'}";
$bin_dir =~ s/ //g;
system("mkdir -m 0777 -p $bin_dir");
# LTNOTE: I'm not sure this is cross platform.
system("/tools/ns/bin/uudecode --output-file=$bin_dir/$tbx{binaryname} < $ARGV[0]");
}
}
sub unlink_log_file {
unlink( $ARGV[0] );
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,782 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib '../bonsai';
require 'globals.pl';
require 'lloydcgi.pl';
require 'imagelog.pl';
require 'header.pl';
$|=1;
# Hack this until I can figure out how to do get default root. -slamm
$default_root = '/cvsroot';
# Show 12 hours by default
#
$nowdate = time;
if (not defined($maxdate = $form{maxdate})) {
$maxdate = $nowdate;
}
if ($form{showall}) {
$mindate = 0;
}
else {
$default_hours = 12;
$hours = $default_hours;
$hours = $form{hours} if $form{hours};
$mindate = $maxdate - ($hours*60*60);
}
%colormap = (
success => '00ff00',
busted => 'red',
building => 'yellow',
testfailed => 'orange'
);
%images = (
flames => '1afi003r.gif',
star => 'star.gif'
);
$tree = $form{tree};
if (exists $form{rebuildguilty} or exists $form{showall}) {
system ("./buildwho.pl -days 7 $tree > /dev/null");
undef $form{rebuildguilty};
}
&show_tree_selector, exit if $form{tree} eq '';
&do_quickparse, exit if $form{quickparse};
&do_express, exit if $form{express};
&do_rdf, exit if $form{rdf};
&do_static, exit if $form{static};
&do_flash, exit if $form{flash};
&do_panel, exit if $form{panel};
&do_tinderbox, exit;
# end of main
#=====================================================================
sub make_tree_list {
my @result;
while(<*>) {
if( -d $_ && $_ ne 'data' && $_ ne 'CVS' && -f "$_/treedata.pl") {
push @result, $_;
}
}
return @result;
}
sub show_tree_selector {
print "Content-type: text/html\n\n";
EmitHtmlHeader("tinderbox");
print "<P><TABLE WIDTH=\"100%\">";
print "<TR><TD ALIGN=CENTER>Select one of the following trees:</TD></TR>";
print "<TR><TD ALIGN=CENTER>\n";
print " <TABLE><TR><TD><UL>\n";
my @list = make_tree_list();
foreach (@list) {
print "<LI><a href=showbuilds.cgi?tree=$_>$_</a>\n";
}
print "<//UL></TD></TR></TABLE></TD></TR></TABLE>";
print "<P><TABLE WIDTH=\"100%\">";
print "<TR><TD ALIGN=CENTER><a href=admintree.cgi>";
print "Administer</a> one of the following trees:</TD></TR>";
print "<TR><TD ALIGN=CENTER>\n";
print " <TABLE><TR><TD><UL>\n";
foreach (@list) {
print "<LI><a href=admintree.cgi?tree=$_>$_</a>\n";
}
print "<//UL></TD></TR></TABLE></TD></TR></TABLE>";
}
sub do_static {
local *OUT;
$form{nocrap}=1;
my @pages = ( ['index.html', 'do_tinderbox'],
['flash.rdf', 'do_flash'],
['panel.html', 'do_panel'] );
$rel_path = '../';
while (($key, $value) = each %images) {
$images{$key} = "$rel_path$value";
}
my $oldfh = select;
foreach $pair (@pages) {
my ($page, $call) = @{$pair};
my $outfile = "$form{tree}/$page";
open(OUT,">$outfile.$$");
select OUT;
eval "$call";
close(OUT);
system "mv $outfile.$$ $outfile";
}
select $oldfh;
}
sub do_tinderbox {
&load_data;
&print_page_head;
&print_table_header;
&print_table_body;
&print_table_footer;
}
sub print_page_head {
print "Content-type: text/html",
($nowdate eq $maxdate ? "\nRefresh: 900" : ''),
"\n\n<HTML>\n" unless $form{static};
# Get the message of the day only on the first pageful
do "$tree/mod.pl" if $nowdate eq $maxdate;
use POSIX qw(strftime);
# Print time in format, "HH:MM timezone"
my $now = strftime("%H:%M %Z", localtime);
EmitHtmlTitleAndHeader("tinderbox: $tree", "tinderbox",
"tree: $tree ($now)");
&print_javascript;
print "$message_of_day\n";
# Quote and Lengend
#
unless ($form{nocrap}) {
my ($imageurl,$imagewidth,$imageheight,$quote) = &get_image;
print qq{
<table width="100%" cellpadding=0 cellspacing=0>
<tr>
<td valign=bottom>
<p><center><a href=addimage.cgi><img src="$rel_path$imageurl"
width=$imagewidth height=$imageheight><br>
$quote</a><br>
</center>
<p>
<td align=right valign=bottom>
<table cellspacing=0 cellpadding=1 border=0><tr><td align=center>
<TT>L</TT></td><td>= Show Build Log
</td></tr><tr><td align=center>
<img src="$images{star}"></td><td>= Show Log comments
</td></tr><tr><td colspan=2>
<table cellspacing=1 cellpadding=1 border=1>
<tr bgcolor="$colormap{success}"><td>Successful Build
<tr bgcolor="$colormap{building}"><td>Build in Progress
<tr bgcolor="$colormap{testfailed}"><td>Successful Build,
but Tests Failed
<tr bgcolor="$colormap{busted}"><td>Build Failed
</table>
</td></tr></table>
</td>
</tr>
</table>
};
}
if ($bonsai_tree) {
print "The tree is currently <font size=+2>";
print (&tree_open ? 'OPEN' : 'CLOSED');
print "</font>\n";
}
}
sub print_table_body {
for (my $tt=1; $tt <= $time_count; $tt++) {
last if $build_time_times->[$tt] < $mindate;
print_table_row($tt);
}
}
sub print_table_row {
my ($tt) = @_;
# Time column
#
my $query_link = '';
my $end_query = '';
my $pretty_time = &print_time($build_time_times->[$tt]);
($hour) = $pretty_time =~ /(\d\d):/;
if ($lasthour != $hour or &has_who_list($tt)) {
$query_link = &query_ref($td1, $build_time_times->[$tt]);
$end_query = '</a>';
}
if ($lasthour == $hour) {
$pretty_time =~ s/^.*&nbsp;//;
} else {
$lasthour = $hour;
}
my $hour_color = '';
$hour_color = ' bgcolor=#e7e7e7' if $build_time_times->[$tt] % 7200 <= 3600;
print "<tr align=center><td align=right$hour_color>",
"$query_link\n$pretty_time$end_query</td>\n";
# Guilty
#
print '<td>';
for $who (sort keys %{$who_list->[$tt]} ){
$qr = &who_menu($td1, $build_time_times->[$tt],
$build_time_times->[$tt-1],$who);
$who =~ s/%.*$//;
print " ${qr}$who</a>\n";
}
print '</td>';
# Build Status
#
for ($bn=1; $bn <= $name_count; $bn++) {
if (not defined($br = $build_table->[$tt][$bn])) {
# No build data for this time
print "<td></td>\n";
next;
}
next if $br == -1; # rowspan has covered this row
$hasnote = $br->{hasnote};
$noteid = $hasnote ? $br->{noteid} : 0;
$rowspan = $br->{rowspan};
$rowspan = $mindate_time_count - $tt + 1
if $tt + $rowspan - 1 > $mindate_time_count;
$color = $colormap{$br->{buildstatus}};
$status = $br->{buildstatus};
print "<td rowspan=$rowspan bgcolor=${color}>\n";
$logfile = $br->{logfile};
$errorparser = $br->{errorparser};
$buildname = $br->{buildname};
$buildtime = $br->{buildtime};
$buildtree = $br->{td}->{name};
print "<tt>\n";
# Build Note
#
$buildname = &url_encode($buildname);
my $logurl = "${rel_path}showlog.cgi?log=$buildtree/$logfile";
if ($hasnote) {
print "<a href='$logurl' onclick=\"return ",
"note(event,$noteid,'$logfile');\">",
"<img src='$images{star}' border=0></a>\n";
}
# Build Log
#
print "<A HREF='$logurl' onclick=\"return log(event,$bn,'$logfile');\">";
print "L</a>";
# What Changed
#
if( $br->{previousbuildtime} ){
my $previous_br = $build_table->[$tt+$rowspan][$bn];
my $previous_rowspan = $previous_br->{rowspan};
if (&has_who_list($tt+$rowspan,
$tt+$rowspan+$previous_rowspan-1)) {
print "\n", &query_ref($br->{td},
$br->{previousbuildtime},
$br->{buildtime});
print "C</a>";
}
}
if ($br->{binaryname} ne '') {
$binfile = "$buildtree/bin/$buildtime/$br->{buildname}/"
."$br->{binaryname}";
$binfile =~ s/ //g;
print " <a href=$rel_path$binfile>B</a>";
}
print "</tt>\n</td>";
}
print "</tr>\n";
}
sub print_table_header {
my $ii, $nspan;
print "<table border=1 bgcolor='#FFFFFF' cellspacing=1 cellpadding=1>\n";
print "<tr align=center>\n";
print "<td rowspan=1><font size=-1>Click time to <br>see changes <br>",
"since time</font></td>";
print "<td><font size=-1>",
"Click name to see what they did</font>";
print "<br><font size=-2>",
&open_showbuilds_href(rebuildguilty=>'1'),
"Rebuild guilty list</a></td>";
for ($ii=1; $ii <= $name_count; $ii++) {
my $bn = $build_name_names->[$ii];
$bn =~ s/Clobber/Clbr/g;
$bn =~ s/Depend/Dep/g;
$bn = "<font face='Helvetica,Arial' size=-1>$bn</font>";
my $last_status = &last_status($ii);
if ($last_status eq 'busted') {
if ($form{nocrap}) {
print "<td rowspan=2 bgcolor=$colormap{busted}>$bn</td>";
} else {
print "<td rowspan=2 bgcolor=000000 background='$images{flames}'>";
print "<font color=white>$bn</font></td>";
}
}
else {
print "<td rowspan=2 bgcolor=$colormap{$last_status}>$bn</td>";
}
}
print "</tr><tr>\n";
print "<TH>Build Time</TH>\n";
print "<TH>Guilty</th>\n";
print "</tr>\n";
}
sub print_table_footer {
print "</table>\n";
my $nextdate = $maxdate - $hours*60*60;
print &open_showbuilds_href(maxdate=>"$nextdate", nocrap=>'1')
."Show next $hours hours</a>";
print "<p><a href='${rel_path}admintree.cgi?tree=$tree'>",
"Administrate Tinderbox Trees</a><br>\n";
}
sub open_showbuilds_url {
my %args = (
nocrap => "$form{nocrap}",
@_
);
my $url = "${rel_path}showbuilds.cgi?tree=$form{tree}";
$url .= "&hours=$hours" if $hours ne $default_hours;
while (my ($key, $value) = each %args) {
$url .= "&$key=$value" if $value ne '';
}
return $url;
}
sub open_showbuilds_href {
return "<a href=".open_showbuilds_url(@_).">";
}
sub query_ref {
my ($td, $mindate, $maxdate, $who) = @_;
my $output = '';
$output = "<a href=${rel_path}../bonsai/cvsquery.cgi";
$output .= "?module=$td->{cvs_module}";
$output .= "&branch=$td->{cvs_branch}" if $td->{cvs_branch} ne 'HEAD';
$output .= "&cvsroot=$td->{cvs_root}" if $td->{cvs_root} ne $default_root;
$output .= "&date=explicit&mindate=$mindate";
$output .= "&maxdate=$maxdate" if $maxdate ne '';
$output .= "&who=$who" if $who ne '';
$output .= ">";
}
sub query_ref2 {
my ($td, $mindate, $maxdate, $who) = @_;
return "${rel_path}../bonsai/cvsquery.cgi?module=$td->{cvs_module}"
."&branch=$td->{cvs_branch}&cvsroot=$td->{cvs_root}"
."&date=explicit&mindate=$mindate&maxdate=$maxdate&who="
. url_encode($who);
}
sub who_menu {
my ($td, $mindate, $maxdate, $who) = @_;
my $treeflag;
$qr = "${rel_path}../registry/who.cgi?email=". url_encode($who)
. "&d=$td->{cvs_module}|$td->{cvs_branch}|$td->{cvs_root}|$mindate|$maxdate";
return "<a href='$qr' onclick=\"return who(event);\">";
}
# Check to see if anyone checked in during time slot.
# ex. has_who_list(1); # Check for checkins in most recent time slot.
# ex. has_who_list(1,5); # Check range of times.
sub has_who_list {
my ($time1, $time2) = @_;
if (not defined(@who_check_list)) {
# Build a static array of true/false values for each time slot.
$who_check_list[$time_count] = 0;
my ($t) = 1;
for (; $t<=$time_count; $t++) {
$who_check_list[$t] = 1 if each %{$who_list->[$t]};
}
}
if ($time2) {
for ($ii=$time1; $ii<=$time2; $ii++) {
return 1 if $who_check_list[$ii]
}
return 0
} else {
return $who_check_list[$time1];
}
}
sub tree_open {
my $done, $line, $a, $b;
open(BID, "<../bonsai/data/$bonsai_tree/batchid")
or print "can't open batchid<br>";
($a,$b,$bid) = split / /, <BID>;
close(BID);
open(BATCH, "<../bonsai/data/$bonsai_tree/batch-${bid}")
or print "can't open batch-${bid}<br>";;
$done = 0;
while (($line = <BATCH>) and not $done){
if ($line =~ /^set treeopen/) {
chop $line;
($a,$b,$treestate) = split / /, $line ;
$done = 1;
}
}
close(BATCH);
return $treestate;
}
sub print_javascript {
my $script;
($script = <<"__ENDJS") =~ s/^ //gm;
<script>
if (parseInt(navigator.appVersion) < 4) {
window.event = 0;
}
function who(d) {
var version = parseInt(navigator.appVersion);
if (version < 4 || version >= 5) {
return true;
}
var l = document.layers['popup'];
l.src = d.target.href;
l.top = d.target.y - 6;
l.left = d.target.x - 6;
if (l.left + l.clipWidth > window.width) {
l.left = window.width - l.clipWidth;
}
l.visibility="show";
return false;
}
function log_url(logfile) {
return "showlog.cgi?log=" + buildtree + "/" + logfile;
}
function note(d,noteid,logfile) {
var version = parseInt(navigator.appVersion);
if (version < 4 || version >= 5) {
document.location = log_url(logfile);
return false;
}
var l = document.layers['popup'];
l.document.write("<table border=1 cellspacing=1><tr><td>"
+ notes[noteid] + "</tr></table>");
l.document.close();
l.top = d.y-10;
var zz = d.x;
if (zz + l.clip.right > window.innerWidth) {
zz = (window.innerWidth-30) - l.clip.right;
if (zz < 0) { zz = 0; }
}
l.left = zz;
l.visibility="show";
return false;
}
function log(e,buildindex,logfile)
{
var logurl = log_url(logfile);
var commenturl = "addnote.cgi?log=" + buildtree + "/" + logfile;
var version = parseInt(navigator.appVersion);
if (version < 4 || version >= 5) {
document.location = logurl;
return false;
}
var q = document.layers["logpopup"];
q.top = e.target.y - 6;
var yy = e.target.x;
if ( yy + q.clip.right > window.innerWidth) {
yy = (window.innerWidth-30) - q.clip.right;
if (yy < 0) { yy = 0; }
}
q.left = yy;
q.visibility="show";
q.document.write("<TABLE BORDER=1><TR><TD><B>"
+ builds[buildindex] + "</B><BR>"
+ "<A HREF=$rel_path" + logurl + ">View Brief Log</A><BR>"
+ "<A HREF=$rel_path" + logurl + "&fulltext=1"+">View Full Log</A><BR>"
+ "<A HREF=$rel_path" + commenturl + ">Add a Comment</A><BR>"
+ "</TD></TR></TABLE>");
q.document.close();
return false;
}
notes = new Array();
builds = new Array();
__ENDJS
print $script;
$ii = 0;
while ($ii < @note_array) {
$ss = $note_array[$ii];
while ($ii < @note_array && $note_array[$ii] eq $ss) {
print "notes[$ii] = ";
$ii++;
}
$ss =~ s/\\/\\\\/g;
$ss =~ s/\"/\\\"/g;
$ss =~ s/\n/\\n/g;
print "\"$ss\";\n";
}
for ($ii=1; $ii <= $name_count; $ii++) {
if (defined($br = $build_table->[1][$ii]) and $br != -1) {
my $bn = $build_name_names->[$ii];
print "builds[$ii]='$bn';\n";
}
}
print "buildtree = '$form{tree}';\n";
($script = <<'__ENDJS') =~ s/^ //gm;
</script>
<layer name="popup" onMouseOut="this.visibility='hide';"
left=0 top=0 bgcolor="#ffffff" visibility="hide">
</layer>
<layer name="logpopup" onMouseOut="this.visibility='hide';"
left=0 top=0 bgcolor="#ffffff" visibility="hide">
</layer>
__ENDJS
print $script;
}
sub do_express {
print "Content-type: text/html\nRefresh: 900\n\n<HTML>\n";
my %build, %times;
loadquickparseinfo($form{tree}, \%build, \%times);
my @keys = sort keys %build;
my $keycount = @keys;
my $tm = &print_time(time);
print "<table border=1 cellpadding=1 cellspacing=1><tr>";
print "<th align=left colspan=$keycount>";
print &open_showbuilds_href."$tree as of $tm</a></tr><tr>\n";
foreach my $buildname (@keys) {
print "<td bgcolor='$colormap{$build{$buildname}}'>$buildname</td>";
}
print "</tr></table>\n";
}
# This is essentially do_express but it outputs a different format
sub do_panel {
print "Content-type: text/html\n\n<HTML>\n" unless $form{static};
my %build, %times;
loadquickparseinfo($form{tree}, \%build, \%times);
print q(
<head>
<META HTTP-EQUIV="Refresh" CONTENT="300">
<style>
body, td {
font-family: Verdana, Sans-Serif;
font-size: 8pt;
}
</style>
</head>
<body BGCOLOR="#FFFFFF" TEXT="#000000"
LINK="#0000EE" VLINK="#551A8B" ALINK="#FF0000">
);
# Hack the panel link for now.
print "<a target='_content' href='http://tinderbox.mozilla.org/seamonkey/'>$tree";
$bonsai_tree = '';
require "$tree/treedata.pl";
if ($bonsai_tree ne '') {
print " is ", tree_open() ? "OPEN" : "CLOSED";
}
# Add the current time
my ($minute,$hour,$mday,$mon) = (localtime)[1..4];
my $tm = sprintf("%d/%d&nbsp;%d:%02d",$mon+1,$mday,$hour,$minute);
print " as of $tm</a><br>";
print "<table border=0 cellpadding=1 cellspacing=1>";
while (my ($name, $status) = each %build) {
print "<tr><td bgcolor='$colormap{$status}'>$name</td></tr>";
}
print "</table></body>";
}
sub do_flash {
print "Content-type: text/rdf\n\n" unless $form{static};
my %build, %times;
loadquickparseinfo($form{tree}, \%build, \%times);
my ($mac,$unix,$win) = (0,0,0);
while (my ($name, $status) = each %build) {
next if $status eq 'success';
$mac = 1, next if $name =~ /Mac/;
$win = 1, next if $name =~ /Win/;
$unix = 1;
}
print q{
<RDF:RDF xmlns:RDF='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:NC='http://home.netscape.com/NC-rdf#'>
<RDF:Description about='NC:FlashRoot'>
};
my $busted = $mac + $unix + $win;
if ($busted) {
# Construct a legible sentence; e.g., "Mac, Unix, and Windows
# are busted", "Windows is busted", etc. This is hideous. If
# you can think of something better, please fix it.
my $text;
if ($mac) {
$text .= 'Mac' . ($busted > 2 ? ', ' : ($busted > 1 ? ' and ' : ''));
}
if ($unix) {
$text .= 'Unix' . ($busted > 2 ? ', and ' : ($win ? ' and ' : ''));
}
if ($win) {
$text .= 'Windows';
}
$text .= ($busted > 1 ? ' are ' : ' is ') . 'busted';
# The Flash spec says we need to give ctime.
use POSIX;
my $tm = POSIX::ctime(time());
$tm =~ s/^...\s//; # Strip day of week
$tm =~ s/:\d\d\s/ /; # Strip seconds
chop $tm;
print qq{
<NC:child>
<RDF:Description ID='flash'>
<NC:type resource='http://www.mozilla.org/RDF#TinderboxFlash' />
<NC:source>$tree</NC:source>
<NC:description>$text</NC:description>
<NC:timestamp>$tm</NC:timestamp>
</RDF:Description>
</NC:child>
};
}
print q{
</RDF:Description>
</RDF:RDF>
};
}
sub do_quickparse {
print "Content-type: text/plain\n\n";
my @treelist = split /,/, $tree;
foreach my $t (@treelist) {
$bonsai_tree = "";
require "$t/treedata.pl";
if ($bonsai_tree ne "") {
my $state = tree_open() ? "Open" : "Close";
print "State|$t|$bonsai_tree|$state\n";
}
my %build, %times;
loadquickparseinfo($t, \%build, \%times);
foreach my $buildname (sort keys %build) {
print "Build|$t|$buildname|$build{$buildname}\n";
}
}
}
sub do_rdf {
print "Content-type: text/plain\n\n";
my $mainurl = "http://$ENV{SERVER_NAME}$ENV{SCRIPT_NAME}?tree=$tree";
my $dirurl = $mainurl;
$dirurl =~ s@/[^/]*$@@;
my %build, %times;
loadquickparseinfo($tree, \%build, \%times);
my $image = "channelok.gif";
my $imagetitle = "OK";
foreach my $buildname (sort keys %build) {
if ($build{$buildname} eq 'busted') {
$image = "channelflames.gif";
$imagetitle = "Bad";
last;
}
}
print qq{<?xml version="1.0"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://my.netscape.com/rdf/simple/0.9/">
<channel>
<title>Tinderbox - $tree</title>
<description>Build bustages for $tree</description>
<link>$mainurl</link>
</channel>
<image>
<title>$imagetitle</title>
<url>$dirurl/$image</url>
<link>$mainurl</link>
</image>
};
$bonsai_tree = '';
require "$tree/treedata.pl";
if ($bonsai_tree ne '') {
my $state = tree_open() ? "OPEN" : "CLOSED";
print "<item><title>The tree is currently $state</title>",
"<link>$mainurl</link></item>\n";
}
foreach my $buildname (sort keys %build) {
if ($build{$buildname} eq 'busted') {
print "<item><title>$buildname is in flames</title>",
"<link>$mainurl</link></item>\n";
}
}
print "</rdf:RDF>\n";
}

View File

@@ -1,136 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
$| = 1;
use lib "../bonsai";
require 'globals.pl';
require 'imagelog.pl';
require 'lloydcgi.pl';
require 'header.pl';
check_password();
print "Content-type: text/html\n\n";
@url = ();
@quote = ();
@width = ();
@height = ();
$i = 0;
EmitHtmlHeader("tinderbox: all images");
print '<UL>
<P>These are all of the images currently in
<A HREF=http://www.mozilla.org/tinderbox.html>Tinderbox</A>.
<P>Please don\'t give out this URL: this is only here for our debugging
needs, and isn\'t linked to by the rest of Tinderbox: because looking at
all the images at once would be be cheating! you\'re supposed to let them
surprise you over time. What, do you read ahead in your desktop calendar,
too? Where\'s your sense of mystery and anticipation?
<P>
</UL>
';
if ($form{'url'} ne "") {
$oldname = "$data_dir/imagelog.txt";
open (OLD, "<$oldname") || die "Oops; can't open imagelog.txt";
$newname = "$oldname-$$";
open (NEW, ">$newname") || die "Can't open $newname";
$foundit = 0;
while (<OLD>) {
chop;
($url, $width, $height, $quote) = split(/\`/);
if ($url eq $form{'url'} && $quote eq $form{'origquote'}) {
$foundit = 1;
if ($form{'nukeit'} ne "") {
next;
}
$quote = $form{'quote'};
}
print NEW "$url`$width`$height`$quote\n";
}
close OLD;
close NEW;
if (!$foundit) {
print "<font color=red>Hey, couldn't find it!</font> Did someone\n";
print "else already edit it?<P>\n";
unlink $newname;
} else {
print "Change made.<P>";
rename ($newname, $oldname) || die "Couldn't rename $newname to $oldname";
}
$form{'doedit'} = "1";
}
$doedit = ($form{'doedit'} ne "");
if (!$doedit) {
print "
<form method=post>
<input type=hidden name=password value=\"$form{'password'}\">
<input type=hidden name=doedit value=1>
<input type=submit value='Let me edit text or remove pictures.'>
</form><P>";
}
open( IMAGELOG, "<$data_dir/imagelog.txt" ) || die "can't open file";
while( <IMAGELOG> ){
chop;
($url[$i],$width[$i],$height[$i],$quote[$i]) = split(/\`/);
$i++;
}
close( IMAGELOG );
$i--;
print "<center>";
while( $i >= 0 ){
$qurl = value_encode($url[$i]);
$qquote = value_encode($quote[$i]);
print "
<img border=2 src='$url[$i]' width='$width[$i]' height='$height[$i]'><br>
<i>$quote[$i]</i>";
if ($doedit) {
print "
<form method=post>
<input type=submit name=nukeit value='Delete this image'><br>
<input name=quote size=60 value=\"$qquote\"><br>
<input type=submit name=edit value='Change text'><hr>
<input type=hidden name=url value=\"$qurl\">
<input type=hidden name=origquote value=\"$qquote\">
<input type=hidden name=password value=\"$form{'password'}\">
</form>";
}
print "<br><br>\n";
$i--;
}

View File

@@ -1,375 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib '../bonsai';
require 'globals.pl';
require 'lloydcgi.pl';
require 'header.pl';
#############################################################
# Global variables
$LINES_AFTER_ERROR = 5;
$LINES_BEFORE_ERROR = 30;
# These variables are set by the error parser functions:
# has_error(), has_warning(), and has_errorline().
$error_file = '';
$error_file_ref = '';
$error_line = 0;
$error_guess = 0;
$next_err = 0;
@log_errors = ();
$log_line = 0;
#############################################################
# CGI inputs
if (defined($args = $form{log}) or defined($args = $form{exerpt})) {
($full_logfile, $linenum) = split /:/, $args;
($tree, $logfile) = split /\//, $full_logfile;
my $br = find_build_record($tree, $logfile);
$errorparser = $br->{errorparser};
$buildname = $br->{buildname};
$buildtime = $br->{buildtime};
$numlines = 50;
$numlines = $form{numlines} if exists $form{numlines};
} else {
$tree = $form{tree};
$errorparser = $form{errorparser};
$logfile = $form{logfile};
$buildname = $form{buildname};
$buildtime = $form{buildtime};
}
$fulltext = $form{fulltext};
$enc_buildname = &url_encode($buildname);
die "the \"tree\" parameter must be provided\n" unless $tree;
require "$tree/treedata.pl";
$time_str = print_time( $buildtime );
$|=1;
if ($linenum) {
&print_fragment;
exit;
}
&print_header;
&print_notes;
# Dynamically load the error parser
#
die "the \"errorparser\" parameter must be provided\n" unless $errorparser;
require "ep_${errorparser}.pl";
if ($fulltext)
{
&print_summary;
&print_log;
}
else
{
$brief_filename = $logfile;
$brief_filename =~ s/.gz$/.brief.html/;
if (-T "$tree/$brief_filename" and -M _ > -M $tree/$logfile)
{
open (BRIEFFILE, "<$tree/$brief_filename");
print while (<BRIEFFILE>)
}
else
{
open (BRIEFFILE, ">$tree/$brief_filename");
&print_summary;
&print_log;
}
}
# end of main
############################################################
sub print_fragment {
print "Content-type: text/html\n\n";
print "<META HTTP-EQUIV=\"EXPIRES\" CONTENT=\"1\">\n";
my $heading = "Build Log (Fragment)";
my $subheading = "$buildname on $time_str";
my $title = "$heading - $subheading";
EmitHtmlTitleAndHeader($title, $heading, $subheading);
print "<a href='showlog.cgi?tree=$tree&errorparser=$errorparser&logfile=$logfile&buildtime=$buildtime&buildname=$enc_buildname&fulltext=1'>Show Full Build Log</a>";
open(BUILD_IN, "$gzip -d -c $tree/$logfile|");
my $first_line = $linenum - ($numlines/2);
my $last_line = $linenum + ($numlines/2);
print "<pre><b>.<br>.<br>.<br></b>";
while(<BUILD_IN>) {
next if $. < $first_line;
last if $. > $last_line;
print "<b><font color='red'>" if $. == $linenum;
print;
print "</font></b>" if $. == $linenum;
}
print "<b>.<br>.<br>.<br></b></pre>";
}
sub print_header {
print "Content-type: text/html\n\n";
if( $fulltext ){
$s = 'Show <b>Brief</b> Log';
$s1 = '';
$s2 = 'Full';
}
else {
$s = 'Show <b>Full</b> Log';
$s1 = 1;
$s2 = 'Brief';
}
print "<META HTTP-EQUIV=\"EXPIRES\" CONTENT=\"1\">\n";
my $heading = "Build Log ($s2)";
my $subheading = "$buildname on $time_str";
my $title = "$heading - $subheading";
EmitHtmlTitleAndHeader($title, $heading, $subheading);
print "
<font size=+1>
<dt><a href='showlog.cgi?tree=$tree&errorparser=$errorparser&logfile=$logfile&buildtime=$buildtime&buildname=$enc_buildname&fulltext=$s1'>$s</a>
<dt><a href=\"showbuilds.cgi?tree=$tree\">Return to the Build Page</a>
<dt><a href=\"addnote.cgi?tree=$tree\&buildname=$enc_buildname\&buildtime=$buildtime\&logfile=$logfile\&errorparser=$errorparser\">
Add a Comment to the Log</a>
</font>
";
}
sub print_notes {
#
# Print notes
#
$found_note = 0;
open(NOTES,"<$tree/notes.txt")
or print "<h2>warning: Couldn't open $tree/notes.txt </h2>\n";
print "$buildtime, $buildname<br>\n";
while(<NOTES>){
chop;
($nbuildtime,$nbuildname,$nwho,$nnow,$nenc_note) = split(/\|/);
#print "$_<br>\n";
if( $nbuildtime == $buildtime && $nbuildname eq $buildname ){
if( !$found_note ){
print "<H2>Build Comments</H2>\n";
$found_note = 1;
}
$now_str = &print_time($nnow);
$note = &url_decode($nenc_note);
print "<pre>\n[<b><a href=mailto:$nwho>$nwho</a> - $now_str</b>]\n$note\n</pre>";
}
}
close(NOTES);
}
sub print_summary {
#
# Print the summary first
#
logprint('<H2>Build Error Summary</H2><PRE>');
$log_line = 0;
open( BUILD_IN, "$gzip -d -c $tree/$logfile|" );
while( $line = <BUILD_IN> ){
&output_summary_line( $line );
}
close( BUILD_IN );
push @log_errors, 9999999;
logprint('</PRE>');
}
sub print_log_section {
my ($tree, $logfile, $line_of_interest, $num_lines) = shift;
local $_;
my $first_line = $line_of_interest - $num_lines / 2;
my $last_line = $first_line + $num_lines;
print "<a href='showlog.cgi?tree=$tree&logfile=$logfile&line="
.($line_of_interest-$num_lines)."&numlines=$num_lines'>"
."Previous $num_lines</a>";
print "<font size='+1'><b>.<br>.<br>.<br></b></font>";
print "<pre>";
my $ii = 0;
open BUILD_IN, "$gzip -d -c $tree/$logfile|";
while (<BUILD_IN>) {
$ii++;
next if $ii < $first_line;
last if $ii > $last_line;
if ($ii == $line_of_intested) {
print "<b>$_</b>";
} else {
print;
}
}
close BUILD_IN;
print "</pre>";
print "<font size='+1'><b>.<br>.<br>.<br></b></font>";
print "<a href='showlog.cgi?tree=$tree&logfile=$logfile&line="
.($line_of_interest+$num_lines)."&numlines=$num_lines'>"
."Next $num_lines</a>";
}
sub print_log {
#
# reset the error counter
#
$next_err = 0;
logprint('<H2>Build Error Log</H2><pre>');
$log_line = 0;
open( BUILD_IN, "$gzip -d -c $tree/$logfile|" );
while( $line = <BUILD_IN> ){
&output_log_line( $line );
}
close( BUILD_IN );
logprint('</PRE><p>'
."<font size=+1><a name=\"err$next_err\">No More Errors</a></font>"
.'<br><br><br>');
}
sub output_summary_line {
my( $line ) = $_[0];
my( $has_error );
$has_error = &has_error( $line );
$line =~ s/&/&amp;/g;
$line =~ s/</&lt;/g;
if( $has_error ){
push @log_errors, $log_line + $LINES_AFTER_ERROR;
if( ! $last_was_error ) {
logprint("<a href=\"#err$next_err\">$line</a>");
$next_err++;
}
$last_was_error = 1;
}
else {
$last_was_error = 0;
}
$log_line++;
}
sub output_log_line {
my( $line ) = $_[0];
my( $has_error, $dur, $dur_min,$dur_sec, $dur_str, $logline );
$has_error = &has_error( $line );
$has_warning = &has_warning( $line );
$line =~ s/&/&amp;/g;
$line =~ s/</&lt;/g;
$logline = '';
if( ($has_error || $has_warning) && &has_errorline( $line ) ) {
$q = quotemeta( $error_file );
#$goto_line = ($error_line ? 10 > $error_line - 10 : 1 );
$goto_line = ($error_line > 10 ? $error_line - 10 : 1 );
$cvsblame = ($error_guess ? "cvsguess.cgi" : "cvsblame.cgi");
$line =~ s@$q@<a href=../bonsai/$cvsblame?file=$error_file_ref&rev=$cvs_branch&mark=$error_line#$goto_line $source_target>$error_file</a>@
}
if( $has_error ){
if( ! $last_was_error ) {
$logline .= "<a name=\"err$next_err\"></a>";
$next_err++;
$logline .= "<a href=\"#err$next_err\">NEXT</a> ";
}
else {
$logline .= " ";
}
$logline .= "<font color=\"000080\">$line</font>";
$last_was_error = 1;
}
elsif( $has_warning ){
$logline .= " ";
$logline .= "<font color=000080>$line</font>";
}
else {
$logline .= " $line";
$last_was_error = 0;
}
&push_log_line( $logline );
}
sub push_log_line {
my( $line ) = $_[0];
if( $fulltext ){
logprint($line);
return;
}
if( $log_line > $log_errors[$cur_error] ){
$cur_error++;
}
if( $log_line >= $log_errors[$cur_error] - $LINES_BEFORE_ERROR ){
if( $log_skip != 0 ){
logprint("\n<i><font size=+1> Skipping $log_skip Lines...</i></font>\n\n");
$log_skip = 0;
}
logprint($line);
}
else {
$log_skip++;
}
$log_line++;
}
sub logprint {
my $line = $_[0];
print $line;
print BRIEFFILE $line if not $fulltext;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

View File

@@ -1,75 +0,0 @@
# The contents of this file are subject to the Mozilla Public License
# Version 1.0 (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/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and
# limitations under the License.
#
# The Original Code is the Tinderbox CVS Tool rpm spec file.
#
# The Initial Developer of this code under the MPL is Christopher
# Seawood, <cls@seawood.org>. Portions created by Christopher Seawood
# are Copyright (C) 1998 Christopher Seawood. All Rights Reserved.
%define ver SNAP
%define perl /usr/bin/perl
%define cvsroot /cvsroot
%define gzip /usr/bin/gzip
%define uudecode /usr/bin/uudecode
%define bonsai ../bonsai
%define prefix /home/httpd/html/tinderbox
# This rpm is not relocateable
Summary: automated build tool
Name: tinderbox
Version: %{ver}
Release: 1
Copyright: NPL
Group: Networking/Admin
Source: %{name}-%{ver}.tar.gz
BuildRoot: /var/tmp/build-%{name}
Requires: bonsai
Packager: Christopher Seawood <cls@seawood.org>
%changelog
* Thu Nov 12 1998 Christopher Seawood <cls@seawood.org>
- Replaced ver with SNAP
* Mon Oct 26 1998 Christopher Seawood <cls@seawood.org>
- Added MPL header
* Sun Aug 31 1998 Christopher Seawood <cls@seawood.org>
- Made rpm from cvs snapshot
%description
Essentially, Tinderbox is a detective tool. It allows you to see what
is happening in the source tree. It shows you who checked in what (by
asking Bonsai); what platforms have built successfully; what platforms
are broken and exactly how they are broken (the build logs); and the
state of the files that made up the build (cvsblame) so you can figure
out who broke the build, so you can do the most important thing, hold
them accountable for their actions.
%prep
%setup -n %{name}
%build
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}%{prefix}/{data,examples}
make install PERL=%perl UUDECODE=%uudecode GZIP=%gzip BONSAI=%bonsai CVSROOT=%cvsroot PREFIX=${RPM_BUILD_ROOT}%prefix
%post
echo "Remember to set the admin passwd via '%{bonsai}/data/trapdoor password > %{prefix}/data/passwd"
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr (-, nobody, nobody)
%doc README Makefile
%{prefix}

View File

@@ -1,123 +0,0 @@
#!/usr/bonsaitools/bin/perl --
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (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 or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is the Tinderbox build tool.
#
# 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.
use lib '../bonsai';
require "globals.pl";
require 'lloydcgi.pl';
require 'header.pl';
use Date::Parse;
use Date::Format;
my $TIMEFORMAT = "%D %T";
$| = 1;
print "Content-type: text/html\n\n<HTML>\n";
my $tree = $form{'tree'};
my $start = $form{'start'};
my $end = $form{'end'};
sub str2timeAndCheck {
my ($str) = (@_);
my $result = str2time($str);
if (defined $result && $result > 7000000) {
return $result;
}
print "<p><font color=red>Can't parse as a date: $str</font><p>\n";
return 0;
}
my $header = "<table border=1><th>Build time</th><th>Build name</th><th>Who</th><th>Note time</th><th>Note</th>";
if (defined $tree && defined $start && defined $end) {
my $first = str2timeAndCheck($start);
my $last = str2timeAndCheck($end);
if ($first > 0 && $last > 0) {
if (open(IN, "<$tree/notes.txt")) {
print "<hr><center><h1>Notes for $tree</H1><H3>from " .
time2str($TIMEFORMAT, $first) . " to " .
time2str($TIMEFORMAT, $last) . "</H3></center>\n";
my %stats;
print "$header\n";
while (<IN>) {
chop;
my ($nbuildtime,$nbuildname,$nwho,$nnow,$nenc_note)
= split /\|/;
if ($nbuildtime >= $first && $nbuildtime <= $last) {
my $note = &url_decode($nenc_note);
$nbuildtime = print_time($nbuildtime);
$nnow = print_time($nnow);
print "<tr>";
print "<td>$nbuildtime</td>";
print "<td>$nbuildname</td>";
print "<td>$nwho</td>";
print "<td>$nnow</td>";
print "<td>$note</td>";
print "</tr>\n";
if (++$count % 100 == 0) {
print "</table>$header\n";
}
}
}
} else {
print "<p><font color=red>There does not appear to be a tree " .
"named '$tree'.</font><p>";
}
}
print "</table>\n";
}
if (!defined $tree) {
$tree = "";
}
if (!defined $start) {
$start = time2str($TIMEFORMAT, time() - 7*24*60*60); # One week ago.
}
if (!defined $end) {
$end = time2str($TIMEFORMAT, time()); # #now
}
print qq|
<form>
<table>
<tr>
<th align=right>Tree:</th>
<td><input name=tree size=30 value="$tree"></td>
</tr>
<tr>
<th align=right>Start time:</th>
<td><input name=start size=30 value="$start"></td>
</tr>
<tr>
<th align=right>End time:</th>
<td><input name=end size=30 value="$end"></td>
</tr>
</table>
<INPUT TYPE=\"submit\" VALUE=\"View Notes \">
</form>
|;

View File

@@ -1,437 +0,0 @@
#! /usr/bonsaitools/bin/perl
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla 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/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is Tinderbox
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are Copyright (C) 1999
# Netscape Communications Corporation. All Rights Reserved.
#
# Contributor(s): Stephen Lamm <slamm@mozilla.org>
use FileHandle;
$tree = 'SeaMonkey';
# tinderbox/globals.pl uses many shameful globals
$form{tree} = $tree;
require 'globals.pl';
$cvsroot = '/cvsroot/mozilla';
$lxr_data_root = '/export2/lxr-data';
@ignore = (
'long long',
'__cmsg_data',
'location of the previous definition',
'\' was hidden',
'declaration of \`index\'',
);
$ignore_pat = "(?:".join('|',@ignore).")";
print STDERR "Building hash of file names...";
($file_bases, $file_fullpaths) = build_file_hash($cvsroot, $tree);
print STDERR "done.\n";
for $br (last_successful_builds($tree)) {
next unless $br->{buildname} =~ /shrike.*\b(Clobber|Clbr)\b/;
my $log_file = "$br->{logfile}";
warn "Parsing build log, $log_file\n";
$fh = new FileHandle "gunzip -c $tree/$log_file |";
&gcc_parser($fh, $cvsroot, $tree, $log_file, $file_bases, $file_fullpaths);
$fh->close;
&build_blame;
my $warn_file = "$tree/warn$log_file";
$warn_file =~ s/.gz$/.html/;
$fh->open(">$warn_file") or die "Unable to open $warn_file: $!\n";
&print_warnings_as_html($fh, $br);
$fh->close;
warn "Wrote output to $warn_file\n";
last;
}
# end of main
# ===================================================================
sub build_file_hash {
my ($cvsroot, $tree) = @_;
$lxr_data_root = "/export2/lxr-data/\L$tree";
$lxr_file_list = "\L$lxr_data_root/.glimpse_filenames";
open(LXR_FILENAMES, "<$lxr_file_list")
or die "Unable to open $lxr_file_list: $!\n";
use File::Basename;
while (<LXR_FILENAMES>) {
my ($base, $dir, $ext) = fileparse($_,'\.[^/]*');
next unless $ext =~ /^\.(cpp|h|C|s|c|mk|in)$/;
$base = "$base$ext";
$dir =~ s|$lxr_data_root/mozilla/||;
$dir =~ s|/$||;
$fullpath{"$dir/$base"}=1;
unless (exists $bases{$base}) {
$bases{$base} = $dir;
} else {
$bases{$base} = '[multiple]';
}
}
return \%bases, \%fullpath;
}
sub last_successful_builds {
my $tree = shift;
my @build_records = ();
my $br;
$maxdate = time;
$mindate = $maxdate - 5*60*60; # Go back 5 hours
print STDERR "Loading build data...";
&load_data;
print STDERR "done\n";
for (my $ii=1; $ii <= $name_count; $ii++) {
for (my $tt=1; $tt <= $time_count; $tt++) {
if (defined($br = $build_table->[$tt][$ii])
and $br->{buildstatus} eq 'success') {
push @build_records, $br;
last;
} } }
return @build_records;
}
sub gcc_parser {
my ($fh, $cvsroot, $tree, $log_file, $file_bases, $file_fullnames) = @_;
my $dir = '';
while (<$fh>) {
# Directory
#
if (/^gmake\[\d\]: Entering directory \`(.*)\'$/) {
($build_dir = $1) =~ s|.*/mozilla/||;
next;
}
# Now only match lines with "warning:"
next unless /warning:/;
next if /$ignore_pat/o;
chomp; # Yum, yum
my ($filename, $line, $warning_text);
($filename, $line, undef, $warning_text) = split /:\s*/, $_, 4;
$filename =~ s/.*\///;
# Special case for Makefiles
$filename =~ s/Makefile$/Makefile.in/;
my $dir = '';
if ($file_fullnames->{"$build_dir/$filename"}) {
$dir = $build_dir;
} else {
unless(defined($dir = $file_bases->{$filename})) {
$dir = '[no_match]';
}
}
my $file = "$dir/$filename";
unless (defined($warnings{$file}{$line})) {
# Special case for "`foo' was hidden\nby `foo2'"
$warning_text = "...was hidden " . $warning_text
if $warning_text =~ /^by \`/;
# Remember where in the build log the warning occured
$warnings{$file}{$line} = {
first_seen_line => $.,
log_file => $log_file,
warning_text => $warning_text,
};
}
$warnings{$file}{$line}->{count}++;
$total_warnings_count++;
}
}
sub dump_warning_data {
while (my ($file, $lines_hash) = each %warnings) {
while (my ($line, $record) = each %{$lines_hash}) {
print join ':',
$file,$line,
$record->{first_seen_line},
$record->{count},
$record->{warning_text};
print "\n";
}
}
}
sub build_blame {
use lib '../bonsai';
require 'utils.pl';
require 'cvsblame.pl';
while (($file, $lines_hash) = each %warnings) {
my $rcs_filename = "$cvsroot/$file,v";
unless (-e $rcs_filename) {
warn "Unable to find $rcs_filename\n";
$unblamed{$file} = 1;
next;
}
my $revision = &parse_cvs_file($rcs_filename);
@text = &extract_revision($revision);
while (my ($line, $warn_rec) = each %{$lines_hash}) {
my $line_rev = $revision_map[$line-1];
my $who = $revision_author{$line_rev};
my $source_text = join '', @text[$line-3..$line+1];
$source_text =~ s/\t/ /g;
$who = "$who%netscape.com" unless $who =~ /[%]/;
$warn_rec->{line_rev} = $line_rev;
$warn_rec->{source} = $source_text;
$warnings_by_who{$who}{$file}{$line} = $warn_rec;
$total_who_count++ unless exists $who_count{$who};
$who_count{$who} += $warn_rec->{count};
}
}
}
sub print_warnings_as_html {
my ($fh, $br) = @_;
my ($buildname, $buildtime) = ($br->{buildname}, $br->{buildtime});
my $time_str = print_time( $buildtime );
# Change the default destination for print to $fh
my $old_fh = select($fh);
print <<"__END_HEADER";
<html>
<head>
<title>Blamed Build Warnings</title>
</head>
<body>
<font size="+2" face="Helvetica,Arial"><b>
Blamed Build Warnings
</b></font><br>
<font size="+1" face="Helvetica,Arial">
$buildname on $time_str<br>
$total_warnings_count total warnings
</font><p>
__END_HEADER
for $who (sort { $who_count{$b} <=> $who_count{$a}
|| $a cmp $b } keys %who_count) {
push @who_list, $who;
}
# Summary Table (name, count)
#
use POSIX;
print "<table border=0 cellpadding=1 cellspacing=0 bgcolor=#ededed>\n";
my $num_columns = 6;
my $num_rows = ceil($#who_list / $num_columns);
for (my $ii=0; $ii < $num_rows; $ii++) {
print "<tr>";
for (my $jj=0; $jj < $num_columns; $jj++) {
my $index = $ii + $jj * $num_rows;
next if $index > $#who_list;
my $name = $who_list[$index];
my $count = $who_count{$name};
$name =~ s/%.*//;
print " " x $jj;
print "<td><a href='#$name'>$name</a>";
print "</td><td>";
print "$count";
print "</td><td>&nbsp;</td>\n";
}
print "</tr>\n";
}
print "</table><p>\n";
# Count Unblamed warnings
#
my $total_unblamed_warnigns=0;
for my $file (keys %unblamed) {
for my $linenum (keys %{$warnings{$file}}) {
$total_unblamed_warnings += $warnings{$file}{$linenum}{count};
$warnings_by_who{Unblamed}{$file}{$linenum} = $warnings{$file}{$linenum};
}
}
$who_count{Unblamed} = $total_unblamed_warnings;
# Print all the warnings
#
for $who (@who_list, "Unblamed") {
my $total_count = $who_count{$who};
my ($name, $email);
($name = $who) =~ s/%.*//;
($email = $who) =~ s/%/@/;
print "<h2>";
print "<a name='$name' href='mailto:$email'>" unless $name eq 'Unblamed';
print "$name";
print "</a>" unless $name eq 'Unblamed';
print " (1 warning)" if $total_count == 1;
print " ($total_count warnings)" if $total_count > 1;
print "</h2>";
print "\n<table>\n";
my $count = 1;
for $file (sort keys %{$warnings_by_who{$who}}) {
for $linenum (sort keys %{$warnings_by_who{$who}{$file}}) {
my $warn_rec = $warnings_by_who{$who}{$file}{$linenum};
print_count($count, $warn_rec->{count});
print_warning($tree, $br, $file, $linenum, $warn_rec);
print_source_code($linenum, $warn_rec) unless $unblamed{$file};
$count += $warn_rec->{count};
}
}
print "</table>\n";
}
print <<"__END_FOOTER";
<p>
<hr align=left>
Send questions or comments to
&lt;<a href="mailto:slamm\@netscape.com?subject=About the Blamed Build Warnings">slamm\@netcape.com</a>&gt;.
</body></html>
__END_FOOTER
# Change default destination back.
select($old_fh);
}
sub print_count {
my ($start, $count) = @_;
print "<tr><td align=right>$start";
print "-".($start+$count-1) if $count > 1;
print ".</td>";
}
sub print_warning {
my ($tree, $br, $file, $linenum, $warn_rec) = @_;
my $warning = $warn_rec->{warning_text};
print "<td>";
# File link
if ($file =~ /\[multiple\]/) {
$file =~ s/\[multiple\]\///;
print "<a href='http://lxr.mozilla.org/seamonkey/find?string=$file'>";
print "$file:$linenum";
print "</a> (multiple file matches)";
} elsif ($file =~ /\[no_match\]/) {
$file =~ s/\[no_match\]\///;
print "<b>$file:$linenum</b> (no file match)";
} else {
print "<a href='"
.file_url($file,$linenum)."'>";
print "$file:$linenum";
print "</a> ";
}
print "</td></tr><tr><td></td><td>";
# Warning text
print "\u$warning";
# Build log link
my $log_line = $warn_rec->{first_seen_line};
print " (<a href='"
.build_url($tree, $br, $log_line)
."'>";
if ($warn_rec->{count} == 1) {
print "See build log excerpt";
} else {
print "See 1st of $warn_rec->{count} occurrences in build log";
}
print "</a>)</td></tr>";
}
sub print_source_code {
my ($linenum, $warn_rec) = @_;
my $warning = $warn_rec->{warning_text};
# Source code fragment
#
my ($keyword) = $warning =~ /\`([^\']*)\'/;
print "<tr><td></td><td bgcolor=#ededed>";
print "<pre><font size='-1'>";
my $source_text = trim_common_leading_whitespace($warn_rec->{source});
$source_text =~ s/&/&amp;/gm;
$source_text =~ s/</&lt;/gm;
$source_text =~ s/>/&gt;/gm;
$source_text =~ s|\b\Q$keyword\E\b|<b>$keyword</b>|gm unless $keyword eq '';
my $line_index = $linenum - 2;
$source_text =~ s/^(.*)$/$line_index++." $1"/gme;
$source_text =~ s|^($linenum.*)$|<font color='red'>\1</font>|gm;
chomp $source_text;
print $source_text;
print "</font>";
#print "</pre>";
print "</td></tr>\n";
}
sub build_url {
my ($tree, $br, $linenum) = @_;
my $name = $br->{buildname};
$name =~ s/ /%20/g;
return "../showlog.cgi?log=$tree/$br->{logfile}:$linenum";
}
sub file_url {
my ($file, $linenum) = @_;
return "http://cvs-mirror.mozilla.org/webtools/bonsai/cvsblame.cgi"
."?file=mozilla/$file&mark=$linenum#".($linenum-10);
}
sub trim_common_leading_whitespace {
# Adapted from dequote() in Perl Cookbook by Christiansen and Torkington
local $_ = shift;
my $white; # common whitespace
if (/(?:(\s*).*\n)(?:(?:\1.*\n)|(?:\s*\n))+$/) {
$white = $1;
} else {
$white = /^(\s+)/;
}
s/^(?:$white)?//gm;
s/^\s+$//gm;
return $_;
}