boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
author Jaroslav Tulach <jtulach@netbeans.org>
Sat, 21 Nov 2015 18:05:03 +0100
changeset 1019 e66e962fed13
parent 932 f2de2ae88589
child 1051 d0e6c8f97dc3
permissions -rw-r--r--
Don't copy the HTML/Java synthetic fields - they will be generated at method visitEnd time. Eliminates class format errors after incremental compilations.
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.boot.impl;
    44 
    45 import java.io.IOException;
    46 import java.io.InputStream;
    47 import java.io.InputStreamReader;
    48 import java.io.Reader;
    49 import java.net.URL;
    50 import java.util.ArrayList;
    51 import java.util.Collections;
    52 import java.util.Enumeration;
    53 import java.util.List;
    54 import net.java.html.js.JavaScriptBody;
    55 import net.java.html.js.JavaScriptResource;
    56 import org.netbeans.html.boot.spi.Fn;
    57 import org.objectweb.asm.AnnotationVisitor;
    58 import org.objectweb.asm.ClassReader;
    59 import org.objectweb.asm.ClassVisitor;
    60 import org.objectweb.asm.ClassWriter;
    61 import org.objectweb.asm.FieldVisitor;
    62 import org.objectweb.asm.Label;
    63 import org.objectweb.asm.MethodVisitor;
    64 import org.objectweb.asm.Opcodes;
    65 import org.objectweb.asm.Type;
    66 import org.objectweb.asm.signature.SignatureReader;
    67 import org.objectweb.asm.signature.SignatureVisitor;
    68 import org.objectweb.asm.signature.SignatureWriter;
    69 
    70 /** Utilities related to bytecode transformations. Depend on asm.jar which
    71  * needs to be added to be provided to classpath to make methods in this 
    72  * class useful.
    73  *
    74  * @author Jaroslav Tulach
    75  */
    76 public final class FnUtils {
    77     
    78     private FnUtils() {
    79     }
    80     
    81     /** Seeks for {@link JavaScriptBody} and {@link JavaScriptResource} annotations
    82      * in the bytecode and converts them into real code. Used by Maven plugin
    83      * postprocessing classes.
    84      * 
    85      * @param bytecode the original bytecode with javascript specific annotations
    86      * @param loader the loader to load resources (scripts and classes) when needed
    87      * @return the transformed bytecode
    88      * @since 0.7
    89      */
    90     public static byte[] transform(byte[] bytecode, ClassLoader loader) {
    91         ClassReader cr = new ClassReader(bytecode) {
    92             // to allow us to compile with -profile compact1 on 
    93             // JDK8 while processing the class as JDK7, the highest
    94             // class format asm 4.1 understands to
    95             @Override
    96             public short readShort(int index) {
    97                 short s = super.readShort(index);
    98                 if (index == 6 && s > Opcodes.V1_7) {
    99                     return Opcodes.V1_7;
   100                 }
   101                 return s;
   102             }
   103         };
   104         FindInClass tst = new FindInClass(loader, null);
   105         cr.accept(tst, 0);
   106         if (tst.found > 0) {
   107             ClassWriter w = new ClassWriterEx(loader, cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
   108             FindInClass fic = new FindInClass(loader, w);
   109             cr.accept(fic, 0);
   110             bytecode = w.toByteArray();
   111         }
   112         return bytecode;
   113     }
   114     
   115     public static ClassLoader newLoader(final FindResources f, final Fn.Presenter d, ClassLoader parent) {
   116         return new JsClassLoaderImpl(parent, f, d);
   117     }
   118 
   119     static String callback(final String body) {
   120         return new JsCallback() {
   121             @Override
   122             protected CharSequence callMethod(
   123                 String ident, String fqn, String method, String params
   124             ) {
   125                 StringBuilder sb = new StringBuilder();
   126                 if (ident != null) {
   127                     sb.append("vm.raw$");
   128                 } else {
   129                     sb.append("vm.");
   130                 }
   131                 sb.append(mangle(fqn, method, params));
   132                 sb.append("(");
   133                 if (ident != null) {
   134                     sb.append(ident);
   135                 }
   136                 return sb;
   137             }
   138 
   139         }.parse(body);
   140     }
   141 
   142     private static final class FindInClass extends ClassVisitor {
   143         private String name;
   144         private int found;
   145         private String resource;
   146 
   147         public FindInClass(ClassLoader l, ClassVisitor cv) {
   148             super(Opcodes.ASM4, cv);
   149         }
   150 
   151         @Override
   152         public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
   153             this.name = name;
   154             super.visit(version, access, name, signature, superName, interfaces);
   155         }
   156 
   157         @Override
   158         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   159             final AnnotationVisitor del = super.visitAnnotation(desc, visible);
   160             if ("Lnet/java/html/js/JavaScriptResource;".equals(desc)) {
   161                 return new LoadResource(del);
   162             }
   163             return del;
   164         }
   165 
   166         @Override
   167         public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
   168             return new FindInMethod(access, name, desc,
   169                     super.visitMethod(access & (~Opcodes.ACC_NATIVE), name, desc, signature, exceptions)
   170             );
   171         }
   172 
   173         @Override
   174         public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
   175             if (name.startsWith("$$fn$$")) {
   176                 return null;
   177             }
   178             return superField(access, name, desc, signature, value);
   179         }
   180 
   181         final FieldVisitor superField(int access, String name, String desc, String signature, Object value) {
   182             return super.visitField(access, name, desc, signature, value);
   183         }
   184 
   185         private final class FindInMethod extends MethodVisitor {
   186 
   187             private final String name;
   188             private final String desc;
   189             private final int access;
   190             private FindInAnno fia;
   191             private boolean bodyGenerated;
   192 
   193             public FindInMethod(int access, String name, String desc, MethodVisitor mv) {
   194                 super(Opcodes.ASM4, mv);
   195                 this.access = access;
   196                 this.name = name;
   197                 this.desc = desc;
   198             }
   199 
   200             @Override
   201             public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   202                 if ("Lnet/java/html/js/JavaScriptBody;".equals(desc)) { // NOI18N
   203                     found++;
   204                     return new FindInAnno();
   205                 }
   206                 return super.visitAnnotation(desc, visible);
   207             }
   208 
   209             private void generateJSBody(FindInAnno fia) {
   210                 this.fia = fia;
   211             }
   212 
   213             @Override
   214             public void visitCode() {
   215                 if (fia == null) {
   216                     return;
   217                 }
   218                 generateBody(true);
   219             }
   220 
   221             private boolean generateBody(boolean hasCode) {
   222                 if (bodyGenerated) {
   223                     return false;
   224                 }
   225                 bodyGenerated = true;
   226                 if (mv != null) {
   227                     AnnotationVisitor va = super.visitAnnotation("Lnet/java/html/js/JavaScriptBody;", false);
   228                     AnnotationVisitor varr = va.visitArray("args");
   229                     for (String argName : fia.args) {
   230                         varr.visit(null, argName);
   231                     }
   232                     varr.visitEnd();
   233                     va.visit("javacall", fia.javacall);
   234                     va.visit("body", fia.body);
   235                     va.visitEnd();
   236                 }
   237                 
   238                 String body;
   239                 List<String> args;
   240                 if (fia.javacall) {
   241                     body = callback(fia.body);
   242                     args = new ArrayList<String>(fia.args);
   243                     args.add("vm");
   244                 } else {
   245                     body = fia.body;
   246                     args = fia.args;
   247                 }
   248 
   249                 super.visitFieldInsn(
   250                         Opcodes.GETSTATIC, FindInClass.this.name,
   251                         "$$fn$$" + name + "_" + found,
   252                         "Lorg/netbeans/html/boot/spi/Fn;"
   253                 );
   254                 super.visitInsn(Opcodes.DUP);
   255                 super.visitMethodInsn(
   256                         Opcodes.INVOKESTATIC,
   257                         "org/netbeans/html/boot/spi/Fn", "isValid",
   258                         "(Lorg/netbeans/html/boot/spi/Fn;)Z"
   259                 );
   260                 Label ifNotNull = new Label();
   261                 super.visitJumpInsn(Opcodes.IFNE, ifNotNull);
   262 
   263                 // init Fn
   264                 super.visitInsn(Opcodes.POP);
   265                 super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   266                 super.visitInsn(fia.keepAlive ? Opcodes.ICONST_1 : Opcodes.ICONST_0);
   267                 super.visitLdcInsn(body);
   268                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   269                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
   270                 boolean needsVM = false;
   271                 for (int i = 0; i < args.size(); i++) {
   272                     assert !needsVM;
   273                     String argName = args.get(i);
   274                     needsVM = "vm".equals(argName);
   275                     super.visitInsn(Opcodes.DUP);
   276                     super.visitIntInsn(Opcodes.BIPUSH, i);
   277                     super.visitLdcInsn(argName);
   278                     super.visitInsn(Opcodes.AASTORE);
   279                 }
   280                 super.visitMethodInsn(Opcodes.INVOKESTATIC,
   281                         "org/netbeans/html/boot/spi/Fn", "define",
   282                         "(Ljava/lang/Class;ZLjava/lang/String;[Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
   283                 );
   284                 Label noPresenter = new Label();
   285                 super.visitInsn(Opcodes.DUP);
   286                 super.visitJumpInsn(Opcodes.IFNULL, noPresenter);
   287                 if (resource != null) {
   288                     super.visitLdcInsn(Type.getObjectType(FindInClass.this.name));
   289                     super.visitLdcInsn(resource);
   290                     super.visitMethodInsn(Opcodes.INVOKESTATIC,
   291                             "org/netbeans/html/boot/spi/Fn", "preload",
   292                             "(Lorg/netbeans/html/boot/spi/Fn;Ljava/lang/Class;Ljava/lang/String;)Lorg/netbeans/html/boot/spi/Fn;"
   293                     );
   294                 }
   295                 super.visitInsn(Opcodes.DUP);
   296                 super.visitFieldInsn(
   297                         Opcodes.PUTSTATIC, FindInClass.this.name,
   298                         "$$fn$$" + name + "_" + found,
   299                         "Lorg/netbeans/html/boot/spi/Fn;"
   300                 );
   301                 // end of Fn init
   302 
   303                 super.visitLabel(ifNotNull);
   304 
   305                 final int offset;
   306                 if ((access & Opcodes.ACC_STATIC) == 0) {
   307                     offset = 1;
   308                     super.visitIntInsn(Opcodes.ALOAD, 0);
   309                 } else {
   310                     offset = 0;
   311                     super.visitInsn(Opcodes.ACONST_NULL);
   312                 }
   313 
   314                 super.visitIntInsn(Opcodes.SIPUSH, args.size());
   315                 super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
   316 
   317                 class SV extends SignatureVisitor {
   318 
   319                     private boolean nowReturn;
   320                     private Type returnType;
   321                     private int index;
   322                     private int loadIndex = offset;
   323 
   324                     public SV() {
   325                         super(Opcodes.ASM4);
   326                     }
   327 
   328                     @Override
   329                     public void visitBaseType(char descriptor) {
   330                         final Type t = Type.getType("" + descriptor);
   331                         if (nowReturn) {
   332                             returnType = t;
   333                             return;
   334                         }
   335                         FindInMethod.super.visitInsn(Opcodes.DUP);
   336                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
   337                         FindInMethod.super.visitVarInsn(t.getOpcode(Opcodes.ILOAD), loadIndex++);
   338                         String factory;
   339                         switch (descriptor) {
   340                             case 'I':
   341                                 factory = "java/lang/Integer";
   342                                 break;
   343                             case 'J':
   344                                 factory = "java/lang/Long";
   345                                 loadIndex++;
   346                                 break;
   347                             case 'S':
   348                                 factory = "java/lang/Short";
   349                                 break;
   350                             case 'F':
   351                                 factory = "java/lang/Float";
   352                                 break;
   353                             case 'D':
   354                                 factory = "java/lang/Double";
   355                                 loadIndex++;
   356                                 break;
   357                             case 'Z':
   358                                 factory = "java/lang/Boolean";
   359                                 break;
   360                             case 'C':
   361                                 factory = "java/lang/Character";
   362                                 break;
   363                             case 'B':
   364                                 factory = "java/lang/Byte";
   365                                 break;
   366                             default:
   367                                 throw new IllegalStateException(t.toString());
   368                         }
   369                         FindInMethod.super.visitMethodInsn(Opcodes.INVOKESTATIC,
   370                                 factory, "valueOf", "(" + descriptor + ")L" + factory + ";"
   371                         );
   372                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   373                     }
   374 
   375                     @Override
   376                     public SignatureVisitor visitArrayType() {
   377                         if (nowReturn) {
   378                             return new SignatureVisitor(Opcodes.ASM4) {
   379                                 @Override
   380                                 public void visitClassType(String name) {
   381                                     returnType = Type.getType("[" + Type.getObjectType(name).getDescriptor());
   382                                 }
   383                             };
   384                         }
   385                         loadObject();
   386                         return new SignatureWriter();
   387                     }
   388 
   389                     @Override
   390                     public void visitClassType(String name) {
   391                         if (nowReturn) {
   392                             returnType = Type.getObjectType(name);
   393                             return;
   394                         }
   395                         loadObject();
   396                     }
   397 
   398                     @Override
   399                     public SignatureVisitor visitReturnType() {
   400                         nowReturn = true;
   401                         return this;
   402                     }
   403 
   404                     private void loadObject() {
   405                         FindInMethod.super.visitInsn(Opcodes.DUP);
   406                         FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, index++);
   407                         FindInMethod.super.visitVarInsn(Opcodes.ALOAD, loadIndex++);
   408                         FindInMethod.super.visitInsn(Opcodes.AASTORE);
   409                     }
   410 
   411                 }
   412                 SV sv = new SV();
   413                 SignatureReader sr = new SignatureReader(desc);
   414                 sr.accept(sv);
   415 
   416                 if (needsVM) {
   417                     FindInMethod.super.visitInsn(Opcodes.DUP);
   418                     FindInMethod.super.visitIntInsn(Opcodes.SIPUSH, sv.index);
   419                     int lastSlash = FindInClass.this.name.lastIndexOf('/');
   420                     String jsCallbacks = FindInClass.this.name.substring(0, lastSlash + 1) + "$JsCallbacks$";
   421                     FindInMethod.super.visitFieldInsn(Opcodes.GETSTATIC, jsCallbacks, "VM", "L" + jsCallbacks + ";");
   422                     FindInMethod.super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, jsCallbacks, "current", "()L" + jsCallbacks + ";");
   423                     FindInMethod.super.visitInsn(Opcodes.AASTORE);
   424                 }
   425 
   426                 if (fia.wait4js) {
   427                     super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   428                             "org/netbeans/html/boot/spi/Fn", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
   429                     );
   430                     switch (sv.returnType.getSort()) {
   431                         case Type.VOID:
   432                             super.visitInsn(Opcodes.RETURN);
   433                             break;
   434                         case Type.ARRAY:
   435                         case Type.OBJECT:
   436                             super.visitTypeInsn(Opcodes.CHECKCAST, sv.returnType.getInternalName());
   437                             super.visitInsn(Opcodes.ARETURN);
   438                             break;
   439                         case Type.BOOLEAN:
   440                             super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");
   441                             super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   442                                     "java/lang/Boolean", "booleanValue", "()Z"
   443                             );
   444                             super.visitInsn(Opcodes.IRETURN);
   445                             break;
   446                         default:
   447                             super.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Number");
   448                             super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   449                                     "java/lang/Number", sv.returnType.getClassName() + "Value", "()" + sv.returnType.getDescriptor()
   450                             );
   451                             super.visitInsn(sv.returnType.getOpcode(Opcodes.IRETURN));
   452                     }
   453                 } else {
   454                     super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
   455                             "org/netbeans/html/boot/spi/Fn", "invokeLater", "(Ljava/lang/Object;[Ljava/lang/Object;)V"
   456                     );
   457                     super.visitInsn(Opcodes.RETURN);
   458                 }
   459                 super.visitLabel(noPresenter);
   460                 if (hasCode) {
   461                     super.visitCode();
   462                 } else {
   463                     super.visitTypeInsn(Opcodes.NEW, "java/lang/IllegalStateException");
   464                     super.visitInsn(Opcodes.DUP);
   465                     super.visitLdcInsn("No presenter active. Use BrwsrCtx.execute!");
   466                     super.visitMethodInsn(Opcodes.INVOKESPECIAL, 
   467                         "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V"
   468                     );
   469                     this.visitInsn(Opcodes.ATHROW);
   470                 }
   471                 return true;
   472             }
   473             
   474             @Override
   475             public void visitEnd() {
   476                 super.visitEnd();
   477                 if (fia != null) {
   478                     if (generateBody(false)) {
   479                         // native method
   480                         super.visitMaxs(1, 0);
   481                     }
   482                     FindInClass.this.superField(
   483                             Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
   484                             "$$fn$$" + name + "_" + found,
   485                             "Lorg/netbeans/html/boot/spi/Fn;",
   486                             null, null
   487                     );
   488                 }
   489             }
   490 
   491             private final class FindInAnno extends AnnotationVisitor {
   492 
   493                 List<String> args = new ArrayList<String>();
   494                 String body;
   495                 boolean javacall = false;
   496                 boolean wait4js = true;
   497                 boolean keepAlive = true;
   498 
   499                 public FindInAnno() {
   500                     super(Opcodes.ASM4);
   501                 }
   502 
   503                 @Override
   504                 public void visit(String name, Object value) {
   505                     if (name == null) {
   506                         args.add((String) value);
   507                         return;
   508                     }
   509                     if (name.equals("javacall")) { // NOI18N
   510                         javacall = (Boolean) value;
   511                         return;
   512                     }
   513                     if (name.equals("wait4js")) { // NOI18N
   514                         wait4js = (Boolean) value;
   515                         return;
   516                     }
   517                     if (name.equals("keepAlive")) { // NOI18N
   518                         keepAlive = (Boolean) value;
   519                         return;
   520                     }
   521                     assert name.equals("body"); // NOI18N
   522                     body = (String) value;
   523                 }
   524 
   525                 @Override
   526                 public AnnotationVisitor visitArray(String name) {
   527                     return this;
   528                 }
   529 
   530                 @Override
   531                 public void visitEnd() {
   532                     if (body != null) {
   533                         generateJSBody(this);
   534                     }
   535                 }
   536             }
   537         }
   538 
   539         private final class LoadResource extends AnnotationVisitor {
   540             public LoadResource(AnnotationVisitor av) {
   541                 super(Opcodes.ASM4, av);
   542             }
   543 
   544             @Override
   545             public void visit(String attrName, Object value) {
   546                 super.visit(attrName, value);
   547                 String relPath = (String) value;
   548                 if (relPath.startsWith("/")) {
   549                     resource = relPath;
   550                 } else {
   551                     int last = name.lastIndexOf('/');
   552                     String fullPath = name.substring(0, last + 1) + relPath;
   553                     resource = fullPath;
   554                 }
   555             }
   556         }
   557     }
   558 
   559     private static class ClassWriterEx extends ClassWriter {
   560 
   561         private final ClassLoader loader;
   562 
   563         public ClassWriterEx(ClassLoader l, ClassReader classReader, int flags) {
   564             super(classReader, flags);
   565             this.loader = l;
   566         }
   567 
   568         @Override
   569         protected String getCommonSuperClass(final String type1, final String type2) {
   570             Class<?> c, d;
   571             try {
   572                 c = Class.forName(type1.replace('/', '.'), false, loader);
   573                 d = Class.forName(type2.replace('/', '.'), false, loader);
   574             } catch (Exception e) {
   575                 throw new RuntimeException(e.toString());
   576             }
   577             if (c.isAssignableFrom(d)) {
   578                 return type1;
   579             }
   580             if (d.isAssignableFrom(c)) {
   581                 return type2;
   582             }
   583             if (c.isInterface() || d.isInterface()) {
   584                 return "java/lang/Object";
   585             } else {
   586                 do {
   587                     c = c.getSuperclass();
   588                 } while (!c.isAssignableFrom(d));
   589                 return c.getName().replace('.', '/');
   590             }
   591         }
   592     }
   593 
   594     static class JsClassLoaderImpl extends JsClassLoader {
   595 
   596         private final FindResources f;
   597         private final Fn.Presenter d;
   598 
   599         public JsClassLoaderImpl(ClassLoader parent, FindResources f, Fn.Presenter d) {
   600             super(parent);
   601             setDefaultAssertionStatus(JsClassLoader.class.desiredAssertionStatus());
   602             this.f = f;
   603             this.d = d;
   604         }
   605 
   606         @Override
   607         protected URL findResource(String name) {
   608             List<URL> l = res(name, true);
   609             return l.isEmpty() ? null : l.get(0);
   610         }
   611 
   612         @Override
   613         protected Enumeration<URL> findResources(String name) {
   614             return Collections.enumeration(res(name, false));
   615         }
   616         
   617         private List<URL> res(String name, boolean oneIsEnough) {
   618             List<URL> l = new ArrayList<URL>();
   619             f.findResources(name, l, oneIsEnough);
   620             return l;
   621         }
   622     
   623         @Override
   624         protected Class<?> findClass(String name) throws ClassNotFoundException {
   625             if (name.startsWith("javafx")) {
   626                 return Class.forName(name);
   627             }
   628             if (name.startsWith("netscape")) {
   629                 return Class.forName(name);
   630             }
   631             if (name.startsWith("com.sun")) {
   632                 return Class.forName(name);
   633             }
   634             if (name.startsWith("org.netbeans.html.context.spi")) {
   635                 return Class.forName(name);
   636             }
   637             if (name.startsWith("net.java.html.BrwsrCtx")) {
   638                 return Class.forName(name);
   639             }
   640             if (name.equals(JsClassLoader.class.getName())) {
   641                 return JsClassLoader.class;
   642             }
   643             if (name.equals(Fn.class.getName())) {
   644                 return Fn.class;
   645             }
   646             if (name.equals(Fn.Presenter.class.getName())) {
   647                 return Fn.Presenter.class;
   648             }
   649             if (name.equals(Fn.ToJavaScript.class.getName())) {
   650                 return Fn.ToJavaScript.class;
   651             }
   652             if (name.equals(Fn.FromJavaScript.class.getName())) {
   653                 return Fn.FromJavaScript.class;
   654             }
   655             if (name.equals(FnUtils.class.getName())) {
   656                 return FnUtils.class;
   657             }
   658             if (
   659                 name.equals("org.netbeans.html.boot.spi.Fn") ||
   660                 name.equals("org.netbeans.html.boot.impl.FnUtils") ||
   661                 name.equals("org.netbeans.html.boot.impl.FnContext")
   662             ) {
   663                 return Class.forName(name);
   664             }
   665             URL u = findResource(name.replace('.', '/') + ".class");
   666             if (u != null) {
   667                 InputStream is = null;
   668                 try {
   669                     is = u.openStream();
   670                     byte[] arr = new byte[is.available()];
   671                     int len = 0;
   672                     while (len < arr.length) {
   673                         int read = is.read(arr, len, arr.length - len);
   674                         if (read == -1) {
   675                             throw new IOException("Can't read " + u);
   676                         }
   677                         len += read;
   678                     }
   679                     is.close();
   680                     is = null;
   681                     if (JsPkgCache.process(this, name)) {
   682                         arr = FnUtils.transform(arr, this);
   683                     }
   684                     return defineClass(name, arr, 0, arr.length);
   685                 } catch (IOException ex) {
   686                     throw new ClassNotFoundException("Can't load " + name, ex);
   687                 } finally {
   688                     try {
   689                         if (is != null) is.close();
   690                     } catch (IOException ex) {
   691                         throw new ClassNotFoundException(null, ex);
   692                     }
   693                 }
   694             }
   695             return super.findClass(name);
   696         }
   697     
   698         protected Fn defineFn(String code, String... names) {
   699             return d.defineFn(code, names);
   700         }
   701         
   702         protected void loadScript(Reader code) throws Exception {
   703             d.loadScript(code);
   704         }
   705     }
   706 }