001: /*
002: * Copyright 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.jdt;
017:
018: import com.google.gwt.dev.jjs.InternalCompilerException;
019: import com.google.gwt.dev.js.JsParser;
020: import com.google.gwt.dev.js.JsParserException;
021: import com.google.gwt.dev.js.ast.JsContext;
022: import com.google.gwt.dev.js.ast.JsExpression;
023: import com.google.gwt.dev.js.ast.JsNameRef;
024: import com.google.gwt.dev.js.ast.JsProgram;
025: import com.google.gwt.dev.js.ast.JsStatement;
026: import com.google.gwt.dev.js.ast.JsVisitor;
027:
028: import org.eclipse.jdt.internal.compiler.ASTVisitor;
029: import org.eclipse.jdt.internal.compiler.ast.Argument;
030: import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
031: import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
032:
033: import java.io.IOException;
034: import java.io.StringReader;
035: import java.util.List;
036: import java.util.Set;
037:
038: /**
039: * Walks the AST to find references to Java identifiers from within JSNI blocks.
040: */
041: public class FindJsniRefVisitor extends ASTVisitor {
042:
043: private final Set<String> jsniClasses;
044: private final JsParser jsParser = new JsParser();
045: private final JsProgram jsProgram = new JsProgram();
046:
047: public FindJsniRefVisitor(Set<String> jsniClasses) {
048: this .jsniClasses = jsniClasses;
049: }
050:
051: public boolean visit(MethodDeclaration methodDeclaration,
052: ClassScope scope) {
053: if (!methodDeclaration.isNative()) {
054: return false;
055: }
056:
057: // Handle JSNI block
058: char[] source = methodDeclaration.compilationResult()
059: .getCompilationUnit().getContents();
060: String jsniCode = String.valueOf(source,
061: methodDeclaration.bodyStart, methodDeclaration.bodyEnd
062: - methodDeclaration.bodyStart + 1);
063: int startPos = jsniCode.indexOf("/*-{");
064: int endPos = jsniCode.lastIndexOf("}-*/");
065: if (startPos < 0 || endPos < 0) {
066: return false; // ignore the error
067: }
068:
069: startPos += 3; // move up to open brace
070: endPos += 1; // move past close brace
071:
072: jsniCode = jsniCode.substring(startPos, endPos);
073:
074: String syntheticFnHeader = "function(";
075: boolean first = true;
076: if (methodDeclaration.arguments != null) {
077: for (int i = 0, c = methodDeclaration.arguments.length; i < c; ++i) {
078: Argument arg = methodDeclaration.arguments[i];
079: if (first) {
080: first = false;
081: } else {
082: syntheticFnHeader += ',';
083: }
084: syntheticFnHeader += String.valueOf(arg.name);
085: }
086: }
087: syntheticFnHeader += ')';
088: StringReader sr = new StringReader(syntheticFnHeader + '\n'
089: + jsniCode);
090: try {
091: // start at -1 to avoid counting our synthetic header
092: List<JsStatement> result = jsParser.parse(jsProgram
093: .getScope(), sr, -1);
094: new JsVisitor() {
095: public void endVisit(JsNameRef x,
096: JsContext<JsExpression> ctx) {
097: String ident = x.getIdent();
098: if (ident.charAt(0) == '@') {
099: String className = ident.substring(1, ident
100: .indexOf(':'));
101: jsniClasses.add(className);
102: }
103: }
104: }.acceptList(result);
105: } catch (IOException e) {
106: throw new InternalCompilerException(
107: "Internal error searching for JSNI references", e);
108: } catch (JsParserException e) {
109: // ignore, we only care about finding valid references
110: }
111:
112: return false;
113: }
114:
115: }
|